Thursday, May 04, 2017

Apple II - Double Hi-Res From The Ground Up
Part 7: Drawing lines with an 8-bit Bresenham algorithm

The story so far...

We've finally developed a routine which plots anywhere on the DHR screen in any colour.  Unfortunately plotting single dots does not a game make.  The next logical step is to explore the world of vector graphics by learning to draw lines.


But first a word about code management...


Most developers will, to a point re-use their code.  Often repurposing snippets, functions or whole programs from project to project.  There is an adage in software development which is well known if not well agreed upon: "old code is good code".  The idea being that something that has been used and reused has stood the test of time.

Code reuse is even more important to the machine language programmer as it often takes a lot of work to get a routine which accomplishes very little.  For example, we just spent seven blog posts to draw a single dot on the screen!   Because of this, assembly programmers often keep a library of short, tightly coded routines called macros which they can drop into any program.

Most assemblers have some kind of code reuse features baked-in.  Today were going to discuss just one for the Merlin Pro assembler: the PUT command.  Check this out:
 LDX #$0A
 LDY #$0A
 PUT PLOT.ROUTINE
 RTS
All this does is tell the assembler to find a file on the local disk called PLOT.ROUTINE and insert the source code from it into our code.  Just as if someone had typed it in between line 02 and line 04.  For even a novice programmer this can be pretty powerful.

To start we are going to break our program up into four pieces:

FilenameLine # RangeDescription
DH.INIT03-18DHR Initialization code, variables used by other DH modules.
DH.PLOT23-40DHR Plot routine, requires DH.INIT and DH.TABLES
DH.COLOUR43-50Set DHR Plot colour, requires DH.PLOT
DH.TABLES52-277DHR Table data

If you're using Merlin Pro then all you need to do is create a new file in the editor (Hit "E" from the main menu and then type "NEW" to clear out any old code), paste in the code snippet and save it with the appropriate name.  
Note, that you do not need to assemble it! In the interests of increasing our ability to use this code in later projects we will need to make a few changes to the labels as well. Below are the files we will be using and some commentary on our changes:
*DH.INIT
*DHR Initialization code, variables used by other DH modules.
*Jonathan Graham/Battlestations
*Free for non-commercial use with attribution
*
GRAPHON EQU $C050 
HIRESON EQU $C057
FULLON EQU $C052
DHRON EQU $C05E
ON80STOR EQU $C001
ON80COL EQU $C00D
PAGE1 EQU $C054
PAGE2 EQU $C055
SCRN_LO EQU $1D ;Zero page location for low byte of our screen row
SCRN_HI EQU $1E ;Zero page location for high byte of our screen row
INIT STA GRAPHON ;Turn on graphics
 STA HIRESON ;Turn on hi-res mode
 STA FULLON ;Turn on fullscreen mode
 STA DHRON ;Turn on Double hi-res
 STA ON80COL ;Turn on 80 Column mode
 STA ON80STOR ;Use PAGE1/PAGE2 to switch between MAIN and AUX memory
No real changes here. I'll point out that some people might decide to put the SCRN_LO/SCRN_HI variables into the next file. This is kind of a personal taste thing. On one hand it encapsulates the variables related to the plot routine in the same source file. On the other hand if you PUT DH.PLOT file multiple times in the same source file you'll get an error.

Another approach is to keep all the variables in the main source file. This approach means you need to remember to create two variables SCRN_LO/SCRN_HI pointing to two consecutive zero page locations whenever you PUT DH.PLOT. A good way to do that is to document the dependency in the source file. However in this case we've put them together in a single file because we just want those lines out of the way so we can concentrate on other things.
*DH.PLOT
*DHR plotting routine, requires DH.INIT and DH.TABLES.
*Jonathan Graham/Battlestations
*Free for non-commercial use with attribution
*
 LDA HTAB_LO,Y ;Find the low byte of the row address
 STA SCRN_LO 
 LDA HTAB_HI,Y ;Find the high byte of the row address
 STA SCRN_HI
 LDY MBOFFSET,X ;Find what byte if any in MAIN we are working in
 BMI :AUX ;If pixel has no bits in MAIN memory - go to aux routine
 STA PAGE1 ;Map $2000 to MAIN memory
 LDA (SCRN_LO),Y ;Load screen data
 AND MAINAND,X ;Erase pixel bits
]ORMAIN ORA MAINGR,X ;Draw coloured bits
 STA (SCRN_LO),Y ;Write back to screen
:AUX LDY ABOFFSET,X ;Find what byte if any in AUX we are working in
 BMI :END ;If no part of the pixel is in AUX - end the program
 STA PAGE2 ;Map $2000 to AUX memory
 LDA (SCRN_LO),Y ;Load screen data
 AND AUXAND,X ;Erase pixel bits
]ORAUX ORA AUXGR,X ;Draw coloured bits
 STA (SCRN_LO),Y ;Write back to screen
:END
The only differences here are that we've removed the RTS and added an empty label called :END. The assembler will assign this to whatever memory location is next in whatever source file this macro is PUT into. We also removed the DPLOT label since if we were to PUT this file multiple times in a program we would get an error due to a duplicate label name.

This is the same reason we changed the ORMAIN/ORAUX labels to have a square bracket in front. The ']' character turns those labels into a variable, each time a ]LABEL appears in the first column the assembler will assign it the next memory location. Subsequent references to it will be translated into the assigned address until the same ]LABEL is used again in the first column. At which point the ]LABEL takes on the current memory location and any subsequent reference will now point to this spot, and so on... The upshot is that we can now PUT DH.PLOT multiple times in a file without worry of generating an error.
*DH.COLOUR
*Sets colour for DH.PLOT routine.  Requires DH.PLOT.
*Jonathan Graham/Battlestations
*Free for non-commercial use with attribution
*
 LDA CLOM,Y ;Lookup low byte of MAIN memory colour table
 STA ]ORMAIN+1 ;Update the ORA instruction
 LDA CHIM,Y ;Lookup high byte of MAIN memory colour table
 STA ]ORMAIN+2 ;Update the ORA instruction
 LDA CLOA,Y ;Lookup low byte of AUX memory colour table
 STA ]ORAUX+1 ;Update the ORA instruction
 LDA CHIA,Y ;Lookup high byte of AUX memory colour table
 STA ]ORAUX+2 ;Update the ORA instruction
Again we've removed the RTS and the initial SETDCOLOR label. We also changed the labels for ORMAIN/ORAUX to match our changes in DH.PLOT.
*DH.TABLES
*Tables for all the DH routines
*Jonathan Graham/Battlestations
*Free for non-commercial use with attribution
*
MBOFFSET DB 255,0,0,0,255,1,1,255,2,2,2,255,3,3
 DB 255,4,4,4,255,5,5,255,6,6,6,255,7,7
 DB 255,8,8,8,255,9,9,255,10,10,10,255,11,11
 DB 255,12,12,12,255,13,13,255,14,14,14,255,15,15
 DB 255,16,16,16,255,17,17,255,18,18,18,255,19,19
 DB 255,20,20,20,255,21,21,255,22,22,22,255,23,23
 DB 255,24,24,24,255,25,25,255,26,26,26,255,27,27
 DB 255,28,28,28,255,29,29,255,30,30,30,255,31,31
 DB 255,32,32,32,255,33,33,255,34,34,34,255,35,35
 DB 255,36,36,36,255,37,37,255,38,38,38,255,39,39
ABOFFSET DB 0,0,255,1,1,1,255,2,2,255,3,3,3,255
 DB 4,4,255,5,5,5,255,6,6,255,7,7,7,255
 DB 8,8,255,9,9,9,255,10,10,255,11,11,11,255
 DB 12,12,255,13,13,13,255,14,14,255,15,15,15,255
 DB 16,16,255,17,17,17,255,18,18,255,19,19,19,255
 DB 20,20,255,21,21,21,255,22,22,255,23,23,23,255
 DB 24,24,255,25,25,25,255,26,26,255,27,27,27,255
 DB 28,28,255,29,29,29,255,30,30,255,31,31,31,255
 DB 32,32,255,33,33,33,255,34,34,255,35,35,35,255
 DB 36,36,244,37,37,37,255,38,38,255,39,39,39,255
MAINAND
 LUP 20
 DB %01111111,%01111110,%01100001,%00011111
 DB %01111111,%01111000,%00000111
 --^
