chiptune: initial playing

This commit is contained in:
Vince Weaver 2018-02-17 00:26:58 -05:00
parent 59ff72b39a
commit 67cf2d7b4b
6 changed files with 276 additions and 105 deletions

View File

@ -0,0 +1,219 @@
; 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 AY-3-8910
;=======================
; register in X
; value in MB_VALUE
write_ay_both:
; address
stx MOCK_6522_ORA1 ; put address on PA
lda #MOCK_AY_LATCH_ADDR ; latch_address on PB
sta MOCK_6522_ORB1
lda #MOCK_AY_INACTIVE ; go inactive
sta MOCK_6522_ORB1
; value
lda MB_VALUE
sta MOCK_6522_ORA1 ; put value on PA
lda #MOCK_AY_WRITE ; write on PB
sta MOCK_6522_ORB1
lda #MOCK_AY_INACTIVE ; go inactive
sta MOCK_6522_ORB1
;=======================
; Write Left AY-3-8910
;=======================
;write_ay_left:
; address
stx MOCK_6522_ORA2 ; put address on PA
lda #MOCK_AY_LATCH_ADDR ; latch_address on PB
sta MOCK_6522_ORB2
lda #MOCK_AY_INACTIVE ; go inactive
sta MOCK_6522_ORB2
; value
lda MB_VALUE
sta MOCK_6522_ORA2 ; put value on PA
lda #MOCK_AY_WRITE ; write on PB
sta MOCK_6522_ORB2
lda #MOCK_AY_INACTIVE ; go inactive
sta MOCK_6522_ORB2
rts
;=======================================
; 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

@ -5,8 +5,10 @@ PNG2GR = ../gr-utils/png2gr
all: mock_player.dsk
mock_player.dsk: CHIPTUNE_PLAYER
mock_player.dsk: CHIPTUNE_PLAYER OUT.0
$(DOS33) -y mock_player.dsk BSAVE -a 0x1000 CHIPTUNE_PLAYER
$(DOS33) -y mock_player.dsk BSAVE -a 0x4000 OUT.0
CHIPTUNE_PLAYER: chiptune_player.o
ld65 -o CHIPTUNE_PLAYER chiptune_player.o -C ../linker_scripts/apple2_1000.inc

BIN
chiptune_player/OUT.0 Normal file

Binary file not shown.

View File

@ -15,7 +15,7 @@
sta DONE_PLAYING
sta XPOS
lda #1
lda #0
sta MB_FRAME_DIFF
lda #<mocking_message ; load loading message
@ -50,23 +50,19 @@ mockingboard_found:
;============================
jsr mockingboard_init
jsr reset_ay_left
jsr reset_ay_right
jsr clear_ay_left
jsr clear_ay_right
jsr reset_ay_both
jsr clear_ay_both
;===========================
; load pointer to the music
; init pointer to the music
;===========================
; lda #<ksp_theme
; sta INL
; lda #>ksp_theme
; sta INH
; lda (INL),Y ; read in frame delay
; sta MB_FRAME_DIFF
; inc INL ; FIXME: should check if we oflowed
lda #$40
sta INH
lda #$00
sta INL
lda #$0
sta MB_CHUNK
;=========================
; Setup Interrupt Handler
@ -105,7 +101,7 @@ mockingboard_found:
; Enable 6502 interrupts
;============================
;
; cli ; clear interrupt mask
cli ; clear interrupt mask
;============================
@ -147,8 +143,7 @@ done_play:
sei ; disable interrupts
jsr clear_ay_left
jsr clear_ay_right
jsr clear_ay_both
lda #0
sta CH
@ -179,100 +174,53 @@ interrupt_handler:
pha ; save A
; Should we save X and Y too?
inc $0404 ; debug
bit $C404 ; can clear 6522 interrupt by reading T1C-L
inc $0401 ; DEBUG: increment text char
ldy MB_FRAME_DIFF
dec MB_FRAME_DIFF
bne done_interrupt
ldx #0
mb_write_loop:
lda (INL),y
cpx #13
bne mb_not_13
cmp #$ff
beq skip_r13
ldy #0
bottom_regs:
lda (INL),Y ; load low reg bitmask
sta MASK
ldx #$ff ; init to -1
bottom_regs_loop:
inx ; increment X
cpx #$8 ; if we reach 8, done
beq top_regs ; move on to top
ror MASK
bcc bottom_regs_loop ; if bit not set in mask, skip reg
stx XX ; save X
iny ; get next output value
lda (INL),Y ; read in value
sty YY ; save Y
tax ; value in X
ldy XX ; register# in Y
; reg in Y, value in X
jsr write_ay_left ; assume 3 channel (not six)
jsr write_ay_right ; so write same to both left/write
ldx XX ; restore X
ldy YY ; restore Y
jmp bottom_regs_loop ; loop
top_regs:
iny ; point to next value
lda (INL),Y ; load top reg bitmask
sta MASK
ldx #$7 ; load X as 7 (we increment first)
top_regs_loop:
inx ; increment
cpx #16
beq done_with_masks ; exit if done
ror MASK
bcc top_regs_loop ; loop if not set
stx XX ; save X value
iny ; point to value
lda (INL),Y ; read in output value
sty YY ; save Y value
tax ; value in X
ldy XX ; register in Y
; reg in Y, value in X
jsr write_ay_left ; assume 3 channel (not six)
jsr write_ay_right ; so write same to both left/write
ldx XX
ldy YY
jmp top_regs_loop
done_with_masks:
new_frame_diff:
iny
lda (INL),Y ; read in frame delay
cmp #$ff ; see if end
bne not_done ; if so, done
inc DONE_PLAYING ; set done playing flag
jmp done_interrupt
not_done:
sta MB_FRAME_DIFF
iny
mb_not_13:
sta MB_VALUE
; INLINE?
jsr write_ay_both ; assume 3 channel (not six)
; so write same to both left/write
clc
tya
adc INL
sta INL
lda #0
adc INH
lda INH
adc #$4
sta INH
inx
cpx #14
bmi mb_write_loop
skip_r13:
lda MB_CHUNK
clc
adc #$40
sta INH
inc MB_FRAME_DIFF
bne done_interrupt
wraparound:
inc MB_CHUNK
lda MB_CHUNK
cmp #$4
bne chunk_good
lda #0
sta MB_CHUNK
chunk_good:
done_interrupt:
pla
rti
@ -286,7 +234,7 @@ done_interrupt:
;=========
.include "../asm_routines/gr_offsets.s"
.include "../asm_routines/text_print.s"
.include "../asm_routines/mockingboard.s"
.include "../asm_routines/mockingboard_a.s"
.include "../asm_routines/lzss_decompress.s"
.include "../asm_routines/gr_fast_clear.s"
.include "../asm_routines/pageflip.s"

Binary file not shown.

View File

@ -100,6 +100,8 @@ KEY_OFFSET EQU $8D
DRAW_BLUE_SKY EQU $8E
RANDOM_POINTER EQU $8F
FRAME_COUNT EQU $90
MB_VALUE EQU $91
MB_CHUNK EQU $92
MB_ADDRL EQU $91
MB_ADDRH EQU $92
DONE_PLAYING EQU $93