Align up assembly source columns

This commit is contained in:
Michaelangel007 2016-01-27 12:07:54 -08:00
parent 9ab55c2dfc
commit a90a4f0a72

515
README.md
View File

@ -1,6 +1,6 @@
#Apple ]\[ //e HGR Font 6502 Assembly Language Tutorial
Revision: 49, Jan 27, 2016.
Revision: 50, Jan 27, 2016.
# Table of Contents
@ -23,6 +23,7 @@ Revision: 49, Jan 27, 2016.
* X Cursor Position
* CursorCol()
* Introduction to Optimization
* Recap - Listing 5
* DrawChar() version 2
* DrawChar() version 3
* Character Inspector
@ -35,7 +36,7 @@ Revision: 49, Jan 27, 2016.
* Exercises
* Exercise 1: ScrollHgrUpLine()
* Exercise 2: ScrollHgrUpPixel()
* Recap
* Recap - Font Draw
* Other Fonts
* Fat Stroke Fonts
* Beautiful Boot
@ -705,23 +706,25 @@ Before we can start a simple `DrawChar(char c)` function, we also first need to
Here's the disassembly of our (hard-coded) DrawChar() program:
Listing Demo 1:
```assembly
; FUNC: PrintChar()
; NOTES: A, X, Y is destroyed
ORG $0900
0900: PrintChar
0900:20 0A 09 JSR HgrToTmpPtr
0903:A9 00 LDA #00 ; glyph 'c' to draw (not used yet)
0905:A0 00 LDY #00 ; Y = column to draw at (hard-coded)
0907:4C 10 03 JMP DrawChar
ORG $0900
0900: PrintChar
0900:20 0A 09 JSR HgrToTmpPtr
0903:A9 00 LDA #00 ; glyph 'c' to draw (not used yet)
0905:A0 00 LDY #00 ; Y = column to draw at (hard-coded)
0907:4C 10 03 JMP DrawChar
; FUNC: HgrToTmpPtr()
090A: HgrToTmpPtr
090A:A5 E5 LDA HgrLo ; Copy initial screen
090C:85 F5 STA TmpLo ; destination pointer
090E:A5 E6 LDA HgrHi ; to working pointer
0910:85 F6 STA TmpHi
0912:60 RTS
090A: HgrToTmpPtr
090A:A5 E5 LDA HgrLo ; Copy initial screen
090C:85 F5 STA TmpLo ; destination pointer
090E:A5 E6 LDA HgrHi ; to working pointer
0910:85 F6 STA TmpHi
0912:60 RTS
```
Listing 1:
@ -733,14 +736,14 @@ Listing 1:
; INPUT : $F5,$F6 pointer to the destination screen scanline
; Must start at every 8 scanlines.
; OUTPUT: The Y-Register (cursor column) is automatically incremented.
ORG $0310
0310: DrawChar
0310:4C 50 03 JMP _DrawChar
ORG $0310
0310: DrawChar
0310:4C 50 03 JMP _DrawChar
ORG $0350
0350: _DrawChar
0350: _DrawChar
0350:A2 00 LDX #0
0352: _LoadFont ; A = font[ offset ]
0352: _LoadFont ; A = font[ offset ]
0352:BD 00 62 LDA Font+$200,X
0355:91 F5 STA (TmpLo),Y ; screen[col] = A
0357:18 CLC
@ -832,7 +835,7 @@ Here's the C pseudo-code of the assembly code:
Since the Y-register controls the column we can inline this function and have the caller take care of setting the Y-Register before calling DrawChar().
```assmebly
```assembly
LDY #column
```
@ -851,14 +854,14 @@ Listing 2a:
; Increment the cursor column and move the destination screen pointer back
; up 8 scan lines previously to what it was when DrawChar() was called.
; Version 1
ORG $0364
0364: IncCursorCol1
0364:C8 INY
0365:18 CLC ; Note:
0366:A5 F6 LDA TmpHi ; (To the astute reader)
0368:E9 1F SBC #$1F ; <-- ??? Shouldn't this be #$20 ?!
036A:85 F6 STA TmpHi
036C:60 RTS
ORG $0364
0364: IncCursorCol1
0364:C8 INY
0365:18 CLC ; Note:
0366:A5 F6 LDA TmpHi ; (To the astute reader)
0368:E9 1F SBC #$1F ; <-- ??? Shouldn't this be #$20 ?!
036A:85 F6 STA TmpHi
036C:60 RTS
```
### Introduction to Optimization
@ -881,13 +884,13 @@ Listing 2b:
; up 8 scan lines previously to what it was when DrawChar() was called.
; Version 2
ORG $0364
0364: IncCursorCol2
0364:C8 INY
0365:38 SEC ; CLC SBC #1F
0366:A5 F6 LDA TmpHi ; was not obvious that we really
0368:E9 20 SBC #$20 ; meant: TmpHi = A - #$20 !!
036A:85 F6 STA TmpHi
036C:60 RTS
0364: IncCursorCol2
0364:C8 INY
0365:38 SEC ; CLC SBC #1F
0366:A5 F6 LDA TmpHi ; was not obvious that we really
0368:E9 20 SBC #$20 ; meant: TmpHi = A - #$20 !!
036A:85 F6 STA TmpHi
036C:60 RTS
```
One thing when writing 6502 assembly is to pay attention to _all_ optimization opportunities due to the slow ~1 MHz of the 6502. Since we only need to modify the upper few bits instead of doing a bulky subtraction `SEC SBC` we might be tempted to see if there is a faster and/or smaller alternative. We just need to be careful that our optimization is "shuffling" the bits around _behaves_ in the _exact_ same way at the end of the day. i.e. "The Right Place at the Right Time."
@ -944,13 +947,13 @@ Listing 3a:
; up 8 scan lines previously to what it was when DrawChar() was called.
; Version 3a
ORG $0364
0364: IncCursorCol3
0364:C8 INY
0365:A5 F6 LDA TmpHi
0367:29 1F AND #%00011111 ; Requires an extra OR
0369:09 20 ORA #$20 ; Hard-code to HGR page 1 (high byte)
036B:85 F6 STA TmpHi
036D:60 RTS
0364: IncCursorCol3
0364:C8 INY
0365:A5 F6 LDA TmpHi
0367:29 1F AND #%00011111 ; Requires an extra OR
0369:09 20 ORA #$20 ; Hard-code to HGR page 1 (high byte)
036B:85 F6 STA TmpHi
036D:60 RTS
```
@ -982,13 +985,13 @@ Listing 3b:
; up 8 scan lines previously to what it was when DrawChar() was called.
; Version 3b
ORG $0364
0364: IncCursorCol3
0364:C8 INY
0365:A5 F6 LDA TmpHi
0367:29 1F AND #%00011111 ; Requires an extra OR
0369:05 E6 ORA HgrHi ; user specified Page 1 or Page 2
036B:85 F6 STA TmpHi
036D:60 RTS
0364: IncCursorCol3
0364:C8 INY
0365:A5 F6 LDA TmpHi
0367:29 1F AND #%00011111 ; Requires an extra OR
0369:05 E6 ORA HgrHi ; user specified Page 1 or Page 2
036B:85 F6 STA TmpHi
036D:60 RTS
```
At least the timing for `ORA HgrHi` is still 2 clock cycles. :-)
@ -1019,11 +1022,11 @@ Listing 4a:
;35F:E0 08 CPX #8
;361:D0 EF BNE _LoadFont
;363:60 RTS
; ; === DrawChar end ===
; === DrawChar end ===
; FUNC: IncCursorCol()
ORG $0363 ; intentional extend _DrawChar by
0363: IncCursorCol ; over-writing RTS
0363: IncCursorCol ; over-writing RTS
0363:C8 INY
0364:A6 FD LDX TopHi ; Move cursor back to top of scanline
0366:86 F6 STX TmpHi
@ -1035,20 +1038,20 @@ We just need to touch up our entry point `PrintChar` at $0310 instead of calling
Listing 4b:
```assembly
ORG $0310
0310: DrawChar
0310:4C 4C 03 JMP _DrawChar1 ; NEW entry point
ORG $0310
0310: DrawChar
0310:4C 4C 03 JMP _DrawChar1 ; NEW entry point
```
Listing 5:
Listing Demo 2:
```assembly
ORG $0A00
0A00: DemoDraw3Char
0A00:20 00 09 JSR PrintChar
0A03:20 10 03 JSR DrawChar
0A06:20 10 03 JMP DrawChar
0A09:60 RTS
ORG $0A00
0A00: DemoDraw3Char
0A00:20 00 09 JSR PrintChar
0A03:20 10 03 JSR DrawChar
0A06:20 10 03 JMP DrawChar
0A09:60 RTS
```
Enter in:
@ -1078,7 +1081,7 @@ We are one step closer to printing a string. We have a total of 5 `@` because w
<hr>
# Recap - Listing 4
## Recap - Listing 5
Here is what our render glyph code looks like so far:
@ -1238,77 +1241,79 @@ IF this is confusing, remember, we are calculating a 16-bit offset:
A naive 6502 glyph/32 calculation would be to use 5 shift right bit-shifts:
```assembly
48 PHA ; save c
-- -- ; calc low byte offset
68 PLA ; pop c = %PQRstuvw to draw
4A LSR ; c / 2 = %0PQRstuv C=w
4A LSR ; c / 4 = %w0PQRstu C=V
4A LSR ; c / 8 = %wv0PQRst C=u
4A LSR ; c / 16 = %uvw0PQRs C=t
4A LSR ; c / 32 = %tUvw0PQR C=s
29 07 AND #07 ; = %00000PQR
18 CLC ;
48 PHA ; save c
-- -- ; calc low byte offset
68 PLA ; pop c = %PQRstuvw to draw
4A LSR ; c / 2 = %0PQRstuv C=w
4A LSR ; c / 4 = %w0PQRstu C=V
4A LSR ; c / 8 = %wv0PQRst C=u
4A LSR ; c / 16 = %uvw0PQRs C=t
4A LSR ; c / 32 = %tUvw0PQR C=s
29 07 AND #07 ; = %00000PQR
18 CLC ;
```
We can optimize the `CLC` out by clearing the bottom bits and _then_ doing the shift:
```assembly
48 PHA ; save c
-- -- ; calc low byte offset
68 PLA ; pop c = %PQRstuvw to draw
29 E0 AND #E0 ; = %PQR00000 s=0, Optimization: implicit CLC
4A LSR ; c / 2 = %0PQR0000
4A LSR ; c / 4 = %00PQR000
4A LSR ; c / 8 = %000PQR00
4A LSR ; c / 16 = %0000PQR0
4A LSR ; c / 32 = %00000PQR
48 PHA ; save c
-- -- ; calc low byte offset
68 PLA ; pop c = %PQRstuvw to draw
29 E0 AND #E0 ; = %PQR00000 s=0, Optimization: implicit CLC
4A LSR ; c / 2 = %0PQR0000
4A LSR ; c / 4 = %00PQR000
4A LSR ; c / 8 = %000PQR00
4A LSR ; c / 16 = %0000PQR0
4A LSR ; c / 32 = %00000PQR
```
However we can save one instruction (and 2 cycles) if we optimize `c/32` to use the counter-intuitive 6502's `ROL` instruction -- which only requires 4 instructions instead:
```assembly
48 PHA ; save c
-- -- ; calc low byte offset
68 PLA ; pop c = %PQRstuvw to draw
29 E0 AND #E0 ; = %PQR00000 s=0, Optimization: implicit CLC
2A ROL ; = %QR000000 C=P
2A ROL ; = %R000000P C=Q
2A ROL ; = %000000PQ C=R
2A ROL ; c / 32 = %00000PQR C=0
48 PHA ; save c
-- -- ; calc low byte offset
68 PLA ; pop c = %PQRstuvw to draw
29 E0 AND #E0 ; = %PQR00000 s=0, Optimization: implicit CLC
2A ROL ; = %QR000000 C=P
2A ROL ; = %R000000P C=Q
2A ROL ; = %000000PQ C=R
2A ROL ; c / 32 = %00000PQR C=0
```
Our prefix code to setup the source address becomes:
Listing 6:
```assembly
; FUNC: _DrawChar2a( c, col )
; PARAM: A = glyph to draw
; PARAM: Y = column to draw at; $0 .. $27 (Columns 0 .. 39) (not modified)
; NOTES: X is destroyed
ORG $0335
0335: _DrawChar2a
0335:48 PHA ; push c = %PQRstuvw to draw
0336:29 1F AND #1F ; = %000stuvw R=0, implicit CLC
0338:0A ASL ; c * 2 %00stuvw0
0339:0A ASL ; c * 4 %0stuvw00
033A:0A ASL ; c * 8 %stuvw000
033B:69 00 ADC #<Font ; += FontLo; Carry = 0 since R=0 from above
033D:8D 53 03 STA _LoadFont+1; AddressLo = FontLo + (c*8)
0340:68 PLA ; pop c = %PQRstuvw to draw
0341:29 E0 AND #E0 ; = %PQR00000 s=0, implicit CLC
0343:2A ROL ; = %QR000000 C=P
0344:2A ROL ; = %R000000P C=Q
0345:2A ROL ; = %000000PQ C=R need one more
0346:2A ROL ; c / 32 = %00000PQR C=0 shift to get R
0347:69 60 ADC #>Font ; += FontHi; Carry = 0 since S=0 from above
0349:8D 54 03 STA _LoadFont+2; AddressHi = FontHi + (c/32)
; intentional fall into _DrawChar1
034C: _DrawChar1
034C:A6 F6 LDX TmpHi
034E:86 FD STX TopHi
0350: _DrawChar
0350:A2 00 LDX #0 ; Note: next instruction is self-modified !
0352: _LoadFont ; A = font[ offset ]
0352:BD 00 00 LDA $0000,X
ORG $0335
0335: _DrawChar2a
0335:48 PHA ; push c = %PQRstuvw to draw
0336:29 1F AND #1F ; = %000stuvw R=0, implicit CLC
0338:0A ASL ; c * 2 %00stuvw0
0339:0A ASL ; c * 4 %0stuvw00
033A:0A ASL ; c * 8 %stuvw000
033B:69 00 ADC #<Font ; += FontLo; Carry = 0 since R=0 from above
033D:8D 53 03 STA _LoadFont+1 ; AddressLo = FontLo + (c*8)
0340:68 PLA ; pop c = %PQRstuvw to draw
0341:29 E0 AND #E0 ; = %PQR00000 s=0, implicit CLC
0343:2A ROL ; = %QR000000 C=P
0344:2A ROL ; = %R000000P C=Q
0345:2A ROL ; = %000000PQ C=R need one more
0346:2A ROL ; c / 32 = %00000PQR C=0 shift to get R
0347:69 60 ADC #>Font ; += FontHi; Carry = 0 since S=0 from above
0349:8D 54 03 STA _LoadFont+2; AddressHi = FontHi + (c/32)
; intentional fall into _DrawChar1
034C: _DrawChar1
034C:A6 F6 LDX TmpHi
034E:86 FD STX TopHi
0350: _DrawChar
0350:A2 00 LDX #0 ; Note: next instruction is self-modified !
0352: _LoadFont ; A = font[ offset ]
0352:BD 00 60 LDA Font,X
```
Did you catch that **note** ? One popular trick on the 6502 was `self-modifying code`. Instead of wasting memory with yet-another-variable we directly change the load/store instructions themselves! This actually has 2 advantages:
@ -1368,21 +1373,21 @@ We would need to start with the carry **pre-loaded** with P, and the QR already
That might look like something like this:
```assembly
PHA ; push c
PHA ; push c
AND #$E0
TAX ; save %PQR00000
PLA ; pop c
TAX ; save %PQR00000
PLA ; pop c
AND #$1F
STA Temp ; save %000stuvw
TXA ; A=PQR00000
ASL ; C=P A=QR000000 <- stuvw not taking advantage of shift :-(
OR Temp ; C=P A=QR0stuvw
ROL ; C=Q A=R0stuvwP
ROL ; C=R A=0stuvwPQ
ROL ; C=0 A=stuvwPQR
STA Temp ; save %000stuvw
TXA ; A=PQR00000
ASL ; C=P A=QR000000 <- stuvw not taking advantage of shift :-(
OR Temp ; C=P A=QR0stuvw
ROL ; C=Q A=R0stuvwP
ROL ; C=R A=0stuvwPQ
ROL ; C=0 A=stuvwPQR
PHA
AND #1F ; = %000stuvw
STA _LoadFont+1; AddressLo = FontLo + (c*8)
AND #1F ; = %000stuvw
STA _LoadFont+1 ; AddressLo = FontLo + (c*8)
PLA
AND #E0
CLC
@ -1411,26 +1416,26 @@ The **lateral** thinking is to _use partial results_.
Let's code this up:
Listing 6:
Listing 7:
```Assembly
; FUNC: DrawCharCol( c, col ) alias _DrawChar2
; PARAM: A = glyph to draw
; PARAM: Y = column to draw at; $0 .. $27 (Columns 0 .. 39) (not modified)
; NOTES: X is destroyed
ORG $033A
033A: DrawCharCol ; A=%PQRstuvw
033A:2A ROL ; C=P A=%QRstuvw?
033B:2A ROL ; C=Q A=%Rstuvw?P
033C:2A ROL ; C=R A=%stuvw?PQ
033D:AA TAX ; X=%stuvw?PQ push glyph
033E:29 F8 AND #F8 ; A=%stuvw000
0340:8D 53 03 STA _LoadFont+1; AddressLo = (c*8)
0343:8A TXA ; A=%stuvw?PQ pop glyph
0344:29 03 AND #3 ; Optimization: s=0 implicit CLC !
0346:2A ROL ; C=s A=%00000PQR and 1 last ROL to get R
0347:69 60 ADC #>Font ; += FontHi; Carry=0 since s=0 from above
0349:8D 54 03 STA _LoadFont+2; AddressHi = FontHi + (c/32)
ORG $033A
033A: DrawCharCol ; A=%PQRstuvw
033A:2A ROL ; C=P A=%QRstuvw?
033B:2A ROL ; C=Q A=%Rstuvw?P
033C:2A ROL ; C=R A=%stuvw?PQ
033D:AA TAX ; X=%stuvw?PQ push glyph
033E:29 F8 AND #F8 ; A=%stuvw000
0340:8D 53 03 STA _LoadFont+1 ; AddressLo = (c*8)
0343:8A TXA ; A=%stuvw?PQ pop glyph
0344:29 03 AND #3 ; Optimization: s=0 implicit CLC !
0346:2A ROL ; C=s A=%00000PQR and 1 last ROL to get R
0347:69 60 ADC #>Font ; += FontHi; Carry=0 since s=0 from above
0349:8D 54 03 STA _LoadFont+2 ; AddressHi = FontHi + (c/32)
; intentional fall into _DrawChar1 @ $034C
```
@ -1509,13 +1514,16 @@ We now have the ability to print any of the 128 ASCII characters!
Let's verify this by writing a character inspector. We'll use the arrow keys to select the glyph and ESC to exit.
Listing Demo2a:
Listing Demo 3a:
```assembly
; FUNC: DemoCharInspect()
KEYBOARD EQU $C000
KEYSTROBE EQU $C010
glyph EQU $FE
HgrLo EQU $F5
HgrHi EQU $F6
glyph EQU $FE
KEYBOARD EQU $C000
KEYSTROBE EQU $C010
ORG $1000
1000: DemoCharInspect
@ -1578,46 +1586,52 @@ We now have an ASCII char inspector!
Let's fix it up to print the hex value of the current character we are inspecting:
Listing Demo2b:
Listing Demo 3b:
```assembly
ORG $1010
1010:20 3C 10 JSR Patch1
ORG $1010
1010:20 3C 10 JSR Patch1
ORG $1037
103C: Patch1
103C:48 PHA ; save c
103D:20 10 03 JSR DrawChar
1040:68 PLA ; restore c so we can print it in hex
1041:4C 01 03 JMP DrawHexByte
ORG $1037
103C: Patch1
103C:48 PHA ; save c
103D:20 10 03 JSR DrawChar
1040:68 PLA ; restore c so we can print it in hex
1041:4C 01 03 JMP DrawHexByte
```
ORG $0303
Listing 8:
```assembly
ORG $0303
; FUNC: DrawHexByte( c ) = $0301
; PARAM: A = byte to print in hex
0301: DrawHexByte
0301:48 PHA ; save low nibble
0302:6A ROR ; shift high nibble
0303:6A ROR ; to low nibble
0304:6A ROR
0305:6A ROR
0306:20 0A 03 JSR DrawHexNib ; print high nib in hex
0309:68 PLA ; pritn low nib in hex
0301: DrawHexByte
0301:48 PHA ; save low nibble
0302:6A ROR ; shift high nibble
0303:6A ROR ; to low nibble
0304:6A ROR ;
0305:6A ROR ;
0306:20 0A 03 JSR DrawHexNib ; print high nib in hex
0309:68 PLA ; pritn low nib in hex
; FUNC: DrawHexNib() = $030C
; PARAM: A = nibble to print as hex char
030A: DrawHexNib
030A:29 0F AND #F ; base 16
030C:AA TAX ;
030D:BD 90 03 LDA NIB2HEX,X ; nibble to ASCII
; intentional fall into PrintChar
ORG $0390
0390:30 31 32 33 NIB2HEX ASC "0123456789ABCDEF"
030A: DrawHexNib
030A:29 0F AND #F ; base 16
030C:AA TAX ;
030D:BD 90 03 LDA NIB2HEX,X ; nibble to ASCII
; intentional fall into PrintChar
ORG $0390
0390:30 31 32 33 NIB2HEX ASC "0123456789ABCDEF"
0394:34 35 36 37
0398:38 39 41 42
039C:43 44 45 46
```
Q. Can you see a way to make DrawHexNib smaller?
Enter in the changes:
1010:20 3C 10
@ -1648,17 +1662,17 @@ And now we have our own DrawHexByte() function.
Let's add a space after the character but before the hex value to improve readability of the output. The new code is:
Listing Demo2c:
Listing Demo 3c:
```assembly
ORG $1010
1010:20 37 10 JSR Patch2
ORG $1010
1010:20 37 10 JSR Patch2
ORG $1037
1037: Patch2
1037:48 PHA ; save c
1038:20 10 03 JSR PrintChar
103B:A9 20 LDA ' ' ; Draw whitespace
ORG $1037
1037: Patch2
1037:48 PHA ; save c
1038:20 10 03 JSR PrintChar
103B:A9 20 LDA ' ' ; Draw whitespace
```
Enter in these changes:
@ -1754,6 +1768,7 @@ Our `HgrHiY` table:
03C0:00 00 01 01 02 02 03 03
03C8:00 00 01 01 02 02 03 03
<hr>
**AppleWin** users: To save this press `F7`, at the debugger console `bsave "hgrtable.bin",3A0:3CF`, press `F7`.
@ -1764,7 +1779,7 @@ Our `HgrHiY` table:
To select which row to draw at we'll pass that in the X register to our DrawCharColRow() routine:
Listing 7:
Listing 9b:
```assembly
; FUNC: SetCursorRow( row )
@ -1772,25 +1787,25 @@ Listing 7:
; INPUT : $E5,$E6 initial pointer to the destination screen scanline
; Note: Must start at every 8 scanlines.
; OUTPUT: $F5,$F5 working pointer to the destination screen scanline
ORG $0313
0313: SetCursorRow
0313:BD A0 03 LDA HgrLoY,X ; HgrLoY[ row ]
0316:85 F5 STA TmpLo
0318:BD B8 03 LDA HgrHiY,X ; HgrHiY[ row ]
031B:18 CLC
031C:65 E6 ADC HgrHi
031E:85 F6 STA TmpHi
0320:60 RTS
ORG $0313
0313: SetCursorRow
0313:BD A0 03 LDA HgrLoY,X ; HgrLoY[ row ]
0316:85 F5 STA TmpLo
0318:BD B8 03 LDA HgrHiY,X ; HgrHiY[ row ]
031B:18 CLC
031C:65 E6 ADC HgrHi
031E:85 F6 STA TmpHi
0320:60 RTS
; FUNC: DrawCharColRow()
; PARAM: A = glyph to draw
; PARAM: Y = column to draw at ; $0 .. $27 (Columns 0 .. 39) (not modified)
; PARAM: X = row to draw at ; $0 .. $17 (Rows 0 .. 23) (destroyed)
ORG $0335
0335: DrawCharColRow
0335:48 PHA
0336:20 13 03 JSR SetCursorRow
0339:68 PLA
ORG $0335
0335: DrawCharColRow
0335:48 PHA
0336:20 13 03 JSR SetCursorRow
0339:68 PLA
; intentional fall into _DrawChar2
```
@ -1803,13 +1818,15 @@ Enter in:
Now we can print a char at any location:
Listing Demo 4:
```assembly
ORG $1100
1100: PrintAYX
1100:A9 41 LDA #41 ; A-register = char
1102:A0 01 LDY #1 ; Y-register = col 1 (2nd column)
1104:A2 02 LDX #2 ; X-register = row 2 (3rd row)
1106:4C 35 03 JMP DrawCharColRow
1100: PrintAYX
1100:A9 41 LDA #41 ; A-register = char
1102:A0 01 LDY #1 ; Y-register = col 1 (2nd column)
1104:A2 02 LDX #2 ; X-register = row 2 (3rd row)
1106:4C 35 03 JMP DrawCharColRow
```
Enter in:
@ -1837,18 +1854,20 @@ Unfortunately, our usage of the X and Y registers are not intuitive. This is due
We could map the X-register to the natural column (x-axis), and the Y-register to the natural row (y-axis). Alas, we're stuck with the X=row and Y=col unless we wanted to add extra code to "swap" the two.
Listing 10:
```assembly
; FUNC: SetCursorColRowYX()
; PARAM: Y = col
; PARAM: X = row
ORG $0369
369: SetCursorColRowYX
369:20 13 03 JSR SetCursorRow
36C:18 CLC
36D:98 TYA
36E:65 F5 ADC $F5
371:85 F5 STA $F5
373:60 RTS
ORG $0369
369: SetCursorColRowYX
369:20 13 03 JSR SetCursorRow
36C:18 CLC
36D:98 TYA
36E:65 F5 ADC $F5
371:85 F5 STA $F5
373:60 RTS
```
Or are we stuck? Since we're using a function to calculate the destination address let's fix the order.
@ -1858,17 +1877,17 @@ We'll need to change the `X` offset in SetCursorColRowXY() to `Y`;
; FUNC: SetCursorColRow2a( row )
; PARAM: Y = row
; NOTES: Version 2a !
0928: ORG $0928
0928: SetCursorColRow2a
0928:B9 A0 03 LDA HgrLoY,Y ; changed from: ,X
092B:18 CLC
092C:65 E5 ADC HgrLo
092E:85 F5 STA TmpLo
0930:B9 B8 03 LDA HgrHiY,Y ; changed from: ,X
0933:18 CLC
0934:65 E6 ADC HgrHi
0936:85 F6 STA TmpHi
0938:60 RTS
0928: ORG $0928
0928: SetCursorColRow2a
0928:B9 A0 03 LDA HgrLoY,Y ; changed from: ,X
092B:18 CLC
092C:65 E5 ADC HgrLo
092E:85 F5 STA TmpLo
0930:B9 B8 03 LDA HgrHiY,Y ; changed from: ,X
0933:18 CLC
0934:65 E6 ADC HgrHi
0936:85 F6 STA TmpHi
0938:60 RTS
```
And change the low byte to add `X` instead:
@ -1878,37 +1897,39 @@ And change the low byte to add `X` instead:
; PARAM: X = col
; PARAM: Y = row
; NOTES: Version 2b !
ORG $0979
979: SetCursorColRow2b
979:20 13 03 JSR SetCursorRow
97C:18 CLC
37D:88 TXA ; changed from: TYA
97E:65 F5 ADC $F5
981:85 F5 STA $F5
ORG $0979
979: SetCursorColRow2b
979:20 13 03 JSR SetCursorRow
97C:18 CLC
37D:88 TXA ; changed from: TYA
97E:65 F5 ADC $F5
981:85 F5 STA $F5
983:60
```
This is a little clunky but it is progress. Let's write the new SetCursorColRow() version with the SetCursorRow() inlined so we don't have to use a JSR.
Listing 11:
```assembly
; FUNC: SetCursorColRow( col, row )
; PARAM: X = column to draw at; $0 .. $27 (Columns 0 .. 39) (not modified)
; PARAM: Y = row to draw at; $0 .. $17 (Rows 0 .. 23) (not modified)
; NOTES: Version 3! X and Y is swapped from earlier version!
; [$F5] = HgrLoY[ Y ] + ScreenLo + X
ORG $0321
0321: SetCursorColRow
0321:86 F5 STX TmpLo
0323:B9 A0 03 LDA HgrLoY,Y ; HgrLoY[ row ]
0326:18 CLC
0327:65 F5 ADC TmpLo ; add column
0329:85 F5 STA TmpLo
032B:B9 B8 03 LDA HgrHiY,Y ; HgrHiY[ row ]
032E:18 CLC ; \ could optimize this into
032F:65 E6 ADC HgrHi ; / single ORA HgrHi
0331:85 F6 STA TmpHi
0333:60 RTS
0334:EA NOP ; pad
ORG $0321
0321: SetCursorColRow
0321:86 F5 STX TmpLo
0323:B9 A0 03 LDA HgrLoY,Y ; HgrLoY[ row ]
0326:18 CLC
0327:65 F5 ADC TmpLo ; add column
0329:85 F5 STA TmpLo
032B:B9 B8 03 LDA HgrHiY,Y ; HgrHiY[ row ]
032E:18 CLC ; \ could optimize this into
032F:65 E6 ADC HgrHi ; / single ORA HgrHi
0331:85 F6 STA TmpHi
0333:60 RTS
0334:EA NOP ; pad
```
Enter in:
@ -1923,23 +1944,25 @@ Enter in:
Now that we have the basic print char working lets extend it to print a C-style string (one that is zero terminated.)
Listing 12:
```assembly
String EQU $F0
; FUNC: DrawString( *text )
; PARAM: X = High byte of string address
; PARAM: Y = Low byte of string address
037E: ORG $037E
037E: DrawString
037E: ORG $037E
037E: DrawString
037E:84 F0 STY String+0
0380:86 F1 STX String+1
0382:A0 00 LDY #0
0384:B1 F0 .1 LDA (String),Y
0384:B1 F0 .1 LDA (String),Y
0386:F0 07 BEQ .2 ; null byte? Done
0388:20 10 03 JSR DrawChar ; or DrawCharCol for speed
038B:C0 28 CPY #40 ; col < 40?
038D:90 F5 BCC .1
038F:60 .2 RTS
038F:60 .2 RTS
```
<hr>
**AppleWin**:
@ -1952,6 +1975,8 @@ Now that we have the basic print char working lets extend it to print a C-style
And our example to verify that it works:
Listing Demo 5:
```assembly
; FUNC: DemoDrawString()
ORG $1200
@ -2211,9 +2236,9 @@ Hey! Homework? Yes, the only (true) way to demonstrate you understand the theo
very bottom scanline should be "blank."
# Recap
# Recap - Font Draw
Here are all the (core) routines we've entered in so far:
Here are all the (core) font rendering routines we've entered in so far:
```
0301: 48 6A 6A 6A 6A 20 0A
@ -2240,7 +2265,7 @@ Here are all the (core) routines we've entered in so far:
03C8:00 00 01 01 02 02 03 03
```
(To save this: `BSAVE CODE_0300.BIN,A$300,L$D0`)
(To save this: `BSAVE FONTDRAW.BIN,A$300,L$D0`)
# Other Fonts
@ -2405,7 +2430,7 @@ Hmm, some of those glyphs are badly designed (inconsistent.) :-/ That's the bigg
Let's write a program to view all the ASCII characters.
```Assembly
.ORG $1050
ORG $1050
; <<Forthcoming!>>
```