AUXAND
 LUP 20
 DB %01110000,%00001111,%01111111,%01111100
 DB %01000011,%00111111,%01111111
 --^
MAINBL
 LUP 20
 DB %00000000,%00000000,%00000000,%00000000
 DB %00000000,%00000000,%00000000
 --^
AUXBL
 LUP 20
 DB %00000000,%00000000,%00000000,%00000000
 DB %00000000,%00000000,%00000000
 --^
MAINMG
 LUP 20
 DB %00000000,%00000001,%00010000,%00000000
 DB %00000000,%00000100,%01000000
 --^
AUXMG
 LUP 20
 DB %00001000,%00000000,%00000000,%00000010
 DB %00100000,%00000000,%00000000
 --^
MAINBR
 LUP 20
 DB %00000000,%00000000,%00001000,%00000000
 DB %00000000,%00000010,%00100000
 --^
AUXBR
 LUP 20
 DB %00000100,%01000000,%00000000,%00000001
 DB %00010000,%00000000,%00000000
 --^
MAINOR
 LUP 20
 DB %00000000,%00000001,%00011000,%00000000
 DB %00000000,%00000110,%01100000
 --^
AUXOR
 LUP 20
 DB %00001100,%01000000,%00000000,%00000011
 DB %00110000,%00000000,%00000000
 --^
MAINDG
 LUP 20
 DB %00000000,%00000000,%00000100,%01000000
 DB %00000000,%00000001,%00010000
 --^
AUXDG
 LUP 20
 DB %00000010,%00100000,%00000000,%00000000
 DB %00001000,%00000000,%00000000
 --^
MAING1
 LUP 20
 DB %00000000,%00000001,%00010100,%01000000
 DB %00000000,%00000101,%01010000
 --^
AUXG1
 LUP 20
 DB %00001010,%00100000,%00000000,%00000010
 DB %00101000,%00000000,%00000000
 --^
MAINGR
 LUP 20
 DB %00000000,%00000000,%00001100,%01000000
 DB %00000000,%00000011,%00110000
 --^
AUXGR
 LUP 20
 DB %00000110,%01100000,%00000000,%000000001
 DB %00011000,%00000000,%00000000
 --^
MAINYE
 LUP 20
 DB %00000000,%00000001,%00011100,%01000000
 DB %00000000,%00000111,%01110000
 --^
AUXYE
 LUP 20
 DB %00001110,%01100000,%00000000,%00000011
 DB %00111000,%00000000,%00000000
 --^
MAINDB
 LUP 20
 DB %00000000,%00000000,%00000010,%00100000
 DB %00000000,%00000000,%00001000
 --^
AUXDB
 LUP 20
 DB %00000001,%00010000,%00000000,%00000000
 DB %00000100,%01000000,%00000000
 --^
MAINVI
 LUP 20
 DB %00000000,%00000001,%00010010,%00100000
 DB %00000000,%00000100,%01001000
 --^
AUXVI
 LUP 20
 DB %00001001,%00010000,%00000000,%00000010
 DB %00100100,%01000000,%00000000
 --^
MAING2
 LUP 20
 DB %00000000,%00000000,%00001010,%00100000
 DB %00000000,%00000010,%00101000
 --^
AUXG2
 LUP 20
 DB %00000101,%01010000,%00000000,%00000001
 DB %00010100,%01000000,%00000000
 --^
MAINPI
 LUP 20
 DB %00000000,%00000001,%00011010,%00100000
 DB %00000000,%00000110,%01101000
 --^
AUXPI
 LUP 20
 DB %00001101,%01010000,%00000000,%00000011
 DB %00110100,%01000000,%00000000
 --^
MAINMB
 LUP 20
 DB %00000000,%00000000,%00000110,%01100000
 DB %00000000,%00000001,%00011000
 --^
AUXMB
 LUP 20
 DB %00000011,%00110000,%00000000,%00000000
 DB %00001100,%01000000,%00000000
 --^
MAINLB
 LUP 20
 DB %00000000,%00000001,%00010110,%01100000
 DB %00000000,%00000101,%01011000
 --^
AUXLB
 LUP 20
 DB %00001011,%00110000,%00000000,%00000010
 DB %00101100,%01000000,%00000000
 --^
MAINAQ
 LUP 20
 DB %00000000,%00000000,%00001110,%01100000
 DB %00000000,%00000011,%00111000
 --^
AUXAQ
 LUP 20
 DB %00000111,%01110000,%00000000,%00000001
 DB %00011100,%01000000,%00000000
 --^
MAINWI
 LUP 20
 DB %00000000,%00000001,%00011110,%01100000
 DB %00000000,%00000111,%01111000
 --^
AUXWI
 LUP 20
 DB %00001111,%01110000,%00000000,%00000011
 DB %00111100,%01000000,%00000000
 --^
CLOM DB <MAINBL,<MAINMG,<MAINBR,<MAINOR,<MAINDG
 DB <MAING1,<MAINGR,<MAINYE,<MAINDB,<MAINVI
 DB <MAING2,<MAINPI,<MAINMB,<MAINLB,<MAINAQ,<MAINWI
CHIM DB >MAINBL,>MAINMG,>MAINBR,>MAINOR,>MAINDG,>MAING1
 DB >MAINGR,>MAINYE,>MAINDB,>MAINVI,>MAING2,>MAINPI
 DB >MAINMB,>MAINLB,>MAINAQ,>MAINWI
CLOA DB <AUXBL,<AUXMG,<AUXBR,<AUXOR,<AUXDG
 DB <AUXG1,<AUXGR,<AUXYE,<AUXDB,<AUXVI
 DB <AUXG2,<AUXPI,<AUXMB,<AUXLB,<AUXAQ,<AUXWI
CHIA DB >AUXBL,>AUXMG,>AUXBR,>AUXOR,>AUXDG,>AUXG1
 DB >AUXGR,>AUXYE,>AUXDB,>AUXVI,>AUXG2,>AUXPI
 DB >AUXMB,>AUXLB,>AUXAQ,>AUXWI
HTAB_LO
 LUP 4
 DB $00,$00,$00,$00,$00,$00,$00,$00
 DB $80,$80,$80,$80,$80,$80,$80,$80
 --^
 LUP 4
 DB $28,$28,$28,$28,$28,$28,$28,$28
 DB $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8
 --^
 LUP 4
 DB $50,$50,$50,$50,$50,$50,$50,$50
 DB $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0
 --^
HTAB_HI
 LUP 3
 DB $20,$24,$28,$2C,$30,$34,$38,$3C
 DB $20,$24,$28,$2C,$30,$34,$38,$3C
 DB $21,$25,$29,$2D,$31,$35,$39,$3D
 DB $21,$25,$29,$2D,$31,$35,$39,$3D
 DB $22,$26,$2A,$2E,$32,$36,$3A,$3E
 DB $22,$26,$2A,$2E,$32,$36,$3A,$3E
 DB $23,$27,$2B,$2F,$33,$37,$3B,$3F
 DB $23,$27,$2B,$2F,$33,$37,$3B,$3F
 --^
...and since these are just data tables there are no changes here at all.

Subroutines vs. Macros:


When writing in higher level languages the most common way of reusing code is to have a separate function or subroutine.  In lower level languages like C you will see both subroutines and some macro use, particularly in performance oriented code. In assembler, as you'll soon see macros are pretty pervasive. Is there a reason for this? Or is it just tradition?  To explain, consider the following code:
*DEMO 0
*Use DH.PLOT to fill the screen cycling through all the colours
*Jonathan Graham/Battlestations
*Free for non-commercial use with attribution
*
 XC ;Required for Merlin Pro to use 65C02 instructions
 ORG $6000 ;Start assembling at memory location $6000
 PUT DH.INIT ;Get everything ready for DHR
 LDA #$00 ;Colour code for white
NEWCOLOR JSR SETDCOLOR
 LDY #$00 ;Set Y co-ordinate to 0
NXTROW LDX #$00 ;Set X co-ordinate to 0
NXTCOL PHY ; Preseve the Y register - as DPLOT destroys it
 JSR DPLOT ;Plot the point
 PLY
 INX
 CPX #$8C
 BNE NXTCOL
 INY
 CPY #$C0
 BNE NXTROW
 LDA NEWCOLOR-1
 INC
 AND #001111
 STA NEWCOLOR-1 
 BRA NEWCOLOR
DPLOT PUT DH.PLOT ;Adding a label as we removed the one from the file
 RTS ;Add an RTS so we can use this as a subroutine
