; guess-the-number.asm

; OUT ports
#define PORT_CONTROL $E ; 1110 - bit 0 is low
#define PORT_LCD $D     ; 1101 - bit 1 is low

; IN ports
#define PORT_BUTTONS $E ; 1110 - bit 0 is low

; Bit flags in LCDCONTROL port
#define LCD_REG_COMMAND $4
#define LCD_REG_DATA $6

; LCD commands
#define LCD_COMMAND_INTERFACE8 $3C  ; 8-bit interface, 2-line display, 5x10 font
#define LCD_COMMAND_INTERFACE4 $2C  ; 4-bit interface, 2-line display, 5x10 font
#define LCD_COMMAND_DISPLAY $0E     ; display on, cursor on, blinking cursor off
#define LCD_COMMAND_CLEAR $01       ; clear display, home cursor
#define LCD_COMMAND_CUSROR_POS_LINE_1 $80 ; OR the desired cursor position with $80 to create a cursor position command. Line 2 begins at pos 64
#define LCD_COMMAND_CURSOR_POS_LINE_2 $C0  
#define LCD_COMMAND_CURSOR_POS_GUESS $CC

; LCD timing constants
#define LCD_CLEAR_HOME_DELAY_US 1520
#define LCD_SINGLE_COMMAND_DELAY_US 37
#define CPU_CLOCKS_PER_US 2.4576

; memory locations
#define LINE_1_MESSAGE $006
#define SECRET_NUMBER $009
#define GUESSED_NUMBER $008
#define PRINTED_NUMBER $007
#define LCD_BUFFER_INDEX $00A
#define LCD_CONTROL_STATE $00E
#define RETURN_ADDRESS $00F
#define PREV_BUTTON_STATE $00D
#define NEW_BUTTON_STATE $00C
#define LCD_DELAY_1 $010
#define LCD_DELAY_2 $011
#define LCD_BUFFER $020
#define AUDIO_LOW_CONTROL_STATE $030
#define AUDIO_HIGH_CONTROL_STATE $031
#define TONE_PERIOD_0 $032
#define TONE_PERIOD_1 $033
#define TONE_PERIOD_2 $034
#define TONE_COUNTER_0 $038
#define TONE_COUNTER_1 $039
#define TONE_COUNTER_2 $03A
#define TONE_DURATION_0 $035
#define TONE_DURATION_1 $036
#define TONE_DURATION_2 $037
#define DEBOUNCE_COUNTER_0 $038
#define DEBOUNCE_COUNTER_1 $039
#define DEBOUNCE_COUNTER_2 $03A
#define DELAY_COUNTER_0 $038
#define DELAY_COUNTER_1 $039
#define DELAY_COUNTER_2 $03A
#define DELAY_COUNTER_3 $03B
#define BLINK_COUNTER $03C

; messages
#define WELCOME 0
#define TOO_HIGH 1
#define TOO_LOW 2
#define CORRECT 3

; buttons
#define DEBOUNCE_TIME_0 $F
#define DEBOUNCE_TIME_1 $F
#define DEBOUNCE_TIME_2 $F
#define BUTTON_LEFT $1
#define BUTTON_NOT_LEFT $E
#define BUTTON_RIGHT $2
#define BUTTON_NOT_RIGHT $D
#define BUTTON_DOWN $4
#define BUTTON_NOT_DOWN $B
#define BUTTON_UP $8
#define BUTTON_NOT_UP $7

;Seeing if output ports work

init:
	; RJA testing a very short loop
	lit #3
loop1:
	addi #1
	st LCD_CONTROL_STATE
	out #PORT_CONTROL
	jnz loop1  

	;should get here when count goes to zero
	lit #$F
loop2:
	;here's how we do subtraction...
	addi #-1
	st RETURN_ADDRESS
	out #PORT_CONTROL
	jnz loop2

	;should get here when count goes to zero again
	lit #$A
	;try storing in RAM
	st $00E
	out #PORT_CONTROL
	lit #$5
	st $00F
	out #PORT_CONTROL

loop3:
	;see if can read back
	ld $00E
	out #PORT_CONTROL
	ld $00F
	out #PORT_CONTROL
	;take input port into A and then put A on port #0
	in #PORT_BUTTONS
	out #PORT_CONTROL
	jmp loop3




    ; To initialize the LCD from an unknown state, we must first set it to 8-bit mode three times. Then it can be set to 4-bit mode.

    ; prepare to send an LCD command
    lit #LCD_REG_COMMAND
    st LCD_CONTROL_STATE
    ; remember where to return 
    lit #1
    st RETURN_ADDRESS
    ; zero more nibbles to read from the buffer
    lit #0
    st LCD_BUFFER_INDEX
    ; the command is INTERFACE8
    lit #<LCD_COMMAND_INTERFACE8
    jmp lcd_write_nibble

next1:
    ; remember where to return
    lit #0
    st RETURN_ADDRESS
    jmp lcd_long_delay

delaynext0:
    ; remember where to return
    lit #2
    st RETURN_ADDRESS
    ; zero more nibbles to read from the buffer
    lit #0
    st LCD_BUFFER_INDEX
    ; the command is INTERFACE8
    lit #<LCD_COMMAND_INTERFACE8
    jmp lcd_write_nibble

next2:
    ; remember where to return
    lit #1
    st RETURN_ADDRESS
    jmp lcd_long_delay

