*-------------------------------
*
* graphics.s
*
* This is a set of graphics routines for dim4, for use
* with Merlin 128's linker.
*
* Currently it contains the line drawing routine, the
* fill routine, and a routine to place blocks of
* graphics characters on the screen.
*
* History:
*  SLJ    6/9/96  -Split into relocatable linker file
*                 -Implemented modified fill routine
*         6/10    -Put old routine back in :)
*                 -Stuck in module to generate symmetric obs
*         6/15    -Fixed up symmetry so that he uses
*                  patterns and such.
*         6/17    -Fixed up SPLAT, enhanced SYMM patterns
*         6/21    -Fixed up SPLAT to keep from writing to CIA!
*         6/22    -Added dotted line capability
                          
         DSK "graphics" ;Assemble to disk
                          
         REL              ;RELocatable
                          
* Constants
                          
BUFF1    EQU $3000        ;Locations of drawing buffers
BUFF2    EQU $3800        
BUFFER1  EQU BUFF1+$0480  ;Offset to secondary objects
BUFFER2  EQU BUFF2+$0480  
                          
PATTERN1 EQU $C100        ;List of fill pattern
PATTERN2 EQU $C180        ;(set up by calling routine)
BITP     EQU $C800        ;Bit table for line routine
LO40     EQU $C900        ;Tables of 40*x/8
HI40     EQU $C980        ;To locate offset into buffer
LO96     EQU $CA00        ;for a given column
HI96     EQU $CA80        
REVLIST  EQU $CB00        ;List of reflected x
                          
SCRNLOC  EQU 1024         ;Where is the screen located?
                          
* Some variables
                          
COLSTEP  = $51            ;Lines: How many bytes per col
COUNTPTS = $52            
                          
NUMOB1   EQU $A5          ;Number of points in object 1
NUMOB12  EQU $A6          
TOTNUM   EQU $A7          ;Total number of points in list
OFFSET   EQU $A8          ;Offset into grid
                          
DOTTED   EQU $B1          ;Flag for dotted line (1=dotted)
                          
POINT1   EQU $6A          ;Some spare pointers
POINT2   EQU $6C          
SCREENP  EQU $6E          ;Pointer used by screen plotter
COLORP   EQU $70          ;pointer into color RAM
COLOR    EQU $72          ;Color to store in color RAM
NUMSIDE  EQU $8B          ;# of chars per side of square
                          
NUMTOROT EQU $69          ;Number of points to rotate in ROTPROJ
                          
BUFFER   EQU $A3          ;Pointer to drawing buffer
X1       EQU $FB          ;Points for drawing a line
Y1       EQU $FC          ;These zero page addresses
X2       EQU $FD          ;don't conflict with BASIC
Y2       EQU $FE          
OLDX     EQU $FD          
CHUNK    EQU $FE          
DX       EQU $67          
DY       EQU $68          
TEMP1    EQU $FB          ;Of course, could conflict with x1
TEMP2    EQU $FC          ;Temporary variables
ZTEMP    EQU $02          ;Used for buffer swap.  Don't touch.
ACC      EQU $22          ;Used by math routine
AUX      EQU $24          
EXT      EQU $26          
                          
*-------------------------------
*
* Symmtery routine
*
* This routine fills out the top four objects by creating
* the D4 symmetry-related objects from the secondary object,
* for a total of four objects.
                          
SYMM     ENT              
                          
         LDX #39          
         LDY #00          
         LDA ZTEMP        
         CMP #>BUFF1      
         BEQ :BUF1        
         JMP :BUF2        
                          
:BUF1                     ;First a reflection through y=0
         LDA BUFFER1,Y    ;Grab columns, one at a time
         EOR #$FF         
         ORA PATTERN1,Y   
         STA BUFFER1+200,X
         LDA BUFFER1+40,Y ;Col 2
         EOR #$FF         
         ORA PATTERN1,Y   
         STA BUFFER1+240,X
         LDA BUFFER1+80,Y ;Col 3
         EOR #$FF         
         ORA PATTERN1,Y   
         STA BUFFER1+280,X
         LDA BUFFER1+120,Y ;You get the picture
         EOR #$FF         
         ORA PATTERN1,Y   
         STA BUFFER1+320,X
         LDA BUFFER1+160,Y
         EOR #$FF         
         ORA PATTERN1,Y   
         STA BUFFER1+360,X
         INY              
         DEX              
         BPL :BUF1        
                          
         LDY #39          ;Now do reflections through x=0
