Sunday, August 20, 2017

Apple II - Double Hi-Res From The Ground Up
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 anything useful. Just look at this code snippet:
*Draw a line from (0,10) to (20,25)
LDA #$00
STA X0
LDA #$0A
STA Y0
LDA #$14
STA X1
LDA #$19
STA Y1
JSR NXTLINE
Each LDA and STA costs us two bytes - that sixteen in total! Add in the JSR and we are up to nineteen bytes just to draw a single line!  Our previous demo programs hid this expense by drawing only simple shapes (filling the screen with horizontal lines) and by using loops but if we ever want to make something more complex we are going to need a better approach.

Penguin had the right idea


The Graphics Magician, despite it's flaws in the speed department solved one problem well. People needed a better way to store Hi-res pictures and storing them as a series of line drawing commands is an idea that definitely saves on memory. So as any great artist does with any great idea - we're going to swipe it!

While we're in a larcenous mood let's steal another memory saving idea used by the ProDOS MLI (machine language interface) - storing our parameters in a data block located after we call our routine. Here's an example of what such a system would look like:
JSR DHDRAWAPI
DB 10 10 10 20 ;Draw a line from (16,16) to (16,32)
DB 10 20 20 20 ;Draw a line from (16,32) to (32,32)
DB 20 20 20 10 ;Draw a line from (32,32) to (32,16)
DB 20 10 10 10 ;Draw a line from (32,16) to (16,16)
DB 255 ;End of data
...and like magic we just cut our memory usage by 75%!

There's still room for improvement though! For example, notice how each line starts where the previous line left off? We can save more memory by creating a mode where we automatically treat the previous end of line (X1,Y1) as the beginning of the next (X0,Y0) leaving us only having to define our new endpoint.

To accomplish this we will add a "command" byte at the beginning of each line draw. If the byte is $00 then we are drawing a line from X0,Y0 to X1,Y1 and the next four bytes define the co-ordinates. If the byte is $01, then we are drawing a line from the previous X1,Y1 to the co-ordinate we provide. So our code will look something like this:
JSR DHDRAWAPI
DB 00 10 10 10 20 ;Draw a line from (10,10) to (10,20)
DB 01 20 20 ;Continue line to (20,20)
DB 01 20 10 ;Continue line to (20,10)
DB 01 10 10 ;Continue line to (10,10)
DB 255 ;End of data
Finally, let's add a way of changing the drawing colour in the same block of data. The Graphics Magician uses single byte codes to accomplish this which again seems like a prime idea to appropriate! We will implement it int the following way:

Command ByteColour NameBitpattern
$10Black0000
$11Magenta0001
$12Brown0010
$13Orange0011
$14Dark Green0100
$15Grey 10101
$16Yellow0110
$17Green0111
$18Dark Blue1000
$19Violet1001
$1AGrey 21010
$1BPink1011
$1CMedium Blue1100
$1DLight Blue1101
$1EAqua1110
$1FWhite1111

...and our image data will look like this:
JSR DHDRAWAPI
DB E1 ;Set colour to magenta
DB 00 10 10 10 20 ;Draw a line from (10,10) to (10,20)
DB E6 ;Set colour to green
DB 01 20 20 ;Continue line to (20,20)
DB EB ;Set colour to medium blue
DB 01 20 10 ;Continue line to (20,10)
DB EF ;Set colour to white
DB 01 10 10 ;Continue line to (10,10)
DB 255 ;End of data
Now we're talking! Drawing four lines, in four different colours would have cost us eighty-eight bytes!  Using our new API we can do it all in just nineteen!  Not only that but we can save and load our pictures as binary files — just like the Graphics Magician!

In fact, all we would need to do to achieve most of its functionality is add a few more commands such as plotting single points and performing flood fills.

So let's implement this!


Doing this magic is easier than you think. Every time we do a JSR the address of the very next byte is stored on the stack. The stack, in case you're not familiar with it is 256 bytes of memory from $100-$1FF. Located just above the zero page. Bytes are put on to the stack starting at the bottom ($100) and new data is added growing upward toward $1FF. The most common way to put data into this are of memory is by using a 6502 instruction like PHA. This takes the byte in the accumulator and puts in the next available stack location. Putting information on to the stack is called "pushing". The 6502 keeps track of which stack location we used last through a register called the stack pointer (SP). When you push something on the stack, it increments the SP and then stores the data in this new spot. When you tell the CPU to do a JSR it stores the address you made the call from by pushing both bytes of it onto the stack. Later on, when we do an RTS these bytes are retrieved — or "popped" — from the stack. The is one of the reasons that a JSR/RTS consumes so many clock cycles.

You can't read the stack pointer directly but you can copy it into the X register by using the TSX instruction. Armed with this knowledge we can start building our API by doing the following.
DHDRAWAPI TSX ;Move stack pointer to X
 LDA $101,X ;Load high-byte and low-byte of the
 STA CP ;return address at the top of the stack
 LDA $102,X ;Store it in a ZP location which we
 STA CP+1 ;will call CP (command pointer)
Here we simply copy the address pushed onto the stack by a JSR DHDRAWAPI into a couple of zero page locations which we will call the Command Pointer or CP. As we move through our block of drawing commands, we will keep moving CP to point to the address of the next byte. Then, when we're finished and want to get back to our program all we need to do is copy the address in CP back to the stack — overwriting our original address — and then perform an RTS. Like so!
ENDCMD TSX
 CLC
 LDA CP ;Add one to the CP and overwrite
 ADC #$01 ;the return address in the
 STA $101,X ;stack.
 LDA CP+1 ;Now when we RTS the CPU will
 ADC #$00 ;start executing just after our
 STA $102,X ;block of data
 RTS
Before we put this all together to make our DHDRAWAPI we need to first do some more code management but before we get into that here's just a taste of how you can roll your own MLI style API. This one has exactly two commands. The first one, $00 will take the next byte and output it on the text screen. The second command $01 will stop processing and return to the calling program.
*API.TEST
*Simple MLI-style API 
*Jonathan Graham/Battlestations
*Free for non-commercial use with attribution
*
 XC
 ORG $6000
CP EQU $F6
 JSR TEXTAPI ;Call our API
*Data bytes which just display the numbers from 0-5
 DB $00,$01,$00,$02,$00,$03,$00,$04,$00,$05
 DB $01
 RTS
TEXTAPI TSX ;Move stack pointer to X
 LDA $101,X ;Load high-byte and low-byte of the
 STA CP ;return address at the top of the stack
 LDA $102,X ;Store it in a ZP location which we
 STA CP+1 ;will call CP (command pointer)
NXTCMD LDY #$01 ;Grab the next byte which
 LDA (CP),Y ;should be our first command
 TAX ;Lookup the distance between
 LDA CMDTAB,X ;our jump point and the routine 
 STA CMDJMP+1 ;Modify the BRA to go to our routine
CMDJMP BRA ENDCMD ;Do our routine
PRINTCHR INY ;CMD 00 - print byte
 LDA (CP),Y 
 JSR $FDDA 
 CLC
 LDA #$02 ;Length of command
 ADC CP ;Move the CP to the next
 STA CP ;command
 LDA CP+1
 ADC #$00
 STA CP+1
 BRA NXTCMD
ENDCMD TSX ;CMD 01 - return to program
 CLC
 LDA CP ;Add one to the CP and overwrite
 ADC #$01 ;the return address in the
 STA $101,X ;stack.
 LDA CP+1 ;Now when we RTS the CPU will
 ADC #$00 ;start executing just after our
 STA $102,X ;block of data
 RTS 
CMDTAB DB PRINTCHR-PRINTCHR,ENDCMD-PRINTCHR
For efficiency, we've implemented each command as part of a jump table. This is a data structure where a number of different routines can be called from the same position using the value in a register. This avoids the cumbersome way we often have to implement a series of "IF...THEN" statements in assembly by doing a series of compares and branches such as...
LDA DATA ;Grab some piece of data
CMP #$01 ;Is it a one?
BEQ DOFIRST ;Yes, do the first routine
CMP #$02 ;Is it a two?
BEQ DOSECOND ;Yes, do the second routine
CMP #$03 ;Is it a three?
BEQ DOTHIRD ;Yes, do third routine
BNE DEFAULT ;It's none of these, do something else
As you can see in our API demo. We use self-modifying code to alter the parameter of a BRA instruction. Since BRA requires a parameter telling us the distance to the instruction we want to branch to. We create a table which contains these distances by telling the assembler to subtract the memory location we are starting at (CMDJMP) from the memory location we want to jump to (PRINTCHR or ENDCMD).

It's worth noting that this approach has one important limitation. Branch instructions can only move us 127 locations forward or 128 locations backward. So we will never be able to jump to a label that's more than 127 bytes away. That said, we could always switch to using self-modifying code to alter the destination of a JMP instruction or even use the 65C02 instruction JMP (-some address-,X) which is designed to implement exactly this kind of system.

