From f7a178384c28c13e0b268eaaf05f9172984d4044 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Sat, 24 Jun 2023 02:59:31 +0000 Subject: [PATCH] scsp: add fm example --- .gitignore | 1 + Makefile | 15 +- common/intback.hpp | 21 +- common/string.hpp | 21 + editor/main_saturn.cpp | 6 +- m68k/Makefile | 2 +- m68k/interrupt.cpp | 6 +- m68k/slot.cpp | 3 +- scsp/fm.cpp | 753 ++++++++++++++++++++++++++++++++++ scsp/sine-44100-s16be-1ch.pcm | Bin 88200 -> 0 bytes scsp/slot.cpp | 5 +- scsp/sound_cpu__interrupt.cpp | 24 +- smpc/input_keyboard.cpp | 26 +- wordle/main_saturn.cpp | 12 +- 14 files changed, 821 insertions(+), 74 deletions(-) create mode 100644 common/string.hpp create mode 100644 scsp/fm.cpp delete mode 100644 scsp/sine-44100-s16be-1ch.pcm diff --git a/.gitignore b/.gitignore index ae39754..ccbd7a5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.ppm *.png *.out +*.pcm res/mai.data tools/ttf-convert tools/ttf-bitmap diff --git a/Makefile b/Makefile index 3ac891c..b831005 100644 --- a/Makefile +++ b/Makefile @@ -92,14 +92,23 @@ wordle/wordle.o: wordle/word_list.hpp wordle/wordle.elf: wordle/main_saturn.o wordle/wordle.o wordle/draw.o sh/lib1funcs.o res/dejavusansmono.font.bin.o common/keyboard.o common/draw_font.o common/palette.o -scsp/sine-44100-s16be-1ch.pcm: +# 88200 bytes +scsp/sine-44100-s16be-1ch-1sec.pcm: sox \ -r 44100 -e signed-integer -b 16 -c 1 -n -B \ $@.raw \ synth 1 sin 440 vol -10dB mv $@.raw $@ -scsp/slot.elf: scsp/slot.o scsp/sine-44100-s16be-1ch.pcm.o +# 200 bytes +scsp/sine-44100-s16be-1ch-100sample.pcm: + sox \ + -r 44100 -e signed-integer -b 16 -c 1 -n -B \ + $@.raw \ + synth 100s sin 440 vol -10dB + mv $@.raw $@ + +scsp/slot.elf: scsp/slot.o scsp/sine-44100-s16be-1ch-1sec.pcm.o m68k: @@ -110,6 +119,8 @@ scsp/sound_cpu__slot.elf: scsp/sound_cpu__slot.o m68k/slot.bin.o scsp/sound_cpu__interrupt.elf: scsp/sound_cpu__interrupt.o m68k/interrupt.bin.o sh/lib1funcs.o res/sperrypc.font.bin.o common/draw_font.o common/palette.o +scsp/fm.elf: scsp/fm.o res/nec.bitmap.bin.o sh/lib1funcs.o saturn/start.o scsp/sine-44100-s16be-1ch-100sample.pcm.o + res/sperrypc.bitmap.bin: tools/ttf-bitmap ./tools/ttf-bitmap 20 7f res/Bm437_SperryPC_CGA.otb $@ diff --git a/common/intback.hpp b/common/intback.hpp index ad69f12..f222b5e 100644 --- a/common/intback.hpp +++ b/common/intback.hpp @@ -1,5 +1,7 @@ +#pragma once + namespace intback { - enum intback_fsm { + enum fsm : uint8_t { PORT_STATUS = 0, PERIPHERAL_ID, DATA1, @@ -9,7 +11,7 @@ namespace intback { FSM_NEXT }; - struct intback_state { + struct state { uint8_t fsm; uint8_t controller_ix; uint8_t port_ix; @@ -21,11 +23,12 @@ namespace intback { uint8_t kbd_bits; }; - typedef void (*keyboard_func_ptr)(const enum keysym k, uint8_t kbd_bits); + typedef void (*keyboard_func_ptr)(uint8_t keysym, uint8_t kbd_bits); + typedef void (*digital_func_ptr)(uint8_t fsm_state, uint8_t data); - static intback_state state; + static struct state state; - inline void keyboard_fsm(keyboard_func_ptr callback) + inline void fsm(digital_func_ptr digital_cb, keyboard_func_ptr keyboard_cb) { if ((smpc.reg.SR & SR__PDL) != 0) { // PDL == 1; 1st peripheral data @@ -65,18 +68,16 @@ namespace intback { state.data_size = PERIPHERAL_ID__DATA_SIZE(oreg); break; case DATA1: + if (digital_cb != nullptr) digital_cb(state.fsm, oreg); break; case DATA2: + if (digital_cb != nullptr) digital_cb(state.fsm, oreg); break; case DATA3: state.kbd_bits = oreg & 0b1111; break; case DATA4: - { - uint32_t keysym = oreg; - enum keysym k = scancode_to_keysym(keysym); - callback(k, state.kbd_bits); - } + if (keyboard_cb != nullptr) keyboard_cb(state.kbd_bits, oreg); break; default: break; diff --git a/common/string.hpp b/common/string.hpp new file mode 100644 index 0000000..e3b90af --- /dev/null +++ b/common/string.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace string { + template + inline void hex(T * c, uint32_t len, uint32_t n) + { + while (len > 0) { + uint32_t nib = n & 0xf; + n = n >> 4; + if (nib > 9) { + nib += (97 - 10); + } else { + nib += (48 - 0); + } + + c[--len] = nib; + } + } +} diff --git a/editor/main_saturn.cpp b/editor/main_saturn.cpp index 735da08..0a8be84 100644 --- a/editor/main_saturn.cpp +++ b/editor/main_saturn.cpp @@ -166,8 +166,10 @@ inline void keyboard_regular_key(const enum keysym k) } } -void keyboard_callback(const enum keysym k, uint8_t kbd_bits) +void keyboard_callback(uint8_t kbd_bits, uint8_t scancode) { + enum keysym k = scancode_to_keysym(scancode); + if (KEYBOARD__MAKE(kbd_bits)) { switch (k) { case keysym::LEFT_SHIFT : modifier_state |= MODIFIER_LEFT_SHIFT; break; @@ -200,7 +202,7 @@ void smpc_int(void) scu.reg.IST &= ~(IST__SMPC); scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN); - intback::keyboard_fsm(keyboard_callback); + intback::fsm(nullptr, keyboard_callback); } constexpr int32_t plane_a = 2; diff --git a/m68k/Makefile b/m68k/Makefile index fbbd87c..bea722a 100644 --- a/m68k/Makefile +++ b/m68k/Makefile @@ -9,6 +9,6 @@ include $(LIB)/m68k/common.mk %.pcm.o: %.pcm $(BUILD_BINARY_O) -slot.elf: slot.o sine-44100-s16be-1ch.pcm.o +slot.elf: slot.o sine-44100-s16be-1ch-1sec.pcm.o interrupt.elf: interrupt.o jojo-11025-s16be-1ch.pcm.o diff --git a/m68k/interrupt.cpp b/m68k/interrupt.cpp index 2c253ac..c43c7e9 100644 --- a/m68k/interrupt.cpp +++ b/m68k/interrupt.cpp @@ -37,8 +37,7 @@ void auto_vector_1(void) slot.LSA = 0; // loop start address (samples) slot.LEA = frame_size; // loop end address (samples) slot.EG = EG__AR(0x1f) | EG__EGHOLD; // d2r d1r ho ar krs dl rr - slot.VOLUME = 0; // stwinh sdir tl - slot.FM = 0; // mdl mdxsl mdysl + slot.FM = 0; // stwinh sdir tl mdl mdxsl mdysl slot.PITCH = PITCH__OCT(-2) | PITCH__FNS(0); // oct fns slot.LFO = 0; // lfof plfows slot.MIXER = MIXER__DISDL(0b101); // disdl dipan efsdl efpan @@ -63,8 +62,7 @@ void main() slot.LSA = 0; slot.LEA = 0; slot.EG = 0; - slot.VOLUME = 0; - slot.FM = 0; + slot.FM = 0; // 32-bit access slot.PITCH = 0; slot.LFO = 0; slot.MIXER = 0; diff --git a/m68k/slot.cpp b/m68k/slot.cpp index c71302a..c798dcc 100644 --- a/m68k/slot.cpp +++ b/m68k/slot.cpp @@ -21,8 +21,7 @@ void main() slot.LSA = 0; // loop start address (samples) slot.LEA = 44100; // loop end address (samples) slot.EG = EG__AR(0x1f) | EG__EGHOLD; // d2r d1r ho ar krs dl rr - slot.VOLUME = 0; // stwinh sdir tl - slot.FM = 0; // mdl mdxsl mdysl + slot.FM = 0; // stwinh sdir tl mdl mdxsl mdysl slot.PITCH = PITCH__OCT(0) | PITCH__FNS(0); // oct fns slot.LFO = 0; // lfof plfows slot.MIXER = MIXER__DISDL(0b101); // disdl dipan efsdl efpan diff --git a/scsp/fm.cpp b/scsp/fm.cpp new file mode 100644 index 0000000..3b04c7d --- /dev/null +++ b/scsp/fm.cpp @@ -0,0 +1,753 @@ +#include +#include + +#include "vdp2.h" +#include "smpc.h" +#include "scu.h" +#include "sh2.h" +#include "scsp.h" + +#include "../common/copy.hpp" +#include "../common/intback.hpp" +#include "../common/vdp2_func.hpp" +#include "../common/string.hpp" + +extern void * _sine_start __asm("_binary_scsp_sine_44100_s16be_1ch_100sample_pcm_start"); +extern void * _sine_size __asm("_binary_scsp_sine_44100_s16be_1ch_100sample_pcm_size"); + +struct mask_bit { + uint32_t mask; + uint32_t bit; +}; + +template +constexpr inline std::remove_volatile_t get(const typename T::reg_type r) +{ + return (r >> T::bit) & T::mask; +} + +template +constexpr inline void set(typename T::reg_type& r, uint32_t n) +{ + r = (r & ~(T::mask << T::bit)) | ((n & T::mask) << T::bit); +} + +template +constexpr inline void incdec(typename T::reg_type& r, int32_t n) +{ + int32_t ni = static_cast(get(r)) + n; + set(r, ni); +} + + +#define BITS(N, T, M, B) \ + struct N { using reg_type = T; \ + static constexpr uint32_t mask = M; \ + static constexpr uint32_t bit = B; } + +namespace loop { + BITS(kyonex, reg16, 0b1, 12); + BITS(kyonb, reg16, 0b1, 11); + BITS(sbctl, reg16, 0b11, 9); + BITS(ssctl, reg16, 0b11, 7); + BITS(lpctl, reg16, 0b11, 5); + BITS(pcm8b, reg16, 0b1, 4); +} + +namespace sa { + BITS(sa, reg32, 0xf'ffff, 0); +} + +namespace lsa { + BITS(lsa, reg16, 0xffff, 0); +} + +namespace lea { + BITS(lea, reg16, 0xffff, 0); +} + +namespace eg { + BITS(d2r, reg32, 0x1f, 11 + 16); + BITS(d1r, reg32, 0x1f, 6 + 16); + BITS(eghold, reg32, 0b1, 5 + 16); + BITS(ar, reg32, 0x1f, 0 + 16); + BITS(lpslnk, reg32, 0b1, 14); + BITS(krs, reg32, 0xf, 10); + BITS(dl, reg32, 0x1f, 5); + BITS(rr, reg32, 0x1f, 0); +} + +namespace fm { + BITS(stwinh, reg32, 0b1, 9 + 16); + BITS(sdir, reg32, 0b1, 8 + 16); + BITS(tl, reg32, 0xff, 0 + 16); + BITS(mdl, reg32, 0xf, 12); + BITS(mdxsl, reg32, 0x3f, 6); + BITS(mdysl, reg32, 0x3f, 0); +} + +namespace pitch { + BITS(oct, reg16, 0xf, 11); + BITS(fns, reg16, 0x3ff, 0); +} + +namespace lfo { + BITS(lfore, reg16, 0b1, 15); + BITS(lfof, reg16, 0x1f, 10); + BITS(plfows, reg16, 0b11, 8); + BITS(plfos, reg16, 0b111, 5); + BITS(alfows, reg16, 0b11, 3); + BITS(alfos, reg16, 0b111, 0); +} + +namespace mixer { + BITS(isel, reg32, 0b1111, 3 + 16); + BITS(imxl, reg32, 0b111, 0 + 16); + BITS(disdl, reg32, 0b111, 13); + BITS(dipan, reg32, 0b11111, 8); + BITS(efsdl, reg32, 0b111, 5); + BITS(efpan, reg32, 0b11111, 0); +} + +enum class scsp_name : uint8_t { + kyonex, + kyonb, + sbctl, + ssctl, + lpctl, + pcm8b, + + sa, + + lsa, + + lea, + + d2r, + d1r, + eghold, + ar, + lpslnk, + krs, + dl, + rr, + + stwinh, + sdir, + tl, + mdl, + mdxsl, + mdysl, + + oct, + fns, + + lfore, + lfof, + plfows, + plfos, + alfows, + alfos, + + isel, + imxl, + disdl, + dipan, + efsdl, + efpan, + + FIRST = kyonex, + LAST = efpan, +}; + +std::optional grid[7][8] = { + // 0 1 2 3 4 5 6 7 + {scsp_name::kyonex, scsp_name::kyonb, scsp_name::sbctl, std::nullopt, scsp_name::ssctl, std::nullopt, scsp_name::lpctl, scsp_name::pcm8b}, + {scsp_name::sa, std::nullopt, std::nullopt, scsp_name::lsa, std::nullopt, std::nullopt, scsp_name::lea, std::nullopt}, + {scsp_name::d2r, scsp_name::d1r, scsp_name::eghold, scsp_name::ar, scsp_name::lpslnk, scsp_name::krs, scsp_name::dl, scsp_name::rr}, + {scsp_name::stwinh, std::nullopt, scsp_name::sdir, scsp_name::tl, scsp_name::mdl, scsp_name::mdxsl, std::nullopt, scsp_name::mdysl}, + {scsp_name::oct, std::nullopt, std::nullopt, std::nullopt, scsp_name::fns, std::nullopt, std::nullopt, std::nullopt}, + {scsp_name::lfore, scsp_name::lfof, scsp_name::plfows, std::nullopt, scsp_name::plfos, scsp_name::alfows, std::nullopt, scsp_name::alfos}, + {scsp_name::isel, scsp_name::imxl, std::nullopt, scsp_name::disdl, scsp_name::dipan, scsp_name::efsdl, std::nullopt, scsp_name::efpan}, +}; + +uint32_t get_reg(scsp_slot& slot, scsp_name reg_name) +{ + switch (reg_name) { + case scsp_name::kyonex: return get(slot.LOOP); break; + case scsp_name::kyonb: return get(slot.LOOP); break; + case scsp_name::sbctl: return get(slot.LOOP); break; + case scsp_name::ssctl: return get(slot.LOOP); break; + case scsp_name::lpctl: return get(slot.LOOP); break; + case scsp_name::pcm8b: return get(slot.LOOP); break; + + case scsp_name::sa: return get(slot.SA); break; + + case scsp_name::lsa: return get(slot.LSA); break; + + case scsp_name::lea: return get(slot.LEA); break; + + case scsp_name::d2r: return get(slot.EG); break; + case scsp_name::d1r: return get(slot.EG); break; + case scsp_name::eghold: return get(slot.EG); break; + case scsp_name::ar: return get(slot.EG); break; + case scsp_name::lpslnk: return get(slot.EG); break; + case scsp_name::krs: return get(slot.EG); break; + case scsp_name::dl: return get(slot.EG); break; + case scsp_name::rr: return get(slot.EG); break; + + case scsp_name::stwinh: return get(slot.FM); break; + case scsp_name::sdir: return get(slot.FM); break; + case scsp_name::tl: return get(slot.FM); break; + case scsp_name::mdl: return get(slot.FM); break; + case scsp_name::mdxsl: return get(slot.FM); break; + case scsp_name::mdysl: return get(slot.FM); break; + + case scsp_name::oct: return get(slot.PITCH); break; + case scsp_name::fns: return get(slot.PITCH); break; + + case scsp_name::lfore: return get(slot.LFO); break; + case scsp_name::lfof: return get(slot.LFO); break; + case scsp_name::plfows: return get(slot.LFO); break; + case scsp_name::plfos: return get(slot.LFO); break; + case scsp_name::alfows: return get(slot.LFO); break; + case scsp_name::alfos: return get(slot.LFO); break; + + case scsp_name::isel: return get(slot.MIXER); break; + case scsp_name::imxl: return get(slot.MIXER); break; + case scsp_name::disdl: return get(slot.MIXER); break; + case scsp_name::dipan: return get(slot.MIXER); break; + case scsp_name::efsdl: return get(slot.MIXER); break; + case scsp_name::efpan: return get(slot.MIXER); break; + } + while (1) {} +} + +void incdec_reg(scsp_slot& slot, scsp_name reg_name, int32_t dir) +{ + switch (reg_name) { + case scsp_name::kyonex: return set(slot.LOOP, 1); break; + case scsp_name::kyonb: return incdec(slot.LOOP, dir); break; + case scsp_name::sbctl: return incdec(slot.LOOP, dir); break; + case scsp_name::ssctl: return incdec(slot.LOOP, dir); break; + case scsp_name::lpctl: return incdec(slot.LOOP, dir); break; + case scsp_name::pcm8b: return incdec(slot.LOOP, dir); break; + + case scsp_name::sa: return incdec(slot.SA, dir); break; + + case scsp_name::lsa: return incdec(slot.LSA, dir); break; + + case scsp_name::lea: return incdec(slot.LEA, dir); break; + + case scsp_name::d2r: return incdec(slot.EG, dir); break; + case scsp_name::d1r: return incdec(slot.EG, dir); break; + case scsp_name::eghold: return incdec(slot.EG, dir); break; + case scsp_name::ar: return incdec(slot.EG, dir); break; + case scsp_name::lpslnk: return incdec(slot.EG, dir); break; + case scsp_name::krs: return incdec(slot.EG, dir); break; + case scsp_name::dl: return incdec(slot.EG, dir); break; + case scsp_name::rr: return incdec(slot.EG, dir); break; + + case scsp_name::stwinh: return incdec(slot.FM, dir); break; + case scsp_name::sdir: return incdec(slot.FM, dir); break; + case scsp_name::tl: return incdec(slot.FM, dir); break; + case scsp_name::mdl: return incdec(slot.FM, dir); break; + case scsp_name::mdxsl: return incdec(slot.FM, dir); break; + case scsp_name::mdysl: return incdec(slot.FM, dir); break; + + case scsp_name::oct: return incdec(slot.PITCH, dir); break; + case scsp_name::fns: return incdec(slot.PITCH, dir); break; + + case scsp_name::lfore: return incdec(slot.LFO, dir); break; + case scsp_name::lfof: return incdec(slot.LFO, dir); break; + case scsp_name::plfows: return incdec(slot.LFO, dir); break; + case scsp_name::plfos: return incdec(slot.LFO, dir); break; + case scsp_name::alfows: return incdec(slot.LFO, dir); break; + case scsp_name::alfos: return incdec(slot.LFO, dir); break; + + case scsp_name::isel: return incdec(slot.MIXER, dir); break; + case scsp_name::imxl: return incdec(slot.MIXER, dir); break; + case scsp_name::disdl: return incdec(slot.MIXER, dir); break; + case scsp_name::dipan: return incdec(slot.MIXER, dir); break; + case scsp_name::efsdl: return incdec(slot.MIXER, dir); break; + case scsp_name::efpan: return incdec(slot.MIXER, dir); break; + } + while (1) {} +} + +struct label_value { + uint8_t name[7]; + uint8_t len; + struct { + int8_t y; + int8_t x; + } label; + struct { + int8_t y; + int8_t x; + } value; +}; + +template +constexpr typename std::underlying_type::type u(E e) noexcept +{ + return static_cast::type>(e); +} + +constexpr inline int32_t clz(uint32_t v) +{ + if (v == 0) return 32; + int32_t n = 0; + if ((v & 0xFFFF0000) == 0) { n = n + 16; v = v << 16; } + if ((v & 0xFF000000) == 0) { n = n + 8; v = v << 8; } + if ((v & 0xF0000000) == 0) { n = n + 4; v = v << 4; } + if ((v & 0xC0000000) == 0) { n = n + 2; v = v << 2; } + if ((v & 0x80000000) == 0) { n = n + 1; } + return n; +} + +constexpr inline int32_t hl(const uint32_t v) +{ + // return: number digits in v when represented as a base16 string + int32_t n = 32 - clz(v); + n = (n + 3) & ~0x03; // round up to nearest multiple of 4 + return n >> 2; // divide by 4 +} + +label_value label_value_table[] = { + [u(scsp_name::kyonex)] = { "KX", hl(loop::kyonex::mask), {1, 2}, {2, 2} }, + [u(scsp_name::kyonb)] = { "KB", hl(loop::kyonb::mask), {1, 7}, {2, 7} }, + [u(scsp_name::sbctl)] = { "SBCTL", hl(loop::sbctl::mask), {1, 12}, {2, 12} }, + [u(scsp_name::ssctl)] = { "SSCTL", hl(loop::ssctl::mask), {1, 20}, {2, 20} }, + [u(scsp_name::lpctl)] = { "LPCTL", hl(loop::lpctl::mask), {1, 28}, {2, 28} }, + [u(scsp_name::pcm8b)] = { "8B", hl(loop::pcm8b::mask), {1, 36}, {2, 36} }, + + [u(scsp_name::sa)] = { "SA", hl(sa::sa::mask), {4, 2}, {4, 5} }, + + [u(scsp_name::lsa)] = { "LSA", hl(lsa::lsa::mask), {4, 15}, {4, 19} }, + + [u(scsp_name::lea)] = { "LEA", hl(lea::lea::mask), {4, 28}, {4, 32} }, + + [u(scsp_name::d2r)] = { "D2R", hl(eg::d2r::mask), {6, 2}, {7, 2} }, + [u(scsp_name::d1r)] = { "D1R", hl(eg::d1r::mask), {6, 7}, {7, 7} }, + [u(scsp_name::eghold)] = { "HO", hl(eg::eghold::mask), {6, 12}, {7, 12} }, + [u(scsp_name::ar)] = { "AR", hl(eg::ar::mask), {6, 16}, {7, 16} }, + [u(scsp_name::lpslnk)] = { "LS", hl(eg::lpslnk::mask), {6, 20}, {7, 20} }, + [u(scsp_name::krs)] = { "KRS", hl(eg::krs::mask), {6, 24}, {7, 24} }, + [u(scsp_name::dl)] = { "DL", hl(eg::dl::mask), {6, 29}, {7, 29} }, + [u(scsp_name::rr)] = { "RR", hl(eg::rr::mask), {6, 33}, {7, 33} }, + + [u(scsp_name::stwinh)] = { "STWINH", hl(fm::stwinh::mask), {9, 2}, {10, 2} }, + [u(scsp_name::sdir)] = { "SDIR", hl(fm::sdir::mask), {9, 10}, {10, 10} }, + [u(scsp_name::tl)] = { "TL", hl(fm::tl::mask), {9, 16}, {10, 16} }, + [u(scsp_name::mdl)] = { "MDL", hl(fm::mdl::mask), {9, 20}, {10, 20} }, + [u(scsp_name::mdxsl)] = { "MDXSL", hl(fm::mdxsl::mask), {9, 25}, {10, 25} }, + [u(scsp_name::mdysl)] = { "MDYSL", hl(fm::mdysl::mask), {9, 32}, {10, 32} }, + + [u(scsp_name::oct)] = { "OCT", hl(pitch::oct::mask), {12, 11}, {12, 15} }, + [u(scsp_name::fns)] = { "FNS", hl(pitch::fns::mask), {12, 21}, {12, 25} }, + + [u(scsp_name::lfore)] = { "LFORE", hl(lfo::lfore::mask), {14, 2}, {15, 2} }, + [u(scsp_name::lfof)] = { "LFOF", hl(lfo::lfof::mask), {14, 8}, {15, 8} }, + [u(scsp_name::plfows)] = { "PLFOWS", hl(lfo::plfows::mask), {14, 13}, {15, 13} }, + [u(scsp_name::plfos)] = { "PLFOS", hl(lfo::plfos::mask), {14, 20}, {15, 20} }, + [u(scsp_name::alfows)] = { "ALFOWS", hl(lfo::alfows::mask), {14, 26}, {15, 26} }, + [u(scsp_name::alfos)] = { "ALFOS", hl(lfo::alfos::mask), {14, 33}, {15, 33} }, + + [u(scsp_name::isel)] = { "ISEL", hl(mixer::isel::mask), {17, 2}, {18, 2} }, + [u(scsp_name::imxl)] = { "IMXL", hl(mixer::imxl::mask), {17, 8}, {18, 8} }, + [u(scsp_name::disdl)] = { "DISDL", hl(mixer::disdl::mask), {17, 14}, {18, 14} }, + [u(scsp_name::dipan)] = { "DIPAN", hl(mixer::dipan::mask), {17, 20}, {18, 20} }, + [u(scsp_name::efsdl)] = { "EFSDL", hl(mixer::efsdl::mask), {17, 26}, {18, 26} }, + [u(scsp_name::efpan)] = { "EFPAN", hl(mixer::efpan::mask), {17, 32}, {18, 32} }, +}; + +extern void * _nec_bitmap_start __asm("_binary_res_nec_bitmap_bin_start"); + +constexpr inline uint16_t rgb15(int32_t r, int32_t g, int32_t b) +{ + return ((b & 31) << 10) | ((g & 31) << 5) | ((r & 31) << 0); +} + +void palette_data() +{ + vdp2.cram.u16[1 + 0 ] = rgb15( 0, 0, 0); + vdp2.cram.u16[2 + 0 ] = rgb15(31, 31, 31); + + vdp2.cram.u16[1 + 16] = rgb15(31, 31, 31); + vdp2.cram.u16[2 + 16] = rgb15( 0, 0, 0); + + vdp2.cram.u16[1 + 32] = rgb15(10, 10, 10); + vdp2.cram.u16[2 + 32] = rgb15(31, 31, 31); +} + +namespace pix_fmt_4bpp +{ + constexpr inline uint32_t + bit(uint8_t n, int32_t i) + { + i &= 7; + auto b = (n >> (7 - i)) & 1; + return ((b + 1) << ((7 - i) * 4)); + } + + constexpr inline uint32_t + bits(uint8_t n) + { + return + bit(n, 0) | bit(n, 1) | bit(n, 2) | bit(n, 3) + | bit(n, 4) | bit(n, 5) | bit(n, 6) | bit(n, 7); + } + + static_assert(bits(0b1100'1110) == 0x2211'2221); + static_assert(bits(0b1010'0101) == 0x2121'1212); + static_assert(bits(0b1000'0000) == 0x2111'1111); +} + +void cell_data() +{ + const uint8_t * normal = reinterpret_cast(&_nec_bitmap_start); + + for (int ix = 0; ix <= (0x7f - 0x20); ix++) { + for (int y = 0; y < 8; y++) { + const uint8_t row_n = normal[ix * 8 + y]; + vdp2.vram.u32[ 0 + (ix * 8) + y] = pix_fmt_4bpp::bits(row_n); + } + } +} + +struct count_flop { + s8 count; + u8 flop; + u8 das; + u8 repeat; +}; + +struct input { + count_flop right; + count_flop left; + count_flop down; + count_flop up; + count_flop start; + count_flop a; + count_flop b; + count_flop c; + count_flop r; + count_flop x; + count_flop y; + count_flop z; + count_flop l; +}; + +constexpr int input_arr = 10; +constexpr int input_das = 20; +constexpr int input_debounce = 2; + +static inline void +input_count(count_flop& button, uint32_t input, uint32_t mask) +{ + if ((input & mask) == 0) { + if (button.count < input_debounce) + button.count += 1; + else + button.das += 1; + } else { + if (button.count == 0) { + button.flop = 0; + button.das = 0; + button.repeat = 0; + } + else if (button.count > 0) + button.count -= 1; + } +} + +static inline int32_t +input_flopped(count_flop& button) +{ + if (button.count == input_debounce && button.flop == 0) { + button.flop = 1; + return 1; + } else if (button.flop == 1 && button.das == input_das && button.repeat == 0) { + button.repeat = 1; + button.das = 0; + return 2; + } else if (button.repeat == 1 && (button.das == input_arr)) { + button.das = 0; + return 2; + } else { + return 0; + } +} + +struct cursor { + int8_t x; + int8_t y; + + void left() { do { x = (x - 1) & 7; } while (!grid[y][x]); } + void right() { do { x = (x + 1) & 7; } while (!grid[y][x]); } + void up() + { + y -= 1; + if (y < 0) y = 6; + while (!grid[y][x]) { x--; }; + } + void down() + { + y += 1; + if (y > 6) y = 0; + while (!grid[y][x]) { x--; }; + } +}; + +struct state { + uint8_t slot_ix; + struct input input; + struct cursor cursor; +}; + +static struct state state = { 0 }; + +void digital_callback(uint8_t fsm_state, uint8_t data) +{ + switch (fsm_state) { + case intback::DATA1: + input_count(state.input.right, data, DIGITAL__1__RIGHT); + input_count(state.input.left, data, DIGITAL__1__LEFT); + input_count(state.input.down, data, DIGITAL__1__DOWN); + input_count(state.input.up, data, DIGITAL__1__UP); + input_count(state.input.start, data, DIGITAL__1__START); + input_count(state.input.a, data, DIGITAL__1__A); + input_count(state.input.c, data, DIGITAL__1__C); + input_count(state.input.b, data, DIGITAL__1__B); + break; + case intback::DATA2: + input_count(state.input.r, data, DIGITAL__2__R); + input_count(state.input.x, data, DIGITAL__2__X); + input_count(state.input.y, data, DIGITAL__2__Y); + input_count(state.input.z, data, DIGITAL__2__Z); + input_count(state.input.l, data, DIGITAL__2__L); + break; + default: break; + } +} + +extern "C" +void smpc_int(void) __attribute__ ((interrupt_handler)); +void smpc_int(void) +{ + scu.reg.IST &= ~(IST__SMPC); + scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN); + + intback::fsm(digital_callback, nullptr); +} + +constexpr int32_t plane_a = 2; +constexpr inline int32_t plane_offset(int32_t n) { return n * 0x2000; } + +constexpr int32_t page_size = 64 * 64 * 2; // N0PNB__1WORD (16-bit) +constexpr int32_t plane_size = page_size * 1; + +constexpr int32_t cell_size = (8 * 8) / 2; // N0CHCN__16_COLOR (4-bit) +constexpr int32_t character_size = cell_size * (1 * 1); // N0CHSZ__1x1_CELL +constexpr int32_t page_width = 64; + +static int plane_ix = 0; + +inline void +set_char(int32_t x, int32_t y, uint8_t palette, uint8_t c) +{ + const auto ix = (plane_offset(plane_a + plane_ix) / 2) + (y * page_width) + x; + vdp2.vram.u16[ix] = + PATTERN_NAME_TABLE_1WORD__PALETTE(palette) + | PATTERN_NAME_TABLE_1WORD__CHARACTER((c - 0x20)); +} + +void render() +{ + // 012345678901234 + uint8_t label[] = "scsp.reg.slot[ ]"; + string::hex(&label[14], 2, state.slot_ix); + for (uint32_t i = 0; label[i] != 0; i++) { + set_char(0 + i, 1, 0, label[i]); + } + + constexpr int32_t y_offset = 3; + + for (uint32_t f_ix = u(scsp_name::FIRST); f_ix <= u(scsp_name::LAST); f_ix++) { + label_value& lv = label_value_table[f_ix]; + for (uint32_t i = 0; lv.name[i] != 0; i++) { + set_char(lv.label.x + i, lv.label.y + y_offset, 0, lv.name[i]); + } + + uint8_t buf[lv.len]; + scsp_name name = static_cast(f_ix); + uint32_t value = get_reg(scsp.reg.slot[state.slot_ix], name); + string::hex(buf, lv.len, value); + bool selected = !(!grid[state.cursor.y][state.cursor.x]) && name == *grid[state.cursor.y][state.cursor.x]; + uint32_t palette = selected ? 1 : 0; + for (uint32_t i = 0; i < lv.len; i++) { + set_char(lv.value.x + i, lv.value.y + y_offset, palette, buf[i]); + } + } +} + +namespace event { + inline bool prev_slot() { return input_flopped(state.input.l) == 1; } + inline bool next_slot() { return input_flopped(state.input.r) == 1; } + inline bool cursor_left() { return input_flopped(state.input.left ) >= 1; } + inline bool cursor_right() { return input_flopped(state.input.right) >= 1; } + inline bool cursor_up() { return input_flopped(state.input.up ) >= 1; } + inline bool cursor_down() { return input_flopped(state.input.down ) >= 1; } + inline bool cursor_dec() { return input_flopped(state.input.x ) >= 1; } + inline bool cursor_inc() { return input_flopped(state.input.y ) >= 1; } +} + +void update() +{ + if (event::prev_slot()) + state.slot_ix = (state.slot_ix - 1) & 31; + if (event::next_slot()) + state.slot_ix = (state.slot_ix + 1) & 31; + if (event::cursor_left()) state.cursor.left(); + if (event::cursor_right()) state.cursor.right(); + if (event::cursor_up()) state.cursor.up(); + if (event::cursor_down()) state.cursor.down(); + if (grid[state.cursor.y][state.cursor.x]) { + if (event::cursor_inc()) incdec_reg(scsp.reg.slot[state.slot_ix], *grid[state.cursor.y][state.cursor.x], +1); + if (event::cursor_dec()) incdec_reg(scsp.reg.slot[state.slot_ix], *grid[state.cursor.y][state.cursor.x], -1); + } +} + +extern "C" +void v_blank_in_int(void) __attribute__ ((interrupt_handler)); +void v_blank_in_int() +{ + scu.reg.IST &= ~(IST__V_BLANK_IN); + scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN); + + // flip planes; + vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(plane_a + plane_ix); + plane_ix = !plane_ix; + + // wait at least 300us, as specified in the SMPC manual. + // It appears reading FRC.H is mandatory and *must* occur before FRC.L on real + // hardware. + while ((sh2.reg.FTCSR & FTCSR__OVF) == 0 && sh2.reg.FRC.H == 0 && sh2.reg.FRC.L < 63); + + if ((vdp2.reg.TVSTAT & TVSTAT__VBLANK) != 0) { + // on real hardware, SF contains uninitialized garbage bits other than the + // lsb. + while ((smpc.reg.SF & 1) != 0); + + smpc.reg.SF = 0; + + smpc.reg.IREG[0].val = INTBACK__IREG0__STATUS_DISABLE; + smpc.reg.IREG[1].val = ( INTBACK__IREG1__PERIPHERAL_DATA_ENABLE + | INTBACK__IREG1__PORT2_15BYTE + | INTBACK__IREG1__PORT1_15BYTE + ); + smpc.reg.IREG[2].val = INTBACK__IREG2__MAGIC; + + smpc.reg.COMREG = COMREG__INTBACK; + } + + update(); + render(); +} + +void init_slots() +{ + while ((smpc.reg.SF & 1) != 0); + smpc.reg.SF = 1; + smpc.reg.COMREG = COMREG__SNDON; + while (smpc.reg.OREG[31].val != 0b00000110); + + for (long i = 0; i < 807; i++) { asm volatile ("nop"); } // wait for (way) more than 30µs + + scsp.reg.ctrl.MIXER = MIXER__MEM4MB | MIXER__MVOL(0); + + const uint32_t * buf = reinterpret_cast(&_sine_start); + const uint32_t size = reinterpret_cast(&_sine_size); + copy(&scsp.ram.u32[0], buf, size); + + for (int i = 0; i < 32; i++) { + scsp_slot& slot = scsp.reg.slot[i]; + // 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 = 100; // loop end address (samples) + slot.EG = EG__EGHOLD; // d2r d1r ho ar krs dl rr + slot.FM = 0; // stwinh sdir tl mdl mdxsl mdysl + slot.PITCH = PITCH__OCT(0) | PITCH__FNS(0); // oct fns + slot.LFO = 0; // lfof plfows + slot.MIXER = MIXER__DISDL(0b101); // disdl dipan efsdl efpan + } + + scsp.reg.ctrl.MIXER = MIXER__MEM4MB | MIXER__MVOL(0xf); +} + +void main() +{ + init_slots(); + + v_blank_in(); + + // 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); + + /* set the color mode to 5bits per channel, 1024 colors */ + vdp2.reg.RAMCTL = RAMCTL__CRMD__RGB_5BIT_1024; + + /* enable display of NBG0 */ + vdp2.reg.BGON = BGON__N0ON; + + /* set character format for NBG0 to palettized 16 color + set enable "cell format" for NBG0 + set character size for NBG0 to 1x1 cell */ + vdp2.reg.CHCTLA = CHCTLA__N0CHCN__16_COLOR + | CHCTLA__N0BMEN__CELL_FORMAT + | CHCTLA__N0CHSZ__1x1_CELL; + /* "Note: In color RAM modes 0 and 2, 2048-color becomes 1024-color" */ + + /* use 1-word (16-bit) pattern names */ + vdp2.reg.PNCN0 = PNCN0__N0PNB__1WORD; + + /* plane size */ + vdp2.reg.PLSZ = PLSZ__N0PLSZ__1x1; + + /* map plane offset + 1-word: value of bit 6-0 * 0x2000 + 2-word: value of bit 5-0 * 0x4000 + */ + vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 8~6 + vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(plane_a); // bits 5~0 + vdp2.reg.MPCDN0 = MPABN0__N0MPD(0) | MPABN0__N0MPC(0); // bits 5~0 + + // zeroize character/cell data from 0 up to plane_a_offset + fill(&vdp2.vram.u32[(0 / 4)], 0, plane_offset(plane_a)); + + // zeroize plane_a; `0` is the ascii 0x20 ("space") which doubles as + // "transparency" character. + fill(&vdp2.vram.u32[(plane_offset(plane_a) / 4)], 0, plane_size * 2); + + palette_data(); + cell_data(); + + // free-running timer + sh2.reg.TCR = TCR__CKS__INTERNAL_DIV128; + sh2.reg.FTCSR = 0; + + // initialize smpc + smpc.reg.DDR1 = 0; // INPUT + smpc.reg.DDR2 = 0; // INPUT + smpc.reg.IOSEL = 0; // SMPC control + smpc.reg.EXLE = 0; // + + sh2_vec[SCU_VEC__SMPC] = (u32)(&smpc_int); + sh2_vec[SCU_VEC__V_BLANK_IN] = (u32)(&v_blank_in_int); + + scu.reg.IST = 0; + scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN); +} diff --git a/scsp/sine-44100-s16be-1ch.pcm b/scsp/sine-44100-s16be-1ch.pcm deleted file mode 100644 index 6d40564a1d7f0d87d1d30f3734860d3ab2ffc5df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88200 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 0) { - uint32_t nib = n & 0xf; - n = n >> 4; - - if (nib > 9) { - nib += (97 - 10); - } else { - nib += (48 - 0); - } - - c[--len] = nib; - - ret++; - } - return ret; -} - int32_t draw_string(const uint8_t * string, const uint32_t length, const int32_t x, @@ -171,7 +151,7 @@ inline void draw_label(const uint8_t(&label)[size], int32_t advance, int32_t & r advance = (advance * 8) << 6; advance += draw_string(label, (sizeof (label)) - 1, advance, row); uint8_t v[n]; - print_hex(v, (sizeof (v)), value); + string::hex(v, (sizeof (v)), value); draw_string(v, (sizeof (v)), advance, row); row++; diff --git a/smpc/input_keyboard.cpp b/smpc/input_keyboard.cpp index 0ca61d7..8f94d27 100644 --- a/smpc/input_keyboard.cpp +++ b/smpc/input_keyboard.cpp @@ -10,6 +10,7 @@ #include "../common/draw_font.hpp" #include "../common/palette.hpp" #include "../common/vdp2_func.hpp" +#include "../common/string.hpp" /* begin font */ @@ -149,27 +150,6 @@ static xy foo[2] = { {200, 100} }; -uint32_t print_hex(char16_t * c, uint32_t len, uint32_t n) -{ - uint32_t ret = 0; - - while (len > 0) { - uint32_t nib = n & 0xf; - n = n >> 4; - - if (nib > 9) { - nib += (97 - 10); - } else { - nib += (48 - 0); - } - - c[--len] = nib; - - ret++; - } - return ret; -} - static struct draw_font::state font_state; static uint32_t global_cmd_ix = 0; @@ -240,7 +220,7 @@ void smpc_int(void) { static int32_t y = 50 << 6; if (kbd_bits & 0b1000) { - print_hex(str_num, 2, keysym); + string::hex(str_num, 2, keysym); enum keysym k = scancode_to_keysym(keysym); int32_t c = keysym_to_char(k, false); @@ -268,7 +248,7 @@ void smpc_int(void) { draw_font::horizontal_string(font_state, cmd_ix, // modified &str_num[0], - 2, + 2, qx, qy); } diff --git a/wordle/main_saturn.cpp b/wordle/main_saturn.cpp index 813a6f0..5952c7b 100644 --- a/wordle/main_saturn.cpp +++ b/wordle/main_saturn.cpp @@ -42,8 +42,10 @@ uint32_t xorshift32(struct xorshift32_state *state) static xorshift32_state random_state = { 0x12345678 }; static uint32_t frame_count = 0; -void keyboard_callback(const enum keysym k, uint8_t kbd_bits) +void keyboard_callback(uint8_t kbd_bits, uint8_t scancode) { + enum keysym k = scancode_to_keysym(scancode); + if (!KEYBOARD__MAKE(kbd_bits)) return; @@ -71,7 +73,7 @@ void smpc_int(void) scu.reg.IST &= ~(IST__SMPC); scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN); - intback::keyboard_fsm(keyboard_callback); + intback::fsm(nullptr, keyboard_callback); } // rendering @@ -140,9 +142,9 @@ void v_blank_in_int() smpc.reg.IREG[0].val = INTBACK__IREG0__STATUS_DISABLE; smpc.reg.IREG[1].val = ( INTBACK__IREG1__PERIPHERAL_DATA_ENABLE - | INTBACK__IREG1__PORT2_15BYTE - | INTBACK__IREG1__PORT1_15BYTE - ); + | INTBACK__IREG1__PORT2_15BYTE + | INTBACK__IREG1__PORT1_15BYTE + ); smpc.reg.IREG[2].val = INTBACK__IREG2__MAGIC; smpc.reg.COMREG = COMREG__INTBACK;