ANESE/roms/tests/ppu/ppu_read_buffer/source/graphics.s
2017-10-21 17:33:02 -07:00

717 lines
14 KiB
ArmAsm
Executable file
Vendored

zp_res nmi_count
zp_res nmi_ready
; Generates the load and store templates
; for using A*$100 as a buffer.
load_transfer_templates:
sta template_subst_ptr+1
lda #$00
sta template_subst_ptr+0
setw template_targetptr, load_cool_graphics
setw template_sourceptr, loader_template_begin
setb template_bytecount, (loader_template_end-loader_template_begin+3)&~3
jsr load_template
setw template_targetptr, transfer_cool_graphics
setw template_sourceptr, writer_template_begin
setb template_bytecount, (writer_template_end-writer_template_begin+3)&~3
;jmp load_template
load_template:
ldy #0
ldx template_subst_ptr+1
@loop: lda (template_sourceptr),y
cmp #$FF
beq @patch
@maybe_wrap:
sta (template_targetptr),y
iny
cpy template_bytecount
bne @loop
rts
@patch: lda template_subst_ptr+0
sta (template_targetptr),y
iny
txa
jmp @maybe_wrap
.pushseg
.segment "TEMPLATE1"
writer_template_begin:
lda #0
sta gfx_source_ppuptr+0
sta gfx_source_ppuptr+1
writer_template_skip_sourceptr:
sty gfx_bytecount
lda PPUSTATUS ; reset flip-flop
: jsr load_cool_graphics
lda addr+1
sta PPUADDR ; poke high 6 bits
lda addr+0
sta PPUADDR ; poke low 8 bits
ldy #0
: .repeat 8
lda $FFFF,y
sta PPUDATA
iny
.endrepeat
cpy gfx_bytecount
bne :-
inc gfx_source_ppuptr+1
inc addr +1
dex
bne :--
rts
writer_template_end:
.segment "TEMPLATE2"
loader_template_begin:
; Load Y bytes of data from PPU address "gfx_source_ppuptr" into oam_buffer.
lda gfx_source_ppuptr+1
sta PPUADDR ; poke high 6 bits
lda gfx_source_ppuptr+0
sta PPUADDR ; poke low 8 bits
lda PPUDATA
ldy #0
: .repeat 4
lda PPUDATA
sta $FFFF,y
iny
.endrepeat
cpy gfx_bytecount
bne :-
rts
loader_template_end:
.popseg
do_cool_graphics:
; Disable the rendering while transferring data from/to PPU memory.
jsr console_hide
; Just for the sake of being peculiar, all our graphics data is in VROM.
; Not only that, the code that loads the data from VROM is also in VROM!
; And not only that, the code that shows the data is there too.
; load_transfer_templates:
; Copies loader_template and writer_template into RAM.
; Patches the oam_buffer offset into the code.
; load_cool_graphics:
; This function is in RAM. It loads data from VROM into buffer.
; transfer_cool_graphics:
; This function is in RAM. It writes data from buffer into PPU memory.
; Load our graphics bootstrap code from VROM
set_vrom_page 1
setw gfx_source_ppuptr, page2_prefix_size
@size = $500 ;show_cool_graphics_size + $100
ldx #>@size
ldy #0
: setb gfx_bytecount,0
txa
bne :+
lda #<@size
beq @rts
sta gfx_bytecount
: txa
pha
tya
pha
clc
adc #>show_cool_graphics-1 ;-1 because we also load OAM template (gfx.spr.bin)
jsr load_transfer_templates
jsr load_cool_graphics
inc gfx_source_ppuptr+1
pla ;y
tay
iny
pla ;x
tax
dex
bpl :--
@rts:
; Jump into the bootstrap code.
jmp show_cool_graphics ; skip gfx.spr.bin
;;;;;;;;;;;;;;;;;;;;;
.segment "CHARS_P2"
@begin:
.incbin "gfx.nta.bin"
.incbin "gfx.attr.bin"
; The following begins at $0400.
.incbin "gfx.spr2.chr"
; The following begins at $0400+sprite_data_size.
.incbin "gfx.pal.bin"
@end:
page2_prefix_size = @end-@begin
.incbin "gfx.spr.bin"
.segment "CHARS_P2"
; THIS CODE IS STORED IN VROM. (PAGE 1, OFFSET $0520)
show_cool_graphics_template_begin:
; Because this code actually resides in VROM, and later in system
; RAM, we must use this c_DELTA factor in any and all absolute
; addresses (jmp,jsr etc) within this code referring to itself.
c_DELTA = show_cool_graphics_template_begin - show_cool_graphics
lda #1
sta console_palettes_disabled
text_white
jsr print_spacer
jsr print_str_
; 0123456789ABCDEF0123456789ABCD
.byte " This next test will take a while.",newline ;DO_DTE
.byte " In order to distract you with",newline ;DO_DTE
.byte " entertainment, art is provided.",newline ;DO_DTE
.byte " Contemplate on the art while",newline ;DO_DTE
.byte " the test is in progress.",newline ;DO_DTE
.byte newline,newline,newline ;DO_DTE
.byte 0 ;DO_DTE
jsr c_test_read_buffer_fade_blank_delay - c_DELTA
lda #0
sta PPUCTRL
sta PPUMASK
;set_vrom_page 1
lda #>oam_buffer
jsr load_transfer_templates
; Transfer $400 bytes of data to $2C00 (second nta regadless of mirroring)
setw addr, $2C00
ldx #4
ldy #0
jsr transfer_cool_graphics
; Transfer $20 bytes of data to $3FE0
setw addr, $3FE0
setw gfx_source_ppuptr, (page2_prefix_size-$20)
ldx #1
ldy #$20
jsr transfer_cool_graphics + (writer_template_skip_sourceptr-writer_template_begin)
; Initialize scrolling
lda #230
sta scroll_framecount
lda #1
sta scroll_y
lda #0
sta scroll_y+1
sta scroll_ydelta
sta scroll_ydelta+1
sta scroll_framecount+1
sta nmi_ready
jsr console_show
set_vrom_page 0
jsr c_test_read_buffer_fade_visible_delay_init - c_DELTA
lda #$00
sta nmi_count
sta PPUSCROLL
lda #1
sta PPUSCROLL
; Load $100 bytes of actual sprite data to oam_buffer
ldx #0
: lda CoolGraphicsSpr, x
sta oam_buffer,x
inx
bne :-
jsr wait_vbl_optional
lda #$A0 ;Select background graphics page 0 & nametable 0
sta PPUCTRL ; & 16pix sprites & enable NMI
lda #($F * 2)
sta PPUMASK ;Show background graphics and sprites
c_main_loop:
lda #0
c_end_flag_loc = *-1
bne @end_loop
jsr c_update_scrolling - c_DELTA - $800
jsr c_update_sprites - c_DELTA - $1000
jsr c_update_split_points - c_DELTA - $1800
jsr c_read_joypad - c_DELTA - $1800
; All set. Wait for NMI
inc nmi_ready
: lda nmi_count
beq :-
dec nmi_count
dec nmi_ready
beq c_main_loop
@end_loop:
lda #0
sta PPUCTRL ; disable NMI
sta console_palettes_disabled
jsr console_hide
set_vrom_page 0
rts
c_nmi_handler:
; That's right, our NMI handler is in RAM.
lda nmi_ready
beq @skip_nmi_reentry
lda nmi_count
bne @skip_nmi_reentry
sta bus_conflict_antidote+0 ; VROM page 0
lda PPUSTATUS
jsr c_test_read_buffer_fade_visible_delay_callback - c_DELTA
ldx scroll_y
ldy #0
sty PPUSCROLL ;0
stx PPUSCROLL ;scroll_y
sty SPRADDR ;sprite 0
lda #>oam_buffer
sta $4014
lda #$A0 ;Select background graphics page 1 & nametable 0
sta PPUCTRL ; & 16pix sprites & enable NMI
ldy #($F*2)
lda #2
bit joy_cur
beq :+
ldy #($F*2)-16
: sty PPUMASK
jsr c_perform_splits - c_DELTA - $1000
inc nmi_count
@skip_nmi_reentry:
rti
c_update_sprites:
lda scroll_y
clc
adc #17
sta scroll_delays
ldy #$F8
ldx #$E0
c_sprloop:
.repeat 8,n
lda CoolGraphicsSpr -n*$20,x
sec
sbc scroll_delays
bcc :+
tya
: sta oam_buffer -n*$20 ,x
.endrepeat
inx
inx
inx
inx
bne c_sprloop
rts
c_perform_splits:
; SECTION 0 (BEFORE GRAPHICS):
; 0.$0000 - BACKGROUND: TEXT.
; 0.$1000 - unused
lda scroll_delays+0
beq :+
ldx #50
jsr delay_n_scanlines_ntsc
set_vrom_page 2
lda #$B1 ;Select background graphics page 1 & nametable 0
sta PPUCTRL ; & 16pix sprites & enable NMI
; SECTION 1:
; 2.$0000 - SPRITES.
; 2.$1000 - BACKGROUND
lda scroll_delays+4
beq :+
ldx #50
jsr delay_n_scanlines_ntsc
set_vrom_page 1
; SECTION 2:
; 1.$0000 - SPRITES. code unused
; 1.$1000 - BACKGROUND
lda scroll_delays+5
beq :+
ldx #20
jsr delay_n_scanlines_ntsc
set_vrom_page 0
; SECTION 3:
; 0.$0000 - unused
; 0.$1000 - SPRITES & BACKGROUND
lda scroll_delays+6
beq :+
ldx #20
jsr delay_n_scanlines_ntsc
set_vrom_page 2
lda #$A1 ;Select background graphics page 0 & nametable 0
sta PPUCTRL ; & 16pix sprites & enable NMI
; SECTION 4:
; 2.$0000 - SPRITES & BACKGROUND
; 2.$1000 - unused
: rts
c_update_split_points:
c_MAXY=242
c_OFFSET=255
lda #255
sta scroll_delays+0
sta scroll_delays+1
sta scroll_delays+2
sta scroll_delays+3
ldx scroll_y
dex
stx scroll_delays+6
.macro sub16 v
lda #<(v)
sec
sbc scroll_delays+6
tax
lda #>(v)
sbc #0
.endmacro
sub16 c_OFFSET+(0)
bne @skip0
stx scroll_delays+0
sub16 c_OFFSET+(64)
bne @skip0
stx scroll_delays+1
sub16 c_OFFSET+(128)
bne @skip1
stx scroll_delays+2
sub16 c_OFFSET+(184)
bne @skip2
stx scroll_delays+3
@skip2:
@skip1:
@skip0:
lda #0
sta scroll_delays+4
sta scroll_delays+5
sta scroll_delays+6
lda scroll_delays+0
tay
cmp #c_MAXY
bcc :+
ldy #0
: sty scroll_delays+0
;
lda scroll_delays+1
cmp #c_MAXY
bcs :+
sec
sbc scroll_delays+0
sta scroll_delays+4
;
lda scroll_delays+2
cmp #c_MAXY
bcs :+
sec
sbc scroll_delays+1
sta scroll_delays+5
;
lda scroll_delays+3
cmp #c_MAXY
bcs :+
sec
sbc scroll_delays+2
sta scroll_delays+6
:
rts
c_update_scrolling:
lda scroll_framecount+1
cmp #2
beq c_scroll_finish
bcs c_scroll_updated
;
inc scroll_framecount
bne :+
inc scroll_framecount+1
: lda scroll_y+1
clc
adc scroll_ydelta
sta scroll_y+1
lda scroll_y
adc scroll_ydelta+1
sta scroll_y
lda scroll_y
cmp #$A0
bcs c_past_end
lsr
lsr
lsr
lsr
tay
lda scroll_ydelta
clc
adc c_scroll_ydeltatable - c_DELTA - $1000,y
sta scroll_ydelta
lda scroll_ydelta+1
adc #0
sta scroll_ydelta+1
jmp c_scroll_updated - c_DELTA
c_past_end:
lda scroll_ydelta
sec
sbc #15
sta scroll_ydelta
lda scroll_ydelta+1
sbc #0
sta scroll_ydelta+1
jmp c_scroll_updated - c_DELTA
c_scroll_finish:
inc scroll_framecount+1
lda #238
sta scroll_y
c_scroll_updated:
rts
c_scroll_ydeltatable:
.byt 1,1,1,1,3, 6,10,15,20,24
c_read_joypad:
ldy #1
sty JOY1
dey
sty JOY1
ldy #8
: lda JOY1
sta joy_temp
lsr
ora joy_temp
lsr
ror joy_cur
dey
bne :-
rts
c_test_read_buffer_fade_blank_delay:
; Because the screen will be blank for 2 seconds,
; make some kind of sound effect to show that
; it is somehow expected.
setb SNDCHN, 3 ; Enable APU square channels 0,1
jsr pre_beep_dual
jsr populate_ciram_quick
; Console is left hidden and rendering disabled
set_ppuaddr $2E13
lda PPUDATA
begin_test TEST_BUFFER_DELAY_BLANK_2SECONDS
jsr pre_beep_length
jsr pre_beep_length
jsr pre_beep_length
jsr long_beep
delay (CLOCK_RATE*161)/100
fail_if_ppuread_y_not $8A ; $77+$13
jsr final_beep
begin_test TEST_BUFFER_DELAY_BLANK_1FRAME
delay 40000 ; 1-2 frames
lda PPUDATA
cmp #$8B
jmp fail_current_test_ifNE
zp_res test_position
zp_res test_ptr, 2
zp_res remaining_delay, 2
c_test_read_buffer_fade_visible_delay_callback:
bit test_position
bmi @elapse_delay
.macro test_set__addr_and_load
lda (test_ptr),y
sta PPUADDR
dey ;2
lda (test_ptr),y
sta PPUADDR
lda PPUDATA
.endmacro
@choose_next_test:
begin_test TEST_BUFFER_DELAY_INTERNAL
;bit PPUSTATUS
; Check if we are in end of list of tests
ldy #0
lda (test_ptr),y
beq @all_done
;
; Set flag that indicates a test is in progress
dec test_position
;
; Load test specifications
ldy #5
lda (test_ptr),y
sta remaining_delay+1
dey ;4
lda (test_ptr),y
sta remaining_delay+0
dey ;3
test_set__addr_and_load
lda PPUDATA
ldy #1
cmp (test_ptr),y
jsr fail_current_test_ifNE
ldy #3
test_set__addr_and_load
ldy #0
lda (test_ptr),y
jmp begin_test_
@all_done:
setb cool_graphics_end_flag, 1
@done: rts
@elapse_delay:
lda remaining_delay+0
sec
sbc #1
sta remaining_delay+0
lda remaining_delay+1
sbc #0
sta remaining_delay+1
ora remaining_delay+0
bne @done
@read_result:
jsr final_beep
ldy #3
test_set__addr_and_load
dey ;1
cmp (test_ptr),y
jsr fail_current_test_ifNE
; Reset flag, to indicate no test is in progress
inc test_position
; Jump to next test
lda test_ptr
clc
adc #6
sta test_ptr
lda test_ptr+1
adc #0
sta test_ptr+1
jmp @choose_next_test - c_DELTA
c_test_read_buffer_fade_visible_delay_init:
setb test_position, 0
sta remaining_delay+0
sta remaining_delay+1
setw test_ptr, test_read_buffer_fade_visible_delay_schedule
rts
cool_graphics_end_flag = c_end_flag_loc - c_DELTA
nmi = c_nmi_handler - c_DELTA
show_cool_graphics_size = * - show_cool_graphics_template_begin
.segment "CHARS_GFX3a"
.incbin "gfx.spr.page3_A.chr"
.segment "CHARS_GFX3b"
.incbin "gfx.spr.page3_B.chr"
.segment "CHARS_SPR1"
.incbin "gfx.spr1.chr"
.segment "CHARS_GFX1"
.incbin "gfx.page1.chr"
.segment "CHARS_GFX2"
.incbin "gfx.page2.chr"
.segment "CHARS_6C00"
.incbin "gfx.6c00.bin"
.segment "CHARS_P4"
.incbin "gfx.bank4.bin"
.segment "STACK"
transfer_cool_graphics: .res (writer_template_end-writer_template_begin+3)&~3
; This function is stored in $100-$15F. Though the memory area
; is reserved for stack, we expect the stack to require
; considerably less space.
; There's no real REASON for it to be stored in this contrived
; manner other than to provide some unusual code for an emulator
; author to test with, and occassional fun for anyone who chooses
; to try to reverse engineer an assembler trace.
.segment "ZEROPAGE"
; Load X times Y bytes of data from PPU address $0000, store
; to PPU address "addr". Use temporary buffer as configured by
; load_transfer_templates.
load_cool_graphics: .res ((loader_template_end-loader_template_begin)+3)&~3
; This function is stored in ZEROPAGE. We don't generally
; use that much of zeropage room, so it is expected to be
; available. As a side effect, we get a linker error if
; the alignment did not work properly, which is good.
; The disclaimer about real reason applies somewhat, though.
; We could easily fit both of these functions in BSS.
.segment "OAM_BUF"
; OAM cache. Sprite data is calculated into this buffer,
; and uploaded to the PPU using the $4014 DMA.
.align $100
oam_buffer: .res $100
.segment "CODE_BUF"
.align $100
CoolGraphicsSpr:
.res $100
; This buffer is for code that runs in the RAM.
; It is initialized by loading the code from VROM.
; The real reason disclaimer is heavily leaned on here.
show_cool_graphics_buf:
.res show_cool_graphics_size
show_cool_graphics = show_cool_graphics_buf+$1800