iigs-game-engine/src/Graphics.s
2022-07-27 23:42:26 -05:00

332 lines
11 KiB
ArmAsm

; Graphic screen initialization
InitGraphics
jsr _ShadowOn
jsr _GrafOn
lda #0
jsr _SetSCBs
ldx #DefaultPalette
lda #0
jsr _SetPalette
ldx #SHR_LINE_WIDTH
ldy #SHR_SCREEN_HEIGHT
jsr _SetScreenMode
jsr _InitBG0 ; Initialize the background layer
lda EngineMode
bit #ENGINE_MODE_TWO_LAYER
beq :no_bg1
jsr _InitBG1
lda #0
jsr _ClearBG1Buffer
:no_bg1
rts
; Allow the user to dynamically select one of the pre-configured screen sizes, or pass
; in a specific width and height. The screen is automatically centered. If this is
; not desired, then SetScreenRect should be used directly
;
; 0. Full Screen : 40 x 25 320 x 200 (32,000 bytes (100.0%))
; 1. Sword of Sodan : 34 x 24 272 x 192 (26,112 bytes ( 81.6%))
; 2. ~NES : 32 x 25 256 x 200 (25,600 bytes ( 80.0%))
; 3. Task Force : 32 x 22 256 x 176 (22,528 bytes ( 70.4%))
; 4. Defender of the World : 35 x 20 280 x 160 (22,400 bytes ( 70.0%))
; 5. Rastan : 32 x 20 256 x 160 (20,480 bytes ( 64.0%))
; 6. Game Boy Advanced : 30 x 20 240 x 160 (19,200 bytes ( 60.0%))
; 7. Ancient Land of Y's : 36 x 16 288 x 128 (18,432 bytes ( 57.6%))
; 8. Game Boy Color : 20 x 18 160 x 144 (11,520 bytes ( 36.0%))
; 9. Agony (Amiga) : 36 x 24 288 x 192 (27,648 bytes ( 86.4%))
; 10. Atari Lynx : 20 x 13 160 x 102 (8,160 bytes ( 25.5%))
;
; X = mode number OR width in bytes
; Y = height in pixels (if X > 8)
_SetScreenMode
cpx #11
bcs :direct ; if x > 10, then assume X and Y are the dimensions
txa
asl
tax
ldy ScreenModeHeight,x
lda ScreenModeWidth,x
tax
:direct cpy #SHR_SCREEN_HEIGHT+1
bcs :exit
cpx #SHR_LINE_WIDTH+1
bcs :exit
phx ; Save X (width) and Y (height)
phy
lda #SHR_LINE_WIDTH ; Center the screen
sec
sbc 3,s
lsr
xba
pha ; Save half the origin coordinate
lda #SHR_SCREEN_HEIGHT
sec
sbc 3,s ; This is now Y because of the PHA above
lsr
ora 1,s
plx ; Throw-away to pop the stack
ply
plx
jsr SetScreenRect
jmp FillScreen ; tail return
:exit
rts
; Return the current border color ($0 - $F) in the accumulator
_GetBorderColor lda #0000
sep #$20
ldal BORDER_REG
and #$0F
rep #$20
rts
; Set the border color to the accumulator value.
_SetBorderColor sep #$20 ; ACC = $X_Y, REG = $W_Z
eorl BORDER_REG ; ACC = $(X^Y)_(Y^Z)
and #$0F ; ACC = $0_(Y^Z)
eorl BORDER_REG ; ACC = $W_(Y^Z^Z) = $W_Y
stal BORDER_REG
rep #$20
rts
; Clear to SHR screen to a specific color
_ClearToColor
ldx #$7D00 ;start at top of pixel data! ($2000-9D00)
:clearloop dex
dex
stal SHR_SCREEN,x ;screen location
bne :clearloop ;loop until we've worked our way down to 0
rts
; Set a palette values
; A = palette number, X = palette address
_SetPalette
and #$000F ; palette values are 0 - 15 and each palette is 32 bytes
asl
asl
asl
asl
asl
txy
tax
]idx equ 0
lup 16
lda: $0000+]idx,y
stal SHR_PALETTES+]idx,x
]idx equ ]idx+2
--^
rts
; Initialize the SCB
_SetSCBs
ldx #$0100 ;set all $100 scbs to A
:scbloop dex
dex
stal SHR_SCB,x
bne :scbloop
rts
; Turn SHR screen On/Off
_GrafOn
sep #$20
lda #$C1 ; SHR On, Linear Memory Map On, Ignore Bank Latch
stal NEW_VIDEO_REG
rep #$20
rts
_GrafOff
sep #$20
lda #$01 ; SHR Off, Linear Memory Map Off
stal NEW_VIDEO_REG
rep #$20
rts
; Enable/Disable Shadowing.
_ShadowOn
sep #$20
ldal SHADOW_REG
and #$F7
stal SHADOW_REG
rep #$20
rts
_ShadowOff
sep #$20
ldal SHADOW_REG
ora #$08
stal SHADOW_REG
rep #$20
rts
_GetVBL
sep #$20
ldal VBL_HORZ_REG
asl
ldal VBL_VERT_REG
rol ; put V5 into carry bit, if needed. See TN #39 for details.
rep #$20
and #$00FF
rts
_WaitForVBL
sep #$20
:wait1 ldal VBL_STATE_REG ; If we are already in VBL, then wait
bmi :wait1
:wait2 ldal VBL_STATE_REG
bpl :wait2 ; spin until transition into VBL
rep #$20
rts
; Set the physical location of the virtual screen on the physical screen. The
; screen size must by a multiple of 8
;
; A = XXYY where XX is the left edge [0, 159] and YY is the top edge [0, 199]
; X = width (in bytes)
; Y = height (in lines)
;
; This subroutine stores the screen positions in the direct page space and fills
; in the double-length ScreenAddrR table that holds the address of the right edge
; of the playfield. This table is used to set addresses in the code banks when the
; virtual origin is changed.
;
; We are not concerned about the raw performance of this function because it should
; usually only be executed once during app initialization. It doesn't get called
; with any significant frequency.
SetScreenRect sty ScreenHeight ; Save the screen height and width
stx ScreenWidth
tax ; Temp save of the accumulator
and #$00FF
sta ScreenY0
clc
adc ScreenHeight
sta ScreenY1
txa ; Restore the accumulator
xba
and #$00FF
sta ScreenX0
clc
adc ScreenWidth
sta ScreenX1
lda ScreenHeight ; Divide the height in scanlines by 8 to get the number tiles
lsr
lsr
lsr
sta ScreenTileHeight
lda ScreenWidth ; Divide width in bytes by 4 to get the number of tiles
lsr
lsr
sta ScreenTileWidth
lda ScreenY0 ; Calculate the address of the first byte
asl ; of the right side of the playfield
tax
lda ScreenAddr,x ; This is the address for the edge of the physical screen
clc
adc ScreenX1
dec
pha ; Save for second loop
ldx #0
ldy ScreenHeight
:loop clc
sta RTable,x
adc #160
inx
inx
dey
bne :loop
ldy ScreenHeight
pla ; Reset the address and continue filling in the
:loop2 clc
sta RTable,x
adc #160
inx
inx
dey
bne :loop2
; Calculate the screen locations for each tile corner
lda ScreenY0 ; Calculate the address of the first byte
asl ; of the right side of the playfield
tax
lda ScreenAddr,x ; This is the address for the left edge of the physical screen
clc
adc ScreenX0
ldx #0
ldy #0
:tsloop
sta TileStore+TS_SCREEN_ADDR,x
clc
adc #4 ; Go to the next tile
iny
cpy #41 ; If we've done 41 columns, move to the next line
bcc :nohop
ldy #0
clc
adc #{8*160}-{4*41}
:nohop
inx
inx
cpx #TILE_STORE_SIZE-2
bcc :tsloop
rts
; Clear the SHR screen and then infill the defined field
FillScreen lda #0
jsr _ClearToColor
ldy ScreenY0
:yloop
tya
asl a
tax
lda ScreenAddr,x
clc
adc ScreenX0
tax
phy
lda ScreenWidth
lsr
tay
lda #$FFFF
:xloop stal $E10000,x ; X is the absolute address
inx
inx
dey
bne :xloop
ply
iny
cpy ScreenY1
bcc :yloop
rts