:L1      LDA BUFFER1,Y    
         TAX              
         LDA REVLIST,X    
         AND PATTERN1,Y   
         STA BUFFER1+560,Y ;Column1 -> column 10
         LDA BUFFER1+40,Y 
         TAX              
         LDA REVLIST,X    
         AND PATTERN1,Y   
         STA BUFFER1+520,Y ;2 -> 9
         LDA BUFFER1+80,Y 
         TAX              
         LDA REVLIST,X    
         AND PATTERN1,Y   
         STA BUFFER1+480,Y
         LDA BUFFER1+120,Y
         TAX              
         LDA REVLIST,X    
         AND PATTERN1,Y   
         STA BUFFER1+440,Y
         LDA BUFFER1+160,Y ;5 -> 1
         TAX              
         LDA REVLIST,X    
         AND PATTERN1,Y   
         STA BUFFER1+400,Y
                          
         LDA BUFFER1+200,Y ;Now do it for y=0 reflection
         TAX              
         LDA REVLIST,X    
         AND PATTERN2,Y   
         STA BUFFER1+760,Y ;Column1 -> column 10
         LDA BUFFER1+240,Y
         TAX              
         LDA REVLIST,X    
         AND PATTERN2,Y   
         STA BUFFER1+720,Y ;2 -> 9
         LDA BUFFER1+280,Y
         TAX              
         LDA REVLIST,X    
         AND PATTERN2,Y   
         STA BUFFER1+680,Y
         LDA BUFFER1+320,Y
         TAX              
         LDA REVLIST,X    
         AND PATTERN2,Y   
         STA BUFFER1+640,Y
         LDA BUFFER1+360,Y ;5 -> 1
         TAX              
         LDA REVLIST,X    
         AND PATTERN2,Y   
         STA BUFFER1+600,Y
                          
         DEY              
         BMI :DONE1       
         JMP :L1          
:DONE1   RTS              
                          
:BUF2                     ;Same thing, for buffer 2
         LDA BUFFER2,Y    ;Grab columns, one at a time
         EOR #$FF         
         ORA PATTERN1,Y   
         STA BUFFER2+200,X
         LDA BUFFER2+40,Y ;Col 2
         EOR #$FF         
         ORA PATTERN1,Y   
         STA BUFFER2+240,X
         LDA BUFFER2+80,Y ;Col 3
         EOR #$FF         
         ORA PATTERN1,Y   
         STA BUFFER2+280,X
         LDA BUFFER2+120,Y ;You get the picture
         EOR #$FF         
         ORA PATTERN1,Y   
         STA BUFFER2+320,X
         LDA BUFFER2+160,Y
         EOR #$FF         
         ORA PATTERN1,Y   
         STA BUFFER2+360,X
         INY              
         DEX              
         BPL :BUF2        
                          
         LDY #39          ;Now do reflections through x=0
:L2      LDA BUFFER2,Y    
         TAX              
         LDA REVLIST,X    
         AND PATTERN1,Y   
         STA BUFFER2+560,Y ;Column1 -> column 10
         LDA BUFFER2+40,Y 
         TAX              
         LDA REVLIST,X    
         AND PATTERN1,Y   
         STA BUFFER2+520,Y ;2 -> 9
         LDA BUFFER2+80,Y 
         TAX              
         LDA REVLIST,X    
         AND PATTERN1,Y   
         STA BUFFER2+480,Y
         LDA BUFFER2+120,Y
         TAX              
         LDA REVLIST,X    
         AND PATTERN1,Y   
         STA BUFFER2+440,Y
         LDA BUFFER2+160,Y ;5 -> 1
         TAX              
         LDA REVLIST,X    
         AND PATTERN1,Y   
         STA BUFFER2+400,Y
                          
         LDA BUFFER2+200,Y ;Now do it for y=0 reflection
         TAX              
         LDA REVLIST,X    
         AND PATTERN2,Y   
         STA BUFFER2+760,Y ;Column1 -> column 10
         LDA BUFFER2+240,Y
         TAX              
         LDA REVLIST,X    
         AND PATTERN2,Y   
         STA BUFFER2+720,Y ;2 -> 9
         LDA BUFFER2+280,Y
         TAX              
         LDA REVLIST,X    
         AND PATTERN2,Y   
         STA BUFFER2+680,Y
         LDA BUFFER2+320,Y
         TAX              
         LDA REVLIST,X    
         AND PATTERN2,Y   
         STA BUFFER2+640,Y
         LDA BUFFER2+360,Y ;5 -> 1
         TAX              
         LDA REVLIST,X    
         AND PATTERN2,Y   
         STA BUFFER2+600,Y
                          
         DEY              
         BMI :DONE2       
         JMP :L2          
