pt3: add a timer module for tracking overhead

This commit is contained in:
Vince Weaver 2019-05-15 11:55:43 -04:00
parent f24e0b2572
commit 2161b5600b
5 changed files with 686 additions and 8 deletions

View File

@ -8,12 +8,13 @@ all: pt3_player.dsk
$(DOS33):
cd ../dos33fs-utils && make
pt3_player.dsk: PT3_PLAYER PT3_DUMPER HELLO DUMP
pt3_player.dsk: PT3_PLAYER PT3_DUMPER PT3_TIMER HELLO DUMP
cp empty.dsk pt3_player.dsk
$(DOS33) -y pt3_player.dsk SAVE A HELLO
$(DOS33) -y pt3_player.dsk SAVE A DUMP
$(DOS33) -y pt3_player.dsk BSAVE -a 0x1000 PT3_DUMPER
$(DOS33) -y pt3_player.dsk BSAVE -a 0x2000 PT3_PLAYER
$(DOS33) -y pt3_player.dsk BSAVE -a 0x2000 PT3_TIMER
$(DOS33) -y pt3_player.dsk BSAVE -a 0x4000 EA.PT3
$(DOS33) -y pt3_player.dsk BSAVE -a 0x4000 VC.PT3
$(DOS33) -y pt3_player.dsk BSAVE -a 0x4000 SR.PT3
@ -42,18 +43,26 @@ HELLO: hello.bas
DUMP: dump.bas
../asoft_basic-utils/tokenize_asoft < dump.bas > DUMP
#
PT3_PLAYER: pt3_player.o
ld65 -o PT3_PLAYER pt3_player.o -C ../linker_scripts/apple2_2000.inc
pt3_player.o: pt3_player.s \
gr_fast_clear.s pt3_lib.s interrupt_handler.s zp.inc
# mockingboard.s \
# keypress_minimal.s \
# qkumba_rts.s \
# rasterbars.s volume_bars.s interrupt_handler.s \
# song_list.inc chip_title.inc zp.inc chip_title_uncompressed.inc
ca65 -o pt3_player.o pt3_player.s -l pt3_player.lst
#
PT3_TIMER: pt3_timer.o
ld65 -o PT3_TIMER pt3_timer.o -C ../linker_scripts/apple2_2000.inc
pt3_timer.o: pt3_timer.s \
gr_fast_clear.s pt3_lib.s interrupt_handler.s zp.inc
ca65 -o pt3_timer.o pt3_timer.s -l pt3_timer.lst
#
PT3_DUMPER: pt3_dumper.o
ld65 -o PT3_DUMPER pt3_dumper.o -C ../linker_scripts/apple2_1000.inc
@ -65,5 +74,5 @@ pt3_dumper.o: pt3_dumper.s \
clean:
rm -f *~ TITLE.GR *.o *.lst \
PT3_PLAYER
PT3_PLAYER PT3_TIMER PT3_DUMPER

206
pt3_player/mockingboard_a.s Normal file
View File