SETDCOLOR TAY ;Adding a label as we removed the one from the file
 PUT DH.COLOUR 
 RTS ;Add an RTS so we can use this as a subroutine
 PUT DH.TABLES
If you compile this yourself and benchmark it. You should see that it draws a full screen in about 2.28 seconds (about 0.44 FPS).  One of the most obvious performance issues is that every time we want to draw a pixel we have to JSR to our subroutine.  The JSR costs us 12 cycles per pixel! ( Six for the JSR and another six for the RTS). Considering that the whole DPLOT routine takes 54-100 cycles. This approach represents a significant performance hit (30%!).  

There is however, an easy solution:
*DEMO 1
*Use DH.PLOT to fill the screen cycling through all the colours
*Jonathan Graham/Battlestations
*Free for non-commercial use with attribution
*
 XC ;Required for Merlin Pro to use 65C02 instructions
 ORG $6000 ;Start assembling at memory location $6000
 PUT DH.INIT ;Get everything ready for DHR
 LDA #$00 ;Colour code for white
NEWCOLOR JSR SETDCOLOR
 LDY #00 ;Set Y co-ordinate to 0
NXTROW LDX #$00 ;Set X co-ordinate to 0
NXTCOL PHY ; Preseve the Y register - as DPLOT destroys it
 PUT DH.PLOT ;DPLOT routine is now inlined
 PLY
 INX
 CPX #$8C
 BNE NXTCOL
 INY
 CPY #$C0
 BNE NXTROW
 LDA NEWCOLOR-1
 INC
 AND #001111
 STA NEWCOLOR-1 
 BRA NEWCOLOR
SETDCOLOR TAY ;Assume the desired colour is in the accumulator
 PUT DH.COLOUR
 RTS ;Add an RTS so we can use this as a subroutine
 PUT DH.TABLES
This simple change drops us down to 1.965 seconds per screen (just over 0.5 FPS) on a 1Mhz machine. This kind of optimization is known as inlining and is one of the reasons that macros are so important to assembly language programming as they make inlining easy. An added bonus is that using drop-in code segments makes your source much easier to read.

There's one more easy optimization which we can get by splitting our code up further.  If you take a look at the first few lines of the DPLOT routine you will see that we look up the horizontal address and store it in ZP locations before we plot our points.  Recall that our program is drawing each line horizontally.  Which means it really only has to do this lookup once every line instead of once every pixel.  We are effectively we doing this 139x times more than necessary.  There are a number of solutions to this but the easiest is to simply split this lookup into it's own macro file.  We will call it DH.ROWSET,  as long as we remember to call it each time our Y coordinate changes we will improve our performance considerably.

DH.ROWSET would look like this:
*DH.ROWSET
*Perform the horizontal co-ordinate lookup.  Requires DH.TABLES.
*Jonathan Graham/Battlestations
*Free for non-commercial with attribution
 LDA HTAB_LO,Y ;Find the low byte of the row address
 STA SCRN_LO 
 LDA HTAB_HI,Y ;Find the high byte of the row address
 STA SCRN_HI
and our new DH.PLOT would look like this:
*DH.PLOT
*DHR plotting routine, requires DH.INIT and DH.TABLES.
*Jonathan Graham/Battlestations
*Free for non-commercial use with attribution
*
 LDY MBOFFSET,X ;Find what byte if any in MAIN we are working in
 BMI :AUX ;If pixel has no bits in MAIN memory - go to aux routine
 STA PAGE1 ;Map $2000 to MAIN memory
 LDA (SCRN_LO),Y ;Load screen data
 AND MAINAND,X ;Erase pixel bits
]ORMAIN ORA MAINGR,X ;Draw coloured bits
 STA (SCRN_LO),Y ;Write back to screen
:AUX LDY ABOFFSET,X ;Find what byte if any in AUX we are working in
 BMI :END ;If no part of the pixel is in AUX - end the program
 STA PAGE2 ;Map $2000 to AUX memory
 LDA (SCRN_LO),Y ;Load screen data
 AND AUXAND,X ;Erase pixel bits
]ORAUX ORA AUXGR,X ;Draw coloured bits
 STA (SCRN_LO),Y ;Write back to screen
:END
And our new demo looks like this:
*DEMO 2
*Use DH.PLOT to fill the screen cycling through all the colours
*Jonathan Graham/Battlestations
*Free for non-commercial use with attribution
*
 XC ;Required for Merlin Pro to use 65C02 instructions
 ORG $6000 ;Start assembling at memory location $6000
 PUT DH.INIT ;Get everything ready for DHR
 LDA #$00 ;Colour code for white
NEWCOLOR JSR SETDCOLOR
 LDY #00 ;Set Y co-ordinate to 0
NXTROW LDX #$00 ;Set X co-ordinate to 0
 PUT DH.ROWSET
NXTCOL PHY ; Preseve the Y register - as DPLOT destroys it
 PUT DH.PLOT ;DPLOT routine is now inlined
 PLY
 INX
 CPX #$8C
 BNE NXTCOL
 INY
 CPY #$C0
 BNE NXTROW
 LDA NEWCOLOR-1
 INC
 AND #001111
 STA NEWCOLOR-1 
 BRA NEWCOLOR
SETDCOLOR TAY ;Assume the desired colour is in the accumulator
 PUT DH.COLOUR
 RTS ;Add an RTS so we can use this as a subroutine
 PUT DH.TABLES
