250 lines
6.9 KiB
C++
250 lines
6.9 KiB
C++
#include <cstdint>
|
|
|
|
#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"
|
|
|
|
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<uint32_t const * const>(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);
|
|
|
|
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]
|
|
: 0;
|
|
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;
|
|
}
|