;**************************************************************** ; 68705P3 6-Sided Die * ;**************************************************************** ; "Rolls" a six sided die with a display composed of seven * ; RGB LEDs. "18-Bit-ish" RGB is generated through software * ; 3-Channel PWM over PortC 0->2. * ; Results can be weighted by modifying Die_WeightTable. * ; * ; Written by: Osman Celimli * ;**************************************************************** PROCESSOR 68705 INCDIR "C:\MC68705" INCLUDE "68705P3.asm" ORG HARDWARE_BASE ; Padding for programmer dc.b $FF ; format restrictions (2048x8) ;**************************************************************** ; RAM Layout * ;**************************************************************** ORG RAM_START PWM_RGBState ds.b 1 ; 0= Switch Diode, Reactivate All ; 1= Deactivate All ; 2= Deactivate 2 ; 3= Deactivate 1 PWM_RGBTTable ds.b 4 ; 0=All Off,1=Last Off,2=Second Off,3=First Off,4=All On PWM_RGBDelays ds.b 4 ; Note that entry 0 is PWM_RGBState, since the value PWM_DiodeSelect ds.b 1 ; can be assumed to be zero when this is required. ; This means all accesses to this are shifted+1 PWM_DeadTime ds.b 7 PWM_BrightL ds.b 7 PWM_BrightM ds.b 7 PWM_BrightF ds.b 7 ; PWM Settings for each element delay, should sum to 64 PWM_Active ds.b 7 ; Number of active RGB Triads in one PWM Sweep PWM_MiddleOff ds.b 7 PWM_FirstOff ds.b 7 ; Order Diodes are shut off, Last not needed, assumed 0 Disp_Retrace ds.b 1 ; Pulled high every full display update (~60Hz) FrameCounter ds.b 1 DieDisp_RBright ds.b 7 DieDisp_GBright ds.b 7 DieDisp_BBright ds.b 7 ; Software modifiable die-display structure Attract_Dir ds.b 1 SetD_Num ds.b 1 SetD_R ds.b 1 SetD_G ds.b 1 SetD_B ds.b 1 SetD_Active ds.b 1 SetD_TempX ds.b 1 SetD_TSlot ds.b 1 SetD_DeadDelay ds.b 1 SetD_SortRGB ds.b 3 SetD_PortSet ds.b 3 ; RGB Setting Routine Temps Die_Rolled ds.b 1 Die_Rolling ds.b 1 Die_RollDel ds.b 1 Die_RollDelAcc ds.b 1 Die_RollDone ds.b 1 Die_Face ds.b 1 ;All these shared redefines are here since we're getting ;dangerously close to the system stack here... ; ; So we need to make sure there's just a little overhead ; for the IRQ handler (5 bytes) plus 3 extra. ;Die_TempX ds.b 1 Die_TempX EQU (SetD_TempX) ;Die_TempY ds.b 1 Die_TempY EQU (SetD_TSlot) ;Attract_Del ds.b 1 Attract_Del EQU (Die_RollDel) ECHO "RAM Left: ",(RAM_END-*) ;**************************************************************** ; ZeroPage EPROM * ; * ; * ;**************************************************************** ORG EPROM_ZEROPAGE ;**************************************************************** ; RGB Diode Transition Table * ;**************************************************************** PWM_Diode_SelO dc.b %01111110,%01111101,%01111011,%01110111,%01101111,%01011111,%00111111 PWM_Diode_SelA dc.b %11111110,%11111101,%11111011,%11110111,%11101111,%11011111,%10111111 ;**************************************************************** ; Timer IRQ * ;**************************************************************** ; Note- Frequency is obtained by a ; ((4Mhz Source Clock / 4 Pre-Prescaler) / 32 Prescaler) / 64 Max SW PWM Period ; ~488Hz IRQ For RGB Diode Select, ~70Hz for all 7 RGB Diode Updates ; Accounting for delays in this IRQ, it's probably more like ~60Hz... ; ; This isn't the most accurate PWM ever, but it's stable and the three ; software channels are good enough for color mixing on the diodes. Timer_IRQ SUBROUTINE LDA #%00000101 STA Timer_Control ; Clear IRQ LDX PWM_RGBState LDA PWM_RGBTTable-1,X STA PortC_Data ; Set RGB State DEX bmi .SelectNextDiode STX PWM_RGBState LDA PWM_RGBDelays,X STA Timer_Data ; Set next PWM Interval rti ; if single RGB PWM period isn't finished .SelectNextDiode LDX PWM_DiodeSelect DEX bpl .SetPortBState INC Disp_Retrace LDX #6 .SetPortBState STX PWM_DiodeSelect LDA PortB_Data ORA PWM_Diode_SelO,X AND PWM_Diode_SelA,X STA PortB_Data ; Set up Diode Select clr PWM_RGBTTable ; Last triad set assumed all off LDA PWM_MiddleOff,X ; Note the shifts present here. See variable descs. STA PWM_RGBTTable+1 LDA PWM_FirstOff,X STA PWM_RGBTTable+2 LDA PWM_DeadTime,X STA PWM_RGBDelays LDA PWM_BrightL,X STA PWM_RGBDelays+1 LDA PWM_BrightM,X STA PWM_RGBDelays+2 LDA PWM_BrightF,X STA PWM_RGBDelays+3 LDA PWM_Active,X STA PWM_RGBState ; Restart PWM State, Set Number of Active Triads TAX LDA PWM_RGBTTable,X STA PortC_Data ; Turn on RGB LDA PWM_RGBDelays,X STA Timer_Data ; Copy next PWM Interval rti ECHO "Zero Page ROM Left: ",(EPROM_USER-*) ;**************************************************************** ; Main User EPROM * ; * ; * ;**************************************************************** ORG EPROM_USER ;**************************************************************** ; Start * ;**************************************************************** Start SUBROUTINE jsr AttractMode jsr RollTrigger jsr RollDie jsr PostRollPhase .WaitforRetrace LDA Disp_Retrace beq .WaitforRetrace clr Disp_Retrace LDX #0 .CopyNewBrights STX SetD_Num LDA DieDisp_RBright,X STA SetD_R LDA DieDisp_GBright,X STA SetD_G LDA DieDisp_BBright,X STA SetD_B jsr SetDiodeColor LDX SetD_Num INX CPX #7 ; Go in the opposite order of the Display bne .CopyNewBrights ; IRQ to minimize delays... INC FrameCounter ; This has to be done outside the PWM IRQ, or else we get an bra Start ; unusually long VBlank. ;**************************************************************** ; Post-Roll Color Phasing * ;**************************************************************** PostRollPhase SUBROUTINE LDA Die_RollDone bne .DelayforAttract rts .DelayforAttract DEC Attract_Del beq .SetAttractMode rts .SetAttractMode LDA #1 STA Attract_Dir clr Die_Rolled ; Re-enable attract Mode (but scale down RGB first...) clr Die_RollDone clr Die_Rolling rts ;**************************************************************** ; Check for Roll Trigger * ;**************************************************************** RollTrigger SUBROUTINE LDA PortA_Data AND #$F CMP #$F bne .PortALow rts .PortALow LDX FrameCounter LDA Die_WeightTable,X STA Die_Face LDA #1 STA Die_Rolled ; Kill Attract Mode STA Die_Rolling LDA #240 STA Attract_Del ; Reset Attract Delay clr Die_RollDel clr Die_RollDelAcc clr Die_RollDone ; Clear Roll delay accumulator and roll completion flag. rts ;**************************************************************** ; Roll * ;**************************************************************** RollDie SUBROUTINE LDA Die_Rolling bne .DoRolling rts .DoRolling DEC Die_RollDel bmi .RollDelayExpire rts .RollDelayExpire LDA Die_RollDelAcc ADD #3 STA Die_RollDelAcc STA Die_RollDel CMP #50 bpl .DelayAccumulated sei LDA PortB_Data EOR #128 STA PortB_Data ; Click! Goes the Piezo. cli LDA Die_Face INC CMP #6 bne .NoFaceRollover LDA #0 .NoFaceRollover STA Die_Face jmp SetDieFace .DelayAccumulated LDA #1 STA Die_RollDone clr Die_Rolling rts ;**************************************************************** ; Set Die Image * ;**************************************************************** SetDieFace SUBROUTINE ; A=Die Number to Display TAX LDA DieRGB_Offsets,X STA Die_TempX LDX #0 .CopyDiePlanes STX Die_TempY LDX Die_TempX LDA DieRGB_Face1,X INX STX Die_TempX LDX Die_TempY STA DieDisp_RBright,X INX CPX #21 bne .CopyDiePlanes rts ; Note ; PCB Layout is like this : ; 0 1 2 ; 3 ; 4 5 6 ; With the piezo at the top left. ; However, the LEDs are wired backwards to the PortB outputs, so ; in terms of array indecies consider things to be the following: ; 6 5 4 ; 3 ; 2 1 0 ; Where each element has an RGB setting from 0-63. ; All the RGB settings below are stored in planarish format, 7x8bppR,G,B DieRGB_Face1 dc.b 0,0,0 dc.b 63 dc.b 0,0,0 dc.b 0,0,0 dc.b 0 dc.b 0,0,0 dc.b 0,0,0 dc.b 0 dc.b 0,0,0 ; Red One-Center DieRGB_Face2 dc.b 0,0,63 dc.b 0 dc.b 63,0,0 dc.b 0,0,63 dc.b 0 dc.b 63,0,0 dc.b 0,0,0 dc.b 0 dc.b 0,0,0 ; Yellow Top-Left and Bottom-Right DieRGB_Face3 dc.b 0,0,0 dc.b 0 dc.b 0,0,0 dc.b 0,0,63 dc.b 63 dc.b 63,0,0 dc.b 0,0,0 dc.b 0 dc.b 0,0,0 ; Green Diagonal Tri-Stripe DieRGB_Face4 dc.b 0,0,0 dc.b 0 dc.b 0,0,0 dc.b 63,0,63 dc.b 0 dc.b 63,0,63 dc.b 63,0,63 dc.b 0 dc.b 63,0,63 ; Turquoise Four-Square DieRGB_Face5 dc.b 0,0,0 dc.b 0 dc.b 0,0,0 dc.b 0,0,0 dc.b 0 dc.b 0,0,0 dc.b 63,0,63 dc.b 63 dc.b 63,0,63 ; Blue Five-Cross DieRGB_Face6 dc.b 63,63,63 dc.b 0 dc.b 63,63,63 dc.b 63,63,63 dc.b 0 dc.b 63,63,63 dc.b 63,63,63 dc.b 0 dc.b 63,63,63 ; White Six-Doubleline DieRGB_Offsets dc.b 0,21,42,63,84,105 ;**************************************************************** ; Startup Initialization * ;**************************************************************** MC68705_Init SUBROUTINE sei RSP ; First, Zero all of RAM. LDX #(RAM_END-RAM_START) .RAMClear clr RAM_START,X DEX bpl .RAMClear ; Now initialize the ports... LDA #0 STA PortA_DataDirection ; Port A is input (Switches) LDA #$FF STA PortB_DataDirection ; Both Port B (Piezo, RGB Cathode Select) and STA PortC_DataDirection ; and Port C (RGB, RUN) are output LDA #00 STA PortB_Data STA PortC_Data ; and are init'd to zero. LDA #7 STA PWM_RGBTTable+3 ; Force highest triad table to be all on. LDX #6 .Init_RGB LDA #%0001 STA PWM_MiddleOff,X LDA #%0011 STA PWM_FirstOff,X LDA #34 STA PWM_DeadTime,X LDA #3 STA PWM_Active,X ; All Active LDA #10 STA PWM_BrightL,X STA PWM_BrightM,X STA PWM_BrightF,X ; Initialize PWM Settings DEX bpl .Init_RGB LDA #63 STA Timer_Data ; Set down counter LDA #%00001101 STA Timer_Control ; Unmask IRQ, Reset Prescale, Start Timer. cli jmp Start ;**************************************************************** ; Set RGB Diode Color * ; Colors in SetD_R,SetD_G,Set_B (0-63). * ; Diode to Adjust in SetD_Num (0-6). * ;**************************************************************** SetDiodeColor SUBROUTINE LDX #2 ; First cap the raw RGB values LDA #3 STA SetD_Active .CapRGBSettings LDA SetD_R,X CMP DiodeColorHiCap,X bmi .CapRGBSettingLo LDA DiodeColorHiCap,X STA SetD_R,X bra .CapRGBSettingNext .CapRGBSettingLo LDA SetD_R,X CMP DiodeColorLoCap,X bpl .CapRGBSettingNext clr SetD_R,X DEC SetD_Active ; If < 1, Decrease the number of available traids .CapRGBSettingNext DEX bpl .CapRGBSettings ; RGB Values are all capped, now ensure =/= LDX #2 .CheckRGBEquality LDA SetD_R,X beq .ValueNotEqual CPX #0 beq .CheckGreen CMP SetD_R beq .AdjustOffset .CheckGreen CPX #1 beq .CheckBlue CMP SetD_G beq .AdjustOffset .CheckBlue CPX #2 beq .ValueNotEqual CMP SetD_B bne .ValueNotEqual .AdjustOffset INC SetD_R,X bra .CheckRGBEquality .ValueNotEqual DEX ; I feel like there should be a better way to do this... bpl .CheckRGBEquality ; But I can't think of anything right now that doesn't use two indexes. ; Anyway, now we need to find the sorted order of these things... LDX #2 .ClearSortList clr SetD_SortRGB,X clr SetD_PortSet,X DEX bpl .ClearSortList LDX #2 .CompareRGBOrder LDA SetD_R,X clr SetD_TSlot CPX #0 beq .CompareGreen CMP SetD_R bpl .CompareGreen INC SetD_TSlot .CompareGreen CPX #1 beq .CompareBlue CMP SetD_G bpl .CompareBlue INC SetD_TSlot .CompareBlue CPX #2 beq .SetRGBOrder CMP SetD_B bpl .SetRGBOrder INC SetD_TSlot .SetRGBOrder STX SetD_TempX LDX SetD_TSlot .CheckSlot LDA SetD_PortSet,X beq .CopySorted INX CPX #2 bne .CheckSlot .CopySorted STX SetD_TSlot LDX SetD_TempX LDA SetD_R,X LDX SetD_TSlot STA SetD_SortRGB,X LDX SetD_TempX LDA DiodeColorOff,X LDX SetD_TSlot STA SetD_PortSet,X LDX SetD_TempX DEX bpl .CompareRGBOrder ; Now everything is sorted, so let's generate the port outputs LDA SetD_PortSet AND SetD_PortSet + 1 AND SetD_PortSet + 2 STA SetD_PortSet LDA SetD_PortSet + 1 AND SetD_PortSet + 2 STA SetD_PortSet + 1 ; Then the RGB Delays LDA #63 SUB SetD_SortRGB STA SetD_DeadDelay LDA SetD_SortRGB SUB SetD_SortRGB + 1 STA SetD_SortRGB LDA SetD_SortRGB + 1 SUB SetD_SortRGB + 2 STA SetD_SortRGB + 1 LDX SetD_Num ; Set things in PWM-Land LDA SetD_SortRGB .WaitforSafeInterval CPX PWM_DiodeSelect beq .WaitforSafeInterval sei ; Hopefully this will be quick enough... STA PWM_BrightL,X LDA SetD_SortRGB + 1 STA PWM_BrightM,X LDA SetD_SortRGB + 2 STA PWM_BrightF,X LDA SetD_DeadDelay STA PWM_DeadTime,X ; First the delays... LDA SetD_Active STA PWM_Active,X ; Then the active triads... LDA SetD_PortSet + 1 STA PWM_MiddleOff,X LDA SetD_PortSet + 2 STA PWM_FirstOff,X ; Then the port settings cli ; Done! rts DiodeColorOff dc.b %0110,%0101,%0011 ; PBGR DiodeColorHiCap dc.b 62,61,60 DiodeColorLoCap dc.b 1,1,1 ;**************************************************************** ; Boot Attract Mode * ;**************************************************************** AttractMode SUBROUTINE LDA Die_Rolled beq .DoRGBStrobes rts .DoRGBStrobes LDA Attract_Dir beq .ScaleUp00 .ScaleDown00 LDX #0 .ScaleDown01 LDA DieDisp_RBright,X bne .ScaleDown02 INX CPX #21 bne .ScaleDown01 clr Attract_Dir rts .ScaleDown02 DEC DieDisp_RBright,X rts .ScaleUp00 LDX #0 .ScaleUp01 LDA DieDisp_RBright,X CMP #63 bne .ScaleUp02 INX CPX #21 bne .ScaleUp01 INC Attract_Dir rts .ScaleUp02 INC DieDisp_RBright,X rts ;**************************************************************** ; Dummy IRQ * ;**************************************************************** Dummy_IRQ SUBROUTINE rti ;**************************************************************** ; WeightTable * ; * ;**************************************************************** ; You can modify this to "weight" the die rolls. ; Right now the distribution is marginally leaning towards 2-3-4-5 Die_WeightTable dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5,1 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5,2 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5,3 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5,4 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 dc.b 0,1,2,3,4,5 ECHO "User EROM Left: ",(EPROM_MASKREG-*) ;**************************************************************** ; Mask Option Regs * ;**************************************************************** ORG EPROM_MASKREG ; Crystal Clock, Software Timer ; Internal Clock Timer Source, External Timer Disable ; and Prescale setting of /32. MaskReg_Setting dc.b %00000101 ;**************************************************************** ; IRQ Vectors * ;**************************************************************** ORG EPROM_IRQVecs TimerIRQVec dc.w Timer_IRQ ExtIRQVec dc.w Dummy_IRQ SWIRQVec dc.w Timer_IRQ ResetVec dc.w MC68705_Init