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 - Part 9: An 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...