Our current implementation is faster than either of those two approaches, so we'll stick with it for now. Oh...and here's a binary version of the API demo you can paste into an emulator
CALL -151 6000: 20 0F 60 00 01 00 02 00 03 00 04 00 05 01 60 BA 6010: BD 01 01 85 AA BD 02 01 85 AB A0 01 B1 AA AA BD 6020: 4C 60 8D 26 60 80 15 C8 B1 AA 20 DA FD 18 A9 02 6030: 65 AA 85 AA A5 AB 69 00 85 AB 80 DE BA A5 AA 69 6040: 01 9D 01 01 A5 AB 69 00 9D 02 01 60 00 15 6000G

More code management


We've been using the pseudo op-code PUT to make our source code easier to read and to allow us the option of re-using parts of it in other projects. Our line drawing routine is now sufficiently large that it deserves a module of it's own. However the Merlin assembler won't let us PUT files that have PUT statements in them already. Since our line drawing algorithm uses PUT to insert our plotting routine we're going to have to make some changes.

There is more than one way to handle this problem (especially if you count using an assembler without this limitation). For today we're going to take the easiest path and simply combine the line drawing routine and the plotting routine into a single file called DH.LINE.
*DH.LINE
*8 bit Bresenham DHR line drawing algorithim
*Requires: DH.TABLES
*Jonathan Graham/Battlestations
*Free for non-commercial use with attribution
*
 LDX X0
 LDY Y0
 STY KEEPY ;Initialize Y preserving spot
 LDA Y1 ;Calculate DY as Y1-Y0
 SEC
 SBC Y0
 BCC Y0BIGGER
 STA DY
 LDA #$C8 ;SMC: Insert INY at CHGYA/CHGYB
 BRA CHGY
Y0BIGGER EOR #$FF
 STA DY
 LDA #$88 ;SMC: Insert DEY at CHGYA/CHGYB
CHGY STA CHGYA
 STA CHGYB
 LDA X1 ;Calculate DX as X1-X0
 SEC
 SBC X0
 BCC X0BIGGER
 STA DX
 LDA #$E8 ;SMC: Insert INX at CHGXA/CHGXB
 BRA CHGX
X0BIGGER EOR #$FF
 STA DX
 LDA #$CA ;SMC: Insert DEX at CHGXA/CHGXB
CHGX STA CHGXA
 STA CHGXB
 LDA DX
 CMP DY
 BCS DXLINE
DYLINE LDA DY ;Set EPSILON to 255-(DY/2)
 LSR
 EOR #$FF
 STA EPSILON
*DH.ROWSET Begins here
DYHORZ 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
 STY KEEPY
*DH.PLOT Begins here
PLOTDYL 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
 LDY KEEPY
 CPY Y1 ;Are we done?
 BEQ ENDLINE
CHGYA INY ;Move along major axis
 LDA EPSILON ;Epsilon = Epsilon + DX
 CLC
 ADC DX
 STA EPSILON
 BCC DYHORZ ;NOTE: we've changed Y so we need to call DH.ROWSET
 SEC ;If epsilon has rolled over subtract DX
 SBC DY
 STA EPSILON
CHGXA INX ;Move along minor axis
 BRA DYHORZ
DXLINE LDA DX ;Set EPSILON to 255-(DX/2)
 LSR
 EOR #$FF
 STA EPSILON
*DH.ROWSET Begins here
DXVERT 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
*DH.PLOT Begins here
DXHORZ 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
 CPX X1 ;Are we done?
 BEQ ENDLINE
CHGXB INX ;Move along major axis
 LDA EPSILON ;Epsilon = Epsilon + DY
 CLC
 ADC DY
 STA EPSILON
 BCC DXHORZ ;If Epsilon hasn't rolled over move to next point
 SEC ;If epsilon has rolled over subtract DX
 SBC DX
 STA EPSILON
 LDY KEEPY
CHGYB INY ;Move along minor axis
 STY KEEPY
 BRA DXVERT
ENDLINE RTS

Clearing things up


One more thing we need before we start writing some actually useful DHR programs. We need an efficient way to clear the screen. There are many ways to do this, all of which make the classic trade off between speed and memory. I'm going to select one of my favorites since it's virtually instantaneous ( < 0.05s ), consumes a mere 113 bytes, and as a bonus you can clear the screen black, white or grey by setting the accumulator to $00,$FF or $55 respectively prior to calling it. Create a new file in your assembler, paste in the following source code and save it as DH.CLEAR.
* DH.CLEAR
*Jonathan Graham/Battlestations
*Free for non-commercial use with attribution
*
 STA PAGE1 ;Select MAIN memory
 LDY #$02 ;Counter for MAIN/AUX
]LOOPA LDX #$00 ;Start at byte 0
]LOOPB STA $2000,X Cycle through
 STA $2100,X ;each of the 32 - 256 byte
 STA $2200,X ;blocks which make up
 STA $2300,X ;Hi-res page 1
 STA $2400,X
 STA $2500,X
 STA $2600,X
 STA $2700,X
 STA $2800,X
 STA $2900,X
 STA $2A00,X
 STA $2B00,X
 STA $2C00,X
 STA $2D00,X
 STA $2E00,X
 STA $2F00,X
 STA $3000,X
 STA $3100,X
 STA $3200,X
 STA $3300,X
 STA $3400,X
 STA $3500,X
 STA $3600,X
 STA $3700,X
 STA $3800,X
 STA $3900,X
 STA $3A00,X
 STA $3B00,X
 STA $3C00,X
 STA $3D00,X
 STA $3E00,X
 STA $3F00,X
 INX
 BNE ]LOOPB
 LSR ;Turn 55 into 2A otherwise do nothing
 DEY
 STA PAGE2 ;Now do it all over again
 BNE ]LOOPA ;in AUX memory

Making our API


All right, now that we have that sorted out. It's a relatively simple matter to put everything we've learned together to make our DHDRAWAPI routine a reality. The source code below is pretty self-explanatory and includes an example drawing of a triangle.
*DH.API
*DHLINE/SETDCOLOR using MLI-style API 
*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
CP EQU $F5
X0 EQU $F7
Y0 EQU $F8
X1 EQU $F9
Y1 EQU $FA
DX EQU $FB
DY EQU $FC
EPSILON EQU $FD
KEEPY EQU $FE
 PUT DH.INIT
 LDA #$00 ;Clear screen to black
 PUT DH.CLEAR
 JSR DHDRAWAPI ;Call our API
 DB $11 ;Draw with Magenta
 DB $00,5,181,70,10 ;Draw from (4,181) to (70,10)
 DB $01,134,181 ;Continue drawing to (134,181)
 DB $01,4,181 ;Continue drawing to (4,181)
 DB $02 ;End of data
 RTS
DHDRAWAPI TSX ;Move stack pointer to X
 LDA $101,X ;Load high-byte and low-byte of the
 STA CP ;return address at the top of the stack
 LDA $102,X ;Store it in a ZP location which we
 STA CP+1 ;will call CP (command pointer)
NXTCMD LDY #$01 ;Grab the next byte which
 LDA (CP),Y ;should be our first command
 TAX ;Lookup the distance between
 LDA CMDTAB,X ;our jump point and the routine 
 STA CMDJMP+1 ;Modify the BRA to go to our routine
CMDJMP BRA ENDCMD ;Do our routine
DRAWLIN INY ;Load each parameter 
 LDA (CP),Y ;and store it in the variables
 STA X0 ; X0,Y0,X1,Y1 for DHLINE to use.
 INY
 LDA (CP),Y
 STA Y0
 INY
 LDA (CP),Y
 STA X1
 INY
 LDA (CP),Y
 STA Y1
 JSR DHLINE
 CLC
 LDA #$05 ;Length of command
 ADC CP ;Move the CP to the next
 STA CP ;command
 LDA CP+1
 ADC #$00
 STA CP+1
 BRA NXTCMD
CONTLIN LDA X1 ;Copy X1 to X0
 STA X0 ;and Y1 to Y0. 
 LDA Y1 ;Then get the other two
 STA Y0 ;parameters and store 
 INY ;them as X1 and Y1
 LDA (CP),Y
 STA X1
 INY
 LDA (CP),Y
 STA Y1
 JSR DHLINE
 CLC
 LDA #$03 ;Length of command
 ADC CP ;Move the CP to the next
 STA CP ;command
 LDA CP+1
 ADC #$00
 STA CP+1
 BRA NXTCMD
CHNGCLR LDA (CP),Y 
 AND #001111 ;Mask the upper four bits
 JSR SETDCOLOR
 CLC
 LDA #$01 ;Length of command
 ADC CP ;Move the CP to the next
 STA CP ;command
 LDA CP+1
 ADC #$00
 STA CP+1
 BRA NXTCMD
