mirror of
https://github.com/lscharen/iigs-game-engine.git
synced 2024-12-27 11:30:10 +00:00
Generalize to allow differrnt play field sizes
This commit is contained in:
parent
f6371f914e
commit
9cc5b2e3af
159
demos/smb/Main.s
159
demos/smb/Main.s
@ -18,7 +18,7 @@ DOWN_ARROW equ $0A
|
||||
; Nametable queue
|
||||
NT_QUEUE_LEN equ $1000
|
||||
NT_QUEUE_SIZE equ {2*NT_QUEUE_LEN}
|
||||
NT_QUEUE_MOD equ {NT_QUEUE_SIZE-1}
|
||||
NT_QUEUE_MOD equ {NT_QUEUE_SIZE-1}
|
||||
|
||||
mx %00
|
||||
|
||||
@ -38,8 +38,26 @@ CurrNTQueueEnd equ 40
|
||||
BGToggle equ 44
|
||||
LastEnable equ 46
|
||||
ShowFPS equ 48
|
||||
HideStatusBar equ 50
|
||||
;ROMPlayerY equ 52
|
||||
YOrigin equ 54
|
||||
|
||||
OldOneSec equ 100
|
||||
OldOneSec equ 100
|
||||
RenderFlags equ 102
|
||||
NesTop equ 104 ; Clip any sprite that has a NES OAM y-coordinate above this line
|
||||
NesBottom equ 106 ; Clip any sprite that has a NES OAM y-coordinate below this line
|
||||
ScreenBase equ 108 ; SHR address of the top-left edge of the play field
|
||||
ScreenRows equ 110 ; Number of 8-line block we are showing on-screen
|
||||
ScreenHeight equ 112
|
||||
ScreenTop equ 114 ; SCB line of the top of the play field
|
||||
|
||||
MinYScroll equ 116 ; Smallest YOrigin value: 0 when showing stratus bar, 16 when hiding status bar
|
||||
MaxYScroll equ 118 ; Largest YOrigin value for GTESetBG0Origin
|
||||
|
||||
;VirtTop equ 104 ; The top virtual line that is the top of the active play field (excludes status bar)
|
||||
;VirtBottom equ 106 ; The bottom virtual line that is bottom of the active play field
|
||||
;MaxNesY equ 108 ; This is the largest NES y coordinate that will display in the active play field
|
||||
;MinNesY equ 110 ; This is the smallest NES y coordinate that will display in the active play field
|
||||
|
||||
Tmp0 equ 240
|
||||
Tmp1 equ 242
|
||||
@ -66,6 +84,8 @@ FTblTmp equ 228
|
||||
stz OldROMScrollEdge
|
||||
stz LastAreaType
|
||||
stz ShowFPS
|
||||
stz HideStatusBar
|
||||
stz YOrigin
|
||||
|
||||
lda #1
|
||||
sta BGToggle
|
||||
@ -73,6 +93,39 @@ FTblTmp equ 228
|
||||
lda #$0008
|
||||
sta LastEnable
|
||||
|
||||
lda #16
|
||||
; lda #32
|
||||
sta NesTop
|
||||
|
||||
lda #16
|
||||
sta MinYScroll
|
||||
|
||||
lda #1
|
||||
sta HideStatusBar
|
||||
|
||||
lda #128
|
||||
sta ScreenHeight
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
sta ScreenRows
|
||||
|
||||
lda #200
|
||||
sec
|
||||
sbc ScreenHeight
|
||||
sta MaxYScroll
|
||||
|
||||
lda NesTop
|
||||
clc
|
||||
adc ScreenHeight
|
||||
sec
|
||||
sbc #8
|
||||
inc
|
||||
sta NesBottom
|
||||
|
||||
; lda #0
|
||||
; sta ScreenTop
|
||||
|
||||
; The next two direct pages will be used by GTE, so get another 2 pages beyond that for the ROM. We get
|
||||
; 4K of DP/Stack space by default, so there is plenty to share
|
||||
|
||||
@ -143,12 +196,36 @@ FTblTmp equ 228
|
||||
; pea #184
|
||||
|
||||
pea #128
|
||||
pea #200
|
||||
pei ScreenHeight
|
||||
|
||||
; pea #80
|
||||
; pea #144
|
||||
_GTESetScreenMode
|
||||
|
||||
pha ; Allocate space for x, y, width, height
|
||||
pha
|
||||
pha
|
||||
pha
|
||||
_GTEGetScreenInfo
|
||||
pla ; Discard x
|
||||
pla
|
||||
sta ScreenTop
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
sta ScreenBase
|
||||
asl
|
||||
asl
|
||||
clc
|
||||
adc ScreenBase
|
||||
clc
|
||||
adc #$2000+x_offset
|
||||
sta ScreenBase
|
||||
pla ; Discard width and height
|
||||
pla
|
||||
|
||||
ldx #Area1Palette
|
||||
lda #TmpPalette
|
||||
jsr NESColorToIIgs
|
||||
@ -391,6 +468,13 @@ EvtLoop
|
||||
brl EvtLoop
|
||||
:not_t
|
||||
|
||||
cmp #'x' ; break
|
||||
bne :not_x
|
||||
lda #1
|
||||
sta user_break
|
||||
brl EvtLoop
|
||||
:not_x
|
||||
|
||||
cmp #'q'
|
||||
beq Exit
|
||||
brl EvtLoop
|
||||
@ -416,6 +500,7 @@ DPSave dw 0
|
||||
LastAreaType dw 0
|
||||
frameCount dw 0
|
||||
show_vbl_cpu dw 0
|
||||
user_break dw 0
|
||||
|
||||
; Toggle an APU control bit
|
||||
ToggleAPUChannel
|
||||
@ -497,16 +582,42 @@ RenderFrame
|
||||
pha
|
||||
|
||||
; Get the player's Y coordinate and determine of we need to adjust the camera based on the physical play field size
|
||||
; sep #$20
|
||||
; ldal ROMBase+$b5 ; Player_Y_HighPos
|
||||
; xba
|
||||
; ldal ROMBase+$ce ; Player_Y_Position
|
||||
; rep #$20
|
||||
;
|
||||
; sec
|
||||
; sbc TopClip ; If we're hiding the
|
||||
ldx ROMZeroPg
|
||||
ldal $0000b5,x ; Player_Y_Page ; 0 = above screen, 1 = on screen, 2 = below
|
||||
and #$00FF
|
||||
beq :max_clamp
|
||||
cmp #2
|
||||
beq :min_clamp
|
||||
|
||||
pea $0000
|
||||
ldal $0000ce,x ; Player_Y_Position
|
||||
and #$00FF
|
||||
; sta ROMPlayerY
|
||||
|
||||
; The "full screen" size is 200 lines that cover NES rows 16 through 216. If the
|
||||
; size of the playfield is less, then we adjust the origin a bit.
|
||||
;
|
||||
; Y_Origin = min(200 - ScreenHeight, max(0, ROMPlayerY - 16 - ScreenHeight/2))
|
||||
|
||||
sec
|
||||
sbc NesTop
|
||||
asl
|
||||
sec
|
||||
sbc ScreenHeight
|
||||
bmi :max_neg
|
||||
lsr
|
||||
cmp MinYScroll
|
||||
bcc :max_clamp
|
||||
|
||||
cmp MaxYScroll
|
||||
bcc :set_y
|
||||
:min_clamp lda MaxYScroll
|
||||
bra :set_y
|
||||
:max_neg
|
||||
:max_clamp
|
||||
lda MinYScroll
|
||||
:set_y
|
||||
sta YOrigin
|
||||
pha
|
||||
_GTESetBG0Origin
|
||||
|
||||
lda ppumask
|
||||
@ -518,11 +629,21 @@ RenderFrame
|
||||
_GTEEnableBackground
|
||||
:bghop
|
||||
|
||||
pea $FFFF ; NES mode
|
||||
; pea $FFFF ; NES mode
|
||||
lda HideStatusBar
|
||||
beq :full_screen
|
||||
|
||||
pea $FFFD
|
||||
_GTERender
|
||||
bra :render_done
|
||||
|
||||
:full_screen
|
||||
pea $FFFF
|
||||
_GTERender
|
||||
:render_done
|
||||
|
||||
; Check the AreaType and see if the palette needs to be changed. We do this after the screen is blitted
|
||||
; so the palette does not get changed too eary while old pixels are still on the screen.
|
||||
; so the palette does not get changed too early while old pixels are still on the screen.
|
||||
|
||||
ldal ROMBase+$074E
|
||||
and #$00FF
|
||||
@ -845,7 +966,8 @@ DrainNTQueue
|
||||
; issues because many frames can pass before Render gets control again. We need to expose a
|
||||
; _SetTileImmediate function in the list of function callbacks....
|
||||
|
||||
_GTESetTile
|
||||
_GTESetTileImmediate
|
||||
; _GTESetTile
|
||||
; inc :Count
|
||||
brl :skip
|
||||
|
||||
@ -1106,7 +1228,9 @@ CopyStatus
|
||||
inx
|
||||
stx Tmp2
|
||||
|
||||
_GTESetTile
|
||||
; _GTESetTile
|
||||
_GTESetTileImmediate
|
||||
|
||||
|
||||
ply
|
||||
plx
|
||||
@ -1193,7 +1317,8 @@ CopyNametable
|
||||
stx Tmp2
|
||||
:x_hop
|
||||
|
||||
_GTESetTile
|
||||
; _GTESetTile
|
||||
_GTESetTileImmediate
|
||||
|
||||
ply
|
||||
plx
|
||||
|
130
demos/smb/ppu.s
130
demos/smb/ppu.s
@ -110,6 +110,7 @@ PPUMASK_WRITE ENT
|
||||
|
||||
; $2002 - PPUSTATUS For "ldx ppustatus"
|
||||
PPUSTATUS_READ_X ENT
|
||||
pha ; spacefor result
|
||||
php
|
||||
pha
|
||||
|
||||
@ -117,31 +118,29 @@ PPUSTATUS_READ_X ENT
|
||||
stal w_bit ; Reset the address latch used by PPUSCROLL and PPUADDR
|
||||
|
||||
ldal ppustatus
|
||||
tax
|
||||
sta 3,s
|
||||
and #$7F ; Clear the VBL flag
|
||||
stal ppustatus
|
||||
|
||||
pla ; Restore the accumulator (return value in X)
|
||||
plp
|
||||
phx ; re-read x to set any relevant flags
|
||||
plx
|
||||
|
||||
rtl
|
||||
|
||||
PPUSTATUS_READ ENT
|
||||
pha ; space for return value
|
||||
php
|
||||
|
||||
lda #1
|
||||
stal w_bit ; Reset the address latch used by PPUSCROLL and PPUADDR
|
||||
|
||||
ldal ppustatus
|
||||
pha
|
||||
sta 2,s
|
||||
and #$7F ; Clear the VBL flag
|
||||
stal ppustatus
|
||||
|
||||
pla ; pop the return value
|
||||
plp
|
||||
pha ; re-read accumulator to set any relevant flags
|
||||
pla
|
||||
rtl
|
||||
|
||||
@ -334,8 +333,8 @@ PPUDATA_WRITE ENT
|
||||
sta nt_queue,y
|
||||
|
||||
:full
|
||||
; lda #1
|
||||
; jsr setborder
|
||||
lda #1
|
||||
jsr setborder
|
||||
rts
|
||||
|
||||
:attrtbl
|
||||
@ -421,6 +420,30 @@ PPUDATA_WRITE ENT
|
||||
dex
|
||||
jmp :enqueue
|
||||
|
||||
incborder
|
||||
php
|
||||
sep #$20
|
||||
ldal $E0C034
|
||||
inc
|
||||
eorl $E0C034
|
||||
and #$0F
|
||||
eorl $E0C034
|
||||
stal $E0C034
|
||||
plp
|
||||
rts
|
||||
|
||||
decborder
|
||||
php
|
||||
sep #$20
|
||||
ldal $E0C034
|
||||
dec
|
||||
eorl $E0C034
|
||||
and #$0F
|
||||
eorl $E0C034
|
||||
stal $E0C034
|
||||
plp
|
||||
rts
|
||||
|
||||
setborder
|
||||
php
|
||||
sep #$20
|
||||
@ -588,25 +611,47 @@ PPUDMA_WRITE ENT
|
||||
plp
|
||||
rtl
|
||||
|
||||
y_offset_rows equ 2
|
||||
y_height_rows equ 25
|
||||
y_offset equ {y_offset_rows*8}
|
||||
y_height equ {y_height_rows*8}
|
||||
max_nes_y equ {y_height+y_offset-8}
|
||||
;y_offset_rows equ 2
|
||||
;y_height_rows equ 25
|
||||
;y_offset equ {y_offset_rows*8}
|
||||
;y_height equ {y_height_rows*8}
|
||||
;max_nes_y equ {y_height+y_offset-8}
|
||||
|
||||
x_offset equ 16
|
||||
|
||||
; Scan the OAM memory and copy the values of the sprites that need to be drawn. There are two reasons to do this
|
||||
;
|
||||
; 1. Freeze the OAM memory at this instanct so that the NES ISR can keep running without changing values
|
||||
; 2. We have to scan this list twice -- once to build up the shadow list and once to actually render the sprites
|
||||
; This code has an optimization that it directly scans the NES RAM that would be DMA copied into the PPU
|
||||
; OAM space. This is ok, because
|
||||
;
|
||||
; 1. The OAM DMA occurs in the NES ROM before running any game logic
|
||||
; 2. This code is running after the prior ISR, so it is loically happening at the beginning of the next NMI
|
||||
;
|
||||
; When scanning the OAM values, sprites that are not visible for any number of reasons are skipped and the
|
||||
; sprite's y-position is adjusted based on the GTE camera view. This allow all of the shadowBitmap and
|
||||
; shadow lits work to assume an index value of zero is the top of the active play field.
|
||||
OAM_COPY ds 256
|
||||
spriteCount ds 0
|
||||
db 0 ; Pad in case we can to access using 16-bit instructions
|
||||
|
||||
mx %00
|
||||
scanOAMSprites
|
||||
stz Tmp5
|
||||
:top_line equ Tmp5
|
||||
:bot_line equ Tmp6
|
||||
|
||||
; In order for the shadow bitmap to be zeroed based on the active playfield, we need to adjust the NES
|
||||
; sprite y-coordinates by the designated top row of the NES graphics screen, and then add an additional
|
||||
; adjustment for the position of the GTE rendering window within that vertical space
|
||||
|
||||
lda NesTop
|
||||
clc
|
||||
adc YOrigin
|
||||
sta :top_line
|
||||
|
||||
lda NesBottom
|
||||
clc
|
||||
adc YOrigin
|
||||
sta :bot_line
|
||||
|
||||
sep #$30
|
||||
|
||||
@ -616,15 +661,23 @@ scanOAMSprites
|
||||
:loop
|
||||
; lda PPU_OAM,x ; Y-coordinate
|
||||
ldal ROMBase+$200,x
|
||||
cmp #max_nes_y+1 ; Skip anything that is beyond this line
|
||||
cmp :bot_line
|
||||
bcs :skip
|
||||
cmp #y_offset
|
||||
cmp :top_line
|
||||
bcc :skip
|
||||
sbc :top_line
|
||||
sta OAM_COPY,y ; Keep the adjusted coordinate
|
||||
|
||||
; cmp #max_nes_y+1 ; Skip sprites that are
|
||||
; bcs :skip
|
||||
; cmp #y_offset
|
||||
; bcc :skip
|
||||
|
||||
; lda PPU_OAM+1,x ; $FC is an empty tile, don't draw it
|
||||
ldal ROMBase+$201,x
|
||||
cmp #$FC
|
||||
beq :skip
|
||||
sta OAM_COPY+1,y
|
||||
|
||||
; lda PPU_OAM+3,x ; If X-coordinate is off the edge skip it, too.
|
||||
ldal ROMBase+$203,x
|
||||
@ -633,8 +686,8 @@ scanOAMSprites
|
||||
|
||||
rep #$20
|
||||
; lda PPU_OAM,x
|
||||
ldal ROMBase+$200,x
|
||||
sta OAM_COPY,y
|
||||
; ldal ROMBase+$200,x
|
||||
; sta OAM_COPY,y
|
||||
; lda PPU_OAM+2,x
|
||||
ldal ROMBase+$202,x
|
||||
sta OAM_COPY+2,y
|
||||
@ -834,19 +887,18 @@ buildShadowBitmap
|
||||
rts
|
||||
|
||||
; Set the SCB values equal to the bitmap to visually debug
|
||||
ldx #0
|
||||
ldx ScreenTop
|
||||
ldy #0
|
||||
:vloop
|
||||
lda #8
|
||||
sta Tmp6
|
||||
lda shadowBitmap+2,y
|
||||
lda shadowBitmap,y
|
||||
:iloop
|
||||
asl
|
||||
pha
|
||||
|
||||
lda #0
|
||||
bcc :zero
|
||||
inc
|
||||
rol
|
||||
:zero stal $E19D00,x
|
||||
pla
|
||||
|
||||
@ -855,7 +907,7 @@ buildShadowBitmap
|
||||
bne :iloop
|
||||
|
||||
iny
|
||||
cpy #25
|
||||
cpy ScreenRows
|
||||
bcc :vloop
|
||||
|
||||
rep #$30
|
||||
@ -1016,7 +1068,7 @@ exposeShadowList
|
||||
|
||||
:exit
|
||||
ldx :last ; Expose the final part
|
||||
ldy #y_height
|
||||
ldy ScreenHeight
|
||||
lda #0
|
||||
jsl LngJmp
|
||||
rts
|
||||
@ -1030,16 +1082,15 @@ shadowBitmapToList
|
||||
|
||||
sep #$30
|
||||
|
||||
ldx #y_offset_rows ; Start at the third row (y_offset = 16) walk the bitmap for 25 bytes (200 lines of height)
|
||||
lda #0
|
||||
sta shadowListCount ; zero out the shadow list count
|
||||
ldx #0 ; List is zero-based to the active play field
|
||||
stz shadowListCount ; zero out the shadow list count
|
||||
|
||||
; This loop is called when we are not tracking a sprite range
|
||||
:zero_loop
|
||||
ldy shadowBitmap,x
|
||||
beq :zero_next
|
||||
|
||||
lda {mul8-y_offset_rows},x ; This is the scanline we're on (offset by the starting byte)
|
||||
lda mul8,x ; This is the scanline we're on (offset by the starting byte)
|
||||
clc
|
||||
adc offset,y ; This is the first line defined by the bit pattern
|
||||
sta :top
|
||||
@ -1047,7 +1098,7 @@ shadowBitmapToList
|
||||
|
||||
:zero_next
|
||||
inx
|
||||
cpx #y_height_rows+y_offset_rows ; +1 ; End at byte 27
|
||||
cpx ScreenRows
|
||||
bcc :zero_loop
|
||||
bra :exit ; ended while not tracking a sprite, so exit the function
|
||||
|
||||
@ -1063,7 +1114,8 @@ shadowBitmapToList
|
||||
and offsetMask,y
|
||||
sta shadowBitmap,x
|
||||
|
||||
lda {mul8-y_offset_rows},x
|
||||
; lda {mul8-y_offset_rows},x
|
||||
lda mul8,x
|
||||
clc
|
||||
adc invOffset,y
|
||||
|
||||
@ -1081,7 +1133,8 @@ shadowBitmapToList
|
||||
|
||||
:one_next
|
||||
inx
|
||||
cpx #y_height_rows+y_offset_rows+1
|
||||
cpx ScreenRows
|
||||
|
||||
bcc :one_loop
|
||||
|
||||
; If we end while tracking a sprite, add to the list as the last item
|
||||
@ -1089,7 +1142,7 @@ shadowBitmapToList
|
||||
ldx shadowListCount
|
||||
lda :top
|
||||
sta shadowListTop,x
|
||||
lda #y_height
|
||||
lda ScreenHeight
|
||||
sta shadowListBot,x
|
||||
inx
|
||||
stx shadowListCount
|
||||
@ -1130,6 +1183,8 @@ drawOAMSprites
|
||||
plb
|
||||
|
||||
pha ; Save the phase indicator
|
||||
pei 124 ; RenderFlags
|
||||
|
||||
tdc ; Keep a copy of the second page of GTE direct page space
|
||||
clc
|
||||
adc #$0100
|
||||
@ -1144,6 +1199,8 @@ drawOAMSprites
|
||||
stx FTblPtr+2
|
||||
|
||||
pla
|
||||
sta RenderFlags
|
||||
pla
|
||||
|
||||
; Check what phase we're in
|
||||
;
|
||||
@ -1160,10 +1217,7 @@ drawOAMSprites
|
||||
; We need to "freeze" the OAM values, otherwise they can change between when we build the rendering pipeline
|
||||
|
||||
sei
|
||||
ldal nmiCount
|
||||
pha
|
||||
jsr scanOAMSprites ; Filter out any sprites that don't need to be drawn
|
||||
pla
|
||||
cli
|
||||
|
||||
jsr buildShadowBitmap ; Run though and quickly create a bitmap of lines with sprites
|
||||
@ -1202,7 +1256,7 @@ drawSprites
|
||||
oam_loop
|
||||
phx ; Save x
|
||||
|
||||
lda OAM_COPY,x ; Y-coordinate
|
||||
lda OAM_COPY,x ; Y-coordinate (zero based to screen)
|
||||
; inc ; Compensate for PPU delayed scanline
|
||||
|
||||
rep #$30
|
||||
@ -1218,7 +1272,7 @@ oam_loop
|
||||
clc
|
||||
adc :tmp
|
||||
clc
|
||||
adc #$2000-{y_offset*160}+x_offset
|
||||
adc ScreenBase
|
||||
sta :tmp
|
||||
|
||||
lda OAM_COPY+3,x
|
||||
|
@ -144,6 +144,12 @@ _GTEEnableSprites MAC
|
||||
_GTEEnableBackground MAC
|
||||
UserTool $3100+GTEToolNum
|
||||
<<<
|
||||
_GTEDrawTileToScreen MAC
|
||||
UserTool $3200+GTEToolNum
|
||||
<<<
|
||||
_GTESetTileImmediate MAC
|
||||
UserTool $3300+GTEToolNum
|
||||
<<<
|
||||
|
||||
; EngineMode definitions
|
||||
; Script definition
|
||||
@ -189,8 +195,9 @@ vblCallback equ $0004
|
||||
extSpriteRenderer equ $0005
|
||||
rawDrawTile equ $0006
|
||||
extBG0TileUpdate equ $0007
|
||||
userTileCallback equ $0008
|
||||
userTileCallback equ $0008 ; Callback for rendering custom tiles into the code field
|
||||
liteBlitter equ $0009
|
||||
userTileDirectCallback equ $000A ; Callback for drawing custom tiles directly to the screen buffer
|
||||
|
||||
; CopyPicToBG1 flags
|
||||
COPY_PIC_NORMAL equ $0000 ; Copy into BG1 buffer in "normal mode"
|
||||
|
@ -208,8 +208,9 @@ vblCallback equ $0004 ; User routine to be called by VBL int
|
||||
extSpriteRenderer equ $0005
|
||||
rawDrawTile equ $0006
|
||||
extBG0TileUpdate equ $0007
|
||||
userTileCallback equ $0008
|
||||
userTileCallback equ $0008 ; Callback for rendering custom tiles into the code field
|
||||
liteBlitter equ $0009
|
||||
userTileDirectCallback equ $000A ; Callback for drawing custom tiles directly to the screen buffer
|
||||
|
||||
; CopyPicToBG1 flags
|
||||
COPY_PIC_NORMAL equ $0000 ; Copy into BG1 buffer in "normal mode" treating the buffer as a 164x208 pixmap with stride of 256
|
||||
|
145
src/Render.s
145
src/Render.s
@ -160,13 +160,96 @@ ExtFuncBlock
|
||||
|
||||
; Special NES renderer that externalizes the sprite rendering in order to exceed the internal limit of 16 sprites
|
||||
_RenderNES
|
||||
; jsr _ApplyBG0YPos
|
||||
; jsr _ApplyBG0XPosPre
|
||||
sta RenderFlags
|
||||
bit #$0002 ; If this bit is off, don't render the top. If the top *is* rendered, assume full-screen
|
||||
bne *+5
|
||||
jmp _RenderNES2
|
||||
|
||||
jsr _ApplyBG0YPosPreLite
|
||||
jsr _ApplyBG0YPosLite
|
||||
jsr _ApplyBG0XPosPre
|
||||
|
||||
; Callback to update the tilestore with any new tiles
|
||||
jsr _RenderNESTileCallback
|
||||
jsr _ApplyTiles ; This function actually draws the new tiles into the code field
|
||||
|
||||
; Render the status bar area, which is fixed
|
||||
|
||||
stz tmp1 ; virt_line_x2
|
||||
lda #16*2
|
||||
sta tmp2 ; lines_left_x2
|
||||
lda #0 ; Xmod164
|
||||
jsr _ApplyBG0XPosAltLite
|
||||
lda tmp4 ; :exit_offset
|
||||
stal nesTopOffset
|
||||
|
||||
; Next render the remaining lines, which should have their screen locations adjusted for the
|
||||
; vertical offset
|
||||
|
||||
lda #16*2
|
||||
clc
|
||||
adc StartYMod208
|
||||
sta tmp1 ; virt_line_x2
|
||||
lda ScreenHeight
|
||||
sec
|
||||
sbc #16
|
||||
asl
|
||||
sta tmp2 ; lines_left_x2
|
||||
lda StartXMod164 ; Xmod164
|
||||
jsr _ApplyBG0XPosAltLite
|
||||
lda tmp4
|
||||
stal nesBottomOffset
|
||||
|
||||
jsr _RenderNESSpritesCallback
|
||||
|
||||
stz tmp1 ; :virt_line_x2
|
||||
lda #16*2
|
||||
sta tmp2 ; :lines_left_x2
|
||||
ldal nesTopOffset
|
||||
sta tmp4 ; :exit_offset
|
||||
jsr _RestoreBG0OpcodesAltLite
|
||||
|
||||
lda #16*2
|
||||
sta tmp1 ; :virt_line_x2
|
||||
lda ScreenHeight
|
||||
sec
|
||||
sbc #16
|
||||
asl
|
||||
sta tmp2 ; lines_left_x2
|
||||
ldal nesBottomOffset
|
||||
sta tmp4 ; :exit_offset
|
||||
jsr _RestoreBG0OpcodesAltLite
|
||||
bra :final_step
|
||||
|
||||
:no_status_restore
|
||||
stz tmp1
|
||||
lda ScreenHeight
|
||||
sta tmp2
|
||||
ldal nesBottomOffset
|
||||
sta tmp4 ; :exit_offset
|
||||
jsr _RestoreBG0OpcodesAltLite
|
||||
|
||||
:final_step
|
||||
jmp _RenderNESExit
|
||||
|
||||
_RenderNES2
|
||||
jsr _ApplyBG0YPosPreLite
|
||||
jsr _ApplyBG0YPosLite
|
||||
jsr _ApplyBG0XPosPre
|
||||
|
||||
jsr _RenderNESTileCallback
|
||||
jsr _ApplyTiles ; This function actually draws the new tiles into the code field
|
||||
|
||||
jsr _ApplyBG0XPosLite ; Do the full screen
|
||||
jsr _RenderNESSpritesCallback
|
||||
|
||||
lda StartYMod208 ; Restore the fields back to their original state
|
||||
ldx ScreenHeight
|
||||
jsr _RestoreBG0OpcodesLite
|
||||
|
||||
jmp _RenderNESExit
|
||||
|
||||
; Callback to update the tilestore with any new tiles
|
||||
_RenderNESTileCallback
|
||||
lda ExtUpdateBG0Tiles
|
||||
ora ExtUpdateBG0Tiles+2
|
||||
beq :no_tile
|
||||
@ -176,31 +259,7 @@ _RenderNES
|
||||
lda ExtUpdateBG0Tiles+1
|
||||
stal :patch0+2
|
||||
:patch0 jsl $000000
|
||||
:no_tile
|
||||
|
||||
jsr _ApplyTiles ; This function actually draws the new tiles into the code field
|
||||
|
||||
stz tmp1 ; virt_line_x2
|
||||
lda #16*2
|
||||
sta tmp2 ; lines_left_x2
|
||||
lda #0 ; Xmod164
|
||||
; jsr _ApplyBG0XPosAlt
|
||||
jsr _ApplyBG0XPosAltLite
|
||||
lda tmp4 ; :exit_offset
|
||||
stal nesTopOffset
|
||||
|
||||
lda #16*2
|
||||
sta tmp1 ; virt_line_x2
|
||||
lda ScreenHeight
|
||||
sec
|
||||
sbc #16
|
||||
asl
|
||||
sta tmp2 ; lines_left_x2
|
||||
lda StartXMod164 ; Xmod164
|
||||
; jsr _ApplyBG0XPosAlt
|
||||
jsr _ApplyBG0XPosAltLite
|
||||
lda tmp4
|
||||
stal nesBottomOffset
|
||||
:no_tile rts
|
||||
|
||||
; This is a tricky part. The NES does not keep sprites sorted, so we need an alternative way to figure out
|
||||
; which lines to shadow and which ones not to. Our compromise is to build a bitmap of lines that the sprite
|
||||
@ -208,7 +267,7 @@ _RenderNES
|
||||
;
|
||||
; This is handled by the callback in two phases. We pass pointers to the internal function the callback needs
|
||||
; access to. If there is no function defined, do nothing
|
||||
|
||||
_RenderNESSpritesCallback
|
||||
lda GTEControlBits
|
||||
bit #CTRL_SPRITE_DISABLE
|
||||
bne :no_render
|
||||
@ -243,32 +302,9 @@ _RenderNES
|
||||
ldx #^ExtFuncBlock
|
||||
ldy #ExtFuncBlock
|
||||
:patch2 jsl $000000
|
||||
:no_render rts
|
||||
|
||||
:no_render
|
||||
stz tmp1 ; :virt_line_x2
|
||||
lda #16*2
|
||||
sta tmp2 ; :lines_left_x2
|
||||
ldal nesTopOffset
|
||||
sta tmp4 ; :exit_offset
|
||||
; jsr _RestoreBG0OpcodesAlt
|
||||
jsr _RestoreBG0OpcodesAltLite
|
||||
|
||||
lda #16*2
|
||||
sta tmp1 ; :virt_line_x2
|
||||
lda ScreenHeight
|
||||
sec
|
||||
sbc #16
|
||||
asl
|
||||
sta tmp2 ; lines_left_x2
|
||||
ldal nesBottomOffset
|
||||
sta tmp4 ; :exit_offset
|
||||
; jsr _RestoreBG0OpcodesAlt
|
||||
jsr _RestoreBG0OpcodesAltLite
|
||||
|
||||
; lda StartYMod208 ; Restore the fields back to their original state
|
||||
; ldx ScreenHeight
|
||||
; jsr _RestoreBG0Opcodes
|
||||
|
||||
_RenderNESExit
|
||||
lda StartY
|
||||
sta OldStartY
|
||||
lda StartX
|
||||
@ -637,6 +673,7 @@ _RenderLite
|
||||
sta RenderFlags
|
||||
jsr _DoTimers ; Run any pending timer tasks
|
||||
|
||||
jsr _ApplyBG0YPosPreLite
|
||||
jsr _ApplyBG0YPosLite ; Set stack addresses for the virtual lines to the physical screen
|
||||
jsr _ApplyBG0XPosPre ; Lock in certain rendering variables (not lite/non-lite specific)
|
||||
|
||||
|
90
src/Tiles.s
90
src/Tiles.s
@ -352,6 +352,75 @@ _CalcTileProcIndex
|
||||
:no_flip_d lda #0
|
||||
rts
|
||||
|
||||
; Set a tile value in the backing store and immediately render into the code field
|
||||
;
|
||||
; A = tile id
|
||||
; X = tile column [0, 40] (41 columns)
|
||||
; Y = tile row [0, 25] (26 rows)
|
||||
;
|
||||
; Registers are not preserved
|
||||
_SetTileImmediate
|
||||
sta newTileId
|
||||
jsr _GetTileStoreOffset0 ; Get the address of the X,Y tile position
|
||||
tax
|
||||
|
||||
lda TileStore+TS_TILE_ID,x
|
||||
cmp newTileId
|
||||
bne :changed
|
||||
rts
|
||||
|
||||
:changed sta oldTileId
|
||||
lda newTileId
|
||||
sta TileStore+TS_TILE_ID,x ; Value is different, store it.
|
||||
|
||||
; If the user bit is set, then skip most of the setup and just fill in the TileProcs with the user callback
|
||||
; target
|
||||
bit #TILE_USER_BIT
|
||||
bne :set_user_tile
|
||||
|
||||
jsr _GetTileAddr
|
||||
sta TileStore+TS_TILE_ADDR,x ; Committed to drawing this tile, so get the address of the tile in the tiledata bank for later
|
||||
|
||||
; Set the renderer procs for this tile.
|
||||
;
|
||||
; NOTE: Later on, optimize this to just take the Tile ID & TILE_CTRL_MASK and lookup the right proc
|
||||
; table address from a lookup table....
|
||||
;
|
||||
; 1. The dirty render proc is always set the same.
|
||||
; 2. If BG1 and DYN_TILES are disabled, then the TS_BASE_TILE_DISP is selected from the Fast Renderers, otherwise
|
||||
; it is selected from the full tile rendering functions.
|
||||
; 3. The copy process is selected based on the flip bits
|
||||
;
|
||||
; When a tile overlaps the sprite, it is the responsibility of the Render function to compose the appropriate
|
||||
; functionality. Sometimes it is simple, but in cases of the sprites overlapping Dynamic Tiles and other cases
|
||||
; it can be more involved.
|
||||
|
||||
; Calculate the base tile proc selector from the tile Id (need X-register set to tile store index)
|
||||
|
||||
lda newTileId
|
||||
jsr _SetNormalTileProcs
|
||||
bra :render_tile
|
||||
|
||||
:set_user_tile
|
||||
and #$01FF
|
||||
jsr _GetTileAddr ; The user tile callback needs access to this info, too, but
|
||||
sta TileStore+TS_TILE_ADDR,x ; we just give the base tile address (hflip bit is ignored)
|
||||
|
||||
lda #UserTileDispatch
|
||||
stal K_TS_BASE_TILE_DISP,x
|
||||
stal K_TS_SPRITE_TILE_DISP,x
|
||||
stal K_TS_ONE_SPRITE,x
|
||||
|
||||
:render_tile
|
||||
phd ; save the current direct page
|
||||
tdc
|
||||
clc
|
||||
adc #$100 ; move to the next page
|
||||
tcd
|
||||
jsr _RenderTile
|
||||
pld
|
||||
rts
|
||||
|
||||
; Set a tile value in the tile backing store. Mark dirty if the value changes
|
||||
;
|
||||
; A = tile id
|
||||
@ -447,6 +516,27 @@ _UTDPatch jsl UserHook1 ; Call the users code
|
||||
:done
|
||||
rts
|
||||
|
||||
; Provides a direct callback without using the TileStore information
|
||||
; A = user data
|
||||
; Y = screen address
|
||||
; X = tile address
|
||||
;
|
||||
; Bank will be set to the tiledata bank, so lda $0000,x will load the first word of the tile's data
|
||||
UserTileDirectDispatch
|
||||
phd
|
||||
pha
|
||||
tdc
|
||||
clc
|
||||
adc #$100
|
||||
tcd
|
||||
pla
|
||||
pei DP2_TILEDATA_AND_TILESTORE_BANKS ; set the bank to the tiledata bank
|
||||
plb
|
||||
_UTD2Patch jsl UserHook1 ; Call the user's code
|
||||
plb ; Restore the curent bank
|
||||
pld
|
||||
rts
|
||||
|
||||
; Stub to have a valid address for initialization / reset
|
||||
UserHook1 rtl
|
||||
|
||||
|
85
src/Tool.s
85
src/Tool.s
@ -105,6 +105,9 @@ _CallTable
|
||||
adrl _TSEnableSprites-1
|
||||
adrl _TSEnableBackground-1
|
||||
|
||||
adrl _TSDrawTileToScreen-1
|
||||
adrl _TSSetTileImmediate-1
|
||||
|
||||
_CTEnd
|
||||
_GTEAddSprite MAC
|
||||
UserTool $1000+GTEToolNum
|
||||
@ -271,19 +274,36 @@ _TSReadControl
|
||||
|
||||
_TSExit #0;#0
|
||||
|
||||
; SetTile(xTile, yTile, tileId)
|
||||
_TSSetTile
|
||||
tileId equ FirstParam
|
||||
yTile equ FirstParam+2
|
||||
xTile equ FirstParam+4
|
||||
; SetTileImmediate(xTile, yTile, tileId)
|
||||
_TSSetTileImmediate
|
||||
:tileId equ FirstParam
|
||||
:yTile equ FirstParam+2
|
||||
:xTile equ FirstParam+4
|
||||
|
||||
_TSEntry
|
||||
|
||||
lda xTile,s ; Valid range [0, 40] (41 columns)
|
||||
lda :xTile,s ; Valid range [0, 40] (41 columns)
|
||||
tax
|
||||
lda yTile,s ; Valid range [0, 25] (26 rows)
|
||||
lda :yTile,s ; Valid range [0, 25] (26 rows)
|
||||
tay
|
||||
lda tileId,s
|
||||
lda :tileId,s
|
||||
jsr _SetTileImmediate
|
||||
|
||||
_TSExit #0;#6
|
||||
|
||||
; SetTile(xTile, yTile, tileId)
|
||||
_TSSetTile
|
||||
:tileId equ FirstParam
|
||||
:yTile equ FirstParam+2
|
||||
:xTile equ FirstParam+4
|
||||
|
||||
_TSEntry
|
||||
|
||||
lda :xTile,s ; Valid range [0, 40] (41 columns)
|
||||
tax
|
||||
lda :yTile,s ; Valid range [0, 25] (26 rows)
|
||||
tay
|
||||
lda :tileId,s
|
||||
jsr _SetTile
|
||||
|
||||
_TSExit #0;#6
|
||||
@ -312,6 +332,8 @@ _TSRender
|
||||
beq :nes
|
||||
cmp #$FFFE ; Experimental gte-lite mode
|
||||
beq :lite
|
||||
cmp #$FFFD
|
||||
beq :nes
|
||||
|
||||
bit #RENDER_WITH_SHADOWING
|
||||
beq :no_shadowing
|
||||
@ -1054,7 +1076,26 @@ _TSSetAddress
|
||||
stal _UTDPatch+2
|
||||
brl :out
|
||||
|
||||
:next_6
|
||||
:next_6 cmp #userTileDirectCallback
|
||||
bne :next_7
|
||||
lda :ptr,s
|
||||
ora :ptr+2,s
|
||||
beq :utd2_restore
|
||||
|
||||
lda :ptr,s
|
||||
stal _UTD2Patch+1 ; long addressing because we're patching code in the K bank
|
||||
lda :ptr+1,s
|
||||
stal _UTD2Patch+2
|
||||
brl :out
|
||||
|
||||
:utd2_restore
|
||||
lda #UserHook1
|
||||
stal _UTD2Patch+1
|
||||
lda #>UserHook1
|
||||
stal _UTD2Patch+2
|
||||
brl :out
|
||||
|
||||
:next_7
|
||||
:out
|
||||
_TSExit #0;#6
|
||||
|
||||
@ -1110,6 +1151,32 @@ _TSEnableBackground
|
||||
:done
|
||||
_TSExit #0;#2
|
||||
|
||||
; Draw a tile directly to the graphics screen. Provides an convenient way to immediately draw
|
||||
; tile content on the the graphics buffer without having to go through a Render function.
|
||||
;
|
||||
; DrawTileToScreen(screenAddr, tileId, tileFlags)
|
||||
_TSDrawTileToScreen
|
||||
:tileId equ FirstParam+4 ; fetches the address of the internal tile data buffer
|
||||
:screenAddr equ FirstParam+2 ; screen address on the SHR
|
||||
:userData equ FirstParam ; used in place of :tileId for user callback function
|
||||
|
||||
_TSEntry
|
||||
|
||||
lda :tileId,s
|
||||
bit #TILE_USER_BIT
|
||||
bne :doUserTile
|
||||
|
||||
bra :done
|
||||
:doUserTile
|
||||
jsr _GetTileAddr
|
||||
tax
|
||||
lda :screenAddr,s
|
||||
tay
|
||||
lda :userData,s
|
||||
jsr UserTileDirectDispatch
|
||||
:done
|
||||
_TSExit #0;#6
|
||||
|
||||
; Insert the GTE code
|
||||
|
||||
put Math.s
|
||||
|
@ -1,38 +1,39 @@
|
||||
; Subroutines that deal with the vertical scrolling and rendering. The primary function
|
||||
; of these routines are to adjust tables and patch in new values into the code field
|
||||
; when the virtual Y-position of the play field changes.
|
||||
|
||||
_ApplyBG0YPosLite
|
||||
|
||||
:rtbl_idx_x2 equ tmp0
|
||||
:virt_line_x2 equ tmp1
|
||||
:lines_left_x2 equ tmp2
|
||||
:draw_count_x2 equ tmp3
|
||||
:stk_save equ tmp4
|
||||
:line_count equ tmp5
|
||||
|
||||
; First task is to fill in the STK_ADDR values by copying them from the RTable array. We
|
||||
; copy from RTable[i] into BlitField[StartY+i].
|
||||
|
||||
stz :rtbl_idx_x2 ; Start copying from the first entry in the table
|
||||
|
||||
_ApplyBG0YPosPreLite
|
||||
lda StartY ; This is the base line of the virtual screen
|
||||
jsr Mod208
|
||||
sta StartYMod208
|
||||
rts
|
||||
|
||||
asl
|
||||
sta :virt_line_x2 ; Keep track of it
|
||||
_ApplyBG0YPosLite
|
||||
|
||||
; copy a range of address from the table into the destination bank. If we restrict ourselves to
|
||||
; rectangular playfields, this can be optimized to just subtracting a constant value. See the
|
||||
; Templates::SetScreenAddrs subroutine.
|
||||
:virt_line_x2 equ tmp1
|
||||
:lines_left_x2 equ tmp2
|
||||
|
||||
; First task is to fill in the STK_ADDR values by copying them from the RTable array. We
|
||||
; copy from RTable[i] into BlitField[StartY+i].
|
||||
|
||||
lda ScreenHeight
|
||||
asl
|
||||
sta :lines_left_x2
|
||||
|
||||
lda StartYMod208
|
||||
asl
|
||||
sta :virt_line_x2 ; Keep track of it
|
||||
|
||||
lda #0
|
||||
|
||||
_ApplyBG0YPosAltLite
|
||||
:rtbl_idx_x2 equ tmp0
|
||||
:virt_line_x2 equ tmp1
|
||||
:lines_left_x2 equ tmp2
|
||||
:draw_count_x2 equ tmp3
|
||||
|
||||
; Check to see if we need to split the update into two parts, e.g. do we wrap around the end
|
||||
; of the code field?
|
||||
sta :rtbl_idx_x2
|
||||
|
||||
ldx :lines_left_x2
|
||||
lda #208*2
|
||||
@ -46,6 +47,11 @@ _ApplyBG0YPosLite
|
||||
|
||||
stz :virt_line_x2 ; virtual line is at the top (by construction)
|
||||
|
||||
lda :rtbl_idx_x2
|
||||
clc
|
||||
adc :draw_count_x2
|
||||
sta :rtbl_idx_x2
|
||||
|
||||
lda :lines_left_x2
|
||||
sec
|
||||
sbc :draw_count_x2 ; this many left to draw. Fall through to finish up
|
||||
@ -62,6 +68,8 @@ _ApplyBG0YPosLite
|
||||
tay
|
||||
iny ; Fill in the first byte (_ENTRY_1 = 0)
|
||||
|
||||
ldx :rtbl_idx_x2 ; Load the stack address from here
|
||||
|
||||
sep #$20 ; Set the data bank to the code field
|
||||
lda BTableHigh
|
||||
pha
|
||||
|
@ -1,3 +1,6 @@
|
||||
; External entrypoint to render a tile directly into the code field
|
||||
RenderTile
|
||||
|
||||
; If there are no sprites, then we copy the tile data into the code field as fast as possible.
|
||||
; If there are sprites, then additional work is required
|
||||
_RenderTile
|
||||
|
Loading…
Reference in New Issue
Block a user