delaynext1:
    ; remember where to return
    lit #3
    st RETURN_ADDRESS
    ; zero more nibbles to read from the buffer
    lit #0
    st LCD_BUFFER_INDEX
    ; the command is INTERFACE8
    lit #<LCD_COMMAND_INTERFACE8
    jmp lcd_write_nibble

next3:
    ; remember where to return
    lit #4
    st RETURN_ADDRESS
    ; zero more nibbles to read from the buffer
    lit #0
    st LCD_BUFFER_INDEX
    ; the command is INTERFACE4
    lit #<LCD_COMMAND_INTERFACE4
    jmp lcd_write_nibble

    ; The LCD is now in 4-bit mode, and we can send byte-wide commands as a pair of nibble, high nibble first.

start:
next4:
    ; prepare to send an LCD command
    lit #LCD_REG_COMMAND
    st LCD_CONTROL_STATE

    lit #<LCD_COMMAND_INTERFACE4
    st LCD_BUFFER+5
    lit #>LCD_COMMAND_INTERFACE4
    st LCD_BUFFER+4
    lit #<LCD_COMMAND_DISPLAY
    st LCD_BUFFER+3
    lit #>LCD_COMMAND_DISPLAY
    st LCD_BUFFER+2
    lit #<LCD_COMMAND_CLEAR
    st LCD_BUFFER+1
    lit #>LCD_COMMAND_CLEAR
    st LCD_BUFFER+0

    ; remember where to return
    lit #5
    st RETURN_ADDRESS
    ; data begins at buffer index 5
    lit #5
    jmp lcd_write_buffer

next5:
    ; remember where to return
    lit #2
    st RETURN_ADDRESS
    jmp lcd_long_delay

delaynext2:
    ; initialize the line 1 message
    lit #WELCOME
    st LINE_1_MESSAGE

    ; prepare to send LCD character data
    lit #LCD_REG_DATA
    st LCD_CONTROL_STATE

    lit #<'P'
    st LCD_BUFFER+15
    lit #>'P'
    st LCD_BUFFER+14
    lit #<'r'
    st LCD_BUFFER+13
    lit #>'r'
    st LCD_BUFFER+12
    lit #<'e'
    st LCD_BUFFER+11
    lit #>'e'
    st LCD_BUFFER+10
    lit #<'s'
    st LCD_BUFFER+9
    lit #>'s'
    st LCD_BUFFER+8
    lit #<'s'
    st LCD_BUFFER+7
    lit #>'s'
    st LCD_BUFFER+6
    lit #<' '
    st LCD_BUFFER+5
    lit #>' '
    st LCD_BUFFER+4
    lit #<'a'
    st LCD_BUFFER+3
    lit #>'a'
    st LCD_BUFFER+2
    lit #<'n'
    st LCD_BUFFER+1
    lit #>'n'
    st LCD_BUFFER+0

    ; remember where to return
    lit #0
    st RETURN_ADDRESS
    ; data begins at buffer index 15
    lit #15
    jmp lcd_write_buffer

next0:
    lit #<'y'
    st LCD_BUFFER+15
    lit #>'y'
    st LCD_BUFFER+14
    lit #<' '
    st LCD_BUFFER+13
    lit #>' '
    st LCD_BUFFER+12
    lit #<'b'
    st LCD_BUFFER+11
    lit #>'b'
    st LCD_BUFFER+10
    lit #<'u'
    st LCD_BUFFER+9
    lit #>'u'
    st LCD_BUFFER+8
    lit #<'t'
    st LCD_BUFFER+7
    lit #>'t'
    st LCD_BUFFER+6
    lit #<'t'
    st LCD_BUFFER+5
    lit #>'t'
    st LCD_BUFFER+4
    lit #<'o'
    st LCD_BUFFER+3
    lit #>'o'
    st LCD_BUFFER+2
    lit #<'n'
    st LCD_BUFFER+1
    lit #>'n'
    st LCD_BUFFER+0

    ; remember where to return
    lit #7
    st RETURN_ADDRESS
    ; data begins at buffer index 15
    lit #15
    jmp lcd_write_buffer

next7:
    ; initialize the secret number and guess number
    lit #0
    st SECRET_NUMBER
    st GUESSED_NUMBER

    ; continuously increment the secret number while waiting for a button to be in the pressed state
-   ld SECRET_NUMBER
    addi #1
    st SECRET_NUMBER
    in #PORT_BUTTONS
    cmpi #$F
    jz -

print_line_1:
    ; prepare to send an LCD command
    lit #LCD_REG_COMMAND
    st LCD_CONTROL_STATE
    lit #<LCD_COMMAND_CLEAR
    st LCD_BUFFER+1
    lit #>LCD_COMMAND_CLEAR
    st LCD_BUFFER+0
    ; remember where to return
    lit #8
    st RETURN_ADDRESS
    ; data begins at buffer index 1
    lit #1
    jmp lcd_write_buffer

next8:
    ; remember where to return
    lit #3
    st RETURN_ADDRESS
    jmp lcd_long_delay

delaynext3:
    ; prepare to send LCD character data
    lit #LCD_REG_DATA
    st LCD_CONTROL_STATE

    ; determine what message to print
    ld LINE_1_MESSAGE
    cmpi #WELCOME
    jz print_welcome1
    cmpi #TOO_HIGH
    jz print_too_high1
    cmpi #TOO_LOW
    jz print_too_low1
    jmp print_correct1