Here's a binary form for you:
CALL -151 6000: 8d 50 c0 8d 57 c0 8d 52 c0 8d 5e c0 8d 0d c0 8d 6010: 01 c0 a9 00 20 60 60 a0 00 a2 00 b9 6a 74 85 1d 6020: b9 2a 75 85 1e 5a bc 7a 60 30 0d 8d 54 c0 b1 1d 6030: 3d 92 61 1d 3a 69 91 1d bc 06 61 30 0d 8d 55 c0 6040: b1 1d 3d 1e 62 1d c6 69 91 1d 7a e8 e0 8c d0 d5 6050: c8 c0 c0 d0 c4 ad 13 60 1a 29 0f 8d 13 60 80 b4 6060: a8 b9 2a 74 8d 34 60 b9 3a 74 8d 35 60 b9 4a 74 6070: 8d 46 60 b9 5a 74 8d 47 60 60 ff 00 00 00 ff 01 6080: 01 ff 02 02 02 ff 03 03 ff 04 04 04 ff 05 05 ff 6090: 06 06 06 ff 07 07 ff 08 08 08 ff 09 09 ff 0a 0a 60a0: 0a ff 0b 0b ff 0c 0c 0c ff 0d 0d ff 0e 0e 0e ff 60b0: 0f 0f ff 10 10 10 ff 11 11 ff 12 12 12 ff 13 13 60c0: ff 14 14 14 ff 15 15 ff 16 16 16 ff 17 17 ff 18 60d0: 18 18 ff 19 19 ff 1a 1a 1a ff 1b 1b ff 1c 1c 1c 60e0: ff 1d 1d ff 1e 1e 1e ff 1f 1f ff 20 20 20 ff 21 60f0: 21 ff 22 22 22 ff 23 23 ff 24 24 24 ff 25 25 ff 6100: 26 26 26 ff 27 27 00 00 ff 01 01 01 ff 02 02 ff 6110: 03 03 03 ff 04 04 ff 05 05 05 ff 06 06 ff 07 07 6120: 07 ff 08 08 ff 09 09 09 ff 0a 0a ff 0b 0b 0b ff 6130: 0c 0c ff 0d 0d 0d ff 0e 0e ff 0f 0f 0f ff 10 10 6140: ff 11 11 11 ff 12 12 ff 13 13 13 ff 14 14 ff 15 6150: 15 15 ff 16 16 ff 17 17 17 ff 18 18 ff 19 19 19 6160: ff 1a 1a ff 1b 1b 1b ff 1c 1c ff 1d 1d 1d ff 1e 6170: 1e ff 1f 1f 1f ff 20 20 ff 21 21 21 ff 22 22 ff 6180: 23 23 23 ff 24 24 f4 25 25 25 ff 26 26 ff 27 27 6190: 27 ff 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 61a0: 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 7f 7e 61b0: 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 61c0: 7f 78 07 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 61d0: 07 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 7f 61e0: 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 7f 7e 61 61f0: 1f 7f 78 07 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 6200: 78 07 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 6210: 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 70 0f 6220: 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 6230: 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 6240: 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 6250: 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 6260: 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 6270: 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 6280: 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 6290: 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 62a0: 43 3f 7f 70 0f 7f 7c 43 3f 7f 00 00 00 00 00 00 62b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 62c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 62d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 62e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 62f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6320: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63c0: 00 00 00 01 10 00 00 04 40 00 01 10 00 00 04 40 63d0: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 63e0: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 63f0: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6400: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 6410: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 6420: 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 6430: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 6440: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 08 00 6450: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 6460: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 6470: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 6480: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 6490: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 64a0: 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 64b0: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 64c0: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 64d0: 20 00 00 08 00 00 02 20 00 00 00 00 08 00 00 02 64e0: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 64f0: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 6500: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 6510: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 6520: 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 6530: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 6540: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 6550: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 6560: 00 08 00 00 02 20 04 40 00 01 10 00 00 04 40 00 6570: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 6580: 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 6590: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 65a0: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 65b0: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 65c0: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 65d0: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 65e0: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 65f0: 00 00 00 01 18 00 00 06 60 00 01 18 00 00 06 60 6600: 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 6610: 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 6620: 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 6630: 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 6640: 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 6650: 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 6660: 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 6670: 00 01 18 00 00 06 60 00 01 18 00 00 06 60 0c 40 6680: 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 6690: 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 66a0: 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 66b0: 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 66c0: 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 66d0: 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 66e0: 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 66f0: 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 6700: 30 00 00 0c 40 00 03 30 00 00 00 00 04 40 00 01 6710: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 6720: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6730: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 6740: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 6750: 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 6760: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 6770: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 6780: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 6790: 00 04 40 00 01 10 02 20 00 00 08 00 00 02 20 00 67a0: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 67b0: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 67c0: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 67d0: 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 67e0: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 67f0: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 6800: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 6810: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 6820: 00 00 00 01 14 40 00 05 50 00 01 14 40 00 05 50 6830: 00 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 6840: 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 40 6850: 00 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 6860: 50 00 01 14 40 00 05 50 00 01 14 40 00 05 50 00 6870: 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 6880: 40 00 05 50 00 01 14 40 00 05 50 00 01 14 40 00 6890: 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 50 68a0: 00 01 14 40 00 05 50 00 01 14 40 00 05 50 0a 20 68b0: 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 68c0: 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 68d0: 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 00 0a 68e0: 20 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 68f0: 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 6900: 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 00 6910: 0a 20 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 6920: 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 6930: 28 00 00 0a 20 00 02 28 00 00 00 00 0c 40 00 03 6940: 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 6950: 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 6960: 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 6970: 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 6980: 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 6990: 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 69a0: 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 69b0: 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 69c0: 00 0c 40 00 03 30 06 60 00 01 18 00 00 06 60 00 69d0: 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 69e0: 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 69f0: 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 6a00: 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 6a10: 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 6a20: 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 6a30: 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 6a40: 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 6a50: 00 00 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 6a60: 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 6a70: 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 40 6a80: 00 07 70 00 01 1c 40 00 07 70 00 01 1c 40 00 07 6a90: 70 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 6aa0: 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 6ab0: 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 40 00 6ac0: 07 70 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 6ad0: 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 0e 60 6ae0: 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 6af0: 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 6b00: 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 00 0e 6b10: 60 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 6b20: 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 6b30: 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 00 6b40: 0e 60 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 6b50: 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 6b60: 38 00 00 0e 60 00 03 38 00 00 00 00 02 20 00 00 6b70: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 6b80: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 6b90: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 6ba0: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 6bb0: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 6bc0: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 6bd0: 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 6be0: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 6bf0: 00 02 20 00 00 08 01 10 00 00 04 40 00 01 10 00 6c00: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6c10: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 6c20: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 6c30: 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 6c40: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 6c50: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 6c60: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 6c70: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6c80: 40 00 00 01 12 20 00 04 48 00 01 12 20 00 04 48 6c90: 00 01 12 20 00 04 48 00 01 12 20 00 04 48 00 01 6ca0: 12 20 00 04 48 00 01 12 20 00 04 48 00 01 12 20 6cb0: 00 04 48 00 01 12 20 00 04 48 00 01 12 20 00 04 6cc0: 48 00 01 12 20 00 04 48 00 01 12 20 00 04 48 00 6cd0: 01 12 20 00 04 48 00 01 12 20 00 04 48 00 01 12 6ce0: 20 00 04 48 00 01 12 20 00 04 48 00 01 12 20 00 6cf0: 04 48 00 01 12 20 00 04 48 00 01 12 20 00 04 48 6d00: 00 01 12 20 00 04 48 00 01 12 20 00 04 48 09 10 6d10: 00 02 24 40 00 09 10 00 02 24 40 00 09 10 00 02 6d20: 24 40 00 09 10 00 02 24 40 00 09 10 00 02 24 40 6d30: 00 09 10 00 02 24 40 00 09 10 00 02 24 40 00 09 6d40: 10 00 02 24 40 00 09 10 00 02 24 40 00 09 10 00 6d50: 02 24 40 00 09 10 00 02 24 40 00 09 10 00 02 24 6d60: 40 00 09 10 00 02 24 40 00 09 10 00 02 24 40 00 6d70: 09 10 00 02 24 40 00 09 10 00 02 24 40 00 09 10 6d80: 00 02 24 40 00 09 10 00 02 24 40 00 09 10 00 02 6d90: 24 40 00 09 10 00 02 24 40 00 00 00 0a 20 00 02 6da0: 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 6db0: 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 00 0a 6dc0: 20 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 6dd0: 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 6de0: 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 00 6df0: 0a 20 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 6e00: 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 6e10: 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 6e20: 00 0a 20 00 02 28 05 50 00 01 14 40 00 05 50 00 6e30: 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 6e40: 40 00 05 50 00 01 14 40 00 05 50 00 01 14 40 00 6e50: 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 50 6e60: 00 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 6e70: 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 40 6e80: 00 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 6e90: 50 00 01 14 40 00 05 50 00 01 14 40 00 05 50 00 6ea0: 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 6eb0: 40 00 00 01 1a 20 00 06 68 00 01 1a 20 00 06 68 6ec0: 00 01 1a 20 00 06 68 00 01 1a 20 00 06 68 00 01 6ed0: 1a 20 00 06 68 00 01 1a 20 00 06 68 00 01 1a 20 6ee0: 00 06 68 00 01 1a 20 00 06 68 00 01 1a 20 00 06 6ef0: 68 00 01 1a 20 00 06 68 00 01 1a 20 00 06 68 00 6f00: 01 1a 20 00 06 68 00 01 1a 20 00 06 68 00 01 1a 6f10: 20 00 06 68 00 01 1a 20 00 06 68 00 01 1a 20 00 6f20: 06 68 00 01 1a 20 00 06 68 00 01 1a 20 00 06 68 6f30: 00 01 1a 20 00 06 68 00 01 1a 20 00 06 68 0d 50 6f40: 00 03 34 40 00 0d 50 00 03 34 40 00 0d 50 00 03 6f50: 34 40 00 0d 50 00 03 34 40 00 0d 50 00 03 34 40 6f60: 00 0d 50 00 03 34 40 00 0d 50 00 03 34 40 00 0d 6f70: 50 00 03 34 40 00 0d 50 00 03 34 40 00 0d 50 00 6f80: 03 34 40 00 0d 50 00 03 34 40 00 0d 50 00 03 34 6f90: 40 00 0d 50 00 03 34 40 00 0d 50 00 03 34 40 00 6fa0: 0d 50 00 03 34 40 00 0d 50 00 03 34 40 00 0d 50 6fb0: 00 03 34 40 00 0d 50 00 03 34 40 00 0d 50 00 03 6fc0: 34 40 00 0d 50 00 03 34 40 00 00 00 06 60 00 01 6fd0: 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 6fe0: 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 6ff0: 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 7000: 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 7010: 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 7020: 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 7030: 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 7040: 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 7050: 00 06 60 00 01 18 03 30 00 00 0c 40 00 03 30 00 7060: 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 7070: 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 7080: 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 7090: 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 70a0: 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 70b0: 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 70c0: 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 70d0: 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 70e0: 40 00 00 01 16 60 00 05 58 00 01 16 60 00 05 58 70f0: 00 01 16 60 00 05 58 00 01 16 60 00 05 58 00 01 7100: 16 60 00 05 58 00 01 16 60 00 05 58 00 01 16 60 7110: 00 05 58 00 01 16 60 00 05 58 00 01 16 60 00 05 7120: 58 00 01 16 60 00 05 58 00 01 16 60 00 05 58 00 7130: 01 16 60 00 05 58 00 01 16 60 00 05 58 00 01 16 7140: 60 00 05 58 00 01 16 60 00 05 58 00 01 16 60 00 7150: 05 58 00 01 16 60 00 05 58 00 01 16 60 00 05 58 7160: 00 01 16 60 00 05 58 00 01 16 60 00 05 58 0b 30 7170: 00 02 2c 40 00 0b 30 00 02 2c 40 00 0b 30 00 02 7180: 2c 40 00 0b 30 00 02 2c 40 00 0b 30 00 02 2c 40 7190: 00 0b 30 00 02 2c 40 00 0b 30 00 02 2c 40 00 0b 71a0: 30 00 02 2c 40 00 0b 30 00 02 2c 40 00 0b 30 00 71b0: 02 2c 40 00 0b 30 00 02 2c 40 00 0b 30 00 02 2c 71c0: 40 00 0b 30 00 02 2c 40 00 0b 30 00 02 2c 40 00 71d0: 0b 30 00 02 2c 40 00 0b 30 00 02 2c 40 00 0b 30 71e0: 00 02 2c 40 00 0b 30 00 02 2c 40 00 0b 30 00 02 71f0: 2c 40 00 0b 30 00 02 2c 40 00 00 00 0e 60 00 03 7200: 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 7210: 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 00 0e 7220: 60 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 7230: 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 7240: 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 00 7250: 0e 60 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 7260: 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 7270: 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 7280: 00 0e 60 00 03 38 07 70 00 01 1c 40 00 07 70 00 7290: 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 72a0: 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 40 00 72b0: 07 70 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 72c0: 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 72d0: 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 40 72e0: 00 07 70 00 01 1c 40 00 07 70 00 01 1c 40 00 07 72f0: 70 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 7300: 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 7310: 40 00 00 01 1e 60 00 07 78 00 01 1e 60 00 07 78 7320: 00 01 1e 60 00 07 78 00 01 1e 60 00 07 78 00 01 7330: 1e 60 00 07 78 00 01 1e 60 00 07 78 00 01 1e 60 7340: 00 07 78 00 01 1e 60 00 07 78 00 01 1e 60 00 07 7350: 78 00 01 1e 60 00 07 78 00 01 1e 60 00 07 78 00 7360: 01 1e 60 00 07 78 00 01 1e 60 00 07 78 00 01 1e 7370: 60 00 07 78 00 01 1e 60 00 07 78 00 01 1e 60 00 7380: 07 78 00 01 1e 60 00 07 78 00 01 1e 60 00 07 78 7390: 00 01 1e 60 00 07 78 00 01 1e 60 00 07 78 0f 70 73a0: 00 03 3c 40 00 0f 70 00 03 3c 40 00 0f 70 00 03 73b0: 3c 40 00 0f 70 00 03 3c 40 00 0f 70 00 03 3c 40 73c0: 00 0f 70 00 03 3c 40 00 0f 70 00 03 3c 40 00 0f 73d0: 70 00 03 3c 40 00 0f 70 00 03 3c 40 00 0f 70 00 73e0: 03 3c 40 00 0f 70 00 03 3c 40 00 0f 70 00 03 3c 73f0: 40 00 0f 70 00 03 3c 40 00 0f 70 00 03 3c 40 00 7400: 0f 70 00 03 3c 40 00 0f 70 00 03 3c 40 00 0f 70 7410: 00 03 3c 40 00 0f 70 00 03 3c 40 00 0f 70 00 03 7420: 3c 40 00 0f 70 00 03 3c 40 00 aa c2 da f2 0a 22 7430: 3a 52 6a 82 9a b2 ca e2 fa 12 62 63 64 65 67 68 7440: 69 6a 6b 6c 6d 6e 6f 70 71 73 36 4e 66 7e 96 ae 7450: c6 de f6 0e 26 3e 56 6e 86 9e 63 64 65 66 67 68 7460: 69 6a 6b 6d 6e 6f 70 71 72 73 00 00 00 00 00 00 7470: 00 00 80 80 80 80 80 80 80 80 00 00 00 00 00 00 7480: 00 00 80 80 80 80 80 80 80 80 00 00 00 00 00 00 7490: 00 00 80 80 80 80 80 80 80 80 00 00 00 00 00 00 74a0: 00 00 80 80 80 80 80 80 80 80 28 28 28 28 28 28 74b0: 28 28 a8 a8 a8 a8 a8 a8 a8 a8 28 28 28 28 28 28 74c0: 28 28 a8 a8 a8 a8 a8 a8 a8 a8 28 28 28 28 28 28 74d0: 28 28 a8 a8 a8 a8 a8 a8 a8 a8 28 28 28 28 28 28 74e0: 28 28 a8 a8 a8 a8 a8 a8 a8 a8 50 50 50 50 50 50 74f0: 50 50 d0 d0 d0 d0 d0 d0 d0 d0 50 50 50 50 50 50 7500: 50 50 d0 d0 d0 d0 d0 d0 d0 d0 50 50 50 50 50 50 7510: 50 50 d0 d0 d0 d0 d0 d0 d0 d0 50 50 50 50 50 50 7520: 50 50 d0 d0 d0 d0 d0 d0 d0 d0 20 24 28 2c 30 34 7530: 38 3c 20 24 28 2c 30 34 38 3c 21 25 29 2d 31 35 7540: 39 3d 21 25 29 2d 31 35 39 3d 22 26 2a 2e 32 36 7550: 3a 3e 22 26 2a 2e 32 36 3a 3e 23 27 2b 2f 33 37 7560: 3b 3f 23 27 2b 2f 33 37 3b 3f 20 24 28 2c 30 34 7570: 38 3c 20 24 28 2c 30 34 38 3c 21 25 29 2d 31 35 7580: 39 3d 21 25 29 2d 31 35 39 3d 22 26 2a 2e 32 36 7590: 3a 3e 22 26 2a 2e 32 36 3a 3e 23 27 2b 2f 33 37 75a0: 3b 3f 23 27 2b 2f 33 37 3b 3f 20 24 28 2c 30 34 75b0: 38 3c 20 24 28 2c 30 34 38 3c 21 25 29 2d 31 35 75c0: 39 3d 21 25 29 2d 31 35 39 3d 22 26 2a 2e 32 36 75d0: 3a 3e 22 26 2a 2e 32 36 3a 3e 23 27 2b 2f 33 37 75e0: 3b 3f 23 27 2b 2f 33 37 3b 3f 6000G
Under my benchmarks this optimizaiton clocks in at 1.6s per screen or 0.62 FPS

