; guess-the-number.asm ; OUT ports #define PORT_CONTROL $E ; 1110 - bit 0 is low #define PORT_LCD $D ; 1101 - bit 1 is low #define PORT_NOWHERE $F ; 1111 - nothing is low ; IN ports #define PORT_BUTTONS $E ; 1110 - bit 0 is low ; Bit flags in LCDCONTROL port #define LCD_REG_COMMAND $0 #define LCD_REG_DATA $2 ; 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 ; 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_NIBBLE $00B #define LCD_CONTROL_STATE $00E #define RETURN_ADDRESS $00F #define PREV_BUTTON_STATE $00D #define NEW_BUTTON_STATE $00C #define LCD_BUFFER $010 ; messages #define WELCOME 0 #define TOO_HIGH 1 #define TOO_LOW 2 #define CORRECT 3 ; buttons #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 init: ; 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 ; the command is INTERFACE8 lit #LCD_COMMAND_INTERFACE4 st LCD_BUFFER+4 lit #LCD_COMMAND_DISPLAY st LCD_BUFFER+2 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 st LCD_BUFFER_INDEX jmp lcd_write_buffer next5: ; 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 st LCD_BUFFER_INDEX 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 st LCD_BUFFER_INDEX 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 je - print_line_1: ; prepare to send an LCD command lit #LCD_REG_COMMAND st LCD_CONTROL_STATE 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 st LCD_BUFFER_INDEX jmp lcd_write_buffer next8: ; 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 je print_welcome1 cmpi #TOO_HIGH je print_too_high1 cmpi #TOO_LOW je 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 st LCD_BUFFER_INDEX jmp lcd_write_buffer next9: ; determine what message to print ld LINE_1_MESSAGE cmpi #WELCOME je print_welcome2 cmpi #TOO_HIGH je print_too_high2 cmpi #TOO_LOW je 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 st LCD_BUFFER_INDEX jmp lcd_write_buffer next10: ; prepare to send an LCD command lit #LCD_REG_COMMAND st LCD_CONTROL_STATE 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 st LCD_BUFFER_INDEX 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 st LCD_BUFFER_INDEX 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 st LCD_BUFFER_INDEX 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+0 ; remember where to return lit #6 st RETURN_ADDRESS ; data begins at buffer index 1 lit #1 st LCD_BUFFER_INDEX 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 lit #0 ; check carry-out from low nibble jnc + addi #1 + addi #<'0' st LCD_BUFFER+1 ; remember where to return lit #14 st RETURN_ADDRESS ; data begins at buffer index 3 lit #3 st LCD_BUFFER_INDEX jmp lcd_write_buffer next14: ; wait for all buttons to be up - in #PORT_BUTTONS cmpi #$F jne - ; wait for button up, down, or right (enter) wait_for_button: - in #PORT_BUTTONS cmpi #$F je - st NEW_BUTTON_STATE check_right_pressed: ; is the right button currently pressed? lit #BUTTON_NOT_RIGHT norm NEW_BUTTON_STATE cmpi #BUTTON_RIGHT jne check_down_pressed ; TODO - debounce ld GUESSED_NUMBER cmpm SECRET_NUMBER jl guess_too_low je guess_correct ; too high lit #TOO_HIGH st LINE_1_MESSAGE jmp print_line_1 guess_too_low: lit #TOO_LOW st LINE_1_MESSAGE jmp print_line_1 guess_correct: lit #CORRECT st LINE_1_MESSAGE jmp print_line_1 check_down_pressed: ; is the down button currently pressed? lit #BUTTON_NOT_DOWN norm NEW_BUTTON_STATE cmpi #BUTTON_DOWN jne check_up_pressed ; TODO - debounce 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 cmpi #BUTTON_UP jne wait_for_button ; TODO - debounce ld GUESSED_NUMBER addi #1 st GUESSED_NUMBER jmp print_guess halt: jmp halt ; ===== LCD ===== ; use LCD_BUFFER_INDEX to get the next nibble from LCD_BUFFER, and put it in LCD_NIBBLE lcd_write_buffer: ld LCD_BUFFER_INDEX cmpi #0 jne + ld LCD_BUFFER st LCD_NIBBLE jmp lcd_write_nibble + cmpi #1 jne + ld LCD_BUFFER+1 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #2 jne + ld LCD_BUFFER+2 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #3 jne + ld LCD_BUFFER+3 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #4 jne + ld LCD_BUFFER+4 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #5 jne + ld LCD_BUFFER+5 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #6 jne + ld LCD_BUFFER+6 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #7 jne + ld LCD_BUFFER+7 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #8 jne + ld LCD_BUFFER+8 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #9 jne + ld LCD_BUFFER+9 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #10 jne + ld LCD_BUFFER+10 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #11 jne + ld LCD_BUFFER+11 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #12 jne + ld LCD_BUFFER+12 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #13 jne + ld LCD_BUFFER+13 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #14 jne + ld LCD_BUFFER+14 st LCD_NIBBLE jmp lcd_write_nibble + cmpi #15 jne + ld LCD_BUFFER+15 st LCD_NIBBLE jmp lcd_write_nibble lcd_write_nibble: ld LCD_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 37 us for the LCD lit #6 ; each loop iteration is 12 clocks = 6 us at 2 MHz. Initial count of 6 = 7 iterations = 42 us. - out #PORT_NOWHERE out #PORT_NOWHERE out #PORT_NOWHERE out #PORT_NOWHERE addi #-1 jc - ; carry will be clear when result goes negative ; decrement the buffer index ld LCD_BUFFER_INDEX addi #-1 jnc + st LCD_BUFFER_INDEX jmp lcd_write_buffer ; determine the return address + ld RETURN_ADDRESS cmpi #0 je next0 cmpi #1 je next1 cmpi #2 je next2 cmpi #3 je next3 cmpi #4 je next4 cmpi #5 je next5 cmpi #6 je next6 cmpi #7 je next7 cmpi #8 je next8 cmpi #9 je next9 cmpi #10 je next10 cmpi #11 je next11 cmpi #12 je next12 cmpi #13 je next13 cmpi #14 je next14 jmp halt