diff --git a/cartridge/Makefile b/cartridge/Makefile new file mode 100644 index 0000000..692c0d0 --- /dev/null +++ b/cartridge/Makefile @@ -0,0 +1,95 @@ +LIB = ../saturn + +### architecture-specific flags ### + +AARCH = --isa=sh2 --big + +CARCH = -m2 -mb + +### general flags ### + +OPT ?= -O3 + +DEBUG = -g -gdwarf-4 + +AFLAGS += --fatal-warnings + +CFLAGS += -falign-functions=4 -ffunction-sections -fdata-sections -fshort-enums -ffreestanding -nostdlib +CFLAGS += -Wall -Werror -Wfatal-errors +CFLAGS += -Wno-array-bounds +CFLAGS += -I$(LIB) + +CXXFLAGS += -fno-exceptions -fno-non-call-exceptions -fno-rtti -fno-threadsafe-statics + +LDFLAGS += --gc-sections --print-gc-sections --no-warn-rwx-segment --print-memory-usage --entry=_start --orphan-handling=error +LDFLAGS += -L$(LIB) + +DEPFLAGS = -MMD -MP + +### target ### + +TARGET = sh2-none-elf- +CC = $(TARGET)gcc +CXX = $(TARGET)g++ +AS = $(TARGET)as +LD = $(TARGET)ld +OBJCOPY = $(TARGET)objcopy +OBJDUMP = $(TARGET)objdump + +### rules ### + +%.o: %.s + $(AS) $(AARCH) $(AFLAGS) $(DEBUG) $< -o $@ + +%.o: %.c + $(CC) $(CARCH) $(CFLAGS) $(OPT) $(DEBUG) $(DEPFLAGS) -MF ${<}.d -c $< -o $@ + +%.o: %.cpp + $(CXX) $(CARCH) $(CFLAGS) $(CXXFLAGS) $(OPT) $(DEBUG) $(DEPFLAGS) -MF ${<}.d -c $< -o $@ + +%.elf: + $(LD) $(LDFLAGS) -T $(LDSCRIPT) $^ -o $@ + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + du -b $@ + +%.ss: %.bin + cp $< $@ + +%.pcm.o: %.pcm + $(OBJCOPY) \ + -I binary -O elf32-sh -B sh2 \ + --rename-section .data=.rom.$(basename $@) \ + $< $@ + +### object files ### + +SYS_IP_OBJ += $(LIB)/ip/sys_id.o +SYS_IP_OBJ += $(LIB)/ip/segasmp/sys_sec.o +SYS_IP_OBJ += $(LIB)/ip/sys_area.o +SYS_IP_OBJ += $(LIB)/ip/sys_areb.o +SYS_IP_OBJ += $(LIB)/ip/sys_aree.o +SYS_IP_OBJ += $(LIB)/ip/sys_arej.o +SYS_IP_OBJ += $(LIB)/ip/sys_arek.o +SYS_IP_OBJ += $(LIB)/ip/sys_arel.o +SYS_IP_OBJ += $(LIB)/ip/sys_aret.o +SYS_IP_OBJ += $(LIB)/ip/sys_areu.o +SYS_IP_OBJ += $(LIB)/ip/sys_init.o + +COMMON_OBJ = start.o runtime.o + +CARTRIDGE_OBJ = main.o wc3.pcm.o +cartridge.elf: LDSCRIPT = $(LIB)/cartridge.lds +cartridge.elf: $(SYS_IP_OBJ) $(COMMON_OBJ) $(CARTRIDGE_OBJ) + +.SUFFIXES: +.INTERMEDIATE: +.SECONDARY: +.PHONY: all clean + +%: RCS/%,v +%: RCS/% +%: %,v +%: s.% +%: SCCS/s.% diff --git a/cartridge/cartridge.ss b/cartridge/cartridge.ss new file mode 100755 index 0000000..2a1969d Binary files /dev/null and b/cartridge/cartridge.ss differ diff --git a/cartridge/main.cpp b/cartridge/main.cpp new file mode 100644 index 0000000..81f072e --- /dev/null +++ b/cartridge/main.cpp @@ -0,0 +1,133 @@ +#include "vdp1.h" +#include "vdp2.h" +#include "scsp.h" +#include "smpc.h" + +#include "../common/copy.hpp" + +extern void * _wc3_pcm_start __asm("_binary_wc3_pcm_start"); +extern void * _wc3_pcm_size __asm("_binary_wc3_pcm_size"); + +void snd_init() +{ + while ((smpc.reg.SF & 1) != 0); + smpc.reg.SF = 1; + smpc.reg.COMREG = COMREG__SNDOFF; + while (smpc.reg.OREG[31].val != OREG31__SNDOFF); + + scsp.reg.ctrl.MIXER = MIXER__MEM4MB; + + volatile uint32_t * dsp_steps = reinterpret_cast(&(scsp.reg.dsp.STEP[0].MPRO[0])); + fill(dsp_steps, 0, (sizeof (scsp.reg.dsp.STEP))); + + for (int i = 0; i < 32; i++) { + scsp.reg.slot[i].SA = 0; + scsp.reg.slot[i].MIXER = 0; + } + + scsp.reg.ctrl.MIXER = MIXER__MEM4MB | MIXER__MVOL(0xf); +} + +void snd_step() +{ + const uint16_t * buf = reinterpret_cast(&_wc3_pcm_start); + const uint32_t size = reinterpret_cast(&_wc3_pcm_size); + constexpr uint32_t chunk_samples = 16384 / 2; + constexpr uint32_t chunk_size = chunk_samples * 2; + copy(&scsp.ram.u16[0], buf, chunk_size); + + scsp_slot& slot = scsp.reg.slot[0]; + // start address (bytes) + slot.SA = SA__KYONB | SA__LPCTL__NORMAL | SA__SA(0); // kx kb sbctl[1:0] ssctl[1:0] lpctl[1:0] 8b sa[19:0] + slot.LSA = 0; // loop start address (samples) + slot.LEA = chunk_samples * 2; // loop end address (samples) + //slot.EG = EG__EGHOLD; // d2r d1r ho ar krs dl rr + slot.EG = EG__AR(0x1F) | EG__D1R(0x00) | EG__D2R(0x00) | EG__RR(0x1F); + slot.FM = 0; // stwinh sdir tl mdl mdxsl mdysl + slot.PITCH = PITCH__OCT(-1) | PITCH__FNS(0); // oct fns + slot.LFO = 0; // lfof plfows + slot.MIXER = MIXER__DISDL(0b110); // disdl dipan efsdl efpan + + slot.LOOP |= LOOP__KYONEX; + + uint32_t offset = 1; + uint32_t chunk = 1; + + constexpr uint32_t timer_a_interrupt = (1 << 6); + scsp.reg.ctrl.TIMA = TIMA__TACTL(7) + | TIMA__TIMA(128); + scsp.reg.ctrl.SCIRE = timer_a_interrupt; + + while (1) { + copy(&scsp.ram.u16[(chunk_size * chunk) / 2], &buf[(chunk_size * offset) / 2], chunk_size); + chunk = !chunk; + offset = offset + 1; + if ((offset * (chunk_size + 1)) > size) { + offset = 0; + } + + /* + uint32_t sample = 0; + const uint32_t target = chunk_samples * 2; + constexpr uint32_t sample_interval = (1 << 10); + while (sample < target) { + scsp.reg.ctrl.SCIRE = sample_interval; + while (!(scsp.reg.ctrl.SCIPD & sample_interval)); + sample++; + } + */ + while (!(scsp.reg.ctrl.SCIPD & timer_a_interrupt)); + scsp.reg.ctrl.TIMA = TIMA__TACTL(7) + | TIMA__TIMA(128); + scsp.reg.ctrl.SCIRE = timer_a_interrupt; + } +} + +extern "C" +void main() __attribute__((section(".text.main"))); +void main() +{ + // DISP: Please make sure to change this bit from 0 to 1 during V blank. + vdp2.reg.TVMD = ( TVMD__DISP | TVMD__LSMD__NON_INTERLACE + | TVMD__VRESO__240 | TVMD__HRESO__NORMAL_320); + + // VDP2 User's Manual: + // "When sprite data is in an RGB format, sprite register 0 is selected" + // "When the value of a priority number is 0h, it is read as transparent" + // + // The power-on value of PRISA is zero. Set the priority for sprite register 0 + // to some number greater than zero, so that the color data is not interpreted + // as "transparent". + vdp2.reg.PRISA = PRISA__S0PRIN(1); // Sprite register 0 Priority Number + + /* TVM settings must be performed from the second H-blank IN interrupt after the + V-blank IN interrupt to the H-blank IN interrupt immediately after the V-blank + OUT interrupt. */ + // "normal" display resolution, 16 bits per pixel, 512x256 framebuffer + vdp1.reg.TVMR = TVMR__TVM__NORMAL; + + // swap framebuffers every 1 cycle; non-interlace + vdp1.reg.FBCR = 0; + + // erase upper-left coordinate + vdp1.reg.EWLR = EWLR__16BPP_X1(0) | EWLR__Y1(0); + + // erase lower-right coordinate + vdp1.reg.EWRR = EWRR__16BPP_X3(319) | EWRR__Y3(239); + + // during a framebuffer erase cycle, write the color "black" to each pixel + const uint16_t black = 1 << 15 | 0x00ff; + vdp1.reg.EWDR = black; + + vdp1.vram.cmd[0].CTRL = CTRL__END; + + snd_init(); + snd_step(); + + vdp2.reg.BGON = 0; + + // start drawing (execute the command list) on every frame + vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE; + + while (1); +} diff --git a/cartridge/runtime.c b/cartridge/runtime.c new file mode 100644 index 0000000..9269c86 --- /dev/null +++ b/cartridge/runtime.c @@ -0,0 +1,67 @@ +#include + +extern uint32_t __text_link_start __asm("__text_link_start"); +extern uint32_t __text_link_end __asm("__text_link_end"); +extern uint32_t __text_load_start __asm("__text_load_start"); + +extern uint32_t __data_link_start __asm("__data_link_start"); +extern uint32_t __data_link_end __asm("__data_link_end"); +extern uint32_t __data_load_start __asm("__data_load_start"); + +extern uint32_t __rodata_link_start __asm("__rodata_link_start"); +extern uint32_t __rodata_link_end __asm("__rodata_link_end"); +extern uint32_t __rodata_load_start __asm("__rodata_load_start"); + +extern uint32_t __ctors_link_start __asm("__ctors_link_start"); +extern uint32_t __ctors_link_end __asm("__ctors_link_end"); + +extern uint32_t __bss_link_start __asm("__bss_link_start"); +extern uint32_t __bss_link_end __asm("__bss_link_end"); + +void copy(uint32_t * start, const uint32_t * end, uint32_t * load) +__attribute__((section(".text.startup.copy"))); + +void copy(uint32_t * start, const uint32_t * end, uint32_t * load) +{ + if (start != load) { + while (start < end) { + *start++ = *load++; + } + } +} + +extern void main(void); + +typedef void(init_t)(void); + +void runtime_init(void) +__attribute__((section(".text.startup.runtime_init"))); + +void runtime_init(void) +{ + // relocate text (if necessary) + copy(&__text_link_start, &__text_link_end, &__text_load_start); + + // relocate data (if necessary) + copy(&__data_link_start, &__data_link_end, &__data_load_start); + + // relocate rodata (if necessary) + copy(&__rodata_link_start, &__rodata_link_end, &__rodata_load_start); + + uint32_t * start; + uint32_t * end; + + // clear BSS + start = &__bss_link_start; + end = &__bss_link_end; + while (start < end) { + *start++ = 0; + } + + // call ctors + start = &__ctors_link_start; + end = &__ctors_link_end; + while (start < end) { + ((init_t*)(*start++))(); + } +} diff --git a/cartridge/start.s b/cartridge/start.s new file mode 100644 index 0000000..259f1b4 --- /dev/null +++ b/cartridge/start.s @@ -0,0 +1,37 @@ + .section .text.start + .global _start +_start: + /* set stack pointer */ + mov.l stack_end_ptr,r15 + + /* mask all interrupts */ + mov.l imask_all,r0 + stc sr,r1 + or r1,r0 + ldc r0,sr + + /* save pr */ + sts.l pr,@-r15 + + /* jump to runtime_init */ + mov.l runtime_init_ptr,r0 + jsr @r0 + nop + + /* restore pr */ + lds.l @r15+,pr + + /* jump to main */ + mov.l main_ptr,r0 + jmp @r0 + nop + + .align 4 +stack_end_ptr: + .long __stack_end +imask_all: + .long 0xf0 +runtime_init_ptr: + .long _runtime_init +main_ptr: + .long _main