How to (quickly) draw a line:


Our code is now sufficiently modular that we can get back to the business of line drawing.  Our goal is to create a program which can take the start and end points of a line and plot all the points in between.  Many of us might be look to our high-school algebra for the answer.  Many of us were taught that the formula for a line is written like so:



Where "m" is the slope - how steep the line is and "b" is the point where the line intercepts with the Y axis.  So if we had a line between (4,2) and (16,2) we could figure out the lines formula like in the following way:


Once we had that we could just run through all the X co-ordinates between 4 and 16 and round up to the nearest whole number to find all the points on this line.

XY (1/3X+2/3)Y (rounded)Point
422(4,2)
52.33332(5,2)
62.66663(6,3)
733(7,3)
83.33333(8,3)
93.66664(9,4)
1044(10,4)
114.33334(11,4)
124.66665(12,5)
1355(13,5)
145.33335(14,5)
155.66666(15,6)
1666(16,6)

The problem here should be pretty clear.  It takes a number of mathematical operations to get the slope of a line, more to solve for b and several to plot each point.  Too many to solve even using tables. Not to mention that most of these calculations involve decimals, something the 6502 doesn't handle natively.

Enter Bresenham's Line:


Jack Elton Bresenham was a technical staff member at IBM and in 1962 he found himself with the problem of using a digital plotter to draw a line.  The device could move in one of eight directions at steps as small as 1/100th of an inch.  Which meant that when the plotter had to draw a long line at a particular angle.  The line had to be broken up into a series of points which the plotter would then connect.  While it wasn't the intent of his original article, breaking up a line into a series of points is called rasterization and is effectively what we are trying to do.


