From 3b82199d0804c4f3b338ecb23a174e819c92312f Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Fri, 19 May 2023 22:13:12 +0000 Subject: [PATCH] sound_cpu__interrupt: add slot visuals This shows the value of some of the most relevant SCSP slot fields. --- Makefile | 10 +- common/draw_font.hpp | 14 ++- m68k/Makefile | 2 +- m68k/interrupt.cpp | 58 ++++----- m68k/slot.cpp | 2 +- res/Bm437_SperryPC_CGA.otb | Bin 0 -> 7956 bytes scsp/sound_cpu__interrupt.cpp | 226 +++++++++++++++++++++++++++++++++- scsp/sound_cpu__slot.cpp | 15 ++- sh/lib1funcs.S | 12 +- tools/Makefile | 9 +- tools/ttf-convert.cpp | 51 ++++++-- wordle/main_saturn.cpp | 9 +- 12 files changed, 339 insertions(+), 69 deletions(-) create mode 100644 res/Bm437_SperryPC_CGA.otb diff --git a/Makefile b/Makefile index 543be9c..81a0718 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ OPT = -Og LIBGCC = $(shell $(CC) -print-file-name=libgcc.a) LIB = ./saturn -all: +all: include $(LIB)/common.mk @@ -51,6 +51,9 @@ res/dejavusansmono.font.bin: tools/ttf-convert res/ipapgothic.font.bin: tools/ttf-convert ./tools/ttf-convert 3000 30ff 28 $(shell fc-match -f '%{file}' 'IPAPGothic') $@ +res/sperrypc.font.bin: tools/ttf-convert + ./tools/ttf-convert 20 7f 8 res/Bm437_SperryPC_CGA.otb $@ + common/keyboard.hpp: common/keyboard.py python common/keyboard.py header > $@ @@ -86,7 +89,7 @@ m68k/%.bin: m68k 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 +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 # clean clean: clean-sh @@ -101,6 +104,7 @@ clean-sh: common/keyboard.cpp \ common/keyboard.hpp \ wordle/word_list.hpp - + make -C tools clean + make -C m68k clean PHONY: m68k tools diff --git a/common/draw_font.hpp b/common/draw_font.hpp index d30dabf..919a041 100644 --- a/common/draw_font.hpp +++ b/common/draw_font.hpp @@ -14,12 +14,13 @@ struct state { uint32_t font_data(void * buf, uint32_t top, state& state); template -uint32_t horizontal_string(state const& s, - uint32_t& cmd_ix, - const T * string, - const uint32_t length, - const int32_t x, - const int32_t y) +inline +int32_t horizontal_string(state const& s, + uint32_t& cmd_ix, + const T * string, + const uint32_t length, + const int32_t x, + const int32_t y) { int32_t total_advance = 0; @@ -50,6 +51,7 @@ uint32_t horizontal_string(state const& s, } template +inline uint32_t single_character_centered(state const& s, uint32_t cmd_ix, const T c, diff --git a/m68k/Makefile b/m68k/Makefile index 68ee351..fbbd87c 100644 --- a/m68k/Makefile +++ b/m68k/Makefile @@ -2,7 +2,7 @@ CFLAGS = -I../saturn OPT = -Og LIB = ../saturn -all: +all: include $(LIB)/m68k/common.mk diff --git a/m68k/interrupt.cpp b/m68k/interrupt.cpp index c3911ed..577f739 100644 --- a/m68k/interrupt.cpp +++ b/m68k/interrupt.cpp @@ -4,13 +4,15 @@ extern void * _jojo_start __asm("_binary_jojo_11025_s16be_1ch_pcm_start"); -static volatile int32_t frame = 0; +static volatile int32_t frame = 0x0; constexpr int32_t tactl = 7; constexpr int32_t frame_size = ((1 << tactl) * 256) / (44100 / 11025); constexpr int32_t frame_count = 507150 / (frame_size * 2); constexpr int32_t tima = 0; +static uint16_t slot_ix = 0; + extern "C" void auto_vector_1(void) __attribute__ ((interrupt_handler)); void auto_vector_1(void) @@ -20,15 +22,16 @@ void auto_vector_1(void) scsp.reg.ctrl.TIMA = TIMA__TACTL(tactl) | TIMA__TIMA(tima); if (frame > frame_count) frame = 0; - + const uint16_t * jojo_start = reinterpret_cast(&_jojo_start); const uint32_t frame_addr = reinterpret_cast(&jojo_start[frame * frame_size]); - - scsp_slot& slotp = scsp.reg.slot[(frame - 1) % 32]; + + scsp_slot& slotp = scsp.reg.slot[(slot_ix - 1) & 31]; slotp.LOOP = 0; slotp.LOOP |= LOOP__KYONEX; - scsp_slot& slot = scsp.reg.slot[(frame) % 32]; + scsp.reg.ctrl.STATUS = STATUS__MSLC(slot_ix & 31); + scsp_slot& slot = scsp.reg.slot[slot_ix & 31]; slot.LOOP = LOOP__KYONB | LOOP__SA(frame_addr); // kx kb sbctl[1:0] ssctl[1:0] lpctl[1:0] 8b sa[19:16] slot.SA = SA__SA(frame_addr); // start address (bytes) slot.LSA = 0; // loop start address (samples) @@ -42,54 +45,45 @@ void auto_vector_1(void) slot.LOOP |= LOOP__KYONEX; + scsp.ram.u32[0] = (0xdead << 16) | (slot_ix); + scsp.ram.u32[1] = frame; frame++; - + slot_ix++; + return; } void main() { for (long i = 0; i < 807; i++) { asm volatile ("nop"); } // wait for (way) more than 30µs - + for (int i = 0; i < 32; i++) { scsp_slot& slot = scsp.reg.slot[i]; slot.LOOP = 0; + slot.SA = 0; + slot.LSA = 0; + slot.LEA = 0; + slot.EG = 0; + slot.VOLUME = 0; + slot.FM = 0; + slot.PITCH = 0; + slot.LFO = 0; + slot.MIXER = 0; slot.LOOP |= LOOP__KYONEX; } scsp.reg.ctrl.MIXER = MIXER__MEM4MB | MIXER__MVOL(0xf); - + // timer A is vector 1 (0b001) scsp.reg.ctrl.SCILV2 = 0; scsp.reg.ctrl.SCILV1 = 0; scsp.reg.ctrl.SCILV0 = SCILV__TIMER_A; - + // enable TIMER_A scsp.reg.ctrl.SCIRE = INT__TIMER_A; scsp.reg.ctrl.SCIEB = INT__TIMER_A; - scsp.reg.ctrl.TIMA = TIMA__TACTL(tactl) | TIMA__TIMA(tima); - + scsp.reg.ctrl.TIMA = TIMA__TACTL(tactl) | TIMA__TIMA(tima); + asm volatile ("move.w #8192,%sr"); - - /* - const uint16_t * jojo_start = reinterpret_cast(&_jojo_start); - const uint32_t frame_addr = reinterpret_cast(&jojo_start[frame * 128]); - scsp_slot& slot = scsp.reg.slot[0]; - slot.LOOP = 0; - slot.LOOP |= LOOP__KYONEX; - - slot.LOOP = LOOP__KYONB | LOOP__SA(frame_addr); // kx kb sbctl[1:0] ssctl[1:0] lpctl[1:0] 8b sa[19:16] - slot.SA = SA__SA(frame_addr); // start address (bytes) - slot.LSA = 0; // loop start address (samples) - slot.LEA = 128; // 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.PITCH = PITCH__OCT(-2) | PITCH__FNS(0); // oct fns - slot.LFO = 0; // lfof plfows - slot.MIXER = MIXER__DISDL(0b101); // disdl dipan efsdl efpan - - slot.LOOP |= LOOP__KYONEX; - */ } diff --git a/m68k/slot.cpp b/m68k/slot.cpp index 86bfeb5..0f57ee2 100644 --- a/m68k/slot.cpp +++ b/m68k/slot.cpp @@ -15,7 +15,7 @@ void main() scsp_slot& slot = scsp.reg.slot[0]; slot.LOOP = 0; slot.LOOP |= LOOP__KYONEX; - + slot.LOOP = LOOP__KYONB | LOOP__LPCTL__NORMAL | LOOP__SA(sine_start); // kx kb sbctl[1:0] ssctl[1:0] lpctl[1:0] 8b sa[19:16] slot.SA = SA__SA(sine_start); // start address (bytes) slot.LSA = 0; // loop start address (samples) diff --git a/res/Bm437_SperryPC_CGA.otb b/res/Bm437_SperryPC_CGA.otb new file mode 100644 index 0000000000000000000000000000000000000000..10d9d0aa8ae937b374aea58f09bd442aa4cbfcce GIT binary patch literal 7956 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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, + const int32_t y) +{ + return draw_font::horizontal_string(draw_state.font, + draw_state.cmd_ix, + string, + length, + (x ), + ((y + 1) * 8) << 6); +} + +static inline uint32_t init_font(uint32_t top) +{ + // 256 is the number of colors in the color palette, not the number of grays + // that are used by the font. + constexpr uint32_t colors_per_palette = 256; + constexpr uint32_t color_bank_index = 0; // completely random and arbitrary value + + palette::vdp2_cram_32grays(colors_per_palette, color_bank_index); + // For color bank color, COLR is concatenated bitwise with pixel data. See + // Figure 6.17 in the VDP1 manual. + draw_state.font.color_address = color_bank_index << 8; + + top = font_data(&_sperrypc_start, top, draw_state.font); + + return top; +} + +void init_sound() { /* SEGA SATURN TECHNICAL BULLETIN # 51 @@ -23,6 +90,12 @@ void main() scsp.reg.ctrl.MIXER = MIXER__MEM4MB; + /* + The Saturn BIOS does not (un)initialize the DSP. Without zeroizing the DSP + program, the SCSP DSP appears to have a program that continuously writes to + 0x30000 through 0x3ffff in sound RAM, which has the effect of destroying any + samples stored there. + */ reg32 * dsp_steps = reinterpret_cast(&(scsp.reg.dsp.STEP[0].MPRO[0])); fill(dsp_steps, 0, (sizeof (scsp.reg.dsp.STEP))); @@ -34,8 +107,157 @@ void main() smpc.reg.SF = 1; smpc.reg.COMREG = COMREG__SNDON; while (smpc.reg.oreg[31] != OREG31__SNDON); +} - // do nothing while the sound CPU manipulates the SCSP +static inline void init_vdp() +{ + // wait for the beginning of a V blank + 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); + + // disable all VDP2 backgrounds (e.g: the Sega bios logo) + vdp2.reg.BGON = 0; + + // zeroize BACK color + vdp2.reg.BKTAU = 0; + vdp2.reg.BKTAL = 0; + vdp2.vram.u16[0] = 0; + + 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; + + // during a framebuffer erase cycle, write the color "black" to each pixel + constexpr uint16_t black = 0x0000; + vdp1.reg.EWDR = black; + + // 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); + + vdp1.vram.cmd[0].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__SYSTEM_CLIP_COORDINATES; + vdp1.vram.cmd[0].LINK = 0; + vdp1.vram.cmd[0].XC = 319; + vdp1.vram.cmd[0].YC = 239; + + vdp1.vram.cmd[1].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__LOCAL_COORDINATE; + vdp1.vram.cmd[1].LINK = 0; + vdp1.vram.cmd[1].XA = 0; + vdp1.vram.cmd[1].YA = 0; + + vdp1.vram.cmd[2].CTRL = CTRL__END; + + draw_state.cmd_ix = 2; + + // start drawing (execute the command list) on every frame + vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE; +} + +template +inline void draw_label(const uint8_t(&label)[size], int32_t advance, int32_t & row, uint32_t value) +{ + advance = (advance * 8) << 6; + advance += draw_string(label, (sizeof (label)) - 1, advance, row); + uint8_t v[n]; + print_hex(v, (sizeof (v)), value); + draw_string(v, (sizeof (v)), advance, row); + + row++; +}; + +static inline void render() +{ + draw_state.cmd_ix = 2; + + if ((scsp.ram.u32[0] & 0xffff0000) != static_cast(0xdead << 16)) + return; + + uint16_t slot_ix = scsp.ram.u32[0] & 0xffff; + uint16_t slotp_ix = (slot_ix - 1) & 31; + slot_ix &= 31; + uint32_t frame = scsp.ram.u32[1]; + + int32_t row = 1; + + const uint8_t frame_l[] = "frame: 0x"; + draw_label<4>(frame_l, 1, row, frame); + + row++; + + // previous slot + const uint8_t slotp_l[] = "slot_previous: 0x"; + draw_label<2>(slotp_l, 1, row, slotp_ix); + + scsp_slot& slot_p = scsp.reg.slot[slotp_ix]; + + const uint8_t kyonb_l[] = "KYONB: "; + const uint32_t kyonb_p = (slot_p.LOOP >> 11) & 1; + draw_label<1>(kyonb_l, 3, row, kyonb_p); + + row++; + + // current slot + const uint8_t slot_l[] = "slot: 0x"; + draw_label<2>(slot_l, 1, row, slot_ix); + + scsp_slot& slot = scsp.reg.slot[slot_ix]; + + uint32_t kyonb = (slot.LOOP >> 11) & 1; + draw_label<1>(kyonb_l, 3, row, kyonb); + + const uint8_t sa_l[] = "SA: 0x"; + const uint32_t sa = ((slot.LOOP & 0b1111) << 16) | slot.SA; + draw_label<5>(sa_l, 3, row, sa); + + const uint8_t lsa_l[] = "LSA: 0x"; + draw_label<4>(lsa_l, 3, row, slot.LSA); + + const uint8_t lea_l[] = "LEA: 0x"; + draw_label<4>(lea_l, 3, row, slot.LEA); + + row++; + + // global + const uint8_t ca_l[] = "STATUS__CA: 0x"; + draw_label<1>(ca_l, 1, row, STATUS__CA(scsp.reg.ctrl.STATUS)); + + vdp1.vram.cmd[draw_state.cmd_ix].CTRL = CTRL__END; +} + +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__V_BLANK_IN); + + render(); +} + +void main() +{ + uint32_t top = (sizeof (union vdp1_vram)); + top = init_font(top); + + init_vdp(); + + sh2_vec[SCU_VEC__V_BLANK_IN] = (u32)(&v_blank_in_int); + + scu.reg.IST = 0; + scu.reg.IMS = ~(IMS__V_BLANK_IN); + + init_sound(); } extern "C" diff --git a/scsp/sound_cpu__slot.cpp b/scsp/sound_cpu__slot.cpp index a5b6b14..4559011 100644 --- a/scsp/sound_cpu__slot.cpp +++ b/scsp/sound_cpu__slot.cpp @@ -15,23 +15,32 @@ void main() The document suggests that Sound RAM is (somewhat) preserved during SNDOFF. */ - + while ((smpc.reg.SF & 1) != 0); smpc.reg.SF = 1; smpc.reg.COMREG = COMREG__SNDOFF; while (smpc.reg.oreg[31] != OREG31__SNDOFF); scsp.reg.ctrl.MIXER = MIXER__MEM4MB; + + /* + The Saturn BIOS does not (un)initialize the DSP. Without zeroizing the DSP + program, the SCSP DSP appears to have a program that continuously writes to + 0x30000 through 0x3ffff in sound RAM, which has the effect of destroying any + samples stored there. + */ + reg32 * dsp_steps = reinterpret_cast(&(scsp.reg.dsp.STEP[0].MPRO[0])); + fill(dsp_steps, 0, (sizeof (scsp.reg.dsp.STEP))); uint32_t * m68k_main_start = reinterpret_cast(&_m68k_start); uint32_t m68k_main_size = reinterpret_cast(&_m68k_size); copy(&scsp.ram.u32[0], m68k_main_start, m68k_main_size); - + while ((smpc.reg.SF & 1) != 0); smpc.reg.SF = 1; smpc.reg.COMREG = COMREG__SNDON; while (smpc.reg.oreg[31] != OREG31__SNDON); - + // do nothing while the sound CPU manipulates the SCSP } diff --git a/sh/lib1funcs.S b/sh/lib1funcs.S index 2805841..2b32b94 100644 --- a/sh/lib1funcs.S +++ b/sh/lib1funcs.S @@ -45,7 +45,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #if defined __vxworks && defined __PIC__ #define NO_FPSCR_VALUES #endif - + #ifdef L_ashiftrt .global GLOBAL(ashiftrt_r4_0) .global GLOBAL(ashiftrt_r4_1) @@ -552,7 +552,7 @@ LOCAL(ashlsi_29): LOCAL(ashlsi_30): shlr2 r0 rts - shll16 r0 + shll16 r0 ENDFUNC(GLOBAL(ashlsi3)) ENDFUNC(GLOBAL(ashlsi3_r0)) @@ -728,7 +728,7 @@ LOCAL(lshrsi_29): LOCAL(lshrsi_30): shll2 r0 rts - shlr16 r0 + shlr16 r0 ENDFUNC(GLOBAL(lshrsi3)) ENDFUNC(GLOBAL(lshrsi3_r0)) @@ -1063,7 +1063,7 @@ GLOBAL(sdivsi3_i4): fdiv dr2,dr0 ftrc dr0,fpul rts - fpchg + fpchg #endif /* __SH4A__ */ @@ -1722,7 +1722,7 @@ LOCAL(div_ge64k_2): and r1,r0 bra LOCAL(div_ge64k_end) xor r4,r0 - + LOCAL(div_r8): shll16 r4 bra LOCAL(div_r8_2) @@ -2257,7 +2257,7 @@ GLOBAL(udiv_qrnnd_16): cmp/hi r6,r0 bt .Lots .rept 16 - div1 r6,r0 + div1 r6,r0 .endr extu.w r0,r1 bt 0f diff --git a/tools/Makefile b/tools/Makefile index e654c0d..cade8b0 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -10,6 +10,11 @@ all: ttf-convert %: %.o $(CXX) $(LDFLAGS) $< -o $@ -.SUFFIXES: +clean: + rm -f *.o ttf-convert + +.SUFFIXES: +.INTERMEDIATE: +.SECONDARY: +.PHONY: all clean -# /usr/share/fonts/OTF/ipagp.ttf diff --git a/tools/ttf-convert.cpp b/tools/ttf-convert.cpp index df9d57a..39d8446 100644 --- a/tools/ttf-convert.cpp +++ b/tools/ttf-convert.cpp @@ -68,7 +68,7 @@ load_outline_char(const FT_Face face, return -1; } - assert(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE); + //assert(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE); error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); if (error) { @@ -79,6 +79,8 @@ load_outline_char(const FT_Face face, uint32_t pitch = (face->glyph->bitmap.pitch + 8 - 1) & -8; uint32_t bitmap_size = face->glyph->bitmap.rows * pitch; + //printf("num_grays %d\n", face->glyph->bitmap.num_grays); + if (!(face->glyph->bitmap.pitch > 0)) { assert(pitch == 0); assert(bitmap_size == 0); @@ -86,11 +88,41 @@ load_outline_char(const FT_Face face, assert(face->glyph->bitmap.rows == 0); } - for (uint32_t y = 0; y < face->glyph->bitmap.rows; y++) { - for (uint32_t x = 0; x < face->glyph->bitmap.width; x++) { - uint8_t i = face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch + x]; - glyph_bitmaps[bitmap_offset + (y * pitch + x)] = i >> 3; + switch (face->glyph->bitmap.num_grays) { + case 2: + { + for (unsigned int y = 0; y < face->glyph->bitmap.rows; y++) { + uint8_t * row = &face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch]; + //uint8_t row_out = 0; + for (unsigned int x = 0; x < face->glyph->bitmap.width; x++) { + int bit; + bit = (row[x / 8] >> (7 - (x % 8))) & 1; + //fprintf(stderr, bit ? "█" : " "); + //row_out |= (bit << x); + + // FIXME: this is lazy: this bloats the storage format from + // 1 bit per pixel to 8 bits per pixel, even though this + // expansion could be done at runtime inside vdp1 vram + // instead. + glyph_bitmaps[bitmap_offset + (y * pitch + x)] = bit ? 31 : 0; + } + //fprintf(stderr, "\n"); + //buf[y] = row_out; + } } + break; + case 256: + { + for (uint32_t y = 0; y < face->glyph->bitmap.rows; y++) { + for (uint32_t x = 0; x < face->glyph->bitmap.width; x++) { + uint8_t i = face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch + x]; + glyph_bitmaps[bitmap_offset + (y * pitch + x)] = i >> 3; + } + } + } + break; + default: + assert(-1 == face->glyph->bitmap.num_grays); } //memcpy(&glyph_bitmaps[bitmap_offset], face->glyph->bitmap.buffer, bitmap_size); @@ -130,7 +162,7 @@ int main(int argc, char *argv[]) FT_Face face; FT_Error error; - if (argc < 5) { + if (argc != 6) { std::cerr << "usage: " << argv[0] << " [start-hex] [end-hex] [pixel-size] [font-file-path] [output-file-path]\n\n"; std::cerr << " ex: " << argv[0] << " 3000 30ff 30 ipagp.ttf font.bin\n"; return -1; @@ -178,9 +210,6 @@ int main(int argc, char *argv[]) ss2 << std::hex << argv[2]; ss2 >> end; - //uint32_t start = 0x20; - //uint32_t end = 0x7f; - glyph glyphs[(end - start) + 1]; uint8_t glyph_bitmaps[1024 * 1024]; memset(glyph_bitmaps, 0x00, 1024 * 1024); @@ -218,6 +247,10 @@ int main(int argc, char *argv[]) std::cerr << "glyph_index: 0x" << std::hex << glyph_index << '\n'; FILE * out = fopen(argv[5], "w"); + if (out == NULL) { + perror("fopen(w)"); + return -1; + } fwrite(reinterpret_cast(&font), (sizeof (font)), 1, out); fwrite(reinterpret_cast(&glyphs[0]), (sizeof (glyph)), glyph_index, out); diff --git a/wordle/main_saturn.cpp b/wordle/main_saturn.cpp index 20aed35..8afe441 100644 --- a/wordle/main_saturn.cpp +++ b/wordle/main_saturn.cpp @@ -203,7 +203,8 @@ void render() } void v_blank_in_int(void) __attribute__ ((interrupt_handler)); -void v_blank_in_int() { +void v_blank_in_int() +{ scu.reg.IST &= ~(IST__V_BLANK_IN); scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN); @@ -262,14 +263,14 @@ void main() uint32_t top = (sizeof (union vdp1_vram)); top = init_font(top); - // wait for the beginning of a V blank - v_blank_in(); - // wordle init const uint8_t word_ix = 6; wordle::init_screen(wordle_state, word_ix); // end wordle init + // wait for the beginning of a V blank + 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);