aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/psxspu
diff options
context:
space:
mode:
Diffstat (limited to 'libpsn00b/psxspu')
-rw-r--r--libpsn00b/psxspu/makefile34
-rw-r--r--libpsn00b/psxspu/readme.txt36
-rw-r--r--libpsn00b/psxspu/spuinit.s130
-rw-r--r--libpsn00b/psxspu/spukeyon.s16
-rw-r--r--libpsn00b/psxspu/spureverbon.s16
-rw-r--r--libpsn00b/psxspu/spusetkey.s26
-rw-r--r--libpsn00b/psxspu/spusetreverb.s24
-rw-r--r--libpsn00b/psxspu/spusetreverbaddr.s25
-rw-r--r--libpsn00b/psxspu/spusetvoiceraw.s60
-rw-r--r--libpsn00b/psxspu/transfer.s107
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