ENDCMD TSX
 CLC
 LDA CP ;Add one to the CP and overwrite
 ADC #$01 ;the return address in the
 STA $101,X ;stack.
 LDA CP+1 ;Now when we RTS the CPU will
 ADC #$00 ;start executing just after our
 STA $102,X ;block of data
 RTS 
CMDTAB DB DRAWLIN-DRAWLIN,CONTLIN-DRAWLIN
 LUP 14
 DB ENDCMD-DRAWLIN
 --^
 LUP 16
 DB CHNGCLR-DRAWLIN
 --^
DHLINE PUT DH.LINE
SETDCOLOR TAY
 PUT   DH.COLOUR
]ORMAIN EQU PLOTDYL+13
]ORAUX EQU PLOTDYL+31
 PUT   DH.COLOUR
 RTS
 PUT DH.TABLES
So what can we do with all that? Quite a bit actually! Use it as the basis of a graphical adventure game in the tradition of Transylvania! Write 3D RPGs like Wizardry! These are all feasible. Just to show what can be done I've bundled the API with a drawing of moderate complexity. You can get it here:
CALL-151 6000: 8D 50 C0 8D 57 C0 8D 52 C0 8D 5E C0 8D 0D C0 8D 6010: 01 C0 A9 00 8D 54 C0 A0 02 A2 00 9D 00 20 9D 00 6020: 21 9D 00 22 9D 00 23 9D 00 24 9D 00 25 9D 00 26 6030: 9D 00 27 9D 00 28 9D 00 29 9D 00 2A 9D 00 2B 9D 6040: 00 2C 9D 00 2D 9D 00 2E 9D 00 2F 9D 00 30 9D 00 6050: 31 9D 00 32 9D 00 33 9D 00 34 9D 00 35 9D 00 36 6060: 9D 00 37 9D 00 38 9D 00 39 9D 00 3A 9D 00 3B 9D 6070: 00 3C 9D 00 3D 9D 00 3E 9D 00 3F E8 D0 9D 4A 88 6080: 8D 55 C0 D0 94 20 15 65 11 00 69 00 8B 00 01 8B 6090: BF 01 69 BF 01 69 00 17 00 7A 20 7A 8F 00 79 21 60A0: 79 8F 00 7B 21 7B 8F 00 78 22 78 8F 00 7C 22 7C 60B0: 8F 15 00 74 8A 74 8F 00 80 8A 80 8F 00 75 8C 75 60C0: 8F 00 7F 8C 7F 8F 00 76 8E 76 8F 00 7E 8E 7E 8F 60D0: 00 74 90 80 90 00 74 91 80 91 00 7A 92 7A 9B 00 60E0: 7B 92 7B 9B 00 79 92 79 9B 00 78 9C 7C 9C 18 00 60F0: 6B 05 6B 14 00 6B 18 6B 1C 00 6C 04 6C 13 00 6C 6100: 19 6C 1D 00 6D 03 6D 05 00 6D 0F 6D 12 00 6D 1B 6110: 6D 1E 00 6E 02 6E 07 00 6E 0E 6E 1F 00 6F 02 6F 6120: 07 00 6F 0D 6F 1F 00 71 02 71 1A 00 72 03 72 1B 6130: 00 73 19 73 1B 00 74 03 74 1B 00 75 03 75 1B 00 6140: 76 19 76 1B 00 77 03 77 1A 00 78 02 78 1A 00 7A 6150: 03 7A 1A 00 7B 02 7B 1B 00 7C 02 7C 03 00 7C 19 6160: 7C 1B 00 7D 02 7D 1B 00 7E 03 7E 1A 00 80 02 80 6170: 1B 00 81 02 81 1B 00 82 02 82 03 00 82 0C 82 0F 6180: 00 83 02 83 0C 00 83 0E 83 1B 00 84 03 84 0B 00 6190: 84 0F 84 1B 00 86 02 86 1F 00 87 02 87 1F 00 88 61A0: 03 88 05 00 88 1B 88 1E 00 89 04 89 1D 00 8A 05 61B0: 8A 1C 00 6C A3 6C BD 00 6D A2 6D BD 00 6E A1 6E 61C0: A3 00 6E AD 6E AF 00 6F A0 6F A5 00 6F AC 6F B0 61D0: 00 70 A0 70 A5 00 70 AB 70 B1 00 72 A5 72 BC 00 61E0: 73 A4 73 BD 00 74 A4 74 A5 00 74 BB 74 BD 00 75 61F0: A4 75 BD 00 76 A5 76 BC 00 78 A4 78 BD 00 79 A4 6200: 79 BD 00 7A A4 7A A5 00 7A AE 7A B1 00 7B A4 7B 6210: AE 00 7B B0 7B BD 00 7C A5 7C AD 00 7C B1 7C BD 6220: 00 7E A5 7E BC 00 7F A4 7F BD 00 80 A4 80 A5 00 6230: 80 BB 80 BD 00 81 A4 81 AA 00 81 AF 81 BD 00 82 6240: A5 82 AA 00 82 AF 82 BC 00 83 AF 83 B1 00 85 A0 6250: 85 BD 00 86 A0 86 BD 00 87 A1 87 A3 00 87 AD 87 6260: AF 00 87 B9 87 BC 00 88 A2 88 A4 00 88 AC 88 B0 6270: 00 88 B8 88 BB 00 89 A3 89 A5 00 89 AB 89 B1 00 6280: 89 B7 89 BA 1F 00 00 00 10 1F 01 10 A3 01 00 BF 6290: 00 69 1F 59 1F 01 59 A3 01 69 A3 00 10 3C 20 3C 62A0: 01 20 84 01 10 84 00 59 1F 49 3C 01 49 84 01 59 62B0: A3 00 20 3C 29 4D 01 29 73 01 20 84 00 49 4C 40 62C0: 4C 01 40 73 01 49 73 15 00 2A 59 3F 59 00 2A 65 62D0: 3F 65 00 2E 59 2E 65 00 3A 59 3A 65 17 00 35 68 62E0: 39 68 00 33 69 3B 69 00 32 6A 3C 6A 00 31 6B 3D 62F0: 6B 00 30 6C 3E 6C 00 2F 6D 3F 6D 00 2E 6E 40 6E 6300: 00 2D 6F 41 6F 00 2D 70 41 70 00 2C 71 42 71 00 6310: 2C 72 42 72 00 2B 73 43 73 00 2B 74 43 74 00 2A 6320: 75 44 75 00 2A 76 44 76 00 2A 77 44 77 00 2A 78 6330: 44 78 00 2A 79 44 79 00 2A 7A 44 79 00 2A 7B 44 6340: 7B 00 2A 7C 44 7C 00 2A 7D 44 7D 00 2A 7E 44 7E 6350: 00 2A 7F 44 7F 00 2A 80 44 80 00 2A 81 44 81 00 6360: 2A 82 44 82 00 2B 83 43 83 00 2B 84 43 84 00 2B 6370: 85 43 85 00 2C 86 42 86 00 2C 87 42 87 00 2D 88 6380: 41 88 00 2D 89 41 89 00 2E 8A 40 8A 00 2F 8B 3F 6390: 8B 00 30 8C 3E 8C 00 30 8D 3E 8D 00 31 8E 3D 8E 63A0: 00 32 8F 3C 8F 00 33 90 3B 90 00 33 91 3B 91 00 63B0: 34 92 3A 92 00 35 93 39 93 00 35 94 39 94 11 00 63C0: 30 95 3E 95 00 31 96 3D 96 00 32 97 3C 97 00 33 63D0: 98 3B 98 00 34 99 3A 99 00 35 9A 39 9A 00 36 9B 63E0: 38 9B 00 37 9C 37 9C 00 2D BA 41 BA 00 2D BC 41 63F0: BC 00 34 8C 3A 8C 00 37 8D 37 8D 1F 00 35 8D 36 6400: 8D 00 38 8D 39 8D 00 36 8E 36 8E 00 38 8E 38 8E 6410: 12 00 2E 96 2E A7 00 2F 96 2F A8 00 30 96 30 A9 6420: 00 31 97 31 9D 00 32 98 32 B1 00 33 99 33 B1 00 6430: 34 9A 34 B1 00 35 9B 35 B1 00 36 9C 36 B1 00 37 6440: 9D 37 B1 00 38 9C 38 B1 00 39 9B 39 B1 00 3A 9A 6450: 3A B1 00 3B 99 3B B1 00 3C 98 3C B1 00 3D 97 3D 6460: 9D 00 3E 96 3E A9 00 3F 96 3F A8 00 40 97 40 A7 6470: 00 31 B2 3D B2 00 31 B3 3D B3 00 30 B4 3E B4 00 6480: 30 B5 3E B5 00 2F B6 3F B6 00 2E B7 40 B7 00 2E 6490: B8 40 B8 00 2D B9 41 B9 00 2D BB 41 BB 00 2D BD 64A0: 41 BD 00 2D BE 41 BE 00 2D BF 41 BF 16 00 36 84 64B0: 38 84 00 35 85 39 85 00 35 86 39 86 00 35 87 39 64C0: 87 00 36 88 38 88 10 00 36 86 38 86 00 37 85 37 64D0: 87 1C 00 33 83 36 80 00 36 80 38 80 01 3B 83 1B 64E0: 00 3B 6D 36 72 01 36 75 00 34 7C 2F 78 01 2D 74 64F0: 00 2F 78 2C 77 00 3C 76 40 72 1D 00 3B 72 3B 7C 6500: 01 40 7C 01 43 79 01 43 77 00 35 74 33 70 00 2F 6510: 72 33 73 02 60 BA BD 01 01 85 F5 BD 02 01 85 F6 6520: A0 01 B1 F5 AA BD 9E 65 8D 2C 65 80 60 C8 B1 F5 6530: 85 F7 C8 B1 F5 85 F8 C8 B1 F5 85 F9 C8 B1 F5 85 6540: FA 20 BE 65 18 A9 05 65 F5 85 F5 A5 F6 69 00 85 6550: F6 80 CD A5 F9 85 F7 A5 FA 85 F8 C8 B1 F5 85 F9 6560: C8 B1 F5 85 FA 20 BE 65 18 A9 03 65 F5 85 F5 A5 6570: F6 69 00 85 F6 80 A9 B1 F5 29 0F 20 9B 66 18 A9 6580: 01 65 F5 85 F5 A5 F6 69 00 85 F6 80 93 BA 18 A5 6590: F5 69 01 9D 01 01 A5 F6 69 00 9D 02 01 60 00 26 65A0: 60 60 60 60 60 60 60 60 60 60 60 60 60 60 4A 4A 65B0: 4A 4A 4A 4A 4A 4A 4A 4A 4A 4A 4A 4A 4A 4A A6 F7 65C0: A4 F8 84 FE A5 FA 38 E5 F8 90 06 85 FC A9 C8 80 65D0: 06 49 FF 85 FC A9 88 8D 39 66 8D 95 66 A5 F9 38 65E0: E5 F7 90 06 85 FB A9 E8 80 06 49 FF 85 FB A9 CA 65F0: 8D 48 66 8D 84 66 A5 FB C5 FC B0 4F A5 FC 4A 49 6600: FF 85 FD B9 BD 7A 85 1D B9 7D 7B 85 1E 84 FE BC 6610: CD 66 30 0D 8D 54 C0 B1 1D 3D E5 67 1D 8D 6F 91 6620: 1D BC 59 67 30 0D 8D 55 C0 B1 1D 3D 71 68 1D 19 6630: 70 91 1D A4 FE C4 FA F0 61 C8 A5 FD 18 65 FB 85 6640: FD 90 C0 38 E5 FC 85 FD E8 80 B8 A5 FB 4A 49 FF 6650: 85 FD B9 BD 7A 85 1D B9 7D 7B 85 1E BC CD 66 30 6660: 0D 8D 54 C0 B1 1D 3D E5 67 1D 8D 6F 91 1D BC 59 6670: 67 30 0D 8D 55 C0 B1 1D 3D 71 68 1D 19 70 91 1D 6680: E4 F9 F0 16 E8 A5 FD 18 65 FC 85 FD 90 CE 38 E5 6690: FB 85 FD A4 FE C8 84 FE 80 B8 60 A8 B9 7D 7A 8D 66A0: 6A 66 B9 8D 7A 8D 6B 66 B9 9D 7A 8D 7C 66 B9 AD 66B0: 7A 8D 7D 66 B9 7D 7A 8D 1D 66 B9 8D 7A 8D 1E 66 66C0: B9 9D 7A 8D 2F 66 B9 AD 7A 8D 30 66 60 FF 00 00 66D0: 00 FF 01 01 FF 02 02 02 FF 03 03 FF 04 04 04 FF 66E0: 05 05 FF 06 06 06 FF 07 07 FF 08 08 08 FF 09 09 66F0: FF 0A 0A 0A FF 0B 0B FF 0C 0C 0C FF 0D 0D FF 0E 6700: 0E 0E FF 0F 0F FF 10 10 10 FF 11 11 FF 12 12 12 6710: FF 13 13 FF 14 14 14 FF 15 15 FF 16 16 16 FF 17 6720: 17 FF 18 18 18 FF 19 19 FF 1A 1A 1A FF 1B 1B FF 6730: 1C 1C 1C FF 1D 1D FF 1E 1E 1E FF 1F 1F FF 20 20 6740: 20 FF 21 21 FF 22 22 22 FF 23 23 FF 24 24 24 FF 6750: 25 25 FF 26 26 26 FF 27 27 00 00 FF 01 01 01 FF 6760: 02 02 FF 03 03 03 FF 04 04 FF 05 05 05 FF 06 06 6770: FF 07 07 07 FF 08 08 FF 09 09 09 FF 0A 0A FF 0B 6780: 0B 0B FF 0C 0C FF 0D 0D 0D FF 0E 0E FF 0F 0F 0F 6790: FF 10 10 FF 11 11 11 FF 12 12 FF 13 13 13 FF 14 67A0: 14 FF 15 15 15 FF 16 16 FF 17 17 17 FF 18 18 FF 67B0: 19 19 19 FF 1A 1A FF 1B 1B 1B FF 1C 1C FF 1D 1D 67C0: 1D FF 1E 1E FF 1F 1F 1F FF 20 20 FF 21 21 21 FF 67D0: 22 22 FF 23 23 23 FF 24 24 F4 25 25 25 FF 26 26 67E0: FF 27 27 27 FF 7F 7E 61 1F 7F 78 07 7F 7E 61 1F 67F0: 7F 78 07 7F 7E 61 1F 7F 78 07 7F 7E 61 1F 7F 78 6800: 07 7F 7E 61 1F 7F 78 07 7F 7E 61 1F 7F 78 07 7F 6810: 7E 61 1F 7F 78 07 7F 7E 61 1F 7F 78 07 7F 7E 61 6820: 1F 7F 78 07 7F 7E 61 1F 7F 78 07 7F 7E 61 1F 7F 6830: 78 07 7F 7E 61 1F 7F 78 07 7F 7E 61 1F 7F 78 07 6840: 7F 7E 61 1F 7F 78 07 7F 7E 61 1F 7F 78 07 7F 7E 6850: 61 1F 7F 78 07 7F 7E 61 1F 7F 78 07 7F 7E 61 1F 6860: 7F 78 07 7F 7E 61 1F 7F 78 07 7F 7E 61 1F 7F 78 6870: 07 70 0F 7F 7C 43 3F 7F 70 0F 7F 7C 43 3F 7F 70 6880: 0F 7F 7C 43 3F 7F 70 0F 7F 7C 43 3F 7F 70 0F 7F 6890: 7C 43 3F 7F 70 0F 7F 7C 43 3F 7F 70 0F 7F 7C 43 68A0: 3F 7F 70 0F 7F 7C 43 3F 7F 70 0F 7F 7C 43 3F 7F 68B0: 70 0F 7F 7C 43 3F 7F 70 0F 7F 7C 43 3F 7F 70 0F 68C0: 7F 7C 43 3F 7F 70 0F 7F 7C 43 3F 7F 70 0F 7F 7C 68D0: 43 3F 7F 70 0F 7F 7C 43 3F 7F 70 0F 7F 7C 43 3F 68E0: 7F 70 0F 7F 7C 43 3F 7F 70 0F 7F 7C 43 3F 7F 70 68F0: 0F 7F 7C 43 3F 7F 70 0F 7F 7C 43 3F 7F 00 00 00 6900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6910: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6920: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 69A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 69B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 69C0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 69D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 69E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 69F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6A00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6A10: 00 00 00 00 00 00 01 10 00 00 04 40 00 01 10 00 6A20: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6A30: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 6A40: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 6A50: 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 6A60: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 6A70: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 6A80: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 6A90: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6AA0: 40 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 6AB0: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 6AC0: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 6AD0: 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 6AE0: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 6AF0: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 6B00: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 6B10: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 6B20: 00 00 02 20 00 00 08 00 00 02 20 00 00 00 00 08 6B30: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 6B40: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 6B50: 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 6B60: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 6B70: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 6B80: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 6B90: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 6BA0: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 6BB0: 02 20 00 00 08 00 00 02 20 04 40 00 01 10 00 00 6BC0: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 6BD0: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 6BE0: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 6BF0: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6C00: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 6C10: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 6C20: 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 6C30: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 6C40: 00 01 10 00 00 00 01 18 00 00 06 60 00 01 18 00 6C50: 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 6C60: 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 6C70: 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 6C80: 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 6C90: 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 6CA0: 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 6CB0: 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 6CC0: 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 6CD0: 60 0C 40 00 03 30 00 00 0C 40 00 03 30 00 00 0C 6CE0: 40 00 03 30 00 00 0C 40 00 03 30 00 00 0C 40 00 6CF0: 03 30 00 00 0C 40 00 03 30 00 00 0C 40 00 03 30 6D00: 00 00 0C 40 00 03 30 00 00 0C 40 00 03 30 00 00 6D10: 0C 40 00 03 30 00 00 0C 40 00 03 30 00 00 0C 40 6D20: 00 03 30 00 00 0C 40 00 03 30 00 00 0C 40 00 03 6D30: 30 00 00 0C 40 00 03 30 00 00 0C 40 00 03 30 00 6D40: 00 0C 40 00 03 30 00 00 0C 40 00 03 30 00 00 0C 6D50: 40 00 03 30 00 00 0C 40 00 03 30 00 00 00 00 04 6D60: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 6D70: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 6D80: 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 6D90: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 6DA0: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 6DB0: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 6DC0: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 6DD0: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 6DE0: 01 10 00 00 04 40 00 01 10 02 20 00 00 08 00 00 6DF0: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 6E00: 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 6E10: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 6E20: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 6E30: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 6E40: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 6E50: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 6E60: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 6E70: 00 00 08 00 00 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 05 50 00 01 14 40 00 05 50 00 01 14 40 00 6EC0: 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 50 6ED0: 00 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 6EE0: 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 40 6EF0: 00 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 6F00: 50 0A 20 00 02 28 00 00 0A 20 00 02 28 00 00 0A 6F10: 20 00 02 28 00 00 0A 20 00 02 28 00 00 0A 20 00 6F20: 02 28 00 00 0A 20 00 02 28 00 00 0A 20 00 02 28 6F30: 00 00 0A 20 00 02 28 00 00 0A 20 00 02 28 00 00 6F40: 0A 20 00 02 28 00 00 0A 20 00 02 28 00 00 0A 20 6F50: 00 02 28 00 00 0A 20 00 02 28 00 00 0A 20 00 02 6F60: 28 00 00 0A 20 00 02 28 00 00 0A 20 00 02 28 00 6F70: 00 0A 20 00 02 28 00 00 0A 20 00 02 28 00 00 0A 6F80: 20 00 02 28 00 00 0A 20 00 02 28 00 00 00 00 0C 6F90: 40 00 03 30 00 00 0C 40 00 03 30 00 00 0C 40 00 6FA0: 03 30 00 00 0C 40 00 03 30 00 00 0C 40 00 03 30 6FB0: 00 00 0C 40 00 03 30 00 00 0C 40 00 03 30 00 00 6FC0: 0C 40 00 03 30 00 00 0C 40 00 03 30 00 00 0C 40 6FD0: 00 03 30 00 00 0C 40 00 03 30 00 00 0C 40 00 03 6FE0: 30 00 00 0C 40 00 03 30 00 00 0C 40 00 03 30 00 6FF0: 00 0C 40 00 03 30 00 00 0C 40 00 03 30 00 00 0C 7000: 40 00 03 30 00 00 0C 40 00 03 30 00 00 0C 40 00 7010: 03 30 00 00 0C 40 00 03 30 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 00 00 06 60 00 01 18 00 00 06 7060: 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 7070: 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 7080: 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 7090: 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 70A0: 00 01 18 00 00 00 01 1C 40 00 07 70 00 01 1C 40 70B0: 00 07 70 00 01 1C 40 00 07 70 00 01 1C 40 00 07 70C0: 70 00 01 1C 40 00 07 70 00 01 1C 40 00 07 70 00 70D0: 01 1C 40 00 07 70 00 01 1C 40 00 07 70 00 01 1C 70E0: 40 00 07 70 00 01 1C 40 00 07 70 00 01 1C 40 00 70F0: 07 70 00 01 1C 40 00 07 70 00 01 1C 40 00 07 70 7100: 00 01 1C 40 00 07 70 00 01 1C 40 00 07 70 00 01 7110: 1C 40 00 07 70 00 01 1C 40 00 07 70 00 01 1C 40 7120: 00 07 70 00 01 1C 40 00 07 70 00 01 1C 40 00 07 7130: 70 0E 60 00 03 38 00 00 0E 60 00 03 38 00 00 0E 7140: 60 00 03 38 00 00 0E 60 00 03 38 00 00 0E 60 00 7150: 03 38 00 00 0E 60 00 03 38 00 00 0E 60 00 03 38 7160: 00 00 0E 60 00 03 38 00 00 0E 60 00 03 38 00 00 7170: 0E 60 00 03 38 00 00 0E 60 00 03 38 00 00 0E 60 7180: 00 03 38 00 00 0E 60 00 03 38 00 00 0E 60 00 03 7190: 38 00 00 0E 60 00 03 38 00 00 0E 60 00 03 38 00 71A0: 00 0E 60 00 03 38 00 00 0E 60 00 03 38 00 00 0E 71B0: 60 00 03 38 00 00 0E 60 00 03 38 00 00 00 00 02 71C0: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 71D0: 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 71E0: 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 71F0: 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 7200: 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 00 7210: 08 00 00 02 20 00 00 08 00 00 02 20 00 00 08 00 7220: 00 02 20 00 00 08 00 00 02 20 00 00 08 00 00 02 7230: 20 00 00 08 00 00 02 20 00 00 08 00 00 02 20 00 7240: 00 08 00 00 02 20 00 00 08 01 10 00 00 04 40 00 7250: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 7260: 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 7270: 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 7280: 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 7290: 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 00 72A0: 00 04 40 00 01 10 00 00 04 40 00 01 10 00 00 04 72B0: 40 00 01 10 00 00 04 40 00 01 10 00 00 04 40 00 72C0: 01 10 00 00 04 40 00 01 10 00 00 04 40 00 01 10 72D0: 00 00 04 40 00 00 01 12 20 00 04 48 00 01 12 20 72E0: 00 04 48 00 01 12 20 00 04 48 00 01 12 20 00 04 72F0: 48 00 01 12 20 00 04 48 00 01 12 20 00 04 48 00 7300: 01 12 20 00 04 48 00 01 12 20 00 04 48 00 01 12 7310: 20 00 04 48 00 01 12 20 00 04 48 00 01 12 20 00 7320: 04 48 00 01 12 20 00 04 48 00 01 12 20 00 04 48 7330: 00 01 12 20 00 04 48 00 01 12 20 00 04 48 00 01 7340: 12 20 00 04 48 00 01 12 20 00 04 48 00 01 12 20 7350: 00 04 48 00 01 12 20 00 04 48 00 01 12 20 00 04 7360: 48 09 10 00 02 24 40 00 09 10 00 02 24 40 00 09 7370: 10 00 02 24 40 00 09 10 00 02 24 40 00 09 10 00 7380: 02 24 40 00 09 10 00 02 24 40 00 09 10 00 02 24 7390: 40 00 09 10 00 02 24 40 00 09 10 00 02 24 40 00 73A0: 09 10 00 02 24 40 00 09 10 00 02 24 40 00 09 10 73B0: 00 02 24 40 00 09 10 00 02 24 40 00 09 10 00 02 73C0: 24 40 00 09 10 00 02 24 40 00 09 10 00 02 24 40 73D0: 00 09 10 00 02 24 40 00 09 10 00 02 24 40 00 09 73E0: 10 00 02 24 40 00 09 10 00 02 24 40 00 00 00 0A 73F0: 20 00 02 28 00 00 0A 20 00 02 28 00 00 0A 20 00 7400: 02 28 00 00 0A 20 00 02 28 00 00 0A 20 00 02 28 7410: 00 00 0A 20 00 02 28 00 00 0A 20 00 02 28 00 00 7420: 0A 20 00 02 28 00 00 0A 20 00 02 28 00 00 0A 20 7430: 00 02 28 00 00 0A 20 00 02 28 00 00 0A 20 00 02 7440: 28 00 00 0A 20 00 02 28 00 00 0A 20 00 02 28 00 7450: 00 0A 20 00 02 28 00 00 0A 20 00 02 28 00 00 0A 7460: 20 00 02 28 00 00 0A 20 00 02 28 00 00 0A 20 00 7470: 02 28 00 00 0A 20 00 02 28 05 50 00 01 14 40 00 7480: 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 50 7490: 00 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 74A0: 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 40 74B0: 00 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 74C0: 50 00 01 14 40 00 05 50 00 01 14 40 00 05 50 00 74D0: 01 14 40 00 05 50 00 01 14 40 00 05 50 00 01 14 74E0: 40 00 05 50 00 01 14 40 00 05 50 00 01 14 40 00 74F0: 05 50 00 01 14 40 00 05 50 00 01 14 40 00 05 50 7500: 00 01 14 40 00 00 01 1A 20 00 06 68 00 01 1A 20 7510: 00 06 68 00 01 1A 20 00 06 68 00 01 1A 20 00 06 7520: 68 00 01 1A 20 00 06 68 00 01 1A 20 00 06 68 00 7530: 01 1A 20 00 06 68 00 01 1A 20 00 06 68 00 01 1A 7540: 20 00 06 68 00 01 1A 20 00 06 68 00 01 1A 20 00 7550: 06 68 00 01 1A 20 00 06 68 00 01 1A 20 00 06 68 7560: 00 01 1A 20 00 06 68 00 01 1A 20 00 06 68 00 01 7570: 1A 20 00 06 68 00 01 1A 20 00 06 68 00 01 1A 20 7580: 00 06 68 00 01 1A 20 00 06 68 00 01 1A 20 00 06 7590: 68 0D 50 00 03 34 40 00 0D 50 00 03 34 40 00 0D 75A0: 50 00 03 34 40 00 0D 50 00 03 34 40 00 0D 50 00 75B0: 03 34 40 00 0D 50 00 03 34 40 00 0D 50 00 03 34 75C0: 40 00 0D 50 00 03 34 40 00 0D 50 00 03 34 40 00 75D0: 0D 50 00 03 34 40 00 0D 50 00 03 34 40 00 0D 50 75E0: 00 03 34 40 00 0D 50 00 03 34 40 00 0D 50 00 03 75F0: 34 40 00 0D 50 00 03 34 40 00 0D 50 00 03 34 40 7600: 00 0D 50 00 03 34 40 00 0D 50 00 03 34 40 00 0D 7610: 50 00 03 34 40 00 0D 50 00 03 34 40 00 00 00 06 7620: 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 7630: 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 7640: 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 7650: 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 7660: 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 01 7670: 18 00 00 06 60 00 01 18 00 00 06 60 00 01 18 00 7680: 00 06 60 00 01 18 00 00 06 60 00 01 18 00 00 06 7690: 60 00 01 18 00 00 06 60 00 01 18 00 00 06 60 00 76A0: 01 18 00 00 06 60 00 01 18 03 30 00 00 0C 40 00 76B0: 03 30 00 00 0C 40 00 03 30 00 00 0C 40 00 03 30 76C0: 00 00 0C 40 00 03 30 00 00 0C 40 00 03 30 00 00 76D0: 0C 40 00 03 30 00 00 0C 40 00 03 30 00 00 0C 40 76E0: 00 03 30 00 00 0C 40 00 03 30 00 00 0C 40 00 03 76F0: 30 00 00 0C 40 00 03 30 00 00 0C 40 00 03 30 00 7700: 00 0C 40 00 03 30 00 00 0C 40 00 03 30 00 00 0C 7710: 40 00 03 30 00 00 0C 40 00 03 30 00 00 0C 40 00 7720: 03 30 00 00 0C 40 00 03 30 00 00 0C 40 00 03 30 7730: 00 00 0C 40 00 00 01 16 60 00 05 58 00 01 16 60 7740: 00 05 58 00 01 16 60 00 05 58 00 01 16 60 00 05 7750: 58 00 01 16 60 00 05 58 00 01 16 60 00 05 58 00 7760: 01 16 60 00 05 58 00 01 16 60 00 05 58 00 01 16 7770: 60 00 05 58 00 01 16 60 00 05 58 00 01 16 60 00 7780: 05 58 00 01 16 60 00 05 58 00 01 16 60 00 05 58 7790: 00 01 16 60 00 05 58 00 01 16 60 00 05 58 00 01 77A0: 16 60 00 05 58 00 01 16 60 00 05 58 00 01 16 60 77B0: 00 05 58 00 01 16 60 00 05 58 00 01 16 60 00 05 77C0: 58 0B 30 00 02 2C 40 00 0B 30 00 02 2C 40 00 0B 77D0: 30 00 02 2C 40 00 0B 30 00 02 2C 40 00 0B 30 00 77E0: 02 2C 40 00 0B 30 00 02 2C 40 00 0B 30 00 02 2C 77F0: 40 00 0B 30 00 02 2C 40 00 0B 30 00 02 2C 40 00 7800: 0B 30 00 02 2C 40 00 0B 30 00 02 2C 40 00 0B 30 7810: 00 02 2C 40 00 0B 30 00 02 2C 40 00 0B 30 00 02 7820: 2C 40 00 0B 30 00 02 2C 40 00 0B 30 00 02 2C 40 7830: 00 0B 30 00 02 2C 40 00 0B 30 00 02 2C 40 00 0B 7840: 30 00 02 2C 40 00 0B 30 00 02 2C 40 00 00 00 0E 7850: 60 00 03 38 00 00 0E 60 00 03 38 00 00 0E 60 00 7860: 03 38 00 00 0E 60 00 03 38 00 00 0E 60 00 03 38 7870: 00 00 0E 60 00 03 38 00 00 0E 60 00 03 38 00 00 7880: 0E 60 00 03 38 00 00 0E 60 00 03 38 00 00 0E 60 7890: 00 03 38 00 00 0E 60 00 03 38 00 00 0E 60 00 03 78A0: 38 00 00 0E 60 00 03 38 00 00 0E 60 00 03 38 00 78B0: 00 0E 60 00 03 38 00 00 0E 60 00 03 38 00 00 0E 78C0: 60 00 03 38 00 00 0E 60 00 03 38 00 00 0E 60 00 78D0: 03 38 00 00 0E 60 00 03 38 07 70 00 01 1C 40 00 78E0: 07 70 00 01 1C 40 00 07 70 00 01 1C 40 00 07 70 78F0: 00 01 1C 40 00 07 70 00 01 1C 40 00 07 70 00 01 7900: 1C 40 00 07 70 00 01 1C 40 00 07 70 00 01 1C 40 7910: 00 07 70 00 01 1C 40 00 07 70 00 01 1C 40 00 07 7920: 70 00 01 1C 40 00 07 70 00 01 1C 40 00 07 70 00 7930: 01 1C 40 00 07 70 00 01 1C 40 00 07 70 00 01 1C 7940: 40 00 07 70 00 01 1C 40 00 07 70 00 01 1C 40 00 7950: 07 70 00 01 1C 40 00 07 70 00 01 1C 40 00 07 70 7960: 00 01 1C 40 00 00 01 1E 60 00 07 78 00 01 1E 60 7970: 00 07 78 00 01 1E 60 00 07 78 00 01 1E 60 00 07 7980: 78 00 01 1E 60 00 07 78 00 01 1E 60 00 07 78 00 7990: 01 1E 60 00 07 78 00 01 1E 60 00 07 78 00 01 1E 79A0: 60 00 07 78 00 01 1E 60 00 07 78 00 01 1E 60 00 79B0: 07 78 00 01 1E 60 00 07 78 00 01 1E 60 00 07 78 79C0: 00 01 1E 60 00 07 78 00 01 1E 60 00 07 78 00 01 79D0: 1E 60 00 07 78 00 01 1E 60 00 07 78 00 01 1E 60 79E0: 00 07 78 00 01 1E 60 00 07 78 00 01 1E 60 00 07 79F0: 78 0F 70 00 03 3C 40 00 0F 70 00 03 3C 40 00 0F 7A00: 70 00 03 3C 40 00 0F 70 00 03 3C 40 00 0F 70 00 7A10: 03 3C 40 00 0F 70 00 03 3C 40 00 0F 70 00 03 3C 7A20: 40 00 0F 70 00 03 3C 40 00 0F 70 00 03 3C 40 00 7A30: 0F 70 00 03 3C 40 00 0F 70 00 03 3C 40 00 0F 70 7A40: 00 03 3C 40 00 0F 70 00 03 3C 40 00 0F 70 00 03 7A50: 3C 40 00 0F 70 00 03 3C 40 00 0F 70 00 03 3C 40 7A60: 00 0F 70 00 03 3C 40 00 0F 70 00 03 3C 40 00 0F 7A70: 70 00 03 3C 40 00 0F 70 00 03 3C 40 00 FD 15 2D 7A80: 45 5D 75 8D A5 BD D5 ED 05 1D 35 4D 65 68 6A 6B 7A90: 6C 6D 6E 6F 70 71 72 73 75 76 77 78 79 89 A1 B9 7AA0: D1 E9 01 19 31 49 61 79 91 A9 C1 D9 F1 69 6A 6B 7AB0: 6C 6D 6F 70 71 72 73 74 75 76 77 78 79 00 00 00 7AC0: 00 00 00 00 00 80 80 80 80 80 80 80 80 00 00 00 7AD0: 00 00 00 00 00 80 80 80 80 80 80 80 80 00 00 00 7AE0: 00 00 00 00 00 80 80 80 80 80 80 80 80 00 00 00 7AF0: 00 00 00 00 00 80 80 80 80 80 80 80 80 28 28 28 7B00: 28 28 28 28 28 A8 A8 A8 A8 A8 A8 A8 A8 28 28 28 7B10: 28 28 28 28 28 A8 A8 A8 A8 A8 A8 A8 A8 28 28 28 7B20: 28 28 28 28 28 A8 A8 A8 A8 A8 A8 A8 A8 28 28 28 7B30: 28 28 28 28 28 A8 A8 A8 A8 A8 A8 A8 A8 50 50 50 7B40: 50 50 50 50 50 D0 D0 D0 D0 D0 D0 D0 D0 50 50 50 7B50: 50 50 50 50 50 D0 D0 D0 D0 D0 D0 D0 D0 50 50 50 7B60: 50 50 50 50 50 D0 D0 D0 D0 D0 D0 D0 D0 50 50 50 7B70: 50 50 50 50 50 D0 D0 D0 D0 D0 D0 D0 D0 20 24 28 7B80: 2C 30 34 38 3C 20 24 28 2C 30 34 38 3C 21 25 29 7B90: 2D 31 35 39 3D 21 25 29 2D 31 35 39 3D 22 26 2A 7BA0: 2E 32 36 3A 3E 22 26 2A 2E 32 36 3A 3E 23 27 2B 7BB0: 2F 33 37 3B 3F 23 27 2B 2F 33 37 3B 3F 20 24 28 7BC0: 2C 30 34 38 3C 20 24 28 2C 30 34 38 3C 21 25 29 7BD0: 2D 31 35 39 3D 21 25 29 2D 31 35 39 3D 22 26 2A 7BE0: 2E 32 36 3A 3E 22 26 2A 2E 32 36 3A 3E 23 27 2B 7BF0: 2F 33 37 3B 3F 23 27 2B 2F 33 37 3B 3F 20 24 28 7C00: 2C 30 34 38 3C 20 24 28 2C 30 34 38 3C 21 25 29 7C10: 2D 31 35 39 3D 21 25 29 2D 31 35 39 3D 22 26 2A 7C20: 2E 32 36 3A 3E 22 26 2A 2E 32 36 3A 3E 23 27 2B 7C30: 2F 33 37 3B 3F 23 27 2B 2F 33 37 3B 3F 6000G
The results speak for themselves I think.


