diff options
Diffstat (limited to 'libpsn00b/psxspu')
| -rw-r--r-- | libpsn00b/psxspu/makefile | 34 | ||||
| -rw-r--r-- | libpsn00b/psxspu/readme.txt | 36 | ||||
| -rw-r--r-- | libpsn00b/psxspu/spuinit.s | 130 | ||||
| -rw-r--r-- | libpsn00b/psxspu/spukeyon.s | 16 | ||||
| -rw-r--r-- | libpsn00b/psxspu/spureverbon.s | 16 | ||||
| -rw-r--r-- | libpsn00b/psxspu/spusetkey.s | 26 | ||||
| -rw-r--r-- | libpsn00b/psxspu/spusetreverb.s | 24 | ||||
| -rw-r--r-- | libpsn00b/psxspu/spusetreverbaddr.s | 25 | ||||
| -rw-r--r-- | libpsn00b/psxspu/spusetvoiceraw.s | 60 | ||||
| -rw-r--r-- | libpsn00b/psxspu/transfer.s | 107 |
10 files changed, 474 insertions, 0 deletions
diff --git a/libpsn00b/psxspu/makefile b/libpsn00b/psxspu/makefile new file mode 100644 index 0000000..d6b3604 --- /dev/null +++ b/libpsn00b/psxspu/makefile @@ -0,0 +1,34 @@ +PREFIX = mipsel-unknown-elf- + +TARGET = ../libpsxspu.a + +CFILES = $(notdir $(wildcard ./*.c)) +AFILES = $(notdir $(wildcard ./*.s)) +OFILES = $(addprefix build/,$(CFILES:.c=.o) $(AFILES:.s=.o)) + +INCLUDE = -I../include + +CFLAGS = -O2 -g -msoft-float -Wa,--strip-local-absolute +AFLAGS = -g -msoft-float --strip-local-absolute + +CC = $(PREFIX)gcc +AS = $(PREFIX)as +AR = $(PREFIX)ar +RANLIB = $(PREFIX)ranlib + +all: $(TARGET) + +$(TARGET): $(OFILES) + $(AR) cr $(TARGET) $(OFILES) + $(RANLIB) $(TARGET) + +build/%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +build/%.o: %.s + @mkdir -p $(dir $@) + $(AS) $(AFLAGS) $(INCLUDE) $< -o $@ + +clean: + rm -Rf build $(TARGET) diff --git a/libpsn00b/psxspu/readme.txt b/libpsn00b/psxspu/readme.txt new file mode 100644 index 0000000..4cac976 --- /dev/null +++ b/libpsn00b/psxspu/readme.txt @@ -0,0 +1,36 @@ +PSX SPU Library, part of PSn00bSDK +2019 Lameguy64 / Meido-Tek Productions + +Licensed under Mozilla Public License + + Open source implementation of the SPU library written mostly in MIPS +assembly. Currently only supports SPU init, uploading sample data using DMA +transfer and basic sample playback but is currently lacking a bunch of +important functions. + + Very work in progress currently. + + +Library developer(s): + + Lameguy64 + + +Library header(s): + + psxspu.h + + +Todo list: + + * SPU RAM allocation routines yet to be implemented (heap must only be + stored in main RAM and not SPU RAM like in the official SDK). + + * SpuKeyOn() is actually not part of the official library. + + * SPU reverb configuration functions yet to be implemented. + + +Changelog: + + None so far... diff --git a/libpsn00b/psxspu/spuinit.s b/libpsn00b/psxspu/spuinit.s new file mode 100644 index 0000000..fa5ec32 --- /dev/null +++ b/libpsn00b/psxspu/spuinit.s @@ -0,0 +1,130 @@ +.set noreorder +.set noat + +.include "hwregs_a.h" + +.section .data + + +.global SpuInit +.type SpuInit, @function +SpuInit: + + addiu $sp, -4 + sw $ra, 0($sp) + + lui $v1, IOBASE + + # Stop and mute everything + + sh $0 , SPUCNT($v1) # Clear control settings + jal SpuCtrlSync + move $a0, $0 + + sh $0 , SPU_MASTER_VOL($v1) # Clear master volume + sh $0 , SPU_MASTER_VOL+2($v1) + + sh $0 , SPU_REVERB_VOL($v1) # Clear reverb volume + sh $0 , SPU_REVERB_VOL+2($v1) + + sh $0 , SPU_CD_VOL($v1) # Clear CD volume + sh $0 , SPU_CD_VOL+2($v1) + + sh $0 , SPU_EXT_VOL($v1) # Clear external audio volume + sh $0 , SPU_EXT_VOL+2($v1) + + sh $0 , SPU_FM_MODE($v1) # Turn off FM modes + sh $0 , SPU_FM_MODE+2($v1) + + sh $0 , SPU_NOISE_MODE($v1) # Turn off noise modes + sh $0 , SPU_NOISE_MODE+2($v1) + + sh $0 , SPU_REVERB_ON($v1) # Turn off reverb modes + sh $0 , SPU_REVERB_ON+2($v1) + + li $v0, 0xfffe + sh $v0, SPU_REVERB_ADDR($v1) + + lui $v0, 0x0200; + ori $v0, 0x3fff; + + # Clear all voices + + addiu $a1, $sp, -20 + sw $0 , 0($a1) + sw $0 , 4($a1) + sw $0 , 8($a1) + sw $0 , 12($a1) + + li $a2, 23 + +.clear_voices: + jal SpuSetVoiceRaw + move $a0, $a2 + addiu $a2, -1 + bgez $a2, .clear_voices + nop + + li $v0, 0xffff # Set all keys to off + sh $v0, SPU_KEY_OFF($v1) + sh $v0, SPU_KEY_OFF+2($v1) + + li $v0, 0x4 # Set SPU data transfer control + sh $v0, SPUDTCNT($v1) # (usually always 0x4) + + lw $v0, DPCR($v1) # Enable DMA channel 4 (SPU DMA) + lui $at, 0xb + or $v0, $at + sw $v0, DPCR($v1) + + li $v0, 0xc000 # Enable SPU + sh $v0, SPUCNT($v1) + jal SpuCtrlSync + move $a0, $v0 + + li $v0, 0x3fff # Activate master volume + sh $v0, SPU_MASTER_VOL($v1) + sh $v0, SPU_MASTER_VOL+2($v1) + + sh $v0, SPU_CD_VOL($v1) # Activate CD volume + sh $v0, SPU_CD_VOL+2($v1) + + lw $ra, 0($sp) + addiu $sp, 4 + jr $ra + nop + + +# Waits until bits 0-5 of SPUSTAT are equal to SPUCNT +# +# Destroys v0, v1, a0 +# +.global SpuCtrlSync +.type SpuCtrlSync, @function +SpuCtrlSync: + lui $v1, IOBASE + andi $a0, 0x3f +.ctrl_wait: + lhu $v0, SPUSTAT($v1) # Get SPUSTAT value + nop + andi $v0, 0x3f + bne $v0, $a0, .ctrl_wait # Wait until SPUCNT and SPUSTAT are equal + nop + jr $ra + nop + + +# Waits until SPU has finished transfers +# +.global SpuWait +.type SpuWait, @function +SpuWait: + lui $v0, IOBASE + lhu $v0, SPUSTAT($v0) + nop + andi $v0, 0x400 + bnez $v0, SpuWait + nop + jr $ra + nop +
\ No newline at end of file diff --git a/libpsn00b/psxspu/spukeyon.s b/libpsn00b/psxspu/spukeyon.s new file mode 100644 index 0000000..556f976 --- /dev/null +++ b/libpsn00b/psxspu/spukeyon.s @@ -0,0 +1,16 @@ +.set noreorder + +.include "hwregs_a.h" + +.section .data + + +.global SpuKeyOn +.type SpuKeyOn, @function +SpuKeyOn: + lui $v1, IOBASE + li $v0, 1 + sll $v0, $a0 + sh $v0, SPU_KEY_ON($v1) + jr $ra + nop
\ No newline at end of file diff --git a/libpsn00b/psxspu/spureverbon.s b/libpsn00b/psxspu/spureverbon.s new file mode 100644 index 0000000..852bff3 --- /dev/null +++ b/libpsn00b/psxspu/spureverbon.s @@ -0,0 +1,16 @@ +.set noreorder + +.include "hwregs_a.h" + +.section .data + + +.global SpuReverbOn +.type SpuReverbOn, @function +SpuReverbOn: + lui $v1, IOBASE + li $v0, 1 + sll $v0, $a0 + sh $v0, SPU_REVERB_ON($v1) + jr $ra + nop
\ No newline at end of file diff --git a/libpsn00b/psxspu/spusetkey.s b/libpsn00b/psxspu/spusetkey.s new file mode 100644 index 0000000..1270b2a --- /dev/null +++ b/libpsn00b/psxspu/spusetkey.s @@ -0,0 +1,26 @@ +.set noreorder + +.include "hwregs_a.h" + +.section .data + + +.global SpuSetKey +.type SpuSetKey, @function +SpuSetKey: + # a0 - 0: key off, 1: key on + # a1 - Voice bit mask + + lui $a2, IOBASE + + beqz $a0, .key_off + nop + + jr $ra + sh $a1, SPU_KEY_ON($v1) + +.key_off: + + jr $ra + sh $a1, SPU_KEY_OFF($v1) +
\ No newline at end of file diff --git a/libpsn00b/psxspu/spusetreverb.s b/libpsn00b/psxspu/spusetreverb.s new file mode 100644 index 0000000..993b166 --- /dev/null +++ b/libpsn00b/psxspu/spusetreverb.s @@ -0,0 +1,24 @@ +.set noreorder + +.include "hwregs_a.h" + +.section .data + + +.global SpuSetReverb +.type SpuSetReverb, @function +SpuSetReverb: + addiu $sp, -4 + sw $ra, 0($sp) + + lhu $v0, SPUCNT($v1) + nop + ori $v0, 0x80 # Enable reverb + sh $v0, SPUCNT($v1) + jal SpuCtrlSync + move $a0, $v0 + + lw $ra, 0($sp) + addiu $sp, 4 + jr $ra + nop
\ No newline at end of file diff --git a/libpsn00b/psxspu/spusetreverbaddr.s b/libpsn00b/psxspu/spusetreverbaddr.s new file mode 100644 index 0000000..6ddbf44 --- /dev/null +++ b/libpsn00b/psxspu/spusetreverbaddr.s @@ -0,0 +1,25 @@ +.set noreorder + +.include "hwregs_a.h" + + +.section .text + +.global SpuSetReverbAddr +.type SpuSetReverbAddr, @function +SpuSetReverbAddr: + lui $a3, 0x1f80 + srl $a0, 3 + sh $a0, SPU_REVERB_ADDR($a3) + jr $ra + nop + + +.global SpuSetReverbVolume +.type SpuSetReverbVolume, @function +SpuSetReverbVolume: + lui $a3, 0x1f80 + sh $a0, SPU_REVERB_VOL($a3) + sh $a1, SPU_REVERB_VOL+2($a3) + jr $ra + nop
\ No newline at end of file diff --git a/libpsn00b/psxspu/spusetvoiceraw.s b/libpsn00b/psxspu/spusetvoiceraw.s new file mode 100644 index 0000000..43450f6 --- /dev/null +++ b/libpsn00b/psxspu/spusetvoiceraw.s @@ -0,0 +1,60 @@ +.set noreorder + +.include "hwregs_a.h" + + +.set PARAM_L, 0 +.set PARAM_R, 2 +.set PARAM_FREQ, 4 +.set PARAM_ADDR, 6 +.set PARAM_LOOP, 8 +.set PARAM_RES, 10 +.set PARAM_ADSR, 12 + + +.section .text + +.global SpuSetVoiceRaw +.type SpuSetVoiceRaw, @function +SpuSetVoiceRaw: + + # a0 - Voice number + # a1 - Address to parameters + + sll $a0, 4 + addiu $a0, SPU_VOICE_BASE + + lui $v1, IOBASE + or $a0, $v1 + + lhu $v0, PARAM_L($a1) + nop + sh $v0, SPU_VOICE_VOL_L($a0) + + lhu $v0, PARAM_R($a1) + nop + sh $v0, SPU_VOICE_VOL_R($a0) + + lhu $v0, PARAM_FREQ($a1) + nop + sh $v0, SPU_VOICE_FREQ($a0) + + lhu $v0, PARAM_ADDR($a1) + nop + sh $v0, SPU_VOICE_ADDR($a0) + + lhu $v0, PARAM_LOOP($a1) + nop + sh $v0, SPU_VOICE_LOOP($a0) + + + lw $v0, PARAM_ADSR($a1) + nop + sh $v0, SPU_VOICE_ADSR_L($a0) + srl $v0, 16 + sh $v0, SPU_VOICE_ADSR_H($a0) + + + jr $ra + nop +
\ No newline at end of file diff --git a/libpsn00b/psxspu/transfer.s b/libpsn00b/psxspu/transfer.s new file mode 100644 index 0000000..aed69dc --- /dev/null +++ b/libpsn00b/psxspu/transfer.s @@ -0,0 +1,107 @@ +.set noreorder + +.include "hwregs_a.h" + + +.section .text + +.global SpuSetTransferMode +.type SpuSetTransferMode, @function +SpuSetTransferMode: + la $v0, _spu_transfer_mode + sb $a0, 0($v0) + jr $ra + move $v0, $a0 + + +.global SpuSetTransferStartAddr +.type SpuSetTransferStartAddr, @function +SpuSetTransferStartAddr: + li $v0, 0x1000 # Check if value is valid + blt $a0, $v0, .bad_value + nop + li $v0, 0xffff + bgt $a0, $v0, .bad_value + nop + + la $v1, _spu_transfer_addr + srl $v0, $a0, 3 # Set transfer destination address + sh $v0, 0($v1) + + jr $ra + move $v0, $a0 + +.bad_value: + jr $ra + move $v0, $0 + + +.global SpuWrite +.type SpuWrite, @function +SpuWrite: + addiu $sp, -8 + sw $ra, 0($sp) + sw $a0, 4($sp) + + lui $a3, IOBASE + + lhu $v0, SPUCNT($a3) # Set transfer mode to Stop + nop + andi $v0, 0xffcf + sh $v0, SPUCNT($a3) + jal SpuCtrlSync + move $a0, $v0 + + la $v1, _spu_transfer_addr # Set SPU write address + lhu $v1, 0($v1) + nop + sh $v1, SPU_ADDR($a3) + + lhu $v0, SPUCNT($a3) # Set transfer mode to DMA write + nop + ori $v0, 0x20 + sh $v0, SPUCNT($a3) + #jal SpuCtrlSync # Locks up on most emulators (bit 5 in + #move $a0, $v0 # SPUSTAT likely not updating, seems to + # be okay to not wait for it on real HW) + + lw $a0, 4($sp) + +.dma_wait: # Wait for SPU to be ready for DMA + lhu $v0, SPUSTAT($a3) + nop + andi $v0, 0x400 # Bit 8 in SPUSTAT never changes to 1 on + bnez $v0, .dma_wait # emulators so use bit 10 instead + nop + + sw $a0, D4_MADR($a3) # Set DMA source address + + li $v0, 0x10 # 16 words per block (64 bytes) + addiu $a1, 63 # Add by 63 to ensure all bytes get sent + srl $a1, 6 # Equivalent to divide by 64 + andi $a1, 0xffff + sll $a1, 16 + or $v0, $a1 + sw $v0, D4_BCR($a3) + + lui $v0, 0x0100 # Commence transfer + ori $v0, 0x0201 + sw $v0, D4_CHCR($a3) + + lw $ra, 0($sp) + addiu $sp, 8 + jr $ra + nop + + +.section .data + +.global _spu_transfer_mode +.type _spu_transfer_mode, @object +_spu_transfer_mode: + .word 0x0 + +.global _spu_transfer_addr +.type _spu_transfer_addr, @object +_spu_transfer_addr: + .word 0x200
\ No newline at end of file |
