How to Write to DSP Registers Without any SPC-700 Code
Note: This tutorial incorrectly uses .smc for ROM that does not have a copier header; could not upload a fixed archive due to https://github.com/uttori/uttori-wiki/issues/34 - jeffythedragonslayer 6/8/23
Usage:
ca65 manual_dsp.s
ld65 -C lorom128.cfg -o manual_dsp.smc manual_dsp.o
lorom128.cfg
# ca65 linker config for 128K SMC
# Physical areas of memory
# Names need not match, but it makes it easier to remember if they do.
MEMORY {
ZEROPAGE: start = 0, size = $100;
BSS: start = $200, size = $1800;
ROM: start = $8000, size = $8000, fill = yes;
BANK1: start = $18000, size = $8000, fill = yes;
BANK2: start = $28000, size = $8000, fill = yes;
BANK3: start = $38000, size = $8000, fill = yes;
}
# Logical areas code/data can be put into.
SEGMENTS {
ZEROPAGE: load = ZEROPAGE, type = zp;
BSS: load = BSS, type = bss, align = $100;
CODE: load = ROM, align = $8000;
RODATA: load = ROM;
HEADER: load = ROM, start = $FFC0;
ROMINFO: load = ROM, start = $FFD5, optional = yes;
VECTORS: load = ROM, start = $FFE0;
# The extra three banks
BANK1: load = BANK1, align = $8000, optional = yes;
BANK2: load = BANK2, align = $8000, optional = yes;
BANK3: load = BANK3, align = $8000, optional = yes;
}
lorom128.inc
; Sets up processor basics and ROM headers/vectors
.p816 ; 65816 processor
.i16 ; X/Y are 16 bits
.a8 ; A is 8 bits
.segment "HEADER" ; +$7FE0 in file
.byte ROM_NAME
.segment "ROMINFO" ; +$7FD5 in file
.byte $30 ; LoROM, fast-capable
.byte 0 ; no battery RAM
.byte $07 ; 128K ROM
.byte 0,0,0,0
.word $AAAA,$5555 ; dummy checksum and complement
.segment "VECTORS"
.word 0, 0, 0, 0, 0, 0, 0, 0
.word 0, 0, 0, 0, 0, 0, reset, 0
.code
.macro init_cpu
clc
xce
rep #$10 ; X/Y 16-bit
sep #$20 ; A 8-bit
.endmacro
spc_upload.s
; High-level interface to SPC-700 bootloader
;
; 1. Call spc_wait_boot
; 2. To upload data:
; A. Call spc_begin_upload
; B. Call spc_upload_byte any number of times
; C. Go back to A to upload to different addr
; 3. To begin execution, call spc_execute
;
; Have your SPC code jump to $FFC0 to re-run bootloader.
; Be sure to call spc_wait_boot after that.
; Waits for SPC to finish booting. Call before first
; using SPC or after bootrom has been re-run.
; Preserved: X, Y
spc_wait_boot:
lda #$AA
@wait: cmp $2140
bne @wait
; Clear in case it already has $CC in it
; (this actually occurred in testing)
sta $2140
lda #$BB
@wait2: cmp $2141
bne @wait2
rts
; Starts upload to SPC addr Y and sets Y to
; 0 for use as index with spc_upload_byte.
; Preserved: X
spc_begin_upload:
sty $2142
; Send command
lda $2140
clc
adc #$22
bne @skip ; special case fully verified
inc
@skip: sta $2141
sta $2140
; Wait for acknowledgement
@wait: cmp $2140
bne @wait
; Initialize index
ldy #0
rts
; Uploads byte A to SPC and increments Y. The low byte
; of Y must not changed between calls.
; Preserved: X
spc_upload_byte:
sta $2141
; Signal that it's ready
tya
sta $2140
iny
; Wait for acknowledgement
@wait: cmp $2140
bne @wait
rts
; Starts executing at SPC addr Y
; Preserved: X, Y
spc_execute:
sty $2142
stz $2141
lda $2140
clc
adc #$22
sta $2140
; Wait for acknowledgement
@wait: cmp $2140
bne @wait
rts
manual_dsp.s
; How to write to DSP registers without any SPC-700 code.
; Makes beep.
;
; ca65 manual_dsp.s
; ld65 -C lorom128.cfg -o manual_dsp.smc manual_dsp.o
.define ROM_NAME "MANUAL DSP"
.include "lorom128.inc"
.include "spc_upload.s"
reset:
init_cpu
jsr spc_wait_boot
; Upload sample to SPC at $200
ldy #$0200
jsr spc_begin_upload
: lda sample,y
jsr spc_upload_byte
cpy #sample_end - sample
bne :-
; Do DSP writes to start playing tone
ldx #$206C
jsr write_dsp
ldx #$004C
jsr write_dsp
ldx #$FF5C
jsr write_dsp
ldx #$025D
jsr write_dsp
ldx #$7F00
jsr write_dsp
ldx #$7F01
jsr write_dsp
ldx #$0002
jsr write_dsp
ldx #$0203
jsr write_dsp
ldx #$0004
jsr write_dsp
ldx #$C305
jsr write_dsp
ldx #$2F06
jsr write_dsp
ldx #$CF07
jsr write_dsp
ldx #$005C
jsr write_dsp
ldx #$003D
jsr write_dsp
ldx #$004D
jsr write_dsp
ldx #$7F0C
jsr write_dsp
ldx #$7F1C
jsr write_dsp
ldx #$002C
jsr write_dsp
ldx #$003C
jsr write_dsp
ldx #$014C
jsr write_dsp
@forever:
jmp @forever
sample:
;.org $200
;directory:
.word $204 ; start
.word $204 ; loop
;sample:
;sample_loop:
.byte $B0,$78,$78,$78,$78,$78,$78,$78,$78
.byte $B3,$78,$78,$78,$78,$78,$78,$78,$78
sample_end:
; Writes high byte of X to SPC-700 DSP register in low byte of X
write_dsp:
phx
; Just do a two-byte upload to $00F2-$00F3, so we
; set the DSP address, then write the byte into that.
ldy #$00F2
jsr spc_begin_upload
pla
jsr spc_upload_byte ; low byte of X to $F2
pla
jsr spc_upload_byte ; high byte of X to $F3
rts
Written by blargg on 2013-08-22.