The package is about 7.2K and renders in about 0.72 seconds. The drawing takes up about 1K of that. Which, in all honesty is still a little on the high side for the purposes of developing graphical adventures but I outline some strategies for getting these sizes down even further later on.

Even without these modifications it shouldn't be difficult to get 40-70 images on a standard ProDOS floppy. Which gets us pretty close to a game like The Wizard and the Princess which fits 116 areas on a single sided disk

For those who want to develop this further Here's a list of ideas that most of you who have been following along could easily implement:

IdeaDifficultyDescription
Plot a single pointEasyIt takes five bytes to draw a single point. Adding a three-byte command to plot a single point would allow us to make more detailed drawings without wasting memory.
Draw a solid rectangleEasyThe monster in our example takes more drawing commands than everything else put together. Being able to draw a solid rectangle would have compressed our code by about 200 bytes or 15%.
Relative DrawEasyGames like The Coveted Mirror represent objects on-screen. To accomplish this you may want to have a library of smaller images which you can reposition.
Filled Polygon or Flood FillIntermediate to AdvancedA flood fill is the most heavily used tool in the Graphics Magician. Writing one of our own would yield even smaller images and while it might be complicated to implement efficiently. You could probably get similar amounts of savings with a command which would take a series of points and create a filled polygon from them.
Fast plotAdvancedThere is still a significant amount of room for improvement here, just in small scale optimizations I could probably get us up to 25% of our theoretical maximum. Changing over to run-slicing or some other algorithm which plots more than one byte could easily double that.