print_welcome1:
    lit #<'I'
    st LCD_BUFFER+15
    lit #>'I'
    st LCD_BUFFER+14
    lit #<' '
    st LCD_BUFFER+13
    lit #>' '
    st LCD_BUFFER+12
    lit #<'k'
    st LCD_BUFFER+11
    lit #>'k'
    st LCD_BUFFER+10
    lit #<'n'
    st LCD_BUFFER+9
    lit #>'n'
    st LCD_BUFFER+8
    lit #<'o'
    st LCD_BUFFER+7
    lit #>'o'
    st LCD_BUFFER+6
    lit #<'w'
    st LCD_BUFFER+5
    lit #>'w'
    st LCD_BUFFER+4
    lit #<' '
    st LCD_BUFFER+3
    lit #>' '
    st LCD_BUFFER+2
    lit #<'a'
    st LCD_BUFFER+1
    lit #>'a'
    st LCD_BUFFER+0
    jmp +

print_too_high1:
    lit #<'T'
    st LCD_BUFFER+15
    lit #>'T'
    st LCD_BUFFER+14
    lit #<'o'
    st LCD_BUFFER+13
    lit #>'o'
    st LCD_BUFFER+12
    lit #<'o'
    st LCD_BUFFER+11
    lit #>'o'
    st LCD_BUFFER+10
    lit #<' '
    st LCD_BUFFER+9
    lit #>' '
    st LCD_BUFFER+8
    lit #<'h'
    st LCD_BUFFER+7
    lit #>'h'
    st LCD_BUFFER+6
    lit #<'i'
    st LCD_BUFFER+5
    lit #>'i'
    st LCD_BUFFER+4
    lit #<'g'
    st LCD_BUFFER+3
    lit #>'g'
    st LCD_BUFFER+2
    lit #<'h'
    st LCD_BUFFER+1
    lit #>'h'
    st LCD_BUFFER+0
    jmp +

print_too_low1:
    lit #<'T'
    st LCD_BUFFER+15
    lit #>'T'
    st LCD_BUFFER+14
    lit #<'o'
    st LCD_BUFFER+13
    lit #>'o'
    st LCD_BUFFER+12
    lit #<'o'
    st LCD_BUFFER+11
    lit #>'o'
    st LCD_BUFFER+10
    lit #<' '
    st LCD_BUFFER+9
    lit #>' '
    st LCD_BUFFER+8
    lit #<'l'
    st LCD_BUFFER+7
    lit #>'l'
    st LCD_BUFFER+6
    lit #<'o'
    st LCD_BUFFER+5
    lit #>'o'
    st LCD_BUFFER+4
    lit #<'w'
    st LCD_BUFFER+3
    lit #>'w'
    st LCD_BUFFER+2
    lit #<'!'
    st LCD_BUFFER+1
    lit #>'!'
    st LCD_BUFFER+0
    jmp +

print_correct1:
    lit #<'Y'
    st LCD_BUFFER+15
    lit #>'Y'
    st LCD_BUFFER+14
    lit #<'o'
    st LCD_BUFFER+13
    lit #>'o'
    st LCD_BUFFER+12
    lit #<'u'
    st LCD_BUFFER+11
    lit #>'u'
    st LCD_BUFFER+10
    lit #<' '
    st LCD_BUFFER+9
    lit #>' '
    st LCD_BUFFER+8
    lit #<'g'
    st LCD_BUFFER+7
    lit #>'g'
    st LCD_BUFFER+6
    lit #<'o'
    st LCD_BUFFER+5
    lit #>'o'
    st LCD_BUFFER+4
    lit #<'t'
    st LCD_BUFFER+3
    lit #>'t'
    st LCD_BUFFER+2
    lit #<' '
    st LCD_BUFFER+1
    lit #>' '
    st LCD_BUFFER+0
    jmp +

    ; remember where to return
+   lit #9
    st RETURN_ADDRESS
    ; data begins at buffer index 15
    lit #15
    jmp lcd_write_buffer

next9:

    ; determine what message to print
    ld LINE_1_MESSAGE
    cmpi #WELCOME
    jz print_welcome2
    cmpi #TOO_HIGH
    jz print_too_high2
    cmpi #TOO_LOW
    jz print_too_low2
    jmp print_correct2

print_welcome2:
    lit #<' '
    st LCD_BUFFER+15
    lit #>' '
    st LCD_BUFFER+14
    lit #<'n'
    st LCD_BUFFER+13
    lit #>'n'
    st LCD_BUFFER+12
    lit #<'u'
    st LCD_BUFFER+11
    lit #>'u'
    st LCD_BUFFER+10
    lit #<'m'
    st LCD_BUFFER+9
    lit #>'m'
    st LCD_BUFFER+8
    lit #<'b'
    st LCD_BUFFER+7
    lit #>'b'
    st LCD_BUFFER+6
    lit #<'e'
    st LCD_BUFFER+5
    lit #>'e'
    st LCD_BUFFER+4
    lit #<'r'
    st LCD_BUFFER+3
    lit #>'r'
    st LCD_BUFFER+2
    lit #<'.'
    st LCD_BUFFER+1
    lit #>'.'
    st LCD_BUFFER+0
    jmp +

print_too_high2:
    lit #<'!'
    st LCD_BUFFER+15
    lit #>'!'
    st LCD_BUFFER+14
    lit #<' '
    st LCD_BUFFER+13
    lit #>' '
    st LCD_BUFFER+12
    lit #<' '
    st LCD_BUFFER+11
    lit #>' '
    st LCD_BUFFER+10
    lit #<' '
    st LCD_BUFFER+9
    lit #>' '
    st LCD_BUFFER+8
    lit #<' '
    st LCD_BUFFER+7
    lit #>' '
    st LCD_BUFFER+6
    lit #<' '
    st LCD_BUFFER+5
    lit #>' '
    st LCD_BUFFER+4
    lit #<' '
    st LCD_BUFFER+3
    lit #>' '
    st LCD_BUFFER+2
    lit #<' '
    st LCD_BUFFER+1
    lit #>' '
    st LCD_BUFFER+0
    jmp +

