* * GRLIB -- hires bitmap graphics library * * Basically just a modified BLARG * * SLJ 5/20/00 * ORG $C000 * Constants CHROUT = $FFD2 X1 = $02 Y1 = $04 X2 = $06 Y2 = $08 DX = $0A DY = $0C ROW = $0D ;Bitmap row COL = $0E ;and column INRANGE = $0F ;Range check flag RADIUS = $10 CHUNK1 = $11 ;Circle routine stuff OLDCH1 = $12 CHUNK2 = $13 OLDCH2 = $14 CX = DX CY = DY X = $15 Y = $16 LCOL = $17 ;Left column RCOL = $18 TROW = $19 ;Top row BROW = $1A ;Bottom row RANGE1 = $1B RANGE2 = INRANGE POINT = $1C TEMP2 = $1E TEMP = $20 ;1 byte * * Jump table * JMP InitGr JMP SetOrg JMP GRON JMP GROFF JMP SETCOLOR JMP MODE JMP BUFFER JMP SWAPBUF JMP PLOT JMP PLOTABS JMP LINE JMP CIRCLE TXT 'judd-o-rama' * * Initialize stuff * InitGr LDA #00 STA ORGX STA ORGY STA BANK LDA #$E0 STA BASE LDA #$FF STA DONTPLOT STA BITMASK LDA #17 STA MODENUM RTS * * Set center of screen * .X = x-coord, .Y = y-coord * ORGX DFB 00 ORGY DFB 00 SetOrg STX ORGX STY ORGY RTS * * PLOT -- plot the point in x1,y1 * * Note that x1 and y1 are 16-bit! * * Out of range values are allowed and * computed, so that pointer updates * will work correctly. ROW and COL are * computed for reference by other * routines. * * INRANGE is set to 0 if point is on screen * DONTPLOT DFB 01 ;0=Don't plot point, just compute ;coordinates (used by e.g. circles) PLOT LDA Y1 SEC SBC ORGY STA Y1 BCS :C1 DEC Y1+1 SEC :C1 LDA X1 SBC ORGX STA X1 BCS PLOTABS DEC X1+1 PLOTABS LDA Y1 STA ROW AND #7 TAY LDA Y1+1 LSR ;Neg is possible ROR ROW LSR ROR ROW LSR ROR ROW LDA #00 STA POINT LDA ROW CMP #$80 ROR ROR POINT CMP #$80 ROR ROR POINT ;row*64 ADC ROW ;+row*256 CLC ADC BASE ;+bitmap base STA POINT+1 LDA X1 TAX STA COL LDA X1+1 LSR ROR COL LSR ROR COL LSR ROR COL TXA AND #$F8 CLC ADC POINT ;+(X AND #$F8) STA POINT LDA X1+1 ADC POINT+1 STA POINT+1 TXA AND #7 TAX LDA ROW CMP #25 BCS :rts LDA COL CMP #40 BCS :rts LDA DONTPLOT BEQ :rts SEI ;Get underneath ROM LDA $01 PHA LDA #$34 STA $01 LDA (POINT),Y EOR BITMASK AND BITTAB,X EOR (POINT),Y STA (POINT),Y PLA STA $01 CLI LDA #00 :rts STA INRANGE RTS *------------------------------- DO 0 SETPOINT ;Alternative entry point ;X=y-coord, LINNUM=x-coord ;On exit, X,Y are AND #$07 ;i.e. are set up correctly. TXA AND #248 STA POINT LSR LSR LSR ADC BASE ;Base of bitmap STA POINT+1 LDA #00 ASL POINT ROL ASL POINT ROL ASL POINT ROL ADC LINNUM+1 ADC POINT+1 STA POINT+1 TXA AND #7 TAY LDA LINNUM AND #248 CLC ;Overflow is possible! ADC POINT STA POINT BCC SETPIXEL INC POINT+1 SETPIXEL LDA LINNUM AND #$07 TAX LDA DONTPLOT BEQ :RTS LDA POINT+1 SEC SBC BASE ;Overflow check CMP #$20 BCS :RTS SEI ;Get underneath ROM LDA #$34 STA $01 LDA (POINT),Y EOR BITMASK AND BITTAB,X EOR (POINT),Y STA (POINT),Y LDA #$37 STA $01 CLI * LDX TEMP2 * LDY TEMP2+1 ;On exit, X,Y are AND #$07 ;i.e. are set up correctly. ;for more plotting :RTS RTS FIN *------------------------------- BITMASK DFB #$FF ;Set point BITTAB DFB $80,$40,$20,$10,$08,$04,$02,$01 *------------------------------- * Drawin' a line. A fahn lahn. * * To deal with off-screen coordinates, the current row * and column (40x25) is kept track of. These are set * negative when the point is off the screen, and made * positive when the point is within the visible screen. * Little bit position table BITCHUNK HEX FF7F3F1F0F070301 CHUNK EQU X2 OLDCHUNK EQU X2+1 * DOTTED -- Set to $01 if doing dotted draws (diligently) * X1,X2 etc. are set up above (x2=LINNUM in particular) * Format is LINE x2,y2,x1,y1 LINE :CHECK LDA X2 ;Make sure x1=y1? LDA Y1 ;Otherwise dy=y1-y2 SEC SBC Y2 TAY LDX #$88 ;DEY :DYPOS STY DY ;8-bit DY -- FIX ME? STX YINCDEC STX XINCDEC LDA #00 STA DONTPLOT JSR PLOT ;Set up .X,.Y,POINT, and INRANGE INC DONTPLOT LDA BITCHUNK,X STA OLDCHUNK STA CHUNK SEI ;Get underneath ROM LDA #$34 STA $01 LDX DY CPX DX ;Who's bigger: dy or dx? BCC STEPINX ;If dx, then... LDA DX+1 BNE STEPINX * * Big steps in Y * * To simplify my life, just use PLOT to plot points. * * No more! * Added special plotting routine -- cool! * * X is now counter, Y is y-coordinate * * On entry, X=DY=number of loop iterations, and Y= * Y1 AND #$07 STEPINY LDA #00 STA OLDCHUNK ;So plotting routine will work right LDA CHUNK LSR ;Strip the bit EOR CHUNK STA CHUNK TXA BNE :CONT ;If dy=0 it's just a point INX :CONT LSR ;Init counter to dy/2 * * Main loop * YLOOP STA TEMP LDA INRANGE ;Range check BNE :SKIP LDA (POINT),Y ;Otherwise plot EOR BITMASK AND CHUNK EOR (POINT),Y STA (POINT),Y :SKIP YINCDEC INY ;Advance Y coordinate CPY #8 BCC :CONT ;No prob if Y=0..7 JSR FIXY :CONT LDA TEMP ;Restore A SEC SBC DX BCC YFIXX YCONT DEX ;X is counter BNE YLOOP YCONT2 LDA (POINT),Y ;Plot endpoint EOR BITMASK AND CHUNK EOR (POINT),Y STA (POINT),Y YDONE LDA #$37 STA $01 CLI RTS YFIXX ;x=x+1 ADC DY LSR CHUNK BNE YCONT ;If we pass a column boundary... ROR CHUNK ;then reset CHUNK to $80 STA TEMP2 LDA COL BMI :C1 ;Skip if column is negative CMP #39 ;End if move past end of screen BCS YDONE :C1 LDA POINT ;And add 8 to POINT ADC #8 STA POINT BCC :CONT INC POINT+1 :CONT INC COL ;Increment column BNE :C2 LDA ROW ;Range check CMP #25 BCS :C2 LDA #00 ;Passed into col 0 STA INRANGE :C2 LDA TEMP2 DEX BNE YLOOP BEQ YCONT2 * * Big steps in X direction * * On entry, X=DY=number of loop iterations, and Y= * Y1 AND #$07 COUNTHI DFB 00 ;Temporary counter ;only used once STEPINX LDX DX LDA DX+1 STA COUNTHI CMP #$80 ROR ;Need bit for initialization STA Y1 ;High byte of counter TXA BNE :CONT ;Could be $100 DEC COUNTHI :CONT ROR * * Main loop * XLOOP LSR CHUNK BEQ XFIXC ;If we pass a column boundary... XCONT1 SBC DY BCC XFIXY ;Time to step in Y? XCONT2 DEX BNE XLOOP DEC COUNTHI ;High bits set? BPL XLOOP XDONE LSR CHUNK ;Advance to last point JSR LINEPLOT ;Plot the last chunk EXIT LDA #$37 STA $01 CLI RTS * * CHUNK has passed a column, so plot and increment pointer * and fix up CHUNK, OLDCHUNK. * XFIXC STA TEMP JSR LINEPLOT LDA #$FF STA CHUNK STA OLDCHUNK LDA COL BMI :C1 ;Skip if column is negative CMP #39 ;End if move past end of screen BCS EXIT :C1 LDA POINT ADC #8 STA POINT BCC :CONT INC POINT+1 :CONT INC COL BNE :C2 LDA ROW CMP #25 BCS :C2 LDA #00 STA INRANGE :C2 LDA TEMP SEC BCS XCONT1 * * Check to make sure there isn't a high bit, plot chunk, * and update Y-coordinate. * XFIXY DEC Y1 ;Maybe high bit set BPL XCONT2 ADC DX STA TEMP LDA DX+1 ADC #$FF ;Hi byte STA Y1 JSR LINEPLOT ;Plot chunk LDA CHUNK STA OLDCHUNK LDA TEMP XINCDEC INY ;Y-coord CPY #8 ;0..7 is ok BCC XCONT2 STA TEMP JSR FIXY LDA TEMP JMP XCONT2 * * Subroutine to plot chunks/points (to save a little * room, gray hair, etc.) * LINEPLOT ;Plot the line chunk LDA INRANGE BNE :SKIP LDA (POINT),Y ;Otherwise plot EOR BITMASK ORA CHUNK AND OLDCHUNK EOR CHUNK EOR (POINT),Y STA (POINT),Y :SKIP RTS * * Subroutine to fix up pointer when Y decreases through * zero or increases through 7. * FIXY CPY #255 ;Y=255 or Y=8 BEQ :DECPTR :INCPTR ;Add 320 to pointer LDY #0 ;Y increased through 7 LDA ROW BMI :C1 ;If negative, then don't update CMP #24 BCS :TOAST ;If at bottom of screen then quit :C1 LDA POINT ADC #<320 STA POINT LDA POINT+1 ADC #>320 STA POINT+1 :CONT1 INC ROW BNE :RTS LDA COL BMI :RTS LDA #00 STA INRANGE :RTS RTS :DECPTR ;Okay, subtract 320 then LDY #7 ;Y decreased through 0 LDA POINT SEC SBC #<320 STA POINT LDA POINT+1 SBC #>320 STA POINT+1 :CONT2 DEC ROW BMI :TOAST LDA ROW CMP #24 BNE :RTS LDA COL BMI :RTS LDA #00 STA INRANGE RTS :TOAST PLA ;Remove old return address PLA JMP EXIT ;Restore interrupts, etc. * * CIRCLE draws a circle of course, using my * super-sneaky algorithm. * * Center of circle is at x1,y1 * Radius of circle in RADIUS * CIRCLE LDA RADIUS STA Y BNE :c1 JMP PLOT ;Plot as a point :c1 CLC ADC Y1 STA Y1 BCC :c2 INC Y1+1 :c2 LDA #00 STA DONTPLOT JSR PLOT ;Compute XC, YC+R LDA INRANGE ;Track row/col separately STA RANGE1 LDA ROW STA BROW LDA COL STA LCOL STA RCOL STY Y2 ;Y AND 07 LDA BITCHUNK,X STA CHUNK1 ;Forwards chunk STA OLDCH1 LSR EOR #$FF STA CHUNK2 ;Backwards chunk STA OLDCH2 LDA POINT STA TEMP2 ;TEMP2 = forwards high pointer STA X2 ;X2 = backwards high pointer LDA POINT+1 STA TEMP2+1 STA X2+1 * Next compute CY-R LDA Y1 SEC SBC RADIUS BCS :C3 DEC Y1+1 SEC :C3 SBC RADIUS BCS :C4 DEC Y1+1 :C4 STA Y1 JSR PLOTABS ;Compute new coords STY Y1 LDA POINT STA X1 ;X1 will be the backwards LDA POINT+1 ;low-pointer STA X1+1 ;POINT will be forwards LDA ROW STA TROW * LDA INRANGE * STA RANGE2 ;RANGE2=INRANGE INC DONTPLOT SEI ;Get underneath ROM LDA #$34 STA $01 LDA RADIUS LSR ;A=r/2 LDX #00 STX X ;y=0 * Main loop :LOOP INC X ;x=x+1 LSR CHUNK1 ;Right chunk BNE :CONT1 JSR UPCHUNK1 ;Update if we move past a column :CONT1 ASL CHUNK2 BNE :CONT2 JSR UPCHUNK2 :CONT2 ;LDA TEMP SEC SBC X ;a=a-x BCS :LOOP ADC Y ;if a<0 then a=a+y; y=y-1 TAX JSR PCHUNK1 JSR PCHUNK2 LDA CHUNK1 STA OLDCH1 LDA CHUNK2 STA OLDCH2 TXA DEC Y ;(y=y-1) DEC Y2 ;Decrement y-offest for upper BPL :CONT3 ;points JSR DECYOFF :CONT3 LDY Y1 INY STY Y1 CPY #8 BCC :CONT4 JSR INCYOFF :CONT4 LDY X CPY Y ;if y<=x then punt BCC :LOOP ;Now draw the other half * * Draw the other half of the circle by exactly reversing * the above! * NEXTHALF LSR OLDCH1 ;Only plot a bit at a time ASL OLDCH2 LDA RADIUS ;A=-R/2-1 LSR EOR #$FF :LOOP TAX JSR PCHUNK1 ;Plot points JSR PCHUNK2 TXA DEC Y2 ;Y2=bottom BPL :CONT1 JSR DECYOFF :CONT1 INC Y1 LDY Y1 CPY #8 BCC :CONT2 JSR INCYOFF :CONT2 LDX Y BEQ :DONE CLC ADC Y ;a=a+y DEC Y ;y=y-1 BCC :LOOP INC X SBC X ;if a<0 then x=x+1; a=a+x LSR CHUNK1 BNE :CONT3 TAX JSR UPCH1 ;Upchunk, but no plot :CONT3 LSR OLDCH1 ;Only the bits... ASL CHUNK2 ;Fix chunks BNE :CONT4 TAX JSR UPCH2 :CONT4 ASL OLDCH2 BCS :LOOP :DONE CIRCEXIT ;Restore interrupts LDA #$37 STA $01 CLI LDA #1 ;Re-enable plotting STA DONTPLOT RTS * * Decrement lower pointers * DECYOFF TAY LDA #7 STA Y2 LDA X2 ;If we pass through zero, then SEC SBC #<320 ;subtract 320 STA X2 LDA X2+1 SBC #>320 STA X2+1 LDA TEMP2 SEC SBC #<320 STA TEMP2 LDA TEMP2+1 SBC #>320 STA TEMP2+1 TYA DEC BROW BMI EXIT2 RTS EXIT2 PLA ;Grab return address PLA JMP CIRCEXIT ;Restore interrupts, etc. * Increment upper pointers INCYOFF TAY LDA #00 STA Y1 LDA X1 CLC ADC #<320 STA X1 LDA X1+1 ADC #>320 STA X1+1 LDA POINT CLC ADC #<320 STA POINT LDA POINT+1 ADC #>320 STA POINT+1 :ISKIP INC TROW BMI :RTS LDA TROW CMP #25 BCS EXIT2 :RTS TYA RTS * * UPCHUNK1 -- Update right-moving chunk pointers * Due to passing through a column * UPCHUNK1 TAX JSR PCHUNK1 UPCH1 LDA #$FF ;Alternative entry point STA CHUNK1 STA OLDCH1 LDA TEMP2 CLC ADC #8 STA TEMP2 BCC :CONT INC TEMP2+1 CLC :CONT LDA POINT ADC #8 STA POINT BCC :DONE INC POINT+1 :DONE TXA INC RCOL RTS * * UPCHUNK2 -- Update left-moving chunk pointers * UPCHUNK2 TAX JSR PCHUNK2 UPCH2 LDA #$FF STA CHUNK2 STA OLDCH2 LDA X2 SEC SBC #8 STA X2 BCS :CONT DEC X2+1 SEC :CONT LDA X1 SBC #8 STA X1 BCS :DONE DEC X1+1 :DONE TXA DEC LCOL RTS * * Plot right-moving chunk pairs for circle routine * PCHUNK1 LDA RCOL ;Make sure we're in range CMP #40 BCS :SKIP2 LDA CHUNK1 ;Otherwise plot EOR OLDCH1 STA TEMP LDA TROW ;Check for underflow BMI :SKIP LDY Y1 LDA (POINT),Y EOR BITMASK AND TEMP EOR (POINT),Y STA (POINT),Y :SKIP LDA BROW ;If CY+Y >= 200... CMP #25 BCS :SKIP2 LDY Y2 LDA (TEMP2),Y EOR BITMASK AND TEMP EOR (TEMP2),Y STA (TEMP2),Y :SKIP2 RTS * * Plot left-moving chunk pairs for circle routine * PCHUNK2 LDA LCOL ;Range check in X CMP #40 BCS :SKIP2 LDA CHUNK2 ;Otherwise plot EOR OLDCH2 STA TEMP LDA TROW ;Check for underflow BMI :SKIP LDY Y1 LDA (X1),Y EOR BITMASK AND TEMP EOR (X1),Y STA (X1),Y :SKIP LDA BROW ;If CY+Y >= 200... CMP #25 BCS :SKIP2 LDY Y2 LDA (X2),Y EOR BITMASK AND TEMP EOR (X2),Y STA (X2),Y :SKIP2 RTS * * GRON -- turn graphics on. * * .A = 0 -> Turn bitmap on * * Otherwise, initialize colomap to .A * and clear bitmap. * BASE DFB $E0 ;Address of bitmap, hi byte BANK DFB 0 ;Bank 3=default OLDBANK DFB $FF ;VIC old bank OLDD018 DFB 00 GRON TAX LDA $D011 ;Skip if bitmap is already on. AND #$20 BNE CLEAR LDA $DD02 ;Set the data direction regs ORA #3 STA $DD02 LDA $DD00 PHA AND #$03 STA OLDBANK PLA AND #252 ORA BANK STA $DD00 LDA $D018 STA OLDD018 LDA #$38 ;Set color map to base+$1C00 STA $D018 ;bitmap to 2nd 8k LDA $D011 ;And turn on bitmap ORA #$20 STA $D011 CLEAR TXA BEQ GRONDONE CLEARCOL LDY #$00 LDA BASE ;Colormap is at base-$14 CMP #$A0 BNE ClearE000 :A000 TXA :l1 STA $8C00,Y STA $8D00,Y STA $8E00,Y STA $8F00,Y INY BNE :l1 TYA :l2 STA $A000,Y STA $A100,Y STA $A200,Y STA $A300,Y STA $A400,Y STA $A500,Y STA $A600,Y STA $A700,Y STA $A800,Y STA $A900,Y STA $AA00,Y STA $AB00,Y STA $AC00,Y STA $AD00,Y STA $AE00,Y STA $AF00,Y STA $B000,Y STA $B100,Y STA $B200,Y STA $B300,Y STA $B400,Y STA $B500,Y STA $B600,Y STA $B700,Y STA $B800,Y STA $B900,Y STA $BA00,Y STA $BB00,Y STA $BC00,Y STA $BD00,Y STA $BE00,Y STA $BF00,Y INY BNE :l2 GRONDONE RTS ClearE000 SEI LDA $01 PHA AND #$FC STA $01 :l0 LDA $FFFA,Y STA :temp,Y INY CPY #6 BNE :l0 PLA STA $01 CLI LDY #$00 TXA :l1 STA $CC00,Y STA $CD00,Y STA $CE00,Y STA $CF00,Y INY BNE :l1 TYA :l2 STA $E000,Y STA $E100,Y STA $E200,Y STA $E300,Y STA $E400,Y STA $E500,Y STA $E600,Y STA $E700,Y STA $E800,Y STA $E900,Y STA $EA00,Y STA $EB00,Y STA $EC00,Y STA $ED00,Y STA $EE00,Y STA $EF00,Y STA $F000,Y STA $F100,Y STA $F200,Y STA $F300,Y STA $F400,Y STA $F500,Y STA $F600,Y STA $F700,Y STA $F800,Y STA $F900,Y STA $FA00,Y STA $FB00,Y STA $FC00,Y STA $FD00,Y STA $FE00,Y STA $FF00,Y INY BNE :l2 :l3 LDA :temp,Y STA $FFFA,Y INY CPY #6 BNE :l3 RTS :temp ds 6 * GROFF -- Restore old values if graphics are on. GROFF LDA $D011 AND #$20 BEQ GDONE GSET LDA $DD02 ;Set the data direction regs ORA #3 STA $DD02 LDA $DD00 AND #$7C ORA OLDBANK STA $DD00 LDA OLDD018 STA $D018 LDA $D011 AND #$FF-$20 STA $D011 GDONE RTS * * SETCOLOR -- Set drawing color * .A = 0 -> background color * .A = 1 -> foreground color * SETCOLOR COLENT CMP #00 ;MODE enters here BEQ :C2 :C1 CMP #01 BNE :RTS LDA #$FF :C2 STA BITMASK :RTS RTS * * MODE -- catch-all command. * * .X contains mode: * * $10 SuperCPU mode -- screen -> A000, etc. * $11 Normal mode * $12 Double buffer mode * * Anything else -> BITMASK * MODENUM DFB 17 ;Current mode MODE CPX #16 BNE :C18 STX MODENUM :SET16 LDA #$A0 ;Bitmap -> $A000 STA BASE LDA #01 STA BANK ;Bank 2 STA OLDBANK LDA #$FF ;End of BASIC memory STA $37 STA $33 LDA #$87 STA $38 STA $34 LDA #$24 ;Screen mem -> $8800 STA OLDD018 JSR GSET ;Part of GROFF LDA #$88 STA 648 ;Tell BASIC where the screen is STA $D07E ;Enable SuperCPU regs STA $D074 ;Bank 2 optimization STA $D07F ;Disable regs RTS :C18 CPX #18 ;Double-buffer mode! BNE :C17 STX MODENUM JSR :SET16 ;Set up mode 16 STA $D07E STA $D077 ;Turn off optimization STA $D07F RTS :C17 CPX #17 BNE MODEDONE MODE17 STX MODENUM LDA #$E0 STA BASE LDA #00 ;Bank 3 STA BANK LDA #3 ;Bank 0 == normal bank STA OLDBANK LDA #$FF STA $37 STA $33 LDA #$9F STA $38 STA $34 LDA #$14 ;Screen mem -> $0400 STA OLDD018 JSR GSET ;Part of GROFF LDA #$04 STA 648 ;Tell BASIC where the screen is STA $D07E STA $D077 ;No optimization STA $D07F RTS MODEDONE STX BITMASK RTS * * BUFFER -- Sets the current drawing buffer to 1 or 2, * * .X = 0 Swap draw buffer * .X = 1 Buffer 1 ($E000) * .X = 2 Buffer 2 ($A000) * BUFFER LDA MODENUM CMP #18 BNE :PUNT LDY #$A0 TXA BNE :CONT CPY BASE BNE :CONT LDA #1 :CONT LSR BCC :LOW ;even = low buffer LDY #$E0 ;odd = high buffer :LOW STY BASE :PUNT RTS * * SWAP -- Swap *displayed* buffers. MODE 18 must * be enabled first. * SWAPBUF LDA MODENUM CMP #18 BNE :PUNT LDA $DD00 ;Ooooooohhh, real tough! EOR #$01 STA $DD00 :PUNT RTS