:DONE2   RTS              
                          
                          
*-------------------------------
*
* Fill routine
*
* This is the old cookie-cutter, spiffed up and a bit
* optimized, since it is only used on the first 5x5 grid
*
* FILLLIST -- a handy table to tell which rows have been
*             filled.
                          
FILL     ENT              
                          
* LDY #39
* LDA #00
*:L1 STA FILLLIST,Y
* DEY
* BPL :L1
                          
         LDY #39          
:LOOP1   LDA #$80         ;Need to add $0480 to get to
         STA BUFFER       ;first 5x5 grid
         LDA ZTEMP        
         CLC              
         ADC #$04         
         STA BUFFER+1     
                          
* The old way.
* Lots of adds and such, but seems to be a bit faster
                          
         LDX #05          
:LOOP2   LDA (BUFFER),Y   
         BNE :GOTCHA      
         LDA #$FF         ;Fill flat, patterns later
         STA (BUFFER),Y   
         LDA BUFFER       
         ADC #40          
         STA BUFFER       
         BCC :CONT        
         INC BUFFER+1     
         CLC              
:CONT    DEX              
         BNE :LOOP2       
         BEQ :NOTHING     
                          
:GOTCHA  LDX #$FF         
:L1      INX              
         ASL              ;Bit, bit, who's got the bit?
         BCC :L1          
                          
         LDA BITP,X       
         EOR #$FF         
         ORA (BUFFER),Y   
         STA (BUFFER),Y   
                          
* Now work backwards.
* Remember: $0480 + 160 = $0520
         LDA #$20         
         STA BUFFER       
         LDA ZTEMP        
         CLC              
         ADC #$05         
         STA BUFFER+1     
                          
:L4      SEC              
:LOOP3   LDA (BUFFER),Y   
         BNE :DING        
         LDA #$FF ;Fill flat, patterns later
         STA (BUFFER),Y   
         LDA  BUFFER      
         SBC #40          
         STA BUFFER       
         BCS :LOOP3 ;Bound to hit something sooner
         DEC BUFFER+1 ;or later.
         BCC :L4          
                          
:DING    LDX #$08 ;We have a winner!
:L2      DEX              
         LSR              
         BCC :L2          
         LDA  BITP,X      
         ORA (BUFFER),Y   
         STA (BUFFER),Y   
                          
:NOTHING DEY              
         BPL :LOOP1 ;Keep going until Y hits zero
                          
* Well, another possibility, but maybe not as good
*
* It goes through the list vertically, thus skipping
* all those column-adds.  The downside is all the
* comparisons.  It assumes that at least one line
* (e.g. y=20) will need to be completely filled going
* forwards.
*
*:LOOP2 LDY  #39 ;Forwards part
*:LOOP1 LDA FILLLIST,Y
* BNE :SKIP
* LDA (BUFFER),Y
* BEQ :NOTYET
* STA FILLLIST,Y ;Nonzero implies filled
* TAX ;Oops, time to fill
* LDA  BITP,X
*:NOTYET EOR #$FF
* STA (BUFFER),Y
*:SKIP DEY
* BPL :LOOP1
* LDA BUFFER
* BPL :BACK ;$xx80 + 160 = xx00 + 32
* CLC
* ADC #40
* STA BUFFER
* BCC :LOOP2
* INC BUFFER+1
* BCS :LOOP2
*
*:BACK ;Now work backwards
*:LOOP3 SEC ;This lets us quit early, once
* ;everyone is filled.
* LDY #39 ;Forwards part
*:LOOP4 LDA FILLLIST,Y
* BEQ :SKIP2
* CLC ;Flag if any entry nonzero
* LDA (BUFFER),Y
* BEQ :NOTEVEN
* TAX ;Oops, time to fill
* LDA  #00
* STA FILLLIST,Y ;Zero out fill flags
* SEC
* LDA BITP,X
*:NOTEVEN
* STA (BUFFER),Y
*:SKIP2 DEY
* BPL :LOOP4
* BCS :DONE ;We can finish early
* LDA BUFFER
* SEC
* SBC #40
* STA BUFFER
* BPL :DONE ;$xx80 is as low as it goes.
* BCS :LOOP3
* DEC BUFFER+1
* BCC :LOOP3
*:DONE
         RTS              
                          
                          