print_too_low2:
    lit #<' '
    st LCD_BUFFER+15
    lit #>' '
    st LCD_BUFFER+14
    lit #<' '
    st LCD_BUFFER+13
    lit #>' '
    st LCD_BUFFER+12
    lit #<' '
    st LCD_BUFFER+11
    lit #>' '
    st LCD_BUFFER+10
    lit #<' '
    st LCD_BUFFER+9
    lit #>' '
    st LCD_BUFFER+8
    lit #<' '
    st LCD_BUFFER+7
    lit #>' '
    st LCD_BUFFER+6
    lit #<' '
    st LCD_BUFFER+5
    lit #>' '
    st LCD_BUFFER+4
    lit #<' '
    st LCD_BUFFER+3
    lit #>' '
    st LCD_BUFFER+2
    lit #<' '
    st LCD_BUFFER+1
    lit #>' '
    st LCD_BUFFER+0
    jmp +

print_correct2:
    lit #<'i'
    st LCD_BUFFER+15
    lit #>'i'
    st LCD_BUFFER+14
    lit #<'t'
    st LCD_BUFFER+13
    lit #>'t'
    st LCD_BUFFER+12
    lit #<'!'
    st LCD_BUFFER+11
    lit #>'!'
    st LCD_BUFFER+10
    lit #<' '
    st LCD_BUFFER+9
    lit #>' '
    st LCD_BUFFER+8
    lit #<' '
    st LCD_BUFFER+7
    lit #>' '
    st LCD_BUFFER+6
    lit #<' '
    st LCD_BUFFER+5
    lit #>' '
    st LCD_BUFFER+4
    lit #<' '
    st LCD_BUFFER+3
    lit #>' '
    st LCD_BUFFER+2
    lit #<' '
    st LCD_BUFFER+1
    lit #>' '
    st LCD_BUFFER+0
    jmp +

    ; remember where to return
+   lit #10
    st RETURN_ADDRESS
    ; data begins at buffer index 15
    lit #15
    jmp lcd_write_buffer

next10:
    ld LINE_1_MESSAGE
    cmpi #CORRECT
    jnz +

    ; success! 
    ; play "charge": measure duration = 48 units, 120 bpm = 2 quarter notes per second = 48 units per 2 seconds: so 1 unit = 1/24 second ~= 40000 us
    ; G 4, 392.00 Hz, 2551 us period = 128 = $80, duration 160K us = 63 periods = $3F
    ; C 4, 523.25 Hz, 1911 us period = 95 = $5F, duration 160K us = 84 periods = $54
    ; E 4, 659.26 Hz, 1517 us period = 75 = $4B, duration 160K us = 105 periods = $69
    ; G 9, 783.99 Hz, 1276 us period = 63 = $3F, duration 360K us = 282 periods = $11A
    ; E 3, 659.26 Hz, 1517 us period = 75 = $4B, duration 120K us = 79 periods = $4F
    ; G 24, 783.99 Hz, 1276 us period = 63 = $3F, duration 960K us = 752 periods = $2F0
    ; -- too low, cut all these periods in half and double the periods
    ; TONE_PERIOD = (period_microseconds * 2.4576 - 128) / 48

    ; G
    lit #$0
    st TONE_PERIOD_2
    lit #$8
    st TONE_PERIOD_1
    lit #$0
    st TONE_PERIOD_0
    lit #$0
    st TONE_DURATION_2
    lit #$3
    st TONE_DURATION_1
    lit #$F
    st TONE_DURATION_0
    ; define the low and high states with LED on
    lit #$0
    st AUDIO_LOW_CONTROL_STATE
    lit #$8
    st AUDIO_HIGH_CONTROL_STATE

    ; remember where to return
    lit #6
    st RETURN_ADDRESS
    jmp audio_play_tone

audionext6:
    ; C
    lit #$0
    st TONE_PERIOD_2
    lit #$5
    st TONE_PERIOD_1
    lit #$F
    st TONE_PERIOD_0
    lit #$0
    st TONE_DURATION_2
    lit #$5
    st TONE_DURATION_1
    lit #$4
    st TONE_DURATION_0

    ; remember where to return
    lit #7
    st RETURN_ADDRESS
    jmp audio_play_tone

audionext7:
    ; E
    lit #$0
    st TONE_PERIOD_2
    lit #$4
    st TONE_PERIOD_1
    lit #$B
    st TONE_PERIOD_0
    lit #$0
    st TONE_DURATION_2
    lit #$6
    st TONE_DURATION_1
    lit #$9
    st TONE_DURATION_0

    ; remember where to return
    lit #8
    st RETURN_ADDRESS
    jmp audio_play_tone

audionext8:
    ; G
    lit #$0
    st TONE_PERIOD_2
    lit #$3
    st TONE_PERIOD_1
    lit #$F
    st TONE_PERIOD_0
    lit #$1
    st TONE_DURATION_2
    lit #$1
    st TONE_DURATION_1
    lit #$A
    st TONE_DURATION_0

    ; remember where to return
    lit #9
    st RETURN_ADDRESS
    jmp audio_play_tone

