1
0
mirror of https://github.com/stid/woz64.git synced 2024-11-25 15:33:34 +00:00

Scroll Progress

This commit is contained in:
stid 2019-11-17 19:28:54 -08:00
parent 04c83fd5a2
commit 18f5c005c2
6 changed files with 274 additions and 231 deletions

View File

@ -10,7 +10,7 @@
.const cSYS_DelayValue = 32 .const cSYS_DelayValue = 32
.const cKeybW_Row1 = $FE .const cKeybW_Row1 = $FE
init: init: {
lda #64 lda #64
sta MemMap.KEYB2.SYS_Lstx sta MemMap.KEYB2.SYS_Lstx
sta MemMap.KEYB2.SYS_Sfdx sta MemMap.KEYB2.SYS_Sfdx
@ -31,24 +31,9 @@ init:
sta MemMap.KEYB2.SYS_Xmax sta MemMap.KEYB2.SYS_Xmax
// CLODE TO RAM // CLODE TO RAM
lda #<cloneStart clone(cloneStart, cloneEnd, $1000)
sta MemMap.MEMORY.from
lda #>cloneStart
sta MemMap.MEMORY.from+1
lda #<$1000
sta MemMap.MEMORY.to
lda #>$1000
sta MemMap.MEMORY.to+1
lda #<cloneEnd-ReadKeyb
sta MemMap.MEMORY.size+1
lda #>cloneEnd-ReadKeyb
sta MemMap.MEMORY.size
jsr Memory.clone
rts rts
}
KeyMapVec: KeyMapVec:
.word KeyMap1, KeyMap2, KeyMap3, KeyMap4 .word KeyMap1, KeyMap2, KeyMap3, KeyMap4
@ -259,8 +244,10 @@ GetKey: lda MemMap.KEYB2.SYS_Ndx
tya tya
clc clc
rts rts
}
* = * "Keyb: cloneEnd"
cloneEnd: cloneEnd:
}

View File

@ -1,4 +1,4 @@
///BasicUpstart2(start) BasicUpstart2(start)
* = $8000 "Main" * = $8000 "Main"
@ -54,9 +54,8 @@ Raster: cmp $D012
jsr Screen.screenNewLine jsr Screen.screenNewLine
jsr Shell.clear jsr Shell.clear
jmp loop jmp loop
inputChar: inputChar:
jsr Shell.push jsr Shell.push
jsr Screen.petToScreen
cPrint() cPrint()

View File