@ -0,0 +1,206 @@
; Mockingboad programming:
; + Has two 6522 I/O chips connected to two AY-3-8910 chips
; + Optionally has some speech chips controlled via the outport on the AY
; + Often in slot 4
; TODO: how to auto-detect?
; References used:
; http://macgui.com/usenet/?group=2&id=8366
; 6522 Data Sheet
; AY-3-8910 Data Sheet
;========================
; Mockingboard card
; Essentially two 6522s hooked to the Apple II bus
; Connected to AY-3-8910 chips
; PA0-PA7 on 6522 connected to DA0-DA7 on AY
; PB0 on 6522 connected to BC1
; PB1 on 6522 connected to BDIR
; PB2 on 6522 connected to RESET
; left speaker
MOCK_6522_ORB1 EQU $C400 ; 6522 #1 port b data
MOCK_6522_ORA1 EQU $C401 ; 6522 #1 port a data
MOCK_6522_DDRB1 EQU $C402 ; 6522 #1 data direction port B
MOCK_6522_DDRA1 EQU $C403 ; 6522 #1 data direction port A
; right speaker
MOCK_6522_ORB2 EQU $C480 ; 6522 #2 port b data
MOCK_6522_ORA2 EQU $C481 ; 6522 #2 port a data
MOCK_6522_DDRB2 EQU $C482 ; 6522 #2 data direction port B
MOCK_6522_DDRA2 EQU $C483 ; 6522 #2 data direction port A
; AY-3-8910 commands on port B
; RESET BDIR BC1
MOCK_AY_RESET EQU $0 ; 0 0 0
MOCK_AY_INACTIVE EQU $4 ; 1 0 0
MOCK_AY_READ EQU $5 ; 1 0 1
MOCK_AY_WRITE EQU $6 ; 1 1 0
MOCK_AY_LATCH_ADDR EQU $7 ; 1 1 1
;========================
; Mockingboard Init
;========================
; Initialize the 6522s
; set the data direction for all pins of PortA/PortB to be output
mockingboard_init:
lda #$ff ; all output (1)
sta MOCK_6522_DDRB1
sta MOCK_6522_DDRA1
sta MOCK_6522_DDRB2
sta MOCK_6522_DDRA2
rts
;======================
; Reset Left AY-3-8910
;======================
reset_ay_both:
lda #MOCK_AY_RESET
sta MOCK_6522_ORB1
lda #MOCK_AY_INACTIVE
sta MOCK_6522_ORB1
;======================
; Reset Right AY-3-8910
;======================
;reset_ay_right:
lda #MOCK_AY_RESET
sta MOCK_6522_ORB2
lda #MOCK_AY_INACTIVE
sta MOCK_6522_ORB2
rts
; Write sequence
; Inactive -> Latch Address -> Inactive -> Write Data -> Inactive
;=========================================
; Write Right/Left to save value AY-3-8910
;=========================================
; register in X
; value in MB_VALUE
write_ay_both:
; address
stx MOCK_6522_ORA1 ; put address on PA1 ; 3
stx MOCK_6522_ORA2 ; put address on PA2 ; 3
lda #MOCK_AY_LATCH_ADDR ; latch_address on PB1 ; 2
sta MOCK_6522_ORB1 ; latch_address on PB1 ; 3
sta MOCK_6522_ORB2 ; latch_address on PB2 ; 3
lda #MOCK_AY_INACTIVE ; go inactive ; 2
sta MOCK_6522_ORB1 ; 3
sta MOCK_6522_ORB2 ; 3
; value
lda MB_VALUE ; 3
sta MOCK_6522_ORA1 ; put value on PA1 ; 3
sta MOCK_6522_ORA2 ; put value on PA2 ; 3
lda #MOCK_AY_WRITE ; ; 2
sta MOCK_6522_ORB1 ; write on PB1 ; 3
sta MOCK_6522_ORB2 ; write on PB2 ; 3
lda #MOCK_AY_INACTIVE ; go inactive ; 2
sta MOCK_6522_ORB1 ; 3
sta MOCK_6522_ORB2 ; 3
rts ; 6
;===========
; 53
;=======================================
; clear ay -- clear all 14 AY registers
; should silence the card
;=======================================
clear_ay_both:
ldx #14
lda #0
sta MB_VALUE
clear_ay_left_loop:
jsr write_ay_both
dex
bpl clear_ay_left_loop
rts
;=======================================
; Detect a Mockingboard card
;=======================================
; Based on code from the French Touch "Pure Noise" Demo
; Attempts to time an instruction sequence with a 6522
;
; If found, puts in bMB
; MB_ADDRL:MB_ADDRH has address of Mockingboard
; returns X=0 if not found, X=1 if found
mockingboard_detect:
lda #0
sta MB_ADDRL
mb_detect_loop: ; self-modifying
lda #$07 ; we start in slot 7 ($C7) and go down to 0 ($C0)
ora #$C0 ; make it start with C
sta MB_ADDRH
ldy #04 ; $CX04
ldx #02 ; 2 tries?
mb_check_cycle_loop:
lda (MB_ADDRL),Y ; timer 6522 (Low Order Counter)
; count down
sta TEMP ; 3 cycles
lda (MB_ADDRL),Y ; + 5 cycles = 8 cycles
; between the two accesses to the timer
sec
sbc TEMP ; subtract to see if we had 8 cycles
cmp #$f8 ; -8
bne mb_not_in_this_slot
dex ; decrement, try one more time
bne mb_check_cycle_loop ; loop detection
inx ; Mockingboard found (X=1)
done_mb_detect:
;stx bMB ; store result to bMB
rts ; return
mb_not_in_this_slot:
dec mb_detect_loop+1 ; decrement the "slot" (self_modify)
bne mb_detect_loop ; loop down to one
ldx #00
beq done_mb_detect
;=======================================
; Detect a Mockingboard card in Slot4
;=======================================
; Based on code from the French Touch "Pure Noise" Demo
; Attempts to time an instruction sequence with a 6522
;
; MB_ADDRL:MB_ADDRH has address of Mockingboard
; returns X=0 if not found, X=1 if found
mockingboard_detect_slot4:
lda #0
sta MB_ADDRL
mb4_detect_loop: ; self-modifying
lda #$04 ; we're only looking in Slot 4
ora #$C0 ; make it start with C
sta MB_ADDRH
ldy #04 ; $CX04
ldx #02 ; 2 tries?
mb4_check_cycle_loop:
lda (MB_ADDRL),Y ; timer 6522 (Low Order Counter)
; count down
sta TEMP ; 3 cycles
lda (MB_ADDRL),Y ; + 5 cycles = 8 cycles
; between the two accesses to the timer
sec
sbc TEMP ; subtract to see if we had 8 cycles
cmp #$f8 ; -8
bne mb4_not_in_this_slot
dex ; decrement, try one more time
bne mb4_check_cycle_loop ; loop detection
inx ; Mockingboard found (X=1)
done_mb4_detect:
rts ; return
mb4_not_in_this_slot:
ldx #00
beq done_mb4_detect

