pt3_lib_ch6: some code trying to play 6 channels of pt3 music

This commit is contained in:
Vince Weaver 2024-01-17 23:48:51 -05:00
parent 981c3235d5
commit 644f4476af
20 changed files with 8298 additions and 0 deletions

View File

@ -0,0 +1,4 @@
This code is dual licensed under one of the following:
0BSD
GPL-2.0-only

View File

@ -0,0 +1,36 @@
include ../../Makefile.inc
DOS33 = ../../utils/dos33fs-utils/dos33
PNG2GR = ../../utils/gr-utils/png2gr
TOKENIZE = ../../utils/asoft_basic-utils/tokenize_asoft
EMPTY_DISK = ../../empty_disk
all: pt3_lib.dsk
$(DOS33):
cd ../../utils/dos33fs-utils && make
pt3_lib.dsk: PT3_TEST HELLO
cp $(EMPTY_DISK)/empty.dsk pt3_lib.dsk
$(DOS33) -y pt3_lib.dsk SAVE A HELLO
$(DOS33) -y pt3_lib.dsk BSAVE -a 0x1000 PT3_TEST
HELLO: hello.bas
$(TOKENIZE) < hello.bas > HELLO
#
PT3_TEST: pt3_test.o
ld65 -o PT3_TEST pt3_test.o -C ../../linker_scripts/apple2_1000.inc
pt3_test.o: pt3_test.s \
pt3_lib_detect_model.s \
pt3_lib_init.s pt3_lib_core.s pt3_lib_irq_handler.s \
pt3_lib_common.s pt3_lib_core2.s \
pt3_lib_mockingboard_detect.s pt3_lib_mockingboard_setup.s \
interrupt_handler.s zp.inc
ca65 -o pt3_test.o pt3_test.s -l pt3_test.lst
#
clean:
rm -f *~ *.o *.lst PT3_TEST HELLO

5
music/pt3_lib_6ch/NOTES Normal file
View File

@ -0,0 +1,5 @@
NOTE: really should have the frequency tables same.
Would you ever not?
remember to update version_smc code

11
music/pt3_lib_6ch/README Normal file
View File

@ -0,0 +1,11 @@
pt3_lib, but playing 6 channels (on both AY-3-8910) of the Mockingboard
Vortex Tracker supports this, essentially just playing two pt3 files at once
this is a hack, just duplicating all the player code.
It would probably be possible to share more common code.
Note: it assumes the songs you are playing are similar. While it would
be possible to set it up so they could use separate note tables,
it seems odd and we currently don't support it.

View File

@ -0,0 +1,91 @@
;; Firmware Routines and Hardware Locations
KEYPRESS = $C000
KEYRESET = $C010
;; SOFT SWITCHES
CLR80COL = $C000 ; PAGE0/PAGE1 normal
SET80COL = $C001 ; PAGE0/PAGE1 switches PAGE0 in Aux instead
EIGHTYCOL = $C00D
SPEAKER = $C030
SET_GR = $C050
SET_TEXT = $C051
FULLGR = $C052
TEXTGR = $C053
PAGE0 = $C054
PAGE1 = $C055
LORES = $C056 ; Enable LORES graphics
HIRES = $C057 ; Enable HIRES graphics
AN3 = $C05E ; Annunciator 3
PADDLE_BUTTON0 = $C061
PADDL0 = $C064
PTRIG = $C070
;; BASIC ROUTINES
NORMAL = $F273
;; MONITOR ROUTINES
HLINE = $F819 ;; HLINE Y,$2C at A
VLINE = $F828 ;; VLINE A,$2D at Y
CLRSCR = $F832 ;; Clear low-res screen
CLRTOP = $F836 ;; clear only top of low-res screen
SETCOL = $F864 ;; COLOR=A
TEXT = $FB36
TABV = $FB5B ;; VTAB to A
BASCALC = $FBC1 ;;
VTAB = $FC22 ;; VTAB to CV
HOME = $FC58 ;; Clear the text screen
WAIT = $FCA8 ;; delay 1/2(26+27A+5A^2) us
SETINV = $FE80 ;; INVERSE
SETNORM = $FE84 ;; NORMAL
COUT = $FDED ;; output A to screen
COUT1 = $FDF0 ;; output A to screen
CROUT = $FD8E ;; send a RETURN
CROUT1 = $FD8B ;; send a RETURN and clear end of line
PRBYTE = $FDDA
PRHEX = $FDE3
;; Applesoft routines
HCLR = $F3F2
;HGR_PAGE = $E6
COLOR_BLACK = 0
COLOR_RED = 1
COLOR_DARKBLUE = 2
COLOR_PURPLE = 3
COLOR_DARKGREEN = 4
COLOR_GREY = 5
COLOR_MEDIUMBLUE = 6
COLOR_LIGHTBLUE = 7
COLOR_BROWN = 8
COLOR_ORANGE = 9
COLOR_GREY2 = 10
COLOR_PINK = 11
COLOR_LIGHTGREEN = 12
COLOR_YELLOW = 13
COLOR_AQUA = 14
COLOR_WHITE = 15
COLOR_BOTH_BLACK = $00
COLOR_BOTH_RED = $11
COLOR_BOTH_DARKBLUE = $22
COLOR_BOTH_DARKGREEN = $44
COLOR_BOTH_GREY = $55
COLOR_BOTH_MEDIUMBLUE = $66
COLOR_BOTH_LIGHTBLUE = $77
COLOR_BOTH_BROWN = $88
COLOR_BOTH_ORANGE = $99
COLOR_BOTH_PINK = $BB
COLOR_BOTH_LIGHTGREEN = $CC
COLOR_BOTH_YELLOW = $DD
COLOR_BOTH_AQUA = $EE
COLOR_BOTH_WHITE = $FF
AUX_BOTH_MEDIUMBLUE = $33 ; 0011 0011
AUX_BOTH_GREY = $AA ; 1010 1010

View File

@ -0,0 +1,2 @@
10 PRINT "PT3 LIB TEST V0.5"
100 PRINT CHR$ (4)"BRUN PT3_TEST"

View File