*-------------------------------
* Drawin' a line.  A fahn lahn.
                          
*** Some useful macros
                          
CINIT    MAC              ;Macro to initialize the counter
         LDA ]1           ;dx or dy
         LSR              
         <<<              ;The dx/2 makes a nicer looking line
                          
***** Macro to take a step in X
                          
XSTEP    MAC              
         LDX DX           ;Number of loop iterations
         >>> CINIT,DX     
XLOOP    LSR CHUNK        
         BEQ FIXC         ;Update column
         SBC DY           
         BCC FIXY         ;Time to step in Y
CONT     DEX              
         BNE XLOOP        
DONE     LDA CHUNK        ;Plot the last chunk
         LSR              ;Make sure to get at least 1 pixel
         EOR OLDX         
         AND DOTTED       
         ORA (BUFFER),Y   
         STA (BUFFER),Y   
         RTS              
                          
FIXC     PHA              
         LDA OLDX         
         AND DOTTED       ;dotted line mask
         ORA (BUFFER),Y   ;Plot
         STA (BUFFER),Y   
         LDA #$FF         ;Update chunk
         STA OLDX         
         STA CHUNK        
         LDA BUFFER       ;Increase the column
         CLC              
         ADC COLSTEP      
         STA BUFFER       
         BCC C2           
         INC BUFFER+1     
C2                        
         PLA              
         SEC              
         SBC DY           
         BCS CONT         ;Up above
         ADC DX           
         IF I,]1          ;Do we use INY or DEY?
         INY              
         ELSE             
         DEY              
         FIN              
         BCS CONT         ;Always branches
*CONT DEX
* BNE XLOOP
* JMP DONE
                          
FIXY     ADC DX           
         PHA              
         LDA OLDX         
         EOR CHUNK        
         AND DOTTED       
         ORA (BUFFER),Y   
         STA (BUFFER),Y   
         LDA CHUNK        
         STA OLDX         
         PLA              
         IF I,]1          ;Update Y
         INY              
         ELSE             
         DEY              
         FIN              
         BCS CONT         ;ADC sets the carry above
         <<<              ;End of Macro xstep
                          
***** Take a step in Y
                          
YSTEP    MAC              
         LDX DY           ;Number of loop iterations
         BEQ DONE         ;If dy=0 it's just a point
         >>> CINIT,DY     
         SEC              
YLOOP    PHA              
         TYA              ;Only plot on even lines if
         AND DOTTED       ;doing the dotted thing.
         BNE SKIPIT       ;(dotted=01 if we are dotting)
         LDA OLDX         
         ORA (BUFFER),Y   
         STA (BUFFER),Y   
SKIPIT   PLA              
         IF I,]1          
         INY              
         ELSE             
         DEY              
         FIN              
         SBC DX           
         BCC FIXX         
CONT     DEX              
         BNE YLOOP        
DONE     LDA OLDX         
         ORA (BUFFER),Y   
         STA (BUFFER),Y   
         RTS              
                          
FIXX     ADC DY           
         LSR OLDX         
         SEC              ;Important!
         BNE CONT         ;up
* BEQ FIXC
* DEX
* BNE YLOOP
* JMP DONE
                          
FIXC     PHA              
         ROR OLDX         ;Carry is set (tricky!)
         LDA COLSTEP      
         ADC BUFFER       
         STA BUFFER       
         BCC C2           
         INC BUFFER+1     
C2       PLA              
         SEC              ;Carry needs to be set
         BCS CONT         
* DEX
* BNE YLOOP
* JMP DONE
         <<<              ;End of Macro ystep
                          
**** Initial line setup
                          
* COLSTEP -- How many bytes per column, either 40 or 96
* LO40, HI40, etc. -- Tables of 40*x/8
* X1,X2,Y1,Y2 -- points to draw line between
* BUFFER -- ZP pointer to buffer to draw into
* ZTEMP -- Contains high byte of buffer base
* DOTTED -- Set to $01 if doing dotted draws (diligently)
                          
DRAW     ENT              
                          
