commit 009a73ab89a156471bb6eb93cf82a1a00d05d4b5 Author: stid Date: Tue Nov 5 20:31:17 2019 -0800 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e4320f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.vscode/* +bin/* +.DS_Store +*.o +*.sym +*.lst \ No newline at end of file diff --git a/arch/keyb.asm b/arch/keyb.asm new file mode 100755 index 0000000..a521a9b --- /dev/null +++ b/arch/keyb.asm @@ -0,0 +1,747 @@ +BasicUpstart2(start) + +// c64 +.const bgcolor = $d020 +.const bordercolor = $d021 +.const screenram = $0400 + +// kernel functions +.const bsout = $ab1e //kernel character output sub +.const chrout = $ffd2 +.const clearscreen = $e544 + +// chars +.const cr = 13 + +// memory map +.const kernel = $ff80 +.const start_basic = $0281 +.const end_basic = $0037 +.const step_size_garb = $000f +.const dataset = $0090 +.const input_dev = 153 +.const out_dev = 154 +.const time_1 = 160 +.const time_2 = 161 +.const time_3 = 162 +.const ds_motor = 192 + +// color petscii +.const color_cyan = $9f +.const color_light_blue = $9a +.const color_green = $1e + +// local const +.const pline_len = 40 +.const hex_open_chr = $5b +.const hex_close_chr = $5d + +.macro printHexByte(stringAddr, value) { + lda #stringAddr + ldx value + jsr printhex_row +} + +.macro printHexWord(stringAddr, value) { + bPrint(stringAddr) + jsr popenhex + lda value+1 + jsr printhex + lda value + jsr printhex + jsr pclosehex + bChar(cr) +} + +.macro bPrint(stringAddr) { + lda #stringAddr + jsr bsout +} + +.macro bChar(char) { + lda #char + jsr chrout +} + +//========================================================== +// code +//========================================================== + +start: + ldx #$00 // color + stx $d021 // set background color + stx $d020 // set border color + + jsr clearscreen + + bChar(color_cyan) + bPrint(str_about) + + bChar(cr) + bChar(color_light_blue) + +getKeyb: + jsr Keyboard + bcs NoValidInput + stx TempX + sty TempY + cmp #$ff + // Check A for Alphanumeric keys + beq NoNewAphanumericKey + // jsr printhex + + cmp #'m' + beq mapMemory + cmp #'q' + beq exit + + jmp getKeyb + +NoNewAphanumericKey: + // Check X & Y for Non-Alphanumeric Keys + ldx TempX + ldy TempY + //stx $0401 + //sty $0402 +NoValidInput: // This may be substituted for an errorhandler if needed. + jmp getKeyb + + +exit: rts + + +mapMemory: + ldx #pline_len // lenht of pline + jsr pline + + // ---------------------- + // ------ map table start + // ---------------------- + + // kernel + printHexByte(str_row_kernel, kernel) + + // start basic area + printHexWord(str_row_basic_start, start_basic) + + // end basic area + printHexWord(str_row_basic_end, end_basic) + + // garbadge + printHexByte(str_row_step_size_garb, printhex_row) + + // dataset + printHexByte(str_row_dataset, dataset) + + // input dev + printHexByte(str_row_input_dev, input_dev) + + // out dev + printHexByte(str_row_out_dev, out_dev) + + // time 1 + printHexByte(str_row_time_1, time_1) + + // time 2 + printHexByte(str_row_time_2, time_2) + + // time 2 + printHexByte(str_row_time_2, time_3) + + // dataset motor + printHexByte(str_row_ds_motor, ds_motor) + + + // ---------------------- + // ------ map table end + // ---------------------- + bChar(cr) + + ldx #pline_len + jsr pline + rts + +// ———————————————————————————————————— +// popenhex +// ———————————————————————————————————— +popenhex: + bChar(hex_open_chr) + bChar(color_green) + rts + +// ———————————————————————————————————— +// pclosehex +// ———————————————————————————————————— +pclosehex: + bChar(color_light_blue) + bChar(hex_close_chr) + rts + +// ———————————————————————————————————— +// printhex_row +// ———————————————————————————————————— +// ———————————————————————————————————— +// preparatory ops: .a: msn ascii char +// .x: lsn ascii char +// .y: value +// +// returned values: none +// ———————————————————————————————————— +printhex_row: + sta a_cache // preserve a + txa // store x using a as it will be lost after bsout + pha + lda a_cache // get back a + jsr bsout + jsr popenhex + pla // saved x back in a from saved a + jsr printhex + jsr pclosehex + bChar(cr) + rts + +// ———————————————————————————————————— +// printhex +// ———————————————————————————————————— +// ———————————————————————————————————— +// preparatory ops: .a: byte to convert +// +// returned values: none +// ———————————————————————————————————— +printhex: + pha + jsr binhex + jsr chrout + txa + jsr chrout + pla + rts + +// ———————————————————————————————————— +// btohex +// ———————————————————————————————————— +// ———————————————————————————————————— +// preparatory ops: .a: byte to convert +// +// returned values: .a: msn ascii char +// .x: lsn ascii char +// .y: entry value +// ———————————————————————————————————— +binhex: pha //save byte + and #%00001111 //extract lsn + tax //save it + pla //recover byte + lsr //extract... + lsr //msn + lsr + lsr + pha //save msn + txa //lsn + jsr binhex1 //generate ascii lsn + tax //save + pla //get msn & fall thru +// +// convert nybble to hex ascii equivalent... +binhex1: cmp #$0a + bcc binhex2 //in decimal range + adc #$66 //hex compensate +binhex2: eor #%00110000 //finalize nybble + rts //done + + + +// ———————————————————————————————————— +// pline +// ———————————————————————————————————— +// ———————————————————————————————————— +// preparatory ops: .x: lenght +// returned values: none +// ———————————————————————————————————— +pline: + cpx #0 + bne pline_start + ldx #12 // default to 12 if 0 is passed in x +pline_start: lda #102 +pline_loop: jsr chrout + dex + cpx #0 + bne pline_loop + lda #cr + jsr chrout + rts + + +.encoding "petscii_mixed" +str_about: + .text "c=64 mapper v2.0 by =stid=" + .byte 13, 0 +str_row_kernel: + .text " kernel " + .byte 0 +str_row_basic_start: + .text " start basic area " + .byte 0 +str_row_basic_end: + .text " end basic area " + .byte 0 +str_row_step_size_garb: + .text " step size garbage " + .byte 0 +str_row_dataset: + .text " dataset " + .byte 0 +str_row_input_dev: + .text " input device " + .byte 0 +str_row_out_dev: + .text " output device " + .byte 0 +str_row_time_1: + .text " time 1 " + .byte 0 +str_row_time_2: + .text " time 2 " + .byte 0 +str_row_time_3: + .text " time 3 " + .byte 0 +str_row_ds_motor: + .text " dataset motor " + .byte 0 + +a_cache: + .byte 0 + +TempX: + .byte 0 +TempY: + .byte 0 + + + + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Keyboard IO Routine + ~~~~~~~~~~~~~~~~~~~ + By: TWW/CTR + + + Preparatory Settings + ~~~~~~~~~~~~~~~~~~~~ + None + + + Destroys + ~~~~~~~~ + Accumulator + X-Register + Y-Register + Carry / Zero / Negative + $dc00 + $dc01 + $50-$5f + + + Footprint + ~~~~~~~~~ + #$206 Bytes + + + Information + ~~~~~~~~~~~ + The routine uses "2 Key rollower" or up to 3 if the key-combination doesen't induce shadowing. + If 2 or 3 keys are pressed simultaneously (within 1 scan) a "No Activity" state has to occur before new valid keys are returned. + RESTORE is not detectable and must be handled by NMI IRQ. + SHIFT LOCK is not detected due to unreliability. + + + Usage + ~~~~~ + Example Code: + + jsr Keyboard + bcs NoValidInput + stx TempX + sty TempY + cmp #$ff + beq NoNewAphanumericKey + // Check A for Alphanumeric keys + sta $0400 + NoNewAphanumericKey: + // Check X & Y for Non-Alphanumeric Keys + ldx TempX + ldy TempY + stx $0401 + sty $0402 + NoValidInput: // This may be substituted for an errorhandler if needed. + + + Returned + ~~~~~~~~ + + +=================================================+ + | Returned in Accumulator | + +===========+===========+=============+===========+ + | $00 - @ | $10 - p | $20 - SPC | $30 - 0 | + | $01 - a | $11 - q | $21 - | $31 - 1 | + | $02 - b | $12 - r | $22 - | $32 - 2 | + | $03 - c | $13 - s | $23 - | $33 - 3 | + | $04 - d | $14 - t | $24 - | $34 - 4 | + | $05 - e | $15 - u | $25 - | $35 - 5 | + | $06 - f | $16 - v | $26 - | $36 - 6 | + | $07 - g | $17 - w | $27 - | $37 - 7 | + | $08 - h | $18 - x | $28 - | $38 - 8 | + | $09 - i | $19 - y | $29 - | $39 - 9 | + | $0a - j | $1a - z | $2a - * | $3a - : | + | $0b - k | $1b - | $2b - + | $3b - ; | + | $0c - l | $1c - £ | $2c - , | $3c - | + | $0d - m | $1d - | $2d - - | $3d - = | + | $0e - n | $1e - ^ | $2e - . | $3e - | + | $0f - o | $1f - <- | $2f - / | $3f - | + +-----------+-----------+-------------+-----------+ + + +================================================================================ + | Return in X-Register | + +=========+=========+=========+=========+=========+=========+=========+=========+ + | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | + +---------+---------+---------+---------+---------+---------+---------+---------+ + | CRSR UD | F5 | F3 | F1 | F7 | CRSR RL | RETURN |INST/DEL | + +---------+---------+---------+---------+---------+---------+---------+---------+ + + +================================================================================ + | Return in Y-Register | + +=========+=========+=========+=========+=========+=========+=========+=========+ + | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | + +---------+---------+---------+---------+---------+---------+---------+---------+ + |RUN STOP | L-SHIFT | C= | R-SHIFT |CLR/HOME | CTRL | | | + +---------+---------+---------+---------+---------+---------+---------+---------+ + + CARRY: + - Set = Error Condition (Check A for code): + A = #$01 => No keyboard activity is detected. + A = #$02 => Control Port #1 Activity is detected. + A = #$03 => Key Shadowing / Ghosting is detected. + A = #$04 => 2 or 3 new keys is detected within one scan + A = #$05 => Awaiting "No Activity" state + - Clear = Valid input + A = #$ff => No new Alphanumeric Keys detected (some key(s) being held down AND/OR some Non-Alphanumeric key is causing valid return). + A <> #$ff => New Alphanumeric Key returned. Non-Alphanumeric keys may also be returned in X or Y Register + + Issues/ToDo: + ~~~~~~~~~~~~ + - None + + + Improvements: + ~~~~~~~~~~~~~ + - Replace the subroutine with a pseudocommand and account for speedcode parameter (Memory vs. Cycles). + - Shorten the routine / Optimize if possible. + + + History: + ~~~~~~~~ + V2.5 - New test tool. + Added return of error codes. + Fixed a bug causing Buffer Overflow. + Fixed a bug in Non Alphanumerical Flags from 2.0. + V2.1 - Shortened the source by adding .for loops & Updated the header and some comments. + Added "simultaneous keypress" check. + V2.0 - Added return of non-Alphanumeric keys into X & Y-Registers. + Small optimizations here and there. + V1.1 - Unrolled code to make it faster and optimized other parts of it. + Removed SHIFT LOCK scanning. + V1.0 - First Working Version along with test tool. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + .pc = * "Keyboard Scan Routine" + + + // ZERO PAGE Varibles + .const ScanResult = $50 // 8 bytes + .const BufferNew = $58 // 3 bytes + .const KeyQuantity = $5b // 1 byte + .const NonAlphaFlagX = $5c // 1 byte + .const NonAlphaFlagY = $5d // 1 byte + .const TempZP = $5e // 1 byte + .const SimultaneousKeys = $5f // 1 byte + + // Operational Variables + .var MaxKeyRollover = 3 + +Keyboard: +{ + jmp Main + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Routine for Scanning a Matrix Row + +KeyInRow: + asl + bcs *+5 + jsr KeyFound + .for (var i = 0 ; i < 7 ; i++) { + inx + asl + bcs *+5 + jsr KeyFound + } + rts + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Routine for handling: Key Found + +KeyFound: + stx TempZP + dec KeyQuantity + bmi OverFlow + ldy KeyTable,x + ldx KeyQuantity + sty BufferNew,x + ldx TempZP + rts + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Routine for handling: Overflow + +OverFlow: + pla // Dirty hack to handle 2 layers of JSR + pla + pla + pla + // Don't manipulate last legal buffer as the routine will fix itself once it gets valid input again. + lda #$03 + sec + rts + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Exit Routine for: No Activity + +NoActivityDetected: + // Exit With A = #$01, Carry Set & Reset BufferOld. + lda #$00 + sta SimultaneousAlphanumericKeysFlag // Clear the too many keys flag once a "no activity" state is detected. + stx BufferOld + stx BufferOld+1 + stx BufferOld+2 + sec + lda #$01 + rts + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Exit Routine for Control Port Activity + +ControlPort: + // Exit with A = #$02, Carry Set. Keep BufferOld to verify input after Control Port activity ceases + sec + lda #$02 + rts + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Configure Data Direction Registers +Main: + ldx #$ff + stx $dc02 // Port A - Output + ldy #$00 + sty $dc03 // Port B - Input + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Check for Port Activity + + sty $dc00 // Connect all Keyboard Rows + cpx $dc01 + beq NoActivityDetected + + lda SimultaneousAlphanumericKeysFlag + beq !+ + // Waiting for all keys to be released before accepting new input. + lda #$05 + sec + rts +!: + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Check for Control Port #1 Activity + + stx $dc00 // Disconnect all Keyboard Rows + cpx $dc01 // Only Control Port activity will be detected + bne ControlPort + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Scan Keyboard Matrix + + lda #%11111110 + sta $dc00 + ldy $dc01 + sty ScanResult+7 + sec + .for (var i = 6 ; i > -1 ; i--) { + rol + sta $dc00 + ldy $dc01 + sty ScanResult+i + } + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Check for Control Port #1 Activity (again) + + stx $dc00 // Disconnect all Keyboard Rows + cpx $dc01 // Only Control Port activity will be detected + bne ControlPort + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Initialize Buffer, Flags and Max Keys + + // Reset current read buffer + stx BufferNew + stx BufferNew+1 + stx BufferNew+2 + + // Reset Non-AlphaNumeric Flag + inx + stx NonAlphaFlagY + + // Set max keys allowed before ignoring result + lda #MaxKeyRollover + sta KeyQuantity + + // Counter to check for simultaneous alphanumeric key-presses + lda #$fe + sta SimultaneousKeys + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Check and flag Non Alphanumeric Keys + + lda ScanResult+6 + eor #$ff + and #%10000000 // Left Shift + lsr + sta NonAlphaFlagY + lda ScanResult+0 + eor #$ff + and #%10100100 // RUN STOP - C= - CTRL + ora NonAlphaFlagY + sta NonAlphaFlagY + lda ScanResult+1 + eor #$ff + and #%00011000 // Right SHIFT - CLR HOME + ora NonAlphaFlagY + sta NonAlphaFlagY + + lda ScanResult+7 // The rest + eor #$ff + sta NonAlphaFlagX + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Check for pressed key(s) + + lda ScanResult+7 + cmp #$ff + beq *+5 + jsr KeyInRow + .for (var i = 6 ; i > -1 ; i--) { + ldx #[7-i]*8 + lda ScanResult+i + beq *+5 + jsr KeyInRow + } + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Key Scan Completed + + // Put any new key (not in old scan) into buffer + ldx #MaxKeyRollover-1 + !: lda BufferNew,x + cmp #$ff + beq Exist // Handle 'null' values + cmp BufferOld + beq Exist + cmp BufferOld+1 + beq Exist + cmp BufferOld+2 + beq Exist + // New Key Detected + inc BufferQuantity + ldy BufferQuantity + sta Buffer,y + // Keep track of how many new Alphanumeric keys are detected + inc SimultaneousKeys + beq TooManyNewKeys + Exist: + dex + bpl !- + + // Anything in Buffer? + ldy BufferQuantity + bmi BufferEmpty + // Yes: Then return it and tidy up the buffer + dec BufferQuantity + lda Buffer + ldx Buffer+1 + stx Buffer + ldx Buffer+2 + stx Buffer+1 + jmp Return + +BufferEmpty: // No new Alphanumeric keys to handle. + lda #$ff + +Return: // A is preset + clc + // Copy BufferNew to BufferOld + ldx BufferNew + stx BufferOld + ldx BufferNew+1 + stx BufferOld+1 + ldx BufferNew+2 + stx BufferOld+2 + // Handle Non Alphanumeric Keys + ldx NonAlphaFlagX + ldy NonAlphaFlagY + rts + +TooManyNewKeys: + sec + lda #$ff + sta BufferQuantity + sta SimultaneousAlphanumericKeysFlag + lda #$04 + rts + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +KeyTable: + .byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff // CRSR DOWN, F5, F3, F1, F7, CRSR RIGHT, RETURN, INST DEL + .byte $ff, $05, $13, $1a, $34, $01, $17, $33 // LEFT SHIFT, "E", "S", "Z", "4", "A", "W", "3" + .byte $18, $14, $06, $03, $36, $04, $12, $35 // "X", "T", "F", "C", "6", "D", "R", "5" + .byte $16, $15, $08, $02, $38, $07, $19, $37 // "V", "U", "H", "B", "8", "G", "Y", "7" + .byte $0e, $0f, $0b, $0d, $30, $0a, $09, $39 // "N", "O" (Oscar), "K", "M", "0" (Zero), "J", "I", "9" + .byte $2c, $00, $3a, $2e, $2d, $0c, $10, $2b // ",", "@", ":", ".", "-", "L", "P", "+" + .byte $2f, $1e, $3d, $ff, $ff, $3b, $2a, $1c // "/", "^", "=", RIGHT SHIFT, HOME, ";", "*", "£" + .byte $ff, $11, $ff, $20, $32, $ff, $1f, $31 // RUN STOP, "Q", "C=" (CMD), " " (SPC), "2", "CTRL", "<-", "1" + +BufferOld: + .byte $ff, $ff, $ff + +Buffer: + .byte $ff, $ff, $ff, $ff + +BufferQuantity: + .byte $ff + +SimultaneousAlphanumericKeysFlag: + .byte $00 +} \ No newline at end of file diff --git a/arch/precursor.asm b/arch/precursor.asm new file mode 100755 index 0000000..693f727 --- /dev/null +++ b/arch/precursor.asm @@ -0,0 +1,801 @@ +BasicUpstart2(start) + +// c64 +.const bgcolor = $d020 +.const bordercolor = $d021 +.const screenram = $0400 + +// kernel functions +.const kBsout = $ab1e //kernel character output sub +.const kChrout = $ffd2 +.const clearscreen = $e544 + +// chars +.const cr = 13 + +// memory map +.const kernel = $ff80 +.const start_basic = $0281 +.const end_basic = $0037 +.const step_size_garb = $000f +.const dataset = $0090 +.const input_dev = 153 +.const out_dev = 154 +.const time_1 = 160 +.const time_2 = 161 +.const time_3 = 162 +.const ds_motor = 192 + +// color petscii +.const color_cyan = $9f +.const color_light_blue = $9a +.const color_green = $1e + +// local const +.const pline_len = 40 +.const hex_open_chr = $5b +.const hex_close_chr = $5d + +// zero page +.const stringLow = $FB +.const stirngHigh = $FC + +.const videoLow = $FD +.const videoHigh = $FE + + +.macro printHexByte(stringAddr, value) { + lda #stringAddr + ldx value + jsr printhex_row +} + +.macro printHexWord(stringAddr, value) { + bPrint(stringAddr) + jsr popenhex + lda value+1 + jsr printhex + lda value + jsr printhex + jsr pclosehex + bChar(cr) +} + +.macro print(stringAddr) { + lda #stringAddr + jsr bsout +} + +.macro bPrint(stringAddr) { + lda #stringAddr + jsr bsout +} + +.macro bChar(char) { + lda #char + jsr kChrout +} + +//========================================================== +// code +//========================================================== + +start: + ldx #$00 // color + stx $d021 // set background color + stx $d020 // set border color + + lda #$04 + sta videoHigh + lda #$00 + sta videoLow + + jsr clearscreen + + bChar(color_cyan) + print(str_about) + + bChar(cr) + bChar(color_light_blue) + +getKeyb: + jsr Keyboard + bcs NoValidInput + stx TempX + sty TempY + cmp #$ff + // Check A for Alphanumeric keys + beq NoNewAphanumericKey + // jsr printhex + + cmp #'m' + beq mapMemory + cmp #'q' + beq exit + + jmp getKeyb + +NoNewAphanumericKey: + // Check X & Y for Non-Alphanumeric Keys + ldx TempX + ldy TempY + //stx $0401 + //sty $0402 +NoValidInput: // This may be substituted for an errorhandler if needed. + jmp getKeyb + +exit: rts + + +bsout: + sty stirngHigh + sta stringLow + + + ldy #$00 +bloop: lda (stringLow), y + cmp #0 + beq blexit + sta (videoLow), y + iny + cpy #$f0 + bne bcontinue + clc + lda videoLow + adc #$f0 + sta videoLow + lda videoHigh + adc #$00 + sta videoHigh +bcontinue: jmp bloop +blexit: + rts + + + + + +mapMemory: { + ldx #pline_len // lenht of pline + jsr pline + + // ---------------------- + // ------ map table start + // ---------------------- + + // kernel + printHexByte(str_row_kernel, kernel) + + // start basic area + printHexWord(str_row_basic_start, start_basic) + + // end basic area + printHexWord(str_row_basic_end, end_basic) + + // garbadge + printHexByte(str_row_step_size_garb, printhex_row) + + // dataset + printHexByte(str_row_dataset, dataset) + + // input dev + printHexByte(str_row_input_dev, input_dev) + + // out dev + printHexByte(str_row_out_dev, out_dev) + + // time 1 + printHexByte(str_row_time_1, time_1) + + // time 2 + printHexByte(str_row_time_2, time_2) + + // time 2 + printHexByte(str_row_time_2, time_3) + + // dataset motor + printHexByte(str_row_ds_motor, ds_motor) + + + // ---------------------- + // ------ map table end + // ---------------------- + bChar(cr) + + ldx #pline_len + jsr pline + rts +} + +// ———————————————————————————————————— +// popenhex +// ———————————————————————————————————— +popenhex: + bChar(hex_open_chr) + bChar(color_green) + rts + +// ———————————————————————————————————— +// pclosehex +// ———————————————————————————————————— +pclosehex: + bChar(color_light_blue) + bChar(hex_close_chr) + rts + +// ———————————————————————————————————— +// printhex_row +// ———————————————————————————————————— +// ———————————————————————————————————— +// preparatory ops: .a: msn ascii char +// .x: lsn ascii char +// .y: value +// +// returned values: none +// ———————————————————————————————————— +printhex_row: + sta a_cache // preserve a + txa // store x using a as it will be lost after ksout + pha + lda a_cache // get back a + jsr bsout + jsr popenhex + pla // saved x back in a from saved a + jsr printhex + jsr pclosehex + bChar(cr) + rts + +// ———————————————————————————————————— +// printhex +// ———————————————————————————————————— +// ———————————————————————————————————— +// preparatory ops: .a: byte to convert +// +// returned values: none +// ———————————————————————————————————— +printhex: + pha + jsr binhex + jsr kChrout + txa + jsr kChrout + pla + rts + +// ———————————————————————————————————— +// btohex +// ———————————————————————————————————— +// ———————————————————————————————————— +// preparatory ops: .a: byte to convert +// +// returned values: .a: msn ascii char +// .x: lsn ascii char +// .y: entry value +// ———————————————————————————————————— +binhex: pha //save byte + and #%00001111 //extract lsn + tax //save it + pla //recover byte + lsr //extract... + lsr //msn + lsr + lsr + pha //save msn + txa //lsn + jsr binhex1 //generate ascii lsn + tax //save + pla //get msn & fall thru +// +// convert nybble to hex ascii equivalent... +binhex1: cmp #$0a + bcc binhex2 //in decimal range + adc #$66 //hex compensate +binhex2: eor #%00110000 //finalize nybble + rts //done + + + +// ———————————————————————————————————— +// pline +// ———————————————————————————————————— +// ———————————————————————————————————— +// preparatory ops: .x: lenght +// returned values: none +// ———————————————————————————————————— +pline: + cpx #0 + bne pline_start + ldx #12 // default to 12 if 0 is passed in x +pline_start: lda #102 +pline_loop: jsr kChrout + dex + cpx #0 + bne pline_loop + lda #cr + jsr kChrout + rts + + +.encoding "screencode_mixed" +str_about: + .text "c=64 mapper v2.0 by =stid=" + .byte 0 +str_row_kernel: + .text " kernel " + .byte 0 +str_row_basic_start: + .text " start basic area " + .byte 0 +str_row_basic_end: + .text " end basic area " + .byte 0 +str_row_step_size_garb: + .text " step size garbage " + .byte 0 +str_row_dataset: + .text " dataset " + .byte 0 +str_row_input_dev: + .text " input device " + .byte 0 +str_row_out_dev: + .text " output device " + .byte 0 +str_row_time_1: + .text " time 1 " + .byte 0 +str_row_time_2: + .text " time 2 " + .byte 0 +str_row_time_3: + .text " time 3 " + .byte 0 +str_row_ds_motor: + .text " dataset motor " + .byte 0 +str_row_generic: + .text " hex value " + .byte 0 + + +a_cache: + .byte 0 + +TempX: + .byte 0 +TempY: + .byte 0 +cursorX: + .byte 0 +cursorY: + .byte 0 + + + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Keyboard IO Routine + ~~~~~~~~~~~~~~~~~~~ + By: TWW/CTR + + + Preparatory Settings + ~~~~~~~~~~~~~~~~~~~~ + None + + + Destroys + ~~~~~~~~ + Accumulator + X-Register + Y-Register + Carry / Zero / Negative + $dc00 + $dc01 + $50-$5f + + + Footprint + ~~~~~~~~~ + #$206 Bytes + + + Information + ~~~~~~~~~~~ + The routine uses "2 Key rollower" or up to 3 if the key-combination doesen't induce shadowing. + If 2 or 3 keys are pressed simultaneously (within 1 scan) a "No Activity" state has to occur before new valid keys are returned. + RESTORE is not detectable and must be handled by NMI IRQ. + SHIFT LOCK is not detected due to unreliability. + + + Usage + ~~~~~ + Example Code: + + jsr Keyboard + bcs NoValidInput + stx TempX + sty TempY + cmp #$ff + beq NoNewAphanumericKey + // Check A for Alphanumeric keys + sta $0400 + NoNewAphanumericKey: + // Check X & Y for Non-Alphanumeric Keys + ldx TempX + ldy TempY + stx $0401 + sty $0402 + NoValidInput: // This may be substituted for an errorhandler if needed. + + + Returned + ~~~~~~~~ + + +=================================================+ + | Returned in Accumulator | + +===========+===========+=============+===========+ + | $00 - @ | $10 - p | $20 - SPC | $30 - 0 | + | $01 - a | $11 - q | $21 - | $31 - 1 | + | $02 - b | $12 - r | $22 - | $32 - 2 | + | $03 - c | $13 - s | $23 - | $33 - 3 | + | $04 - d | $14 - t | $24 - | $34 - 4 | + | $05 - e | $15 - u | $25 - | $35 - 5 | + | $06 - f | $16 - v | $26 - | $36 - 6 | + | $07 - g | $17 - w | $27 - | $37 - 7 | + | $08 - h | $18 - x | $28 - | $38 - 8 | + | $09 - i | $19 - y | $29 - | $39 - 9 | + | $0a - j | $1a - z | $2a - * | $3a - : | + | $0b - k | $1b - | $2b - + | $3b - ; | + | $0c - l | $1c - £ | $2c - , | $3c - | + | $0d - m | $1d - | $2d - - | $3d - = | + | $0e - n | $1e - ^ | $2e - . | $3e - | + | $0f - o | $1f - <- | $2f - / | $3f - | + +-----------+-----------+-------------+-----------+ + + +================================================================================ + | Return in X-Register | + +=========+=========+=========+=========+=========+=========+=========+=========+ + | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | + +---------+---------+---------+---------+---------+---------+---------+---------+ + | CRSR UD | F5 | F3 | F1 | F7 | CRSR RL | RETURN |INST/DEL | + +---------+---------+---------+---------+---------+---------+---------+---------+ + + +================================================================================ + | Return in Y-Register | + +=========+=========+=========+=========+=========+=========+=========+=========+ + | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | + +---------+---------+---------+---------+---------+---------+---------+---------+ + |RUN STOP | L-SHIFT | C= | R-SHIFT |CLR/HOME | CTRL | | | + +---------+---------+---------+---------+---------+---------+---------+---------+ + + CARRY: + - Set = Error Condition (Check A for code): + A = #$01 => No keyboard activity is detected. + A = #$02 => Control Port #1 Activity is detected. + A = #$03 => Key Shadowing / Ghosting is detected. + A = #$04 => 2 or 3 new keys is detected within one scan + A = #$05 => Awaiting "No Activity" state + - Clear = Valid input + A = #$ff => No new Alphanumeric Keys detected (some key(s) being held down AND/OR some Non-Alphanumeric key is causing valid return). + A <> #$ff => New Alphanumeric Key returned. Non-Alphanumeric keys may also be returned in X or Y Register + + Issues/ToDo: + ~~~~~~~~~~~~ + - None + + + Improvements: + ~~~~~~~~~~~~~ + - Replace the subroutine with a pseudocommand and account for speedcode parameter (Memory vs. Cycles). + - Shorten the routine / Optimize if possible. + + + History: + ~~~~~~~~ + V2.5 - New test tool. + Added return of error codes. + Fixed a bug causing Buffer Overflow. + Fixed a bug in Non Alphanumerical Flags from 2.0. + V2.1 - Shortened the source by adding .for loops & Updated the header and some comments. + Added "simultaneous keypress" check. + V2.0 - Added return of non-Alphanumeric keys into X & Y-Registers. + Small optimizations here and there. + V1.1 - Unrolled code to make it faster and optimized other parts of it. + Removed SHIFT LOCK scanning. + V1.0 - First Working Version along with test tool. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + .pc = * "Keyboard Scan Routine" + + + // ZERO PAGE Varibles + .const ScanResult = $50 // 8 bytes + .const BufferNew = $58 // 3 bytes + .const KeyQuantity = $5b // 1 byte + .const NonAlphaFlagX = $5c // 1 byte + .const NonAlphaFlagY = $5d // 1 byte + .const TempZP = $5e // 1 byte + .const SimultaneousKeys = $5f // 1 byte + + // Operational Variables + .var MaxKeyRollover = 3 + +Keyboard: +{ + jmp Main + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Routine for Scanning a Matrix Row + +KeyInRow: + asl + bcs *+5 + jsr KeyFound + .for (var i = 0 ; i < 7 ; i++) { + inx + asl + bcs *+5 + jsr KeyFound + } + rts + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Routine for handling: Key Found + +KeyFound: + stx TempZP + dec KeyQuantity + bmi OverFlow + ldy KeyTable,x + ldx KeyQuantity + sty BufferNew,x + ldx TempZP + rts + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Routine for handling: Overflow + +OverFlow: + pla // Dirty hack to handle 2 layers of JSR + pla + pla + pla + // Don't manipulate last legal buffer as the routine will fix itself once it gets valid input again. + lda #$03 + sec + rts + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Exit Routine for: No Activity + +NoActivityDetected: + // Exit With A = #$01, Carry Set & Reset BufferOld. + lda #$00 + sta SimultaneousAlphanumericKeysFlag // Clear the too many keys flag once a "no activity" state is detected. + stx BufferOld + stx BufferOld+1 + stx BufferOld+2 + sec + lda #$01 + rts + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Exit Routine for Control Port Activity + +ControlPort: + // Exit with A = #$02, Carry Set. Keep BufferOld to verify input after Control Port activity ceases + sec + lda #$02 + rts + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Configure Data Direction Registers +Main: + ldx #$ff + stx $dc02 // Port A - Output + ldy #$00 + sty $dc03 // Port B - Input + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Check for Port Activity + + sty $dc00 // Connect all Keyboard Rows + cpx $dc01 + beq NoActivityDetected + + lda SimultaneousAlphanumericKeysFlag + beq !+ + // Waiting for all keys to be released before accepting new input. + lda #$05 + sec + rts +!: + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Check for Control Port #1 Activity + + stx $dc00 // Disconnect all Keyboard Rows + cpx $dc01 // Only Control Port activity will be detected + bne ControlPort + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Scan Keyboard Matrix + + lda #%11111110 + sta $dc00 + ldy $dc01 + sty ScanResult+7 + sec + .for (var i = 6 ; i > -1 ; i--) { + rol + sta $dc00 + ldy $dc01 + sty ScanResult+i + } + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Check for Control Port #1 Activity (again) + + stx $dc00 // Disconnect all Keyboard Rows + cpx $dc01 // Only Control Port activity will be detected + bne ControlPort + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Initialize Buffer, Flags and Max Keys + + // Reset current read buffer + stx BufferNew + stx BufferNew+1 + stx BufferNew+2 + + // Reset Non-AlphaNumeric Flag + inx + stx NonAlphaFlagY + + // Set max keys allowed before ignoring result + lda #MaxKeyRollover + sta KeyQuantity + + // Counter to check for simultaneous alphanumeric key-presses + lda #$fe + sta SimultaneousKeys + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Check and flag Non Alphanumeric Keys + + lda ScanResult+6 + eor #$ff + and #%10000000 // Left Shift + lsr + sta NonAlphaFlagY + lda ScanResult+0 + eor #$ff + and #%10100100 // RUN STOP - C= - CTRL + ora NonAlphaFlagY + sta NonAlphaFlagY + lda ScanResult+1 + eor #$ff + and #%00011000 // Right SHIFT - CLR HOME + ora NonAlphaFlagY + sta NonAlphaFlagY + + lda ScanResult+7 // The rest + eor #$ff + sta NonAlphaFlagX + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Check for pressed key(s) + + lda ScanResult+7 + cmp #$ff + beq *+5 + jsr KeyInRow + .for (var i = 6 ; i > -1 ; i--) { + ldx #[7-i]*8 + lda ScanResult+i + beq *+5 + jsr KeyInRow + } + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Key Scan Completed + + // Put any new key (not in old scan) into buffer + ldx #MaxKeyRollover-1 + !: lda BufferNew,x + cmp #$ff + beq Exist // Handle 'null' values + cmp BufferOld + beq Exist + cmp BufferOld+1 + beq Exist + cmp BufferOld+2 + beq Exist + // New Key Detected + inc BufferQuantity + ldy BufferQuantity + sta Buffer,y + // Keep track of how many new Alphanumeric keys are detected + inc SimultaneousKeys + beq TooManyNewKeys + Exist: + dex + bpl !- + + // Anything in Buffer? + ldy BufferQuantity + bmi BufferEmpty + // Yes: Then return it and tidy up the buffer + dec BufferQuantity + lda Buffer + ldx Buffer+1 + stx Buffer + ldx Buffer+2 + stx Buffer+1 + jmp Return + +BufferEmpty: // No new Alphanumeric keys to handle. + lda #$ff + +Return: // A is preset + clc + // Copy BufferNew to BufferOld + ldx BufferNew + stx BufferOld + ldx BufferNew+1 + stx BufferOld+1 + ldx BufferNew+2 + stx BufferOld+2 + // Handle Non Alphanumeric Keys + ldx NonAlphaFlagX + ldy NonAlphaFlagY + rts + +TooManyNewKeys: + sec + lda #$ff + sta BufferQuantity + sta SimultaneousAlphanumericKeysFlag + lda #$04 + rts + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +KeyTable: + .byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff // CRSR DOWN, F5, F3, F1, F7, CRSR RIGHT, RETURN, INST DEL + .byte $ff, $05, $13, $1a, $34, $01, $17, $33 // LEFT SHIFT, "E", "S", "Z", "4", "A", "W", "3" + .byte $18, $14, $06, $03, $36, $04, $12, $35 // "X", "T", "F", "C", "6", "D", "R", "5" + .byte $16, $15, $08, $02, $38, $07, $19, $37 // "V", "U", "H", "B", "8", "G", "Y", "7" + .byte $0e, $0f, $0b, $0d, $30, $0a, $09, $39 // "N", "O" (Oscar), "K", "M", "0" (Zero), "J", "I", "9" + .byte $2c, $00, $3a, $2e, $2d, $0c, $10, $2b // ",", "@", ":", ".", "-", "L", "P", "+" + .byte $2f, $1e, $3d, $ff, $ff, $3b, $2a, $1c // "/", "^", "=", RIGHT SHIFT, HOME, ";", "*", "£" + .byte $ff, $11, $ff, $20, $32, $ff, $1f, $31 // RUN STOP, "Q", "C=" (CMD), " " (SPC), "2", "CTRL", "<-", "1" + +BufferOld: + .byte $ff, $ff, $ff + +Buffer: + .byte $ff, $ff, $ff, $ff + +BufferQuantity: + .byte $ff + +SimultaneousAlphanumericKeysFlag: + .byte $00 +} \ No newline at end of file diff --git a/hex.asm b/hex.asm new file mode 100644 index 0000000..d4095ac --- /dev/null +++ b/hex.asm @@ -0,0 +1,35 @@ +#importonce +#import "mem_map.asm" + + +// ———————————————————————————————————— +// btohex +// ———————————————————————————————————— +// ———————————————————————————————————— +// preparatory ops: .a: byte to convert +// +// returned values: .a: msn ascii char +// .x: lsn ascii char +// .y: entry value +// ———————————————————————————————————— +binhex: pha //save byte + and #%00001111 //extract lsn + tax //save it + pla //recover byte + lsr //extract... + lsr //msn + lsr + lsr + pha //save msn + txa //lsn + jsr binhex1 //generate ascii lsn + tax //save + pla //get msn & fall thru +// +// convert nybble to hex ascii equivalent... +binhex1: cmp #$0a + bcc binhex2 //in decimal range + adc #$66 //hex compensate +binhex2: eor #%00110000 //finalize nybble + rts //done + diff --git a/main.asm b/main.asm new file mode 100644 index 0000000..c8d2810 --- /dev/null +++ b/main.asm @@ -0,0 +1,74 @@ +//BasicUpstart2(start) + +* = $8000 "Main" + +// Constants +.namespace constants { + .label MainColor = $05 + .label BorderColor = $03 + .label ScreenMem = $0400 +} + +.word coldstart // coldstart vector +.word start // coldstart vector + +.byte $C3 +.byte $C2 +.byte $CD +.byte $38 +.byte $30 + + +coldstart: + sei + stx $d016 + jsr $fda3 // Prepare IRQ + jsr $fd50 // Init memory. Rewrite this routine to speed up boot process. + jsr $fd15 // Init I/O + jsr $ff5b // Init video + cli +/* + jsr $E453 + jsr $E3BF + jsr $E422 + ldx #$FB +*/ + //tsx + +start: + jsr initApp; + print(testString) + .for(var i=0; i<23; i++) { + print(testString2) + } +deadLoop: jmp deadLoop + + +initApp: { + ClearColorRam($00) + ClearScreen(constants.ScreenMem, ' ') + SetBorderColor(constants.MainColor) + SetBackgroundColor(constants.BorderColor) + jsr Screen.init + rts +} + +#import "screen.asm" + + +.encoding "screencode_mixed" +testString: + .text "fantastic job 1.0 by =stid=" + .byte 13 + .byte 13 + .byte 0 + +testString2: + .text ".======================================." + .byte 13 + .byte 0 + +* = $9FFF "EpromFiller" + .byte 0 + + diff --git a/math.asm b/math.asm new file mode 100644 index 0000000..7e04493 --- /dev/null +++ b/math.asm @@ -0,0 +1,29 @@ +#importonce +.filenamespace Math + +#import "mem_map.asm" + +multiply: { + stx MemMap.MATH_SPACE.multiTmpX + pha + lda #$00 + ldx #$08 + clc + m0: bcc.r m1 + clc + adc MemMap.MATH_SPACE.factor2 + m1: ror + ror MemMap.MATH_SPACE.factor1 + dex + bpl.r m0 + ldx MemMap.MATH_SPACE.factor1 + + sta MemMap.MATH_SPACE.result + stx MemMap.MATH_SPACE.result+1 + + pla + ldx MemMap.MATH_SPACE.multiTmpX + + rts + +} \ No newline at end of file diff --git a/mem_map.asm b/mem_map.asm new file mode 100644 index 0000000..55bd12e --- /dev/null +++ b/mem_map.asm @@ -0,0 +1,20 @@ +.filenamespace MemMap +#importonce + +.const base = $10 + + +.namespace SCREEN_SPACE { + .label TempVideoPointer = base // W + .label TempStringPointer = base+2 // W + .label CursorCol = base+4 + .label CursorRow = base+5 + .label tempY = base+6 +} + +.namespace MATH_SPACE { + .label factor1 = base+7 + .label factor2 = base+8 + .label multiTmpX = base+9 + .label result = base+10 // W +} \ No newline at end of file diff --git a/screen.asm b/screen.asm new file mode 100644 index 0000000..3103284 --- /dev/null +++ b/screen.asm @@ -0,0 +1,181 @@ +#importonce +#import "math.asm" +#import "mem_map.asm" + +// ----------------------- +// MACROS +// ----------------------- + +.macro print(stringAddr) { + lda #stringAddr // High byte + jsr Screen.print +} + +.macro ClearScreen(screen, clearByte) { + lda #clearByte + ldx #0 +!loop: + sta screen, x + sta screen + $100, x + sta screen + $200, x + sta screen + $300, x + inx + bne !loop- +} + +.macro ClearColorRam(clearByte) { + lda #clearByte + ldx #0 +!loop: + sta $D800, x + sta $D800 + $100, x + sta $D800 + $200, x + sta $D800 + $300, x + inx + bne !loop- +} + +.macro SetBorderColor(color) { + lda #color + sta $d020 +} + +.macro SetBackgroundColor(color) { + lda #color + sta $d021 +} + +.macro SetMultiColor1(color) { + lda #color + sta $d022 +} + +.macro SetMultiColor2(color) { + lda #color + sta $d023 +} + +.macro SetMultiColorMode() { + lda $d016 + ora #16 + sta $d016 +} + +.macro SetScrollMode() { + lda $D016 + eor #%00001000 + sta $D016 +} + +.filenamespace Screen + + +// ----------------------- +// CONSTANTS +// ----------------------- + +.namespace constants { + .label VIDEO_ADDR = $0400 + .label COLUMN_NUM = 40 + .label ROWS_NUM = 25 +} + + +// ----------------------- +// CODE +// ----------------------- + +init: { + lda #$00 + sta MemMap.SCREEN_SPACE.CursorCol + sta MemMap.SCREEN_SPACE.CursorRow + rts +} + +printChar: { + + // New Line + cmp #13 + bne.r !+ + jsr screenNewLine + iny + rts +!: + // Store Base Video Address 16 bit + ldx #constants.VIDEO_ADDR // High byte + stx MemMap.SCREEN_SPACE.TempVideoPointer+1 + + // Temp Save Y + sty MemMap.SCREEN_SPACE.tempY + + // CursorRow * 40 + ldy MemMap.SCREEN_SPACE.CursorRow + sty MemMap.MATH_SPACE.factor1 + ldy #constants.COLUMN_NUM + sty MemMap.MATH_SPACE.factor2 + jsr Math.multiply + + // Add mul result to TempVideoPointer + clc + pha + lda MemMap.MATH_SPACE.result + adc MemMap.SCREEN_SPACE.TempVideoPointer+1 + sta MemMap.SCREEN_SPACE.TempVideoPointer+1 + lda MemMap.MATH_SPACE.result+1 + adc MemMap.SCREEN_SPACE.TempVideoPointer + sta MemMap.SCREEN_SPACE.TempVideoPointer + pla + + // Add column + ldy MemMap.SCREEN_SPACE.CursorCol + sta (MemMap.SCREEN_SPACE.TempVideoPointer), y + + ldy MemMap.SCREEN_SPACE.tempY + iny + + inc MemMap.SCREEN_SPACE.CursorCol + lda MemMap.SCREEN_SPACE.CursorCol + cmp #constants.COLUMN_NUM+1 + bcc exita + + // CursorCol > COLUMN_NUM ? new line + jsr screenNewLine +exita: + rts + +} + + +// —————————————————————————————————————————————————————— +// print +// —————————————————————————————————————————————————————— +// —————————————————————————————————————————————————————— +// preparatory ops: .a: low byte string address +// .x: high byte string address +// +// returned values: none +// —————————————————————————————————————————————————————— +print: { + ldy #$00 + sta MemMap.SCREEN_SPACE.TempStringPointer + stx MemMap.SCREEN_SPACE.TempStringPointer+1 + printLoop: + lda (MemMap.SCREEN_SPACE.TempStringPointer), y + cmp #0 + beq exit + jsr Screen.printChar + jmp printLoop + exit: + rts +} + +screenNewLine: { + lda #0 + sta MemMap.SCREEN_SPACE.CursorCol + inc MemMap.SCREEN_SPACE.CursorRow + rts +} +