@ -0,0 +1,89 @@
;================================
;================================
; mockingboard interrupt handler
;================================
;================================
; On Apple II/6502 the interrupt handler jumps to address in 0xfffe
; This is in the ROM, which saves the registers
; on older IIe it saved A to $45 (which could mess with DISK II)
; newer IIe doesn't do that.
; It then calculates if it is a BRK or not (which trashes A)
; Then it sets up the stack like an interrupt and calls 0x3fe
; Note: the IIc is much more complicated
; its firmware tries to decode the proper source
; based on various things, including screen hole values
; we bypass that by switching out ROM and replacing the
; $fffe vector with this, but that does mean we have
; to be sure status flag and accumulator set properly
interrupt_handler:
php ; save status flags
cld ; clear decimal mode
pha ; save A ; 3
; A is saved in $45 by firmware
txa
pha ; save X
tya
pha ; save Y
; inc $0404 ; debug (flashes char onscreen)
.include "pt3_lib_irq_handler.s"
jmp exit_interrupt
;=================================
; Finally done with this interrupt
;=================================
quiet_exit:
stx DONE_PLAYING
jsr clear_ay_both
ldx #$ff ; also mute the channel
stx AY_REGISTERS_2+7 ; just in case
.if 0
stx AY_REGISTERS+7 ; just in case
.endif
exit_interrupt:
pla
tay ; restore Y
pla
tax ; restore X
pla ; restore a ; 4
; on II+/IIe (but not IIc) we need to do this?
interrupt_smc:
lda $45 ; restore A
plp
rti ; return from interrupt ; 6
;============
; typical
; ???? cycles
;=============================
; Disable Interrupt
;=============================
; disables all the 6522 timer interrupts
mockingboard_disable_interrupt:
sei ; disable interrupts just in case
lda #$40 ; Continuous interrupts, don't touch PB7
disable_irq_smc1:
sta MOCK_6522_ACR ; ACR register
lda #$7F ; clear all interrupt flags
disable_irq_smc2:
sta MOCK_6522_IER ; IER register (interrupt enable)
rts

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,98 @@
;===========================
; Check Apple II model
;===========================
; this is mostly for IIc and IIgs support
; as they do interrupts differently
; some of this info from the document:
; Apple II Family Identification Routines 2.2
;
; note the more obscure are not well tested
; Returns one of the following in A
; ' ' = Apple II
; '+' = Apple II+
; 'e' = Apple IIe
; 'c' = Apple IIc
; 'g' = Apple IIgs
; 'm' = mac L/C with board
; 'j' = jplus
; '3' = Apple III
detect_appleii_model:
lda #' '
ldx $FBB3
; II is $38
; J-plus is $C9
; II+ is $EA (so is III)
; IIe and newer is $06
cpx #$38 ; ii
beq done_apple_detect
; ii+ is EA FB1E=AD
; iii is EA FB1E=8A 00
cpx #$EA
bne not_ii_iii
ii_or_iii:
lda #'+' ; ii+/iii
ldx $FB1E
cpx #$AD
beq done_apple_detect ; ii+
lda #'3'
bne done_apple_detect ; bra iii
not_ii_iii:
lda #'j' ; jplus
cpx #$C9
beq done_apple_detect
cpx #$06
bne done_apple_detect
apple_iie_or_newer:
ldx $FBC0 ; $EA on a IIe
; $E0 on a IIe enhanced
; $00 on a IIc/IIc+
; $FE1F = $60, IIgs
beq apple_iic
lda #'e'
cpx #$EA
beq done_apple_detect
; cpx #$E0
; beq done_apple_detect
; should do something if not $E0
; GS and IIe enhanced are the same, need to check
sec ; set carry
jsr $FE1F
bcs done_apple_detect ;If carry then IIe enhanced
; get here we're a IIgs?
lda #'g'
bne done_apple_detect
apple_iic:
lda #'c'
done_apple_detect:
sta APPLEII_MODEL
rts

View File

@ -0,0 +1,15 @@
; On Apple IIgs the interrupt handler clears the
; Language Card settings
; If you have your player in the Language card area ($D000-$FFFF)
; You will need to have this code elsewhere, and jump through
; this to the actual handler
gs_interrupt_handler:
; swap back in language card
; read/write RAM, use $d000 bank1
bit $C083
bit $C083
jmp interrupt_handler

View File