Where to now?


My next post will deal with double-hires character generators. Not only is this an important tool for the implementation of graphical adventures and role-playing games. It's also a nice jumping off point for developing tile-based games which include another popular RPG genre – top-down third-person games like Ultima. As well as just about every arcade game ever made!

See you then!

Thursday, May 11, 2017

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

A big shoutout to the Folks at OpenApple for covering my humble blog. You can check out the episode here
The story so far...

After a lot of background work we now have the basis for two of the fundamental features of a graphics package: Drawing a point and drawing a line. These basic elements: points, lines, rectangles, etc.. are often referred to collectively as primitives.

Our line drawing algorithm works but can only handle lines where X1 > X0, Y1 > Y0 and DX > DY. So lets fix that!

So what happens when DY > DX?


The answer is simple! The Y axis becomes the major axis for our line. In other words, our line moves farther along the y-axis than the x-axis. Let's take a look at just part of the code from last post:
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
Notice that here, when X is the major axis we are doing five things:
  1. We set epsilon to 255-DX/2 (lines 9-11)
  2. We compare the X co-ordinate with X1 to see if were done drawing. (lines 18-19)
  3. We advance the X co-ordinate. (line 20)
  4. We add DY to epsilon at each iteration to determine if we need to move along the minor (Y) access (lines 21-25)
  5. If so, we subtract DX from epsilon and advance the Y co-ordinate (lines 26-29)
