6502 Pen plotter - Drawing lines by Lee Davison
[Back]
Because of the nature of the plotter's motors the number of possible lines that can be drawn can be halved by reversing the direction of the X axis motor in the case of lines with a -ve end X co-ordinate.Interpolation.The same can be done again for lines with a -ve end Y co-ordinate. The result of this is that all possible lines can be drawn using only unsigned integer maths.
Drawing a line between any two arbitary points on a grid the exact line cannot be drawn as we are limited to either horizontals, verticals or diagonals. So to draw a representation of the line on the square lattice the program can only generate the best approximation.The routine.The routine used by the plotter does just this. Given the distant endpoint of a line (the near endpoint is always (0,0) as all lines are drawn relative to the current pen position) the routine generates the intermediate points.
The main feature of this routine is that the drawing of the line involves no multiplication or division operations so even long lines are generated quicker that they are drawn.
This graphic shows how three possible lines can be drawn using just one horizontal or vertical and one diagonal.
First the endpoint X co-ordinate is tested and if it is -ve the X motor direction is set -ve and the sign of the X co-ordinate is changed.More to come.WriteLine LDA #$02 ; set X direction +ve STA Dirbyte ; and save it LDA Xwordh ; get X axis high byte BPL Xispos ; branch if not -ve EOR #$FF ; invert byte STA Xwordh ; save it LDA Xwordl ; get low byte EOR #$FF ; invert it STA Xwordl ; save it INC Xwordl ; +1 BNE Nohighx ; branch if no carry INC Xwordh ; else increment high byte Nohighx LDA #$00 ; clear A (direction -ve) STA Dirbyte ; clear direction byte XisposNext exactly the same thing is done for the endpoint Y co-ordinate.LDA Ywordh ; get Y axis high byte BPL Yispos ; branch if not -ve EOR #$FF ; invert byte STA Ywordh ; save it LDA Ywordl ; get low byte EOR #$FF ; invert it STA Ywordl ; save it INC Ywordl ; +1 BNE Nohighy ; branch if no carry INC Ywordh ; else increment high byte Nohighy LDA #$08 ; set Y direction +ve ORA Dirbyte ; OR in X direction STA Dirbyte ; and save it YisposNow both X and Y are positive and the motor step directions are set according to the original signs of the X and Y co-ordinates.Next we test to see if the line is vertical, i.e. X = 0. If it is then we have only a simple line and no interpolation is needed as the line can be drawn with only steps along the Y axis.
LDA Xwordh ; get X high byte ORA Xwordl ; OR with low byte BNE Testhoriz ; if not 0 go test horizontal line LDX Ywordl ; get Y low byte LDY Ywordh ; get Y high byte LDA #$04 ; step Y motor BNE Savnwrite2 ; go save & draw segment (branch always) ... Savnwrite2 STX Lcntl ; save as count low byte STY Lcnth ; save as count high byte STA Stepbyte ; save step byte JMP WriteSeg ; go draw segmentIf it wasn't a vertical line we then test for a horizontal line as this can be simply drawn with only X axis steps.Testhoriz LDA Ywordh ; get Y high byte ORA Ywordl ; OR with low byte BNE Testdiag ; if not 0 go test diagonal line LDA #$01 ; step X motor BNE Savnwrite ; go save & draw segment (branch always) ... Savnwrite LDX Xwordl ; get X low byte LDY Xwordh ; get X high byte Savnwrite2 STX Lcntl ; save as count low byte STY Lcnth ; save as count high byte STA Stepbyte ; save step byte JMP WriteSeg ; go draw segmentIf it wasn't a horizontal line the final test compares the X and Y co-ordinates. If they are equal our final simple case, where the line can be drawn by stepping simultaneously along both the X and Y axes, has been found.Testdiag LDA Xwordh ; get X high byte CMP Ywordh ; compare Y high byte BNE Diffbyte ; branch if not equal LDA Xwordl ; get X low byte CMP Ywordl ; compare Y low byte BNE Diffbyte ; branch if not equal LDA #$05 ; step both motors Savnwrite LDX Xwordl ; get X low byte LDY Xwordh ; get X high byte Savnwrite2 STX Lcntl ; save as count low byte STY Lcnth ; save as count high byte STA Stepbyte ; save step byte JMP WriteSeg ; go draw segmentAt this point the three simple cases have been disposed of and only lines requiring interpolation remain. The first thing to do is determine which is the bigger of X and Y. This is conviniently already available inthe carry flag as a result of the earlier X and Y compare.Diffbyte BCC Yisbigger ; branch if Y gt XNow we can set the total number of steps (always equal to the greater of X and Y) and we also know if the non diagonal step is horizontal or vertical (along the X or Y axis) from which was greater.We also know how many diagonal moves we need to make (the smaller of X and Y) and we save this as Doublel/h. Now we can calculate the number of horizontal/vertical moves needed by subtracting this from the total number of moves and saving it as Singlel/h. P> Here is the code for the case of X gt Y.
; carry is set for subtract LDA Xwordl ; get X low byte STA Totall ; save as total low byte STA Testl ; save as test low byte SBC Ywordl ; subtract Y low byte STA Singlel ; save single low byte LDA Xwordh ; get X high byte STA Totalh ; save as total high byte STA Testh ; save as test high byte SBC Ywordh ; subtract Y high byte STA Singleh ; save single high byte LDX Ywordl ; get Y low byte LDY Ywordh ; get Y high byte LDA #$01 ; set X as stepThe last thing we need is a way to decide which of the steps we need to take next as we are drawing the line. For this we use the test variable which is initialised to the smaller of X and Y minus half the number of the larger of X and Y. (This gives the smallest errors at the ends of the line, where they would be noticed most.)Dotest STX Doublel ; save as double low byte STY Doubleh ; save as double high byte STA Stepsav ; save it LSR Testh ; shift test high byte ROR Testl ; shift test low byte SEC ; set carry for subtract LDA Doublel ; get double low byte SBC Testl ; subtract test low byte STA Testl ; save as test low byte LDA Doubleh ; get double high byte SBC Testh ; subtract test high byte STA Testh ; save as test high byte (Test=Double-(Single/2))