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!

2 comments:

Michaelangel007 said...

Excellent write-up as usual..

One minor correction:

> by using the instructions TSX, TSY, TSA respectively.

While TSX is correct the remaining instructions are incorrect.. There is no TSY or TSA instructions on the 6502 / 65c02 / 65816.

Michael
AppleWin Dev

Axon Punch said...

Thanks again for the correction. I wonder why I made this assumption? Other than lack of sleep/caffeine. Perhaps it's because some effort was put into the orthogonality of the 65c02's ISA. Maybe I figured they had gone all the way?

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