@ -11,14 +11,16 @@
.label CursorRow = ZPAGE_BASE+5 // 1 byte .label CursorRow = ZPAGE_BASE+5 // 1 byte
.label tempY = ZPAGE_BASE+6 // 1 byte .label tempY = ZPAGE_BASE+6 // 1 byte
.label tempX = ZPAGE_BASE+7 // 1 byte .label tempX = ZPAGE_BASE+7 // 1 byte
.label cTempY = ZPAGE_BASE+8 // 1 byte .label PrintPetCharY = ZPAGE_BASE+8 // 1 byte
.label PrintPetCharX = ZPAGE_BASE+9 // 1 byte
.label ScrollUpTriggered = ZPAGE_BASE+10 // 1 byte
} }
.namespace MATH { .namespace MATH {
.label factor1 = ZPAGE_BASE+9 // 1 byte .label factor1 = ZPAGE_BASE+11 // 1 byte
.label factor2 = ZPAGE_BASE+10 // 1 byte .label factor2 = ZPAGE_BASE+12 // 1 byte
.label multiTmpX = ZPAGE_BASE+11 // 1 byte .label multiTmpX = ZPAGE_BASE+13 // 1 byte
.label result = ZPAGE_BASE+12 // 2 bytes .label result = ZPAGE_BASE+14 // 2 bytes
} }
.namespace KEYB2 { .namespace KEYB2 {
@ -36,7 +38,7 @@
.namespace MEMORY { .namespace MEMORY {
.label from = ZPAGE_BASE+56 // 2 bytes .label from = ZPAGE_BASE+56 // 2 bytes
.label to = ZPAGE_BASE+58 // 2 bytes .label dest = ZPAGE_BASE+58 // 2 bytes
.label size = ZPAGE_BASE+60 // 2 bytes .label size = ZPAGE_BASE+60 // 2 bytes
} }
@ -46,8 +48,8 @@
.label L = ZPAGE_BASE+64 // 1 bytes .label L = ZPAGE_BASE+64 // 1 bytes
.label H = ZPAGE_BASE+65 // 1 bytes .label H = ZPAGE_BASE+65 // 1 bytes
.label YSAV = ZPAGE_BASE+66 // 1 bytes .label YSAV = ZPAGE_BASE+66 // 1 bytes
.label STL = ZPAGE_BASE+67 // 1 bytes .label STL = ZPAGE_BASE+67 // 1 bytes
.label STH = ZPAGE_BASE+68 // 1 bytes .label STH = ZPAGE_BASE+68 // 1 bytes
.label XAML = ZPAGE_BASE+69 // 1 bytes .label XAML = ZPAGE_BASE+69 // 1 bytes
.label XAMH = ZPAGE_BASE+70 // 1 bytes .label XAMH = ZPAGE_BASE+70 // 1 bytes

View File

@ -1,33 +1,55 @@
#importonce #importonce
.filenamespace Memory
#import "mem_map.asm" #import "mem_map.asm"
* = * "Memory Routines" * = * "Memory Routines"
.macro clone(from, to, dest) {
lda #<from
sta MemMap.MEMORY.from
lda #>from
sta MemMap.MEMORY.from+1
lda #<dest
sta MemMap.MEMORY.dest
lda #>dest
sta MemMap.MEMORY.dest+1
lda #<to-from
sta MemMap.MEMORY.size+1
lda #>to-from
sta MemMap.MEMORY.size
jsr Memory._clone
}
.filenamespace Memory
// move memory down // move memory down
// //
// from = source start address // from = source start address
// to = destination start address // to = destination start address
// size = number of bytes to move // size = number of bytes to move
// //
clone: _clone: {
ldy #0 ldy #0
ldx MemMap.MEMORY.size ldx MemMap.MEMORY.size
beq md2 beq md2
md1: lda (MemMap.MEMORY.from),y // move a page at a time md1: lda (MemMap.MEMORY.from),y // move a page at a time
sta (MemMap.MEMORY.to),y sta (MemMap.MEMORY.dest),y
iny iny
bne md1 bne md1
inc MemMap.MEMORY.from+1 inc MemMap.MEMORY.from+1
inc MemMap.MEMORY.to+1 inc MemMap.MEMORY.dest+1
dex dex
bne md1 bne md1
md2: ldx MemMap.MEMORY.size+1 md2: ldx MemMap.MEMORY.size+1
beq md4 beq md4
md3: lda (MemMap.MEMORY.from),y // move the remaining bytes md3: lda (MemMap.MEMORY.from),y // move the remaining bytes
sta (MemMap.MEMORY.to),y sta (MemMap.MEMORY.dest),y
iny iny
dex dex
bne md3 bne md3
md4: rts md4: rts
}

View File

@ -1,6 +1,7 @@
#importonce #importonce
#import "math.asm" #import "math.asm"
#import "mem_map.asm" #import "mem_map.asm"
#import "memory.asm"
// ----------------------- // -----------------------
// MACROS // MACROS
@ -15,53 +16,51 @@
} }
.macro cPrint() { .macro cPrint() {
sty MemMap.SCREEN.cTempY jsr Screen.printPetChar
jsr Screen.printChar
ldy MemMap.SCREEN.cTempY
} }
.macro ClearScreen(screen, clearByte) { .macro ClearScreen(screen, clearByte) {
lda #clearByte lda #clearByte
ldx #0 ldx #0
!loop: !loop:
sta screen, x sta screen, x
sta screen + $100, x sta screen + $100, x
sta screen + $200, x sta screen + $200, x
sta screen + $300, x sta screen + $300, x
inx inx
bne.r !loop- bne.r !loop-
} }
.macro ClearColorRam(clearByte) { .macro ClearColorRam(clearByte) {
lda #clearByte lda #clearByte
ldx #0 ldx #0
!loop: !loop:
sta $D800, x sta $D800, x
sta $D800 + $100, x sta $D800 + $100, x
sta $D800 + $200, x sta $D800 + $200, x
sta $D800 + $300, x sta $D800 + $300, x
inx inx
bne.r !loop- bne.r !loop-
} }
.macro SetBorderColor(color) { .macro SetBorderColor(color) {
lda #color lda #color
sta $d020 sta $d020
} }
.macro SetBackgroundColor(color) { .macro SetBackgroundColor(color) {
lda #color lda #color
sta $d021 sta $d021
} }
.macro SetMultiColor1(color) { .macro SetMultiColor1(color) {
lda #color lda #color
sta $d022 sta $d022
} }
.macro SetMultiColor2(color) { .macro SetMultiColor2(color) {
lda #color lda #color
sta $d023 sta $d023
} }
.macro SetMultiColorMode() { .macro SetMultiColorMode() {
@ -71,9 +70,9 @@
} }
.macro SetScrollMode() { .macro SetScrollMode() {
lda $D016 lda $D016
eor #%00001000 eor #%00001000
sta $D016 sta $D016
} }
.filenamespace Screen .filenamespace Screen
@ -83,11 +82,9 @@
// CONSTANTS // CONSTANTS
// ----------------------- // -----------------------
.namespace constants { .const VIDEO_ADDR = $0400
.label VIDEO_ADDR = $0400 .const COLUMN_NUM = 40
.label COLUMN_NUM = 40 .const ROWS_NUM = 25
.label ROWS_NUM = 25
}
// ----------------------- // -----------------------
@ -95,66 +92,107 @@
// ----------------------- // -----------------------
init: { init: {
lda #$00 lda #$00
sta MemMap.SCREEN.CursorCol sta MemMap.SCREEN.CursorCol
sta MemMap.SCREEN.CursorRow sta MemMap.SCREEN.CursorRow
rts rts
}
scrollUp: {
pha
clone(VIDEO_ADDR+40, VIDEO_ADDR+(COLUMN_NUM*(ROWS_NUM)), VIDEO_ADDR)
lda #32
ldx #00
!:
sta VIDEO_ADDR+(COLUMN_NUM*(ROWS_NUM-1)), x
inx
cpx #40
bne !-
dec MemMap.SCREEN.CursorRow
pla
rts
}
printPetChar: {
pha
stx MemMap.SCREEN.PrintPetCharX
sty MemMap.SCREEN.PrintPetCharY
jsr Screen.petToScreen
jsr Screen.printChar
ldy MemMap.SCREEN.PrintPetCharY
ldx MemMap.SCREEN.PrintPetCharX
pla
rts
} }
printChar: { printChar: {
stx MemMap.SCREEN.tempX stx MemMap.SCREEN.tempX
// New Line // New Line
cmp #$8e cmp #$8e
bne.r !+ bne.r !+
jsr screenNewLine jsr screenNewLine
iny iny
rts rts
!: !:
// Store Base Video Address 16 bit // Store Base Video Address 16 bit
ldx #<constants.VIDEO_ADDR // Low byte ldx #<VIDEO_ADDR // Low byte
stx MemMap.SCREEN.TempVideoPointer stx MemMap.SCREEN.TempVideoPointer
ldx #>constants.VIDEO_ADDR // High byte ldx #>VIDEO_ADDR // High byte
stx MemMap.SCREEN.TempVideoPointer+1 stx MemMap.SCREEN.TempVideoPointer+1
// Temp Save Y // Temp Save Y
sty MemMap.SCREEN.tempY sty MemMap.SCREEN.tempY
// CursorRow * 40 // CursorRow * 40
ldy MemMap.SCREEN.CursorRow ldy MemMap.SCREEN.CursorRow
sty MemMap.MATH.factor1 sty MemMap.MATH.factor1
ldy #constants.COLUMN_NUM ldy #COLUMN_NUM
sty MemMap.MATH.factor2 sty MemMap.MATH.factor2
jsr Math.multiply jsr Math.multiply
// Add mul result to TempVideoPointer // Add mul result to TempVideoPointer
clc clc
pha pha
lda MemMap.MATH.result lda MemMap.MATH.result
adc MemMap.SCREEN.TempVideoPointer+1 adc MemMap.SCREEN.TempVideoPointer+1
sta MemMap.SCREEN.TempVideoPointer+1 sta MemMap.SCREEN.TempVideoPointer+1
lda MemMap.MATH.result+1 lda MemMap.MATH.result+1
adc MemMap.SCREEN.TempVideoPointer adc MemMap.SCREEN.TempVideoPointer
sta MemMap.SCREEN.TempVideoPointer sta MemMap.SCREEN.TempVideoPointer
ldy MemMap.SCREEN.CursorCol
cpy #COLUMN_NUM // Is this > col num?
bcc.r noEndOfLine
jsr screenNewLine // Yes? Add new list first
ldy #1
cpy MemMap.SCREEN.ScrollUpTriggered
bne noScrollTriggered
.break
// Compesat Scroll
sec
lda MemMap.SCREEN.TempVideoPointer
sbc #1
sta MemMap.SCREEN.TempVideoPointer
bcs !+
dec MemMap.SCREEN.TempVideoPointer+1
!:
noScrollTriggered:
noEndOfLine:
pla pla
// Add column // insert into screen
ldy MemMap.SCREEN.CursorCol sta (MemMap.SCREEN.TempVideoPointer), y
sta (MemMap.SCREEN.TempVideoPointer), y ldy MemMap.SCREEN.tempY
ldy MemMap.SCREEN.tempY
iny iny
inc MemMap.SCREEN.CursorCol
inc MemMap.SCREEN.CursorCol exit:
lda MemMap.SCREEN.CursorCol ldx MemMap.SCREEN.tempX
cmp #constants.COLUMN_NUM+1
bcc.r exita
// CursorCol > COLUMN_NUM ? new line
jsr screenNewLine
exita:
ldx MemMap.SCREEN.tempX
rts rts
} }
@ -168,23 +206,37 @@ exita:
// returned values: none // returned values: none
// //
print: { print: {
ldy #$00 ldy #$00
sta MemMap.SCREEN.TempStringPointer sta MemMap.SCREEN.TempStringPointer
stx MemMap.SCREEN.TempStringPointer+1 stx MemMap.SCREEN.TempStringPointer+1
printLoop: printLoop:
lda (MemMap.SCREEN.TempStringPointer), y lda (MemMap.SCREEN.TempStringPointer), y
cmp #0 cmp #0
beq exit beq exit
jsr Screen.printChar jsr Screen.printChar
jmp printLoop jmp printLoop
exit: exit:
rts rts
} }
screenNewLine: { screenNewLine: {
lda #0 pha
sta MemMap.SCREEN.CursorCol lda #0
inc MemMap.SCREEN.CursorRow sta MemMap.SCREEN.CursorCol
lda #ROWS_NUM-1
cmp MemMap.SCREEN.CursorRow // Are we at the screen bottom?
bne noScrollUp
jsr Screen.scrollUp
lda #1 // Yes - Scroll up
sta MemMap.SCREEN.ScrollUpTriggered
jmp done
noScrollUp:
lda #0
sta MemMap.SCREEN.ScrollUpTriggered
done:
inc MemMap.SCREEN.CursorRow
pla
rts rts
} }
@ -199,65 +251,55 @@ screenNewLine: {
// //
petToScreen: { petToScreen: {
// $00-$1F // $00-$1F
cmp #$1f cmp #$1f
bcs !+ bcs !+
sec sec
adc #128 adc #128
jmp convDone jmp convDone
// $20-$3F // $20-$3F
!: !:
cmp #$3f cmp #$3f
bcs !+ bcs !+
jmp convDone jmp convDone
// $40-$5F // $40-$5F
!: !:
cmp #$5f cmp #$5f
bcs !+ bcs !+
sec sec
sbc #$40 sbc #$40
jmp convDone jmp convDone
// $60-$7F // $60-$7F
!: !:
cmp #$7F cmp #$7F
bcs !+ bcs !+
sec sec
sbc #32 sbc #32
jmp convDone jmp convDone
// $80-$9F // $80-$9F
!: !:
cmp #$9F cmp #$9F
bcs !+ bcs !+
sec sec
adc #64 adc #64
jmp convDone jmp convDone
// $A0-$BF // $A0-$BF
!: !:
cmp #$BF cmp #$BF
bcs !+ bcs !+
sec sec
sbc #64 sbc #64
jmp convDone jmp convDone
// $C0-$DF // $C0-$DF
// $E0-$FE // $E0-$FE
!: !:
cmp #$FE cmp #$FE
bcs !+ bcs !+
sec sec
sbc #128 sbc #128
jmp convDone jmp convDone
// $FF // $FF
!: !:
lda $5E lda $5E
convDone: convDone:
rts rts
} }

127
shell.asm
View File

@ -8,7 +8,7 @@
.const CR = $0d .const CR = $0d
clear:
init: { init: {
lda #-1 lda #-1
sta MemMap.SHELL.pos sta MemMap.SHELL.pos
@ -16,45 +16,37 @@ init: {
} }
push: { push: {
ldy MemMap.SHELL.pos ldy MemMap.SHELL.pos
iny iny
cpy #127 cpy #127
beq done beq.r done
sty MemMap.SHELL.pos sty MemMap.SHELL.pos
sta MemMap.SHELL.buffer, y sta MemMap.SHELL.buffer, y
done: done:
rts rts
} }
clear: {
ldy #-1
sty MemMap.SHELL.pos
rts
}
// WOZ MONITOR FLOW - FROM APPLE1 // WOZ MONITOR FLOW - FROM APPLE1
wozExec: { wozExec: {
ldy #-1 ldy #-1
lda #0 lda #0
tax tax
SETSTOR: SETSTOR:
asl asl
SETMODE: SETMODE:
cmp #0 cmp #0
beq !+ beq.r !+
eor #%10000000 eor #%10000000
!: !:
sta MemMap.SHELL.MODE sta MemMap.SHELL.MODE
BLSKIP: iny BLSKIP: iny
NEXTITEM: NEXTITEM:
.break
lda MemMap.SHELL.buffer,Y //Get character lda MemMap.SHELL.buffer,Y //Get character
cmp #$0d cmp #CR
bne CONT // We're done if it's CR! bne.r CONT // We're done if it's CR!
rts rts
CONT: CONT:
cmp #'.' cmp #'.'
@ -70,8 +62,8 @@ wozExec: {
// Here we're trying to parse a new hex value // Here we're trying to parse a new hex value
NEXTHEX: NEXTHEX:
lda MemMap.SHELL.buffer,y // Get character for hex test lda MemMap.SHELL.buffer,y // Get character for hex test
eor #$30 // Map digits to 0-9 eor #$30 // Map digits to 0-9
cmp #9+1 // Is it a decimal digit? cmp #9+1 // Is it a decimal digit?
bcc DIG // Yes! bcc DIG // Yes!
@ -79,27 +71,27 @@ wozExec: {
cmp #$FA // Hex letter? cmp #$FA // Hex letter?
bcc NOTHEX // No! Character not hex bcc NOTHEX // No! Character not hex
DIG: DIG:
asl asl
asl // Hex digit to MSD of A asl // Hex digit to MSD of A
asl asl
asl asl
ldx #4 // Shift count ldx #4 // Shift count
HEXSHIFT: asl // Hex digit left, MSB to carry HEXSHIFT: asl // Hex digit left, MSB to carry
rol MemMap.SHELL.L // Rotate into LSD rol MemMap.SHELL.L // Rotate into LSD
rol MemMap.SHELL.H // Rotate into MSD's rol MemMap.SHELL.H // Rotate into MSD's
dex // Done 4 shifts? dex // Done 4 shifts?
bne HEXSHIFT // No, loop bne HEXSHIFT // No, loop
iny // Advance text index iny // Advance text index
bne NEXTHEX // Always taken bne NEXTHEX // Always taken
NOTHEX: cpy MemMap.SHELL.YSAV //Was at least 1 hex digit given? NOTHEX: cpy MemMap.SHELL.YSAV //Was at least 1 hex digit given?
bne !+ // No! Ignore all, start from scratch bne !+ // No! Ignore all, start from scratch
rts rts
!: !:
bit MemMap.SHELL.MODE //Test MODE byte bit MemMap.SHELL.MODE //Test MODE byte
bvc NOTSTOR // B6=0 is STOR, 1 is XAM or BLOCK XAM bvc NOTSTOR // B6=0 is STOR, 1 is XAM or BLOCK XAM
// STOR mode, save LSD of new hex byte // STOR mode, save LSD of new hex byte
@ -120,59 +112,59 @@ RUN: jmp (MemMap.SHELL.XAML) // Run user's program
// We're not in Store mode // We're not in Store mode
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
NOTSTOR: bmi XAMNEXT // B7 = 0 for XAM, 1 for BLOCK XAM NOTSTOR: bmi XAMNEXT // B7 = 0 for XAM, 1 for BLOCK XAM
// We're in XAM mode now // We're in XAM mode now
ldx #2 // Copy 2 bytes ldx #2 // Copy 2 bytes
SETADR: lda MemMap.SHELL.L-1,X // Copy hex data to SETADR: lda MemMap.SHELL.L-1,X // Copy hex data to
sta MemMap.SHELL.STL-1,X // 'store index' sta MemMap.SHELL.STL-1,X // 'store index'
sta MemMap.SHELL.XAML-1,X // and to 'XAM index' sta MemMap.SHELL.XAML-1,X // and to 'XAM index'
dex // Next of 2 bytes dex // Next of 2 bytes
bne SETADR // Loop unless X = 0 bne SETADR // Loop unless X = 0
// Print address and data from this address, fall through next BNE. // Print address and data from this address, fall through next BNE.
NXTPRNT: bne PRDATA // NE means no address to print NXTPRNT: bne PRDATA // NE means no address to print
lda #$8e // Print CR first lda #CR // Print CR first
cPrint() cPrint()
lda MemMap.SHELL.XAMH // Output high-order byte of address lda MemMap.SHELL.XAMH // Output high-order byte of address
jsr PRBYTE jsr PRBYTE
lda MemMap.SHELL.XAML // Output low-order byte of address lda MemMap.SHELL.XAML // Output low-order byte of address
jsr PRBYTE jsr PRBYTE
lda #':' // Print colon lda #':' // Print colon
cPrint() cPrint()
PRDATA: lda #' ' // Print space PRDATA: lda #' ' // Print space
cPrint() cPrint()
lda (MemMap.SHELL.XAML,X) // Get data from address (X=0) lda (MemMap.SHELL.XAML,X) // Get data from address (X=0)
jsr PRBYTE // Output it in hex format jsr PRBYTE // Output it in hex format
XAMNEXT: stx MemMap.SHELL.MODE // 0 -> MODE (XAM mode). XAMNEXT: stx MemMap.SHELL.MODE // 0 -> MODE (XAM mode).
lda MemMap.SHELL.XAML // See if there's more to print lda MemMap.SHELL.XAML // See if there's more to print
cmp MemMap.SHELL.L cmp MemMap.SHELL.L
lda MemMap.SHELL.XAMH lda MemMap.SHELL.XAMH
sbc MemMap.SHELL.H sbc MemMap.SHELL.H
bcs TONEXTITEM // Not less! No more data to output bcs TONEXTITEM // Not less! No more data to output
inc MemMap.SHELL.XAML // Increment 'examine index' inc MemMap.SHELL.XAML // Increment 'examine index'
bne MOD8CHK // No carry! bne MOD8CHK // No carry!
inc MemMap.SHELL.XAMH inc MemMap.SHELL.XAMH
MOD8CHK: lda MemMap.SHELL.XAML // If address MOD 8 = 0 start new line MOD8CHK: lda MemMap.SHELL.XAML // If address MOD 8 = 0 start new line
and #%00000111 and #%00000111
bpl NXTPRNT // Always taken. bpl NXTPRNT // Always taken.
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Subroutine to print a byte in A in hex form (destructive) // Subroutine to print a byte in A in hex form (destructive)
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
PRBYTE: pha // Save A for LSD PRBYTE: pha // Save A for LSD
lsr lsr
lsr lsr
lsr // MSD to LSD position lsr // MSD to LSD position
lsr lsr
jsr PRHEX // Output hex digit jsr PRHEX // Output hex digit
pla // Restore A pla // Restore A
// Fall through to print hex routine // Fall through to print hex routine
@ -180,12 +172,11 @@ PRBYTE: pha // Save A for LSD
// Subroutine to print a hexadecimal digit // Subroutine to print a hexadecimal digit
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
PRHEX: and #%00001111 // Mask LSD for hex print PRHEX: and #%00001111 // Mask LSD for hex print
ora #'0' // Add "0" ora #'0' // Add "0"
cmp #'9'+1 // Is it a decimal digit? cmp #'9'+1 // Is it a decimal digit?
bcc !+ // Yes! output it bcc !+ // Yes! output it
adc #6 // Add offset for letter A-F adc #6 // Add offset for letter A-F
jsr Screen.petToScreen
!: !:
cPrint() cPrint()
rts rts