@ -0,0 +1,644 @@
; pt3_lib_init.s
; Initialize a song
; this is done before song starts playing so it is not
; as performance / timing critical
;====================================
; pt3_init_song
;====================================
;
pt3_init_song:
lda #$0
sta DONE_SONG_2 ; 3
ldx #(end_vars_2-begin_vars_2)
zero_song_structs_loop_2:
dex
sta note_a_2,X
bne zero_song_structs_loop_2
;=================
sta pt3_noise_period_smc_2+1 ; 4
sta pt3_noise_add_smc_2+1 ; 4
sta pt3_envelope_period_l_smc_2+1 ; 4
sta pt3_envelope_period_h_smc_2+1 ; 4
sta pt3_envelope_slide_l_smc_2+1 ; 4
sta pt3_envelope_slide_h_smc_2+1 ; 4
sta pt3_envelope_slide_add_l_smc_2+1 ; 4
sta pt3_envelope_slide_add_h_smc_2+1 ; 4
sta pt3_envelope_add_smc_2+1 ; 4
sta pt3_envelope_type_smc_2+1 ; 4
sta pt3_envelope_type_old_smc_2+1 ; 4
sta pt3_envelope_delay_smc_2+1 ; 4
sta pt3_envelope_delay_orig_smc_2+1 ; 4
sta PT3_MIXER_VAL_2 ; 3
sta current_pattern_smc_2+1 ; 4
sta current_line_smc_2+1 ; 4
sta current_subframe_smc_2+1 ; 4
lda #$f ; 2
sta note_a_2+NOTE_VOLUME ; 4
sta note_b_2+NOTE_VOLUME ; 4
sta note_c_2+NOTE_VOLUME ; 4
; default ornament/sample in A
; X is zero coming in here
;ldx #(NOTE_STRUCT_SIZE*0) ; 2
jsr load_ornament0_sample1_2 ; 6+93
; default ornament/sample in B
ldx #(NOTE_STRUCT_SIZE*1) ; 2
jsr load_ornament0_sample1_2 ; 6+93
; default ornament/sample in C
ldx #(NOTE_STRUCT_SIZE*2) ; 2
jsr load_ornament0_sample1_2 ; 6+93
;=======================
; load default speed
lda PT3_LOC_2+PT3_SPEED ; 4
sta pt3_speed_smc_2+1 ; 4
;=======================
; load loop
lda PT3_LOC_2+PT3_LOOP ; 4
sta pt3_loop_smc_2+1 ; 4
.if 0
;===============================
;
pt3_init_song_first:
lda #$0
sta DONE_SONG ; 3
ldx #(end_vars-begin_vars)
zero_song_structs_loop:
dex
sta note_a,X
bne zero_song_structs_loop
sta pt3_noise_period_smc+1 ; 4
sta pt3_noise_add_smc+1 ; 4
sta pt3_envelope_period_l_smc+1 ; 4
sta pt3_envelope_period_h_smc+1 ; 4
sta pt3_envelope_slide_l_smc+1 ; 4
sta pt3_envelope_slide_h_smc+1 ; 4
sta pt3_envelope_slide_add_l_smc+1 ; 4
sta pt3_envelope_slide_add_h_smc+1 ; 4
sta pt3_envelope_add_smc+1 ; 4
sta pt3_envelope_type_smc+1 ; 4
sta pt3_envelope_type_old_smc+1 ; 4
sta pt3_envelope_delay_smc+1 ; 4
sta pt3_envelope_delay_orig_smc+1 ; 4
sta PT3_MIXER_VAL ; 3
sta current_pattern_smc+1 ; 4
sta current_line_smc+1 ; 4
sta current_subframe_smc+1 ; 4
lda #$f ; 2
sta note_a+NOTE_VOLUME ; 4
sta note_b+NOTE_VOLUME ; 4
sta note_c+NOTE_VOLUME ; 4
; default ornament/sample in A
; X is zero coming in here
;ldx #(NOTE_STRUCT_SIZE*0) ; 2
jsr load_ornament0_sample1 ; 6+93
; default ornament/sample in B
ldx #(NOTE_STRUCT_SIZE*1) ; 2
jsr load_ornament0_sample1 ; 6+93
; default ornament/sample in C
ldx #(NOTE_STRUCT_SIZE*2) ; 2
jsr load_ornament0_sample1 ; 6+93
;=======================
; load default speed
lda PT3_LOC+PT3_SPEED ; 4
sta pt3_speed_smc+1 ; 4
;=======================
; load loop
lda PT3_LOC+PT3_LOOP ; 4
sta pt3_loop_smc+1 ; 4
.endif
;========================
;========================
; set up note/freq table
; this saves some space and makes things marginally faster longrun
;========================
;========================
; note (heh) that there are separate tables if version 3.3
; but we are going to assume we are only going to be playing
; newer 3.4+ version files so only need the newer tables
ldx PT3_LOC_2+PT3_HEADER_FREQUENCY ; 4
beq use_freq_table_0
dex
beq use_freq_table_1
dex
beq use_freq_table_2
; fallthrough (freq table 3)
use_freq_table_3:
;=================================================
; Create Table #3, v4+, "PT3NoteTable_REAL_34_35"
;=================================================
ldy #11 ; !2
freq_table_3_copy_loop:
; note, high lookup almost same as 2v4, just need to adjust one value
lda base2_v4_high,Y ; !3
sta NoteTable_high,Y ; !3
lda base3_low,Y ; !3
sta NoteTable_low,Y ; !3
dey ; !1
bpl freq_table_3_copy_loop ; !2
dec NoteTable_high ; adjust to right value
jsr NoteTablePropogate ; !3
lda #<table3_v4_adjust
sta note_table_adjust_smc+1
lda #>table3_v4_adjust
sta note_table_adjust_smc+2
jsr NoteTableAdjust
jmp done_set_freq_table
use_freq_table_2:
;=================================================
; Create Table #2, v4+, "PT3NoteTable_ASM_34_35"
;=================================================
ldy #11
freq_table_2_copy_loop:
lda base2_v4_high,Y
sta NoteTable_high,Y
lda base2_v4_low,Y
sta NoteTable_low,Y
dey
bpl freq_table_2_copy_loop
jsr NoteTablePropogate ; !3
lda #<table2_v4_adjust
sta note_table_adjust_smc+1
lda #>table2_v4_adjust
sta note_table_adjust_smc+2
jsr NoteTableAdjust
jmp done_set_freq_table
use_freq_table_1:
;=================================================
; Create Table #1, "PT3NoteTable_ST"
;=================================================
ldy #11
freq_table_1_copy_loop:
lda base1_high,Y
sta NoteTable_high,Y
lda base1_low,Y
sta NoteTable_low,Y
dey
bpl freq_table_1_copy_loop
jsr NoteTablePropogate ; !3
; last adjustments
lda #$FD ; Tone[23]=$3FD
sta NoteTable_low+23
dec NoteTable_low+46 ; Tone[46]-=1;
jmp done_set_freq_table
use_freq_table_0:
;=================================================
; Create Table #0, "PT3NoteTable_PT_34_35"
;=================================================
ldy #11
freq_table_0_copy_loop:
lda base0_v4_high,Y
sta NoteTable_high,Y
lda base0_v4_low,Y
sta NoteTable_low,Y
dey
bpl freq_table_0_copy_loop
jsr NoteTablePropogate ; !3
lda #<table0_v4_adjust
sta note_table_adjust_smc+1
lda #>table0_v4_adjust
sta note_table_adjust_smc+2
jsr NoteTableAdjust
done_set_freq_table:
;======================
; calculate version
ldx #6 ; 2
lda PT3_LOC_2+PT3_VERSION ; 4
sec ; 2
sbc #'0' ; 2
cmp #9 ; 2
bcs not_ascii_number ; bge ; 2/3
tax ; 2
not_ascii_number:
; adjust version<6 SMC code in the slide code
; FIXME: I am sure there's a more clever way to do this
lda #$2C ; BIT ; 2
cpx #$6 ; 2
bcs version_greater_than_or_equal_6 ; bgt ; 3
; less than 6, jump
; also carry is known to be clear
adc #$20 ; BIT->JMP 2C->4C ; 2
version_greater_than_or_equal_6:
sta version_smc_2 ; 4
pick_volume_table:
;=======================
; Pick which volume number, based on version
; if (PlParams.PT3.PT3_Version <= 4)
cpx #5 ; 2
; carry clear = 3.3/3.4 table
; carry set = 3.5 table
;==========================
; VolTableCreator
;==========================
; Creates the appropriate volume table
; based on z80 code by Ivan Roshin ZXAYHOBETA/VTII10bG.asm
;
; Called with carry==0 for 3.3/3.4 table
; Called with carry==1 for 3.5 table
; 177f-1932 = 435 bytes, not that much better than 512 of lookup
VolTableCreator:
; Init initial variables
lda #$0
sta z80_d_smc+1
ldy #$11
; Set up self modify
ldx #$2A ; ROL for self-modify
bcs vol_type_35
vol_type_33:
; For older table, we set initial conditions a bit
; different
dey
tya
ldx #$ea ; NOP for self modify
vol_type_35:
sty z80_l_smc+1 ; l=16 or 17
sta z80_e_smc+1 ; e=16 or 0
stx vol_smc ; set the self-modify code
ldy #16 ; skip first row, all zeros
ldx #16 ; c=16
vol_outer:
clc ; add HL,DE
z80_l_smc:
lda #$d1
z80_e_smc:
adc #$d1
sta z80_e_smc+1
lda #0
z80_d_smc:
adc #$d1
sta z80_d_smc+1 ; carry is important
; sbc hl,hl
lda #0
adc #$ff
eor #$ff
vol_write:
sta z80_h_smc+1
pha
vol_inner:
pla
pha
vol_smc:
nop ; nop or ROL depending
z80_h_smc:
lda #$d1
adc #$0 ; a=a+carry;
sta VolumeTable,Y
iny
pla ; add HL,DE
adc z80_e_smc+1
pha
lda z80_h_smc+1
adc z80_d_smc+1
sta z80_h_smc+1
inx ; inc C
txa ; a=c
and #$f
bne vol_inner
pla
lda z80_e_smc+1 ; a=e
cmp #$77
bne vol_m3
inc z80_e_smc+1
vol_m3:
txa ; a=c
bne vol_outer
vol_done:
rts
;=========================================
; copy note table seed to proper location
;=========================================
; faster inlined
;NoteTableCopy:
; ldy #11 ; !2
;note_table_copy_loop:
;ntc_smc1:
; lda base1_high,Y ; !3
; sta NoteTable_high,Y ; !3
;ntc_smc2:
; lda base1_low,Y ; !3
; sta NoteTable_low,Y ; !3
; dey ; !1
; bpl note_table_copy_loop ; !2
; rts ; !1
;==========================================
; propogate the freq down, dividing by two
;==========================================
NoteTablePropogate:
ldy #0
note_table_propogate_loop:
clc
lda NoteTable_high,Y
ror
sta NoteTable_high+12,Y
lda NoteTable_low,Y
ror
sta NoteTable_low+12,Y
iny
cpy #84
bne note_table_propogate_loop
rts
;================================================
; propogation isn't enough, various values
; are often off by one, so adjust using a bitmask
;================================================
NoteTableAdjust:
ldx #0
note_table_adjust_outer:
note_table_adjust_smc:
lda table0_v4_adjust,X
sta PT3_TEMP
; reset smc
lda #<NoteTable_low
sta ntl_smc+1
lda #>NoteTable_low
sta ntl_smc+2
ldy #7
note_table_adjust_inner:
ror PT3_TEMP
bcc note_table_skip_adjust
ntl_smc:
inc NoteTable_low,X
note_table_skip_adjust:
clc
lda #12
adc ntl_smc+1
sta ntl_smc+1
lda #0
adc ntl_smc+2 ; unnecessary if aligned
sta ntl_smc+2
skip_adjust_done:
dey
bpl note_table_adjust_inner
inx
cpx #12
bne note_table_adjust_outer
rts
;base0_v3_high:
;.byte $0C,$0B,$0A,$0A,$09,$09,$08,$08,$07,$07,$06,$06
;base0_v3_low:
;.byte $21,$73,$CE,$33,$A0,$16,$93,$18,$A4,$36,$CE,$6D
; note: same as base0_v3_high
base0_v4_high:
.byte $0C,$0B,$0A,$0A,$09,$09,$08,$08,$07,$07,$06,$06
base0_v4_low:
.byte $22,$73,$CF,$33,$A1,$17,$94,$19,$A4,$37,$CF,$6D
base1_high:
.byte $0E,$0E,$0D,$0C,$0B,$0B,$0A,$09,$09,$08,$08,$07
base1_low:
.byte $F8,$10,$60,$80,$D8,$28,$88,$F0,$60,$E0,$58,$E0
;base2_v3_high:
;.byte $0D,$0C,$0B,$0B,$0A,$09,$09,$08,$08,$07,$07,$07
;base2_v3_low:
;.byte $3E,$80,$CC,$22,$82,$EC,$5C,$D6,$58,$E0,$6E,$04
; note almost same as above
base2_v4_high:
.byte $0D,$0C,$0B,$0A,$0A,$09,$09,$08,$08,$07,$07,$06
base2_v4_low:
.byte $10,$55,$A4,$FC,$5F,$CA,$3D,$B8,$3B,$C5,$55,$EC
; note almost same as above
;base3_high:
;.byte $0C,$0C,$0B,$0A,$0A,$09,$09,$08,$08,$07,$07,$06
base3_low:
.byte $DA,$22,$73,$CF,$33,$A1,$17,$94,$19,$A4,$37,$CF
; Adjustment factors
table0_v4_adjust:
.byte $40,$e6,$9c,$66,$40,$2c,$20,$30,$48,$6c,$1c,$5a
table2_v4_adjust:
.byte $20,$a8,$40,$f8,$bc,$90,$78,$70,$74,$08,$2a,$50
table3_v4_adjust:
.byte $B4,$40,$e6,$9c,$66,$40,$2c,$20,$30,$48,$6c,$1c
; Table #1 of Pro Tracker 3.3x - 3.5x
;PT3NoteTable_ST_high:
;.byte $0E,$0E,$0D,$0C,$0B,$0B,$0A,$09
;.byte $09,$08,$08,$07,$07,$07,$06,$06
;.byte $05,$05,$05,$04,$04,$04,$04,$03
;.byte $03,$03,$03,$03,$02,$02,$02,$02
;.byte $02,$02,$02,$01,$01,$01,$01,$01
;.byte $01,$01,$01,$01,$01,$01,$01,$00
;.byte $00,$00,$00,$00,$00,$00,$00,$00
;.byte $00,$00,$00,$00,$00,$00,$00,$00
;.byte $00,$00,$00,$00,$00,$00,$00,$00
;.byte $00,$00,$00,$00,$00,$00,$00,$00
;.byte $00,$00,$00,$00,$00,$00,$00,$00
;.byte $00,$00,$00,$00,$00,$00,$00,$00
;PT3NoteTable_ST_low:
;.byte $F8,$10,$60,$80,$D8,$28,$88,$F0
;.byte $60,$E0,$58,$E0,$7C,$08,$B0,$40
;.byte $EC,$94,$44,$F8,$B0,$70,$2C,$FD
;.byte $BE,$84,$58,$20,$F6,$CA,$A2,$7C
;.byte $58,$38,$16,$F8,$DF,$C2,$AC,$90
;.byte $7B,$65,$51,$3E,$2C,$1C,$0A,$FC
;.byte $EF,$E1,$D6,$C8,$BD,$B2,$A8,$9F
;.byte $96,$8E,$85,$7E,$77,$70,$6B,$64
;.byte $5E,$59,$54,$4F,$4B,$47,$42,$3F
;.byte $3B,$38,$35,$32,$2F,$2C,$2A,$27
;.byte $25,$23,$21,$1F,$1D,$1C,$1A,$19
;.byte $17,$16,$15,$13,$12,$11,$10,$0F
; Table #2 of Pro Tracker 3.4x - 3.5x
;PT3NoteTable_ASM_34_35_high:
;.byte $0D,$0C,$0B,$0A,$0A,$09,$09,$08
;.byte $08,$07,$07,$06,$06,$06,$05,$05
;.byte $05,$04,$04,$04,$04,$03,$03,$03
;.byte $03,$03,$02,$02,$02,$02,$02,$02
;.byte $02,$01,$01,$01,$01,$01,$01,$01
;.byte $01,$01,$01,$01,$01,$00,$00,$00
;.byte $00,$00,$00,$00,$00,$00,$00,$00
;.byte $00,$00,$00,$00,$00,$00,$00,$00
;.byte $00,$00,$00,$00,$00,$00,$00,$00
;.byte $00,$00,$00,$00,$00,$00,$00,$00
;.byte $00,$00,$00,$00,$00,$00,$00,$00
;.byte $00,$00,$00,$00,$00,$00,$00,$00
;PT3NoteTable_ASM_34_35_low:
;.byte $10,$55,$A4,$FC,$5F,$CA,$3D,$B8
;.byte $3B,$C5,$55,$EC,$88,$2A,$D2,$7E
;.byte $2F,$E5,$9E,$5C,$1D,$E2,$AB,$76
;.byte $44,$15,$E9,$BF,$98,$72,$4F,$2E
;.byte $0F,$F1,$D5,$BB,$A2,$8B,$74,$60
;.byte $4C,$39,$28,$17,$07,$F9,$EB,$DD
;.byte $D1,$C5,$BA,$B0,$A6,$9D,$94,$8C
;.byte $84,$7C,$75,$6F,$69,$63,$5D,$58
;.byte $53,$4E,$4A,$46,$42,$3E,$3B,$37
;.byte $34,$31,$2F,$2C,$29,$27,$25,$23
;.byte $21,$1F,$1D,$1C,$1A,$19,$17,$16
;.byte $15,$14,$12,$11,$10,$0F,$0E,$0D
;PT3VolumeTable_33_34:
;.byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
;.byte $0,$0,$0,$0,$0,$0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1
;.byte $0,$0,$0,$0,$0,$0,$1,$1,$1,$1,$1,$2,$2,$2,$2,$2
;.byte $0,$0,$0,$0,$1,$1,$1,$1,$2,$2,$2,$2,$3,$3,$3,$3
;.byte $0,$0,$0,$0,$1,$1,$1,$2,$2,$2,$3,$3,$3,$4,$4,$4
;.byte $0,$0,$0,$1,$1,$1,$2,$2,$3,$3,$3,$4,$4,$4,$5,$5
;.byte $0,$0,$0,$1,$1,$2,$2,$3,$3,$3,$4,$4,$5,$5,$6,$6
;.byte $0,$0,$1,$1,$2,$2,$3,$3,$4,$4,$5,$5,$6,$6,$7,$7
;.byte $0,$0,$1,$1,$2,$2,$3,$3,$4,$5,$5,$6,$6,$7,$7,$8
;.byte $0,$0,$1,$1,$2,$3,$3,$4,$5,$5,$6,$6,$7,$8,$8,$9
;.byte $0,$0,$1,$2,$2,$3,$4,$4,$5,$6,$6,$7,$8,$8,$9,$A
;.byte $0,$0,$1,$2,$3,$3,$4,$5,$6,$6,$7,$8,$9,$9,$A,$B
;.byte $0,$0,$1,$2,$3,$4,$4,$5,$6,$7,$8,$8,$9,$A,$B,$C
;.byte $0,$0,$1,$2,$3,$4,$5,$6,$7,$7,$8,$9,$A,$B,$C,$D
;.byte $0,$0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$A,$B,$C,$D,$E
;.byte $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$A,$B,$C,$D,$E,$F
;PT3VolumeTable_35:
;.byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
;.byte $0,$0,$0,$0,$0,$0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1
;.byte $0,$0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$2,$2,$2,$2
;.byte $0,$0,$0,$1,$1,$1,$1,$1,$2,$2,$2,$2,$2,$3,$3,$3
;.byte $0,$0,$1,$1,$1,$1,$2,$2,$2,$2,$3,$3,$3,$3,$4,$4
;.byte $0,$0,$1,$1,$1,$2,$2,$2,$3,$3,$3,$4,$4,$4,$5,$5
;.byte $0,$0,$1,$1,$2,$2,$2,$3,$3,$4,$4,$4,$5,$5,$6,$6
;.byte $0,$0,$1,$1,$2,$2,$3,$3,$4,$4,$5,$5,$6,$6,$7,$7
;.byte $0,$1,$1,$2,$2,$3,$3,$4,$4,$5,$5,$6,$6,$7,$7,$8
;.byte $0,$1,$1,$2,$2,$3,$4,$4,$5,$5,$6,$7,$7,$8,$8,$9
;.byte $0,$1,$1,$2,$3,$3,$4,$5,$5,$6,$7,$7,$8,$9,$9,$A
;.byte $0,$1,$1,$2,$3,$4,$4,$5,$6,$7,$7,$8,$9,$A,$A,$B
;.byte $0,$1,$2,$2,$3,$4,$5,$6,$6,$7,$8,$9,$A,$A,$B,$C
;.byte $0,$1,$2,$3,$3,$4,$5,$6,$7,$8,$9,$A,$A,$B,$C,$D
;.byte $0,$1,$2,$3,$4,$5,$6,$7,$7,$8,$9,$A,$B,$C,$D,$E
;.byte $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$A,$B,$C,$D,$E,$F