audionext9:
    ; E
    lit #$0
    st TONE_PERIOD_2
    lit #$4
    st TONE_PERIOD_1
    lit #$B
    st TONE_PERIOD_0
    lit #$0
    st TONE_DURATION_2
    lit #$4
    st TONE_DURATION_1
    lit #$F
    st TONE_DURATION_0

    ; remember where to return
    lit #10
    st RETURN_ADDRESS
    jmp audio_play_tone

audionext10:
    ; G
    lit #$0
    st TONE_PERIOD_2
    lit #$3
    st TONE_PERIOD_1
    lit #$F
    st TONE_PERIOD_0
    lit #$2
    st TONE_DURATION_2
    lit #$F
    st TONE_DURATION_1
    lit #$0
    st TONE_DURATION_0

    ; remember where to return
    lit #11
    st RETURN_ADDRESS
    jmp audio_play_tone

audionext11:    
    
    ; blink the LED for a few seconds, then restart
    lit #10
    st BLINK_COUNTER

blink_led:
    ; LED off
    lit #$4
    out #PORT_CONTROL

    lit #$F
    st DELAY_COUNTER_0
    st DELAY_COUNTER_1
    st DELAY_COUNTER_2
    lit #6
    st DELAY_COUNTER_3

-   ld DELAY_COUNTER_0
    addi #-1
    st DELAY_COUNTER_0
    jc -
    ld DELAY_COUNTER_1
    addi #-1
    st DELAY_COUNTER_1
    jc -
    ld DELAY_COUNTER_2
    addi #-1
    st DELAY_COUNTER_2
    jc -
    ld DELAY_COUNTER_3
    addi #-1
    st DELAY_COUNTER_3
    jc -

    ; LED on
    lit #0
    out #PORT_CONTROL

lit #$F
    st DELAY_COUNTER_0
    st DELAY_COUNTER_1
    st DELAY_COUNTER_2
    lit #6
    st DELAY_COUNTER_3

-   ld DELAY_COUNTER_0
    addi #-1
    st DELAY_COUNTER_0
    jc -
    ld DELAY_COUNTER_1
    addi #-1
    st DELAY_COUNTER_1
    jc -
    ld DELAY_COUNTER_2
    addi #-1
    st DELAY_COUNTER_2
    jc -
    ld DELAY_COUNTER_3
    addi #-1
    st DELAY_COUNTER_3
    jc -

    ld BLINK_COUNTER
    addi #-1
    st BLINK_COUNTER
    jc blink_led

    jmp start

+   ; prepare to send an LCD command
    lit #LCD_REG_COMMAND
    st LCD_CONTROL_STATE
    lit #<LCD_COMMAND_CURSOR_POS_LINE_2
    st LCD_BUFFER+1
    lit #>LCD_COMMAND_CURSOR_POS_LINE_2
    st LCD_BUFFER+0
    ; remember where to return
    lit #11
    st RETURN_ADDRESS
    ; data begins at buffer index 1
    lit #1
    jmp lcd_write_buffer
        
next11:   
    ; prepare to send LCD character data
    lit #LCD_REG_DATA
    st LCD_CONTROL_STATE

    lit #<'Y'
    st LCD_BUFFER+15
    lit #>'Y'
    st LCD_BUFFER+14
    lit #<'o'
    st LCD_BUFFER+13
    lit #>'o'
    st LCD_BUFFER+12
    lit #<'u'
    st LCD_BUFFER+11
    lit #>'u'
    st LCD_BUFFER+10
    lit #<'r'
    st LCD_BUFFER+9
    lit #>'r'
    st LCD_BUFFER+8
    lit #<' '
    st LCD_BUFFER+7
    lit #>' '
    st LCD_BUFFER+6
    lit #<'g'
    st LCD_BUFFER+5
    lit #>'g'
    st LCD_BUFFER+4
    lit #<'u'
    st LCD_BUFFER+3
    lit #>'u'
    st LCD_BUFFER+2
    lit #<'e'
    st LCD_BUFFER+1
    lit #>'e'
    st LCD_BUFFER+0

    ; remember where to return
    lit #12
    st RETURN_ADDRESS
    ; data begins at buffer index 15
    lit #15
    jmp lcd_write_buffer

next12:   
    ; prepare to send LCD character data
    lit #LCD_REG_DATA
    st LCD_CONTROL_STATE

    lit #<'s'
    st LCD_BUFFER+7
    lit #>'s'
    st LCD_BUFFER+6
    lit #<'s'
    st LCD_BUFFER+5
    lit #>'s'
    st LCD_BUFFER+4
    lit #<'?'
    st LCD_BUFFER+3
    lit #>'?'
    st LCD_BUFFER+2
    lit #<' '
    st LCD_BUFFER+1
    lit #>' '
    st LCD_BUFFER+0

    ; remember where to return
    lit #13
    st RETURN_ADDRESS
    ; data begins at buffer index 7
    lit #7
    jmp lcd_write_buffer

next13:

print_guess:
    ; prepare to send an LCD command
    lit #LCD_REG_COMMAND
    st LCD_CONTROL_STATE
    lit #<LCD_COMMAND_CURSOR_POS_GUESS
    st LCD_BUFFER+1
    lit #>LCD_COMMAND_CURSOR_POS_GUESS
    st LCD_BUFFER+0
   
    ; remember where to return
    lit #6
    st RETURN_ADDRESS
    ; data begins at buffer index 1
    lit #1
    jmp lcd_write_buffer

