; name:  Kevin Wu
; final project:  Make LEDs flash in interesting manner when the exact string "go" is input into P1 serial port.

$MOD52 ; include 8052 definitions

; setup constants
Rate9600 EQU 0FDh ; 9600 baud setup
BaudRateCount EQU Rate9600 ; setup timer1 reload value
TimerCount EQU 14h ; setup the counter which counts TF0 overflows
MemStart EQU 5000h ; starting address of our pretend external memory
CReturn EQU 0Dh ; carriage return character is ASCII 0Dh

; init
org 4000h ; start program at SDK code space base
clr ea ; disable global interrupts to ensure that program can run at full speed
mov DPTR,#MemStart ; initialize dptr to hold starting address of 5000h
mov R1,#00h ; initialize R1 as counter for number of characters received and processed.
mov R2,#00h ; initialize R2 as counter for checking if input character string is "go".

; setup timers and serial port
mov TMOD,#21h ; setup timer1 for mode 2 (used for serial port) and timer0 for mode 1 (used for on/off timing of LEDs)
mov TL1,#BaudRateCount ; setup timer1 with desired baud rate
mov TH1,#BaudRateCount ; setup timer1's reload
clr TF0 ; clear timer0 overflow bit
clr TF1 ; clear timer1 overflow bit
mov TL0,#00h ; load LS bits of "A600h" (this value is for a 25-ms countdown) into TL0
mov TH0,#0A6h ; load MS bits of "A600h" (this value is for a 25-ms countdown) into TH0
mov SCON,#50h ; setup serial port for mode 1, and enable receive

; go
setb TR1 ; start timer1
setb TI ; set serial port transmit empty


WaitLoop:
    jbc RI,CheckReturn ; wait until a character is finished being received; when character is done being received, clear RI, then go to CheckReturn
    jmp WaitLoop ; keep looping waitloop until character is finished being received.


CheckReturn:
    mov A,SBUF ; temporarily retrieve the character in SBUF to A
    cjne R1,#00h,checkIfR1Is1 ; check if this is the first character being input (R1 == 0 means no previous characters have been entered); if not, check if it's the second character by going to checkIfR1Is1.
    cjne A,#'g',continueCheckReturn ; if it IS the first character, check if it's a 'g'.  If not, continue the carriage-return-checking process per usual.
    inc R2 ; increment the "go" counter, meaning that the first letter of "go" has indeed been passed through.
    jmp ProcessChar ; if you've gotten this far, then we know that the first character entered was a 'g'; we've accounted for it by incrementing R2, now go to ProcessChar to store it in memory and wait for next char.
checkIfR1Is1:
    cjne R1,#01h,continueCheckReturn ; check if this is the second character being input (R1 == 1 means one previous character has been entered); if not, we don't care anymore cuz we only care about first two characters being 'g' or 'o'.
    cjne A,#'o',continueCheckReturn ; check if this second character is an 'o'; if not, don't care, just continue checking if it's a carriage return.
    inc R2 ; alright, if you've gotten this far, it means that R2 should now equal 2, which means we've received the characters 'g' and 'o' as the first two characters, which means gogogo for LEDs on! (when the time comes)
    jmp ProcessChar ; process the 'o' like normal.
continueCheckReturn:
    cjne A,#CReturn,ProcessChar ; if it's a carriage return, skip ProcessChar and go below to SendStr to send the stored string; otherwise, process the character by jumping to ProcessChar.


; SendStr is called only when CheckReturn finds that the received character was a carriage return.
SendStr:
; --- output carriage return ---
    jnb TI,SendStr
    clr TI
    mov SBUF,#0Dh
    jnb TI,$
    clr TI
    mov SBUF,#0Ah
; --- end output carriage return ---
    mov B,#00h ; B is copy of character counter R1; reset it to 0.
    mov DPTR,#MemStart ; set DPTR to point to beginning of MemStart; R1 stores how many times DPTR was incremented.
    cjne R1,#00h,sendloop ; if R1 is 0 (no characters were stored before CR was hit), just go back to WaitLoop to wait for more characters, because there's nothing to send; otherwise, go to sendloop to start transmitting characters.
    jmp WaitLoop
