SAVMSC = $58 VDSLST = $200 VDSLSTL = $200 VDSLSTH = $201 COLPF0 = $D016 COLPF1 = $D017 COLPF2 = $D018 COLPF3 = $D019 COLBK = $D01A AUDC1 = $D201 DMACTL = $D400 DLISTL = $D402 DLISTH = $D403 WSYNC = $D40A VCOUNT = $D40B NMIEN = $D40E temp1l = $80 temp1h = $81 temp1 = temp1l temp2l = $82 temp2h = $83 temp2 = temp2l sample_ptrl = $84 sample_ptrh = $85 sample_ptr = sample_ptrl scanline = $86 audiotemp = $87 ;height = 160 height = 192 bytes_per_line = 40 pages_per_frame = 32 lines_per_frame = 262 ;scanline_offset = 31 + (40 - 24) / 2 scanline_offset = 30 scanline_max = (lines_per_frame - scanline_offset) / 2 .data .import audio_samples .import audio_samples_end .import frame1_top .import frame1_bottom .import frame1_palette1_even .import frame1_palette1_odd .import frame1_palette2_even .import frame1_palette2_odd .import frame1_palette3_even .import frame1_palette3_odd .import displaylist audio_high_byte: .scope .macro byteseq val .repeat 16 .byte val | $10 .endrep .endmacro byteseq $0 byteseq $1 byteseq $2 byteseq $3 byteseq $4 byteseq $5 byteseq $6 byteseq $7 byteseq $8 byteseq $9 byteseq $a byteseq $b byteseq $c byteseq $d byteseq $e byteseq $f .endscope .code .export start .proc start ; Set up the audio sample buffer lda #.lobyte(audio_samples) sta sample_ptrl lda #.hibyte(audio_samples) sta sample_ptrh ; Disable display DMA lda #$00 sta DMACTL ; Disable VBI and DLI but allow Reset lda #$20 sta NMIEN ; Set up the display list lda #.lobyte(displaylist) sta DLISTL lda #.hibyte(displaylist) sta DLISTH ; Disable VBI but allow Reset and DLI lda #$a0 sta NMIEN ; Manually wait for first scan line wait_vblank: sta WSYNC lda VCOUNT bne wait_vblank ; Re-enable display DMA lda #$22 sta DMACTL wait_start: ; Wait for the vblank ; Resynchronize the scanline counter wait_loop: ldy VCOUNT ; 4 cycles bne wait_loop ; 2 cycles .macro inner_scanline frame_offset, line_offset ; Y should be VCOUNT at entry ; it'll fire on unused lines, but harmlessly ; 23-26 cycles before break ; Leisurely memory fetches lda frame1_palette1_even + frame_offset + line_offset - scanline_offset / 2,y ; 4/5 @FIXME alternate pha ; 3 ldx frame1_palette2_even + frame_offset + line_offset - scanline_offset / 2,y ; 4/5 lda frame1_palette3_even + frame_offset + line_offset - scanline_offset / 2,y ; 4/5 tay ; 2 pla ; 3 ; Wait for horizontal blank sta WSYNC ; 4 ; 12 cycles after break ; Update color registers as fast as possible sta COLPF0 ; 4 stx COLPF1 ; 4 sty COLPF2 ; 4 .endmacro .macro audio_play_raw ;ldy VCOUNT ; set on entry lda (sample_ptr),y ; 5/6 cyc sta AUDC1 ; 4 cyc .endmacro .macro audio_prep ; 8-9 cycles ; Y is VCOUNT at entry lda (sample_ptr),y ; 5/6 cyc sta audiotemp ; 3 cyc .endmacro .macro audio_play_lo ; 8 cycles ; A is loaded with packed audio byte at entry and #$0f ; 2 cyc ora #$10 ; 2 cyc sta AUDC1 ; 4 cyc .endmacro ; clobbers Y .macro audio_play_hi ; 12 cycles ldy audiotemp ; 3 cyc lda audio_high_byte,y ; 5 cyc sta AUDC1 ; 4 cyc .endmacro .macro audio_inc ; 22 cycles lda sample_ptrl ; 3 cyc clc ; 2 cyc adc #131 ; 2 cyc sta sample_ptrl ; 3 cyc lda sample_ptrh ; 3 cyc adc #0 ; 2 cyc sta sample_ptrh ; 3 cyc cmp #.hibyte(audio_samples_end) ; 2 cyc bmi audio_cont ; 2 cyc ; 10 cycles, optional lda #.lobyte(audio_samples) ; 2 sta sample_ptrl ; 3 lda #.hibyte(audio_samples) ; 2 sta sample_ptrh ; 3 audio_cont: .endmacro .macro run_frame frame_offset .scope ; each scanline is 228 color clocks ; that's 114 CPU cycles ; minus 41-43 for DMA leaves 71-73 clock cycles per line each_scanline_pair: sty scanline ; 3 cycles inner_scanline frame_offset, 0 ; 23-26 cycles before break, 12 cycles after ldy scanline ; 3 cycles audio_prep ; 8-9 cycles audio_play_lo ; 8 cycles ldy scanline ; 3 cycles inner_scanline frame_offset, 128 ; 23-26 cycles before break, 12 cycles after audio_play_hi ; 12 cycles ldy VCOUNT ; 4 cycles bne each_scanline_pair ; 2 cycles ; Do bookkeeping during vblank! audio_inc ; 22-32 cycles ;ldy VCOUNT ; 4 cycles jmp each_frame ; 3 cycles .endscope .endmacro each_frame: run_frame1: run_frame 0 .endproc