├── long.wav ├── voice.wav ├── bugloop.wav ├── dcbiasend.wav ├── saturnboot.bin ├── test.cue ├── test.ld ├── adp68k.ld ├── bintoinc.cpp ├── tables.h ├── README ├── types.h ├── dsp-macros.h ├── Makefile ├── adp68k.h ├── smpc.h ├── adplink.cpp ├── scsp.h ├── sh2.h ├── gen-data.cpp ├── test.c ├── adpencode.cpp └── adp68k.c /long.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celeriyacon/scspadpcm/HEAD/long.wav -------------------------------------------------------------------------------- /voice.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celeriyacon/scspadpcm/HEAD/voice.wav -------------------------------------------------------------------------------- /bugloop.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celeriyacon/scspadpcm/HEAD/bugloop.wav -------------------------------------------------------------------------------- /dcbiasend.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celeriyacon/scspadpcm/HEAD/dcbiasend.wav -------------------------------------------------------------------------------- /saturnboot.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celeriyacon/scspadpcm/HEAD/saturnboot.bin -------------------------------------------------------------------------------- /test.cue: -------------------------------------------------------------------------------- 1 | FILE "test.iso" BINARY 2 | TRACK 01 MODE1/2048 3 | INDEX 01 00:00:00 4 | -------------------------------------------------------------------------------- /test.ld: -------------------------------------------------------------------------------- 1 | /*OUTPUT_FORMAT(binary)*/ 2 | OUTPUT_ARCH(sh2) 3 | 4 | SECTIONS 5 | { 6 | .init 0x06004000 : AT(0x00000000) { *(.init) } 7 | .text : { *(.text) } 8 | .data : { *(.rodata) *(.data) *(.bss) } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /adp68k.ld: -------------------------------------------------------------------------------- 1 | /*OUTPUT_FORMAT(binary)*/ 2 | OUTPUT_ARCH(m68k) 3 | 4 | SECTIONS 5 | { 6 | .exception_table 0x00000000 : AT(0x00000000) { LONG(0x00000080) LONG(_start) } 7 | .control_block 0x00000080 : AT(0x00000080) { *(.control_block) } 8 | .stuff 0x00000180 : AT(0x00000180) { *(.bss) *(.data) *(.rodata) *(.text) } 9 | .padding 0x00001FFF : AT(0x00001FFF) { BYTE(0x00) } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /bintoinc.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * bintoinc.cpp 3 | * 4 | * Copyright (C) 2021 celeriyacon - https://github.com/celeriyacon 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 3. This notice may not be removed or altered from any source distribution. 21 | * 22 | */ 23 | 24 | #include 25 | 26 | int main(int argc, char* argv[]) 27 | { 28 | int c; 29 | 30 | while((c = fgetc(stdin)) >= 0) 31 | printf("0x%02x,\n", c); 32 | 33 | if(ferror(stdin) || ferror(stdout)) 34 | return -1; 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /tables.h: -------------------------------------------------------------------------------- 1 | #ifndef TABLES_H 2 | #define TABLES_H 3 | 4 | #define FILTER_TAB_COUNT 16 5 | 6 | // 2048 = 1.0 7 | // Must be quantities that can fit into a signed 13-bit integer 8 | static const int filter_tab[FILTER_TAB_COUNT][3] = 9 | { 10 | // 11 | // These first two filters must remain unaltered, due to hardcoded 12 | // assumptions in the code. 13 | // 14 | { 1920, 0, 0 }, 15 | { 0, 0, 0 }, 16 | // 17 | // 18 | { 3680, -1664, 0 }, 19 | { 3136, -1760, 0 }, 20 | { 3904, -1920, 0 }, 21 | { 3136, -2368, 1248 }, 22 | { 2976, -1472, 544 }, 23 | { 3680, -2880, 1280 }, 24 | { 3168, -2592, 1440 }, 25 | { 1728, -832, 896 }, 26 | { 1856, 288, -128 }, 27 | { 2144, -288, -512 }, 28 | { 2976, -992, 256 }, 29 | { 2144, -480, 256 }, 30 | { 4095, -2048, -32 }, 31 | { 3616, -3584, 1792 }, 32 | }; 33 | 34 | // Must be quantities that can fit into a signed 13-bit integer 35 | static const int scale_tab[16] = 36 | { 37 | 1, 2, 3, 5, 9, 16, 28, 48, 84, 147, 256, 446, 776, 1351, 2352, 4095 38 | }; 39 | 40 | 41 | // 42 | // Change #if 0 to #if 1 if you want a low amplitude 22050Hz tone instead of a 43 | // much higher amplitude, but constant, DC bias after 1-bit or 2-bit sample 44 | // playback on a channel ends. 45 | // 46 | static const uint8 encoded_byte_xor_tab[3] = 47 | { 48 | #if 0 49 | 0x00, 0xCC, 0xAA 50 | #else 51 | 0x00, 0x00, 0x00 52 | #endif 53 | }; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is a proof of concept Sega Saturn 8-channel ADPCM decoder, with the 2 | decoding being done on the SCSP, utilizing all 32 SCSP slots and the majority 3 | of the SCSP DSP's resources. It runs at a fixed rate of 44.1KHz, and supports 4 | (custom) 1.5-bit, 2.5-bit and 4.5-bit(average) encodings, similar in concept 5 | to other ADPCM formats like BRR and CD-XA ADPCM. 6 | 7 | Two DSP-generated PSG-like channels are also provided, one 50/50 square and 8 | a 4-operator saw wave channel. The square wave channel isn't suited to music, 9 | as it only supports frequencies of 44100 / (2 * n), where 'n' is an integer, 10 | but should be adequate for sound effects. The saw wave channel can be 11 | programmed to generate noise, and its output has a programmable simple IIR 12 | filter, which can be used to color white noise. The relative phases of the 13 | saw wave channel's operators cannot be guaranteed nor reset currently. 14 | 15 | Each ADPCM block encodes 16 samples, and playback startup on a channel 16 | currently has a delay of 16 samples(1 block) plus sound driver overhead. 17 | 18 | To enable looping, set the comment tag/field in the WAV file to "adp_loop=n", 19 | where 'n' is the loop target, aligned to a 16 sample boundary. 20 | 21 | Streaming playback is not currently supported by the included demo sound 22 | driver and tools, but could be supported in theory. 23 | 24 | The tools require "libsndfile" to compile and run, and building the ISO 25 | image requires the "genisoimage" utility. The 68K sound driver and 26 | SH-2 test program were developed with and tested with gcc 4.7.4. Later 27 | versions of gcc may work, but were not tested. 28 | 29 | The contents of "saturnboot.bin" between offsets 0x100 and 0xEFF, inclusive, 30 | should not be used outside of the limited purpose of allowing disc 31 | recognition and booting by the Sega Saturn's BIOS. 32 | -------------------------------------------------------------------------------- /types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * types.h 3 | * 4 | * Copyright (C) 2021 celeriyacon - https://github.com/celeriyacon 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 3. This notice may not be removed or altered from any source distribution. 21 | * 22 | */ 23 | 24 | #ifndef GIGASANTA_TYPES_H 25 | #define GIGASANTA_TYPES_H 26 | 27 | #ifdef __m68k__ 28 | typedef signed char int8; 29 | typedef signed short int16; 30 | typedef signed long int32; 31 | typedef signed long long int64; 32 | 33 | typedef unsigned char uint8; 34 | typedef unsigned short uint16; 35 | typedef unsigned long uint32; 36 | typedef unsigned long long uint64; 37 | #elif defined(__sh2__) 38 | typedef signed char int8; 39 | typedef signed short int16; 40 | typedef signed int int32; 41 | typedef signed long long int64; 42 | 43 | typedef unsigned char uint8; 44 | typedef unsigned short uint16; 45 | typedef unsigned int uint32; 46 | typedef unsigned long long uint64; 47 | #else 48 | #include 49 | 50 | typedef int8_t int8; 51 | typedef int16_t int16; 52 | typedef int32_t int32; 53 | typedef int64_t int64; 54 | 55 | typedef uint8_t uint8; 56 | typedef uint16_t uint16; 57 | typedef uint32_t uint32; 58 | typedef uint64_t uint64; 59 | #endif 60 | 61 | #ifndef __cplusplus 62 | typedef _Bool bool; 63 | #endif 64 | 65 | #define INLINE inline __attribute__((always_inline)) 66 | 67 | #define sign_extend(n, v) ((int32)((uint32)(v) << (32 - (n))) >> (32 - (n))) 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /dsp-macros.h: -------------------------------------------------------------------------------- 1 | /* 2 | * dsp-macros.h 3 | * 4 | * Copyright (C) 2021 celeriyacon - https://github.com/celeriyacon 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 3. This notice may not be removed or altered from any source distribution. 21 | * 22 | */ 23 | 24 | #ifndef GIGASANTA_DSP_MACROS_H 25 | #define GIGASANTA_DSP_MACROS_H 26 | 27 | #define DSP_NXADDR ((uint64)( 1) << 0) 28 | #define DSP_ADRGB ((uint64)( 1) << 1) 29 | #define DSP_MASA(v) ((uint64)((v)&0x1F) << 2) 30 | #define DSP_NOFL ((uint64)( 1) << 8) 31 | #define DSP_CRA(v) ((uint64)((v)&0x3F) << 9) 32 | #define DSP_BSEL(v) ((uint64)((v)&0x01) << 16) 33 | #define DSP_ZERO ((uint64)( 1) << 17) 34 | #define DSP_NEGB ((uint64)( 1) << 18) 35 | #define DSP_YRL ((uint64)( 1) << 19) 36 | #define DSP_SHFT0 ((uint64)( 1) << 20) 37 | #define DSP_SHFT1 ((uint64)( 1) << 21) 38 | #define DSP_FRCL ((uint64)( 1) << 22) 39 | #define DSP_ADRL ((uint64)( 1) << 23) 40 | #define DSP_EWA(v) ((uint64)((v)&0x0F) << 24) 41 | #define DSP_EWT ((uint64)( 1) << 28) 42 | #define DSP_MRT ((uint64)( 1) << 29) 43 | #define DSP_MWT ((uint64)( 1) << 30) 44 | #define DSP_TABLE ((uint64)( 1) << 31) 45 | #define DSP_IWA(v) ((uint64)((v)&0x1F) << 32) 46 | #define DSP_IWT ((uint64)( 1) << 37) 47 | #define DSP_IRA(v) ((uint64)((v)&0x3F) << 38) 48 | #define DSP_YSEL(v) ((uint64)((v)&0x03) << 45) 49 | #define DSP_XSEL(v) ((uint64)((v)&0x01) << 47) 50 | #define DSP_TWA(v) ((uint64)((v)&0x7F) << 48) 51 | #define DSP_TWT ((uint64)( 1) << 55) 52 | #define DSP_TRA(v) ((uint64)((v)&0x7F) << 56) 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CXXFLAGS=-std=gnu++11 -O2 -fwrapv -Wall 3 | CPPFLAGS=-D_GNU_SOURCE=1 4 | # 5 | M68K_CC=m68k-unknown-elf-gcc 6 | M68K_CFLAGS=-Os -g -std=gnu99 -march=68000 -fwrapv -fsigned-char -fno-strict-aliasing -Wall -Werror -Werror="stack-usage=" -Wstack-usage=64 7 | M68K_AS=m68k-unknown-elf-as 8 | M68K_ASFLAGS=-march=68000 -mcpu=68ec000 9 | M68K_OBJCOPY=m68k-unknown-elf-objcopy 10 | # 11 | SH2_CC=sh-elf-gcc 12 | SH2_CFLAGS=-O2 -std=gnu99 -m2 -fwrapv -fsigned-char -fno-inline -fno-unit-at-a-time -Wall -Wformat=0 13 | SH2_OBJCOPY=sh-elf-objcopy 14 | # 15 | # 16 | # 17 | all: test.iso 18 | 19 | adp68k.elf: Makefile adp68k.c adp68k.h adp68k.ld adp68k-data.h types.h scsp.h dsp-macros.h 20 | $(M68K_CC) $(M68K_CFLAGS) -nostdlib -Xlinker -Tadp68k.ld -o adp68k.elf adp68k.c -lgcc 21 | 22 | adp68k.bin: Makefile adp68k.elf 23 | $(M68K_OBJCOPY) -O binary adp68k.elf adp68k.bin 24 | 25 | adp68k.bin.inc: Makefile adp68k.bin bintoinc 26 | cat adp68k.bin | ./bintoinc > adp68k.bin.inc 27 | 28 | adp68k-data.h: Makefile gen-data.cpp dsp-macros.h tables.h 29 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o gen-data gen-data.cpp 30 | ./gen-data > adp68k-data.h 31 | 32 | adpencode: Makefile adpencode.cpp types.h tables.h 33 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o adpencode adpencode.cpp -lsndfile 34 | 35 | adplink: Makefile adplink.cpp 36 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o adplink adplink.cpp 37 | 38 | bintoinc: Makefile bintoinc.cpp 39 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o bintoinc bintoinc.cpp 40 | # 41 | # 42 | # 43 | # 44 | # 45 | ADP_OBJ=voice-4bit.adp voice-2bit.adp voice-1bit.adp dcbiasend-4bit.adp dcbiasend-2bit.adp dcbiasend-1bit.adp bugloop-2bit.adp long-1bit.adp 46 | 47 | %-4bit.adp: adpencode %.wav 48 | ./adpencode 0 $*.wav $*-4bit.adp 49 | 50 | %-2bit.adp: adpencode %.wav 51 | ./adpencode 1 $*.wav $*-2bit.adp 52 | 53 | %-1bit.adp: adpencode %.wav 54 | ./adpencode 2 $*.wav $*-1bit.adp 55 | 56 | samples.ladp: $(ADP_OBJ) adplink 57 | ./adplink samples.ladp $(ADP_OBJ) > samples.h 58 | 59 | samples.ladp.inc:bintoinc samples.ladp 60 | cat samples.ladp | ./bintoinc > samples.ladp.inc 61 | 62 | test.bin: Makefile samples.ladp.inc test.c test.ld adp68k.h adp68k.bin.inc scsp.h dsp-macros.h types.h sh2.h smpc.h 63 | $(SH2_CC) $(SH2_CFLAGS) -nostdlib -Xlinker -Ttest.ld -o test.elf test.c -lgcc && $(SH2_OBJCOPY) -O binary test.elf test.bin 64 | 65 | test.iso: test.bin 66 | genisoimage -iso-level 1 -sysid "SEGA SEGASATURN" -appid "" -volid "SCSPADPCM_DEMO" -volset "SCSPADPCM_DEMO" -G saturnboot.bin -o test.iso test.bin 67 | # 68 | # 69 | # 70 | .PHONY: clean 71 | clean: 72 | rm --force --one-file-system -- adp68k.elf adp68k.bin adp68k.bin.inc adp68k-data.h gen-data adpencode adplink bintoinc $(ADP_OBJ) samples.h samples.ladp samples.ladp.inc test.elf test.bin test.iso 73 | -------------------------------------------------------------------------------- /adp68k.h: -------------------------------------------------------------------------------- 1 | /* 2 | * adp68k.h 3 | * 4 | * Copyright (C) 2021 celeriyacon - https://github.com/celeriyacon 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 3. This notice may not be removed or altered from any source distribution. 21 | * 22 | */ 23 | 24 | #ifndef ADP68K_H 25 | #define ADP68K_H 26 | 27 | #include "types.h" 28 | #include "scsp.h" 29 | 30 | // Don't change these constants. 31 | #define ADP68K_ACTION_NOP 0x88 32 | #define ADP68K_ACTION_PLAY 0x11 33 | #define ADP68K_ACTION_STOP 0x01 34 | 35 | // 36 | // Volume of 0x4000 = 1.0 37 | // 38 | 39 | typedef struct 40 | { 41 | uint8 action; 42 | uint8 id; 43 | uint16 volume[2]; 44 | } ADPCMChannelControl; 45 | 46 | typedef struct 47 | { 48 | uint32 freq __attribute__((aligned(4))); 49 | uint16 volume; // Output volume, pre-IIR 50 | // 51 | uint16 fb_level; // Self-feedback 52 | uint16 mod_level; // Controls how much the previous operator modulates this operator. 53 | } SawOperatorControl; 54 | 55 | typedef struct 56 | { 57 | // 58 | // Square wave frequency = 44100 / (2 * square_freq) 59 | // Not suitable for music, but probably ok for sound effects. 60 | // Obviously, don't set square_freq to 0 or your toes may explode. 61 | // 62 | uint16 square_freq; 63 | uint16 square_volume[2]; 64 | 65 | uint16 saw_volume[2]; 66 | uint16 saw_iir[3]; 67 | 68 | SawOperatorControl saw_operator[4]; 69 | } PSGControl; 70 | 71 | typedef struct 72 | { 73 | ADPCMChannelControl adpcm[8]; 74 | PSGControl psg; 75 | 76 | // [0]: CD left audio->left out 77 | // [1]: CD right audio->left out 78 | // [2]: CD left audio->right out 79 | // [3]: CD right audio->right out 80 | // 81 | // Normal: { 0x4000, 0x0000, 0x0000, 0x4000 } 82 | // Force mono: { 0x2000, 0x2000, 0x2000, 0x2000 } 83 | // 84 | uint16 cd_volume[4]; 85 | } SoundControlBlock; 86 | 87 | static volatile SoundControlBlock* const adp68k_scblock = (volatile SoundControlBlock*)SCSPVP(0x080); 88 | static volatile uint8* const adp68k_sdbase = (volatile uint8*)SCSPVP(0x2000); 89 | static volatile uint32* const adp68k_effect_table = (volatile uint32*)SCSPVP(0x2000); 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /smpc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * smpc.h 3 | * 4 | * Copyright (C) 2021 celeriyacon - https://github.com/celeriyacon 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 3. This notice may not be removed or altered from any source distribution. 21 | * 22 | */ 23 | 24 | #ifndef GIGASANTA_SMPC_H 25 | #define GIGASANTA_SMPC_H 26 | 27 | #define SMPC_PDR1 (*(volatile uint8*)0x20100075) 28 | #define SMPC_DDR1 (*(volatile uint8*)0x20100079) 29 | 30 | #define SMPC_PDR2 (*(volatile uint8*)0x20100077) 31 | #define SMPC_DDR2 (*(volatile uint8*)0x2010007B) 32 | 33 | #define SMPC_IOSEL (*(volatile uint8*)0x2010007D) 34 | #define SMPC_EXLE (*(volatile uint8*)0x2010007F) 35 | 36 | #define SMPC_COMREG (*(volatile uint8*)0x2010001F) 37 | #define SMPC_SR (*(volatile uint8*)0x20100061) 38 | #define SMPC_SF (*(volatile uint8*)0x20100063) 39 | 40 | #define SMPC_IREG(n) (*(volatile uint8*)(0x20100001 + ((n) << 1))) 41 | #define SMPC_OREG(n) (*(volatile uint8*)(0x20100021 + ((n) << 1))) 42 | 43 | #define SMPC_CMD_MSHON 0x00 44 | #define SMPC_CMD_SSHON 0x02 45 | #define SMPC_CMD_SSHOFF 0x03 46 | 47 | #define SMPC_CMD_SNDON 0x06 48 | #define SMPC_CMD_SNDOFF 0x07 49 | 50 | #define SMPC_CMD_CDON 0x08 51 | #define SMPC_CMD_CDOFF 0x09 52 | 53 | #define SMPC_CMD_SYSRES 0x0D 54 | 55 | #define SMPC_CMD_CKCHG352 0x0E 56 | #define SMPC_CMD_CKCHG320 0x0F 57 | 58 | #define SMPC_CMD_INTBACK 0x10 59 | #define SMPC_CMD_SETTIME 0x16 60 | #define SMPC_CMD_SETSMEM 0x17 61 | 62 | #define SMPC_CMD_NMIREQ 0x18 63 | #define SMPC_CMD_RESENAB 0x19 64 | #define SMPC_CMD_RESDISA 0x1A 65 | 66 | static INLINE void SMPC_SoundOn(void) 67 | { 68 | while(SMPC_SF & 0x1); 69 | SMPC_SF = 0x1; 70 | SMPC_COMREG = SMPC_CMD_SNDON; 71 | while(SMPC_SF & 0x1); 72 | } 73 | 74 | static INLINE void SMPC_SoundOff(void) 75 | { 76 | while(SMPC_SF & 0x1); 77 | SMPC_SF = 0x1; 78 | SMPC_COMREG = SMPC_CMD_SNDOFF; 79 | while(SMPC_SF & 0x1); 80 | } 81 | 82 | static INLINE void SMPC_CDOn(void) 83 | { 84 | while(SMPC_SF & 0x1); 85 | SMPC_SF = 0x1; 86 | SMPC_COMREG = SMPC_CMD_CDON; 87 | while(SMPC_SF & 0x1); 88 | } 89 | 90 | static INLINE void SMPC_CDOff(void) 91 | { 92 | while(SMPC_SF & 0x1); 93 | SMPC_SF = 0x1; 94 | SMPC_COMREG = SMPC_CMD_CDOFF; 95 | while(SMPC_SF & 0x1); 96 | } 97 | 98 | static INLINE void SMPC_SSHOn(void) 99 | { 100 | while(SMPC_SF & 0x1); 101 | SMPC_SF = 0x1; 102 | SMPC_COMREG = SMPC_CMD_SSHON; 103 | while(SMPC_SF & 0x1); 104 | } 105 | 106 | static INLINE void SMPC_SSHOff(void) 107 | { 108 | while(SMPC_SF & 0x1); 109 | SMPC_SF = 0x1; 110 | SMPC_COMREG = SMPC_CMD_SSHOFF; 111 | while(SMPC_SF & 0x1); 112 | } 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /adplink.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * adplink.cpp 3 | * 4 | * Copyright (C) 2021 celeriyacon - https://github.com/celeriyacon 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 3. This notice may not be removed or altered from any source distribution. 21 | * 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "types.h" 32 | 33 | const unsigned driver_size = 0x2000; 34 | 35 | bool write32(FILE* fp, uint32 v) 36 | { 37 | const uint8 tmp[4] = { (uint8)(v >> 24), (uint8)(v >> 16), (uint8)(v >> 8), (uint8)v }; 38 | 39 | return fwrite(tmp, 1, 4, fp) == sizeof(tmp); 40 | } 41 | 42 | static void print_id(const char* path, unsigned n) 43 | { 44 | const char* a = strrchr(path, '/'); 45 | const char* b = strrchr(path, '\\'); 46 | 47 | if((a && b && b > a) || !a) 48 | a = b; 49 | 50 | if(a) 51 | a++; 52 | else 53 | a = path; 54 | 55 | const char* d = strrchr(a, '.'); 56 | 57 | if(!d) 58 | d = a + strlen(a); 59 | 60 | printf("#define SAMPLE_ID_"); 61 | while(a != d) 62 | { 63 | char c = toupper(*a); 64 | 65 | if(!((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))) 66 | c = '_'; 67 | 68 | fputc(c, stdout); 69 | a++; 70 | } 71 | printf(" %u\n", n); 72 | } 73 | 74 | int main(int argc, char* argv[]) 75 | { 76 | int ret = 0; 77 | FILE* fp = NULL; 78 | FILE* ofp = NULL; 79 | std::vector table; 80 | 81 | table.resize(argc - 2); 82 | 83 | if(!(ofp = fopen(argv[1], "wb"))) 84 | { 85 | fprintf(stderr, "Error opening \"%s\": %m\n", argv[1]); 86 | ret = -1; 87 | goto cleanup; 88 | } 89 | 90 | for(unsigned i = 0; i < table.size(); i++) 91 | { 92 | if(!write32(ofp, table[i])) 93 | { 94 | fprintf(stderr, "Error writing to file: %m\n"); 95 | ret = -1; 96 | goto cleanup; 97 | } 98 | } 99 | 100 | for(int i = 2; i < argc; i++) 101 | { 102 | int c; 103 | long fpos; 104 | 105 | if(!(fp = fopen(argv[i], "rb"))) 106 | { 107 | fprintf(stderr, "Error opening \"%s\": %m\n", argv[i]); 108 | ret = -1; 109 | goto cleanup; 110 | } 111 | 112 | print_id(argv[i], i - 2); 113 | 114 | if((fpos = ftell(ofp)) == -1) 115 | { 116 | fprintf(stderr, "Error getting position in file: %m\n"); 117 | ret = -1; 118 | goto cleanup; 119 | } 120 | table[i - 2] = driver_size + fpos; 121 | 122 | while((c = fgetc(fp)) >= 0) 123 | { 124 | if(fputc(c, ofp) == EOF) 125 | { 126 | fprintf(stderr, "Error writing to file: %m\n"); 127 | ret = -1; 128 | goto cleanup; 129 | } 130 | } 131 | 132 | if((fpos = ftell(ofp)) == -1) 133 | { 134 | fprintf(stderr, "Error getting position in file: %m\n"); 135 | ret = -1; 136 | goto cleanup; 137 | } 138 | if(fpos & 1) 139 | { 140 | if(fputc(0x00, ofp) == EOF) 141 | { 142 | fprintf(stderr, "Error writing to file: %m\n"); 143 | ret = -1; 144 | goto cleanup; 145 | } 146 | } 147 | 148 | fclose(fp); 149 | fp = NULL; 150 | } 151 | 152 | if(fseek(ofp, 0, SEEK_SET) == -1) 153 | { 154 | fprintf(stderr, "Error seeking in file: %m\n"); 155 | ret = -1; 156 | goto cleanup; 157 | } 158 | 159 | for(unsigned i = 0; i < table.size(); i++) 160 | { 161 | if(!write32(ofp, table[i])) 162 | { 163 | fprintf(stderr, "Error writing to file: %m\n"); 164 | ret = -1; 165 | goto cleanup; 166 | } 167 | } 168 | // 169 | // 170 | // 171 | cleanup:; 172 | if(fp) 173 | { 174 | fclose(fp); 175 | fp = NULL; 176 | } 177 | 178 | if(ofp) 179 | { 180 | if(fclose(ofp) == EOF) 181 | { 182 | fprintf(stderr, "Error closing file: %m\n"); 183 | ret = -1; 184 | } 185 | ofp = NULL; 186 | } 187 | 188 | return ret; 189 | } 190 | -------------------------------------------------------------------------------- /scsp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * scsp.h 3 | * 4 | * Copyright (C) 2021 celeriyacon - https://github.com/celeriyacon 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 3. This notice may not be removed or altered from any source distribution. 21 | * 22 | */ 23 | 24 | #ifndef GIGASANTA_SCSP_H 25 | #define GIGASANTA_SCSP_H 26 | 27 | #ifdef __m68k__ 28 | #define SCSPVP(offset) ((volatile void*)(offset)) 29 | #else 30 | #define SCSPVP(offset) ((volatile void*)(0x25A00000 + (offset))) 31 | #endif 32 | 33 | #define SCSP(offset) (*(volatile uint16*)SCSPVP(offset)) 34 | #define SCSP16(offset) SCSP(offset) 35 | #define SCSP8(offset) (*(volatile uint8*)SCSPVP(offset)) 36 | #define SCSP_MEM(offset) SCSP(offset) 37 | 38 | #define SCSP_SREG(slot, offset) SCSP16(0x100000 + (slot) * 0x20 + (offset)) 39 | #define SCSP_SREG_HI(slot, offset) SCSP8(0x100000 + (slot) * 0x20 + (offset)) 40 | #define SCSP_SREG_LO(slot, offset) SCSP8(0x100001 + (slot) * 0x20 + (offset)) 41 | 42 | #define SCSP_CREG(offset) SCSP16(0x100400 + (offset)) 43 | #define SCSP_CREG_HI(offset) SCSP8(0x100400 + (offset)) 44 | #define SCSP_CREG_LO(offset) SCSP8(0x100401 + (offset)) 45 | 46 | #define SCSP_SCIEB SCSP_CREG(0x0F << 1) 47 | #define SCSP_SCIPD SCSP_CREG(0x10 << 1) 48 | #define SCSP_SCIRE SCSP_CREG(0x11 << 1) 49 | 50 | #define SCSP_MCIEB SCSP_CREG(0x15 << 1) 51 | #define SCSP_MCIPD SCSP_CREG(0x16 << 1) 52 | #define SCSP_MCIRE SCSP_CREG(0x17 << 1) 53 | 54 | #define SCSP_SCIEB_HI SCSP_CREG_HI(0x0F << 1) 55 | #define SCSP_SCIPD_HI SCSP_CREG_HI(0x10 << 1) 56 | #define SCSP_SCIRE_HI SCSP_CREG_HI(0x11 << 1) 57 | 58 | #define SCSP_MCIEB_HI SCSP_CREG_HI(0x15 << 1) 59 | #define SCSP_MCIPD_HI SCSP_CREG_HI(0x16 << 1) 60 | #define SCSP_MCIRE_HI SCSP_CREG_HI(0x17 << 1) 61 | 62 | #define SCSP_SCIEB_LO SCSP_CREG_LO(0x0F << 1) 63 | #define SCSP_SCIPD_LO SCSP_CREG_LO(0x10 << 1) 64 | #define SCSP_SCIRE_LO SCSP_CREG_LO(0x11 << 1) 65 | 66 | #define SCSP_MCIEB_LO SCSP_CREG_LO(0x15 << 1) 67 | #define SCSP_MCIPD_LO SCSP_CREG_LO(0x16 << 1) 68 | #define SCSP_MCIRE_LO SCSP_CREG_LO(0x17 << 1) 69 | 70 | static INLINE void WaitSampleIRQ(void) 71 | { 72 | SCSP_SCIRE = 0x400; 73 | while(!(SCSP_SCIPD & 0x400)); 74 | } 75 | 76 | static INLINE void SCSP_Init(void) 77 | { 78 | #ifndef __m68k__ 79 | SMPC_SoundOff(); 80 | #endif 81 | 82 | SCSP(0x100400) = (1 << 9); 83 | 84 | for(unsigned slot = 0; slot < 32; slot++) 85 | { 86 | SCSP_SREG(slot, 0x00) = 0x0000; 87 | SCSP_SREG(slot, 0x0A) = 0x001F; 88 | } 89 | SCSP_SREG(0, 0x00) = 1 << 12; 90 | 91 | for(unsigned i = 256; i; i--) 92 | WaitSampleIRQ(); 93 | 94 | for(unsigned t = 0; t < 2; t++) 95 | { 96 | for(uint32 i = 0x100000; i < 0x100400; i += 2) 97 | SCSP(i) = 0; 98 | 99 | for(uint32 i = 0x100402; i < 0x101000; i += 2) 100 | SCSP(i) = 0; 101 | 102 | for(unsigned i = 256; i; i--) 103 | WaitSampleIRQ(); 104 | } 105 | 106 | SCSP_SCIRE = 0xFFFF; 107 | SCSP_MCIRE = 0xFFFF; 108 | 109 | #ifndef __m68k__ 110 | for(unsigned i = 0x25A00000; i < 0x25A80000; i += 2) 111 | *(volatile uint16*)i = 0; 112 | #endif 113 | } 114 | 115 | #include "dsp-macros.h" 116 | 117 | static volatile uint64* const MPROG = (volatile uint64*)SCSPVP(0x100800); // 128 118 | static volatile uint16* const MADRS = (volatile uint16*)SCSPVP(0x100780); // 32 119 | static volatile uint16* const COEF = (volatile uint16*)SCSPVP(0x100700); // 64 120 | 121 | static volatile uint32* const TEMP = (volatile uint32*)SCSPVP(0x100C00); // 128 122 | static volatile uint32* const MEMS = (volatile uint32*)SCSPVP(0x100E00); // 32 123 | static volatile uint16* const EFREG = (volatile uint16*)SCSPVP(0x100EC0); // 16 124 | 125 | #define DSP_MAKE_MEMS(v) (((uint32)((v) & 0xFF) << 16) | (((v) >> 8) & 0xFFFF)) 126 | #define DSP_MAKE_COEF(v) ((v) << 3) 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /sh2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * sh2.h 3 | * 4 | * Copyright (C) 2021 celeriyacon - https://github.com/celeriyacon 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 3. This notice may not be removed or altered from any source distribution. 21 | * 22 | */ 23 | 24 | #define CCR (*(volatile uint8*)0xFFFFFE92) 25 | #define CCR_CE 0x01 26 | #define CCR_ID 0x02 27 | #define CCR_OD 0x04 28 | #define CCR_TW 0x08 29 | #define CCR_CP 0x10 30 | #define CCR_W0 0x40 31 | #define CCR_W1 0x80 32 | 33 | #define SBYCR (*(volatile uint8*)0xFFFFFE91) 34 | #define WTCSR_R (*(volatile uint8*)0xFFFFFE80) 35 | #define WTCNT_R (*(volatile uint8*)0xFFFFFE81) 36 | 37 | #define WRITE_WTCNT(value) (*(volatile uint16*)0xFFFFFE88 = (0x5A00 | (uint8)(value))) 38 | #define WRITE_WTCSR(value) (*(volatile uint16*)0xFFFFFE88 = (0xA500 | (uint8)(value))) 39 | 40 | static INLINE uint32 ReadVBR(void) 41 | { 42 | uint32 ret; 43 | 44 | asm volatile("stc VBR,%0\n\t" : "=r"(ret)); 45 | 46 | return ret; 47 | } 48 | 49 | static INLINE void WriteVBR(uint32 value) 50 | { 51 | asm volatile("ldc %0,VBR\n\t" : :"r"(value) : "memory"); 52 | } 53 | 54 | static INLINE uint32 ReadSR(void) 55 | { 56 | uint32 ret; 57 | 58 | asm volatile("stc SR,%0\n\t" : "=r"(ret)); 59 | 60 | return ret; 61 | } 62 | 63 | static INLINE void WriteSR(uint32 value) 64 | { 65 | asm volatile("ldc %0,SR\n\tnop\n\t" : :"r"(value) : "memory"); 66 | } 67 | 68 | #define SCI_SMR (*(volatile uint8*)0xFFFFFE00) 69 | #define SCI_BRR (*(volatile uint8*)0xFFFFFE01) 70 | #define SCI_SCR (*(volatile uint8*)0xFFFFFE02) 71 | #define SCI_TDR (*(volatile uint8*)0xFFFFFE03) 72 | #define SCI_SSR (*(volatile uint8*)0xFFFFFE04) 73 | #define SCI_RDR (*(volatile uint8*)0xFFFFFE05) 74 | 75 | #define SCI_SSR_MPBT 0x01 76 | #define SCI_SSR_MPB 0x02 77 | #define SCI_SSR_TEND 0x04 78 | #define SCI_SSR_PER 0x08 79 | #define SCI_SSR_FER 0x10 80 | #define SCI_SSR_ORER 0x20 81 | #define SCI_SSR_RDRF 0x40 82 | #define SCI_SSR_TDRE 0x80 83 | 84 | #define ICR (*(volatile uint16_t*)0xFFFFFEE0) 85 | #define IPRA (*(volatile uint16_t*)0xFFFFFEE2) 86 | #define IPRB (*(volatile uint16_t*)0xFFFFFE60) 87 | 88 | #define VCRA (*(volatile uint16_t*)0xFFFFFE62) 89 | #define VCRB (*(volatile uint16_t*)0xFFFFFE64) 90 | #define VCRC (*(volatile uint16_t*)0xFFFFFE66) 91 | #define VCRD (*(volatile uint16_t*)0xFFFFFE68) 92 | #define VCRWDT (*(volatile uint16_t*)0xFFFFFEE4) 93 | 94 | #define TIER (*(volatile uint8*)0xFFFFFE10) 95 | #define FTCSR (*(volatile uint8*)0xFFFFFE11) 96 | #define FRCH (*(volatile uint8*)0xFFFFFE12) 97 | #define FRCL (*(volatile uint8*)0xFFFFFE13) 98 | #define OCRABH (*(volatile uint8*)0xFFFFFE14) 99 | #define OCRABL (*(volatile uint8*)0xFFFFFE15) 100 | #define TCR (*(volatile uint8*)0xFFFFFE16) 101 | #define TOCR (*(volatile uint8*)0xFFFFFE17) 102 | #define FICRH (*(volatile uint8*)0xFFFFFE18) 103 | #define FICRL (*(volatile uint8*)0xFFFFFE19) 104 | 105 | #define DMA_SAR(n) (*(volatile uint32*)(0xFFFFFF80 + ((n) << 4))) 106 | #define DMA_DAR(n) (*(volatile uint32*)(0xFFFFFF84 + ((n) << 4))) 107 | #define DMA_TCR(n) (*(volatile uint32*)(0xFFFFFF88 + ((n) << 4))) 108 | #define DMA_CHCR(n) (*(volatile uint32*)(0xFFFFFF8C + ((n) << 4))) 109 | #define DMA_VCR(n) (*(volatile uint32*)(0xFFFFFFA0 + ((n) << 3))) 110 | #define DMA_DRCR(n) (*(volatile uint8*)(0xFFFFFE71 + (n))) 111 | 112 | #define DMA_OR (*(volatile uint32 *)0xFFFFFFB0) 113 | 114 | static uint32 prev_time; 115 | static uint32 time_accum; 116 | static uint32 time_mult; 117 | 118 | static INLINE void TimeBegin(void) 119 | { 120 | time_mult = 8; 121 | time_accum = 0; 122 | TCR = 0; // /8 123 | TOCR = 0; 124 | prev_time = FRCH << 8; 125 | prev_time |= FRCL << 0; 126 | } 127 | 128 | static INLINE void TimeBeginExt(void) 129 | { 130 | time_mult = 128; 131 | time_accum = 0; 132 | TCR = 0x2; // /128 133 | TOCR = 0; 134 | prev_time = FRCH << 8; 135 | prev_time |= FRCL << 0; 136 | } 137 | 138 | static INLINE void TimeUpdate(void) 139 | { 140 | uint32 cur_time; 141 | 142 | // Order is important, so don't cram them into one statement! 143 | cur_time = FRCH << 8; 144 | cur_time |= FRCL << 0; 145 | 146 | time_accum += (cur_time - prev_time) & 0xFFFF; 147 | prev_time = cur_time; 148 | } 149 | 150 | static INLINE uint32 TimeGet(void) 151 | { 152 | return time_accum * time_mult; 153 | } 154 | 155 | 156 | #define DVSR (*(volatile uint32*)0xFFFFFF00) 157 | #define DVDNT (*(volatile uint32*)0xFFFFFF04) 158 | #define DVCR (*(volatile uint32*)0xFFFFFF08) 159 | #define VCRDIV (*(volatile uint32*)0xFFFFFF0C) 160 | #define DVDNTH (*(volatile uint32*)0xFFFFFF10) 161 | #define DVDNTL (*(volatile uint32*)0xFFFFFF14) 162 | #define DVDNTH_SHADOW (*(volatile uint32*)0xFFFFFF18) 163 | #define DVDNTL_SHADOW (*(volatile uint32*)0xFFFFFF1C) 164 | 165 | #define BCR1 (*(volatile uint32 *)0xFFFFFFE0) 166 | #define BCR2 (*(volatile uint32 *)0xFFFFFFE4) 167 | #define WCR (*(volatile uint32 *)0xFFFFFFE8) 168 | #define MCR (*(volatile uint32 *)0xFFFFFFEC) 169 | #define RTCSR (*(volatile uint32 *)0xFFFFFFF0) 170 | #define RTCNT (*(volatile uint32 *)0xFFFFFFF4) 171 | #define RTCOR (*(volatile uint32 *)0xFFFFFFF8) 172 | -------------------------------------------------------------------------------- /gen-data.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * gen-data.cpp 3 | * 4 | * Copyright (C) 2021 celeriyacon - https://github.com/celeriyacon 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 3. This notice may not be removed or altered from any source distribution. 21 | * 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | #include "types.h" 28 | #include "dsp-macros.h" 29 | #include "tables.h" 30 | 31 | int main(int argc, char* argv[]) 32 | { 33 | uint64 mp[128] = { 0 }; 34 | uint16 scale[0x100] = { 0 }; 35 | 36 | for(unsigned ads = 0; ads < 8; ads++) 37 | { 38 | uint64* mps = &mp[ads << 4]; 39 | // 40 | // 41 | mps[0x0] = DSP_IRA(0x20 + (ads << 1)) | DSP_ADRL | // ADPCM block header byte->ADRS_REG 42 | DSP_XSEL(1) | DSP_ZERO | DSP_YSEL(0x1) | DSP_CRA(0x3E); // ADPCM block header filter selection->SFT_REG 43 | 44 | mps[0x1] = DSP_ADRGB | DSP_TABLE | DSP_MASA(0x0) | DSP_MRT | DSP_NOFL | // Read scale value. 45 | DSP_ADRL | DSP_SHFT0 | DSP_SHFT1 | // ADPCM block header filter->ADRS_REG 46 | DSP_IRA(0x21 + (ads << 1)) | DSP_XSEL(1) | DSP_ZERO | DSP_YSEL(0x1) | DSP_CRA(0x3F); // ADPCM nybble->SFT_REG 47 | // 48 | // 49 | // 50 | mps[0x2] = DSP_TWT | DSP_TWA((ads << 2) + 0x0); // ADPCM nybble->TEMP[adpcmslot * 4 + 0x0] 51 | 52 | mps[0x3] = DSP_ADRGB | DSP_TABLE | DSP_MASA(0x1) | DSP_MRT | DSP_NOFL | // Read first filter coefficient 53 | DSP_IWT | DSP_IWA(0x0); // Scale value->MEMS[0x0] 54 | 55 | mps[0x4] = 0; 56 | 57 | mps[0x5] = DSP_ADRGB | DSP_TABLE | DSP_MASA(0x2) | DSP_MRT | DSP_NOFL | // Read second filter coefficient 58 | DSP_IWT | DSP_IWA(0x1); // First filter coefficient->MEMS[0x1] 59 | 60 | mps[0x6] = 0; 61 | 62 | mps[0x7] = DSP_ADRGB | DSP_TABLE | DSP_MASA(0x3) | DSP_MRT | DSP_NOFL | // Read third filter coefficient 63 | DSP_IWT | DSP_IWA(0x2) | // Second filter cofficient->MEMS[0x2] 64 | DSP_IRA(0x00) | DSP_YRL; // Nybble scale coefficient, MEMS[0x0]->Y_REG 65 | 66 | mps[0x8] = 0; 67 | 68 | mps[0x9] = DSP_IWT | DSP_IWA(0x3); 69 | // 70 | // 71 | // 72 | mps[0xA] = DSP_YSEL(0x2) | DSP_XSEL(0) | DSP_TRA((ads << 2) + 0x0) | DSP_ZERO |// MAC init, nybble * scale 73 | DSP_IRA(0x01) | DSP_YRL; // First filter coefficient, MEMS[0x1]->Y_REG 74 | 75 | mps[0xB] = DSP_YSEL(0x2) | DSP_XSEL(0) | DSP_TRA((ads << 2) + 0x1) | DSP_BSEL(1) | // MAC, first filter coefficient * prev0 76 | DSP_IRA(0x02) | DSP_YRL; // Second filter coefficient, MEMS[0x2]->Y_REG 77 | 78 | mps[0xC] = DSP_YSEL(0x2) | DSP_XSEL(0) | DSP_TRA((ads << 2) + 0x2) | DSP_BSEL(1) | // MAC, second filter coefficient * prev1 79 | DSP_IRA(0x03) | DSP_YRL; // Third filter cofficient, MEMS[0x3]->Y_REG 80 | 81 | mps[0xD] = DSP_YSEL(0x2) | DSP_XSEL(0) | DSP_TRA((ads << 2) + 0x3) | DSP_BSEL(1); // MAC, third filter coefficient * prev2 82 | 83 | mps[0xE] = DSP_SHFT0 | DSP_TWT | DSP_TWA((ads << 2) + 0x0); // MAC output, decoded ADPCM sample->TEMP[ads * 4 + 0x0] 84 | 85 | mps[0xF] = 0; 86 | // 87 | // 88 | // 89 | if(ads & 1) 90 | { 91 | // Apply left output volume. 92 | mps[0x2] |= DSP_TRA((((ads & 0x6) + 0) << 2) + 0x1) | DSP_XSEL(0) | DSP_ZERO | DSP_YSEL(0x1) | DSP_CRA((((ads & 0x6) + 0x0) << 1) + 0x0); 93 | mps[0x3] |= DSP_TRA((((ads & 0x6) + 1) << 2) + 0x1) | DSP_XSEL(0) | DSP_BSEL(1) | DSP_YSEL(0x1) | DSP_CRA((((ads & 0x6) + 0x1) << 1) + 0x0); 94 | mps[0x4] |= DSP_TRA((ads == 5) ? 0x4C : 0x61) | DSP_XSEL(0) | DSP_BSEL(1) | DSP_YSEL(0x1) | DSP_CRA((ads == 7) ? 0x12 : (ads == 5) ? 0x10 : 0x3C); // Saw/Square 95 | mps[0x5] |= DSP_EWA((ads & 0x6) + 0) | DSP_EWT | DSP_SHFT0; // Output left sample 96 | 97 | // Apply right output volume. 98 | mps[0x5] |= DSP_TRA((((ads & 0x6) + 0) << 2) + 0x1) | DSP_XSEL(0) | DSP_ZERO | DSP_YSEL(0x1) | DSP_CRA((((ads & 0x6) + 0x0) << 1) + 0x1); 99 | mps[0x6] |= DSP_TRA((((ads & 0x6) + 1) << 2) + 0x1) | DSP_XSEL(0) | DSP_BSEL(1) | DSP_YSEL(0x1) | DSP_CRA((((ads & 0x6) + 0x1) << 1) + 0x1); 100 | mps[0x7] |= DSP_TRA((ads == 5) ? 0x4C : 0x61) | DSP_XSEL(0) | DSP_BSEL(1) | DSP_YSEL(0x1) | DSP_CRA((ads == 7) ? 0x13 : (ads == 5) ? 0x11 : 0x3C);// Saw/square 101 | mps[0x8] |= DSP_EWA((ads & 0x6) + 1) | DSP_EWT | DSP_SHFT0; // Output right sample 102 | 103 | if(ads == 1 || ads == 3) 104 | { 105 | mps[0x8] |= DSP_ZERO | DSP_XSEL(1) | DSP_IRA(0x30) | DSP_YSEL(0x1) | DSP_CRA(0x20 + (ads >> 1)); // CD audio left in 106 | mps[0x9] |= DSP_BSEL(1) | DSP_XSEL(1) | DSP_IRA(0x31) | DSP_YSEL(0x1) | DSP_CRA(0x22 + (ads >> 1)); // CD audio right in 107 | mps[0xA] |= DSP_SHFT0 | DSP_EWT | DSP_EWA(0x8 + (ads >> 1)); // -> audio out 108 | } 109 | } 110 | else if(ads == 0) 111 | { 112 | // 113 | // Square wave, parts 1 and 3. 114 | // 115 | mps[0x2] |= DSP_BSEL(0) | DSP_TRA(0x44) | DSP_XSEL(1) | DSP_IRA(0x1E) | DSP_YSEL(0x1) | DSP_CRA(0x3D); 116 | mps[0x3] |= DSP_TWT | DSP_TWA(0x44) | DSP_FRCL; 117 | mps[0x4] |= DSP_BSEL(0) | DSP_TRA(0x44) | DSP_XSEL(1) | DSP_IRA(0x10) | DSP_YSEL(0x0); 118 | mps[0x5] |= DSP_TWT | DSP_TWA(0x43) | 119 | DSP_ZERO | DSP_XSEL(1) | DSP_IRA(0x1F) | DSP_YSEL(0x0); 120 | mps[0x6] |= DSP_SHFT0 | DSP_SHFT1 | DSP_FRCL; 121 | // 122 | // Saw operator 0 123 | // 124 | mps[0x6] |= DSP_ZERO | DSP_XSEL(0) | DSP_TRA(0x5D) | DSP_YSEL(0x1) | DSP_CRA(0x1B); 125 | mps[0x7] |= DSP_BSEL(1) | DSP_XSEL(0) | DSP_TRA(0x51) | DSP_YSEL(0x1) | DSP_CRA(0x14); 126 | mps[0x8] |= DSP_BSEL(1) | DSP_XSEL(1) | DSP_IRA(0x14) | DSP_YSEL(0x1) | DSP_CRA(0x3B); 127 | mps[0x9] |= DSP_SHFT1 | DSP_TWT | DSP_TWA(0x50); 128 | } 129 | else if(ads == 2) 130 | { 131 | // 132 | // Square wave, part 2, Oscillatron's Revenge 133 | // 134 | mps[0x2] |= DSP_BSEL(0) | DSP_TRA(0x48) | DSP_XSEL(1) | DSP_IRA(0x1F) | DSP_YSEL(0x0); 135 | mps[0x3] |= DSP_SHFT0 | DSP_SHFT1 | DSP_TWT | DSP_TWA(0x48); 136 | mps[0x4] |= DSP_BSEL(0) | DSP_TRA(0x48) | DSP_XSEL(1) | DSP_IRA(0x1F) | DSP_YSEL(0x0); 137 | mps[0x5] |= DSP_SHFT0 | DSP_SHFT1 | DSP_TWT | DSP_TWA(0x47); 138 | // 139 | mps[0x6] |= DSP_BSEL(0) | DSP_TRA(0x47) | DSP_XSEL(1) | DSP_IRA(0x1F) | DSP_YSEL(0x1) | DSP_CRA(0x3F); // Center square wave 140 | mps[0x7] |= DSP_SHFT0 | DSP_SHFT1 | DSP_TWT | DSP_TWA(0x4B); 141 | // 142 | // Saw operator 1 143 | // 144 | mps[0x7] |= DSP_ZERO | DSP_XSEL(0) | DSP_TRA(0x50) | DSP_YSEL(0x1) | DSP_CRA(0x1C); 145 | mps[0x8] |= DSP_BSEL(1) | DSP_XSEL(0) | DSP_TRA(0x55) | DSP_YSEL(0x1) | DSP_CRA(0x15); 146 | mps[0x9] |= DSP_BSEL(1) | DSP_XSEL(1) | DSP_IRA(0x15) | DSP_YSEL(0x1) | DSP_CRA(0x3B); 147 | mps[0xA] |= DSP_SHFT1 | DSP_TWT | DSP_TWA(0x54); 148 | } 149 | else if(ads == 4) 150 | { 151 | // 152 | // Saw operator 2 153 | // 154 | mps[0x2] |= DSP_ZERO | DSP_XSEL(0) | DSP_TRA(0x54) | DSP_YSEL(0x1) | DSP_CRA(0x1D); 155 | mps[0x3] |= DSP_BSEL(1) | DSP_XSEL(0) | DSP_TRA(0x59) | DSP_YSEL(0x1) | DSP_CRA(0x16); 156 | mps[0x4] |= DSP_BSEL(1) | DSP_XSEL(1) | DSP_IRA(0x16) | DSP_YSEL(0x1) | DSP_CRA(0x3B); 157 | mps[0x5] |= DSP_SHFT1 | DSP_TWT | DSP_TWA(0x58); 158 | // 159 | // Saw operator 3 160 | // 161 | mps[0x6] |= DSP_ZERO | DSP_XSEL(0) | DSP_TRA(0x58) | DSP_YSEL(0x1) | DSP_CRA(0x1E); 162 | mps[0x7] |= DSP_BSEL(1) | DSP_XSEL(0) | DSP_TRA(0x5D) | DSP_YSEL(0x1) | DSP_CRA(0x17); 163 | mps[0x8] |= DSP_BSEL(1) | DSP_XSEL(1) | DSP_IRA(0x17) | DSP_YSEL(0x1) | DSP_CRA(0x3B); 164 | mps[0x9] |= DSP_SHFT1 | DSP_TWT | DSP_TWA(0x5C); 165 | } 166 | else if(ads == 6) 167 | { 168 | // 169 | // Saw mix and IIR 170 | // 171 | mps[0x2] |= DSP_ZERO | DSP_XSEL(0) | DSP_TRA(0x50) | DSP_YSEL(0x1) | DSP_CRA(0x30); 172 | mps[0x3] |= DSP_BSEL(1) | DSP_XSEL(0) | DSP_TRA(0x54) | DSP_YSEL(0x1) | DSP_CRA(0x31); 173 | mps[0x4] |= DSP_BSEL(1) | DSP_XSEL(0) | DSP_TRA(0x58) | DSP_YSEL(0x1) | DSP_CRA(0x32); 174 | mps[0x5] |= DSP_BSEL(1) | DSP_XSEL(0) | DSP_TRA(0x5C) | DSP_YSEL(0x1) | DSP_CRA(0x33); 175 | mps[0x6] |= DSP_SHFT1 | DSP_TWT | DSP_TWA(0x5D) | 176 | DSP_ZERO | DSP_XSEL(0) | DSP_TRA(0x5E) | DSP_YSEL(0x1) | DSP_CRA(0x18); 177 | 178 | mps[0x7] |= DSP_BSEL(1) | DSP_XSEL(0) | DSP_TRA(0x5F) | DSP_YSEL(0x1) | DSP_CRA(0x19); 179 | mps[0x8] |= DSP_BSEL(1) | DSP_XSEL(0) | DSP_TRA(0x61) | DSP_YSEL(0x1) | DSP_CRA(0x1A); 180 | mps[0x9] |= DSP_SHFT0 | DSP_TWT | DSP_TWA(0x60); 181 | } 182 | } 183 | 184 | printf("uint64 mprog[128] =\n{\n"); 185 | 186 | for(unsigned i = 0; i < 128; i++) 187 | printf(" 0x%016llx,\n", (unsigned long long)mp[i]); 188 | 189 | printf("};\n\n"); 190 | // 191 | // 192 | // 193 | printf("uint16 scale[0x100] =\n{\n"); 194 | for(unsigned i = 0; i < 256; i++) 195 | { 196 | unsigned sv = scale_tab[i & 0xF]; 197 | 198 | assert(sv >= 0 && sv <= 4095); 199 | 200 | scale[0x080 ^ i] = sv << 3; 201 | } 202 | 203 | for(unsigned i = 0; i < 0x100; i++) 204 | printf(" 0x%04x,\n", scale[i]); 205 | printf("};\n\n"); 206 | // 207 | // 208 | printf("uint16 coeff[0x30] =\n{\n"); 209 | for(unsigned i = 0; i < 0x30; i++) 210 | printf(" 0x%04x,\n", (uint16)((uint32)filter_tab[(i ^ 0x8) & 0xF][i >> 4] << 3)); 211 | printf("};\n\n"); 212 | // 213 | // 214 | printf("uint16 selector[0x8] =\n{\n"); 215 | for(unsigned i = 0; i < 8; i++) 216 | printf(" 0x%04x,\n", ((i + 0) & 7) << 5); 217 | printf("};\n\n"); 218 | // 219 | // 220 | printf("uint8 isolator[0x200] =\n{\n"); 221 | for(unsigned i = 0; i < 512; i++) 222 | { 223 | const uint8 bv = (i >> 1) ^ encoded_byte_xor_tab[0] ^ 0x80; 224 | 225 | printf(" 0x%02x,\n", ((bv >> ((i & 0x1) << 2)) & 0xF) << 4); 226 | } 227 | printf("};\n\n"); 228 | // 229 | // 230 | printf("uint8 isolator2bit[0x400] =\n{\n"); 231 | for(unsigned i = 0; i < 1024; i++) 232 | { 233 | static const uint8 tab[4] = { 0x30, 0x70, 0x90, 0xD0 }; 234 | const uint8 bv = (i >> 2) ^ encoded_byte_xor_tab[1] ^ 0x80; 235 | 236 | printf(" 0x%02x,\n", tab[(bv >> ((i & 0x3) << 1)) & 0x3]); 237 | } 238 | printf("};\n\n"); 239 | // 240 | // 241 | printf("uint8 isolator1bit[0x800] =\n{\n"); 242 | for(unsigned i = 0; i < 2048; i++) 243 | { 244 | static const uint8 tab[2] = { 0x30, 0xD0 }; 245 | const uint8 bv = (i >> 3) ^ encoded_byte_xor_tab[2] ^ 0x80; 246 | 247 | printf(" 0x%02x,\n", tab[(bv >> (i & 0x7)) & 0x1]); 248 | } 249 | printf("};\n\n"); 250 | 251 | return 0; 252 | } 253 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * test.c 3 | * 4 | * Copyright (C) 2021 celeriyacon - https://github.com/celeriyacon 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 3. This notice may not be removed or altered from any source distribution. 21 | * 22 | */ 23 | 24 | #include "types.h" 25 | #include "sh2.h" 26 | #include "smpc.h" 27 | #include "scsp.h" 28 | 29 | #include "adp68k.h" 30 | 31 | #include "samples.h" 32 | 33 | static const uint8 driver_data[0x2000] = 34 | { 35 | #include "adp68k.bin.inc" 36 | }; 37 | 38 | static const uint8 sample_data[] = 39 | { 40 | #include "samples.ladp.inc" 41 | }; 42 | 43 | static void Wait(uint32 t) 44 | { 45 | TimeBegin(); do { TimeUpdate(); } while(TimeGet() < t); 46 | } 47 | 48 | static void TestADPCMVoice(void) 49 | { 50 | static const uint8 ids[3] = { SAMPLE_ID_VOICE_4BIT, SAMPLE_ID_VOICE_2BIT, SAMPLE_ID_VOICE_1BIT }; 51 | 52 | for(unsigned i = 0; i < 3; i++) 53 | { 54 | while(SCSP_SCIPD & 0x20); 55 | volatile ADPCMChannelControl* adpcc = &adp68k_scblock->adpcm[(0 + i) & 0x7]; 56 | 57 | adpcc->id = ids[i % 3]; 58 | adpcc->volume[0] = 0x4000; 59 | adpcc->volume[1] = 0x4000; 60 | adpcc->action = ADP68K_ACTION_PLAY; 61 | SCSP_SCIPD_LO = 0x20; 62 | 63 | Wait(28000000 * 3); 64 | } 65 | 66 | for(unsigned i = 0; i < 8; i++) 67 | { 68 | while(SCSP_SCIPD & 0x20); 69 | volatile ADPCMChannelControl* adpcc = &adp68k_scblock->adpcm[(0 + i) & 0x7]; 70 | 71 | adpcc->id = ids[i % 3]; 72 | adpcc->volume[0] = (!(i & 1) ? 0x4000 : 0x0000); 73 | adpcc->volume[1] = ( (i & 1) ? 0x4000 : 0x0000); 74 | adpcc->action = ADP68K_ACTION_PLAY; 75 | SCSP_SCIPD_LO = 0x20; 76 | 77 | Wait(10000000); 78 | } 79 | Wait(28000000 * 3); 80 | } 81 | 82 | static void TestADPCMDCBias(void) 83 | { 84 | static const uint8 ids[] = { SAMPLE_ID_DCBIASEND_4BIT, SAMPLE_ID_DCBIASEND_2BIT, SAMPLE_ID_DCBIASEND_1BIT }; 85 | 86 | for(unsigned i = 0; i < sizeof(ids) / sizeof(ids[0]); i++) 87 | { 88 | volatile ADPCMChannelControl* adpcc = &adp68k_scblock->adpcm[0]; 89 | 90 | while(SCSP_SCIPD & 0x20); 91 | adpcc->id = ids[i]; 92 | adpcc->volume[0] = 0x4000; 93 | adpcc->volume[1] = 0x4000; 94 | adpcc->action = ADP68K_ACTION_PLAY; 95 | SCSP_SCIPD_LO = 0x20; 96 | 97 | Wait(40000000); 98 | 99 | while(SCSP_SCIPD & 0x20); 100 | adpcc->id = ids[i]; 101 | adpcc->volume[0] = 0x4000; 102 | adpcc->volume[1] = 0x4000; 103 | adpcc->action = ADP68K_ACTION_PLAY; 104 | SCSP_SCIPD_LO = 0x20; 105 | 106 | Wait(28000000 / 2); 107 | 108 | while(SCSP_SCIPD & 0x20); 109 | adpcc->volume[0] = 0x4000; 110 | adpcc->volume[1] = 0x4000; 111 | adpcc->action = ADP68K_ACTION_STOP; 112 | SCSP_SCIPD_LO = 0x20; 113 | Wait(28000000 / 2); 114 | } 115 | } 116 | 117 | static void TestADPCMLoop(void) 118 | { 119 | for(unsigned i = 0; i < 8; i++) 120 | { 121 | volatile ADPCMChannelControl* adpcc = &adp68k_scblock->adpcm[i]; 122 | 123 | while(SCSP_SCIPD & 0x20); 124 | adpcc->id = SAMPLE_ID_BUGLOOP_2BIT; 125 | adpcc->volume[0] = (i & 4) ? 0 : (0x4000 >> (i & 3)); 126 | adpcc->volume[1] = !(i & 4) ? 0 : (0x4000 >> (i & 3)); 127 | adpcc->action = ADP68K_ACTION_PLAY; 128 | SCSP_SCIPD_LO = 0x20; 129 | 130 | Wait(40000000); 131 | } 132 | 133 | while(SCSP_SCIPD & 0x20); 134 | for(unsigned i = 0; i < 8; i++) 135 | { 136 | volatile ADPCMChannelControl* adpcc = &adp68k_scblock->adpcm[i]; 137 | 138 | adpcc->action = ADP68K_ACTION_STOP; 139 | } 140 | SCSP_SCIPD_LO = 0x20; 141 | } 142 | 143 | static void TestADPCMLong(void) 144 | { 145 | volatile ADPCMChannelControl* adpcc = &adp68k_scblock->adpcm[0]; 146 | 147 | while(SCSP_SCIPD & 0x20); 148 | adpcc->id = SAMPLE_ID_LONG_1BIT; 149 | adpcc->volume[0] = 0x4000; 150 | adpcc->volume[1] = 0x4000; 151 | adpcc->action = ADP68K_ACTION_PLAY; 152 | SCSP_SCIPD_LO = 0x20; 153 | 154 | Wait(324000000); 155 | 156 | while(SCSP_SCIPD & 0x20); 157 | adpcc->action = ADP68K_ACTION_STOP; 158 | SCSP_SCIPD_LO = 0x20; 159 | } 160 | 161 | static void TestSquare(void) 162 | { 163 | TimeBegin(); 164 | for(unsigned i = 1; i < 1024; i++) 165 | { 166 | while(SCSP_SCIPD & 0x20); 167 | adp68k_scblock->psg.square_freq = i; 168 | adp68k_scblock->psg.square_volume[0] = ((i & 0x080) ? 0x0000 : 0x1000) >> (i >> 9); 169 | adp68k_scblock->psg.square_volume[1] = ((i & 0x180) ? 0x1000 : 0x0000) >> (i >> 9); 170 | SCSP_SCIPD_LO = 0x20; 171 | 172 | do { TimeUpdate(); } while(TimeGet() < i * 100000); 173 | } 174 | 175 | while(SCSP_SCIPD & 0x20); 176 | adp68k_scblock->psg.square_freq = 1; 177 | adp68k_scblock->psg.square_volume[0] = 0; 178 | adp68k_scblock->psg.square_volume[1] = 0; 179 | SCSP_SCIPD_LO = 0x20; 180 | } 181 | 182 | static void TestNoise(void) 183 | { 184 | volatile PSGControl* psgc = &adp68k_scblock->psg; 185 | 186 | psgc->saw_operator[0].freq = DSP_MAKE_MEMS(-7896769); 187 | psgc->saw_operator[1].freq = DSP_MAKE_MEMS(-6936619); 188 | psgc->saw_operator[2].freq = DSP_MAKE_MEMS(-4583429); 189 | psgc->saw_operator[3].freq = DSP_MAKE_MEMS(-5991397); 190 | 191 | psgc->saw_operator[0].fb_level = DSP_MAKE_COEF(3767); 192 | psgc->saw_operator[1].fb_level = DSP_MAKE_COEF(3643); 193 | psgc->saw_operator[2].fb_level = DSP_MAKE_COEF(3271); 194 | psgc->saw_operator[3].fb_level = DSP_MAKE_COEF(2927); 195 | 196 | psgc->saw_operator[0].mod_level = DSP_MAKE_COEF(4096); 197 | psgc->saw_operator[1].mod_level = DSP_MAKE_COEF(4096); 198 | psgc->saw_operator[2].mod_level = DSP_MAKE_COEF(4096); 199 | psgc->saw_operator[3].mod_level = DSP_MAKE_COEF(4096); 200 | 201 | psgc->saw_operator[0].volume = 0; 202 | psgc->saw_operator[1].volume = 0; 203 | psgc->saw_operator[2].volume = 0; 204 | psgc->saw_operator[3].volume = DSP_MAKE_COEF(2048); 205 | // 206 | // 207 | // 208 | TimeBegin(); 209 | for(unsigned i = 0; i < 1536; i++) 210 | { 211 | unsigned m = (i + 1) * 0x010; 212 | 213 | if(m > 0x7FF0) 214 | m = 0x7FF0; 215 | 216 | while(SCSP_SCIPD & 0x20); 217 | adp68k_scblock->psg.saw_iir[0] = m; 218 | adp68k_scblock->psg.saw_iir[1] = 0x0000; 219 | adp68k_scblock->psg.saw_iir[2] = 0x4000 - (m >> 1); 220 | 221 | adp68k_scblock->psg.saw_volume[0] = 0x1000; 222 | adp68k_scblock->psg.saw_volume[1] = 0x1000; 223 | SCSP_SCIPD_LO = 0x20; 224 | 225 | do { TimeUpdate(); } while(TimeGet() < (1 + i) * 220000); 226 | } 227 | 228 | while(SCSP_SCIPD & 0x20); 229 | adp68k_scblock->psg.saw_volume[0] = 0; 230 | adp68k_scblock->psg.saw_volume[1] = 0; 231 | SCSP_SCIPD_LO = 0x20; 232 | } 233 | 234 | static void TestSawVroom(void) 235 | { 236 | while(SCSP_SCIPD & 0x20); 237 | volatile PSGControl* psgc = &adp68k_scblock->psg; 238 | 239 | adp68k_scblock->psg.saw_iir[0] = 0x4000; 240 | adp68k_scblock->psg.saw_iir[1] = -0x2000; 241 | adp68k_scblock->psg.saw_iir[2] = 0x1000; 242 | 243 | psgc->saw_volume[0] = 0x1000; 244 | psgc->saw_volume[1] = 0x1000; 245 | 246 | psgc->saw_operator[0].fb_level = DSP_MAKE_COEF(2048); 247 | psgc->saw_operator[1].fb_level = DSP_MAKE_COEF(2048); 248 | psgc->saw_operator[2].fb_level = DSP_MAKE_COEF(2048); 249 | psgc->saw_operator[3].fb_level = DSP_MAKE_COEF(2048); 250 | 251 | psgc->saw_operator[0].mod_level = DSP_MAKE_COEF(8); 252 | psgc->saw_operator[1].mod_level = DSP_MAKE_COEF(8); 253 | psgc->saw_operator[2].mod_level = DSP_MAKE_COEF(16); 254 | psgc->saw_operator[3].mod_level = DSP_MAKE_COEF(8); 255 | 256 | psgc->saw_operator[0].volume = DSP_MAKE_COEF(256); 257 | psgc->saw_operator[1].volume = DSP_MAKE_COEF(512); 258 | psgc->saw_operator[2].volume = DSP_MAKE_COEF(1024); 259 | psgc->saw_operator[3].volume = DSP_MAKE_COEF(2048); 260 | 261 | SCSP_SCIPD_LO = 0x20; 262 | 263 | TimeBegin(); 264 | for(unsigned i = 128; i < 1024; i++) 265 | { 266 | while(SCSP_SCIPD & 0x20); 267 | psgc->saw_operator[0].freq = DSP_MAKE_MEMS(-1666 * 32 * i / 512); 268 | psgc->saw_operator[1].freq = DSP_MAKE_MEMS(-3000 * 32 * i / 512); 269 | psgc->saw_operator[2].freq = DSP_MAKE_MEMS(-2000 * 32 * i / 512); 270 | psgc->saw_operator[3].freq = DSP_MAKE_MEMS(-1333 * 32 * i / 512); 271 | SCSP_SCIPD_LO = 0x20; 272 | 273 | do { TimeUpdate(); } while(TimeGet() < (1 + (i - 128)) * 200000); 274 | } 275 | 276 | while(SCSP_SCIPD & 0x20); 277 | adp68k_scblock->psg.saw_volume[0] = 0; 278 | adp68k_scblock->psg.saw_volume[1] = 0; 279 | SCSP_SCIPD_LO = 0x20; 280 | } 281 | 282 | static void TestNightmare(void) 283 | { 284 | while(SCSP_SCIPD & 0x20); 285 | volatile PSGControl* psgc = &adp68k_scblock->psg; 286 | 287 | adp68k_scblock->psg.saw_iir[0] = 0x2000; 288 | adp68k_scblock->psg.saw_iir[1] = 0x0000; 289 | adp68k_scblock->psg.saw_iir[2] = 0x2000; 290 | 291 | psgc->saw_volume[0] = 0x1000; 292 | psgc->saw_volume[1] = 0x1000; 293 | 294 | psgc->saw_operator[0].fb_level = DSP_MAKE_COEF(2048); 295 | psgc->saw_operator[1].fb_level = DSP_MAKE_COEF(2048); 296 | psgc->saw_operator[2].fb_level = DSP_MAKE_COEF(2048); 297 | psgc->saw_operator[3].fb_level = DSP_MAKE_COEF(2048); 298 | 299 | psgc->saw_operator[0].mod_level = DSP_MAKE_COEF(16); 300 | psgc->saw_operator[1].mod_level = DSP_MAKE_COEF(2); 301 | psgc->saw_operator[2].mod_level = DSP_MAKE_COEF(2); 302 | psgc->saw_operator[3].mod_level = DSP_MAKE_COEF(2); 303 | 304 | psgc->saw_operator[0].volume = DSP_MAKE_COEF(-512); 305 | psgc->saw_operator[1].volume = DSP_MAKE_COEF(-512); 306 | psgc->saw_operator[2].volume = DSP_MAKE_COEF(512); 307 | psgc->saw_operator[3].volume = DSP_MAKE_COEF(512); 308 | SCSP_SCIPD_LO = 0x20; 309 | 310 | uint32 wait_accum = 0; 311 | 312 | TimeBegin(); 313 | for(int f = 248; f <= 568; f++) 314 | { 315 | for(int v = 0; v < 16; v++) 316 | { 317 | uint16 vt[2]; 318 | 319 | vt[0 ^ (f & 1)] = (0x3000 * (v + 1)) >> 4; 320 | vt[1 ^ (f & 1)] = (0x3000 * (16 - (v + 1))) >> 4; 321 | 322 | while(SCSP_SCIPD & 0x20); 323 | psgc->saw_volume[0] = vt[0]; 324 | psgc->saw_volume[1] = -vt[1]; 325 | 326 | psgc->saw_operator[0].freq = DSP_MAKE_MEMS(-( 5121 * f >> 8)); 327 | psgc->saw_operator[1].freq = DSP_MAKE_MEMS(-(36451 * f >> 8)); 328 | psgc->saw_operator[2].freq = DSP_MAKE_MEMS(-(36450 * f >> 8)); 329 | psgc->saw_operator[3].freq = DSP_MAKE_MEMS(-(25600 * f >> 8)); 330 | SCSP_SCIPD_LO = 0x20; 331 | 332 | wait_accum += 1000000 / 2 - ((f - 192) * 1000); 333 | do { TimeUpdate(); } while(TimeGet() < wait_accum); 334 | } 335 | } 336 | 337 | while(SCSP_SCIPD & 0x20); 338 | psgc->saw_volume[0] = 0x0000; 339 | psgc->saw_volume[1] = 0x0000; 340 | SCSP_SCIPD_LO = 0x20; 341 | } 342 | 343 | static void LoadDriver(void) 344 | { 345 | SMPC_SoundOff(); 346 | SCSP_Init(); 347 | SCSP_MCIRE = 0xFFFF; 348 | SCSP_SCIRE = 0xFFFF; 349 | // 350 | for(unsigned i = 0; i < sizeof(driver_data); i++) 351 | SCSP8(i) = driver_data[i]; 352 | // 353 | SMPC_SoundOn(); 354 | while(!(SCSP_MCIPD & 0x20)); 355 | SCSP_MCIRE_LO = 0x20; 356 | } 357 | 358 | static void LoadSamples(void) 359 | { 360 | while(SCSP_SCIPD & 0x20); 361 | 362 | for(unsigned i = 0; i < 8; i++) 363 | adp68k_scblock->adpcm[i].action = ADP68K_ACTION_STOP; 364 | 365 | SCSP_SCIPD_LO = 0x20; 366 | while(SCSP_SCIPD & 0x20); 367 | // 368 | // 369 | for(unsigned i = 0; i < sizeof(sample_data); i++) 370 | adp68k_sdbase[i] = sample_data[i]; 371 | } 372 | 373 | void start(void) asm("start") __attribute__((noreturn)) __attribute__((section(".init"))); 374 | void start(void) 375 | { 376 | WriteSR(0xF0); 377 | // 378 | LoadDriver(); 379 | LoadSamples(); 380 | 381 | for(;;) 382 | { 383 | TestADPCMVoice(); 384 | TestADPCMDCBias(); 385 | TestADPCMLoop(); 386 | TestADPCMLong(); 387 | TestSquare(); 388 | TestNoise(); 389 | TestSawVroom(); 390 | TestNightmare(); 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /adpencode.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * adpencode.cpp 3 | * 4 | * Copyright (C) 2021 celeriyacon - https://github.com/celeriyacon 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 3. This notice may not be removed or altered from any source distribution. 21 | * 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "types.h" 36 | 37 | #include "tables.h" 38 | 39 | enum : unsigned { max_samples_per_block = 16 }; 40 | enum : unsigned { max_bytes_per_block = 9 }; 41 | 42 | struct ADContext 43 | { 44 | unsigned samples_per_block; 45 | unsigned samples_per_byte; 46 | unsigned bits_per_sample; 47 | unsigned encoded_byte_xor; 48 | int32 samps[3]; 49 | }; 50 | 51 | static inline int32 CalcPredicted(ADContext* const c, const uint8 pwhich) 52 | { 53 | int32 predicted = 0; 54 | 55 | for(unsigned j = 0; j < 3; j++) 56 | { 57 | predicted += ((int64)c->samps[j] * filter_tab[pwhich][j]) >> 12; 58 | predicted = sign_extend(26, predicted); 59 | } 60 | 61 | return predicted; 62 | } 63 | 64 | static inline int32 DecodeSample(ADContext* const c, const uint8 shift, const int32 predicted, unsigned encoded) 65 | { 66 | const int32 scale = scale_tab[shift]; 67 | int32 ret = predicted; 68 | 69 | if(c->bits_per_sample == 1) 70 | { 71 | const int tab[2] = { 3, -3 }; 72 | 73 | ret += ((int64)tab[encoded] * 524288 * scale) >> 12; 74 | } 75 | else if(c->bits_per_sample == 2) 76 | { 77 | const int tab[4] = { 3, 7, -7, -3 }; 78 | 79 | ret += ((int64)tab[encoded] * 524288 * scale) >> 12; 80 | } 81 | else 82 | ret += ((int64)sign_extend(4, encoded) * 524288 * scale) >> 12; 83 | 84 | ret = sign_extend(26, ret) << 1; 85 | 86 | return ret; 87 | } 88 | 89 | static inline void PushSampleHistory(ADContext* const c, int32 decoded) 90 | { 91 | c->samps[2] = c->samps[1]; 92 | c->samps[1] = c->samps[0]; 93 | c->samps[0] = decoded; 94 | } 95 | 96 | static inline void TestEncodeBlock(ADContext* c, uint8 pwhich, uint8 shift, const int16* input, int16* output, uint8* nibbles_output) 97 | { 98 | for(unsigned i = 0; i < c->samples_per_block; i++) 99 | { 100 | const int32 predicted = CalcPredicted(c, pwhich);; 101 | int32 min_error = 0x7FFFFFFF; 102 | uint32 gonib = 0; 103 | int32 gopt = 0; 104 | 105 | for(unsigned trynib = 0x0; trynib < (1U << c->bits_per_sample); trynib++) 106 | { 107 | int32 pt = DecodeSample(c, shift, predicted, trynib); 108 | const int32 error = abs(input[i] - (pt >> 8)); 109 | 110 | if(error < min_error) 111 | { 112 | gonib = trynib; 113 | gopt = pt; 114 | min_error = error; 115 | } 116 | } 117 | 118 | PushSampleHistory(c, gopt); 119 | 120 | nibbles_output[i] = gonib; 121 | output[i] = gopt >> 8; 122 | } 123 | } 124 | 125 | static inline void DecodeBlock(ADContext* const c, const uint8 header, uint8* const encoded_samples, int16* output) 126 | { 127 | const uint8 shift = header & 0xF; 128 | const uint8 pw = header >> 4; 129 | 130 | for(unsigned i = 0; i < c->samples_per_block; i++) 131 | { 132 | const unsigned encoded = ((encoded_samples[(i / c->samples_per_byte)] ^ c->encoded_byte_xor) >> ((i % c->samples_per_byte) * c->bits_per_sample)) & ((1U << c->bits_per_sample) - 1); 133 | const int32 predicted = CalcPredicted(c, pw); 134 | const int32 decoded = DecodeSample(c, shift, predicted, encoded); 135 | 136 | PushSampleHistory(c, decoded); 137 | 138 | if(output) 139 | output[i] = decoded >> 8; 140 | } 141 | } 142 | 143 | static inline int32 EncodeBlock(ADContext* c, const int16* input, int16* output, uint8* block_data, bool is_loop_target) 144 | { 145 | double max_error = 0x7FFFFFFF; 146 | unsigned used_pw = 0; 147 | unsigned used_shift = 0; 148 | ADContext c_gowith; 149 | uint8 nibbles[max_samples_per_block]; 150 | unsigned pw_min = 0; 151 | unsigned pw_bound = FILTER_TAB_COUNT; 152 | 153 | if(is_loop_target) 154 | { 155 | pw_min = 0x01; 156 | pw_bound = pw_min + 1; 157 | assert(!(filter_tab[pw_min][0] | filter_tab[pw_min][1] | filter_tab[pw_min][2])); 158 | } 159 | 160 | for(unsigned pw = pw_min; pw < pw_bound; pw++) 161 | { 162 | for(unsigned shift = 0; shift < 16; shift++) 163 | { 164 | ADContext tmpc = *c; 165 | double error = 0; 166 | uint8 nibbles_tmp[max_samples_per_block]; 167 | int16 output_tmp[max_samples_per_block]; 168 | 169 | TestEncodeBlock(&tmpc, pw, shift, input, output_tmp, nibbles_tmp); 170 | 171 | for(unsigned i = 0; i < c->samples_per_block; i++) 172 | error += abs(input[i] - output_tmp[i]) * sqrtf(1.0 + fabsf(i - ((c->samples_per_block - 1) * 0.5)) / 2); 173 | 174 | if(error < max_error) 175 | { 176 | memcpy(nibbles, nibbles_tmp, sizeof(nibbles)); 177 | memcpy(output, output_tmp, sizeof(output_tmp)); 178 | max_error = error; 179 | used_pw = pw; 180 | used_shift = shift; 181 | c_gowith = tmpc; 182 | } 183 | } 184 | } 185 | 186 | *c = c_gowith; 187 | 188 | // 189 | // 190 | // 191 | block_data[0] = (used_shift & 0xF) | (used_pw << 4); 192 | 193 | memset(block_data + 1, 0x00, c->samples_per_block / c->samples_per_byte); 194 | for(unsigned i = 0; i < c->samples_per_block; i++) 195 | block_data[1 + (i / c->samples_per_byte)] |= nibbles[i] << ((i % c->samples_per_byte) * c->bits_per_sample); 196 | 197 | for(unsigned i = 0; i < c->samples_per_block / c->samples_per_byte; i++) 198 | block_data[1 + i] ^= c->encoded_byte_xor; 199 | 200 | return max_error; 201 | } 202 | 203 | static bool write16(const uint16 value, FILE* fp) 204 | { 205 | return fputc(value >> 8, fp) != EOF && fputc(value >> 0, fp) != EOF; 206 | } 207 | 208 | int main(int argc, char* argv[]) 209 | { 210 | int ret = 0; 211 | int16 inbuf[max_samples_per_block]; 212 | ADContext c; 213 | SNDFILE* insf = NULL; 214 | SF_INFO sfi; 215 | FILE* outfp = NULL; 216 | // 217 | SNDFILE *outsf = NULL; 218 | uint32 pw_usage[FILTER_TAB_COUNT] = { 0 }; 219 | uint32 shift_usage[16] = { 0 }; 220 | // 221 | std::vector header_data; 222 | std::vector nybble_data; 223 | uint8 block_data[max_bytes_per_block]; 224 | int16 outbuf[max_samples_per_block]; 225 | int64 total_error = 0; 226 | 227 | if(argc < 4 || argc > 5) 228 | { 229 | printf("Invalid number of arguments.\n\n"); 230 | printf("Usage: %s [FORMAT] [INPUT_FILE] [OUTPUT_FILE]\n\n", argv[0]); 231 | return -1; 232 | } 233 | 234 | const int format = atoi(argv[1]); 235 | 236 | if(format < 0 || format > 2) 237 | { 238 | printf("Unsupported format.\n"); 239 | return -1; 240 | } 241 | 242 | memset(&sfi, 0, sizeof(sfi)); 243 | if(!(insf = sf_open(argv[2], SFM_READ, &sfi))) 244 | { 245 | printf("Error opening \"%s\".\n", argv[2]); 246 | ret = -1; 247 | goto cleanup; 248 | } 249 | 250 | if(!(outfp = fopen(argv[3], "wb"))) 251 | { 252 | printf("Error opening \"%s\": %m\n", argv[3]); 253 | ret = -1; 254 | goto cleanup; 255 | } 256 | 257 | if(argc >= 5) 258 | { 259 | SF_INFO outsfi = sfi; 260 | 261 | outsfi.channels = 2; 262 | outsfi.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; 263 | 264 | if(!(outsf = sf_open(argv[4], SFM_WRITE, &outsfi))) 265 | { 266 | printf("Error opening \"%s\".\n", argv[4]); 267 | ret = -1; 268 | goto cleanup; 269 | } 270 | } 271 | // 272 | // 273 | // 274 | int32 input_block_count, input_loop_block; 275 | 276 | memset(&c, 0, sizeof(c)); 277 | c.bits_per_sample = 4 >> format; 278 | c.samples_per_block = 16; 279 | c.samples_per_byte = 8 / c.bits_per_sample; 280 | c.encoded_byte_xor = encoded_byte_xor_tab[format]; 281 | assert(c.samples_per_block <= max_samples_per_block); 282 | assert((c.samples_per_block / c.samples_per_byte + 1) <= max_bytes_per_block); 283 | 284 | input_block_count = (sfi.frames + c.samples_per_block - 1) / c.samples_per_block; 285 | input_loop_block = input_block_count; 286 | 287 | { 288 | const char* comment; 289 | 290 | if((comment = sf_get_string(insf, SF_STR_COMMENT))) 291 | { 292 | unsigned input_loop_sample; 293 | 294 | if(sscanf(comment, "adp_loop=%u", &input_loop_sample) == 1) 295 | { 296 | if(input_loop_sample % c.samples_per_block) 297 | { 298 | printf("Loop point of %u is not on a %u-sample boundary.\n", input_loop_sample, c.samples_per_block); 299 | ret = -1; 300 | goto cleanup; 301 | } 302 | input_loop_block = input_loop_sample / c.samples_per_block; 303 | } 304 | } 305 | } 306 | 307 | if(1) 308 | { 309 | const uint32 max_sample_count = ((0xFFFE - 2) / (c.samples_per_block / c.samples_per_byte) - 1) * c.samples_per_block; 310 | 311 | if((c.samples_per_block * input_block_count) > max_sample_count) 312 | { 313 | printf("Input too large. Maximum size for format %u is %u samples.\n", format, max_sample_count); 314 | ret = -1; 315 | goto cleanup; 316 | } 317 | } 318 | 319 | for(unsigned i = 0; i < (1 + 1); i++) 320 | header_data.push_back(0x10); 321 | 322 | for(unsigned i = 0; i < (1 + c.samples_per_block / c.samples_per_byte); i++) 323 | nybble_data.push_back(0x00); 324 | 325 | DecodeBlock(&c, header_data[1], &nybble_data[1], NULL); 326 | 327 | assert((!c.samps[0] && !c.samps[1] && !c.samps[2]) || (c.bits_per_sample < 4)); 328 | 329 | for(int32 block_counter = 0; block_counter < input_block_count; block_counter++) 330 | { 331 | sf_count_t incount = sf_read_short(insf, inbuf, c.samples_per_block); 332 | 333 | if(incount <= 0) 334 | break; 335 | 336 | for(unsigned i = incount; i < c.samples_per_block; i++) 337 | inbuf[i] = inbuf[incount - 1]; 338 | 339 | total_error += EncodeBlock(&c, inbuf, outbuf, block_data, block_counter == input_loop_block); 340 | 341 | header_data.push_back(block_data[0]); 342 | for(unsigned j = 0; j < (c.samples_per_block / c.samples_per_byte); j++) 343 | nybble_data.push_back(block_data[1 + j]); 344 | // 345 | // 346 | // 347 | pw_usage[block_data[0] >> 4]++; 348 | shift_usage[block_data[0] & 0xF]++; 349 | 350 | if(outsf) 351 | { 352 | int16 oebuf[max_samples_per_block * 2]; 353 | 354 | for(unsigned i = 0; i < c.samples_per_block; i++) 355 | { 356 | oebuf[(i << 1) + 0] = outbuf[i]; 357 | oebuf[(i << 1) + 1] = std::max(-32768, std::min(32767, inbuf[i] - outbuf[i])); 358 | } 359 | sf_write_short(outsf, oebuf, c.samples_per_block * 2); 360 | } 361 | } 362 | 363 | // 364 | // Make sure last byte is 0x00(min shift, filter that will decay to 0), as we'll infinitely loop playback 365 | // on it instead of allowing the waveform to end normally, to work around an SCSP bug. 366 | // 367 | assert(nybble_data.front() == 0x00); 368 | nybble_data.push_back(0x00); 369 | 370 | write16(1 + input_block_count, outfp); 371 | write16(1 + input_loop_block, outfp); 372 | fputc(format, outfp); 373 | fwrite(header_data.data(), 1, header_data.size(), outfp); 374 | fwrite(nybble_data.data(), 1, nybble_data.size(), outfp); 375 | 376 | if(ftell(outfp) & 1) 377 | fputc(0x00, outfp); 378 | 379 | printf("Total Error: %16lld\n", (long long)total_error); 380 | 381 | for(unsigned i = 0; i < FILTER_TAB_COUNT; i++) 382 | { 383 | printf("/* 0x%08x 0x%02x */ { % 6d, % 6d, % 6d }, \n", pw_usage[i], i, filter_tab[i][0], filter_tab[i][1], filter_tab[i][2]); 384 | } 385 | 386 | for(unsigned i = 0; i < 16; i++) 387 | { 388 | printf("Shift 0x%02x: %10u\n", i, shift_usage[i]); 389 | } 390 | // 391 | // 392 | // 393 | cleanup:; 394 | if(outfp) 395 | { 396 | fclose(outfp); 397 | outfp = NULL; 398 | } 399 | 400 | if(insf) 401 | { 402 | sf_close(insf); 403 | insf = NULL; 404 | } 405 | 406 | if(outsf) 407 | { 408 | sf_close(outsf); 409 | outsf = NULL; 410 | } 411 | 412 | return ret; 413 | } 414 | -------------------------------------------------------------------------------- /adp68k.c: -------------------------------------------------------------------------------- 1 | /* 2 | * adp68k.c 3 | * 4 | * Copyright (C) 2021 celeriyacon - https://github.com/celeriyacon 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 3. This notice may not be removed or altered from any source distribution. 21 | * 22 | */ 23 | 24 | #include "adp68k.h" 25 | 26 | // 27 | // Tables must be in the first 65536 bytes of SCSP RAM 28 | // 29 | #include "adp68k-data.h" 30 | 31 | SoundControlBlock screserve __attribute__((section(".control_block"))); 32 | 33 | static const uint32 isolator_addr[3] = { (uint32)&isolator, (uint32)&isolator2bit, (uint32)&isolator1bit }; 34 | 35 | static INLINE void InitADPCM(void) 36 | { 37 | for(unsigned slot = 0; slot < 32; slot++) 38 | SCSP_SREG(slot, 0x0C) = 1 << 9; 39 | 40 | WaitSampleIRQ(); 41 | WaitSampleIRQ(); 42 | // 43 | // 44 | // 45 | SCSP_CREG(0x01 << 1) = (0x0 << 7) | (0 << 0); 46 | 47 | MADRS[0x00] = ((uint16)(uint32)&scale >> 1) + 0x0080; 48 | MADRS[0x01] = ((uint16)(uint32)&coeff >> 1) + 0x008; 49 | MADRS[0x02] = ((uint16)(uint32)&coeff >> 1) + 0x018; 50 | MADRS[0x03] = ((uint16)(uint32)&coeff >> 1) + 0x028; 51 | 52 | // Square frequency: 53 | MEMS[0x10] = DSP_MAKE_MEMS(-1 * 4096); 54 | 55 | // Square volume: 56 | COEF[0x10] = DSP_MAKE_COEF(0); 57 | COEF[0x11] = DSP_MAKE_COEF(0); 58 | 59 | // Saw volume: 60 | COEF[0x12] = DSP_MAKE_COEF(0); 61 | COEF[0x13] = DSP_MAKE_COEF(0); 62 | 63 | // Saw IIR: 64 | COEF[0x18] = DSP_MAKE_COEF(0); 65 | COEF[0x19] = DSP_MAKE_COEF(0); 66 | COEF[0x1A] = DSP_MAKE_COEF(0); 67 | 68 | for(unsigned i = 0; i < 4; i++) 69 | { 70 | // Saw freq: 71 | MEMS[0x14 + i] = DSP_MAKE_MEMS(0); 72 | 73 | // Saw operator volume: 74 | COEF[0x30 + i] = DSP_MAKE_COEF(0); 75 | 76 | // Saw feedback level: 77 | COEF[0x14 + i] = DSP_MAKE_COEF(0); 78 | 79 | // Saw modulation level: 80 | COEF[0x1B + i] = DSP_MAKE_COEF(0); 81 | } 82 | 83 | // CD audio volume: 84 | COEF[0x20] = DSP_MAKE_COEF(0); // CD left->SCSP left 85 | COEF[0x21] = DSP_MAKE_COEF(0); // CD left->SCSP right 86 | COEF[0x22] = DSP_MAKE_COEF(0); // CD right->SCSP left 87 | COEF[0x23] = DSP_MAKE_COEF(0); // CD right->SCSP right 88 | 89 | // General constants: 90 | MEMS[0x1E] = DSP_MAKE_MEMS(0x1000); 91 | MEMS[0x1F] = DSP_MAKE_MEMS(0x800000); 92 | 93 | // General constants: 94 | COEF[0x3B] = DSP_MAKE_COEF(4096); 95 | COEF[0x3C] = DSP_MAKE_COEF(0); 96 | COEF[0x3D] = DSP_MAKE_COEF(-1); 97 | COEF[0x3E] = 0x8000 >> 8; 98 | COEF[0x3F] = 0x4000; 99 | 100 | WaitSampleIRQ(); 101 | WaitSampleIRQ(); 102 | 103 | for(unsigned i = 0; i < 128; i++) 104 | MPROG[i] = mprog[i] & ~DSP_TWT; 105 | 106 | WaitSampleIRQ(); 107 | WaitSampleIRQ(); 108 | 109 | for(unsigned i = 0; i < 128; i++) 110 | MPROG[i] = mprog[i]; 111 | // 112 | // 113 | // 114 | for(unsigned adslot = 0; adslot < 8; adslot++) 115 | { 116 | const unsigned nybbles_slot = 0 + (adslot << 1); 117 | const unsigned selector_slot = 1 + (adslot << 1); 118 | const unsigned header_slot = 16 + (adslot << 1); 119 | const unsigned isolator_slot = 17 + (adslot << 1); 120 | 121 | SCSP(0x100600 + (header_slot << 1)) = 0x03FF; 122 | SCSP(0x100640 + (header_slot << 1)) = 0x03FF; 123 | // 124 | // The header and nybbles slots' envelope level needs to get to 0x3C0 ASAP after keying on, to prevent garbage 125 | // from getting into the sound path later after key off, in the time between the reconfiguration of the sound 126 | // slot for a new sample and the key on. 127 | // 128 | // 129 | SCSP_SREG(nybbles_slot, 0x04) = 0x0000; // LSA 130 | SCSP_SREG(nybbles_slot, 0x08) = (0x1F << 0) | (0x1F << 6) | (0x1F << 11); // Attack rate, decay rate, sustain rate 131 | SCSP_SREG(nybbles_slot, 0x0C) = (1 << 8); // Bypass EG and TL and ALFO 132 | SCSP_SREG(nybbles_slot, 0x0E) = (0x10 << 0) | (0x10 << 6) | (0x05 << 12); 133 | SCSP_SREG(nybbles_slot, 0x16) = (0x0 << 13) | (0x00 << 8) | (0x7 << 5) | (0x1F << 0); // Direct send level, pan 134 | // 135 | SCSP_SREG(selector_slot, 0x02) = (uint16)(uint32)&selector; // SA 136 | SCSP_SREG(selector_slot, 0x04) = 0x0000; // LSA 137 | SCSP_SREG(selector_slot, 0x06) = 0x0008; 138 | SCSP_SREG(selector_slot, 0x0A) = (0x1F << 0); // Release rate 139 | SCSP_SREG(selector_slot, 0x0C) = (1 << 8); // Bypass EG and TL and ALFO 140 | SCSP_SREG(selector_slot, 0x0E) = (0x00 << 0) | (0x00 << 6) | (0x00 << 12); 141 | SCSP_SREG(selector_slot, 0x16) = (0x0 << 13) | (0x00 << 8) | (0x7 << 5) | (0x0F << 0); // Direct send level, pan 142 | // 143 | SCSP_SREG(header_slot, 0x04) = 0x0000; 144 | SCSP_SREG(header_slot, 0x08) = (0x1F << 0) | (0x1F << 6) | (0x1F << 11); // Attack rate, decay rate, sustain rate 145 | SCSP_SREG(header_slot, 0x0C) = (1 << 8) | (1 << 9); 146 | SCSP_SREG(header_slot, 0x0E) = (0x00 << 0) | (0x00 << 6) | (0x05 << 12); 147 | SCSP_SREG(header_slot, 0x14) = ((0x00 + (adslot << 1)) << 3) | (0x07 << 0); // dsp mix select, mix level 148 | SCSP_SREG(header_slot, 0x16) = (0x0 << 13) | (0x00 << 8); // Direct send level, pan 149 | // 150 | SCSP_SREG(isolator_slot, 0x04) = 0x0000; // LSA 151 | SCSP_SREG(isolator_slot, 0x06) = 0x0000; // LEA 152 | SCSP_SREG(isolator_slot, 0x0A) = (0x1F << 0); // Release rate 153 | SCSP_SREG(isolator_slot, 0x0C) = (1 << 8); // Bypass EG and TL and ALFO 154 | SCSP_SREG(isolator_slot, 0x10) = 0x400; // FNS 155 | SCSP_SREG(isolator_slot, 0x14) = ((0x01 + (adslot << 1)) << 3) | (0x07 << 0); // dsp mix select, mix level 156 | SCSP_SREG(isolator_slot, 0x16) = (0x0 << 13) | (0x00 << 8); // Direct send level, pan 157 | } 158 | } 159 | 160 | static uint16 nybbles_freg[8]; 161 | static const uint16 header_freg = 0x400 + 0x040; // header FNS 162 | 163 | static INLINE void PlayADPCM(unsigned adslot, const uint32 addr) 164 | { 165 | //const unsigned format = 1; // 0 = 4 bits, 1 = 2 bits, 2 = 1 bit 166 | const unsigned nybbles_slot = 0 + (adslot << 1); 167 | const unsigned selector_slot = 1 + (adslot << 1); 168 | const unsigned header_slot = 16 + (adslot << 1); 169 | const unsigned isolator_slot = 17 + (adslot << 1); 170 | // 171 | const uint16 block_count = SCSP16(addr + 0); 172 | const uint16 loop_block = SCSP16(addr + 2); 173 | const unsigned format = SCSP8(addr + 4); 174 | const uint32 header_addr = addr + 5; 175 | const uint32 nybbles_addr = 1 + header_addr + block_count; 176 | // 177 | const uint16 header_lsa = loop_block; 178 | const uint16 nybbles_lsa = loop_block << (3 - format); 179 | const uint16 header_lea = block_count + (loop_block == block_count); 180 | const uint16 nybbles_lea = (block_count << (3 - format)) + (loop_block == block_count); 181 | 182 | SCSP_SREG(nybbles_slot, 0x00) = ((nybbles_addr >> 16) & 0xF) | (1 << 4) | (0x1 << 5) | (0x0 << 7) | (0x0 << 9) | (1 << 11); 183 | SCSP_SREG(nybbles_slot, 0x02) = nybbles_addr; // SA 184 | SCSP_SREG(nybbles_slot, 0x04) = nybbles_lsa; 185 | SCSP_SREG(nybbles_slot, 0x06) = nybbles_lea; 186 | SCSP_SREG(nybbles_slot, 0x0A) = (0x1F << 0) | (0x1F << 5) | (0xE << 10) | (1 << 15); // Release rate, decay level, KRS, EG bypass 187 | SCSP_SREG(nybbles_slot, 0x10) = 0x400 + (0x200 >> format) + 0x4; // FNS 188 | // 189 | SCSP_SREG(selector_slot, 0x00) = (0 << 4) | (0x1 << 5) | (0x0 << 7) | (0x0 << 9) | (1 << 11); 190 | SCSP_SREG(selector_slot, 0x10) = ((2 - format) << 11); // FNS, Octave 191 | // 192 | SCSP_SREG(header_slot, 0x00) = ((header_addr >> 16) & 0xF) | (1 << 4) | (0x1 << 5) | (0x0 << 7) | (0x0 << 9) | (1 << 11); 193 | SCSP_SREG(header_slot, 0x02) = header_addr; // SA 194 | SCSP_SREG(header_slot, 0x04) = header_lsa; 195 | SCSP_SREG(header_slot, 0x06) = header_lea; 196 | SCSP_SREG(header_slot, 0x0A) = (0x1F << 0) | (0x1F << 5) | (0xE << 10) | (1 << 15); // Release rate, decay level, KRS, EG bypass 197 | SCSP_SREG(header_slot, 0x10) = 0x400 + 0x040 + 0x4; // FNS 198 | // 199 | SCSP_SREG(isolator_slot, 0x00) = (1 << 4) | (0x1 << 5) | (0x0 << 7) | (0x0 << 9) | (1 << 11); 200 | SCSP_SREG(isolator_slot, 0x02) = isolator_addr[format] + (0x100 << format); // SA 201 | SCSP_SREG(isolator_slot, 0x0E) = (0x2F << 0) | (0x30 << 6) | ((0x09 + format) << 12); 202 | 203 | nybbles_freg[adslot] = 0x400 + (0x200 >> format); // nybbles FNS 204 | } 205 | 206 | static INLINE void Update(void) 207 | { 208 | { 209 | volatile PSGControl* const psgc = &adp68k_scblock->psg; 210 | 211 | COEF[0x10] = psgc->square_volume[0]; 212 | COEF[0x11] = psgc->square_volume[1]; 213 | 214 | MEMS[0x10] = DSP_MAKE_MEMS(-(psgc->square_freq << 12)); 215 | 216 | COEF[0x12] = psgc->saw_volume[0]; 217 | COEF[0x13] = psgc->saw_volume[1]; 218 | 219 | COEF[0x18] = psgc->saw_iir[0]; 220 | COEF[0x19] = psgc->saw_iir[1]; 221 | COEF[0x1A] = psgc->saw_iir[2]; 222 | 223 | for(unsigned i = 0; i < 4; i++) 224 | { 225 | MEMS[0x14 + i] = psgc->saw_operator[i].freq; 226 | COEF[0x30 + i] = psgc->saw_operator[i].volume; 227 | COEF[0x14 + i] = psgc->saw_operator[i].fb_level; 228 | COEF[0x1B + i] = psgc->saw_operator[i].mod_level; 229 | } 230 | } 231 | // 232 | // 233 | // 234 | COEF[0x20] = adp68k_scblock->cd_volume[0]; 235 | COEF[0x21] = adp68k_scblock->cd_volume[1]; 236 | COEF[0x22] = adp68k_scblock->cd_volume[2]; 237 | COEF[0x23] = adp68k_scblock->cd_volume[3]; 238 | // 239 | // 240 | // 241 | for(unsigned ads = 0; ads < 8; ads++) 242 | { 243 | const unsigned nybbles_slot = 0 + (ads << 1); 244 | const unsigned selector_slot = 1 + (ads << 1); 245 | const unsigned header_slot = 16 + (ads << 1); 246 | const unsigned isolator_slot = 17 + (ads << 1); 247 | const unsigned mask = 0xF7 | (adp68k_scblock->adpcm[ads].action & ADP68K_ACTION_NOP); 248 | const unsigned egcmask = 0x7F | (adp68k_scblock->adpcm[ads].action & ADP68K_ACTION_NOP); 249 | 250 | SCSP_SREG_HI(header_slot, 0x0A) &= egcmask; 251 | SCSP_SREG_HI(nybbles_slot, 0x0A) &= egcmask; 252 | 253 | SCSP_SREG_HI(header_slot, 0x00) &= mask; 254 | SCSP_SREG_HI(nybbles_slot, 0x00) &= mask; 255 | SCSP_SREG_HI(selector_slot, 0x00) &= mask; 256 | SCSP_SREG_HI(isolator_slot, 0x00) &= mask; 257 | } 258 | SCSP_SREG(31, 0x00) |= (1 << 12); 259 | 260 | WaitSampleIRQ(); 261 | WaitSampleIRQ(); 262 | // 263 | // 264 | // 265 | for(unsigned ads = 0; ads < 8; ads++) 266 | { 267 | volatile ADPCMChannelControl* const chcc = &adp68k_scblock->adpcm[ads]; 268 | 269 | COEF[(ads << 1) + 0x00] = chcc->volume[0]; 270 | COEF[(ads << 1) + 0x01] = chcc->volume[1]; 271 | } 272 | // 273 | // 274 | // 275 | for(unsigned ads = 0; ads < 8; ads++) 276 | { 277 | volatile ADPCMChannelControl* const chcc = &adp68k_scblock->adpcm[ads]; 278 | 279 | if(chcc->action == ADP68K_ACTION_PLAY) 280 | PlayADPCM(ads, adp68k_effect_table[chcc->id]); 281 | 282 | chcc->action = ADP68K_ACTION_NOP; 283 | } 284 | // 285 | uint16 header_freg_tmp = header_freg; 286 | 287 | asm volatile( 288 | "bset #12, %1@(0x3E0)\n\t" 289 | "move.w #0x400, %1@(0x422)\n\t" "1: btst #10, %1@(0x420)\n\t" "beqs 1b\n\t" 290 | "move.w #0x400, %1@(0x422)\n\t" "1: btst #10, %1@(0x420)\n\t" "beqs 1b\n\t" 291 | "move.w #0x400, %1@(0x422)\n\t" "1: btst #10, %1@(0x420)\n\t" "beqs 1b\n\t" 292 | "move.w #0x400, %1@(0x422)\n\t" "1: btst #10, %1@(0x420)\n\t" "beqs 1b\n\t" 293 | "move.w #0x400, %1@(0x422)\n\t" "1: btst #10, %1@(0x420)\n\t" "beqs 1b\n\t" 294 | "move.w #0x400, %1@(0x422)\n\t" "1: btst #10, %1@(0x420)\n\t" "beqs 1b\n\t" 295 | "move.w #0x400, %1@(0x422)\n\t" "1: btst #10, %1@(0x420)\n\t" "beqs 1b\n\t" 296 | "move.w #0x400, %1@(0x422)\n\t" "1: btst #10, %1@(0x420)\n\t" "beqs 1b\n\t" 297 | "move.w #0x400, %1@(0x422)\n\t" "1: btst #10, %1@(0x420)\n\t" "beqs 1b\n\t" 298 | "move.w #0x400, %1@(0x422)\n\t" "1: btst #10, %1@(0x420)\n\t" "beqs 1b\n\t" 299 | "move.w #0x400, %1@(0x422)\n\t" "1: btst #10, %1@(0x420)\n\t" "beqs 1b\n\t" 300 | "move.w #0x400, %1@(0x422)\n\t" "1: btst #10, %1@(0x420)\n\t" "beqs 1b\n\t" 301 | 302 | "move.w %0, %1@(0x210)\n\t" 303 | "move.w %0, %1@(0x250)\n\t" 304 | "move.w %0, %1@(0x290)\n\t" 305 | "move.w %0, %1@(0x2D0)\n\t" 306 | "move.w %0, %1@(0x310)\n\t" 307 | "move.w %0, %1@(0x350)\n\t" 308 | "move.w %0, %1@(0x390)\n\t" 309 | "move.w %0, %1@(0x3D0)\n\t" 310 | "move.w %2, %0\n\t" 311 | "move.w %0, %1@(0x010)\n\t" 312 | "move.w %3, %1@(0x050)\n\t" 313 | "move.w %4, %1@(0x090)\n\t" 314 | "move.w %5, %1@(0x0D0)\n\t" 315 | "move.w %6, %1@(0x110)\n\t" 316 | "move.w %7, %1@(0x150)\n\t" 317 | "move.w %8, %1@(0x190)\n\t" 318 | "move.w %9, %1@(0x1D0)\n\t" 319 | : "+&d"(header_freg_tmp) 320 | : "a"(SCSPVP(0x100000)), "a"(nybbles_freg[0]), "d"(nybbles_freg[1]), "d"(nybbles_freg[2]), "d"(nybbles_freg[3]), "d"(nybbles_freg[4]), "d"(nybbles_freg[5]), "d"(nybbles_freg[6]), "d"(nybbles_freg[7]) 321 | : "memory", "cc"); 322 | } 323 | 324 | void _start(void) 325 | { 326 | SCSP_Init(); 327 | 328 | SCSP_CREG_LO(0x00 << 1) = (0x0F << 0); 329 | 330 | InitADPCM(); 331 | 332 | for(unsigned ads = 0; ads < 8; ads++) 333 | { 334 | volatile ADPCMChannelControl* const chcc = &adp68k_scblock->adpcm[ads]; 335 | 336 | chcc->action = ADP68K_ACTION_NOP; 337 | chcc->id = 0; 338 | chcc->volume[0] = 0; 339 | chcc->volume[1] = 0; 340 | } 341 | 342 | { 343 | volatile PSGControl* const psgc = &adp68k_scblock->psg; 344 | 345 | psgc->square_volume[0] = 0; 346 | psgc->square_volume[1] = 0; 347 | 348 | psgc->saw_volume[0] = 0; 349 | psgc->saw_volume[1] = 0; 350 | 351 | psgc->saw_iir[0] = 0x4000; 352 | psgc->saw_iir[1] = 0; 353 | psgc->saw_iir[2] = 0; 354 | 355 | for(unsigned i = 0; i < 4; i++) 356 | { 357 | psgc->saw_operator[i].freq = 0; 358 | psgc->saw_operator[i].volume = 0; 359 | psgc->saw_operator[i].fb_level = 0; 360 | psgc->saw_operator[i].mod_level = 0; 361 | } 362 | } 363 | 364 | adp68k_scblock->cd_volume[0] = 0x4000; 365 | adp68k_scblock->cd_volume[1] = 0x0000; 366 | adp68k_scblock->cd_volume[2] = 0x0000; 367 | adp68k_scblock->cd_volume[3] = 0x4000; 368 | // 369 | // 370 | // 371 | SCSP_MCIPD = 1U << 5; 372 | 373 | for(;;) 374 | { 375 | if(SCSP_SCIPD & (1U << 5)) 376 | { 377 | Update(); 378 | SCSP_SCIRE = 1U << 5; 379 | } 380 | } 381 | } 382 | --------------------------------------------------------------------------------