Compare commits

...

2 Commits

Author SHA1 Message Date
Lucas Scharenbroich b2ead87d25 Add configurable 240Hz, 120Hz and 60Hz APU update rates 2023-07-04 16:20:00 -05:00
Lucas Scharenbroich ab202470a9 Disable OAM DMA in ROM 2023-07-04 16:19:33 -05:00
3 changed files with 257 additions and 35 deletions

View File

@ -87,6 +87,7 @@ FTblTmp equ 228
; Initialize the sound hardware for APU emulation
lda #2 ; 0 = 240Hz, 1 = 120Hz, 2 = 60Hz (external)
jsr APUStartUp
; Start up GTE to drive the graphics
@ -119,6 +120,12 @@ FTblTmp equ 228
pea #NESTileBlitter
_GTESetAddress
; Install a custom tile callback to draw tiles directly on the screen w/proper palettes
; pea userTileDirectCallback
; pea #^NESDirectTileBlitter
; pea #NESDirectTileBlitter
; _GTESetAddress
; Get the address of a low-level routine that can be used to draw a tile directly to the graphics screen
; pea rawDrawTile
; _GTEGetAddress
@ -132,8 +139,12 @@ FTblTmp equ 228
; Initialize the graphics screen playfield (256x160). The NES is 240 lines high, so 160
; is a reasonable compromise.
; pea #128
; pea #184
pea #128
pea #200
; pea #80
; pea #144
_GTESetScreenMode
@ -183,6 +194,9 @@ FTblTmp equ 228
pla
sta OldOneSec
; Show the configuration screen
; jsr ShowConfig
; Set an internal flag to tell the VBL interrupt handler that it is
; ok to start invoking the game logic. The ROM code has to be run
; at 60 Hz because it controls the audio. Bad audio is way worse
@ -368,8 +382,15 @@ EvtLoop
ldy #33
jsr CopyNametable
brl EvtLoop
:not_v
cmp #'t' ; show VBL interrupt time
bne :not_t
lda show_vbl_cpu
eor #1
sta show_vbl_cpu
brl EvtLoop
:not_t
cmp #'q'
beq Exit
brl EvtLoop
@ -393,7 +414,9 @@ singleStepMode dw 0
nmiCount dw 0
DPSave dw 0
LastAreaType dw 0
frameCount dw 0
frameCount dw 0
show_vbl_cpu dw 0
; Toggle an APU control bit
ToggleAPUChannel
pha
@ -472,6 +495,17 @@ RenderFrame
sta OldROMScrollEdge ; Stash a copy for the next round through
lsr
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
pea $0000
_GTESetBG0Origin
@ -535,6 +569,122 @@ AreaPalettes dw WaterPalette,Area1Palette,Area2Palette,Area3Palette,Area4Pale
SwizzleTables adrl AT0_T0,AT1_T0,AT2_T0,AT3_T0,AT2_T0
SwizzlePtr adrl AT1_T0
; Draw PPU tiles to the screen for a UI
;
; 0 - 9 starts at tile 256
; A - Z starts at tile 266
; mushroom is $1CE = 462
TILE_ZERO equ 256
TILE_A equ 266
TILE_SHROOM equ 462
ShowConfig
lda #0
jsr SetAreaType
lda #$0000
stal $E19E00
ldx #TILE_SHROOM+TILE_USER_BIT
:loop
phx
phx
pea $2000
pea $0000 ; Bits 1-2, 5 and 7 are valid
_GTEDrawTileToScreen
ldx #msg1
ldy #$2000+{8*160}
lda #$0000
jsr ConfigDrawString
ldx #msg1
ldy #$2000+{16*160}
lda #$0001
jsr ConfigDrawString
ldx #msg1
ldy #$2000+{24*160}
lda #$0002
jsr ConfigDrawString
ldx #msg1
ldy #$2000+{32*160}
lda #$0003
jsr ConfigDrawString
:waitloop
pha
_GTEReadControl
pla
bit #PAD_KEY_DOWN
beq :waitloop
plx
and #$007F
cmp #LEFT_ARROW
beq :decrement
cmp #RIGHT_ARROW
beq :increment
rts
:increment
inx
inx
:decrement dex
bra :loop
; X = string pointer
; Y = address
ConfigDrawString
stx Tmp0
sty Tmp1
sta Tmp2
lda (Tmp0)
and #$00FF
tax
ldy #1
:loop
phx
phy
lda (Tmp0),y
and #$007F
cmp #'A'
bcc :not_letter
sbc #'A'
clc
adc #TILE_A
bra :draw
:not_letter
cmp #'0'
bcc :skip
sbc #'0'
clc
adc #TILE_ZERO
:draw
ora #TILE_USER_BIT
pha
pei Tmp1
pei Tmp2 ; palette select
_GTEDrawTileToScreen
:skip
lda Tmp1
clc
adc #4
sta Tmp1
ply
plx
iny
dex
bne :loop
rts
msg1 str 'HELLO 123'
; Take a PPU address and convert it to a tile store coordinate
;
; Inputs
@ -1021,7 +1171,7 @@ CopyNametable
ldx Tmp2 ; Nametable address
lda PPU_MEM+$2000,x
and #$00FF
ora #$0100+TILE_USER_BIT ; USe top 256 tiles and set as a user-defined tile
ora #$0100+TILE_USER_BIT ; Use top 256 tiles and set as a user-defined tile
pha
jsr GetPaletteSelect
ora 1,s ; Merge bits 9 and 10 into the Tile ID that's on the stack
@ -1072,6 +1222,10 @@ CopyNametable
; Trigger an NMI in the ROM
triggerNMI
sep #$30
jsl quarter_speed_driver
rep #$30
ldal ppuctrl ; If the ROM has not enabled VBL NMI, also skip
bit #$80
beq :skip
@ -1617,20 +1771,25 @@ nmiTask
ldal singleStepMode
bne :no_nmi
; lda #1
; jsr setborder
ldal show_vbl_cpu
beq :no_show_1
jsr incborder
:no_show_1
jsr triggerNMI
; lda #0
; jsr setborder
:no_nmi
ldal show_vbl_cpu
beq :no_nmi
jsr decborder
:no_nmi
pld
plb
plp
:skip
rtl
mx %00
readInput