next6:
    ; prepare to send LCD character data
    lit #LCD_REG_DATA
    st LCD_CONTROL_STATE

    ld GUESSED_NUMBER
    st PRINTED_NUMBER

    ; if the number is greater than 10, subtract 10 and print a 1
    ld PRINTED_NUMBER
    cmpi #10
    jl +
    
    addi #-10
    st PRINTED_NUMBER

    lit #<'1'
    st LCD_BUFFER+3
    lit #>'1'
    st LCD_BUFFER+2
    jmp handleOnesDigit

+   lit #<' '
    st LCD_BUFFER+3
    lit #>' '
    st LCD_BUFFER+2

handleOnesDigit:
    ; add the digit to the byte literal '0' 
    ; low nibble
    ld PRINTED_NUMBER
    addi #>'0'
    st LCD_BUFFER+0
    ; high nibble
    ; check carry-out from low nibble
    jc +
    lit #0
    jmp ++
+   lit #1
++  addi #<'0'
    st LCD_BUFFER+1

    ; remember where to return
    lit #14
    st RETURN_ADDRESS
    ; data begins at buffer index 3
    lit #3
    jmp lcd_write_buffer

next14:
    ; wait for all buttons to be up
-   in #PORT_BUTTONS
    cmpi #$F
    jnz -
    ; wait for button up, down, or right (enter)

wait_for_button:
-   in #PORT_BUTTONS
    cmpi #$F
    jz -

    st NEW_BUTTON_STATE

    ; init debounce
    lit #DEBOUNCE_TIME_0
    st DEBOUNCE_COUNTER_0
    lit #DEBOUNCE_TIME_1
    st DEBOUNCE_COUNTER_1
    lit #DEBOUNCE_TIME_2
    st DEBOUNCE_COUNTER_2

debounce:
    ; each loop is at least 7 instructions, 14 clocks
    in #PORT_BUTTONS
    cmpm NEW_BUTTON_STATE
    jnz wait_for_button
    ld DEBOUNCE_COUNTER_0
    addi #-1
    st DEBOUNCE_COUNTER_0
    jc debounce
    ld DEBOUNCE_COUNTER_1
    addi #-1
    st DEBOUNCE_COUNTER_1
    jc debounce
    ld DEBOUNCE_COUNTER_2
    addi #-1
    st DEBOUNCE_COUNTER_2
    jc debounce

check_right_pressed:
    ; is the right button currently pressed?
    lit #BUTTON_NOT_RIGHT
    norm NEW_BUTTON_STATE
    jz key_click

    ld GUESSED_NUMBER
    cmpm SECRET_NUMBER
    jl guess_too_low
    jz guess_correct

    ; too high:
    ; actual period in microseconds = (48 * (TONE_PERIOD + 1) + 80) / CPU_CLOCKS_PER_US
    ; TONE_PERIOD = (desired_period_us * CPU_CLOCKS_PER_US - 128) / 48
    ; play a 400 Hz tone: TONE_PERIOD = (2500 * 2.4576 - 128) / 48 = 125 (125.33) = $7D hex 
    lit #$0
    st TONE_PERIOD_2
    lit #$7
    st TONE_PERIOD_1
    lit #$D
    st TONE_PERIOD_0
    ; play the tone for 250 ms = 100 periods = $64 hex
    lit #$0
    st TONE_DURATION_2
    lit #$6
    st TONE_DURATION_1
    lit #$4
    st TONE_DURATION_0
    ; define the low and high states with LED on
    lit #$0
    st AUDIO_LOW_CONTROL_STATE
    lit #$8
    st AUDIO_HIGH_CONTROL_STATE

    ; remember where to return
    lit #3
    st RETURN_ADDRESS
    jmp audio_play_tone

audionext3:
    ; create a short delay between tones
    lit #$F
    st DELAY_COUNTER_0
    st DELAY_COUNTER_1
    st DELAY_COUNTER_2

-   ld DELAY_COUNTER_0
    addi #-1
    st DELAY_COUNTER_0
    jc -
    ld DELAY_COUNTER_1
    addi #-1
    st DELAY_COUNTER_1
    jc -
    ld DELAY_COUNTER_2
    addi #-1
    st DELAY_COUNTER_2
    jc -

    ; play a second tone, same as the first
    ; play the tone for 250 ms = 100 periods = $64 hex
    lit #$0
    st TONE_DURATION_2
    lit #$6
    st TONE_DURATION_1
    lit #$4
    st TONE_DURATION_0

    ; remember where to return
    lit #4
    st RETURN_ADDRESS
    jmp audio_play_tone

audionext4:
    lit #TOO_HIGH
    st LINE_1_MESSAGE
    jmp print_line_1

guess_too_low:
    ; actual period in microseconds = (48 * (TONE_PERIOD + 1) + 80) / CPU_CLOCKS_PER_US
    ; TONE_PERIOD = (desired_period_us * CPU_CLOCKS_PER_US - 128) / 48
    ; play a 400 Hz tone: TONE_PERIOD = (2500 * 2.4576 - 128) / 48 = 125 (125.33) = $7D hex 
    lit #$0
    st TONE_PERIOD_2
    lit #$7
    st TONE_PERIOD_1
    lit #$D
    st TONE_PERIOD_0
    ; play the tone for 250 ms = 100 periods = $64 hex
    lit #$0
    st TONE_DURATION_2
    lit #$6
    st TONE_DURATION_1
    lit #$4
    st TONE_DURATION_0
    ; define the low and high states with LED on
    lit #$0
    st AUDIO_LOW_CONTROL_STATE
    lit #$8
    st AUDIO_HIGH_CONTROL_STATE

     ; remember where to return
    lit #0
    st RETURN_ADDRESS
    jmp audio_play_tone

