;------------------------------------------------------------------------------- ;@doc:file ; ; === Graphics.asm === ; ; Provides methods for drawing graphics. ; ;@doc:end ;------------------------------------------------------------------------------- .module Graphics PlotMode.Plot = 0 PlotMode.Or = 1 PlotMode.And = 2 PlotMode.Eor = 3 PlotMode.Invert = 4 g_wndXMin = Graphics.Bounds.MinX ; The g_wnd* variables must appear g_wndXMax = Graphics.Bounds.MaxX ; in this order. g_wndYMin = Graphics.Bounds.MinY g_wndYMax = Graphics.Bounds.MaxY .include "Line.asm" .include "Clip.asm" ; qarnos .include "Fillers.asm" ; qarnos .include "Ellipse.asm" ; qarnos .include "Rectangle.asm" .include "SMC.asm" .include "Sprite.asm" .include "FloodFill.asm" .include "Triangle.asm" .include "Plot.asm" ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.VisitPoint === ; ; Adds a point to the VisitedPoints list. ; ; INPUTS: ; REGISTERS ; * HL - X coordinate of the point to visit. ; * DE - Y coordinate of the point to visit. ; ;@doc:end ;------------------------------------------------------------------------------- VisitPoint push hl push de push bc ld hl,VisitedPoints+VisitedPoints.Size-SizeOf(Point16)-1 ld de,VisitedPoints+VisitedPoints.Size-1 ld bc,2*SizeOf(Point16) lddr pop bc pop de pop hl ld (VisitedPoints+Point16.Y),de push hl ld de,(Origin.X) add hl,de ld (VisitedPoints+Point16.X),hl push de ld hl,(VisitedPoints+Point16.Y) ld de,(Origin.Y) add hl,de ld (VisitedPoints+Point16.Y),hl pop de pop hl ret VisitPointAbsolute push hl push de push bc ld hl,VisitedPoints+VisitedPoints.Size-SizeOf(Point16)-1 ld de,VisitedPoints+VisitedPoints.Size-1 ld bc,2*SizeOf(Point16) lddr pop bc pop de pop hl ld (VisitedPoints+Point16.Y),de ld (VisitedPoints+Point16.X),hl ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.Initialise === ; ; Initialises the graphics routines. ; ;@doc:end ;------------------------------------------------------------------------------- Initialise push af ld hl,Line.DrawCode ld de,Line.Draw ld bc,Line.DrawCodeEnd - Line.DrawCode ldir xor a ld (PlotMode),a ld (PlotShape),a ld (BackgroundColour),a ld a,127 ld (ForegroundColour),a ld (CurrentColour),a ld hl,0 ld (CustomFill),hl ld a,$C3 ; JP ld (HLineFill.JumpInstruction),a ld (FlipY16.JumpInstruction),a ld (FlipY8.JumpInstruction),a ld hl,FlipY16.Routine ld (FlipY16.Target),hl ld hl,FlipY8.Routine ld (FlipY8.Target),hl ld a,16 call Graphics.SetScaleFactor.X call Graphics.SetScaleFactor.Y .fcall "Graphics.PlotUpdateColours" pop af ; Deliberate run-on. ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.Reset === ; ; Reset the visited points, origin and viewport. ; ;@doc:end ;------------------------------------------------------------------------------- Reset push hl push de push bc ld hl,0 ld (Origin.X),hl ld (Origin.Y),hl ld b,3 - call VisitPoint djnz - ld hl,95<<8 ld (Bounds+0),hl ld h,63 ld (Bounds+2),hl pop bc pop de pop hl ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.GetPixelInfo === ; ; Gets information about a pixel on the buffer. ; ; INPUTS: ; REGISTERS ; * A - X coordinate of the pixel. ; * L - Y coordinate of the pixel. ; ; OUTPUTS: ; REGISTERS ; * HL - Pointer to pixel on LCD buffer. ; * A - bitmask of pixel on LCD buffer. ; ;@doc:end ;------------------------------------------------------------------------------- GetPixelInfo push de ; hl = (l*12)+Lcd.Buffer ld h,0 add hl,hl add hl,hl ld d,h ld e,l add hl,hl add hl,de ld de,(Lcd.Buffer) add hl,de ; de = a\8 ld d,0 ld e,a srl e srl e srl e ; hl = (l*12)+Lcd.Buffer+(a\8) add hl,de and 7 push hl ld l,a ld h,PointOffsets>>8 ld a,(hl) pop hl pop de ret PutPixel call GetPixelInfo or (hl) ld (hl),a ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.GetCurrentCoordinate === ; ; Gets the current graphics cursor position as an 8-bit value. ; ; OUTPUTS: ; REGISTERS ; * IXH - X coordinate of the current cursor position. ; * IXL, B - Y coordinate of the current cursor position. ; * F - Carry set if the point is out-of-bounds. ; ; DESTROYED: ; REGISTERS ; * AF, B. ; ;@doc:end ;------------------------------------------------------------------------------- GetCurrentCoordinate push hl ld hl,(VisitedPoints+0) call ScaleX.HL ld (OP1+0),hl ld hl,(VisitedPoints+2) call ScaleY.HL ld (OP1+2),hl pop hl ld a,(OP1+1) or a scf ret nz ld a,(OP1+3) or a scf ret nz ld a,(Bounds+0) ; MinX ld b,a ld a,(OP1+0) ; X cp b ret c ld b,a ld a,(Bounds+1) ; MaxX cp b ret c ld ixh,b ld a,(Bounds+2) ; MinY ld b,a ld a,(OP1+2) ; Y call FlipY8 cp b ret c ld b,a ld a,(Bounds+3) ; MaxY cp b ret c ld ixl,b or a ret Clip16 bit 7,h jr nz,Clip16Neg ld a,h or a jr nz,Clip16MuchTooBig ld a,l cp d ; Upper bound. jr c,+ ;a > d ld a,d ret + cp e ; Lower bound. ret nc ld a,e ret Clip16MuchTooBig ld a,d ret Clip16Neg ld a,e ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.Clear === ; ; Clears the graphics device to the current background colour. ; ; DESTROYED: ; REGISTERS ; * AF, AF', BC, DE, HL, IX. ; ;@doc:end ;------------------------------------------------------------------------------- Clear xor a ld (PlotShape),a call PlotUpdateColours call SetPatternBuffer jp Rectangle.Clear ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.PlotUpdateColours === ; ; Updates the drawing operation colours based on CurrentColour, PlotMode and ; PlotShape. ; ;@doc:end ;------------------------------------------------------------------------------- PlotUpdateColours call PlotIsInverted jr z,PlotModeInvert ld a,(PlotMode) cp 5 jr c,+ ld a,4 + or a \ jp z,PlotModePlot dec a \ jp z,PlotModeOr dec a \ jp z,PlotModeAnd dec a \ jp z,PlotModeEor PlotModeInvert ld hl,SMC.InvertHLTimesB \ ld (Rectangle.ColumnPlotRoutine),hl ld hl,SMC.InvertHLMaskCD \ ld (Rectangle.CapPlotRoutine),hl ld hl,HLineFill.Invert \ ld (HLineFill.Target),hl jp Line.SetColourXor PlotModeEor ld hl,SMC.EorHLCTimesB \ ld (Rectangle.ColumnPlotRoutine),hl ld hl,SMC.EorHLIXMaskCD \ ld (Rectangle.CapPlotRoutine),hl ld hl,HLineFill.Eor \ ld (HLineFill.Target),hl ld a,(CurrentColour) bit 6,a jp z,Line.SetColourInvisible jp Line.SetColourXor PlotModeAnd ld hl,SMC.AndHLCTimesB \ ld (Rectangle.ColumnPlotRoutine),hl ld hl,SMC.AndHLIXMaskCD \ ld (Rectangle.CapPlotRoutine),hl ld hl,HLineFill.And \ ld (HLineFill.Target),hl ld a,(CurrentColour) bit 6,a jp z,Line.SetColourWhite jp Line.SetColourInvisible PlotModeOr ld hl,SMC.OrHLCTimesB \ ld (Rectangle.ColumnPlotRoutine),hl ld hl,SMC.OrHLIXMaskCD \ ld (Rectangle.CapPlotRoutine),hl ld hl,HLineFill.Or \ ld (HLineFill.Target),hl ld a,(CurrentColour) bit 6,a jp z,Line.SetColourInvisible jp Line.SetColourBlack PlotModePlot ld hl,SMC.PlotHLCTimesB \ ld (Rectangle.ColumnPlotRoutine),hl ld hl,SMC.PlotHLIXMaskCD \ ld (Rectangle.CapPlotRoutine),hl ld hl,HLineFill.Plot \ ld (HLineFill.Target),hl ld a,(CurrentColour) bit 6,a jp z,Line.SetColourWhite jp Line.SetColourBlack ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.SetPatternBuffer === ; ; Fills the pattern buffer with the current colour's dither pattern. ; ;@doc:end ;------------------------------------------------------------------------------- SetPatternBuffer ; Colour. ld hl,(CustomFill) ld a,h or l jr nz,+ ld a,(CurrentColour) and %01111000 ld l,a ld h,0 ld de,Resources.DitherPatterns add hl,de + ld de,PatternBuffer ld bc,8 ldir ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.ConditionalCopy === ; ; Copies the LCD buffer to the LCD hardware if Vdu.Text.WriteToLcdEnabled is ; currently set. ; ;@doc:end ;------------------------------------------------------------------------------- ConditionalCopy ld a,(Vdu.WriteToLcdEnabled) or a ret z ; Deliberate run-on. ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.Copy === ; ; Lcd.Copy but doesn't preserve registers and is on the same page as all ; the other Graphics stuff. ; ;@doc:end ;------------------------------------------------------------------------------- Copy .fcall "Speed.SlowDown" di - in a,(Lcd.InstPort) or a jp m,- ld a,$80 out (Lcd.InstPort),a ld hl,(Lcd.Buffer) ld de,-12-(-(12*64)+1) add hl,de ld a,$20 ld c,a inc hl dec hl --- ld b,64 inc c ld de,-(12*64)+1 push af - in a,(Lcd.InstPort) or a jp m,- pop af out (Lcd.InstPort),a add hl,de ld de,10 -- add hl,de inc hl inc hl inc de - in a,(Lcd.InstPort) or a jp m,- ld a,(hl) out (Lcd.DataPort),a dec de djnz -- ld a,c cp $2B+1 jr nz,--- .fcall "Speed.SpeedUp" ei ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.FlipY16 === ; ; If the Y axis is inverted, this flips the Y coordinate. ; ; INPUTS: ; REGISTERS ; * HL - Y coordinate to flip. ; ; OUTPUTS: ; REGISTERS ; * HL - Flipped Y coordinate. ; ; DESTROYED: ; REGISTERS ; * F. ; ;@doc:end ;------------------------------------------------------------------------------- FlipY16.Routine push de or a ld de,63 ex de,hl sbc hl,de pop de ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.FlipY8 === ; ; If the Y axis is inverted, this flips the Y coordinate. ; ; INPUTS: ; REGISTERS ; * A - Y coordinate to flip. ; ; OUTPUTS: ; REGISTERS ; * A - Flipped Y coordinate. ; ; DESTROYED: ; REGISTERS ; * F. ; ;@doc:end ;------------------------------------------------------------------------------- FlipY8.Routine neg add a,63 ret Scale16HL.Routine sra h \ rr l sra h \ rr l sra h \ rr l sra h \ rr l sra h \ rr l sra h \ rr l sra h \ rr l ret Scale16DE.Routine sra d \ rr e sra d \ rr e sra d \ rr e sra d \ rr e sra d \ rr e sra d \ rr e sra d \ rr e ret Scale16BC.Routine sra b \ rr c sra b \ rr c sra b \ rr c sra b \ rr c sra b \ rr c sra b \ rr c sra b \ rr c ret Scale8A.Routine sra a sra a sra a sra a sra a sra a sra a ret Scale16HLDiv5.Routine bit 7,h push af jr z,+ ld a,h \ cpl \ ld h,a ld a,l \ cpl \ ld l,a inc hl + xor a .rept 16 add hl,hl rla cp 5 jr c,$+5 sub 5 inc l .loop pop af ret z push af ld a,h \ cpl \ ld h,a ld a,l \ cpl \ ld l,a inc hl pop af ret Scale16DEDiv5.Routine ex de,hl call Scale16HLDiv5.Routine ex de,hl ret Scale16BCDiv5.Routine push hl ld h,b ld l,c call Scale16HLDiv5.Routine ld b,h ld c,l pop hl ret Scale8ADiv5.Routine push hl ld h,0 ld l,a call Scale16HLDiv5.Routine ld a,l pop hl ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.SetScaleFactor.X === ; ; Sets the graphics X scale factor. ; ; INPUTS: ; REGISTERS ; * A - Scale factor (one of 1, 2, 4, 8, 16, 32, 64 or 128). ; ;@doc:end ;------------------------------------------------------------------------------- SetScaleFactor.X ld (Graphics.Scale.X),a SetScaleFactor.Routine push af push de push bc push hl ; Special case: 5 cp 5 jr z,SetScaleFactor.5 ; If it's a scale factor of 1, don't bother scaling at all. srl a jr c,SetScaleFactor.1 ; Calculate an offset into each "scale" routine. ld hl,Scale16HL.Routine+4*6 ld de,Scale16DE.Routine-Scale16HL.Routine ld bc,-4 srl a jr c,SetScaleFactor.Scaled ; *2 add hl,bc srl a jr c,SetScaleFactor.Scaled ; *4 add hl,bc srl a jr c,SetScaleFactor.Scaled ; *8 add hl,bc srl a jr c,SetScaleFactor.Scaled ; *16 add hl,bc srl a jr c,SetScaleFactor.Scaled ; *32 add hl,bc srl a jr c,SetScaleFactor.Scaled ; *64 add hl,bc SetScaleFactor.Scaled ; hl = absolute offset into Scale16HL routine. push hl ld (ScaleX.HL.Target),hl add hl,de ld (ScaleX.DE.Target),hl add hl,de ld (ScaleX.BC.Target),hl pop hl ; Find relative offset into Scale16HL routine. ld de,-Scale16HL.Routine add hl,de ; Divide by 2 (sra b \ rr c = 4 bytes; sra a = 2 bytes). srl h \ rr l ; Offset by start of Scale8A. ld de,Scale8A.Routine add hl,de ld (ScaleX.A.Target),hl ld a,$C3 ; JP nnnn jr SetScaleFactor.SetJump SetScaleFactor.1 ld a,$C9 ; RET SetScaleFactor.SetJump ld (ScaleX.HL.JumpInstruction),a ld (ScaleX.DE.JumpInstruction),a ld (ScaleX.BC.JumpInstruction),a ld (ScaleX.A.JumpInstruction),a pop hl pop bc pop de pop af ret SetScaleFactor.5 ld hl,Scale16HLDiv5.Routine ld (ScaleX.HL.Target),hl ld hl,Scale16DEDiv5.Routine ld (ScaleX.DE.Target),hl ld hl,Scale16BCDiv5.Routine ld (ScaleX.BC.Target),hl ld hl,Scale8ADiv5.Routine ld (ScaleX.A.Target),hl ld a,$C3 jr SetScaleFactor.SetJump ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.SetScaleFactor.Y === ; ; Sets the graphics Y scale factor. ; ; INPUTS: ; REGISTERS ; * A - Scale factor (one of 1, 2, 4, 8, 16, 32, 64 or 128). ; ;@doc:end ;------------------------------------------------------------------------------- SetScaleFactor.Y ld (Graphics.Scale.Y),a push hl push de push bc ld hl,ScaleX.HL ld de,OP1 ld bc,3*4 ; 3 bytes per vector; 4 vectors. ldir ld hl,ScaleY.HL ld de,ScaleX.HL ld bc,3*4 ldir call SetScaleFactor.Routine ld hl,ScaleX.HL ld de,ScaleY.HL ld bc,3*4 ldir ld hl,OP1 ld de,ScaleX.HL ld bc,3*4 ldir pop bc pop de pop hl ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === Graphics.IsValidScaleFactor === ; ; Checks if a value in A is a valid scale factor. ; ; INPUTS: ; REGISTERS ; * A - Scale factor. ; ; OUTPUTS: ; REGISTERS ; * F - Z set if A was a valid scale factor; reset otherwise. ; ; DESTROYED ; * F. ; ;@doc:end ;------------------------------------------------------------------------------- IsValidScaleFactor cp 5 ret z push bc ld c,a xor a ld b,8 - rlc c adc a,0 djnz - cp 1 ld a,c pop bc ret ClampX push de push hl ld a,(Bounds+0) ; Min X ld e,a ld d,0 or a sbc hl,de pop hl jp p,+ ex de,hl + push hl ld a,(Bounds+1) ; Max X ld e,a ld d,0 or a sbc hl,de pop hl jp m,+ ex de,hl + pop de ret ClampY push de push hl ld a,(Bounds+2) ; Min Y ld e,a ld d,0 or a sbc hl,de pop hl jp p,+ ex de,hl + push hl ld a,(Bounds+3) ; Max Y ld e,a ld d,0 or a sbc hl,de pop hl jp m,+ ex de,hl + pop de ret .endmodule