How it works:


Just like how Bresenham's plotter only moved discrete steps in a limited number of directions.  Every line on a pixel display can only move in two.  Either it moves one step along it's major axis, the direction (Horizontal or vertical) which our line moves farthest or it moves one step along both it's major and minor axes.


So what our algorithm really needs to answer is: How many moves on the major access do we need to make before we move once on the minor axis?

Earlier when we talked about high school algebra you might recall that the slope of a line is not just defined as it's "steepness" or DX/DY but is also sometimes referred to as "rise over run". The proportion of Y movements to X movements. So if we know the slope, we already have our answer and when given the start and end points of a line the slope is easy to calculate it's just X1-X0/Y1-Y0, where (X0,Y0) and (X1,Y1) are the beginning and ending points of the line respectively.

There is still one problem though. Often, as in the example above when we calculate slope we end up with a result that is a fraction or decimal, neither thing a 6502 can work with very easily.

Again, there is a simple solution. In the same way that multiplication is simply repeated addition i.e. 3 * 7 = 7 + 7 + 7. Division can be calculated by repeated subtraction. So if, as is our case here DX is larger than DY.  We can store DX in a variable that we will call "epsilon" and before each plot we will subtract DY, if this variable dips below zero. Then we know we need to move down the minor axis. At which point we will add DX to epsilon and start the process all over again.

Ok, that sounds all well and good but does it work? Good question! Here's a chart using our new algorithm:

Plot #LocationEpsilonDXDY
1(4,2) 12 12 4
2(5,2)8124
3(6,2)4124
4(7,3)12124
5(8,3)8124
6(9,3)4124
7(10,4)12124
8(11,4)6124
9(12,4)3124
10(13,5)12124
11(14,5)6124
12(15,5)3124
13(16,6)12124


As you can see it comes pretty close to matching the points we calculated using the slope of the line. The only difference is that our algorithm has three points at Y=2 and only one at Y=6. Whereas when we were rounding the calculated values of each point we had two points with Y=2 and two points with Y=6.

This comes from our initial choice for epsilon being a little too high. You can see this clearly if you attempt to plot a really shallow line like (0,0) to (138,1).  Epsilon will start at 138 and it will take 138 plots before it will reach zero.  This results in a line that would look identical to a line drawn from (0,0) to (138,0) up until the very last point.  

To fix this, we start epsilon at DX/2 and our line matches perfectly in both cases. Not only that but the complexity has dropped dramatically since we can now calculate our points just by using two additions.

But we can do even better. Thinking for a moment about how to implement this on the 6502. Normally to check if the value has crossed a boundary we need to do a CMP and then branch on the basis of the result.  However we can avoid this comparison altogether by initially setting epsilon equal to 255-DX/2.  When DX > 255 the carry flag will be set and we will know it's time to make a move on the minor axis.

With these ideas in mind we can write the following assembly:
*DEMO 3
*Use DH.PLOT to draw coloured lines from X0,Y0 to X1,Y1
*Jonathan Graham/Battlestations
*Free for non-commercial use with attribution
*
 XC ;Required for Merlin Pro to use 65C02 instructions
 ORG $6000 ;Start assembling at memory location $6000
COLOUR EQU $F6 
X0 EQU $F7
Y0 EQU $F8
X1 EQU $F9
Y1 EQU $FA
DX EQU $FB
DY EQU $FC
EPSILON EQU $FD
 PUT DH.INIT
 LDA #$00 ;Start our line at (0,0)
 STA X0
 LDA #$00
 STA Y0
 LDA #$8B ;End our line at (139,138)
 STA X1
:LOOP1 LDA #$8A
 STA Y1
:LOOP2 LDA COLOUR ;Set the colour to whatever is in $F6
 AND #$0F ;AND the four high bits to make a valid colour
 JSR SETDCOLOR ;Set colour to whatever is in $F6
 JSR NXTLINE ;Draw our line
 DEC COLOUR ;Change the colour
 LDA Y1 ;Move the end of the line up one column
 DEC
 CMP #$FF ;See if we're done
 STA Y1
 BNE :LOOP2 ;Not done draw another line
 BRA :LOOP1 ;Done, reset the line and keep drawing
NXTLINE LDA Y1 ;Calculate DY as Y1-Y0
 SEC
 SBC Y0
 STA DY
 LDA X1 ;Calculate DX as X1-X0
 SEC
 SBC X0
 STA DX
 LSR ; Set epsilon to DX/2
 EOR #$FF
 STA EPSILON
 LDX X0 ;Start plotting at (X0,Y0)
 LDY Y0 
NXTVERT PUT DH.ROWSET ;Calculate horizontal value
NXTHORZ PHY
 PUT DH.PLOT ;Plot point
 PLY
 CPX X1 ;Are we done?
 BEQ ENDLINE
 INX ;Move along major axis
 LDA EPSILON ;Epsilon = Epsilon + DY
 CLC
 ADC DY
 STA EPSILON
 BCC NXTHORZ ;If Epsilon hasn't rolled over move to next point 
 SEC ;If epsilon has rolled over subtract DX
 SBC DX
 STA EPSILON
 INY ;Move along minor axis
 BRA NXTVERT
ENDLINE RTS
SETDCOLOR TAY ;Assume the desired colour is in the accumulator
 PUT DH.COLOUR
 RTS
 PUT DH.TABLES