audionext0:
    ; create a short delay between tones
    lit #$F
    st DELAY_COUNTER_0
    st DELAY_COUNTER_1
    st DELAY_COUNTER_2

-   ld DELAY_COUNTER_0
    addi #-1
    st DELAY_COUNTER_0
    jc -
    ld DELAY_COUNTER_1
    addi #-1
    st DELAY_COUNTER_1
    jc -
    ld DELAY_COUNTER_2
    addi #-1
    st DELAY_COUNTER_2
    jc -

    ; play a second tone, same as the first
    ; play the tone for 250 ms = 100 periods = $64 hex
    lit #$0
    st TONE_DURATION_2
    lit #$6
    st TONE_DURATION_1
    lit #$4
    st TONE_DURATION_0

    ; remember where to return
    lit #2
    st RETURN_ADDRESS
    jmp audio_play_tone

audionext2:
    lit #TOO_LOW
    st LINE_1_MESSAGE
    jmp print_line_1

guess_correct:
    lit #CORRECT
    st LINE_1_MESSAGE
    jmp print_line_1

key_click:
    ; actual period in microseconds = (48 * (TONE_PERIOD + 1) + 80) / CPU_CLOCKS_PER_US
    ; TONE_PERIOD = (desired_period_us * CPU_CLOCKS_PER_US - 128) / 48
    ; play a 500 Hz tone: TONE_PERIOD = (2000 * 2.4576 - 128) / 48 = 100 (99.73) = $64 hex 
    lit #$0
    st TONE_PERIOD_2
    lit #$6
    st TONE_PERIOD_1
    lit #$4
    st TONE_PERIOD_0
    ; play the tone for 50 ms = 25 periods = $019 hex
    lit #$0
    st TONE_DURATION_2
    lit #$1
    st TONE_DURATION_1
    lit #$9
    st TONE_DURATION_0
    ; define the low and high states with LED off
    lit #$4
    st AUDIO_LOW_CONTROL_STATE
    lit #$C
    st AUDIO_HIGH_CONTROL_STATE

    ; remember where to return
    lit #1
    st RETURN_ADDRESS
    jmp audio_play_tone

audionext1:

check_down_pressed:
    ; is the down button currently pressed?
    lit #BUTTON_NOT_DOWN
    norm NEW_BUTTON_STATE
    jz check_up_pressed

    ld GUESSED_NUMBER
    addi #-1
    st GUESSED_NUMBER
    jmp print_guess

check_up_pressed:
    ; is the up button currently pressed?
    lit #BUTTON_NOT_UP
    norm NEW_BUTTON_STATE
    jz wait_for_button

    ld GUESSED_NUMBER
    addi #1
    st GUESSED_NUMBER
    jmp print_guess

halt:
    jmp halt

; ==== AUDIO ====
audio_play_tone:
    ; actual period in microseconds = (48 * (TONE_PERIOD + 1) + 80) / CPU_CLOCKS_PER_US
    ; TONE_PERIOD = (desired_period_us * CPU_CLOCKS_PER_US - 128) / 48
    ld AUDIO_LOW_CONTROL_STATE
    out #PORT_CONTROL

    ld TONE_PERIOD_0
    st TONE_COUNTER_0
    ld TONE_PERIOD_1
    st TONE_COUNTER_1
    ld TONE_PERIOD_2
    st TONE_COUNTER_2

    ; TEMP PADDING
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP

    ; each pass through the loop takes 12 instructions
wait_low:
    ld TONE_COUNTER_0
    addi #-1
    st TONE_COUNTER_0
    jc period_delay_low_1
    ld TONE_COUNTER_1
    addi #-1
    st TONE_COUNTER_1
    jc period_delay_low_2
    ld TONE_COUNTER_2
    addi #-1
    st TONE_COUNTER_2
    jc wait_low
    jmp done_low

    ; make the period be constant, regardless of how many period nibbles were checked
period_delay_low_1:
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
period_delay_low_2:
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
    jmp wait_low

done_low:
    ld AUDIO_HIGH_CONTROL_STATE
    out #PORT_CONTROL
      
    ld TONE_PERIOD_0
    st TONE_COUNTER_0
    ld TONE_PERIOD_1
    st TONE_COUNTER_1
    ld TONE_PERIOD_2
    st TONE_COUNTER_2

    ; TEMP PADDING
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP

    ; each pass through the loop takes 12 instructions
wait_high:
    ld TONE_COUNTER_0
    addi #-1
    st TONE_COUNTER_0
    jc period_delay_high_1
    ld TONE_COUNTER_1
    addi #-1
    st TONE_COUNTER_1
    jc period_delay_high_2
    ld TONE_COUNTER_2
    addi #-1
    st TONE_COUNTER_2
    jc wait_high
    jmp done_high

    ; make the period be constant, regardless of how many period nibbles were checked
period_delay_high_1:
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
period_delay_high_2:
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
    jmp wait_high

