#include #include "vdp2.h" #include "scu.h" #include "smpc.h" #include "sh2.h" #include "common/copy.hpp" #include "common/vdp2_func.hpp" #include "common/intback.hpp" #include "input.hpp" #include "gen/maps.hpp" #include "map_objects.hpp" 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[0] = rgb15( 0, 0, 0); vdp2.cram.u16[1] = rgb15(10, 10, 10); vdp2.cram.u16[2] = rgb15(21, 21, 21); vdp2.cram.u16[3] = rgb15(31, 31, 31); } uint32_t cell_data(const start_size_t& buf, const uint32_t top) { // round to nearest multiple of 32 const uint32_t table_size = ((buf.size * 2) + 0x20 - 1) & (-0x20); const uint32_t base_address = top - table_size; // in bytes uint32_t * vram = &vdp2.vram.u32[(base_address / 4)]; for (uint32_t ix = 0; ix < buf.size / 4; ix += 1) { const uint32_t pixels = reinterpret_cast(buf.start)[ix]; const uint32_t px0 = pixels >> 16 & 0xffff; const uint32_t px1 = pixels >> 0 & 0xffff; #define lshift(n) ((7 - n) * 2) #define rshift(n) ((7 - n) * 4) #define px(p, n) (((p >> lshift(n)) & 0b11) << rshift(n)) #define p0(n) (px(px0, n)) #define p1(n) (px(px1, n)) vram[ix * 2 + 0] = p0(7) | p0(6) | p0(5) | p0(4) | p0(3) | p0(2) | p0(1) | p0(0); vram[ix * 2 + 1] = p1(7) | p1(6) | p1(5) | p1(4) | p1(3) | p1(2) | p1(1) | p1(0); #undef p1 #undef p0 #undef px #undef lshift #undef rshift } return base_address; } constexpr inline void render_block(const uint32_t base_pattern, const tileset_t& tileset, const uint32_t map_x, const uint32_t map_y, const uint8_t block) { for (uint32_t block_y = 0; block_y < 4; block_y++) { for (uint32_t block_x = 0; block_x < 4; block_x++) { const uint32_t block_ix = 4 * block_y + block_x; const uint8_t tile_xy = tileset.blockset.start[block * 4 * 4 + block_ix]; const uint8_t tile_x = (tile_xy >> 0) & 0xf; const uint8_t tile_y = (tile_xy >> 4) & 0xf; const uint32_t tile_ix = tile_y * 16 + tile_x; const uint32_t cell_y = map_y * 4 + block_y; const uint32_t cell_x = map_x * 4 + block_x; vdp2.vram.u16[64 * cell_y + cell_x] = (base_pattern & 0xfff) + tile_ix; //vdp2.vram.u32[64 * cell_y + cell_x] = base_pattern + tile_ix; } } } constexpr int32_t last_map = map_t::wardens_house; struct state_t { int32_t map_ix; enum tileset_t::tileset tileset; uint32_t base_pattern; }; static state_t state = { 0, tileset_t::cavern, 0 }; enum tileset_t::tileset load_tileset(enum tileset_t::tileset tileset) { uint32_t top = (sizeof (union vdp2_vram)); uint32_t base_address = top = cell_data(tilesets[tileset].tileset, top); state.base_pattern = base_address / 32; /* use 1-word (16-bit) pattern names */ /* update N0SCN in the event base_pattern moves (it usually does not) */ vdp2.reg.PNCN0 = PNCN0__N0PNB__1WORD | PNCN0__N0CNSM | PNCN0__N0SCN((state.base_pattern >> 10) & 0x1f); return tileset; } // fixme: remove hack #include "map.hpp" void render() { const map_t& map = maps[maps_ix[state.map_ix]]; if (map.tileset != state.tileset) state.tileset = load_tileset(map.tileset); const uint8_t border_block = map_objects[maps_ix[state.map_ix]].border_block; for (uint32_t map_y = 0; map_y < 16; map_y++) { for (uint32_t map_x = 0; map_x < 16; map_x++) { const uint8_t block = (map_x < map.width && map_y < map.height) ? map.blocks.start[map.width * map_y + map_x] : border_block; render_block(state.base_pattern, tilesets[map.tileset], map_x, map_y, block); } } } void update() { if (event::cursor_right()) { state.map_ix++; if (state.map_ix >= map_ix_last) state.map_ix = 0; } if (event::cursor_left()) { state.map_ix--; if (state.map_ix < 0) state.map_ix = last_map; } } 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); sh2.reg.FRC.H = 0; sh2.reg.FRC.L = 0; sh2.reg.FTCSR = 0; // clear flags render(); update(); // 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); // 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; } 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); } void main() { 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__CRKTE | RAMCTL__CRMD__RGB_5BIT_1024 | RAMCTL__VRAMD | RAMCTL__VRBMD; /* 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; /* 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 */ constexpr int plane_a = 0; constexpr int plane_a_offset = plane_a * 0x2000; constexpr int page_size = 64 * 64 * 2; // N0PNB__1WORD (16-bit) constexpr int plane_size = page_size * 1; 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 palette_data(); vdp2.reg.CYCA0 = 0x0fff'ffff; vdp2.reg.CYCA1 = 0xffff'ffff; vdp2.reg.CYCB0 = 0xffff'ffff; vdp2.reg.CYCB1 = 0x4fff'ffff; // 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); state.map_ix = map_t::celadon_city; }