Lest we forget a binary copy...
CALL -151 6000: 8d 50 c0 8d 57 c0 8d 52 c0 8d 5e c0 8d 0d c0 8d 6010: 01 c0 a9 00 85 f7 a9 00 85 f8 a9 8b 85 f9 a9 8a 6020: 85 fa a5 f6 29 0f 20 97 60 20 39 60 c6 f6 a5 fa 6030: 3a c9 ff 85 fa d0 eb 80 e5 a5 fa 38 e5 f8 85 fc 6040: a5 f9 38 e5 f7 85 fb 4a 49 ff 85 fd a6 f7 a4 f8 6050: b9 a1 74 85 1d b9 61 75 85 1e 5a bc b1 60 30 0d 6060: 8d 54 c0 b1 1d 3d c9 61 1d 71 69 91 1d bc 3d 61 6070: 30 0d 8d 55 c0 b1 1d 3d 55 62 1d fd 69 91 1d 7a 6080: e4 f9 f0 12 e8 a5 fd 18 65 fc 85 fd 90 cc 38 e5 6090: fb 85 fd c8 80 ba 60 a8 b9 61 74 8d 69 60 b9 71 60a0: 74 8d 6a 60 b9 81 74 8d 7b 60 b9 91 74 8d 7c 60 60b0: 60 ff 00 00 00 ff 01 01 ff 02 02 02 ff 03 03 ff 60c0: 04 04 04 ff 05 05 ff 06 06 06 ff 07 07 ff 08 08 60d0: 08 ff 09 09 ff 0a 0a 0a ff 0b 0b ff 0c 0c 0c ff 60e0: 0d 0d ff 0e 0e 0e ff 0f 0f ff 10 10 10 ff 11 11 60f0: ff 12 12 12 ff 13 13 ff 14 14 14 ff 15 15 ff 16 6100: 16 16 ff 17 17 ff 18 18 18 ff 19 19 ff 1a 1a 1a 6110: ff 1b 1b ff 1c 1c 1c ff 1d 1d ff 1e 1e 1e ff 1f 6120: 1f ff 20 20 20 ff 21 21 ff 22 22 22 ff 23 23 ff 6130: 24 24 24 ff 25 25 ff 26 26 26 ff 27 27 00 00 ff 6140: 01 01 01 ff 02 02 ff 03 03 03 ff 04 04 ff 05 05 6150: 05 ff 06 06 ff 07 07 07 ff 08 08 ff 09 09 09 ff 6160: 0a 0a ff 0b 0b 0b ff 0c 0c ff 0d 0d 0d ff 0e 0e 6170: ff 0f 0f 0f ff 10 10 ff 11 11 11 ff 12 12 ff 13 6180: 13 13 ff 14 14 ff 15 15 15 ff 16 16 ff 17 17 17 6190: ff 18 18 ff 19 19 19 ff 1a 1a ff 1b 1b 1b ff 1c 61a0: 1c ff 1d 1d 1d ff 1e 1e ff 1f 1f 1f ff 20 20 ff 61b0: 21 21 21 ff 22 22 ff 23 23 23 ff 24 24 f4 25 25 61c0: 25 ff 26 26 ff 27 27 27 ff 7f 7e 61 1f 7f 78 07 61d0: 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 7f 7e 61e0: 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 61f0: 7f 78 07 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 6200: 07 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 7f 6210: 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 7f 7e 61 6220: 1f 7f 78 07 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 6230: 78 07 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 6240: 7f 7e 61 1f 7f 78 07 7f 7e 61 1f 7f 78 07 7f 7e 6250: 61 1f 7f 78 07 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 6260: 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 6270: 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 6280: 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 6290: 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 62a0: 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 62b0: 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 62c0: 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 62d0: 43 3f 7f 70 0f 7f 7c 43 3f 7f 70 0f 7f 7c 43 3f 62e0: 7f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 62f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6320: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63f0: 00 00 00 00 00 00 00 00 00 00 01 10 00 00 04 40 6400: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 6410: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 6420: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6430: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 6440: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 6450: 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 6460: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 6470: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 6480: 10 00 00 04 40 08 00 00 02 20 00 00 08 00 00 02 6490: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 64a0: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 64b0: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 64c0: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 64d0: 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 64e0: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 64f0: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 6500: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 6510: 00 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 6520: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 6530: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 6540: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 6550: 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 6560: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 6570: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 6580: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 6590: 00 08 00 00 02 20 00 00 08 00 00 02 20 04 40 00 65a0: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 65b0: 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 65c0: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 65d0: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 65e0: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 65f0: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6600: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 6610: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 6620: 00 00 04 40 00 01 10 00 00 00 01 18 00 00 06 60 6630: 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 6640: 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 6650: 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 6660: 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 6670: 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 6680: 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 6690: 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 66a0: 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 66b0: 18 00 00 06 60 0c 40 00 03 30 00 00 0c 40 00 03 66c0: 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 66d0: 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 66e0: 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 66f0: 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 6700: 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 6710: 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 6720: 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 6730: 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 6740: 00 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 6750: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6760: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 6770: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 6780: 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 6790: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 67a0: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 67b0: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 67c0: 00 04 40 00 01 10 00 00 04 40 00 01 10 02 20 00 67d0: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 67e0: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 67f0: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 6800: 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 6810: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 6820: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 6830: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 6840: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 6850: 00 00 02 20 00 00 08 00 00 00 01 14 40 00 05 50 6860: 00 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 6870: 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 40 6880: 00 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 6890: 50 00 01 14 40 00 05 50 00 01 14 40 00 05 50 00 68a0: 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 68b0: 40 00 05 50 00 01 14 40 00 05 50 00 01 14 40 00 68c0: 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 50 68d0: 00 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 68e0: 14 40 00 05 50 0a 20 00 02 28 00 00 0a 20 00 02 68f0: 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 6900: 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 00 0a 6910: 20 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 6920: 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 6930: 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 00 6940: 0a 20 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 6950: 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 6960: 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 6970: 00 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 6980: 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 6990: 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 69a0: 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 69b0: 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 69c0: 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 69d0: 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 69e0: 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 69f0: 00 0c 40 00 03 30 00 00 0c 40 00 03 30 06 60 00 6a00: 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 6a10: 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 6a20: 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 6a30: 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 6a40: 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 6a50: 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 6a60: 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 6a70: 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 6a80: 00 00 06 60 00 01 18 00 00 00 01 1c 40 00 07 70 6a90: 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 6aa0: 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 40 6ab0: 00 07 70 00 01 1c 40 00 07 70 00 01 1c 40 00 07 6ac0: 70 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 6ad0: 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 6ae0: 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 40 00 6af0: 07 70 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 6b00: 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 6b10: 1c 40 00 07 70 0e 60 00 03 38 00 00 0e 60 00 03 6b20: 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 6b30: 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 00 0e 6b40: 60 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 6b50: 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 6b60: 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 00 6b70: 0e 60 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 6b80: 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 6b90: 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 6ba0: 00 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 6bb0: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 6bc0: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 6bd0: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 6be0: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 6bf0: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 6c00: 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 6c10: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 6c20: 00 02 20 00 00 08 00 00 02 20 00 00 08 01 10 00 6c30: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6c40: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 6c50: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 6c60: 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 6c70: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 6c80: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 6c90: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 6ca0: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6cb0: 40 00 01 10 00 00 04 40 00 00 01 12 20 00 04 48 6cc0: 00 01 12 20 00 04 48 00 01 12 20 00 04 48 00 01 6cd0: 12 20 00 04 48 00 01 12 20 00 04 48 00 01 12 20 6ce0: 00 04 48 00 01 12 20 00 04 48 00 01 12 20 00 04 6cf0: 48 00 01 12 20 00 04 48 00 01 12 20 00 04 48 00 6d00: 01 12 20 00 04 48 00 01 12 20 00 04 48 00 01 12 6d10: 20 00 04 48 00 01 12 20 00 04 48 00 01 12 20 00 6d20: 04 48 00 01 12 20 00 04 48 00 01 12 20 00 04 48 6d30: 00 01 12 20 00 04 48 00 01 12 20 00 04 48 00 01 6d40: 12 20 00 04 48 09 10 00 02 24 40 00 09 10 00 02 6d50: 24 40 00 09 10 00 02 24 40 00 09 10 00 02 24 40 6d60: 00 09 10 00 02 24 40 00 09 10 00 02 24 40 00 09 6d70: 10 00 02 24 40 00 09 10 00 02 24 40 00 09 10 00 6d80: 02 24 40 00 09 10 00 02 24 40 00 09 10 00 02 24 6d90: 40 00 09 10 00 02 24 40 00 09 10 00 02 24 40 00 6da0: 09 10 00 02 24 40 00 09 10 00 02 24 40 00 09 10 6db0: 00 02 24 40 00 09 10 00 02 24 40 00 09 10 00 02 6dc0: 24 40 00 09 10 00 02 24 40 00 09 10 00 02 24 40 6dd0: 00 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 6de0: 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 00 0a 6df0: 20 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 6e00: 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 6e10: 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 00 6e20: 0a 20 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 6e30: 00 02 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 6e40: 28 00 00 0a 20 00 02 28 00 00 0a 20 00 02 28 00 6e50: 00 0a 20 00 02 28 00 00 0a 20 00 02 28 05 50 00 6e60: 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 6e70: 40 00 05 50 00 01 14 40 00 05 50 00 01 14 40 00 6e80: 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 50 6e90: 00 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 6ea0: 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 40 6eb0: 00 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 6ec0: 50 00 01 14 40 00 05 50 00 01 14 40 00 05 50 00 6ed0: 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 6ee0: 40 00 05 50 00 01 14 40 00 00 01 1a 20 00 06 68 6ef0: 00 01 1a 20 00 06 68 00 01 1a 20 00 06 68 00 01 6f00: 1a 20 00 06 68 00 01 1a 20 00 06 68 00 01 1a 20 6f10: 00 06 68 00 01 1a 20 00 06 68 00 01 1a 20 00 06 6f20: 68 00 01 1a 20 00 06 68 00 01 1a 20 00 06 68 00 6f30: 01 1a 20 00 06 68 00 01 1a 20 00 06 68 00 01 1a 6f40: 20 00 06 68 00 01 1a 20 00 06 68 00 01 1a 20 00 6f50: 06 68 00 01 1a 20 00 06 68 00 01 1a 20 00 06 68 6f60: 00 01 1a 20 00 06 68 00 01 1a 20 00 06 68 00 01 6f70: 1a 20 00 06 68 0d 50 00 03 34 40 00 0d 50 00 03 6f80: 34 40 00 0d 50 00 03 34 40 00 0d 50 00 03 34 40 6f90: 00 0d 50 00 03 34 40 00 0d 50 00 03 34 40 00 0d 6fa0: 50 00 03 34 40 00 0d 50 00 03 34 40 00 0d 50 00 6fb0: 03 34 40 00 0d 50 00 03 34 40 00 0d 50 00 03 34 6fc0: 40 00 0d 50 00 03 34 40 00 0d 50 00 03 34 40 00 6fd0: 0d 50 00 03 34 40 00 0d 50 00 03 34 40 00 0d 50 6fe0: 00 03 34 40 00 0d 50 00 03 34 40 00 0d 50 00 03 6ff0: 34 40 00 0d 50 00 03 34 40 00 0d 50 00 03 34 40 7000: 00 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 7010: 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 7020: 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 7030: 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 7040: 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 7050: 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 7060: 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 7070: 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 7080: 00 06 60 00 01 18 00 00 06 60 00 01 18 03 30 00 7090: 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 70a0: 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 70b0: 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 70c0: 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 70d0: 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 70e0: 00 03 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 70f0: 30 00 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 7100: 00 0c 40 00 03 30 00 00 0c 40 00 03 30 00 00 0c 7110: 40 00 03 30 00 00 0c 40 00 00 01 16 60 00 05 58 7120: 00 01 16 60 00 05 58 00 01 16 60 00 05 58 00 01 7130: 16 60 00 05 58 00 01 16 60 00 05 58 00 01 16 60 7140: 00 05 58 00 01 16 60 00 05 58 00 01 16 60 00 05 7150: 58 00 01 16 60 00 05 58 00 01 16 60 00 05 58 00 7160: 01 16 60 00 05 58 00 01 16 60 00 05 58 00 01 16 7170: 60 00 05 58 00 01 16 60 00 05 58 00 01 16 60 00 7180: 05 58 00 01 16 60 00 05 58 00 01 16 60 00 05 58 7190: 00 01 16 60 00 05 58 00 01 16 60 00 05 58 00 01 71a0: 16 60 00 05 58 0b 30 00 02 2c 40 00 0b 30 00 02 71b0: 2c 40 00 0b 30 00 02 2c 40 00 0b 30 00 02 2c 40 71c0: 00 0b 30 00 02 2c 40 00 0b 30 00 02 2c 40 00 0b 71d0: 30 00 02 2c 40 00 0b 30 00 02 2c 40 00 0b 30 00 71e0: 02 2c 40 00 0b 30 00 02 2c 40 00 0b 30 00 02 2c 71f0: 40 00 0b 30 00 02 2c 40 00 0b 30 00 02 2c 40 00 7200: 0b 30 00 02 2c 40 00 0b 30 00 02 2c 40 00 0b 30 7210: 00 02 2c 40 00 0b 30 00 02 2c 40 00 0b 30 00 02 7220: 2c 40 00 0b 30 00 02 2c 40 00 0b 30 00 02 2c 40 7230: 00 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 7240: 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 00 0e 7250: 60 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 7260: 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 7270: 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 00 7280: 0e 60 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 7290: 00 03 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 72a0: 38 00 00 0e 60 00 03 38 00 00 0e 60 00 03 38 00 72b0: 00 0e 60 00 03 38 00 00 0e 60 00 03 38 07 70 00 72c0: 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 72d0: 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 40 00 72e0: 07 70 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 72f0: 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 7300: 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 40 7310: 00 07 70 00 01 1c 40 00 07 70 00 01 1c 40 00 07 7320: 70 00 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 7330: 01 1c 40 00 07 70 00 01 1c 40 00 07 70 00 01 1c 7340: 40 00 07 70 00 01 1c 40 00 00 01 1e 60 00 07 78 7350: 00 01 1e 60 00 07 78 00 01 1e 60 00 07 78 00 01 7360: 1e 60 00 07 78 00 01 1e 60 00 07 78 00 01 1e 60 7370: 00 07 78 00 01 1e 60 00 07 78 00 01 1e 60 00 07 7380: 78 00 01 1e 60 00 07 78 00 01 1e 60 00 07 78 00 7390: 01 1e 60 00 07 78 00 01 1e 60 00 07 78 00 01 1e 73a0: 60 00 07 78 00 01 1e 60 00 07 78 00 01 1e 60 00 73b0: 07 78 00 01 1e 60 00 07 78 00 01 1e 60 00 07 78 73c0: 00 01 1e 60 00 07 78 00 01 1e 60 00 07 78 00 01 73d0: 1e 60 00 07 78 0f 70 00 03 3c 40 00 0f 70 00 03 73e0: 3c 40 00 0f 70 00 03 3c 40 00 0f 70 00 03 3c 40 73f0: 00 0f 70 00 03 3c 40 00 0f 70 00 03 3c 40 00 0f 7400: 70 00 03 3c 40 00 0f 70 00 03 3c 40 00 0f 70 00 7410: 03 3c 40 00 0f 70 00 03 3c 40 00 0f 70 00 03 3c 7420: 40 00 0f 70 00 03 3c 40 00 0f 70 00 03 3c 40 00 7430: 0f 70 00 03 3c 40 00 0f 70 00 03 3c 40 00 0f 70 7440: 00 03 3c 40 00 0f 70 00 03 3c 40 00 0f 70 00 03 7450: 3c 40 00 0f 70 00 03 3c 40 00 0f 70 00 03 3c 40 7460: 00 e1 f9 11 29 41 59 71 89 a1 b9 d1 e9 01 19 31 7470: 49 62 63 65 66 67 68 69 6a 6b 6c 6d 6e 70 71 72 7480: 73 6d 85 9d b5 cd e5 fd 15 2d 45 5d 75 8d a5 bd 7490: d5 63 64 65 66 67 68 69 6b 6c 6d 6e 6f 70 71 72 74a0: 73 00 00 00 00 00 00 00 00 80 80 80 80 80 80 80 74b0: 80 00 00 00 00 00 00 00 00 80 80 80 80 80 80 80 74c0: 80 00 00 00 00 00 00 00 00 80 80 80 80 80 80 80 74d0: 80 00 00 00 00 00 00 00 00 80 80 80 80 80 80 80 74e0: 80 28 28 28 28 28 28 28 28 a8 a8 a8 a8 a8 a8 a8 74f0: a8 28 28 28 28 28 28 28 28 a8 a8 a8 a8 a8 a8 a8 7500: a8 28 28 28 28 28 28 28 28 a8 a8 a8 a8 a8 a8 a8 7510: a8 28 28 28 28 28 28 28 28 a8 a8 a8 a8 a8 a8 a8 7520: a8 50 50 50 50 50 50 50 50 d0 d0 d0 d0 d0 d0 d0 7530: d0 50 50 50 50 50 50 50 50 d0 d0 d0 d0 d0 d0 d0 7540: d0 50 50 50 50 50 50 50 50 d0 d0 d0 d0 d0 d0 d0 7550: d0 50 50 50 50 50 50 50 50 d0 d0 d0 d0 d0 d0 d0 7560: d0 20 24 28 2c 30 34 38 3c 20 24 28 2c 30 34 38 7570: 3c 21 25 29 2d 31 35 39 3d 21 25 29 2d 31 35 39 7580: 3d 22 26 2a 2e 32 36 3a 3e 22 26 2a 2e 32 36 3a 7590: 3e 23 27 2b 2f 33 37 3b 3f 23 27 2b 2f 33 37 3b 75a0: 3f 20 24 28 2c 30 34 38 3c 20 24 28 2c 30 34 38 75b0: 3c 21 25 29 2d 31 35 39 3d 21 25 29 2d 31 35 39 75c0: 3d 22 26 2a 2e 32 36 3a 3e 22 26 2a 2e 32 36 3a 75d0: 3e 23 27 2b 2f 33 37 3b 3f 23 27 2b 2f 33 37 3b 75e0: 3f 20 24 28 2c 30 34 38 3c 20 24 28 2c 30 34 38 75f0: 3c 21 25 29 2d 31 35 39 3d 21 25 29 2d 31 35 39 7600: 3d 22 26 2a 2e 32 36 3a 3e 22 26 2a 2e 32 36 3a 7610: 3e 23 27 2b 2f 33 37 3b 3f 23 27 2b 2f 33 37 3b 7620: 3f 6000G
That gets us pretty far but in case you haven't noticed what we've written only works for cases where DX > DY and X0 < X1  and  Y0 < Y1.  

Change any one of those assumptions and our line won't get where we want it to go. Some people solve this by coding eight separate cases, deciding which one is needed and then jumping to it.

We will have a slightly better solution than that but it will have to wait until my next post. While were at it we will also do some performance testing against other commercial and non-commercial DHR solutions, talk about further optimization and do some work with pseudo-3d demos.

2 comments:

vengefultacos said...

Excellent series so far! I hope you'll finish it!

Axon Punch said...

Thanks! I have one more larger post to finish off this bit. However summer is a paradoxically busy time for me. I had originally planned to get it all done by the end of May.

Apple II - Double Hi-Res From The Ground Up<br \>Part 9: API and a demo!

A better interface Perhaps you've noticed that all these drawing routines we've developed require a fair amount of memory to do an...