sendloop:
    push Acc ; hold A on stack for now...
    mov A,B ; temporarily store B in A...
    cjne A,01h,sendchar ; effectively compare B with (direct address of) R1; if they're the same, that means all characters have been sent, so reset DPTR and jump to WaitLoop to wait for more characters; otherwise, jump to sendchar.
    call TurnOnLEDsMaybe ; now that the string has been sent, call TurnOnLEDsMaybe subroutine, which checks if the sent string was "go", and if so, goes to the LED-lighting subroutine; if not, we continue below to return to WaitLoop.
    pop Acc ; restore whatever A was.
    mov DPTR,#MemStart ; re-init everything because we are starting over!
    mov R1,#00h ; re-init
    mov R2,#00h ; re-init
    jmp WaitLoop ; start over
sendchar:
    jnb TI,sendchar ; wait here until transmit buffer is empty, i.e., when TI becomes 1.
    clr TI ; TI became 1, that means transmit buffer is empty and ready to output another character; clear TI for this next character that's about to be transmitted.
    push Acc ; hold A on stack for now...
    movx A,@DPTR ; move character stored at DPTR in pretend external memory into A in preparation for moving to SBUF.
    mov SBUF,A ; transmit character!
    pop Acc ; restore whatever A was.
    inc B ; after character has been sent, increment B
    inc DPTR ; point DPTR to the next character to send.
    jmp sendloop ; restart at sendloop


ProcessChar:
    movx @DPTR,A ; put retrieved character into address stored in DPTR; movx cuz we're pretending we're using external memory
    inc DPTR ; increment the address in DPTR so the next character will go into the next memory location
    inc R1 ; increment character counter register R1
    jmp WaitLoop ; now that the character is stored, go back to WaitLoop to wait for another character.


TurnOnLEDsMaybe:
    cjne R2,#02h,ExitCheck ; first we check if R2 == 2.  R2 only becomes 2 if the first two characters of the input string are 'g' and 'o', in that order.  If not, return back to the end of sendloop.
    cjne R1,#02h,ExitCheck ; ok, R2 == 2; now we see if R1 == 2, because if it's more than 2, that means that more characters were input after 'g' and 'o', which makes the "go" invalid (no need to check if it's less than 2)
    jmp LEDsFTW ; jump to LEDs for the win!


ExitCheck: ; this is called if R2 and R1 don't both equal 2.
    RET



LEDsFTW: ; stands for "LEDs For The Win"
; turn off all LEDs
mov P1,#00h

; LED Team, GO!
setb TR0 ; start timer0

LED1:
    setb 90h ; turn on LED1
    call wait500ms ; call 500-ms delay routine
    clr 90h ; turn off LED1
    jmp LED2Fwd ; jump to LED2Fwd routine

LED2Fwd:
    setb 93h ; turn on LED2
    call wait500ms ; call 500-ms delay routine
    clr 93h ; turn off LED2
    jmp LED3 ; jump to LED3 routine

LED2Back:
    setb 93h ; turn on LED2
    call wait500ms ; call 500-ms delay routine
    clr 93h ; turn off LED2
    jmp LED1 ; jump to LED1 routine

LED3:
    setb 96h ; turn on LED3
    call wait500ms ; call 500-ms delay routine
    clr 96h ; turn off LED3
    jmp LED2Back ; jump to LED2Back routine


wait500ms:
    mov A,#TimerCount ; move 20d into A; if we've gotten this far, we don't really care what A was, so no Push is needed first.
loop1:
    jnb TF0,loop1 ; wait here until timer0 overflow flag bit is set
    clr TF0 ; clear timer0 overflow flag
    call reload ; reload timer1
    djnz Acc,loop1 ; repeat loop1 until A is 0
    RET


reload:
    mov TL0,#00h ; load LS bits of "A600h" into TL0
    mov TH0,#0A6h ; load MS bits of "A600h" into TH0
    RET


END