View File

@ -584,7 +584,7 @@ song_list:
;=========
.include "../asm_routines/gr_offsets.s"
.include "text_print.s"
.include "../asm_routines/mockingboard_a.s"
.include "mockingboard_a.s"
.include "gr_fast_clear.s"
.include "../asm_routines/pageflip.s"
;.include "../asm_routines/gr_unrle.s"

463
pt3_player/pt3_timer.s Normal file
View File

@ -0,0 +1,463 @@
; VMW Chiptune Player
.include "zp.inc"
PT3_LOC = $4000
NUM_FILES EQU 15
;=============================
; Setup
;=============================
pt3_setup:
jsr HOME
jsr TEXT
; Init disk code
jsr rts_init
; init variables
lda #0
sta DRAW_PAGE
sta DONE_PLAYING
sta WHICH_FILE
jsr mockingboard_detect_slot4 ; call detection routine
cpx #$1
beq mockingboard_found
mockingboard_found:
;============================
; Init the Mockingboard
;============================
jsr mockingboard_init
jsr reset_ay_both
jsr clear_ay_both
;=========================
; Setup Interrupt Handler
;=========================
; Vector address goes to 0x3fe/0x3ff
; FIXME: should chain any existing handler
lda #<interrupt_handler
sta $03fe
lda #>interrupt_handler
sta $03ff
;============================
; Enable 50Hz clock on 6522
;============================
sei ; disable interrupts just in case
lda #$40 ; Continuous interrupts, don't touch PB7
sta $C40B ; ACR register
lda #$7F ; clear all interrupt flags
sta $C40E ; IER register (interrupt enable)
lda #$C0
sta $C40D ; IFR: 1100, enable interrupt on timer one oflow
sta $C40E ; IER: 1100, enable timer one interrupt
lda #$E7
sta $C404 ; write into low-order latch
lda #$4f
sta $C405 ; write into high-order latch,
; load both values into counter
; clear interrupt and start counting
; 4fe7 / 1e6 = .020s, 50Hz
;==================
; load first song
;==================
jsr new_song
;============================
; Enable 6502 interrupts
;============================
cli ; clear interrupt mask
;============================
; Loop forever
;============================
main_loop:
; play song as fast as possible
jsr interrupt_simulator
check_done:
lda #$ff
bit DONE_PLAYING
beq main_loop ; if was all zeros, loop
; right pressed
done_play:
sei ; disable interrupts
; print results
jsr CROUT
jsr CROUT
lda timer_seconds_h
jsr PRBYTE
lda timer_seconds_l
jsr PRBYTE
lda #'.'+$80
jsr COUT
lda timer_fractions
jsr PRBYTE
jsr CROUT
jsr CROUT
lda #0
sta DONE_PLAYING
forever_loop:
jmp forever_loop
;=================
; load a new song
;=================
new_song:
;=========================
; Init Variables
;=========================
lda #$0
sta FRAME_COUNT
;===========================
; Load in PT3 file
;===========================
jsr get_filename
; needs to be space-padded $A0 30-byte filename
lda #<readfile_filename
sta namlo
lda #>readfile_filename
sta namhi
ldy #0
ldx #30 ; 30 chars
name_loop:
lda (INL),Y
beq space_loop
ora #$80
sta (namlo),Y
iny
dex
bne name_loop
beq done_name_loop
space_loop:
lda #$a0 ; pad with ' '
sta (namlo),Y
iny
dex
bne space_loop
done_name_loop:
; open and read a file
; loads to whatever it was BSAVED at (default is $2000)
jsr read_file ; read PT3 file from disk
;=========================
; Print Info
;=========================
; re-init, as we've run through it
lda #0
sta DONE_PLAYING
jsr pt3_init_song
rts
;==================
; Get filename
;==================
; WHICH_FILE holds number
; MAX_FILES has max
; Scroll through until find
; point INH:INL to it
get_filename:
ldy #0
ldx WHICH_FILE
lda #<song_list ; point to filename
sta INL
lda #>song_list
sta INH
get_filename_loop:
cpx #0
beq filename_found
inner_loop:
iny
lda (INL),Y
bne inner_loop
iny
dex
jmp get_filename_loop
filename_found:
tya
clc
adc INL
sta INL
lda INH
adc #0
sta INH
rts
;===============================
; Increment file we want to load
;===============================
increment_file:
inc WHICH_FILE
lda WHICH_FILE
cmp #NUM_FILES
bne done_increment
lda #0
sta WHICH_FILE
done_increment:
rts
;===============================
; Decrement file we want to load
;===============================
decrement_file:
dec WHICH_FILE
bpl done_decrement
lda #(NUM_FILES-1)
sta WHICH_FILE
done_decrement:
rts
;==========
; filenames
;==========
song_list:
.include "song_list.inc"
;=========
;routines
;=========
.include "mockingboard_a.s"
.include "qkumba_rts.s"
.include "pt3_lib.s"
TIME_OFFSET EQU 13
timer_fractions: .byte $0
timer_seconds_l: .byte $0
timer_seconds_h: .byte $0
;======================================
; simply time things with a 50Hz clock
interrupt_handler:
; pha ; save A ; 3
; A is saved in $45 by firmware
txa
pha ; save X
tya
pha ; save Y
bit $C404 ; clear 6522 interrupt by reading T1C-L ; 4
count_time:
inc timer_fractions
lda timer_fractions
cmp #50 ; 50Hz
bne done_count_time
lda #$0
sta timer_fractions
inc timer_seconds_l
bne done_count_time
inc timer_seconds_h
done_count_time:
exit_interrupt_handler:
; pla ; restore a ; 4
pla
tay ; restore Y
pla
tax ; restore X
lda $45 ; restore A
rti ; return from interrupt ; 6
interrupt_simulator:
; pha ; save A ; 3
; A is saved in $45 by firmware
txa
pha ; save X
tya
pha ; save Y
; bit $C404 ; clear 6522 interrupt by reading T1C-L ; 4
lda DONE_PLAYING ; 3
beq pt3_play_music ; if song done, don't play music ; 3/2nt
jmp check_keyboard ; 3
;============
; 13
pt3_play_music:
jsr pt3_make_frame
;======================================
; Write frames to Mockingboard
;======================================
; actually plays frame loaded at end of
; last interrupt, so 20ms behind?
mb_write_frame:
ldx #0 ; set up reg count ; 2
;============
; 2
;==================================
; loop through the 14 registers
; reading the value, then write out
;==================================
mb_write_loop:
lda REGISTER_DUMP,X ; load register value ; 4
; special case R13. If it is 0xff, then don't update
; otherwise might spuriously reset the envelope settings
cpx #13 ; 2
bne mb_not_13 ; 3/2nt
cmp #$ff ; 2
beq mb_skip_13 ; 3/2nt
;============
; typ 5
mb_not_13:
; address
stx MOCK_6522_ORA1 ; put address on PA1 ; 4
stx MOCK_6522_ORA2 ; put address on PA2 ; 4
lda #MOCK_AY_LATCH_ADDR ; latch_address for PB1 ; 2
sta MOCK_6522_ORB1 ; latch_address on PB1 ; 4
sta MOCK_6522_ORB2 ; latch_address on PB2 ; 4
lda #MOCK_AY_INACTIVE ; go inactive ; 2
sta MOCK_6522_ORB1 ; 4
sta MOCK_6522_ORB2 ; 4
; value
lda REGISTER_DUMP,X ; load register value ; 4
sta MOCK_6522_ORA1 ; put value on PA1 ; 4
sta MOCK_6522_ORA2 ; put value on PA2 ; 4
lda #MOCK_AY_WRITE ; ; 2
sta MOCK_6522_ORB1 ; write on PB1 ; 4
sta MOCK_6522_ORB2 ; write on PB2 ; 4
lda #MOCK_AY_INACTIVE ; go inactive ; 2
sta MOCK_6522_ORB1 ; 4
sta MOCK_6522_ORB2 ; 4
;===========
; 62
mb_no_write:
inx ; point to next register ; 2
cpx #14 ; if 14 we're done ; 2
bmi mb_write_loop ; otherwise, loop ; 3/2nt
;============
; 7
mb_skip_13:
;=================================
; Finally done with this interrupt
;=================================
done_interrupt:
update_time:
done_time:
check_keyboard:
jmp exit_interrupt
quiet_exit:
sta DONE_PLAYING
jsr clear_ay_both
exit_interrupt:
; pla ; restore a ; 4
pla
tay ; restore Y
pla
tax ; restore X
lda $45 ; restore A
rts ; return from interrupt ; 6