:SETUP   SEC              ;Make sure x1<x2
         LDA X2           
         SBC X1           
         BPL :CONT        
         LDA Y2           ;If not, swap P1 and P2
         LDY Y1           
         STA Y1           
         STY Y2           
         LDA X1           
         LDY X2           
         STY X1           
         STA X2           
                          
         SEC              
         SBC X1           ;Now A=dx
:CONT    STA DX           
         LDX X1           ;Put x1 into X, now we can trash X1
                          
COLUMN   LDA COLSTEP      ;Find the first column for X
         CMP #96          ;And set up the drawing buffers
         BEQ :CONT96      
         LDA LO40,X       ;If 40, then we need to offset into
* CLC
         ADC #$80         ;the buffer by 8*12*12=$480
         STA BUFFER       
         LDA HI40,X       
         ADC #$04         ;Could even incorporate into table
         BCC :DONE        
                          
:CONT96  LDA LO96,X       ;Otherwise we start at beginning
         STA BUFFER       ;of buffer
         LDA HI96,X       
         CLC              
:DONE    ADC ZTEMP        
         STA BUFFER+1     
                          
         SEC              
         LDA Y2           ;Calculate dy
         SBC Y1           
         BPL :CONT2       ;Is y2>y1?
         EOR #$FF         ;Otherwise dy=y1-y2
         ADC #$01         ;Carry SHOULD be clear
:CONT2   STA DY           
         CMP DX           ;Who's bigger: dy or dx?
         BCC STEPINX      ;If dx, then...
         JMP STEPINY      
                          
STEPINX  LDA DOTTED       
         BEQ :CONT        
         LDA #%10101010   ;Dotted line mask
:CONT    EOR #$FF         
         STA DOTTED       
         LDY Y1           
         CPY Y2           ;y1,y2,oldx,chunk share same mem
         LDA BITP,X       ;X currently contains x1
         STA OLDX         
         STA CHUNK        
         BCS XDECY        ;Do we step forwards or backwards in Y?
                          
XINCY    >>> XSTEP,INY    
                          
XDECY    >>> XSTEP,DEY    
                          
STEPINY  LDY Y1           ;Note I assume DOTTED = 00 or 01
         LDA BITP,X       ;X=x1
         STA OLDX         
         LSR              ;Y doesn't use chunks
         EOR OLDX         ;So we just want the bit
         STA OLDX         
         CPY Y2           
         BCS YDECY        
                          
YINCY    >>> YSTEP,INY    
                          
YDECY    >>> YSTEP,DEY    
                          
*-------------------------------
*
* This routine will place a square chunk of characters
* at a specified place in memory, and store
* color in the corresponding color memory.
*
* On entry: Y=row, X=column, A=Starting character
*           COLOR=color  NUMSIDE=# of characters per
*                                side of square
                          
SPLAT    ENT              
         STA TEMP1        
         LDA #00          
         STA SCREENP+1    
         TYA              ;Now we need to *40
         ASL              ;which is *32 + *8
         ASL              
         ASL              
         STA TEMP2        ;times 8 (no worse than 196)
         ASL              
         ROL SCREENP+1    
:CONT1   ASL              
         ROL SCREENP+1    
:CONT2   ADC TEMP2        
         STA SCREENP      
         STA COLORP       
         LDA SCREENP+1    
         ADC #04          ;Screen starts at $0400
         STA SCREENP+1    
         ADC #$D4         ;color at $D800
         STA COLORP+1     ;Wow, lots of code there :(
                          
         STX TEMP2        
         LDA NUMSIDE      
         STA AUX          ;Need this guy too :(
:L2      LDA TEMP1        
         LDX NUMSIDE      
         LDY TEMP2        
:L1      STA (SCREENP),Y  
         PHA              
         LDA COLOR        
         STA (COLORP),Y   
         PLA              
         INY              
         ADC NUMSIDE      ;Characters are ordered vertically
         DEX              
         BNE :L1          
         INC TEMP1        
         LDA SCREENP      
         CLC              
         ADC #40          
         STA SCREENP      
         STA COLORP       
         BCC :CONT4       
         INC SCREENP+1    
         INC COLORP+1     
:CONT4   LDA COLORP+1     
         CMP #$DB         ;Don't get too big!!!
         BCC :CONT5       
         LDA COLORP       
         CMP #$C1         ;We're past the last row, and
         BCS :DONE        ;I don't feel like writing to CIA
:CONT5   DEC AUX          
         BNE :L2          
                          
:DONE    RTS              ;Good Lord I hope that's it.