View File

@ -0,0 +1,197 @@
pt3_irq_handler:
pt3_irq_smc1:
bit MOCK_6522_T1CL ; clear 6522 interrupt by reading T1C-L ; 4
lda DONE_PLAYING ; 3
beq pt3_play_music_2 ; if song done, don't play music ; 3/2nt
jmp done_pt3_irq_handler ; 3
;============
; 13
pt3_play_music_2:
; decode a frame of music
jsr pt3_make_frame_2
; handle song over condition
lda DONE_SONG_2
beq pt3_play_music ; if not done, continue
lda LOOP_2 ; see if looping
beq pt3_play_music
pt3_loop_smc_2:
lda #$d1 ; looping, move to loop location
; non-zero to avoid the temptation
; to merge with following lda #$0
sta current_pattern_smc_2+1
lda #$0
sta current_line_smc_2+1
sta current_subframe_smc_2+1
sta DONE_SONG_2 ; undo the next song
; beq done_pt3_irq_handler_2 ; branch always
beq pt3_play_music_2 ; branch always
;move_to_next:
; ; same as "press right"
; ldx #$20
; jmp quiet_exit
pt3_play_music:
.if 0
jsr pt3_make_frame
; handle song over condition
lda DONE_SONG
beq mb_write_frame ; if not done, continue
lda LOOP ; see if looping
beq move_to_next
pt3_loop_smc:
lda #$d1 ; looping, move to loop location
; non-zero to avoid the temptation
; to merge with following lda #$0
sta current_pattern_smc+1
lda #$0
sta current_line_smc+1
sta current_subframe_smc+1
sta DONE_SONG ; undo the next song
; beq done_pt3_irq_handler ; branch always
beq pt3_play_music ; branch always
move_to_next:
; same as "press right"
ldx #$20
jmp quiet_exit
.endif
;======================================
; Write frames to Mockingboard
;======================================
; for speed could merge this into
; the decode code
mb_write_frame:
.if 0
tax ; set up reg count ; 2
;============
; 2
;==================================
; loop through the 14 registers
; reading the value, then write out
;==================================
mb_write_loop:
lda AY_REGISTERS,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
pt3_irq_smc2:
stx MOCK_6522_ORA1 ; put address on PA1 ; 4
ldy #MOCK_AY_LATCH_ADDR ; latch_address for PB1 ; 2
pt3_irq_smc3:
sty MOCK_6522_ORB1 ; latch_address on PB1 ; 4
ldy #MOCK_AY_INACTIVE ; go inactive ; 2
pt3_irq_smc4:
sty MOCK_6522_ORB1 ; 4
; value
pt3_irq_smc5:
sta MOCK_6522_ORA1 ; put value on PA1 ; 4
lda #MOCK_AY_WRITE ; ; 2
pt3_irq_smc6:
sta MOCK_6522_ORB1 ; write on PB1 ; 4
sty MOCK_6522_ORB1 ; 4
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:
.endif
;==================================
; do 6522 #2
;==================================
mb_write_frame_2:
ldx #0
mb_write_loop_2:
lda AY_REGISTERS_2,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_2 ; 3/2nt
cmp #$ff ; 2
beq mb_skip_13_2 ; 3/2nt
;============
; typ 5
mb_not_13_2:
; address
pt3_irq_smc2_2:
stx MOCK_6522_ORA2 ; put address on PA2 ; 4
ldy #MOCK_AY_LATCH_ADDR ; latch_address for PB1 ; 2
pt3_irq_smc3_2:
sty MOCK_6522_ORB2 ; latch_address on PB2 ; 4
ldy #MOCK_AY_INACTIVE ; go inactive ; 2
pt3_irq_smc4_2:
sty MOCK_6522_ORB2 ; 4
; value
pt3_irq_smc5_2:
sta MOCK_6522_ORA2 ; put value on PA2 ; 4
lda #MOCK_AY_WRITE ; ; 2
pt3_irq_smc7:
sta MOCK_6522_ORB2 ; write on PB2 ; 4
sty MOCK_6522_ORB2 ; 4
mb_no_write_2:
inx ; point to next register ; 2
cpx #14 ; if 14 we're done ; 2
bmi mb_write_loop_2 ; otherwise, loop ; 3/2nt
;============
; 7
mb_skip_13_2:
;=================================
; Finally done with this interrupt
;=================================
done_pt3_irq_handler:

View File

@ -0,0 +1,340 @@
;===================================================================
; code to detect mockingboard
;===================================================================
; this isn't always easy
; my inclination is to just assume slot #4 but that isn't always realistic
; code below based on "hw.mockingboard.a" from "Total Replay"
;license:MIT
; By Andrew Roughan
; in the style of 4am for Total Replay
;
; Mockingboard support functions
;
;------------------------------------------------------------------------------
; HasMockingboard
; detect Mockingboard card by searching for 6522 timers across all slots
; access 6522 timers with deterministic cycle counts
;
; based on prior art in Mockingboard Developers Toolkit
; with optimisation from deater/french touch
; also takes into account FastChip //e clock difference
;
; in: none
; accelerators should be off
; out: C set if Mockingboard found in any slot
; if card was found, X = #$Cn where n is the slot number of the card
; C clear if no Mockingboard found
; other flags clobbered
; A/Y clobbered
;------------------------------------------------------------------------------
mockingboard_detect:
; activate Mockingboard IIc
; + the Mockingboard has to take over Slot#4 (IIc has no slots)
; in theory any write to the firmware area in $C400 will
; activate it, but that might not be fast enough when detecting
; so writing $FF to $C403/$C404 is official way to enable
; + Note this disables permanently the mouse firmware in $C400
; so "normal" interrupts are broken :( The hack to fix things
; is to switch in RAM for $F000 and just replace the IRQ
; vectors at $FFFE/$FFFF instead of $3FE/$3FF but that makes
; it difficult if you actually wanted to use any
; Applesoft/Monitor ROM routines
.ifdef PT3_ENABLE_APPLE_IIC
lda APPLEII_MODEL
cmp #'c'
bne not_iic
lda #$ff
; don't bother patching these, IIc mockingboard always slot 4
sta MOCK_6522_DDRA1 ; $C403
sta MOCK_6522_T1CL ; $C404
.endif
not_iic:
lda #$00
sta MB_ADDR_L
ldx #$C7 ; start at slot #7
mb_slot_loop:
stx MB_ADDR_H
ldy #$04 ; 6522 #1 $Cx04
jsr mb_timer_check
bne mb_next_slot
ldy #$84 ; 6522 #2 $Cx84
jsr mb_timer_check
bne mb_next_slot
mb_found:
sec ; found
rts
mb_next_slot:
dex
cpx #$C0
bne mb_slot_loop
clc ; not found
rts
mb_timer_check:
lda (MB_ADDR_L),Y ; read 6522 timer low byte
sta MB_VALUE
lda (MB_ADDR_L),Y ; second time
sec
sbc MB_VALUE
cmp #$F8 ; looking for (-)8 cycles between reads
beq mb_timer_check_done
cmp #$F7 ; FastChip //e clock is different
mb_timer_check_done:
rts
;===================================================================
; code to patch mockingboard if not in slot#4
;===================================================================
; this is the brute force version, we have to patch 39 locations
; see further below if you want to try a smaller, more dangerous, patch
.if 0
mockingboard_patch:
lda MB_ADDR_H
sta pt3_irq_smc1+2 ; 1
sta pt3_irq_smc2+2 ; 2
sta pt3_irq_smc2+5 ; 3
sta pt3_irq_smc3+2 ; 4
sta pt3_irq_smc3+5 ; 5
sta pt3_irq_smc4+2 ; 6
sta pt3_irq_smc4+5 ; 7
sta pt3_irq_smc5+2 ; 8
sta pt3_irq_smc5+5 ; 9
sta pt3_irq_smc6+2 ; 10
sta pt3_irq_smc6+5 ; 11
sta pt3_irq_smc7+2 ; 12
sta pt3_irq_smc7+5 ; 13
sta mock_init_smc1+2 ; 14
sta mock_init_smc1+5 ; 15
sta mock_init_smc2+2 ; 16
sta mock_init_smc2+5 ; 17
sta reset_ay_smc1+2 ; 18
sta reset_ay_smc2+2 ; 19
sta reset_ay_smc3+2 ; 20
sta reset_ay_smc4+2 ; 21
sta write_ay_smc1+2 ; 22
sta write_ay_smc1+5 ; 23
sta write_ay_smc2+2 ; 24
sta write_ay_smc2+5 ; 25
sta write_ay_smc3+2 ; 26
sta write_ay_smc3+5 ; 27
sta write_ay_smc4+2 ; 28
sta write_ay_smc4+5 ; 29
sta write_ay_smc5+2 ; 30
sta write_ay_smc5+5 ; 31
sta write_ay_smc6+2 ; 32
sta write_ay_smc6+5 ; 33
sta setup_irq_smc1+2 ; 34
sta setup_irq_smc2+2 ; 35
sta setup_irq_smc3+2 ; 36
sta setup_irq_smc4+2 ; 37
sta setup_irq_smc5+2 ; 38
sta setup_irq_smc6+2 ; 39
rts
.endif
;===================================================================
; dangerous code to patch mockingboard if not in slot#4
;===================================================================
; this code patches any $C4 value to the proper slot# if not slot4
; this can be dangerous, it might over-write other important values
; that should be $C4
; safer ways to do this:
; only do this if 2 bytes after a LDA/STA/LDX/STX
; count total and if not 39 then print error message
mockingboard_patch:
; from mockingboard_init $1BBF
; to done_pt3_irq_handler $1D85
ldx MB_ADDR_H
ldy #0
lda #<mockingboard_init
sta MB_ADDR_L
lda #>mockingboard_init
sta MB_ADDR_H
mb_patch_loop:
lda (MB_ADDR_L),Y
cmp #$C4
bne mb_patch_nomatch
txa
sta (MB_ADDR_L),Y
mb_patch_nomatch:
inc MB_ADDR_L
lda MB_ADDR_L
bne mb_patch_oflo
inc MB_ADDR_H
mb_patch_oflo:
lda MB_ADDR_H
cmp #>done_pt3_irq_handler
bne mb_patch_loop
lda MB_ADDR_L
cmp #<done_pt3_irq_handler
bne mb_patch_loop
mb_patch_done:
rts
.if 0
;=======================================
; 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 PT3_TEMP ; 3 cycles
lda (MB_ADDRL),Y ; + 5 cycles = 8 cycles
; between the two accesses to the timer
sec
sbc PT3_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
;alternative MB detection from Nox Archaist
; lda #$04
; sta MB_ADDRL
; ldx #$c7
;
;find_mb:
; stx MB_ADDRH
;
; ;detect sound I
;
; sec
; ldy #$00
; lda (MB_ADDRL), y
; sbc (MB_ADDRL), y
; cmp #$05
; beq found_mb
; dex
; cpx #$c0
; bne find_mb
; ldx #$00 ;no mockingboard found
; rts
;
;found_mb:
; ldx #$01 ;mockingboard found
; rts
;
; ;optionally detect sound II
;
; sec
; ldy #$80
; lda (MB_ADDRL), y
; sbc (MB_ADDRL), y
; cmp #$05
; beq found_mb
;=======================================
; 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 PT3_TEMP ; 3 cycles
lda (MB_ADDRL),Y ; + 5 cycles = 8 cycles
; between the two accesses to the timer
sec
sbc PT3_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
.endif

View File

@ -0,0 +1,326 @@
; 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. We autodetect and patch
; 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 = $C400 ; 6522 #1 port b data
MOCK_6522_ORA1 = $C401 ; 6522 #1 port a data
MOCK_6522_DDRB1 = $C402 ; 6522 #1 data direction port B
MOCK_6522_DDRA1 = $C403 ; 6522 #1 data direction port A
MOCK_6522_T1CL = $C404 ; 6522 #1 t1 low order latches
MOCK_6522_T1CH = $C405 ; 6522 #1 t1 high order counter
MOCK_6522_T1LL = $C406 ; 6522 #1 t1 low order latches
MOCK_6522_T1LH = $C407 ; 6522 #1 t1 high order latches
MOCK_6522_T2CL = $C408 ; 6522 #1 t2 low order latches
MOCK_6522_T2CH = $C409 ; 6522 #1 t2 high order counters
MOCK_6522_SR = $C40A ; 6522 #1 shift register
MOCK_6522_ACR = $C40B ; 6522 #1 auxilliary control register
MOCK_6522_PCR = $C40C ; 6522 #1 peripheral control register
MOCK_6522_IFR = $C40D ; 6522 #1 interrupt flag register
MOCK_6522_IER = $C40E ; 6522 #1 interrupt enable register
MOCK_6522_ORANH = $C40F ; 6522 #1 port a data no handshake
; right speaker
MOCK_6522_ORB2 = $C480 ; 6522 #2 port b data
MOCK_6522_ORA2 = $C481 ; 6522 #2 port a data
MOCK_6522_DDRB2 = $C482 ; 6522 #2 data direction port B
MOCK_6522_DDRA2 = $C483 ; 6522 #2 data direction port A
; AY-3-8910 commands on port B
; RESET BDIR BC1
MOCK_AY_RESET = $0 ; 0 0 0
MOCK_AY_INACTIVE = $4 ; 1 0 0
MOCK_AY_READ = $5 ; 1 0 1
MOCK_AY_WRITE = $6 ; 1 1 0
MOCK_AY_LATCH_ADDR = $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)
mock_init_smc1:
sta MOCK_6522_DDRB1
sta MOCK_6522_DDRA1
mock_init_smc2:
sta MOCK_6522_DDRB2
sta MOCK_6522_DDRA2
rts
;===================================
;===================================
; Reset Both AY-3-8910s
;===================================
;===================================
;======================
; Reset Left AY-3-8910
;======================
reset_ay_both:
lda #MOCK_AY_RESET
reset_ay_smc1:
sta MOCK_6522_ORB1
lda #MOCK_AY_INACTIVE
reset_ay_smc2:
sta MOCK_6522_ORB1
;======================
; Reset Right AY-3-8910
;======================
;reset_ay_right:
;could be merged with both
lda #MOCK_AY_RESET
reset_ay_smc3:
sta MOCK_6522_ORB2
lda #MOCK_AY_INACTIVE
reset_ay_smc4:
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
write_ay_smc1:
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 on PB1 ; 2
write_ay_smc2:
sta MOCK_6522_ORB1 ; latch_address on PB1 ; 4
sta MOCK_6522_ORB2 ; latch_address on PB2 ; 4
ldy #MOCK_AY_INACTIVE ; go inactive ; 2
write_ay_smc3:
sty MOCK_6522_ORB1 ; 4
sty MOCK_6522_ORB2 ; 4
;===========
; 28
; value
lda MB_VALUE ; 3
write_ay_smc4:
sta MOCK_6522_ORA1 ; put value on PA1 ; 4
sta MOCK_6522_ORA2 ; put value on PA2 ; 4
lda #MOCK_AY_WRITE ; ; 2
write_ay_smc5:
sta MOCK_6522_ORB1 ; write on PB1 ; 4
sta MOCK_6522_ORB2 ; write on PB2 ; 4
write_ay_smc6:
sty MOCK_6522_ORB1 ; 4
sty MOCK_6522_ORB2 ; 4
;===========
; 29
rts ; 6
;===========
; 63
write_ay_both_end:
;.assert >write_ay_both = >write_ay_both_end, error, "write_ay_both crosses page"
;=======================================
; clear ay -- clear all 14 AY registers
; should silence the card
;=======================================
; 7+(74*14)+5=1048
clear_ay_both:
ldx #13 ; 2
lda #0 ; 2
sta MB_VALUE ; 3
clear_ay_left_loop:
jsr write_ay_both ; 6+63
dex ; 2
bpl clear_ay_left_loop ; 3
; -1
rts ; 6
;=======================================
; mute AY -- just turn off all 3 channels
; should silence the card
;
;=======================================
mute_ay_both:
ldx #7 ;
lda #$FF ;
sta MB_VALUE ;
mute_ay_left_loop:
jsr write_ay_both ;
rts
;=======================================
; unmute AY
; restore to value we had before muting
;=======================================
unmute_ay_both:
ldx #7 ;
.if 0
lda ENABLE
.endif
lda ENABLE_2 ;
sta MB_VALUE ;
unmute_ay_left_loop:
jsr write_ay_both ;
rts ;
clear_ay_end:
;.assert >clear_ay_both = >clear_ay_end, error, "clear_ay_both crosses page"
;=============================
; Setup
;=============================
mockingboard_setup_interrupt:
.ifdef PT3_ENABLE_APPLE_IIC
lda APPLEII_MODEL
cmp #'c'
bne done_iic_hack
;==================================================
; On IIc we use a hack and swap RAM into the langauge
; card and replace the interrupt vectors
; (should we do this on IIe too? probably faster)
; This does mean you can't use any ROM routines when
; playing music
;====================================================
; If we need the ROM routines we need to copy them
; first we have to copy the ROM to the language card
sei ; disable interrupts
lda $c08B ; read/write RAM1
lda $c08B ;
.ifdef PT3_ENABLE_IIC_COPY_ROM
copy_rom_loop:
lda $c089 ; read ROM, write RAM1
lda $c089
ldy #0
read_rom_loop:
lda $D000,Y
sta $400,Y ; note this uses text page as
; temporary data store
iny
bne read_rom_loop
lda $c08B ; read/write RAM1
lda $c08B ;
; should probably use $800 instead of $400
; as we over-write screen holes here
write_rom_loop:
lda $400,Y
sta $D000,Y
iny
bne write_rom_loop
inc read_rom_loop+2
inc write_rom_loop+5
bne copy_rom_loop
.endif
lda #<interrupt_handler
sta $fffe
lda #>interrupt_handler
sta $ffff
lda #$EA ; nop out the "lda $45" in the irq handler
; as it's not needed on IIc (and maybe others?)
sta interrupt_smc
sta interrupt_smc+1
.endif
done_iic_hack:
;=========================
; 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
;============================
; Note, on Apple II the clock isn't 1MHz but is actually closer to
; roughly 1.023MHz, and every 65th clock is stretched (it's complicated)
; 4fe7 / 1.023e6 = .020s, 50Hz
; 9c40 / 1.023e6 = .040s, 25Hz
; 411a / 1.023e6 = .016s, 60Hz
; French Touch uses
; 4e20 / 1.000e6 = .020s, 50Hz, which assumes 1MHz clock freq
sei ; disable interrupts just in case
lda #$40 ; Continuous interrupts, don't touch PB7
setup_irq_smc1:
sta MOCK_6522_ACR ; ACR register
lda #$7F ; clear all interrupt flags
setup_irq_smc2:
sta MOCK_6522_IER ; IER register (interrupt enable)
lda #$C0
setup_irq_smc3:
sta MOCK_6522_IFR ; IFR: 1100, enable interrupt on timer one oflow
setup_irq_smc4:
sta MOCK_6522_IER ; IER: 1100, enable timer one interrupt
lda #$E7
; lda #$20
setup_irq_smc5:
sta MOCK_6522_T1CL ; write into low-order latch
lda #$4f
; lda #$4E
setup_irq_smc6:
sta MOCK_6522_T1CH ; write into high-order latch,
; load both values into counter
; clear interrupt and start counting
rts

View File

@ -0,0 +1,271 @@
;=================
; VMW PT3_LIB test
;=================
; template for using the pt3_lib
; zero page definitions
.include "zp.inc"
; some firmware locations
.include "hardware.inc"
; Location the files load at.
; If you change this, you need to update the Makefile
;PT3_LOC = song
PT3_LOC_2 = song2
; the below will make for more compact code, at the expense
; of using $80 - $ff zero page addresses by the decoder.
; PT3_USE_ZERO_PAGE = 1
; Comment this to disable Apple IIc support which will free a small amount
; of room
PT3_ENABLE_APPLE_IIC = 1
; Enable ROM copying on IIc
;PT3_ENABLE_IIC_COPY_ROM = 1
; The Vortex Tracker by default generates Atari-ST style pt3 files
; which assume there is a 1.77MHz clock frequency driving
; the AY-3-8910. Apple II Mockingboards run at 1MHz, so unless
; you convert the frequency the music will sound pitched lower.
; The 1.77MHz frequency conversion code can be in three states:
; Enabled (always convert)
; PT3_DISABLE_FREQ_CONVERSION commented out
; PT3_DISABLE_SWITCHABLE_FREQ_CONVERSION = 1
; Disabled (don't convert)
; PT3_DISABLE_FREQ_CONVERSION = 1
; PT3_DISABLE_SWITCHABLE_FREQ_CONVERSION = 1
; Switchable (can disable/enable on the fly)
; this is the default for historical reasons
; PT3_DISABLE_FREQ_CONVERSION commented out
; PT3_DISABLE_SWITCHABLE_FREQ_CONVERSION commented out
; PT3_DISABLE_FREQ_CONVERSION = 1
; PT3_DISABLE_SWITCHABLE_FREQ_CONVERSION = 1
;=============================
; Setup
;=============================
pt3_setup:
jsr HOME
jsr TEXT
;===============
; init variables
;===============
lda #0
sta DONE_PLAYING
; lda #1
.if 0
sta LOOP ; change to 1 to loop forever
.endif
sta LOOP_2
;=======================
; Detect Apple II Model
;========================
; IRQ setup is different on IIc
; You can possibly skip this if you only care about II+/IIe
.ifdef PT3_ENABLE_APPLE_IIC
jsr detect_appleii_model
.endif
;=======================
; Detect mockingboard
;========================
jsr print_mockingboard_detect ; print message
jsr mockingboard_detect ; call detection routine
bcs mockingboard_found
jsr print_mocking_notfound
; possibly can't detect on IIc so just try with slot#4 anyway
; even if not detected
jmp setup_interrupt
mockingboard_found:
; print found message
; modify message to print slot value
lda MB_ADDR_H
sec
sbc #$10
sta found_message+11
jsr print_mocking_found
;==================================================
; patch the playing code with the proper slot value
;==================================================
jsr mockingboard_patch
setup_interrupt:
;=======================
; Set up 50Hz interrupt
;========================
jsr mockingboard_init
jsr mockingboard_setup_interrupt
;============================
; Init the Mockingboard
;============================
jsr reset_ay_both
jsr clear_ay_both
;==================
; init song
;==================
jsr pt3_init_song
;============================
; Enable 6502 interrupts
;============================
start_interrupts:
cli ; clear interrupt mask
;============================
; Loop forever
;============================
forever_loop:
jmp forever_loop
;========================================
;========================================
; Helper routines below
;========================================
;========================================
;==================================
; Print mockingboard detect message
;==================================
; note: on IIc must do this before enabling interrupt
; as we disable ROM (COUT won't work?)
print_mockingboard_detect:
lda APPLEII_MODEL
sta apple_message+17
; print detection message for Apple II type
ldy #0
print_apple_message:
lda apple_message,Y ; load loading message
beq done_apple_message
ora #$80
jsr COUT
iny
jmp print_apple_message
done_apple_message:
jsr CROUT1
; print detection message
ldy #0
print_mocking_message:
lda mocking_message,Y ; load loading message
beq done_mocking_message
ora #$80
jsr COUT
iny
jmp print_mocking_message
done_mocking_message:
jsr CROUT1
rts
print_mocking_notfound:
ldy #0
print_not_message:
lda not_message,Y ; load loading message
beq print_not_message_done
ora #$80
jsr COUT
iny
jmp print_not_message
print_not_message_done:
rts
print_mocking_found:
ldy #0
print_found_message:
lda found_message,Y ; load loading message
beq done_found_message
ora #$80
jsr COUT
iny
jmp print_found_message
done_found_message:
rts
;=========
; strings
;=========
apple_message: .asciiz "DETECTED APPLE II "
mocking_message: .asciiz "LOOKING FOR MOCKINGBOARD: "
not_message: .byte "NOT "
found_message: .asciiz "FOUND SLOT#4"
;=========
; vars
;=========
;=========
;routines
;=========
.ifdef PT3_ENABLE_APPLE_IIC
.include "pt3_lib_detect_model.s"
.endif
.include "pt3_lib_common.s"
.include "pt3_lib_core2.s"
.include "pt3_lib_init.s"
.include "pt3_lib_mockingboard_setup.s"
.include "interrupt_handler.s"
; if you're self patching, detect has to be after interrupt_handler.s
.include "pt3_lib_mockingboard_detect.s"
;=============
; include song
;=============
.align 256 ; must be on page boundary
; this can be fixed but some changes would have
; to be made throughout the player code
song:
;.incbin "music/dya_dance_1.pt3"
.incbin "../pt3_player/music/EA.PT3"
.align 256
song2:
;.incbin "music/dya_dance_2.pt3"
.incbin "../pt3_player/music/EA.PT3"

68
music/pt3_lib_6ch/zp.inc Normal file
View File

@ -0,0 +1,68 @@
;; Zero page addresses
.if 0
ORNAMENT_L = $60
ORNAMENT_H = $61
SAMPLE_L = $62
SAMPLE_H = $63
LOOP = $64
DONE_SONG = $69
.endif
MB_ADDR_L = $65
MB_ADDR_H = $66
MB_VALUE = $67
DONE_PLAYING = $68
PT3_TEMP = $6A
APPLEII_MODEL = $6B
ORNAMENT_L_2 = $6C
ORNAMENT_H_2 = $6D
SAMPLE_L_2 = $6E
SAMPLE_H_2 = $6F
.if 0
AY_REGISTERS = $70
A_FINE_TONE = $70
A_COARSE_TONE = $71
B_FINE_TONE = $72
B_COARSE_TONE = $73
C_FINE_TONE = $74
C_COARSE_TONE = $75
NOISE = $76
ENABLE = $77
PT3_MIXER_VAL = $77
A_VOLUME = $78
B_VOLUME = $79
C_VOLUME = $7A
ENVELOPE_FINE = $7B
ENVELOPE_COARSE = $7C
ENVELOPE_SHAPE = $7D
PATTERN_L = $7E
PATTERN_H = $7F
.endif
AY_REGISTERS_2 = $80
A_FINE_TONE_2 = $80
A_COARSE_TONE_2 = $81
B_FINE_TONE_2 = $82
B_COARSE_TONE_2 = $83
C_FINE_TONE_2 = $84
C_COARSE_TONE_2 = $85
NOISE_2 = $86
ENABLE_2 = $87
PT3_MIXER_VAL_2 = $87
A_VOLUME_2 = $88
B_VOLUME_2 = $89
C_VOLUME_2 = $8A
ENVELOPE_FINE_2 = $8B
ENVELOPE_COARSE_2 = $8C
ENVELOPE_SHAPE_2 = $8D
PATTERN_L_2 = $8E
PATTERN_H_2 = $8F
LOOP_2 = $90
DONE_SONG_2 = $91