LED Pac-man in Assembly

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:

Circuit Diagram!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