When DY is larger than DX, our algorithm changes to:
  1. Set epsilon to 255-DY/2 (lines 5-7)
  2. Compare the Y co-ordinate with Y1 to see if were done drawing. (lines 18-19)
  3. Advance the Y co-ordinate. (line 20)
  4. Add DX to epsilon at each iteration to determine if we need to move along the minor (X) access (lines 21-25)
  5. If so, we subtract DY from epsilon and advance the X co-ordinate (lines 26-29)
That code would look something like this:
NXTLINE LDA Y1 ;Calculate DY as Y1-Y0
 SEC
 SBC Y0
 STA DY
 LSR ;Set epsilon to 255-DY/2
 EOR #$FF
 STA EPSILON 
 LDA X1 ;Calculate DX as X1-X0
 SEC
 SBC X0
 STA DX
 LDX X0 ;Start plotting at (X0,Y0)
 LDY Y0 
NXTVERT PUT DH.ROWSET ;Calculate horizontal value
NXTHORZ PHY
 PUT DH.PLOT ;Plot point
 PLY
 CPY Y1 ;Are we done?
 BEQ ENDLINE
 INY ;Move along major axis
 LDA EPSILON ;Epsilon = Epsilon + DX
 CLC
 ADC DX
 STA EPSILON
 BCC NXTVERT ;NOTE: we've changed Y so we need to call DH.ROWSET
 SEC ;If epsilon has rolled over subtract DX
 SBC DY
 STA EPSILON
 INX ;Move along minor axis
 BRA NXTVERT