done_high:         
    ; end of one tone period
    ld TONE_DURATION_0
    addi #-1
    st TONE_DURATION_0
    jc duration_delay_1
    ld TONE_DURATION_1
    addi #-1
    st TONE_DURATION_1
    jc duration_delay_2
    ld TONE_DURATION_2
    addi #-1
    st TONE_DURATION_2
    jc audio_play_tone
          
    ; determine the return address
    ld RETURN_ADDRESS
    jz audionext0
    cmpi #1
    jz audionext1
    cmpi #2
    jz audionext2
    cmpi #3
    jz audionext3
    cmpi #4
    jz audionext4
    ;cmpi #5
    ;jz audionext5
    cmpi #6
    jz audionext6
    cmpi #7
    jz audionext7
    cmpi #8
    jz audionext8
    cmpi #9
    jz audionext9
    cmpi #10
    jz audionext10
    cmpi #11
    jz audionext11

    ; unknown return address
    jmp halt

    ; make the period be constant, regardless of how many duration nibbles were checked
duration_delay_1:
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
duration_delay_2:
    addi #0 ; NOP
    addi #0 ; NOP
    addi #0 ; NOP
    jmp audio_play_tone
                
; ===== LCD ===== 

; use LCD_BUFFER_INDEX to get the next nibble from LCD_BUFFER, and put it in LCD_NIBBLE
lcd_write_buffer:
    ; value for LCD_BUFFER_INDEX is in the accumulator
    st LCD_BUFFER_INDEX
    
    jnz +
    ld LCD_BUFFER
    jmp lcd_write_nibble

+   cmpi #1
    jnz +
    ld LCD_BUFFER+1
    jmp lcd_write_nibble

+   cmpi #2
    jnz +
    ld LCD_BUFFER+2
    jmp lcd_write_nibble

+   cmpi #3
    jnz +
    ld LCD_BUFFER+3
    jmp lcd_write_nibble

+   cmpi #4
    jnz +
    ld LCD_BUFFER+4
    jmp lcd_write_nibble

+   cmpi #5
    jnz +
    ld LCD_BUFFER+5
    jmp lcd_write_nibble

+   cmpi #6
    jnz +
    ld LCD_BUFFER+6
    jmp lcd_write_nibble

+   cmpi #7
    jnz +
    ld LCD_BUFFER+7
    jmp lcd_write_nibble

+   cmpi #8
    jnz +
    ld LCD_BUFFER+8
    jmp lcd_write_nibble

+   cmpi #9
    jnz +
    ld LCD_BUFFER+9
    jmp lcd_write_nibble

+   cmpi #10
    jnz +
    ld LCD_BUFFER+10
    jmp lcd_write_nibble

+   cmpi #11
    jnz +
    ld LCD_BUFFER+11
    jmp lcd_write_nibble

+   cmpi #12
    jnz +
    ld LCD_BUFFER+12
    jmp lcd_write_nibble

+   cmpi #13
    jnz +
    ld LCD_BUFFER+13
    jmp lcd_write_nibble

+   cmpi #14
    jnz +
    ld LCD_BUFFER+14
    jmp lcd_write_nibble

+   ld LCD_BUFFER+15

lcd_write_nibble:
    out #PORT_LCD
    ld LCD_CONTROL_STATE
    out #PORT_CONTROL ; setup RS
    _ori #1 ; set bit 1 (E)
    out #PORT_CONTROL
    _andi #$E ; clear bit 1 (E)
    out #PORT_CONTROL

    ; wait at least LCD_SINGLE_COMMAND_DELAY_US us for the LCD
    ; each loop iteration is 6 clocks
    ; initial count = round_up(LCD_SINGLE_COMMAND_DELAY_US * CPU_CLOCKS_PER_US / 6) - 1
    ; Initial count of 15 = 16 iterations = 96 clocks = 39 us @ 2.4576 MHz.
    lit #15
-   addi #0 ; NOP
    addi #-1
    jc -    ; carry will be clear when result goes negative

    ; decrement the buffer index
    ld LCD_BUFFER_INDEX
    addi #-1
    jc lcd_write_buffer

    ; determine the return address
    ld RETURN_ADDRESS
    jz next0
    cmpi #1
    jz next1
    cmpi #2
    jz next2
    cmpi #3
    jz next3
    cmpi #4
    jz next4
    cmpi #5
    jz next5
    cmpi #6
    jz next6
    cmpi #7
    jz next7
    cmpi #8
    jz next8
    cmpi #9
    jz next9
    cmpi #10
    jz next10
    cmpi #11
    jz next11
    cmpi #12
    jz next12
    cmpi #13
    jz next13
    cmpi #14
    jz next14

    ; unknown return address
    jmp halt

lcd_long_delay:
    ; wait at least LCD_CLEAR_HOME_DELAY_US us for the LCD
    ; each loop iteration is 6 clocks
    ; initial count = round_up(LCD_CLEAR_HOME_DELAY_US * CPU_CLOCKS_PER_US / 6) - 1
    ; Initial count of 656 = $290 hex = 657 iterations = 3942 clocks = 1604 us @ 2.4576 MHz
    lit #2
    st LCD_DELAY_2
    lit #9
    st LCD_DELAY_1
    lit #0
-   addi #0 ; NOP
    addi #-1
    jc -    ; carry will be clear when result goes negative
    ld LCD_DELAY_1
    addi #-1
    st LCD_DELAY_1
    jnc +
    lit #$F
    jmp -
+   ld LCD_DELAY_2
    addi #-1
    st LCD_DELAY_2
    jnc +
    lit #$F
    jmp -
+   ; end of delay loop

    ; determine the return address
    ld RETURN_ADDRESS
    jz delaynext0
    cmpi #1
    jz delaynext1
    cmpi #2
    jz delaynext2
    cmpi #3
    jz delaynext3

    ; unknown return address
    jmp halt
