As part of training for new employees at BNG, we planned, soldered, and programmed (in assembly) games on a practice circuit boards with Renesas 16-bit H8/300H Tiny Series processors (specifically the H8/3687).
Here's a video of what I made:
As you can see, it's a Pac-man themed dot-eating game. I implemented 3 modes:
- The first mode is an attract mode, like you'd see in arcades (when nobody's playing the game, it plays itself). The dots slowly enter Pac-man's mouth and he eats them and gets larger and it flashes when he's at maximum size. Also the digital display (the green number 8 LED) spins around in a circle.
- The second mode is a timing game, where you have to push the green button when the dot is in Pac-man's mouth. He eats it and grows larger.
- The third mode is a button sequence game in which you just push the buttons in the correct order to feed pack man. (When we were soldering on the components, there was a lot of colored switches, so I put as many on as I could, and I had to think of something to use them for so they didn't go to waste.)
In addition to being able to change modes with the black button, the brown button changes difficulty, which is actually adjusting the speed of the timer that controls everything.
Here's a picture of the circuit diagram as planned in advance, though it is a little different than how it turned out in the end:
The code was written entirely in assembly, which was a new challenge for me. Compared to writing in C++, it felt like everything took years to do. But it does make you realize how every line of code you write in C++ is really comprised of a multitude of these moves between registers and RAM (not to mention chip cache) and how all this complex logic breaks simplifies down into branch-if-zero's and other simple math equations. One thing I didn't have time to do was to understand argument passing in functions. It looked complex and I had a lot to implement, so I just copied and pasted a few functions rather than mess with trying to use a feature I don't fully understand on a platform with no debugging to help me understand what I'm doing wrong. But it would have been nice to learn if I had the time.
The (amateurishly written) source is available for download here, and is also after the break, with some unfortunately pink syntax highlighting.
----
;H8Tiny Sample for H8/3687 ;tab=4 ; CPU Type .CPU 300HN ;CPU種別 ; Machine language list, and cross reference output settings .PRINT LIST,CREF ;機械語リスト、クロスリファレンスの出力指定 ;----------------------------------------------------------------------------- ;各内蔵ペリフェラルのアドレスをマクロ定数で定義 ;----------------------------------------------------------------------------- ; Turns on and off the individual on board LEDs (the tiny ones fabricated on the board) PDR1 .EQU H'FFD4 ;データレジスタ Port Data Register 1 ; Turns on and off the user-installed (soldered on) LEDs ; (The 16 user LEDs are controlled by two separate 8-bit registers) PDR3 .EQU H'FFD6 ;データレジスタ Port Data Register 3 PDR6 .EQU H'FFD9 ;データレジスタ Port Data Register 6 ; Indicates the state of the user-installed switches PDR5 .EQU H'FFD8 ;データレジスタ Port Data Register 5 ; Controls the functionality of the on-board LEDs PMR1 .EQU H'FFE0 ;モードレジスタ Port Mode Register 1 ; Controls the functionality of the user-installed switches PMR5 .EQU H'FFE1 ;モードレジスタ Port Mode Register 5 ; (I'm going to be honest here and say I don't really know why there are both a Port Mode and Port Control Register for everything. ; But both need to be set correctly to use the various peripherals.) ; Controls the functionality of the on-board LEDs PCR1 .EQU H'FFE4 ;ポートコントロール Port Control Register 1 ; Controls the functionality fo the user-installed LEDs PCR3 .EQU H'FFE6 ;ポートコントロール Port Control Register 3 PCR6 .EQU H'FFE9 ;ポートコントロール Port Control Register 6 ; Controls the functionality of the user-installed switches PCR5 .EQU H'FFE8 ;ポートコントロール Port Control Register 5 ; I forget what exactly the pull-up control registers were. ; But like everything else, you just had to pass them some value to make them happy so you could use their features PUCR1 .EQU H'FFD0 ;プルアップコントロール Port Pull-Up Control Register 1 - Related to the on-board LEDs PUCR5 .EQU H'FFD1 ;プルアップコントロール Port Pull-Up Control Register 5 - Related to the user-installed switches ; This register controls the mode and "speed" (what the cycle count is divided by) of Timer B1 TMB1 .EQU H'F760 ;Timer setup (higher numbers are faster clocks) ; Here is where you can read the clock TCB1 .EQU H'F761 ;Timer output ; Enable Timer B1 interrupts by setting bit 5 to 1 ; (When the clock's 8-bit buffer overflows, call an interrupt function IENR2 .EQU H'FFF5 ;Interrupt enable register 2 ; After you've handled the interrupt, set this flag back to 0 so the interrupt ; (Otherwise the interrupt function will continue to be called endlessly) IRR2 .EQU H'FFF7 ;Interrupt flag for Timer ; Digital display circle ; Defines what values need to be passed to the digital LED display to get the desired portion of the number 8 pattern ; The way I soldered it, it was split between the two user LED controls (PDR3 and PDR6), so it was slightly more complicated to code DIGITAL_DISPLAY_TOP .EQU 8 ;PDR6 LED control 12 DIGITAL_DISPLAY_TOP_RIGHT .EQU 16 ;PDR6 LED control 13 DIGITAL_DISPLAY_BOTTOM_RIGHT .EQU 32 ;PDR6 LED control 14 DIGITAL_DISPLAY_BOTTOM .EQU 32 ;PDR3 LED control 6 DIGITAL_DISPLAY_BOTTOM_LEFT .EQU 64 ;PDR3 LED control 7 DIGITAL_DISPLAY_TOP_LEFT .EQU 1 ;PDR6 LED control 9 DIGITAL_DISPLAY_MIDDLE .EQU 128 ;PDR3 LED control 8 ; The decimal point on the digital display (kinda useless) DIGITAL_DISPLAY_DECIMAL .EQU 64 ;PDR6 LED control 15 ; These were useful defines to turn on and off all the digital display LEDs, separated by LED control DIGITAL_DISPLAY_ALL_PDR3 .EQU 224 ; DIGITAL_DISPLAY_BOTTOM + DIGITAL_DISPLAY_BOTTOM_LEFT + DIGITAL_DISPLAY_MIDDLE DIGITAL_DISPLAY_NOT_ALL_PDR3 .EQU 31 ; ! DIGITAL_DISPLAY_ALL_PDR3 DIGITAL_DISPLAY_ALL_PDR6 .EQU 57 ; DIGITAL_DISPLAY_TOP + DIGITAL_DISPLAY_TOP_RIGHT + DIGITAL_DISPLAY_BOTTOM_RIGHT + DIGITAL_DISPLAY_TOP_LEFT DIGITAL_DISPLAY_NOT_ALL_PDR6 .EQU 198 ; ! DIGITAL_DISPLAY_ALL_PDR6 ; How to make various numbers on the digital display ; Can also be used for 0 (zero) DIGITAL_DISPLAY_CIRCLE_PDR3 .EQU DIGITAL_DISPLAY_BOTTOM_LEFT+DIGITAL_DISPLAY_BOTTOM;224 DIGITAL_DISPLAY_CIRCLE_PDR6 .EQU DIGITAL_DISPLAY_TOP+DIGITAL_DISPLAY_TOP_RIGHT+DIGITAL_DISPLAY_BOTTOM_RIGHT+DIGITAL_DISPLAY_TOP_LEFT;57 DIGITAL_DISPLAY_NUM_1 .EQU DIGITAL_DISPLAY_TOP_RIGHT+DIGITAL_DISPLAY_BOTTOM_RIGHT ;PDR6 only DIGITAL_DISPLAY_NUM_2_PDR3 .EQU DIGITAL_DISPLAY_MIDDLE+DIGITAL_DISPLAY_BOTTOM_LEFT+DIGITAL_DISPLAY_BOTTOM DIGITAL_DISPLAY_NUM_2_PDR6 .EQU DIGITAL_DISPLAY_TOP+DIGITAL_DISPLAY_TOP_RIGHT DIGITAL_DISPLAY_NUM_3_PDR3 .EQU DIGITAL_DISPLAY_MIDDLE+DIGITAL_DISPLAY_BOTTOM DIGITAL_DISPLAY_NUM_3_PDR6 .EQU DIGITAL_DISPLAY_TOP+DIGITAL_DISPLAY_TOP_RIGHT+DIGITAL_DISPLAY_BOTTOM_RIGHT DIGITAL_DISPLAY_NUM_4_PDR3 .EQU DIGITAL_DISPLAY_MIDDLE DIGITAL_DISPLAY_NUM_4_PDR6 .EQU DIGITAL_DISPLAY_TOP_LEFT+DIGITAL_DISPLAY_TOP_RIGHT+DIGITAL_DISPLAY_BOTTOM_RIGHT DIGITAL_DISPLAY_NUM_5_PDR3 .EQU DIGITAL_DISPLAY_MIDDLE+DIGITAL_DISPLAY_BOTTOM DIGITAL_DISPLAY_NUM_5_PDR6 .EQU DIGITAL_DISPLAY_TOP+DIGITAL_DISPLAY_TOP_LEFT+DIGITAL_DISPLAY_BOTTOM_RIGHT DIGITAL_DISPLAY_NUM_6_PDR3 .EQU DIGITAL_DISPLAY_ALL_PDR3 DIGITAL_DISPLAY_NUM_6_PDR6 .EQU DIGITAL_DISPLAY_TOP+DIGITAL_DISPLAY_TOP_LEFT+DIGITAL_DISPLAY_BOTTOM_RIGHT DIGITAL_DISPLAY_NUM_7_PDR6 .EQU DIGITAL_DISPLAY_TOP+DIGITAL_DISPLAY_TOP_RIGHT+DIGITAL_DISPLAY_BOTTOM_RIGHT DIGITAL_DISPLAY_NUM_8_PDR3 .EQU DIGITAL_DISPLAY_ALL_PDR3 DIGITAL_DISPLAY_NUM_8_PDR6 .EQU DIGITAL_DISPLAY_ALL_PDR6 DIGITAL_DISPLAY_NUM_9_PDR3 .EQU DIGITAL_DISPLAY_MIDDLE+DIGITAL_DISPLAY_BOTTOM DIGITAL_DISPLAY_NUM_9_PDR6 .EQU DIGITAL_DISPLAY_ALL_PDR6 ; Game constants DOT_ATTRACT_STATE_MAX .EQU 5 ; Maximum number of states for the dot LEDs in attract mode PAC_ATTRACT_STATE_MAX .EQU 5 ; Max states for Pac-man LEDs in attract mode DOT_GAME_STATE_MAX .EQU 5 ; Maximum number of states for the dot LEDs in game mode ; PAC-DOT LEDs DOT_1 .EQU 8 ;PDR3 LED control 4 DOT_2 .EQU 16 ;PDR3 LED control 5 DOT_3 .EQU 4 ;PDR6 LED control 11 DOT_4 .EQU 128 ;PDR6 LED control 16 DOT_ALL_PDR3 .EQU 24 ; DOT_1+DOT_2 DOT_ALL_PDR6 .EQU 132 ; DOT_3+DOT_4 DOT_NOT_ALL_PDR3 .EQU 231 ; !DOT_ALL_PDR3 DOT_NOT_ALL_PDR6 .EQU 123 ; !DOT_ALL_PDR6 ; PAC-MAN LEDs PAC_BOTTOM .EQU 4 ;PDR3 LED control 3 PAC_MOUTH .EQU 2 ;PDR3 LED control 2 PAC_TOP .EQU 1 ;PDR3 LED control 1 PAC_ALL .EQU 7 ; PAC_BOTTOM+PAC_MOUTH+PAC_TOP PAC_NOT_ALL .EQU 248 ; !PAC_ALL ; Switch bits ; Mapping the bits of the switch register to my various colored switches ; (These variable names should really not be plural. It is 1 bit per switch.) SWITCH_GREEN_BITS .EQU 128 SWITCH_BLUE_BITS .EQU 64 SWITCH_RED_BITS .EQU 32 SWITCH_ORANGE_BITS .EQU 16 SWITCH_YELLOW_BITS .EQU 8 SWITCH_BROWN_BITS .EQU 4 SWITCH_BLACK_BITS .EQU 2 ;SWITCH_ALL_BUT_GREEN .EQU SWITCH_BLUE_BITS+SWITCH_RED_BITS+SWITCH_ORANGE_BITS... ; Game mode constants MODE_ATTRACT .EQU 1 MODE_TIMING .EQU 2 MODE_BUTTON .EQU 3 MAX_MODE .EQU 4 ; Maximum number of game modes + 1 ; Difficulty (speed) constants MAX_DIFFICULTY .EQU 7 ; Number of difficulties DEFAULT_DIFFICULTY .EQU 4 ; The one that it isn't too hard or too easy to play on ; At one point I was considering limiting the speed of the reading of the input buffer, but it turned out to not be necessary STORE_INPUT_FREQUENCY .EQU 0 ; How often to check input STORE_INPUT_DELAY .EQU 25 ; Delay ; Controls what the internal CPU clock count is divided by before being stored in the timer register ; Lower numbers are faster, not linear, maybe not even exponential. I think its just be some predefined numbers from the manufacturor TIMER_SETTING .EQU 5 ; Speed of timer interrupt ; Another game constant GAME_WIN_FLASH_LENGTH .EQU 1 ; How long to flash when you win a game ;----------------------------------------------------------------------------- ;割り込みベクタ定義 ; Interrupt Vector Definition ; This defines what memory address to jump to on the Timer B1 interrupt, and also on the start of the code ;----------------------------------------------------------------------------- VECTOR: .ORG H'0000 ;割り込みベクタのアドレス(ROMに配置) - Address of the interrupt vector .DATA.W _START ;Vector0 ;タイマ割り込みベクタのアドレス(ROMに配置) - Address of the timer interrupt vector .ORG H'003A .DATA.W _TIMER_INTERRUPT ;----------------------------------------------------------------------------- ;プログラムコード ; Program Code ;----------------------------------------------------------------------------- CODE: .ORG H'0042 ;プログラムの先頭アドレス(ROMに配置) ; Address of the start of the program code in ROM ;------------------------------------------------------------------------------ ;割り込みプログラム Interrupt program ;------------------------------------------------------------------------------ ;割り込みフラグクリア ;処理実体 ;割り込み戻り ; This is the Timer B1 interrupt function, called when the timer overflows ; Here I increment another buffer everytime the hardware Timer B1 overflows, creating a slower timer _TIMER_INTERRUPT: ;Save Previous State of registers MOV.L ER0,@_TEMP_INTERRUPT_ER0 MOV.L ER1,@_TEMP_INTERRUPT_ER1 MOV.L ER2,@_TEMP_INTERRUPT_ER2 MOV.L ER3,@_TEMP_INTERRUPT_ER3 MOV.L ER4,@_TEMP_INTERRUPT_ER4 MOV.L ER5,@_TEMP_INTERRUPT_ER5 MOV.L ER6,@_TEMP_INTERRUPT_ER6 ; ER7 is used for the stack, so never touch that ; By the way, memory addresses on this machine are as follows: ; ER0 through ER7 -> 32 Bits (but 7 is used for the stack) ; E1 through E7 -> The first 16 bits of ER0 through ER7 ; R1 through R7 -> The last 16 bits of ER0 through ER7 ; R1H through R7H -> The first 8 bits of R1 through R7 (This architecture is Big-Endian, so these are the High bits, hence the H) ; R1L through R1L -> The last 8 bits of R1 through R7 ; Possibly slow the timer down based on Difficulty setting ; If difficulty is 0 (hardest/fastest), we always increment, so jump straight to _INC_TIMER_OVERFLOW MOV.B @_DIFFICULTY,R5L CMP.B #0,R5L BEQ _INC_TIMER_OVERFLOW ; Increment Difficulty Skip Count MOV.B @_DIFFICULTY_SKIP_COUNT,R5L INC.B R5L MOV.B R5L,@_DIFFICULTY_SKIP_COUNT ; Everytime the difficulty skip count "wraps" (is equal to the desired difficulty), check input ; (Basically, the value stored in _DIFFICULTY is the number of times we skip incrementing the overflow count) MOV.B @_DIFFICULTY,R5H CMP.B R5H,R5L BNE _RESTORE_PRE_INT_STATE ; Reset _DIFFICULTY_SKIP_COUNT back to 0 MOV.B #0,R5L MOV.B R5L,@_DIFFICULTY_SKIP_COUNT _INC_TIMER_OVERFLOW: ; Increment Timer Overflow Count MOV.B @_TIMER_OVERFLOW_COUNT,R6L INC.B R6L MOV.B R6L,@_TIMER_OVERFLOW_COUNT ; Everytime the hardware Timer B1 overflow count wraps CMP.B #0,R6L BNE _RESTORE_PRE_INT_STATE ; Everytime the on-board Timer B1 overflows (or perhaps every few times based on difficulty setting)... ; ...we jump to this subroutine, incrementing our tertiary timer JSR @_TIMER_INTERRUPT_OVERFLOW _RESTORE_PRE_INT_STATE: ;Restore Previous State of the registers MOV.L @_TEMP_INTERRUPT_ER0,ER0 MOV.L @_TEMP_INTERRUPT_ER1,ER1 MOV.L @_TEMP_INTERRUPT_ER2,ER2 MOV.L @_TEMP_INTERRUPT_ER3,ER3 MOV.L @_TEMP_INTERRUPT_ER4,ER4 MOV.L @_TEMP_INTERRUPT_ER5,ER5 MOV.L @_TEMP_INTERRUPT_ER6,ER6 ; Clear the interrupt flag so we return to normal processing (for now) _CLEAR_INTERRUPT_FLAG: MOV.B #0,R6H MOV.B R6H,@IRR2 ; Return from interrupt command RTE ; This is our tertiary timer, incremented every time _TIMER_OVERFLOW_COUNT itself overflows (thus tertiary) _TIMER_INTERRUPT_OVERFLOW: ; Increment dot_attract_state ; This makes the dots light up in succession MOV.B @_DOT_ATTRACT_STATE,R6L INC.B R6L ; Everytime dot_attract_state reaches Max, increment pac_attract_state, ; And reset dot_attract_state ; This makes Pac-man light up progressively. Done in time with dot_attract_state, it makes him appear to "grow" while eating CMP.B #DOT_ATTRACT_STATE_MAX,R6L BNE _SET_DOT_ATTRACT MOV.B #1,R6L ;Reset MOV.B @_PAC_ATTRACT_STATE,R6H INC.B R6H ; Everytime pac_attract_state reaches max, reset CMP.B #PAC_ATTRACT_STATE_MAX,R6H BNE _SET_PAC_ATTRACT MOV.B #1,R6H ; In attract mode, every other time PAC_ATTRACT_STATE wraps, we flash for the whole next count ; Flip attract flash state MOV.B @_ATTRACT_FLASH_STATE,R5L NOT.B R5L MOV.B R5L,@_ATTRACT_FLASH_STATE ; Check game flash state ; We do the same flashing thing in game mode, but it's only when a certain condition is met (you have won) MOV.B @_GAME_FLASH_STATE,R5L CMP.B #0,R5L BEQ _SET_PAC_ATTRACT ; Count down if Game Flash state is > 0 DEC.B R5L MOV.B R5L,@_GAME_FLASH_STATE ; Store pac attract state _SET_PAC_ATTRACT: MOV.B R6H,@_PAC_ATTRACT_STATE ; Store dot attract state _SET_DOT_ATTRACT: MOV.B R6L,@_DOT_ATTRACT_STATE ; Return from subroutine command RTS _START: ;SP SET スタックトップのアドレス(RAMに配置) ; Set the top of the stack in RAM MOV.W #H'EF00,R7 _TIMER_INIT: ;タイマーの設定 Timer Settings MOV.B #TIMER_SETTING,R0L MOV.B R0L,@TMB1 _INTR_ENABLE: ;割り込み許可 ;Enable Timer B1 interrupts MOV.B #H'FF,R4L MOV.B R4L,@IENR2 ;Enable Interrupts Globally LDC.B #0,CCR _IO_INIT: ;入出力の設定 ; Init input MOV.W #H'FF00,R0 MOV.W #H'F00F,R1 MOV.W #H'FFFF,R2 MOV.W #H'0000,R3 ;PORT1 IN[0-3] OUT[4-7] ; Setup the on-board LEDs ;MOV.B R1H,@PDR1 MOV.B R1H,@PCR1 ; Additional setup for on-board LEDs? MOV.B R0L,@PMR1 ; Yet more setup for on-board LEDs? MOV.B R1L,@PUCR1 ; Setup the user LEDs 1-8 ;MOV.B R1H,@PDR3 MOV.B R2H,@PCR3 ; Setup user LEDs 9-16 ;MOV.B R1H,@PDR6 MOV.B R2H,@PCR6 ; Setup switches MOV.B R3H,@PCR5 ; Wants to be all 0s MOV.B R2H,@PUCR5 ; Wants to be all 1s MOV.B R3H,@PMR5 ; Wants to be all 0s _VAR_INIT: ;RAMに取った変数の初期化 MOV.B #1,R0L MOV.B R0L,@_MODE ; Start off in attract mode ;TODO: Make this a constant MOV.B #DEFAULT_DIFFICULTY,R0L MOV.B R0L,@_DIFFICULTY MOV.B #0,R0L MOV.B R0L,@_PDR3_BUFFER ; Turn off all LEDs to begin MOV.B R0L,@_PDR6_BUFFER MOV.B #1,R0L MOV.B R0L,@_DOT_ATTRACT_STATE ; Pac and dot attract state start at 1 MOV.B R0L,@_PAC_ATTRACT_STATE MOV.B R0L,@_PAC_GAME_STATE ; Pac-Game state is 1 too MOV.B #0, R0L MOV.B R0L,@_ATTRACT_FLASH_STATE ; Attract flash state starts at 0 MOV.B R0L,@_GAME_FLASH_STATE ; Game flash state also 0 MOV.B #0, R0L ; Set initial switch input to 0 MOV.B R0L,@_SWITCH_CUR_STATE ; Current state of the switches (buttons) MOV.B R0L,@_SWITCH_PREV_STATE ; Last frame's button state MOV.B R0L,@_SWITCH_PREV_STATE_2 ; Two frames ago's button state, etc MOV.B R0L,@_SWITCH_PREV_STATE_3 MOV.B R0L,@_SWITCH_PREV_STATE_4 MOV.B R0L,@_SWITCH_PREV_STATE_5 MOV.B R0L,@_SWITCH_PREV_STATE_6 MOV.B R0L,@_SWITCH_PREV_STATE_7 MOV.B R0L,@_AGGREGATE_PREV_FRAMES_OR ; Previous frame inputs OR'd together MOV.B R0L,@_AGGREGATE_PREV_FRAMES_AND ; Previous frame inputs AND'd together MOV.B R0L,@_SWITCHED_ON_THIS_FRAME ; Which buttons were just pushed on this frame MOV.B R0L,@_SWITCHED_OFF_THIS_FRAME ; Which buttons were just released this frame MOV.B R0L,@_SWITCH_CHANGED_THIS_FRAME_OR ; Which buttons had their state change this frame (OR version) MOV.B R0L,@_SWITCH_CHANGED_THIS_FRAME_AND ; Which buttons had their state change this frame (AND version) MOV.B R0L,@_SWITCHED_OFF_THIS_FRAME ; Which buttons turned off this frame MOV.B R0L,@_SWITCHED_ON_THIS_FRAME ; Which buttons turned on this frame MOV.B R0L,@_DIFFICULTY_SKIP_COUNT ; Timer counts to skip based on difficulty (make it go slower) MOV.B #0, R0L MOV.B R0L,@_DOT_GAME_STATE ; Initial state of the dots in the game mode is 0 ; This is the main program loop _LOOP: ; This just counts up once a "frame" MOV.L @_COUNT,ER1 INC.L #1,ER1 MOV.L ER1,@_COUNT ; Flash on-board LEDs based on "Frame Count" ; (just a silly side-feature) MOV.W E1,R0 MOV.B R0L,@PDR1 ; REAL GAME CODE STARTS HERE ; First read the input on the switches, interpret it, and store it for later processing JSR @_STORE_INPUT ; See if the user requested a mode change JSR @_CHECK_MODE_CHANGE ; See if the user requested a difficulty change JSR @_CHECK_DIFFICULTY_CHANGE ; Execute the current mode JSR @_DO_CUR_MODE ; Set the actual hardware registers for the LEDs JSR @_SET_LEDS ;Reset variables that are only for this frame MOV.B #0, R4L MOV.B R4L,@_SWITCH_CHANGED_THIS_FRAME_OR MOV.B R4L,@_SWITCH_CHANGED_THIS_FRAME_AND MOV.B R4L,@_SWITCHED_OFF_THIS_FRAME MOV.B R4L,@_SWITCHED_ON_THIS_FRAME MOV.B R4L,@_SWITCHED_ON_THIS_FRAME MOV.B R4L,@_SWITCHED_OFF_THIS_FRAME ; End of main Loop BRA _LOOP ; Did the user request a mode change _CHECK_MODE_CHANGE: ; Check what buttons were pushed this frame MOV.B @_SWITCHED_ON_THIS_FRAME,R5L AND.B #SWITCH_BLACK_BITS, R5L ; Or with black switch bits CMP.B #0,R5L BEQ _DONT_CHANGE_MODE ; Maybe change mode (only change if the black button was pushed) ; Change mode by incrementing _INCREMENT_MODE: MOV.B @_MODE,R5L INC.B R5L ;Check if max mode CMP.B #MAX_MODE,R5L BNE _STORE_MODE ; Maybe reset mode to 1 ; Reset to 1 MOV.B #1,R5L _STORE_MODE: ; Store mode if we changed it MOV.B R5L,@_MODE RTS _DONT_CHANGE_MODE: ; Return RTS ; Did the user request a difficulty change _CHECK_DIFFICULTY_CHANGE: ; Check what buttons were pushed this frame MOV.B @_SWITCHED_ON_THIS_FRAME,R5L AND.B #SWITCH_BROWN_BITS, R5L ; Or with brown switch bits CMP.B #0,R5L BEQ _DONT_CHANGE_DIFFICULTY ; Maybe change difficulty _DECREMENT_DIFFICULTY: ; We decrement here because difficulty is displayed to the user as increasing MOV.B @_DIFFICULTY,R5L ; While the actual variable decreases to make the game harder DEC.B R5L ;Check if max difficulty CMP.B #-1,R5L BNE _STORE_DIFFICULTY ; Maybe reset difficulty to 1 ; Reset to Max MOV.B #MAX_DIFFICULTY,R5L _STORE_DIFFICULTY: ; Store difficulty if we changed it MOV.B R5L,@_DIFFICULTY RTS _DONT_CHANGE_DIFFICULTY: ; Return RTS ; Execute the current mode _DO_CUR_MODE: ; Mode Override while pushing change-mode button (Displays the current mode on the digital display) ; See if we're currently holding down Mode change button MOV.B @_AGGREGATE_PREV_FRAMES_AND,R5L AND.B #SWITCH_BLACK_BITS, R5L ; Or with black switch bits CMP.B #0,R5L BNE _DISPLAY_MODE_STATE ; Display current mode ; Mode Override while pushing change-difficulty button (Displays current difficulty) ; See if we're holding down Mode change button MOV.B @_AGGREGATE_PREV_FRAMES_AND,R5L AND.B #SWITCH_BROWN_BITS, R5L ; Or with brown switch bits CMP.B #0,R5L BNE _DISPLAY_DIFFICULTY_STATE ; Display current mode ;Attract Mode MOV.B @_MODE,R6H CMP.B #MODE_ATTRACT,R6H BEQ _MODE_0 ;Timing Game Mode CMP.B #MODE_TIMING,R6H BEQ _MODE_1 ;Button Game Mode CMP.B #MODE_BUTTON,R6H BEQ _MODE_2 ; Default ; Do nothing RTS ; Jump subroutine to the appropriate mode, then return _MODE_0: JSR @_ATTRACT_MODE RTS _MODE_1: JSR @_TIMING_GAME RTS _MODE_2: JSR @_BUTTON_GAME RTS _DISPLAY_DIFFICULTY_STATE: JSR @_DISPLAY_CURRENT_DIFFICULTY RTS _DISPLAY_MODE_STATE: JSR @_DISPLAY_CURRENT_MODE RTS ; This displays the current mode to the digital display (the green number 8 LED) _DISPLAY_CURRENT_MODE: ; Kill all user LEDs JSR @_DISABLE_ALL_USER_LEDS ;Attract Mode MOV.B @_MODE,R6H CMP.B #MODE_ATTRACT,R6H BEQ _DISPLAY_MODE_1 ;Timing Game Mode CMP.B #MODE_TIMING,R6H BEQ _DISPLAY_MODE_2 ; Button Sequence Game Mode CMP.B #3,R6H BEQ _DISPLAY_MODE_3 ; Could display other modes if they existed CMP.B #4,R6H BEQ _DISPLAY_MODE_4 CMP.B #5,R6H BEQ _DISPLAY_MODE_5 CMP.B #6,R6H BEQ _DISPLAY_MODE_6 CMP.B #7,R6H BEQ _DISPLAY_MODE_7 CMP.B #8,R6H BEQ _DISPLAY_MODE_8 CMP.B #9,R6H BEQ _DISPLAY_MODE_9 CMP.B #10,R6H BEQ _DISPLAY_MODE_10 ; Default RTS ; Jump to the corresponding function to display the appropriate number on the digital display _DISPLAY_MODE_1: JSR @_DISPLAY_NUMBER_1 RTS _DISPLAY_MODE_2: JSR @_DISPLAY_NUMBER_2 RTS _DISPLAY_MODE_3: JSR @_DISPLAY_NUMBER_3 RTS _DISPLAY_MODE_4: JSR @_DISPLAY_NUMBER_4 RTS _DISPLAY_MODE_5: JSR @_DISPLAY_NUMBER_5 RTS _DISPLAY_MODE_6: JSR @_DISPLAY_NUMBER_6 RTS _DISPLAY_MODE_7: JSR @_DISPLAY_NUMBER_7 RTS _DISPLAY_MODE_8: JSR @_DISPLAY_NUMBER_8 RTS _DISPLAY_MODE_9: JSR @_DISPLAY_NUMBER_9 RTS _DISPLAY_MODE_10: JSR @_DISPLAY_NUMBER_0 RTS ; This displays the current difficulty to the digital display (the green number 8 LED) _DISPLAY_CURRENT_DIFFICULTY: ; Kill all user LEDs JSR @_DISABLE_ALL_USER_LEDS MOV.B @_DIFFICULTY,R6H CMP.B #0,R6H BEQ _DISPLAY_DIFFICULTY_1 CMP.B #1,R6H BEQ _DISPLAY_DIFFICULTY_2 CMP.B #2,R6H BEQ _DISPLAY_DIFFICULTY_3 CMP.B #3,R6H BEQ _DISPLAY_DIFFICULTY_4 CMP.B #4,R6H BEQ _DISPLAY_DIFFICULTY_5 CMP.B #5,R6H BEQ _DISPLAY_DIFFICULTY_6 CMP.B #6,R6H BEQ _DISPLAY_DIFFICULTY_7 CMP.B #7,R6H BEQ _DISPLAY_DIFFICULTY_8 ; Only have 8 difficulty levels ; CMP.B #8,R6H ; BEQ _DISPLAY_DIFFICULTY_9 ; CMP.B #9,R6H ; BEQ _DISPLAY_DIFFICULTY_10 ; Default RTS ; Hardest difficulty is lowest number ; ( In game code, a lower value means faster game speed, so harder difficulty ) _DISPLAY_DIFFICULTY_1: JSR @_DISPLAY_NUMBER_8 RTS _DISPLAY_DIFFICULTY_2: JSR @_DISPLAY_NUMBER_7 RTS _DISPLAY_DIFFICULTY_3: JSR @_DISPLAY_NUMBER_6 RTS _DISPLAY_DIFFICULTY_4: JSR @_DISPLAY_NUMBER_5 RTS _DISPLAY_DIFFICULTY_5: JSR @_DISPLAY_NUMBER_4 RTS _DISPLAY_DIFFICULTY_6: JSR @_DISPLAY_NUMBER_3 RTS _DISPLAY_DIFFICULTY_7: JSR @_DISPLAY_NUMBER_2 RTS _DISPLAY_DIFFICULTY_8: JSR @_DISPLAY_NUMBER_1 RTS ; _DISPLAY_DIFFICULTY_9: ; JSR @_DISPLAY_NUMBER_9 ; RTS ; _DISPLAY_DIFFICULTY_10: ; JSR @_DISPLAY_NUMBER_0 ; RTS ; In attract mode, the game basically plays itself. ; Dots enter pac-man's mouth from the right, and he grows larger ; When he is at max size, he flashes _ATTRACT_MODE: ; Check flash state ( Are we currently flashing? ) MOV.B @_ATTRACT_FLASH_STATE,R2L CMP.B #0,R2L BNE _DISPLAY_FLASH ; Goto flash ; Otherwise, make a light run around the edges of the digital circle JSR @_DISPLAY_DIGITAL_CIRCLE ; Set the pac-dot LEDs for attract mode JSR @_ATTRACT_MODE_SET_DOTS ; Set pac-man LEDs for attract mode JSR @_ATTRACT_MODE_SET_PAC ;Return RTS ; If it's the flashing state _DISPLAY_FLASH: ; Do the flash and return JSR @_DO_ATTRACT_FLASH ; Return RTS ; Flash all the user LEDs on and off (well, except the middle of the digital display 8, and the decimal point) _DO_ATTRACT_FLASH: ; Reusing dot attract state ; (It's still counting up, so use it to control the flashing) MOV.B @_DOT_ATTRACT_STATE,R2L ; If it's 1 or 3, we're off, otherwise on CMP.B #1,R2L BEQ _DO_FLASH_OFF CMP.B #3,R2L BEQ _DO_FLASH_OFF ; All ON JSR @_DISPLAY_ALL_USER_LEDS RTS _DO_FLASH_OFF: ; All OFF JSR @_DISABLE_ALL_USER_LEDS RTS ; Play the timing game _TIMING_GAME: ; Check flash state ; ( Here, like attract mode, we flash when the game is over) MOV.B @_GAME_FLASH_STATE,R2L CMP.B #0,R2L BNE _DISPLAY_FLASH_TIMING ; Otherwise ; Run the light around the edge of the digital display JSR @_DISPLAY_DIGITAL_CIRCLE ; Check our input for the timing game JSR @_CHECK_TIMING_GAME_INPUT ; Set the LEDs JSR @_ATTRACT_MODE_SET_DOTS JSR @_GAME_MODE_SET_PAC ;Return RTS _DISPLAY_FLASH_TIMING: ; Do the flash JSR @_DO_ATTRACT_FLASH ; Return RTS ; Check if we're pushing the green button while the dot is in Pac-man's mouth _CHECK_TIMING_GAME_INPUT ;Has Green Switch just been pushed? ; Check what buttons were pushed this frame MOV.B @_SWITCHED_ON_THIS_FRAME,R5L AND.B #SWITCH_GREEN_BITS, R5L ; Or with green switch bits CMP.B #0,R5L BEQ _GREEN_NOT_PUSHED ; Skip if no input _GREEN_PUSHED: ; Was it pushed with the correct timing? (Dot state 4 means the dot is in pac-man's mouth) MOV.B @_DOT_ATTRACT_STATE, R5L CMP.B #4, R5L BEQ _INCREMENT_PAC_GAME_STATE ; If it was incorrectly pushed, progress is lost _WRONG_TIMING: MOV.B @_PAC_GAME_STATE,R5L DEC.B R5L ; Make sure it's not below 1 CMP.B #0,R5L BNE _STORE_PAC_GAME_STATE ; Reset to 1 MOV.B #1,R5L BRA _STORE_PAC_GAME_STATE ; Correct timing, increment pac_game_state _INCREMENT_PAC_GAME_STATE: MOV.B @_PAC_GAME_STATE,R5L INC.B R5L ;Check if max state CMP.B #PAC_ATTRACT_STATE_MAX,R5L BNE _STORE_PAC_GAME_STATE ; Maybe reset mode to 1 ; Reset to 1 ; Set to max for now MOV.B #1,R5L ; Flash for win MOV.B #GAME_WIN_FLASH_LENGTH,R5H ; This is used as a countdown timer for how long to flash MOV.B R5H, @_GAME_FLASH_STATE ; Using _PAC_ATTRACT_STATE to control flashing (even numbers ON, odd numbers OFF) ; (it's still being incremented regularly, just reset it to 1 so the timing matches up) MOV.B #1, R5H MOV.B R5H,@_PAC_ATTRACT_STATE _STORE_PAC_GAME_STATE: ; Store pac-game-state if we changed it MOV.B R5L,@_PAC_GAME_STATE RTS ; Do nothing if there's no input _GREEN_NOT_PUSHED: ; Return RTS ; The sequential button press game ; Game state progresses as you push buttons in the correct order to feed pac-man _BUTTON_GAME: ; Check flash state (again, when you win it flashes) MOV.B @_GAME_FLASH_STATE,R2L CMP.B #0,R2L BNE _BUT_DISPLAY_FLASH_TIMING ; Otherwise we are playing the game ; The light goes around in a circle on the digital display JSR @_DISPLAY_DIGITAL_CIRCLE ; Check the input for our button sequence game JSR @_CHECK_BUTTON_GAME_INPUT ; Set the LEDs for the Pac dots and Pac-man JSR @_GAME_MODE_SET_DOTS JSR @_GAME_MODE_SET_PAC ;Return RTS ; If we won, flash _BUT_DISPLAY_FLASH_TIMING: ; Use the logic from attract mode to flash JSR @_DO_ATTRACT_FLASH ; Return RTS ; Here we check to see if we're pushing the buttons in order _CHECK_BUTTON_GAME_INPUT: ;Has Yellow Switch just been pushed? ; Check what buttons were pushed this frame MOV.B @_SWITCHED_ON_THIS_FRAME,R5L AND.B #SWITCH_YELLOW_BITS, R5L ; Or with yellow switch bits CMP.B #0,R5L BEQ _YELLOW_NOT_PUSHED ; Skip if no input _YELLOW_PUSHED: ; Was it pushed with the correct timing? MOV.B @_DOT_GAME_STATE, R5L CMP.B #0, R5L BEQ _INCREMENT_DOT_GAME_STATE ; Otherwise do wrong button logic JMP @_WRONG_BUTTON _YELLOW_NOT_PUSHED: ;Has Orange Switch just been pushed? ; Check what buttons were pushed this frame MOV.B @_SWITCHED_ON_THIS_FRAME,R5L AND.B #SWITCH_ORANGE_BITS, R5L ; Or with orange switch bits CMP.B #0,R5L BEQ _ORANGE_NOT_PUSHED ; Skip if no input _ORANGE_PUSHED: ; Was it pushed with the correct timing? MOV.B @_DOT_GAME_STATE, R5L CMP.B #1, R5L BEQ _INCREMENT_DOT_GAME_STATE ; Otherwise do wrong button logic JMP @_WRONG_BUTTON _ORANGE_NOT_PUSHED: ;Has Red Switch just been pushed? ; Check what buttons were pushed this frame MOV.B @_SWITCHED_ON_THIS_FRAME,R5L AND.B #SWITCH_RED_BITS, R5L ; Or with red switch bits CMP.B #0,R5L BEQ _RED_NOT_PUSHED ; Skip if no input _RED_PUSHED: ; Was it pushed with the correct timing? MOV.B @_DOT_GAME_STATE, R5L CMP.B #2, R5L BEQ _INCREMENT_DOT_GAME_STATE ; Otherwise do wrong button logic JMP @_WRONG_BUTTON _RED_NOT_PUSHED: ;Has Blue Switch just been pushed? ; Check what buttons were pushed this frame MOV.B @_SWITCHED_ON_THIS_FRAME,R5L AND.B #SWITCH_BLUE_BITS, R5L ; Or with blue switch bits CMP.B #0,R5L BEQ _BLUE_NOT_PUSHED ; Skip if no input _BLUE_PUSHED: ; Was it pushed with the correct timing? MOV.B @_DOT_GAME_STATE, R5L CMP.B #3, R5L BEQ _INCREMENT_DOT_GAME_STATE ; Otherwise do wrong button logic JMP @_WRONG_BUTTON _BLUE_NOT_PUSHED: ;Has Green Switch just been pushed? ; Check what buttons were pushed this frame MOV.B @_SWITCHED_ON_THIS_FRAME,R5L AND.B #SWITCH_GREEN_BITS, R5L ; Or with green switch bits CMP.B #0,R5L BEQ _GREEN_NOT_PUSHED_BG ; Skip if no input _GREEN_PUSHED_BG: ; Was it pushed with the correct timing? MOV.B @_DOT_GAME_STATE, R5L CMP.B #4, R5L BEQ _INCREMENT_DOT_GAME_STATE ; Otherwise do wrong button logic JMP @_WRONG_BUTTON _GREEN_NOT_PUSHED_BG: ; No input, return RTS ; Reset game state for an incorrect button press _WRONG_BUTTON: JSR @_BUTTON_GAME_RESET RTS ; Increment game state for correct button presses _INCREMENT_DOT_GAME_STATE: JSR @_BUTTON_GAME_INCREMENT_STATE RTS ; Return RTS RTS ; This resets our two game state variables _BUTTON_GAME_RESET: MOV.B #0,R5L MOV.B R5L,@_DOT_GAME_STATE MOV.B #1,R5L MOV.B R5L,@_PAC_GAME_STATE RTS ; This increments pac-dot state until it reaches the max, then increments pac-man's state and resets dot state. ; When pac-man's state reaches the max, all is reset _BUTTON_GAME_INCREMENT_STATE: ; Correct timing, increment dot_game_state MOV.B @_DOT_GAME_STATE,R5L INC.B R5L ;Check if max state CMP.B #DOT_GAME_STATE_MAX,R5L BNE _STORE_DOT_GAME_STATE ; Maybe reset mode to 1 ; Reset to 0 MOV.B #0,R5L ; If done a whole Dot-loop, increment pac-state MOV.B @_PAC_GAME_STATE,R5H INC.B R5H ;Check if max state CMP.B #PAC_ATTRACT_STATE_MAX,R5H BNE _STORE_PAC_GAME_STATE_BG ; Maybe reset mode to 1 ; Reset to 1 MOV.B #1,R5H ; Flash for win MOV.B #GAME_WIN_FLASH_LENGTH,R5H MOV.B R5H, @_GAME_FLASH_STATE ; Using _PAC_ATTRACT_STATE to control flash timing MOV.B #1, R5H MOV.B R5H,@_PAC_ATTRACT_STATE _STORE_PAC_GAME_STATE_BG: MOV.B R5H,@_PAC_GAME_STATE _STORE_DOT_GAME_STATE: ; Store dot-game-state if we changed it MOV.B R5L,@_DOT_GAME_STATE RTS RTS ; Makes a light run around the circle of the digital display (green number 8) _DISPLAY_DIGITAL_CIRCLE: ; Use timer overflow count MOV.B @_TIMER_OVERFLOW_COUNT,R1L ; Digital display circle ; _x CMP.B #-84,R1L ; |_| BLT _LOOP_STAGE_1 ; |_| ; ; _ CMP.B #-44,R1L ; |_|x BLT _LOOP_STAGE_2 ; |_| ; ; _ CMP.B #-2,R1L ; |_| BLT _LOOP_STAGE_3 ; |_|x ; ; _ CMP.B #40,R1L ; |_| BLT _LOOP_STAGE_4 ; |_| ; x ; _ CMP.B #82,R1L ; |_| BLT _LOOP_STAGE_5 ;x|_| ; ; _ CMP.B #124,R1L ;x|_| BLT _LOOP_STAGE_6 ; |_| ; _LOOP_STAGE_1: JSR @_SET_DIGITAL_DISPLAY_TOP RTS _LOOP_STAGE_2: JSR @_SET_DIGITAL_DISPLAY_TOP_RIGHT RTS _LOOP_STAGE_3: JSR @_SET_DIGITAL_DISPLAY_BOTTOM_R RTS _LOOP_STAGE_4: JSR @_SET_DIGITAL_DISPLAY_BOTTOM RTS _LOOP_STAGE_5: JSR @_SET_DIGITAL_DISPLAY_BOTTOM_L RTS _LOOP_STAGE_6: JSR @_SET_DIGITAL_DISPLAY_TOP_LEFT RTS ; We check the current input state and compare it against the previous input. _STORE_INPUT: ;Reset Changed this frame MOV.B #0, R4L MOV.B R4L,@_SWITCH_CHANGED_THIS_FRAME MOV.B R4L,@_SWITCHED_ON_THIS_FRAME MOV.B R4L,@_SWITCHED_OFF_THIS_FRAME ; Store prev buffered input MOV.B @_SWITCH_BUFFERED_INPUT_CURRENT,R4L MOV.B R4L,@_SWITCH_BUFFERED_INPUT_PREV ; Save 7 Frames ago input MOV.B @_SWITCH_PREV_STATE_6,R4L MOV.B R4L,@_SWITCH_PREV_STATE_7 ; Save 6 Frames ago input MOV.B @_SWITCH_PREV_STATE_5,R4L MOV.B R4L,@_SWITCH_PREV_STATE_6 ; Save 5 Frames ago input MOV.B @_SWITCH_PREV_STATE_4,R4L MOV.B R4L,@_SWITCH_PREV_STATE_5 ; Save 4 Frames ago input MOV.B @_SWITCH_PREV_STATE_3,R4L MOV.B R4L,@_SWITCH_PREV_STATE_4 ; Save 3 Frames ago input MOV.B @_SWITCH_PREV_STATE_2,R4L MOV.B R4L,@_SWITCH_PREV_STATE_3 ; Save 2 Frames ago input MOV.B @_SWITCH_PREV_STATE,R4L MOV.B R4L,@_SWITCH_PREV_STATE_2 ;Save Previous Frame Input MOV.B @_SWITCH_CUR_STATE,R4L MOV.B R4L,@_SWITCH_PREV_STATE ; Get current switch state from the hardware registers MOV.B @PDR5,R4L NOT.B R4L ;Input is 1 OFF, 0 ON, so flip that to make it easier to understand MOV.B R4L,@_SWITCH_CUR_STATE ; Save Prev Frames ;Aggregate previous frames OR (only using 3 frames of previous input) MOV.B @_SWITCH_PREV_STATE, R4H MOV.B @_SWITCH_PREV_STATE_2, R4L OR.B R4L,R4H MOV.B @_SWITCH_PREV_STATE_3, R4L OR.B R4L,R4H ; MOV.B @_SWITCH_PREV_STATE_4, R4L ; OR.B R4L,R4H ; MOV.B @_SWITCH_PREV_STATE_5, R4L ; OR.B R4L,R4H ; MOV.B @_SWITCH_PREV_STATE_6, R4L ; OR.B R4L,R4H ; MOV.B @_SWITCH_CUR_STATE, R4L ; AND.B R4L,R4H MOV.B R4H,@_AGGREGATE_PREV_FRAMES_OR ; XOR current state with the OR of the previous frames ; to see what was off for the previous x frames but is now on ; (This also will return any inputs that were on at all for the previous x frames but are now off, so we do one more thing) MOV.B @_AGGREGATE_PREV_FRAMES_OR, R4H MOV.B @_SWITCH_CUR_STATE, R4L XOR.B R4H, R4L MOV.B R4L,@_SWITCH_CHANGED_THIS_FRAME_OR ; AND that result with what we know to be on this frame ; To get what turned on this frame MOV.B @_SWITCH_CHANGED_THIS_FRAME_OR, R4H MOV.B @_SWITCH_CUR_STATE, R4L AND.B R4H,R4L MOV.B R4L,@_SWITCHED_ON_THIS_FRAME ;Aggregate previous frames AND MOV.B @_SWITCH_PREV_STATE, R4H MOV.B @_SWITCH_PREV_STATE_2, R4L AND.B R4L,R4H MOV.B @_SWITCH_PREV_STATE_3, R4L AND.B R4L,R4H ; MOV.B @_SWITCH_PREV_STATE_4, R4L ; AND.B R4L,R4H ; MOV.B @_SWITCH_PREV_STATE_5, R4L ; AND.B R4L,R4H ; MOV.B @_SWITCH_PREV_STATE_6, R4L ; AND.B R4L,R4H ; MOV.B @_SWITCH_CUR_STATE, R4L ; AND.B R4L,R4H MOV.B R4H,@_AGGREGATE_PREV_FRAMES_AND ; XOR current state with prev frame AND ; to see what was on for the previous x frames but now off ; (This also will return what was off at all for the previous x frames, but is now on, so we do one more step) MOV.B @_AGGREGATE_PREV_FRAMES_AND, R4H MOV.B @_SWITCH_CUR_STATE, R4L XOR.B R4H, R4L MOV.B R4L,@_SWITCH_CHANGED_THIS_FRAME_AND ; AND that result with what we know to be OFF this frame ; To get what turned off this frame MOV.B @_SWITCH_CHANGED_THIS_FRAME_AND, R4H MOV.B @_SWITCH_CUR_STATE, R4L NOT.B R4L ; What's not on this frame AND.B R4H,R4L ; I actually never ended up using this result, just finding what turned ON this frame was enough for these games MOV.B R4L,@_SWITCHED_OFF_THIS_FRAME RTS ; Set the actual hardware registers to enable the user LEDs ; I am only setting the actual hardware buffer once a frame ; Other functions write to these buffers, which are later copied to the hardware _SET_LEDS: ; User LEDs on PDR3 MOV.B @_PDR3_BUFFER, R4L MOV.B R4L,@PDR3 ; User LEDs on PDR6 MOV.B @_PDR6_BUFFER, R4L MOV.B R4L,@PDR6 RTS ; ======================= Start display subroutines =========================== ; The following functions just set the buffers to turn on or off various LEDs _SET_DIGITAL_DISPLAY_TOP: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_TOP,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L MOV.B R4L,@_PDR3_BUFFER RTS _SET_DIGITAL_DISPLAY_TOP_RIGHT: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_TOP_RIGHT,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 MOV.B R4L,@_PDR3_BUFFER RTS _SET_DIGITAL_DISPLAY_BOTTOM_R: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_BOTTOM_RIGHT,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 MOV.B R4L,@_PDR3_BUFFER RTS _SET_DIGITAL_DISPLAY_TOP_LEFT: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_TOP_LEFT,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 MOV.B R4L,@_PDR3_BUFFER RTS _SET_DIGITAL_DISPLAY_BOTTOM: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DIGITAL_DISPLAY_BOTTOM,R4L ;Then turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _SET_DIGITAL_DISPLAY_BOTTOM_L: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DIGITAL_DISPLAY_BOTTOM_LEFT,R4L ;Then turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _DISPLAY_NUMBER_1: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_NUM_1,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 MOV.B R4L,@_PDR3_BUFFER RTS _DISPLAY_NUMBER_2: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_NUM_2_PDR6,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DIGITAL_DISPLAY_NUM_2_PDR3,R4L ;Then turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _DISPLAY_NUMBER_3: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_NUM_3_PDR6,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DIGITAL_DISPLAY_NUM_3_PDR3,R4L ;Then turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _DISPLAY_NUMBER_4: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_NUM_4_PDR6,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DIGITAL_DISPLAY_NUM_4_PDR3,R4L ;Then turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _DISPLAY_NUMBER_5: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_NUM_5_PDR6,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DIGITAL_DISPLAY_NUM_5_PDR3,R4L ;Then turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _DISPLAY_NUMBER_6: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_NUM_6_PDR6,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DIGITAL_DISPLAY_NUM_6_PDR3,R4L ;Then turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _DISPLAY_NUMBER_7: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_NUM_7_PDR6,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 MOV.B R4L,@_PDR3_BUFFER RTS _DISPLAY_NUMBER_8: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_NUM_8_PDR6,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DIGITAL_DISPLAY_NUM_8_PDR3,R4L ;Then turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _DISPLAY_NUMBER_9: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_NUM_9_PDR6,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DIGITAL_DISPLAY_NUM_9_PDR3,R4L ;Then turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _DISPLAY_NUMBER_0: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_CIRCLE_PDR6,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DIGITAL_DISPLAY_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DIGITAL_DISPLAY_CIRCLE_PDR3,R4L ;Then turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _SET_PAC_DOT_4_ON: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DOT_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DOT_4,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DOT_NOT_ALL_PDR3,R4L ;Turn off all PDR3 MOV.B R4L,@_PDR3_BUFFER RTS _SET_PAC_DOT_3_ON: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DOT_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DOT_3,R4L ;Then turn on what we need MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DOT_NOT_ALL_PDR3,R4L ;Turn off all PDR3 MOV.B R4L,@_PDR3_BUFFER RTS _SET_PAC_DOT_2_ON: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DOT_NOT_ALL_PDR6,R4L ;Turn off all PDR6 MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DOT_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DOT_2,R4L ;Then turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _SET_PAC_DOT_1_ON: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DOT_NOT_ALL_PDR6,R4L ;Turn off all PDR6 MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DOT_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DOT_1,R4L ;Then turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _SET_PAC_DOTS_OFF: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DOT_NOT_ALL_PDR6,R4L ;Turn off all PDR6 MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DOT_NOT_ALL_PDR3,R4L ;Turn off all PDR3 MOV.B R4L,@_PDR3_BUFFER RTS _SET_PAC_OFF: MOV.B @_PDR3_BUFFER,R4L AND.B #PAC_NOT_ALL,R4L ;Turn off all PDR3 MOV.B R4L,@_PDR3_BUFFER RTS _SET_PAC_BOTTOM_ON: MOV.B @_PDR3_BUFFER,R4L AND.B #PAC_NOT_ALL,R4L ;Turn off all PDR3 OR.B #PAC_BOTTOM,R4L ;Turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _SET_PAC_BOTTOM_N_MOUTH_ON: MOV.B @_PDR3_BUFFER,R4L AND.B #PAC_NOT_ALL,R4L ;Turn off all PDR3 OR.B #PAC_BOTTOM,R4L ;Turn on what we need OR.B #PAC_MOUTH,R4L ;Turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS _SET_PAC_ALL_ON: MOV.B @_PDR3_BUFFER,R4L AND.B #PAC_NOT_ALL,R4L ;Turn off all PDR3 OR.B #PAC_ALL,R4L ;Turn on what we need MOV.B R4L,@_PDR3_BUFFER RTS ; ======================= End display subroutines =========================== ; This turns on the appropriate pac-dots for attract mode based on the dot-attract-state variable _ATTRACT_MODE_SET_DOTS: MOV.B @_DOT_ATTRACT_STATE,R4H CMP.B #1,R4H BNE _ATTRACT_MODE_SET_DOTS2 JSR @_SET_PAC_DOT_4_ON RTS _ATTRACT_MODE_SET_DOTS2: CMP.B #2,R4H BNE _ATTRACT_MODE_SET_DOTS3 JSR @_SET_PAC_DOT_3_ON RTS _ATTRACT_MODE_SET_DOTS3: CMP.B #3,R4H BNE _ATTRACT_MODE_SET_DOTS4 JSR @_SET_PAC_DOT_2_ON RTS _ATTRACT_MODE_SET_DOTS4: JSR @_SET_PAC_DOT_1_ON RTS ; This turns on the appropriate pac-dots for attract mode based on the dot-attract-state variable _GAME_MODE_SET_DOTS: MOV.B @_DOT_GAME_STATE,R4H CMP.B #0,R4H BNE _GAME_MODE_SET_DOTS1 JSR @_SET_PAC_DOTS_OFF RTS _GAME_MODE_SET_DOTS1: CMP.B #1,R4H BNE _GAME_MODE_SET_DOTS2 JSR @_SET_PAC_DOT_4_ON RTS _GAME_MODE_SET_DOTS2: CMP.B #2,R4H BNE _GAME_MODE_SET_DOTS3 JSR @_SET_PAC_DOT_3_ON RTS _GAME_MODE_SET_DOTS3: CMP.B #3,R4H BNE _GAME_MODE_SET_DOTS4 JSR @_SET_PAC_DOT_2_ON RTS _GAME_MODE_SET_DOTS4: JSR @_SET_PAC_DOT_1_ON RTS ; Set PAC-Man's LED state in attract mode _ATTRACT_MODE_SET_PAC: MOV.B @_PAC_ATTRACT_STATE,R4H ; Turn all off CMP.B #1,R4H BNE _ATTRACT_MODE_SET_PAC2 JSR @_SET_PAC_OFF RTS ; Turn bottom on _ATTRACT_MODE_SET_PAC2: CMP.B #2,R4H BNE _ATTRACT_MODE_SET_PAC3 JSR @_SET_PAC_BOTTOM_ON RTS ; Turn bottom and mouth on _ATTRACT_MODE_SET_PAC3: CMP.B #3,R4H BNE _ATTRACT_MODE_SET_PAC4 JSR @_SET_PAC_BOTTOM_N_MOUTH_ON RTS ; Turn all PAC-Man on _ATTRACT_MODE_SET_PAC4: JSR @_SET_PAC_ALL_ON RTS ; Set PAC-Man's LED state in attract mode _GAME_MODE_SET_PAC: MOV.B @_PAC_GAME_STATE,R4H ;MOV.B #2,R4H ; Turn all off CMP.B #1,R4H BNE _GAME_MODE_SET_PAC2 JSR @_SET_PAC_OFF RTS ; Turn bottom on _GAME_MODE_SET_PAC2: CMP.B #2,R4H BNE _GAME_MODE_SET_PAC3 JSR @_SET_PAC_BOTTOM_ON RTS ; Turn bottom and mouth on _GAME_MODE_SET_PAC3: CMP.B #3,R4H BNE _GAME_MODE_SET_PAC4 JSR @_SET_PAC_BOTTOM_N_MOUTH_ON RTS ; Turn all PAC-Man on _GAME_MODE_SET_PAC4: JSR @_SET_PAC_ALL_ON RTS ; Turn all the user LEDs on _DISPLAY_ALL_USER_LEDS: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #DOT_NOT_ALL_PDR6,R4L ;Turn off all PDR6 OR.B #DIGITAL_DISPLAY_CIRCLE_PDR6,R4L ;Then turn on what we need OR.B #DOT_ALL_PDR6,R4L MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #DOT_NOT_ALL_PDR3,R4L ;Turn off all PDR3 OR.B #DIGITAL_DISPLAY_CIRCLE_PDR3,R4L ;Then turn on what we need OR.B #DOT_ALL_PDR3,R4L OR.B #PAC_ALL,R4L MOV.B R4L,@_PDR3_BUFFER RTS ; Turn all the user LEDs off _DISABLE_ALL_USER_LEDS: ;PDR6 MOV.B @_PDR6_BUFFER,R4L AND.B #0,R4L ;Turn off all PDR6 MOV.B R4L,@_PDR6_BUFFER ;PDR3 MOV.B @_PDR3_BUFFER,R4L AND.B #0,R4L ;Turn off all PDR3 MOV.B R4L,@_PDR3_BUFFER RTS ;------------------------------------------------------------------------------ ;変数定義 Variable definitions (well, reservations) ;------------------------------------------------------------------------------ .ORG H'E800 ;RAMのどこかのアドレス(RAMに配置) ; Where to put them in RAM _COUNT: .RES.L 1 ; Variables for storing the current RAM state before the interrupt happens, so nothing gets overwritten _TEMP_INTERRUPT_ER0: .RES.L 1 _TEMP_INTERRUPT_ER1: .RES.L 1 _TEMP_INTERRUPT_ER2: .RES.L 1 _TEMP_INTERRUPT_ER3: .RES.L 1 _TEMP_INTERRUPT_ER4: .RES.L 1 _TEMP_INTERRUPT_ER5: .RES.L 1 _TEMP_INTERRUPT_ER6: .RES.L 1 ; Game Mode _MODE: .RES.B 1 ; Game difficulty (lower numbers are harder) _DIFFICULTY .RES.B 1 ; How many times Timer B1 has overflowed (this too overflows) _TIMER_OVERFLOW_COUNT: .RES.B 1 ; State of the dots in attract mode (reused for game modes also) _DOT_ATTRACT_STATE: .RES.B 1 ; State of Pac-man in attract mode _PAC_ATTRACT_STATE: .RES.B 1 ; State of Pac-man in game mode _PAC_GAME_STATE: .RES.B 1 ; State of flashing in attract mode _ATTRACT_FLASH_STATE: .RES.B 1 ; Flash count-down timer for game mode _GAME_FLASH_STATE: .RES.B 1 ; Game code writes to these LED data buffers, which are then copied to the hardware registers once per frame _PDR3_BUFFER: .RES.B 1 _PDR6_BUFFER: .RES.B 1 ; Current state of the switches _SWITCH_CUR_STATE: .RES.B 1 ; Previous frame switch states _SWITCH_PREV_STATE: .RES.B 1 _SWITCH_PREV_STATE_2: .RES.B 1 _SWITCH_PREV_STATE_3: .RES.B 1 _SWITCH_PREV_STATE_4: .RES.B 1 _SWITCH_PREV_STATE_5: .RES.B 1 _SWITCH_PREV_STATE_6: .RES.B 1 _SWITCH_PREV_STATE_7: .RES.B 1 ; Previous frames aggregate OR and ANDs of the switch state _AGGREGATE_PREV_FRAMES_OR: .RES.B 1 _AGGREGATE_PREV_FRAMES_AND: .RES.B 1 ; Which variables changed this frame _SWITCH_CHANGED_THIS_FRAME_OR: .RES.B 1 _SWITCH_CHANGED_THIS_FRAME_AND: .RES.B 1 ; Which variables turned on or off this frame _SWITCHED_ON_THIS_FRAME: .RES.B 1 _SWITCHED_OFF_THIS_FRAME: .RES.B 1 ; Used to slow the timer based on difficulty _DIFFICULTY_SKIP_COUNT: .RES.B 1 ; State of the dots in game mode _DOT_GAME_STATE: .RES.B 1 .END