If you recall, last post I mentioned that one of the ways we can implement Bresenham's algorithm is to write multiple routines and that's exactly what we are going to do here. The combined code looks like this:
NXTLINE LDX X0
 LDY Y0
 LDA Y1 ;Calculate DY as Y1-Y0
 SEC
 SBC Y0
 STA DY
 LDA X1 ;Calculate DX as X1-X0
 SEC
 SBC X0
 STA DX
 CMP DY
 BCS DXLINE
DYLINE LDA DY ;Set EPSILON to 255-(DY/2)
 LSR
 EOR #$FF
 STA EPSILON
DYHORZ PUT DH.ROWSET ;Calculate horizontal value
 PHY
 PUT DH.PLOT ;Plot point
 PLY
 CPY Y1 ;Are we done?
 BEQ ENDLINE
 INY ;Move along major axis
 LDA EPSILON ;Epsilon = Epsilon + DX
 CLC
 ADC DX
 STA EPSILON
 BCC DYHORZ ;NOTE: we've changed Y so we need to call DH.ROWSET
 SEC ;If epsilon has rolled over subtract DX
 SBC DY
 STA EPSILON
 INX ;Move along minor axis
 BRA DYHORZ
DXLINE LDA DX ;Set EPSILON to 255-(DX/2)
 LSR
 EOR #$FF
 STA EPSILON
DXVERT PUT DH.ROWSET ;Calculate horizontal value
DXHORZ 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 DXHORZ ;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 DXVERT
ENDLINE RTS
Keep in mind that we are still assuming that X0 < X1 and that Y0 < Y1. But what if that's not the case? Well if you think about it, as far as drawing the line goes the only difference between X0 > X1 and X0 < X1 is that we move along the X axis in the opposite direction. The proportion of steps on the X axis to the Y axis doesn't change. The same applies for the Y axis.

Clearly we could solve this problem by writing more routines but considering that there are two cases - that gives us four possibilities and each one would have to handle situations where DX > DY and DY < DX. Implementing each of these would yield eight separate routines!

So is there another way?


After all, what we really need to do is change the direction we are moving along one or both axes. We even know how to implement that! Just flip our INX to a DEX for the X axis and our INY to DEY to do the same thing for the Y axis.

To accomplish this we're going to, once again use self-modifying code. While we are calculating X1 - X0, we will check if the result is less than zero - in other words the carry bit will be cleared. At which point we will do two things: flip the sign back from negative to positive (accomplished with an EOR $FF) and then modify the INX/INY instructions in our two routines as needed.