View File

@ -31,6 +31,7 @@ access_doc_ram_no_inc = *
mx %00
APUStartUp
stal apu_mode
sei
phd
pea $c000
@ -51,11 +52,16 @@ APUShutDown = *
jsr stop_playing
ldal apu_mode
cmp #2
beq :no_doc_interrupts
lda backup_interrupt_ptr ; restore old interrupt ptr
stal sound_interrupt_ptr
lda backup_interrupt_ptr+2
stal sound_interrupt_ptr+2
:no_doc_interrupts
cli
pld
clc
@ -109,7 +115,7 @@ copy_instruments_to_doc
lda #$0600
jsr copy_noise
lda #$8000
; lda #$8000
; jsr gen_noise
rts
@ -313,6 +319,10 @@ copy_register_config
;--------------------------
setup_interrupt = *
ldal apu_mode
cmp #2 ; mode 2 = external driver at 60Hz
beq :no_doc_interrupts
ldal sound_interrupt_ptr
sta backup_interrupt_ptr
ldal sound_interrupt_ptr+2
@ -330,6 +340,19 @@ setup_interrupt = *
sep #$20
mx %10
ldal apu_mode
beq :do_240hz
lda #598
ldy #598/256
bra :set_timer_freq
:do_240hz lda #1195
ldy #1195/256
:set_timer_freq
stal timer_sound_settings+1 ; low byte of timer frequency
tya
stal timer_sound_settings+3 ; high byte of timer frequency
jsr access_doc_registers
ldy #0
@ -344,7 +367,7 @@ setup_interrupt = *
rep #$20
mx %00
:no_doc_interrupts
rts
interrupt_oscillator = 31
@ -398,6 +421,7 @@ noise_sound_settings = *
dfb $a0+noise_oscillator,0 ; mode register, set to free run
backup_interrupt_ptr ds 4
apu_mode ds 2
;-----------------------------------------------------------------------------------------
; APU internals
@ -557,6 +581,45 @@ PULSE_CONST_VOL_FLAG equ $10
NOISE_CONST_VOL_FLAG equ $10
TRIANGLE_HALT_FLAG equ $80
mx %11
quarter_speed_driver = *
phb
phd
phk
plb
pea $c000
pld
ldx apu_frame_counter
inx
inx
cpx #2*apu_frame_steps ; TODO: This is set by MSB in $4017 (4 or 5). 4 = PAL, 5 = NTSC.
bcc *+4
ldx #0
stx apu_frame_counter
jmp (:frame_counter_proc,x)
:frame_counter_proc da :quarter_frame,:half_frame,:quarter_frame,:no_frame_60,:half_frame
; Quarter-speed interrupts (60Hz) -- clock four times in each handler
:half_frame
:quarter_frame
jsr half_frame_clock
jsr quarter_frame_clock
jsr quarter_frame_clock
jsr half_frame_clock
jsr quarter_frame_clock
jsr quarter_frame_clock
brl update_doc_registers
:no_frame_60
pld
plb
clc
rtl
mx %11
interrupt_handler = *
@ -588,9 +651,7 @@ interrupt_handler = *
beq *+5
brl :not_timer ; Only service timer interrupts
; Update the frame counter. We double-count so that frame counter can be used directly to dispatch to the
; appropriate tick handler
; Update the frame counter and leave the doubled countin x-register for dispatch
ldx apu_frame_counter
inx
inx
@ -598,31 +659,30 @@ interrupt_handler = *
bcc *+4
ldx #0
stx apu_frame_counter
jmp (:frame_counter_proc,x)
:frame_counter_proc da :quarter_frame,:half_frame,:quarter_frame,:no_frame,:half_frame
; Figure out which speed we are dispatching
lda apu_mode
beq :do_240hz_mode
jmp (:apu_120hz_table,x)
:do_240hz_mode jmp (:apu_240hz_table,x)
:apu_240hz_table da :quarter_frame_240,:half_frame_240,:quarter_frame_240,no_frame,:half_frame_240
:apu_120hz_table da :quarter_frame_120,:half_frame_120,:quarter_frame_120,no_frame,:half_frame_120
; Full speed emulation (240Hz)
:half_frame jsr half_frame_clock
:quarter_frame jsr quarter_frame_clock
:half_frame_240 jsr half_frame_clock
:quarter_frame_240 jsr quarter_frame_clock
bra update_doc_registers
; Half-speed interrupts (120Hz) -- clock twice in each handler
;:half_frame
;:quarter_frame
; jsr half_frame_clock
; jsr quarter_frame_clock
; jsr quarter_frame_clock
; Quarter-speed interrupts (60Hz) -- clock four times in each handler
;:half_frame
;:quarter_frame
; jsr half_frame_clock
; jsr quarter_frame_clock
; jsr quarter_frame_clock
; jsr half_frame_clock
; jsr quarter_frame_clock
; jsr quarter_frame_clock
:half_frame_120
:quarter_frame_120
jsr half_frame_clock
jsr quarter_frame_clock
jsr quarter_frame_clock
; Apply any changes to the DOC registers
update_doc_registers
jsr access_doc_registers
; Set the parameters for the first square wave channel.
@ -802,8 +862,8 @@ interrupt_handler = *
:high_pitch pla
sta sound_data
:no_frame
:not_timer
no_frame
ldal show_border
beq :no_show2
ldal border_color

View File

@ -988,8 +988,11 @@ ScreenOff sta Mirror_PPU_CTRL_REG2 ;save bits for later but not in registe
lda #$00
jsr InitScroll
jsr OAM_ADDR_W ;reset spr-ram address register
lda #$02 ;perform spr-ram DMA access on $0200-$02ff
jsr SPR_DMA_W
; PPU harness just reads from $200 before calling the NMI task
; lda #$02 ;perform spr-ram DMA access on $0200-$02ff
; jsr SPR_DMA_W
ldx VRAM_Buffer_AddrCtrl ;load control for pointer to buffer contents
lda VRAM_AddrTable_Low,x ;set indirect at $00 to pointer
sta $00