;----------------------------------------------------------------------------- ; ui.inc ; Part of penetrator, the zx spectrum game, made for Apple II ; ; Stefan Wessels, 2019 ; This is free and unencumbered software released into the public domain. ;----------------------------------------------------------------------------- .segment "CODE" ;----------------------------------------------------------------------------- .proc uiTitleScreen zaCounterL = tempBlock + 17 ; timer for flipping text zaCounterH = tempBlock + 18 zaState = tempBlock + 19 ; which text to show lda #0 bit KBDSTRB ; flush the keyboard buffer before uiWriteName sta lastInput jsr drawClearScreen jsr uiWriteName ; also looks for a key press jsr uiShowHighScores jsr drawPresent ; present and jsr uiShowHighScores ; show again so it's on both layers loopo: lda #0 ; set all keys to down for joystick bit KBDSTRB sta lastInput ; debounce the last key if it was a joy button sta zaCounterL sta zaState loop: lda #$08 sta zaCounterH lda zaState bne :+ print textPhilip , #(00), #(8*21) ; the text shows in 3 stages print textCopyright, #(00), #(8*22) lda zaState : cmp #1 bne :+ print textApple2 , #(00), #(8*21) print textStefan , #(00), #(8*22) lda zaState : cmp #2 bne :+ print textSupport , #(00), #(8*21) print textOliver , #(00), #(8*22) lda zaState : cmp #3 bne :+ print textSource , #(00), #(8*21) print textGitHub , #(00), #(8*22) : jsr drawPresent ; show the sceen : jsr inputCheckForInput ; wait for user interaction bne done dec zaCounterL bne :- dec zaCounterH bne :- inc zaState lda zaState cmp #4 bcc :+ jmp loopo : jmp loop done: rts .endproc ;----------------------------------------------------------------------------- .proc uiShowHighScores zaStrL = tempBlock + 1 zaStrH = tempBlock + 2 zaForce = tempBlock + 3 textX = tempBlock + 10 textY = tempBlock + 11 scorePtrL = tempBlock + 14 ; 2 + 3 used by textOut scorePtrH = tempBlock + 15 scoreIdx = tempBlock + 16 scoreShown = tempBlock + 17 lda #highScores sta scorePtrH lda #0 ; and init two counters sta scoreIdx ; which high-score is being processed sta scoreShown ; were any high-scores printed loop: ldy #3 ; 3 bytes to a score ldx #0 ; don't force a number lda scorePtrL ; get the score to convert sta zaStrL lda scorePtrH sta zaStrH jsr textBCDtoSZ ; convert it dex ; step back from the null-terminator lda textNumber, x ; get the last character out cmp #' ' ; was it a space bne show ; no - there was a score so show it beq heading ; as soon as there's no score there won't be any more next: inc scoreIdx ; go to next score lda scoreIdx cmp #5 ; all 5 scores shown? bcc cont ; if not, carry on processing heading: lda scoreShown ; were any score printed beq done ; if not, nothing more print textHighScores, #(11), #(8*0) ; Show the scores' heading done: rts cont: lda scorePtrL ; advance the score ptr to the next 3 bytes adc #3 sta scorePtrL bcc loop inc scorePtrH bne loop ; JuMP show: ; a high-score was set, so show it inc scoreShown inx ; back to the null terminator lda #'0' ; Show score as 10x the actual score sta textNumber, x ; append the 0 to the buffer ldx scoreIdx ; get the index of which score lda #textNumber sta zaStrH lda scorePosX, x ; get the X position of where sta textX lda scoreTextPosY, x ; and the Y position sta textY jsr textOut ; now render the text ldx scoreIdx ; get the score index again lda scoreTextOffset, x ; and figure out the start of the name text for this score clc adc #textHS sta zaStrH lda scoreTextPosX, x ; get the x position sta textX lda scoreTextPosY, x ; and the y position sta textY jsr textOut ; and render the text jmp next ; and go to the next score .endproc ;----------------------------------------------------------------------------- ; Uses line drawing to spell out the Penetrator logo .proc uiWriteName x0 = tempBlock + 0 ; local variables x1 = tempBlock + 1 y0 = tempBlock + 2 y1 = tempBlock + 3 dx = tempBlock + 4 dy = tempBlock + 5 sx = tempBlock + 6 sy = tempBlock + 7 dyNeg = tempBlock + 8 err = tempBlock + 9 err2 = tempBlock + 10 index = tempBlock + 11 remain = tempBlock + 12 skip = tempBlock + 13 width = tempBlock + 14 height = tempBlock + 15 lda #0 sta audioExplFrame ; use this as a delay counter for logo sound lda #((dataLogoLinesEnd - dataLogoLines) / 4) sta remain ldx #0 stx index ; current line stx skip ; impatient user lda #1 ; set up the plot sta width lda #1 sta height loop: dec remain ; iterate over all lines bpl :+ lda #0 sta audioExplFrame rts : ldx index lda dataLogoLines,x ; Load x0,y0 x1,y1 into variables sta x0 inx lda dataLogoLines,x sta y0 inx lda dataLogoLines,x sta x1 inx lda dataLogoLines,x sta y1 inx stx index ; update the index to next line params ldy #2 ; dx = abs(x1-x0) - sx=2 lda x1 sec sbc x0 bcs :+ ; x1 >= x0 eor #$ff ; abs adc #1 ldy #0 ; sx=0 : sta dx dey ; sx=-1|1 sty sx ldy #2 ; dy = abs(y1-y0) - sy=2 lda y1 sec sbc y0 bcs :+ ; y1 >= y0 eor #$ff ; abs adc #1 ldy #0 ; sy=0 sec : sta dy lda #0 sbc dy sta dyNeg ; = -dy dey ; sy=-1|1 sty sy lda dx ; err = dx-dy sec sbc dy sta err plot: ldx x0 ldy y0 jsr drawPlotXY ; plot a "pixel" at x y jsr drawPresent ; present this plot ldx x0 ; do for the 2nd buffer the same ldy y0 jsr drawPlotXY lda skip ; if key/button pressed - fast draw logo bne :+ lda audioMask beq nosnd snd: ldy audioExplFrame ldx #4 jsr playNote ; play the note dec audioExplFrame ; alter the note dec audioExplFrame bmi nosnd ; variable decay so that the duration dec audioExplFrame ; is a close match to when the logo ends dec audioExplFrame nosnd: jsr inputCheckForInput ; check for user interaction sta skip bne :+ wait #$01 : lda x0 ; if(x0=x1 && y0=y1) done cmp x1 bne :+ lda y0 cmp y1 bne :+ jmp loop : lda err ; e2 = err << 1 asl sta err2 lda dyNeg ; if(e2 > -dy) sec sbc err2 bpl :+ lda err ; err = err - dy sec sbc dy sta err lda x0 ; x0 = x0 + sx clc adc sx sta x0 : lda dx ; if(e2 < dx) sec sbc err2 bpl :+ jmp plot : lda err ; err = err + dx clc adc dx sta err lda y0 ; y0 = y0 + sy clc adc sy sta y0 jmp plot .endproc ;----------------------------------------------------------------------------- ; Shows the main menu and waits for user choice .proc uiMainMenu jsr drawClearScreen redraw: print textInstructions, #(10), #(8*04) print textPress , #(13), #(8*08) print textUnderline , #(13), #(8*09) print textOneTwo , #(00), #(8*11) print textTrain , #(00), #(8*12) print textEdit , #(02), #(8*13) print textLoad , #(01), #(8*14) print textSirens , #(05), #(8*15) print textQuit , #(12), #(8*16) lda audioMask beq none bit Bit8432Mask bne :+ print textAudio2 , #(20), #(8*15) jmp present : print textAudio1 , #(20), #(8*15) jmp present none: print textAudio3 , #(20), #(8*15) present: jsr drawPresent lda #GAME_MODE_PLAY ; assume the player will play (play is 0) sta gameMode sta stage ; and set the stage to 0 sta lastInput loop: lda #(optionsEnd-options) ; how many options on this menu ldx #options ; y - hi options "string" jsr uiHandleOptions ; on return, x is the option selected (0 based) cpx #(optionsEnd-options) + 1 bne :+ rts : cpx #2 ; 0 & 1 for 1/2 player game bcs train ; greater means another option stx numPlayers ; x happens to be the value for numPlayers jmp gamePlay ; go gameplay and return to title screen train: bne edit ; still on the cpx #2 here jmp uiTrainerMode ; 2 means train, and then return to title screen edit: cpx #3 bne load jsr editLoop ; 3 means edit jmp uiMainMenu ; and go to main menu, not title load: cpx #4 bne sirens lda #1 ; 1 means load jsr uiFileLoadSave ; call the code to do file name and load jmp uiMainMenu sirens: ; For Apple II this is the "set sound type" cpx #5 bne quit lda audioMask beq audioSetAll bit Bit8432Mask bne :+ lda #0 beq :++ : lda #(AUDIO_EXPLOSION | AUDIO_BOMB_DROP | AUDIO_FIRE | AUDIO_UI_TICK) bne :+ audioSetAll: lda #%01111111 : sta audioMask jmp redraw quit: inc PWREDUP ; Make sure this isn't a power vector jsr MLI ; call the prodos mli ($bf00) - never returns here .byte QUIT_CALL ; call type = quit .addr quitParam ; pointer to parameter table quitParam: .byte 4 ; number of parameters is 4 .byte 0 ; quit type .word 0000 ; reserved .byte 0 ; reserved .word 0000 ; reserved options: .byte "12TELSQ" optionsEnd: .endproc ;----------------------------------------------------------------------------- ; Shows the trainer menu asking for a stage to train (1-4). Allows ; backspace to go back to main menu .proc uiTrainerMode jsr drawClearScreen print textTrainingMode, #(11), #(8*04) print textPressStage , #(02), #(8*12) print textOneToFour , #(08), #(8*13) print textBackup , #(03), #(8*23) jsr drawPresent lda #GAME_MODE_TRAIN sta gameMode lda #(optionsEnd-options) ldx #options jsr uiHandleOptions cpx #4 ; backspace option bcc stageSel cpx #(optionsEnd-options)+1 ; time out goes to title, not main menu like backup bne :+ rts : jmp uiMainMenu ; on backspace go back to main menu stageSel: stx stage jmp gamePlay rts options: .byte "1234", $1b ; $1b is ESC optionsEnd: .endproc ;----------------------------------------------------------------------------- ; Take the number of options in accumulator ; Take a pointer to a number of options in x (lo) y (hi) ; returns when an option was selected, the index of the selected option in x .proc uiHandleOptions sta len + 1 ; set the length for the number of options to look at stx option + 1 ; set address of the options "string" sty option + 2 lda #0 bit KBDSTRB ; flush the keyboard buffer sta lastInput loop: jsr inputCheckForInput ; get the input beq loop ; no input keep looping bit KBDSTRB ; reset the keyboard buffer cmp len + 1 ; if a >= num options bcs len ; then it's a keyboard menu tax ; it's a joystick selection dex ; so 0 based, x-1 is the chosen option rts ; done len: ldx #0 ; placeholder 0 option: cmp PLACEHOLDER,x ; placeholder address beq done ; compare the keyboard to the options dex bpl option ; check all options bmi loop ; when x is -1 it was an invalid entry so loop back done: rts ; x contains the number of the chosen option .endproc ;----------------------------------------------------------------------------- .proc uiShowEditHelp lda #0 ; clear the keyboard buffer bit KBDSTRB sta lastInput jsr drawClearScreen ; clear the screen print textEdtHlp01, #(04), #(8*00) ; show all the help text print textEdtHlp02, #(04), #(8*01) print textEdtHlp03, #(00), #(8*03) print textEdtHlp04, #(00), #(8*05) print textEdtHlp05, #(00), #(8*06) print textEdtHlp06, #(00), #(8*08) print textEdtHlp07, #(00), #(8*09) print textEdtHlp08, #(00), #(8*11) print textEdtHlp09, #(00), #(8*12) print textEdtHlp10, #(00), #(8*14) print textEdtHlp11, #(00), #(8*15) print textEdtHlp12, #(00), #(8*17) print textEdtHlp13, #(00), #(8*18) print textEdtHlp14, #(00), #(8*19) print textEdtHlp15, #(04), #(8*21) print textEdtHlp16, #(04), #(8*22) print textEdtHlp17, #(03), #(8*23) jsr drawPresent : jsr inputCheckForInput ; wait for the user to dismiss the help beq :- jsr drawClearScreen ; clear the screen lda #2 ; and force a sta updateHUD ; HUD update rts .endproc ;----------------------------------------------------------------------------- .proc uiWinScreen cntrl = tempBlock + 14 ; some delay counters, past the text* temporary vars cntrh = tempBlock + 15 jsr drawClearScreen lda #$0 ; init the counters - value sta cntrl lda #2 ; init the counters - value sta cntrh ; not important what these are init with jsr drawPresent print textBonus , #(13), #(8*00) ; show the win screen text printBig textPoints, #$08, #$04, #$01, #$01 printBig text1000 , #$03, #$0D, #$02, #$02 lda stage ; last word changes for home vs victory beq :+ printBig textWow , #$02, #$1D, #$03, #$03 jmp present : printBig textHome , #$04, #$1D, #$02, #$03 present: jsr drawPresent lda #0 bit KBDSTRB ; flush the keyboard buffer sta lastInput loop: dec cntrl bne :+ dec cntrh bne :+ ldy #$0 sty cntrl ldy #2 sty cntrh jsr drawInvertVisibleScreen : jsr inputCheckForInput beq loop ; exit on a key / button done: rts .endproc ;----------------------------------------------------------------------------- ; Show the portions of the HUD that don't change between runs .proc uiShowGameLabels lda #2 ; mark the HUD as needing update sta updateHUD loop: print textTopLine , #(00), #(8*00) print textPlayerNum, #(17), #(8*00) print textDanger , #(00), #(8*23) lda gameMode bne :+ print textShips , #(23), #(8*23) beq show ; JuMP to show - print always leaves with Z flag set : print textTrainer , #(24), #(8*23) show: jsr uiUpdateGameHud jsr drawPresent lda updateHUD beq done jmp loop done: rts .endproc ;----------------------------------------------------------------------------- ; show the HUD variables that change over time .proc uiUpdateGameHud clc ; prep the HUD text for the stage lda stage adc #'1' ; zero based but HUD display 1-5 not 0-4 sta textStage print textStage , #(06), #(8*00) print textDangerBar, #(06), #(8*23) printBCD score , #3, #0, #(25), #(8*00) lda gameMode bne :+ printBCD lives , #1, #1, #(30), #(8*23) : dec updateHUD ; dec "show" counter rts .endproc ;----------------------------------------------------------------------------- ; Show the portions of the HUD that don't change between runs .proc uiShowEditLabels brushType = tempBlock + 16 lda #2 ; mark the HUD as needing update sta updateHUD clc ; prep the HUD text for the stage lda stage adc #'1' ; zero based but HUD display 1-5 not 0-4 sta textStage loop: print textEditStage , #(05), #(8*00) print textStage , #(12), #(8*00) print textEditHelp , #(01), #(8*23) print textEditBrush , #(19), #(8*00) lda brushType bne modeEnemy print textEditTerrain, #(26), #(8*00) jmp :+ modeEnemy: print textEditEnemies, #(26), #(8*00) : jsr drawPresent dec updateHUD ; dec "show" counter beq done jmp loop done: rts .endproc ;----------------------------------------------------------------------------- .proc uiCheckHighScore scoreIdx = tempBlock + 16 clc lda score ; make sure one of the score components is non-zero adc score + 1 ; because a zero score is not high-score eligible adc score + 2 bne didScore rts didScore: lda #((highScoresEnd-highScores) / 3) ; score insert point (1 past end of 0 based table) sta scoreIdx ; save the index ldx #((highScoresEnd-highScores) - 1) ; point x at the high byte of the last score nextScore: ldy #2 ; point y at the high byte of the current score nextByte: lda score, y ; get the score byte cmp highScores, x ; compare to the high score byte bcc lt ; if it's smaller, then this high-score is bigger than the score beq eq : ; score > highscore so ignore remaining bytes in this comparison dex dey bpl:- bmi gt ; step over this smaller highscore eq: dex ; go to the next bytes dey bpl nextByte ; look at the whole score gt: dec scoreIdx ; the score is bigger than this high score so move "up" one bne nextScore ; compare against all high-scores in the table lt: ldx #((highScoresEnd-highScores) / 3) ; get the index past end cpx scoreIdx ; see if the scoreIdx still matches bne newHS ; if not, then a new table entry needs to be made rts newHS: dex ; x now points at last table entry (zero based) cpx scoreIdx ; see if the score overwrites the last table entry beq copyScore ; if yes, just copy score over last table entry jsr uiInsertHighScoreRow ; otherwise scores need to be moved so the score inserts copyScore: lda scoreIdx ; calc the offset of the last byte of the score being replaced asl ; start with index * 2 (and clears carry) adc scoreIdx ; + index (* 3 now sice 3 bytes / score) adc #2 ; and add 2 to point at the last byte tax ; save this offset in x ldy #2 ; last byte of the score : lda score, y ; copy the score sta highScores, x ; over the high-score dex ; for all 3 bytes dey bpl :- jsr uiGetHighScoreName ; now get the new name jmp saveHighScores .endproc ;----------------------------------------------------------------------------- ; move the score and the name in the highscore table "down" a row ; so the new entry can go into the "space" ; scoreIdx is the number of the row that will move down .proc uiInsertHighScoreRow bytes = tempBlock + 15 scoreIdx = tempBlock + 16 rows = tempBlock + 17 lda #(((highScoresEnd-highScores) / 3) - 1) ; Get the length of the table 0 based sec sbc scoreIdx ; subtract the insert row sta rows ; number of rows to move asl ; mult by 3 adc rows sta bytes ; that's how many bytes to move :beq:- ldy #((highScoresEnd-highScores)-4) ; point at the second last row, last byte ldx #((highScoresEnd-highScores)-1) ; point at the last row, last byte scoreLoop: lda highScores, y ; copy the bytes sta highScores, x dex dey dec bytes bne scoreLoop ; till all bytes copied lda rows ; prep to copy name rows asl ; * 8 - 1 is * 7 (7 bytes/name - incl null terminator) asl asl sec sbc rows sta bytes ; set the number of bytes to copy ldy #((textHSEnd-textHS)-8) ; point at the second last row, last byte ldx #((textHSEnd-textHS)-1) ; point at the last row, last byte nameLoop: lda textHS, y ; copy the bytes sta textHS, x dex dey dec bytes bne nameLoop ; till all bytes copied rts .endproc ;----------------------------------------------------------------------------- ; Let the player enter a name for the high score table ; the table entry (0 based) is in scoreIdx when called .proc uiGetHighScoreName zaStrL = tempBlock + 1 ; ptr to a string to print zaStrH = tempBlock + 2 zaEntryL = zWorldPtr ; ptr to a string being entered (abusing world ptr) zaEntryH = zWorldPtr + 1 scoreIdx = tempBlock + 16 ; 0 based index into score table entryLen = tempBlock + 17 ; how many characters the player has entered - shared with uiGetUserText textLen = tempBlock + 18 ; how many characters the player may enter with uiGetUserText jsr drawClearScreen clc ldx scoreIdx ; get the index into the score table lda #textHS sta zaEntryH lda #95 ; the "_" character so stomp the current entry with underscores ldx #((textHighScore2-textHighScore1)-1) ; all scores have the same length stx textLen ; save how long the name may be ldy #0 ; start at character 0 : sta (zaEntryL), y ; write the underscores (also restores the original length) iny ; by overwriting terminators of old entered text dex bne :- stx entryLen ; save 0 to the length of the new name showName: print textCongrats , #(00), #(8*05) ; show the screen print textHSPlayer , #(10), #(8*06) print textPlayerNum, #(20), #(8*06) print textHSPlayer , #(10), #(8*06) print textTop5 , #(03), #(8*08) print textTypeName , #(04), #(8*09) lda zaEntryL ; prep the print string sta zaStrL ; by using the entry string location lda zaEntryH ; zaStrL is modified through printing sta zaStrH ; but the entry copy is stable printZAStr #(13), #(8*11) ; print the name jsr drawPresent jsr uiGetUserText ; Get a character bcc enter ; on carry clear enter was pressed jmp showName ; show the updated name enter: ldy entryLen ; get how many characters entered lda #0 ; load a null terminator sta (zaEntryL), y ; terminate the string done: jsr drawClearScreen rts .endproc ;----------------------------------------------------------------------------- ; Show a UI screen asking for a file name to load or save ; Parameter in acc. 0 is save and 1 is load .proc uiFileLoadSave zaStrL = tempBlock + 1 ; internal - ptr to a string to print zaStrH = tempBlock + 2 ; internal zaEntryL = zWorldPtr ; internal - ptr to a string being entered (abusing world ptr) zaEntryH = zWorldPtr + 1 ; internal entryLen = tempBlock + 17 ; internal - how many characters the player has entered textLen = tempBlock + 18 ; internal - how many characters the player may enter forLoad = tempBlock + 15 ; Parameter - 0 means save, 1 means load allOver = tempBlock + 16 ; internal - on enter, redraw the screen 1 more time sta forLoad jsr drawClearScreen lda #textFileName sta zaEntryH lda #((textFileNameEnd - textFileName) - 1) ; get how long the name may be sta textLen ldy #0 sty KBDSTRB ; clear the keyboard buffer sty lastInput ; and the last key sty allOver ; 1 when the name has been entered - for redraw : ; get the length of the file name there already lda (zaEntryL), y ; get the first letter beq :+ ; if trailing zero then name is known iny bne :- : sty entryLen ; save the length of the name lda #95 ; the "_" character so stomp the remaining name with underscores : cpy textLen bcs showName sta (zaEntryL), y ; write the underscores (also restores the original length) iny ; by overwriting terminators of old entered text bne :- showName: lda forLoad beq :+ print textFileLoad , #(12), #(8*02) print textFileInfoL , #(09), #(8*08) jmp cont : print textFileSave , #(12), #(8*02) print textFileInfoS , #(09), #(8*08) cont: print textFileLines , #(12), #(8*03) print textFileInfo , #(14), #(8*08) print textFileEnter , #(05), #(8*10) lda zaEntryL ; prep the print string sta zaStrL ; by using the entry string location lda zaEntryH ; zaStrL is modified through printing sta zaStrH ; but the entry copy is stable printZAStr #(11), #(8*14) lda allOver ; is this the last redraw before going to disc bne disc ; if <> 0 then time to go to disc jsr drawPresent ; show the screen with changes to file name jsr uiGetUserText ; Get a character bcc enter ; on carry clear enter was pressed jmp showName ; show the updated name enter: ldy entryLen ; get how many characters entered lda #0 ; and null terminate the string sta (zaEntryL), y jsr setWorldFileName lda #1 ; enter has been pressed - it's all over sta allOver ; time to attempt a disc action, but just redraw once more jmp showName disc: lda forLoad ; load or save beq save jsr loadWorld ; load the file by the name bcs fail ; if carry set then there's an error jmp success ; no error - all good save: jsr saveWorld ; save the file bcs fail ; if carry set then there's an error success: print textFileSuccess, #(13), #(8*18) jmp show fail: ; there was an error saving the file pha ; error code in the accumulator, so save it print textFileThe , #(01), #(8*18) print textFileFailed , #(10), #(8*18) lda forLoad ; message for load/save differ beq :+ print textFileInfoL , #(05), #(8*18) jmp errcde : print textFileInfoS , #(05), #(8*18) errcde: pla ; get the saved error code jsr toHex ; print it as human readable hex and show it to the user print szHex , #(30), #(8*18) show: jsr drawPresent ; finally show the whole screen getkey: jsr inputCheckForInput ; wait for the user to acknowledge beq getkey jsr drawClearScreen ; rts .endproc ;----------------------------------------------------------------------------- ; Called with zaEntry pointing at a location to receive some text ; and entryLen should be 0 and is used internally to track where the current ; edit location in the string is. ; returns with partial string when carry set ; User pressed enter and accepted the string when carry clear on return .proc uiGetUserText zaEntryL = zWorldPtr ; ptr to a string being entered (abusing world ptr) entryLen = tempBlock + 17 ; Parameter/Internal - how many characters the player has entered textLen = tempBlock + 18 ; how many characters the player may enter lda #0 bit KBDSTRB ; flush the keyboard buffer loop: lda KBD ; wait for a key bit Bit8Mask beq loop bit KBDSTRB pha lda #AUDIO_UI_TICK sta audioFrame jsr serviceAudio pla and #$7F cmp #$0D ; enter key then done beq enter cmp #$08 ; backspace key is handled beq erase cmp #$20 beq accept ; space is okay cmp #'0' bcc loop ; ignore below '0' cmp #('9'+1) bcc accept ; 0-9 okay cmp #'A' ; < 'A' ignore bcc loop cmp #('Z'+1) bcc accept cmp #'a' bcc loop cmp #('z'+1) bcs loop ; ignore past 'Z' accept: ; lda theChar ldy entryLen ; how many characters already entered cpy textLen ; vs how many can be entered bcs loop ; already full, don't accept sta (zaEntryL), y ; and write this character there inc entryLen ; and increase the length entered goShow: sec ; on carry set, not done rts erase: ldy entryLen ; get how many characters have been entered beq loop ; if none then ignore the backspace key dec entryLen ; 1 less character entered dey lda #95 ; load the underscore sta (zaEntryL), y ; stomp the last character with the underscore bne goShow ; redraw the screen enter: clc rts .endproc