This last part will require us to create a few more labels. CHGYA and CHGYB pointing to the two INY instructions and CHGXA and CHGXB for the two INX instructions.

The result looks like this:(self-modifying code and the portions that will be modified marked out in yellow)
NXTLINE LDX X0
 LDY Y0
 LDA Y1 ;Calculate DY as Y1-Y0
 SEC
 SBC Y0
 BCC Y0BIGGER
 STA DY
 LDA #$C8 ;SMC: Hex code for INY 
 BRA CHGY
Y0BIGGER EOR #$FF
 STA DY
 LDA #$88 ;SMC: Hex code for DEY 
CHGY STA CHGYA ;SMC: Modify instructions at CHGYA/CHGYB
 STA CHGYB
 LDA X1 ;Calculate DX as X1-X0
 SEC
 SBC X0
 BCC X0BIGGER
 STA DX
 LDA #$E8 ;SMC: Hex code for INX
 BRA CHGX
X0BIGGER EOR #$FF
 STA DX
 LDA #$CA ;SMC: Hex code for DEX
CHGX STA CHGXA ;Modify instructions at CHGXA/CHGXB
 STA CHGXB
 LDA DX
 CMP DY
 BCS DXLINE
DYLINE LDA DY ;Set EPSILON to 255-(DY/2)
 LSR
 EOR #$FF
 STA EPSILON
DYHORZ PUT DH.ROWSET ;Calculate horizontal value
 PHY
 PUT DH.PLOT ;Plot point
 PLY
 CPY Y1 ;Are we done?
 BEQ ENDLINE
CHGYA INY ;SMC: Move along major axis
 LDA EPSILON ;Epsilon = Epsilon + DX
 CLC
 ADC DX
 STA EPSILON
 BCC DYHORZ ;NOTE: we've changed Y so we need to call DH.ROWSET
 SEC ;If epsilon has rolled over subtract DX
 SBC DY
 STA EPSILON
CHGXA INX ;SMC: Move along minor axis
 BRA DYHORZ
DXLINE LDA DX ;Set EPSILON to 255-(DX/2)
 LSR
 EOR #$FF
 STA EPSILON
DXVERT PUT DH.ROWSET ;Calculate horizontal value
DXHORZ PHY
 PUT DH.PLOT ;Plot point
 PLY
 CPX X1 ;Are we done?
 BEQ ENDLINE
CHGXB INX ;SMC: Move along major axis
 LDA EPSILON ;Epsilon = Epsilon + DY
 CLC
 ADC DY
 STA EPSILON
 BCC DXHORZ ;If Epsilon hasn't rolled over move to next point 
 SEC ;If epsilon has rolled over subtract DX
 SBC DX
 STA EPSILON
CHGYB INY ;SMC: Move along minor axis
 BRA DXVERT
ENDLINE RTS
...and we now have a fully functioning line drawing routine! There's just one problem. Remember our DH.COLOUR macro? It modifies the DH.PLOT code! So what will happen here when we call SETDCOLOR?
*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
If you recall the ]ORMAIN and ]ORAUX variables take on the value they were defined with most recently in the code. Since they are defined in the DH.PLOT macro. These variables will point to the part of the line draw routine where DX > DY. Which means for our other code, which is used when DX < DY our line will be the default colour: green.

This problem can be solved in a number of ways but to preserve the modularity of our code we're going to be a little less than efficient. Our code, as it stands will modify the correct ORMAIN/ORAUX codes in the DXLINE routine (used when DX > DY). All we need is another to change the code in the DYLINE routine. If you take a look in DH.PLOT you will see that ]ORMAIN is 13 bytes from the begining, and ORAUX is 31 bytes from the beginning. So all we need is one more label at the point where we PUT DH.PLOT called PLOTDYL and then use the pseudo opcode EQU to do the following
SETDCOLOR TAY
 PUT DH.COLOUR
]ORMAIN EQU PLOTDYL+13 ;Redefine ORMAIN to point to DYLINE routine
]ORAUX EQU PLOTDYL+31 ;Redefine ORAUX to point to DYLINE routine
 PUT DH.COLOUR
 RTS
By placing the label PLOTDYL right where te second DH.PLOT macro is and then using the EQU's as above. We've tricked the assembler into making our second macro modify the DYLINE rounte....and that's it.

Are we on the right track?


Our original goals were to produce code that was not just understandable but also fast.  So exactly how fast is this code we are writing anyway? When benchmarking code in the real world. I always find it handy to compare my results as well as the value of any improvements I'm about to make by comparing them to the theoretical maximum for whatever technique I'm using.

As we've seen, in our approach addressing a single pixel on the DHR screen requires us to load the screen data from the place we are about to plot, clear the bits we need to use with AND, insert our pixel data with OR then write the value back to screen memory.  At it's cheapest a LDA from some arbitrary memory location is 3 machine cycles, an AND is two, an OR is two more and a store is 4 more again.  This brings us to 11 machine cycles.  Which means we could fill the entire screen with a pixel of a particular colour in 295,680 cycles or about .39 seconds on a 1 Mhz machine.  Which is about 3 FPS.  

This figure may seem small but keep in mind that in a real game you almost never address every point on the screen in a single frame. Furthermore a single pixel plot is considerably - about seven times - less efficient than drawing a bitmap on the screen a byte at a time.

I should also stress that what we are calculating is very much a unobtainable result since to draw a whole screen would require a stream of instructions about 107K in length which would consume all of the memory on a typical Apple //c or //e.

Another, perhaps more common benchmark is to compare your code against other applications or implementations. For the sake of completeness I've gathered a few popular DHR tools. In each case I've written the fastest code I could manage with the goal of filling the entire screen by drawing horizontal lines from left to right. Once the screen is filled we change colours and start again. After cycling through all 16 colours we divide our time by 16 to get the average time to fill an entire screen.

So without further ado here are our challengers...


Beagle Graphics - Created by the ever popular Beagle Bros. It contains a number of tools for drawing graphics on the DHR screen from basic. Here I used the following program:
&MODE(2):FOR C = 0 TO 15:&HCOLOR=C:FOR Y = 0 to 191:HPLOT 0,Y to 139,Y: NEXT Y,C:

Graphics Magician - Developed in 1982 by Penguin Software, this program was the basis for many graphical adventure games. It allows people to create drawings using a special program which records various primitive graphical operations (line draws, fills, text). The user can then save these in a file and redraw them on the screen from BASIC or assembly using a free-to-use engine written in assembly. In this case I reverse-engineered the file format and created a file with a series of line drawing commands covering the entire screen. This is followed by a command to change colour and another screens-worth of line drawing commands.

cc65 - One of the more popular solutions for doing cross-compilation for the Apple II. I adapted routines from Bill Buckles PDF on drawing DHR graphics using CC65 to write the following code:
int main(void)
{
 int c,i;
    videomode(VIDEOMODE_80COL);
    clrscr();
    dhireson();
    dhiresclear();
 for (c=0;c<16;c++)
  for (i=0;i<192;i++)
   dhrfbox(0, i, 139, i,c);
    return 0;
}
The source code and disk images for Bill's library can be found here

The results speak for themselves:

ApplicationScreen fill% of Theoretical
Theoretical0.39 seconds100%
Our Application2 seconds19.5%
cc653.4 seconds11%
Beagle Graphics10 seconds3.9%
Graphics Magician (DHR)24 seconds1.62%


In my experience 20% of the theoretical max on your first go is quite a good showing. We also appear to have beat out all of the other approaches we tested including commercial applications.

Can we do any better?


The answer is a definite "yes". You will notice that every time we plot a point we push the Y register to preserve it's contents, then we PULL it to get the value back so we can update it. However when DX > DY the majority of the time this is entirely unnecessary. Making this change brings us down to 1.85s or 20.1% of our theoretical max!

Another optimization comes from the fact that the values DX, DY and EPSILON never change once the line drawing has begun. Which means we could, by means of self-modifying code replace all the places where we use these values with absolute addressing rather than zero page addressing. This would save us about three cycles per plot.


Large scale vs small scale patterns.


All the optimizations I've just mentioned are all what I call "small scale" they involve taking the existing approach - in this case plotting a single point at a time - and making it run more tightly.  A large scale optimization is where we change the way we are solving the problem.  

For example, if we know we are going to be plotting lines and that lines generally contain more than one point.  We could re-write our plot routine to draw two pixels at a time. We could handle lines with an odd number of pixels by writing a special case.

If you look closely at the CC65 program you will see that we are using the dhrfbox function. This was actually designed for drawing filled boxes instead of straight lines. Why did we use it then? Because it uses this trick extensively, and will draw just about two pixels per plot by writing a whole byte to the screen at a time. If we were to use the line drawing function from the CC65 library it would be about 20 times slower!

Ok that's it for this post, next up we will build out a simple API for the drawing routine as well as an demo app doing some 3D style drawing. After that I think we should cover writing a character generator.

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.

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...