2024-10-15 19:11:29 +00:00
|
|
|
/* This file is included into vb.c and cannot be compiled on its own. */
|
|
|
|
#ifdef VBAPI
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-16 21:15:39 +00:00
|
|
|
/********************************** Macros ***********************************/
|
|
|
|
|
|
|
|
/* Compute how many clocks are in some number of milliseconds */
|
|
|
|
#define vipClocksMs(x) x * 20000
|
|
|
|
|
2024-10-19 22:01:36 +00:00
|
|
|
/* Read a world line parameter value */
|
|
|
|
#define vipParam(x) busReadBuffer(&sim->vip.ram[x], VB_U16)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/******************************** Lookup Data ********************************/
|
|
|
|
|
|
|
|
/* BG map arrangement by world background dimensions */
|
|
|
|
static const uint8_t BG_TEMPLATES[][64] = {
|
|
|
|
{ 0 }, /* 1x1 */
|
|
|
|
{ 0, 1 }, /* 1x2 */
|
|
|
|
{ 0, 1, 2, 3 }, /* 1x4 */
|
|
|
|
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* 1x8 */
|
|
|
|
{ 0, 1 }, /* 2x1 */
|
|
|
|
{ 0, 1, 2, 3 }, /* 2x2 */
|
|
|
|
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* 2x4 */
|
|
|
|
{ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 }, /* 2x8 */
|
|
|
|
{ 0, 1, 2, 3 }, /* 4x1 */
|
|
|
|
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* 4x2 */
|
|
|
|
{ 0, 1, 0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 6, 7, 6, 7 }, /* 4x4 */
|
|
|
|
{ 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, /* 4x8 */
|
|
|
|
4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7 },
|
|
|
|
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* 8x1 */
|
|
|
|
{ 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7 }, /* 8x2 */
|
|
|
|
{ 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, /* 8x4 */
|
|
|
|
4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7 },
|
|
|
|
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, /* 8x8 */
|
|
|
|
2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
|
|
6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7 }
|
|
|
|
};
|
|
|
|
|
2024-10-22 00:39:35 +00:00
|
|
|
/* 8-bit color magnitude by brightness level */
|
|
|
|
static const uint8_t BRIGHT8[] = {
|
2024-10-23 00:53:27 +00:00
|
|
|
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
|
|
|
|
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
|
|
|
|
64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94,
|
|
|
|
96, 98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,
|
|
|
|
129,131,133,135,137,139,141,143,145,147,149,151,153,155,157,159,
|
|
|
|
161,163,165,167,169,171,173,175,177,179,181,183,185,187,189,191,
|
|
|
|
193,195,197,199,201,203,205,207,209,211,213,215,217,219,221,223,
|
|
|
|
225,227,229,231,233,235,237,239,241,243,245,247,249,251,253,255
|
2024-10-22 00:39:35 +00:00
|
|
|
};
|
|
|
|
|
2024-10-16 21:15:39 +00:00
|
|
|
|
|
|
|
|
2024-10-24 15:59:11 +00:00
|
|
|
/*********************************** Types ***********************************/
|
|
|
|
|
|
|
|
/* World attribtues per eye */
|
|
|
|
typedef struct {
|
|
|
|
int32_t bp; /* BG source parallax */
|
|
|
|
int32_t left, right; /* Window bounds */
|
|
|
|
int32_t wx; /* World horizontal position */
|
|
|
|
} EyeAttribs;
|
|
|
|
|
|
|
|
/* Parsed world attributes */
|
|
|
|
typedef struct {
|
|
|
|
int32_t base; /* Base BG map index */
|
|
|
|
uint8_t *bg; /* Background template */
|
|
|
|
int32_t bgm; /* World type */
|
|
|
|
int32_t bgw, bgh; /* Background dimension masks */
|
|
|
|
int32_t bottom, top; /* Window bounds */
|
|
|
|
int32_t gx, gp, gy; /* World screen position */
|
|
|
|
int32_t mx, mp, my; /* Background scroll */
|
|
|
|
int32_t over; /* Overplane flag */
|
|
|
|
uint32_t params; /* Line parameters address */
|
|
|
|
int32_t scx, scy; /* Background dimensions */
|
|
|
|
int32_t w, h; /* Window size */
|
|
|
|
} WorldAttribs;
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-15 19:11:29 +00:00
|
|
|
/***************************** Module Functions ******************************/
|
|
|
|
|
2024-10-19 22:01:36 +00:00
|
|
|
/* Retrieve a pointer to character data in host memory */
|
|
|
|
static uint8_t* vipCharacter(VB *sim, uint32_t c) {
|
|
|
|
return &sim->vip.ram[0x06000 | (c << 6 & 0x18000) | (c << 4 & 0x01FF0)];
|
|
|
|
}
|
|
|
|
|
2024-10-15 19:11:29 +00:00
|
|
|
/* Read a palette */
|
|
|
|
static int32_t vipReadPalette(uint8_t *entries) {
|
|
|
|
return entries[3] << 6 | entries[2] << 4 | entries[1] << 2;
|
|
|
|
}
|
|
|
|
|
2024-10-16 21:15:39 +00:00
|
|
|
/* Raise an interrupt request */
|
|
|
|
static void vipThrow(VB *sim, uint16_t cause) {
|
|
|
|
sim->vip.intpnd |= cause;
|
2024-10-23 00:53:27 +00:00
|
|
|
if (sim->vip.intenb & cause)
|
|
|
|
sim->cpu.irq |= 0x0010;
|
2024-10-16 21:15:39 +00:00
|
|
|
}
|
|
|
|
|
2024-10-15 19:11:29 +00:00
|
|
|
/* Write a palette */
|
|
|
|
static void vipWritePalette(uint8_t *entries, int32_t mask, int32_t value) {
|
|
|
|
if (mask & 0x00FF)
|
|
|
|
return;
|
|
|
|
entries[3] = value >> 6 & 3;
|
|
|
|
entries[2] = value >> 4 & 3;
|
|
|
|
entries[1] = value >> 2 & 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read a typed value from an I/O register */
|
|
|
|
static int32_t vipReadIO(VB *sim, uint32_t address, int type) {
|
2024-10-19 22:01:36 +00:00
|
|
|
int32_t mask = 0; /* Byte access mask */
|
|
|
|
int32_t value; /* Return value */
|
2024-10-15 19:11:29 +00:00
|
|
|
|
|
|
|
/* Adjustments by type */
|
|
|
|
switch (type) {
|
|
|
|
|
|
|
|
case VB_S32: /* Word */
|
|
|
|
return vipReadIO(sim, address, VB_U16) |
|
|
|
|
(uint32_t) vipReadIO(sim, address + 2, VB_U16) << 16;
|
|
|
|
|
|
|
|
case VB_S8: /* Byte */
|
|
|
|
case VB_U8:
|
|
|
|
mask = 0x00FF << ((address & 1) << 3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VB_S16: /* Halfword */
|
|
|
|
case VB_U16:
|
|
|
|
mask = 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Access by register */
|
|
|
|
switch (address >> 1) {
|
|
|
|
|
|
|
|
case 0x5F800>>1: /* INTPND */
|
|
|
|
value = sim->vip.intpnd;
|
|
|
|
break;
|
|
|
|
case 0x5F802>>1: /* INTENB */
|
|
|
|
value = sim->vip.intenb;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F820>>1: /* DPSTTS */
|
|
|
|
value =
|
2024-10-16 21:15:39 +00:00
|
|
|
(int32_t) sim->vip.dp.lock << 10 |
|
|
|
|
(int32_t) sim->vip.dp.synce << 9 |
|
|
|
|
(int32_t) sim->vip.dp.re << 8 |
|
|
|
|
(int32_t) sim->vip.dp.fclk << 7 |
|
|
|
|
(int32_t) sim->vip.dp.scanrdy << 6 |
|
|
|
|
(int32_t) sim->vip.dp.r1bsy << 5 |
|
|
|
|
(int32_t) sim->vip.dp.l1bsy << 4 |
|
|
|
|
(int32_t) sim->vip.dp.r0bsy << 3 |
|
|
|
|
(int32_t) sim->vip.dp.l0bsy << 2 |
|
|
|
|
(int32_t) sim->vip.dp.disp << 1
|
2024-10-15 19:11:29 +00:00
|
|
|
;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F824>>1: /* BRTA */
|
|
|
|
value = sim->vip.brtRest[0];
|
|
|
|
break;
|
|
|
|
case 0x5F826>>1: /* BRTB */
|
|
|
|
value = sim->vip.brtRest[1];
|
|
|
|
break;
|
|
|
|
case 0x5F828>>1: /* BRTC */
|
|
|
|
value = sim->vip.brtRest[2];
|
|
|
|
break;
|
|
|
|
case 0x5F82A>>1: /* REST */
|
|
|
|
value = sim->vip.brtRest[3];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F82E>>1: /* FRMCYC */
|
|
|
|
value = sim->vip.frmcyc;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F830>>1: /* CTA */
|
|
|
|
value = (int32_t) sim->vip.cta.cta_r << 8 | sim->vip.cta.cta_l;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F840>>1: /* XPSTTS */
|
|
|
|
value =
|
2024-10-17 01:14:38 +00:00
|
|
|
(int32_t) !!sim->vip.xp.sbout << 15 |
|
2024-10-16 21:15:39 +00:00
|
|
|
(int32_t) sim->vip.xp.sbcount << 8 |
|
|
|
|
(int32_t) sim->vip.xp.overtime << 4 |
|
|
|
|
(int32_t) sim->vip.xp.f1bsy << 3 |
|
|
|
|
(int32_t) sim->vip.xp.f0bsy << 2 |
|
|
|
|
(int32_t) sim->vip.xp.xpen << 1
|
2024-10-15 19:11:29 +00:00
|
|
|
;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F844>>1: /* VER */
|
|
|
|
value = 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F848>>1: /* SPT0 */
|
|
|
|
value = sim->vip.spt[0];
|
|
|
|
break;
|
|
|
|
case 0x5F84A>>1: /* SPT1 */
|
|
|
|
value = sim->vip.spt[1];
|
|
|
|
break;
|
|
|
|
case 0x5F84C>>1: /* SPT2 */
|
|
|
|
value = sim->vip.spt[2];
|
|
|
|
break;
|
|
|
|
case 0x5F84E>>1: /* SPT3 */
|
|
|
|
value = sim->vip.spt[3];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F860>>1: /* GPLT0 */
|
|
|
|
value = vipReadPalette(sim->vip.gplt[0]);
|
|
|
|
break;
|
|
|
|
case 0x5F862>>1: /* GPLT1 */
|
|
|
|
value = vipReadPalette(sim->vip.gplt[1]);
|
|
|
|
break;
|
|
|
|
case 0x5F864>>1: /* GPLT2 */
|
|
|
|
value = vipReadPalette(sim->vip.gplt[2]);
|
|
|
|
break;
|
|
|
|
case 0x5F866>>1: /* GPLT3 */
|
|
|
|
value = vipReadPalette(sim->vip.gplt[3]);
|
|
|
|
break;
|
|
|
|
case 0x5F868>>1: /* JPLT0 */
|
|
|
|
value = vipReadPalette(sim->vip.jplt[0]);
|
|
|
|
break;
|
|
|
|
case 0x5F86A>>1: /* JPLT1 */
|
|
|
|
value = vipReadPalette(sim->vip.jplt[1]);
|
|
|
|
break;
|
|
|
|
case 0x5F86C>>1: /* JPLT2 */
|
|
|
|
value = vipReadPalette(sim->vip.jplt[2]);
|
|
|
|
break;
|
|
|
|
case 0x5F86E>>1: /* JPLT3 */
|
|
|
|
value = vipReadPalette(sim->vip.jplt[3]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F870>>1: /* BKCOL */
|
|
|
|
value = sim->vip.bkcol;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Unmapped */
|
|
|
|
default: value = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Select byte bits as necessary */
|
|
|
|
return
|
|
|
|
mask == 0x00FF ? value & 0x00FF :
|
|
|
|
mask == 0xFF00 ? value >> 8 :
|
|
|
|
value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write a typed value to an I/O register */
|
|
|
|
static void vipWriteIO(
|
|
|
|
VB *sim, uint32_t address, int type, int32_t value, int debug) {
|
2024-10-16 21:15:39 +00:00
|
|
|
int32_t mask; /* Byte access mask */
|
2024-10-15 19:11:29 +00:00
|
|
|
|
|
|
|
/* Adjustments by type */
|
|
|
|
switch (type) {
|
|
|
|
|
|
|
|
case VB_S32: /* Word */
|
|
|
|
vipWriteIO(sim, address , VB_U16, value , debug);
|
|
|
|
vipWriteIO(sim, address + 2, VB_U16, value >> 16, debug);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case VB_S8: /* Byte */
|
|
|
|
case VB_U8:
|
|
|
|
|
|
|
|
/* Select which half of the register to access */
|
|
|
|
if (debug) {
|
|
|
|
mask = (address & 1) << 3;
|
|
|
|
value = (value & 0x00FF) << mask;
|
|
|
|
mask = 0xFF00 >> mask;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert to a halfword access */
|
|
|
|
if ((address & 1) != 0)
|
|
|
|
value = (value & 0x00FF) << 8;
|
|
|
|
|
|
|
|
/* Fallthrough */
|
|
|
|
default: /* Halfword */
|
|
|
|
mask = 0x0000;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Access by register */
|
|
|
|
switch (address >> 1) {
|
|
|
|
|
|
|
|
case 0x5F802>>1: /* INTENB */
|
|
|
|
sim->vip.intenb = (sim->vip.intenb & mask) | (value & 0xE01F);
|
|
|
|
break;
|
|
|
|
case 0x5F804>>1: /* INTCLR */
|
|
|
|
sim->vip.intpnd &= ~value;
|
2024-10-19 22:01:36 +00:00
|
|
|
if ((sim->vip.intpnd & sim->vip.intenb) == 0)
|
2024-10-15 19:11:29 +00:00
|
|
|
sim->cpu.irq &= ~0x0010;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F822>>1: /* DPCTRL */
|
|
|
|
if ((mask & 0xFF00) == 0) {
|
2024-10-16 21:15:39 +00:00
|
|
|
sim->vip.dp.lock = value >> 10 & 1;
|
|
|
|
sim->vip.dp.synce = value >> 9 & 1;
|
|
|
|
sim->vip.dp.re = value >> 8 & 1;
|
2024-10-15 19:11:29 +00:00
|
|
|
}
|
|
|
|
if ((mask & 0x00FF) == 0) {
|
2024-10-16 21:15:39 +00:00
|
|
|
sim->vip.dp.disp = value >> 1 & 1;
|
2024-10-19 22:01:36 +00:00
|
|
|
|
|
|
|
/* DPRST */
|
|
|
|
if (value & 1) {
|
|
|
|
sim->vip.intenb &= ~0x801F;
|
|
|
|
sim->vip.intpnd &= ~0x801F;
|
|
|
|
if ((sim->vip.intpnd & sim->vip.intenb) == 0)
|
|
|
|
sim->cpu.irq &= ~0x0010;
|
|
|
|
/* TODO: Research exact operation */
|
|
|
|
}
|
|
|
|
|
2024-10-15 19:11:29 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F824>>1: /* BRTA */
|
|
|
|
sim->vip.brtRest[0] = (sim->vip.brtRest[0] & mask) | value;
|
|
|
|
break;
|
|
|
|
case 0x5F826>>1: /* BRTB */
|
|
|
|
sim->vip.brtRest[1] = (sim->vip.brtRest[1] & mask) | value;
|
|
|
|
break;
|
|
|
|
case 0x5F828>>1: /* BRTC */
|
|
|
|
sim->vip.brtRest[2] = (sim->vip.brtRest[2] & mask) | value;
|
|
|
|
break;
|
|
|
|
case 0x5F82A>>1: /* REST */
|
|
|
|
sim->vip.brtRest[3] = (sim->vip.brtRest[3] & mask) | value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F82E>>1: /* FRMCYC */
|
|
|
|
sim->vip.frmcyc = (sim->vip.frmcyc & mask) | (value & 15);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F830>>1: /* CTA */
|
|
|
|
if ((mask & 0xFF00) == 0)
|
|
|
|
sim->vip.cta.cta_r = value >> 8;
|
|
|
|
if ((mask & 0x00FF) == 0)
|
|
|
|
sim->vip.cta.cta_l = value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F842>>1: /* XPCTRL */
|
|
|
|
if ((mask & 0xFF00) == 0)
|
2024-10-16 21:15:39 +00:00
|
|
|
sim->vip.xp.sbcmp = value >> 8 & 31;
|
2024-10-15 19:11:29 +00:00
|
|
|
if ((mask & 0x00FF) == 0) {
|
2024-10-16 21:15:39 +00:00
|
|
|
sim->vip.xp.xpen = value >> 1 & 1;
|
2024-10-19 22:01:36 +00:00
|
|
|
|
|
|
|
/* XPRST */
|
|
|
|
if (value & 1) {
|
|
|
|
sim->vip.intenb &= ~0xE000;
|
|
|
|
sim->vip.intpnd &= ~0xE000;
|
|
|
|
if ((sim->vip.intpnd & sim->vip.intenb) == 0)
|
|
|
|
sim->cpu.irq &= ~0x0010;
|
|
|
|
/* TODO: Research exact operation */
|
|
|
|
}
|
|
|
|
|
2024-10-15 19:11:29 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F848>>1: /* SPT0 */
|
|
|
|
sim->vip.spt[0] = (sim->vip.spt[0]&mask) | (value&0x03FF&~mask);
|
|
|
|
break;
|
|
|
|
case 0x5F84A>>1: /* SPT1 */
|
|
|
|
sim->vip.spt[1] = (sim->vip.spt[1]&mask) | (value&0x03FF&~mask);
|
|
|
|
break;
|
|
|
|
case 0x5F84C>>1: /* SPT2 */
|
|
|
|
sim->vip.spt[2] = (sim->vip.spt[2]&mask) | (value&0x03FF&~mask);
|
|
|
|
break;
|
|
|
|
case 0x5F84E>>1: /* SPT3 */
|
|
|
|
sim->vip.spt[3] = (sim->vip.spt[3]&mask) | (value&0x03FF&~mask);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F860>>1: /* GPLT0 */
|
|
|
|
vipWritePalette(sim->vip.gplt[0], mask, value);
|
|
|
|
break;
|
|
|
|
case 0x5F862>>1: /* GPLT1 */
|
|
|
|
vipWritePalette(sim->vip.gplt[1], mask, value);
|
|
|
|
break;
|
|
|
|
case 0x5F864>>1: /* GPLT2 */
|
|
|
|
vipWritePalette(sim->vip.gplt[2], mask, value);
|
|
|
|
break;
|
|
|
|
case 0x5F866>>1: /* GPLT3 */
|
|
|
|
vipWritePalette(sim->vip.gplt[3], mask, value);
|
|
|
|
break;
|
|
|
|
case 0x5F868>>1: /* JPLT0 */
|
|
|
|
vipWritePalette(sim->vip.jplt[0], mask, value);
|
|
|
|
break;
|
|
|
|
case 0x5F86A>>1: /* JPLT1 */
|
|
|
|
vipWritePalette(sim->vip.jplt[1], mask, value);
|
|
|
|
break;
|
|
|
|
case 0x5F86C>>1: /* JPLT2 */
|
|
|
|
vipWritePalette(sim->vip.jplt[2], mask, value);
|
|
|
|
break;
|
|
|
|
case 0x5F86E>>1: /* JPLT3 */
|
|
|
|
vipWritePalette(sim->vip.jplt[3], mask, value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5F870>>1: /* BKCOL */
|
|
|
|
sim->vip.bkcol = (sim->vip.bkcol & mask) | (value & 3 * ~mask);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-16 21:15:39 +00:00
|
|
|
/***************************** Callback Handlers *****************************/
|
|
|
|
|
|
|
|
/* Prepare to handle an exception */
|
|
|
|
#ifndef VB_DIRECT_FRAME
|
|
|
|
#define VB_ON_FRAME sim->onFrame
|
|
|
|
#else
|
|
|
|
extern int VB_DIRECT_FRAME(VB *);
|
|
|
|
#define VB_ON_FRAME VB_DIRECT_FRAME
|
|
|
|
#endif
|
|
|
|
static int vipOnFrame(VB *sim) {
|
|
|
|
return sim->onFrame != NULL && VB_ON_FRAME(sim);
|
|
|
|
}
|
|
|
|
#undef VB_ON_FRAME
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-16 21:53:33 +00:00
|
|
|
/***************************** Display Processor *****************************/
|
2024-10-15 19:11:29 +00:00
|
|
|
|
2024-10-17 01:14:38 +00:00
|
|
|
/* Precompute brightness values for image output */
|
|
|
|
static void vipComputeBrightness(VB *sim) {
|
|
|
|
int32_t brt[4]; /* Working output */
|
|
|
|
int32_t repeat; /* Multiplier from column table */
|
|
|
|
int x; /* Iterator */
|
|
|
|
|
|
|
|
/* Compute brightness from hardware state */
|
|
|
|
repeat = (int32_t) sim->vip.ram[sim->vip.dp.cta + 1] + 1;
|
|
|
|
brt[1] = repeat * sim->vip.brtRest[0];
|
|
|
|
brt[2] = repeat * sim->vip.brtRest[1];
|
|
|
|
brt[3] = repeat * sim->vip.brtRest[2] + brt[2] + brt[1];
|
|
|
|
|
2024-10-22 00:39:35 +00:00
|
|
|
/* Transform brightness values to 0..255 */
|
2024-10-17 01:14:38 +00:00
|
|
|
for (x = 1; x < 4; x++)
|
2024-10-22 00:39:35 +00:00
|
|
|
sim->vip.dp.brt[x] = brt[x] > 127 ? 255 : BRIGHT8[brt[x]];
|
2024-10-17 01:14:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Transfer one column of frame buffer pixels to output */
|
|
|
|
static void vipTransferColumn(VB *sim, int32_t eye) {
|
|
|
|
int32_t bits; /* Pixel bits from frame buffer */
|
|
|
|
uint8_t *dest; /* Host frame image output */
|
|
|
|
uint8_t *src; /* Simulation frame buffer input */
|
|
|
|
int y, z; /* Iterators */
|
|
|
|
|
|
|
|
/* Select destination address in host memory */
|
2024-10-19 22:01:36 +00:00
|
|
|
dest = &sim->vip.output
|
2024-10-17 01:14:38 +00:00
|
|
|
[sim->vip.dp.buffer][eye][sim->vip.dp.column];
|
|
|
|
|
|
|
|
/* Output is disabled */
|
2024-10-24 01:35:40 +00:00
|
|
|
if (!sim->vip.dp.enabled) {
|
2024-10-23 00:53:27 +00:00
|
|
|
for (y = 0; y < 224; y += 16)
|
|
|
|
for (z = 0; z < 16; z++, dest += 384)
|
|
|
|
*dest = 0;
|
2024-10-17 01:14:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Select source address in host memory */
|
|
|
|
src = &sim->vip.ram[
|
|
|
|
eye << 16 |
|
|
|
|
sim->vip.dp.buffer << 15 |
|
|
|
|
sim->vip.dp.column << 6
|
|
|
|
];
|
|
|
|
|
|
|
|
/* Transfer all rows as words */
|
|
|
|
for (y = 0; y < 224; y += 16, src += 4) {
|
|
|
|
bits = busReadBuffer(src, VB_S32);
|
|
|
|
for (z = 0; z < 16; bits >>= 2, z++, dest += 384)
|
|
|
|
*dest = sim->vip.dp.brt[bits & 3];
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-10-16 21:53:33 +00:00
|
|
|
/* Process sub-component */
|
2024-10-16 21:15:39 +00:00
|
|
|
static int vipEmulateDisplay(VB *sim, uint32_t clocks) {
|
|
|
|
|
|
|
|
/* Process all clocks */
|
2024-10-16 23:19:31 +00:00
|
|
|
for (;;) {
|
2024-10-16 21:15:39 +00:00
|
|
|
|
|
|
|
/* The next event is after the time remaining */
|
|
|
|
if (sim->vip.dp.clocks > clocks) {
|
|
|
|
sim->vip.dp.clocks -= clocks;
|
2024-10-16 23:19:31 +00:00
|
|
|
sim->vip.dp.until -= clocks;
|
2024-10-16 21:15:39 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Advance forward the component's number of clocks */
|
|
|
|
clocks -= sim->vip.dp.clocks;
|
2024-10-16 23:19:31 +00:00
|
|
|
sim->vip.dp.until -= sim->vip.dp.clocks;
|
2024-10-16 21:15:39 +00:00
|
|
|
sim->vip.dp.clocks = 0;
|
|
|
|
|
|
|
|
/* Processing by operation phase */
|
|
|
|
switch (sim->vip.dp.step) {
|
|
|
|
|
|
|
|
case 0: /* 0ms - FCLK rising edge */
|
2024-10-24 01:35:40 +00:00
|
|
|
sim->vip.dp.clocks = vipClocksMs(3);
|
|
|
|
sim->vip.dp.enabled = sim->vip.dp.disp & sim->vip.dp.synce;
|
|
|
|
sim->vip.dp.fclk = 1;
|
|
|
|
sim->vip.dp.step = 1;
|
|
|
|
sim->vip.dp.until = vipClocksMs(8);
|
2024-10-16 21:15:39 +00:00
|
|
|
vipThrow(sim, 0x0010); /* FRAMESTART */
|
2024-10-17 01:14:38 +00:00
|
|
|
|
|
|
|
/* Game frame */
|
2024-10-19 22:01:36 +00:00
|
|
|
if (sim->vip.xp.xpen) {
|
|
|
|
if (sim->vip.xp.frame >= sim->vip.frmcyc) {
|
|
|
|
sim->vip.xp.frame = 0;
|
|
|
|
|
|
|
|
/* Initiate drawing procedure */
|
|
|
|
if (sim->vip.xp.step == 0) {
|
|
|
|
sim->vip.dp.buffer ^= 1;
|
2024-10-24 01:35:40 +00:00
|
|
|
sim->vip.xp.enabled = 1;
|
2024-10-19 22:01:36 +00:00
|
|
|
sim->vip.xp.overtime = 0;
|
|
|
|
sim->vip.xp.step = 1;
|
|
|
|
vipThrow(sim, 0x0008); /* GAMESTART */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Drawing procedure has run into overtime */
|
|
|
|
else {
|
|
|
|
sim->vip.xp.overtime = 1;
|
|
|
|
vipThrow(sim, 0x8000); /* TIMEERR */
|
|
|
|
}
|
|
|
|
|
|
|
|
} else sim->vip.xp.frame++;
|
|
|
|
}
|
2024-10-17 01:14:38 +00:00
|
|
|
|
2024-10-16 21:15:39 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* 0ms-3ms - Idle */
|
|
|
|
|
|
|
|
case 1: /* 3ms - L*BSY rising edge */
|
2024-10-24 01:35:40 +00:00
|
|
|
if (sim->vip.dp.enabled) {
|
|
|
|
if (sim->vip.dp.buffer == 0)
|
|
|
|
sim->vip.dp.l0bsy = 1;
|
|
|
|
else sim->vip.dp.l1bsy = 1;
|
|
|
|
}
|
2024-10-16 21:15:39 +00:00
|
|
|
sim->vip.dp.column = 0;
|
|
|
|
sim->vip.dp.cta = 0x3DC00 | (uint32_t)sim->vip.cta.cta_l<<1;
|
|
|
|
sim->vip.dp.step = 2;
|
|
|
|
vipComputeBrightness(sim);
|
|
|
|
|
|
|
|
/* Fallthrough */
|
|
|
|
case 2: /* 3ms-8ms - Display left frame buffer */
|
|
|
|
if ((sim->vip.dp.column & 3) == 0)
|
|
|
|
vipComputeBrightness(sim);
|
|
|
|
vipTransferColumn(sim, 0);
|
|
|
|
sim->vip.dp.clocks =
|
|
|
|
260 + (0xA94 >> sim->vip.dp.column % 12 & 1);
|
|
|
|
sim->vip.dp.column++;
|
|
|
|
if ((sim->vip.dp.column & 3) == 0)
|
|
|
|
sim->vip.dp.cta -= 2;
|
|
|
|
if (sim->vip.dp.column == 384)
|
|
|
|
sim->vip.dp.step = 3;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* 8ms - L*BSY falling edge */
|
2024-10-24 01:35:40 +00:00
|
|
|
if (sim->vip.dp.enabled) {
|
|
|
|
if (sim->vip.dp.buffer == 0)
|
|
|
|
sim->vip.dp.l0bsy = 0;
|
|
|
|
else sim->vip.dp.l1bsy = 0;
|
|
|
|
vipThrow(sim, 0x0002); /* LFBEND */
|
|
|
|
}
|
2024-10-16 21:15:39 +00:00
|
|
|
sim->vip.dp.clocks = vipClocksMs(2);
|
|
|
|
sim->vip.dp.step = 4;
|
2024-10-16 23:19:31 +00:00
|
|
|
sim->vip.dp.until = vipClocksMs(10);
|
2024-10-16 21:15:39 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* 8ms-10ms - Idle */
|
|
|
|
|
|
|
|
case 4: /* 10ms - FCLK falling edge */
|
|
|
|
sim->vip.dp.clocks = vipClocksMs(3);
|
|
|
|
sim->vip.dp.fclk = 0;
|
|
|
|
sim->vip.dp.step = 5;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* 10ms-13ms - Idle */
|
|
|
|
|
|
|
|
case 5: /* 13ms - R*BSY rising edge */
|
2024-10-24 01:35:40 +00:00
|
|
|
if (sim->vip.dp.enabled) {
|
|
|
|
if (sim->vip.dp.buffer == 0)
|
|
|
|
sim->vip.dp.r0bsy = 1;
|
|
|
|
else sim->vip.dp.r1bsy = 1;
|
|
|
|
}
|
2024-10-16 21:15:39 +00:00
|
|
|
sim->vip.dp.column = 0;
|
|
|
|
sim->vip.dp.cta = 0x3DE00 | (uint32_t)sim->vip.cta.cta_r<<1;
|
|
|
|
sim->vip.dp.step = 6;
|
|
|
|
|
|
|
|
/* Fallthrough */
|
|
|
|
case 6: /* 13ms-18ms - Display right frame buffer */
|
2024-10-16 23:19:31 +00:00
|
|
|
if ((sim->vip.dp.column & 3) == 0)
|
|
|
|
vipComputeBrightness(sim);
|
2024-10-16 21:15:39 +00:00
|
|
|
vipTransferColumn(sim, 1);
|
|
|
|
sim->vip.dp.clocks =
|
|
|
|
260 + (0xA94 >> sim->vip.dp.column % 12 & 1);
|
|
|
|
sim->vip.dp.column++;
|
2024-10-16 23:19:31 +00:00
|
|
|
if ((sim->vip.dp.column & 3) == 0)
|
|
|
|
sim->vip.dp.cta -= 2;
|
2024-10-16 21:15:39 +00:00
|
|
|
if (sim->vip.dp.column == 384)
|
|
|
|
sim->vip.dp.step = 7;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7: /* 18ms - R*BSY falling edge */
|
2024-10-24 01:35:40 +00:00
|
|
|
if (sim->vip.dp.enabled) {
|
|
|
|
if (sim->vip.dp.buffer == 0)
|
|
|
|
sim->vip.dp.r0bsy = 0;
|
|
|
|
else sim->vip.dp.r1bsy = 0;
|
|
|
|
vipThrow(sim, 0x0004); /* RFBEND */
|
|
|
|
}
|
|
|
|
sim->vip.dp.clocks = vipClocksMs(2);
|
|
|
|
sim->vip.dp.enabled = 0;
|
|
|
|
sim->vip.dp.step = 0;
|
|
|
|
sim->vip.dp.until = vipClocksMs(2);
|
2024-10-16 21:15:39 +00:00
|
|
|
if (vipOnFrame(sim))
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* 18ms-20ms - Idle */
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-10-16 21:53:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
/****************************** Pixel Processor ******************************/
|
|
|
|
|
2024-10-24 15:59:11 +00:00
|
|
|
/* Parse eye attributes and test window bounds */
|
|
|
|
static int vipParseEye(WorldAttribs *wttr, EyeAttribs *ettr, int eye) {
|
|
|
|
|
|
|
|
/* Validate the world isn't right of frame */
|
|
|
|
ettr->wx = wttr->gx - (eye == 0 ? wttr->gp : -wttr->gp);
|
|
|
|
if (ettr->wx > 383)
|
|
|
|
return 1;
|
|
|
|
ettr->left = ettr->wx < 0 ? 0 : ettr->wx;
|
|
|
|
|
|
|
|
/* Validate the world isn't left of frame */
|
|
|
|
ettr->right = ettr->wx + wttr->w - 1;
|
|
|
|
if (ettr->right < 0)
|
|
|
|
return 1;
|
|
|
|
if (ettr->right > 383)
|
|
|
|
ettr->right = 383;
|
|
|
|
|
|
|
|
/* BG source parallax */
|
|
|
|
if (wttr->bgm != 2)
|
|
|
|
ettr->bp = eye == 0 ? -wttr->mp : wttr->mp;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse world attributes and test window bounds */
|
|
|
|
static int vipParseWorld(VB*sim,uint8_t*world,uint16_t bits,WorldAttribs*attr){
|
|
|
|
int32_t z; /* Scratch */
|
|
|
|
|
|
|
|
/* Validate the world isn't below the frame */
|
|
|
|
attr->gy = busReadBuffer(world + 6, VB_S16);
|
|
|
|
if (attr->gy > 223)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Validate the world isn't above the frame, and height is positive */
|
|
|
|
attr->bgm = bits >> 12 & 3;
|
|
|
|
attr->h = busReadBuffer(world + 16, VB_S16) + 1;
|
|
|
|
z = attr->bgm == 2 ? 0 : 8;
|
|
|
|
if (attr->h < z)
|
|
|
|
attr->h = z;
|
|
|
|
if (attr->h == 0)
|
|
|
|
return 1;
|
|
|
|
attr->bottom = attr->gy + attr->h - 1;
|
|
|
|
attr->top = (int32_t) sim->vip.xp.sbcount << 3;
|
|
|
|
if (attr->bottom < attr->top)
|
|
|
|
return 1;
|
|
|
|
if (attr->bottom > 223)
|
|
|
|
attr->bottom = 223;
|
|
|
|
if (attr->top < attr->gy)
|
|
|
|
attr->top = attr->gy;
|
|
|
|
|
|
|
|
/* Validate width is positive */
|
|
|
|
attr->w = SignExtend(busReadBuffer(world + 14, VB_U16),
|
|
|
|
attr->bgm == 2 ? 10 : 13) + 1;
|
|
|
|
if (attr->w < 1)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Parse attributes */
|
|
|
|
attr->bg = (uint8_t *) BG_TEMPLATES[bits >> 8 & 15];
|
|
|
|
attr->gx = SignExtend(busReadBuffer(world + 2, VB_U16), 10);
|
|
|
|
attr->gp = SignExtend(busReadBuffer(world + 4, VB_U16), 10);
|
|
|
|
attr->over = bits >> 7 & 1;
|
|
|
|
attr->scx = bits >> 10 & 3;
|
|
|
|
attr->scy = bits >> 8 & 3;
|
|
|
|
attr->bgw = (1 << attr->scx) - 1;
|
|
|
|
attr->bgh = (1 << attr->scy) - 1;
|
|
|
|
z = attr->scx + attr->scy;
|
|
|
|
attr->base = bits & 15 & ~((1 << (z < 3 ? z : 3)) - 1);
|
|
|
|
if (attr->bgm != 2) {
|
|
|
|
attr->mx = SignExtend(busReadBuffer(world + 8, VB_U16), 13);
|
|
|
|
attr->mp = SignExtend(busReadBuffer(world + 10, VB_U16), 15);
|
|
|
|
attr->my = SignExtend(busReadBuffer(world + 12, VB_U16), 13);
|
|
|
|
}
|
|
|
|
if (attr->bgm != 0) {
|
|
|
|
attr->params =
|
|
|
|
0x20000 +
|
|
|
|
(attr->top - attr->gy) * (attr->bgm == 1 ? 4 : 16) +
|
|
|
|
((uint32_t) busReadBuffer(world + 18, VB_U16) << 1)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-10-19 22:01:36 +00:00
|
|
|
/* Draw an object group into shadow memory */
|
|
|
|
static void vipDrawObjects(VB *sim, int group) {
|
|
|
|
uint8_t *dest; /* Pointer to object position in shadow memory */
|
|
|
|
int fx; /* Output horizontal position */
|
|
|
|
int fy; /* Output vertical position */
|
|
|
|
uint8_t *obj; /* Pointer to object attributes in host memory */
|
|
|
|
int32_t ox; /* Eye horizontal position */
|
|
|
|
int pixel; /* Character input pixel */
|
|
|
|
int start; /* Index of first object in group */
|
|
|
|
int stop; /* Index of last object in group */
|
|
|
|
int sx; /* Input horizontal position */
|
|
|
|
int sy; /* Input vertical position */
|
|
|
|
int32_t top; /* Window boundary */
|
|
|
|
int i, o, x, y; /* Iterators */
|
|
|
|
|
|
|
|
/* Object attributes */
|
|
|
|
uint32_t attr1; /* Attribute bits */
|
|
|
|
uint32_t attr2; /* Attribute bits */
|
|
|
|
int jhflp; /* Is flipped horizontally */
|
|
|
|
int32_t jp; /* Stereo parallax */
|
|
|
|
int jvflp; /* Is flipped vetically */
|
|
|
|
int32_t jx; /* Base horizontal position */
|
|
|
|
int32_t jy; /* Vertical position */
|
|
|
|
uint8_t *plt; /* Palette */
|
|
|
|
uint8_t *src; /* Pointer to character source data in host memory */
|
|
|
|
|
|
|
|
/* Process all objects in the group */
|
|
|
|
start = group == 0 ? 0 : (sim->vip.spt[group - 1] + 1) & 1023;
|
|
|
|
stop = sim->vip.spt[group];
|
|
|
|
top = (int32_t) sim->vip.xp.sbcount << 3;
|
2024-10-20 03:56:23 +00:00
|
|
|
for (o = stop; o != -1; o = o == start ? -1 : (o - 1) & 1023) {
|
2024-10-19 22:01:36 +00:00
|
|
|
obj = &sim->vip.ram[0x3E000 | o << 3];
|
|
|
|
|
|
|
|
/* Validate object is enabled */
|
|
|
|
attr1 = busReadBuffer(obj + 2, VB_U16);
|
|
|
|
if ((attr1 & 0xC000) == 0) /* JLON, JRON */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Validate Y position */
|
|
|
|
jy = (int8_t) obj[4];
|
|
|
|
if (jy < 7)
|
|
|
|
jy &= 0xFF;
|
|
|
|
if (jy < top - 7 || jy > 223)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Parse attributes */
|
|
|
|
jx = SignExtend(busReadBuffer(obj, VB_U16), 10);
|
|
|
|
jp = SignExtend(attr1, 10);
|
|
|
|
attr2 = busReadBuffer(obj + 6, VB_U16);
|
|
|
|
jhflp = attr2 >> 13 & 1;
|
|
|
|
jvflp = attr2 >> 12 & 1;
|
|
|
|
|
|
|
|
/* Locate palette and character state in host memory */
|
|
|
|
plt = sim->vip.jplt[attr2 >> 14];
|
|
|
|
src = vipCharacter(sim, attr2);
|
|
|
|
|
|
|
|
/* Draw the character */
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
if ((attr1 >> (15 - i) & 1) == 0) /* J*ON */
|
|
|
|
continue;
|
|
|
|
ox = jx - (i == 0 ? jp : -jp);
|
|
|
|
dest = &sim->vip.shadow[i][ox * 224 + jy];
|
|
|
|
|
|
|
|
/* Draw all columns */
|
|
|
|
for (x = 0; x < 8; x++, dest += 224) {
|
|
|
|
fx = ox + x;
|
|
|
|
if (fx < 0 || fx > 383)
|
|
|
|
continue;
|
|
|
|
sx = jhflp ? 7 - x : x;
|
|
|
|
|
|
|
|
/* Draw all rows */
|
|
|
|
for (y = 0; y < 8; y++) {
|
|
|
|
fy = jy + y;
|
|
|
|
if (fy < top || fy > 223)
|
|
|
|
continue;
|
|
|
|
sy = jvflp ? 7 - y : y;
|
|
|
|
pixel = src[sy << 1 | sx >> 2] >> ((sx & 3) << 1) & 3;
|
|
|
|
if (pixel != 0)
|
|
|
|
dest[y] = plt[pixel]; /* TODO: Research clocks */
|
2024-10-20 03:56:23 +00:00
|
|
|
} /* y */
|
2024-10-19 22:01:36 +00:00
|
|
|
|
2024-10-20 03:56:23 +00:00
|
|
|
} /* x */
|
2024-10-19 22:01:36 +00:00
|
|
|
|
|
|
|
} /* i */
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Draw a background world into shadow memory */
|
2024-10-24 15:59:11 +00:00
|
|
|
static void vipDrawWorld(VB *sim, uint8_t *world, uint16_t bits) {
|
|
|
|
uint16_t bgAttr; /* BG map cell attributes */
|
|
|
|
int32_t bx; /* BG source X */
|
|
|
|
int32_t by; /* BG source Y */
|
|
|
|
uint8_t *cell; /* Pointer to BG map cell in host memory */
|
|
|
|
uint8_t *chr; /* Pointer to character data in host memory */
|
|
|
|
int32_t cx; /* BG cell/character source X */
|
|
|
|
int32_t cy; /* BG cell/character source Y */
|
|
|
|
uint8_t *dest; /* Pointer to output in shadow memory */
|
|
|
|
EyeAttribs ettr; /* Eye attributes */
|
|
|
|
uint32_t param; /* Current line parameter address */
|
|
|
|
int8_t pixel; /* Character pixel value */
|
|
|
|
int32_t i, x, y; /* Iterators */
|
2024-10-19 22:01:36 +00:00
|
|
|
|
|
|
|
/* World attributes */
|
2024-10-24 15:59:11 +00:00
|
|
|
WorldAttribs wttr; /* Common attributes */
|
|
|
|
int32_t dx; /* Affine per-pixel X delta */
|
|
|
|
int32_t dy; /* Affine per-pixel Y delta */
|
|
|
|
int32_t mp; /* BG source parallax */
|
|
|
|
int32_t mx; /* BG source X */
|
|
|
|
int32_t my; /* BG source Y */
|
|
|
|
int16_t hofst; /* H-bias shift */
|
2024-10-19 22:01:36 +00:00
|
|
|
|
2024-10-24 15:59:11 +00:00
|
|
|
/* Working variables */
|
|
|
|
ettr.bp = wttr.mp = wttr.mx = wttr.my = wttr.params = 0;
|
2024-10-19 22:01:36 +00:00
|
|
|
|
|
|
|
/* Parse attributes */
|
2024-10-24 15:59:11 +00:00
|
|
|
if (vipParseWorld(sim, world, bits, &wttr))
|
|
|
|
return; /* Window not in frame */
|
2024-10-19 22:01:36 +00:00
|
|
|
|
|
|
|
/* Draw the world */
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
|
2024-10-24 15:59:11 +00:00
|
|
|
/* World is not visible */
|
|
|
|
if ((bits & (int32_t) 0x8000 >> i) == 0) /* LON, RON */
|
2024-10-19 22:01:36 +00:00
|
|
|
continue;
|
|
|
|
|
2024-10-24 15:59:11 +00:00
|
|
|
/* Process attributes */
|
|
|
|
if (vipParseEye(&wttr, &ettr, i))
|
|
|
|
continue;
|
2024-10-19 22:01:36 +00:00
|
|
|
|
|
|
|
/* Draw all rows */
|
2024-10-24 15:59:11 +00:00
|
|
|
for (y = wttr.top, param = wttr.params; y <= wttr.bottom; y++) {
|
2024-10-19 22:01:36 +00:00
|
|
|
|
|
|
|
/* Parse line parameters */
|
2024-10-24 15:59:11 +00:00
|
|
|
hofst = 0;
|
|
|
|
mp = wttr.mp;
|
|
|
|
mx = wttr.mx;
|
|
|
|
my = wttr.my;
|
|
|
|
switch (wttr.bgm) {
|
2024-10-19 22:01:36 +00:00
|
|
|
case 1: /* H-bias */
|
|
|
|
hofst = SignExtend(vipParam(param | i << 1), 13);
|
|
|
|
param += 4;
|
|
|
|
break;
|
|
|
|
case 2: /* Affine */
|
2024-10-20 03:56:23 +00:00
|
|
|
dx = (int32_t) (int16_t) vipParam(param|6) << 7;
|
|
|
|
dy = (int32_t) (int16_t) vipParam(param|8) << 7;
|
|
|
|
mp = (int32_t) (int16_t) vipParam(param|2);
|
2024-10-24 15:59:11 +00:00
|
|
|
mp = ettr.left - ettr.wx - ((mp < 0) ^ i ? mp : 0);
|
2024-10-20 03:56:23 +00:00
|
|
|
mx = ((int32_t) (int16_t) vipParam(param ) << 13) + dx*mp;
|
|
|
|
my = ((int32_t) (int16_t) vipParam(param|4) << 13) + dy*mp;
|
2024-10-19 22:01:36 +00:00
|
|
|
param += 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Select output in shadow memory */
|
2024-10-24 15:59:11 +00:00
|
|
|
dest = &sim->vip.shadow[i][ettr.left * 224 + y];
|
2024-10-19 22:01:36 +00:00
|
|
|
|
|
|
|
/* Draw all columns */
|
2024-10-24 15:59:11 +00:00
|
|
|
for (x = ettr.left; x <= ettr.right; x++, dest += 224) {
|
2024-10-19 22:01:36 +00:00
|
|
|
|
|
|
|
/* Locate the pixel in the background */
|
2024-10-24 15:59:11 +00:00
|
|
|
if (wttr.bgm != 2) { /* Normal, H-bias */
|
|
|
|
cx = x - ettr.wx + mx + ettr.bp + hofst;
|
|
|
|
cy = y - wttr.gy + my;
|
2024-10-19 22:01:36 +00:00
|
|
|
} else { /* Affine */
|
|
|
|
cx = (int16_t) (mx >> 16);
|
|
|
|
cy = (int16_t) (my >> 16);
|
|
|
|
mx += dx;
|
|
|
|
my += dy;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Locate the BG map in the background */
|
|
|
|
bx = SignExtend(cx >> 9, 23);
|
|
|
|
by = SignExtend(cy >> 9, 23);
|
|
|
|
cell = NULL;
|
|
|
|
|
|
|
|
/* BG map is out of bounds */
|
2024-10-24 15:59:11 +00:00
|
|
|
if (
|
|
|
|
bx < 0 || bx > wttr.bgw ||
|
|
|
|
by < 0 || by > wttr.bgh
|
|
|
|
) {
|
|
|
|
if (wttr.over)
|
2024-10-23 00:53:27 +00:00
|
|
|
continue; /* TODO: Research overplane */
|
2024-10-24 15:59:11 +00:00
|
|
|
bx &= wttr.bgw;
|
|
|
|
by &= wttr.bgh;
|
2024-10-19 22:01:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Locate the cell in the BG map */
|
|
|
|
if (cell == NULL) {
|
|
|
|
cell = &sim->vip.ram[
|
2024-10-24 15:59:11 +00:00
|
|
|
0x20000 | ((
|
|
|
|
wttr.base +
|
|
|
|
wttr.bg[by << wttr.scx | bx]
|
|
|
|
) << 13) |
|
|
|
|
(cy << 4 & 0x1F80) |
|
2024-10-19 22:01:36 +00:00
|
|
|
(cx >> 2 & 0x007E)
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extract the pixel from the character */
|
|
|
|
bgAttr = busReadBuffer(cell, VB_U16);
|
|
|
|
chr = vipCharacter(sim, bgAttr);
|
|
|
|
cx = (bgAttr & 0x2000 ? 7 - cx : cx) & 7;
|
|
|
|
cy = (bgAttr & 0x1000 ? 7 - cy : cy) & 7;
|
|
|
|
pixel = chr[cy << 1 | cx >> 2] >> ((cx & 3) << 1) & 3;
|
|
|
|
|
|
|
|
/* Write the pixel into shadow memory */
|
|
|
|
if (pixel != 0)
|
|
|
|
*dest = sim->vip.gplt[bgAttr >> 14 & 3][pixel];
|
|
|
|
|
|
|
|
} /* x */
|
|
|
|
|
|
|
|
} /* y */
|
|
|
|
|
|
|
|
} /* i */
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Draw the current graphics configuration into shadow memory */
|
|
|
|
static void vipRender(VB *sim) {
|
|
|
|
uint16_t attr; /* World attribute bits */
|
|
|
|
int group; /* Next object group index */
|
|
|
|
uint16_t *timing; /* Column timing in host memory */
|
|
|
|
uint8_t *world; /* World attribute source in host memory */
|
|
|
|
int32_t x, y; /* Iterators */
|
|
|
|
|
|
|
|
/* Erase all pixels */
|
|
|
|
for (x = 0; x < 384*224; x += 224)
|
|
|
|
for (y = (int32_t) sim->vip.xp.sbcount << 3; y < 224; y++)
|
|
|
|
sim->vip.shadow[0][x + y] = sim->vip.shadow[1][x + y] = sim->vip.bkcol;
|
|
|
|
|
|
|
|
/* Process all worlds */
|
|
|
|
group = 3;
|
|
|
|
world = &sim->vip.ram[0x3DBE0]; /* World 31 */
|
|
|
|
for (x = 31; x >= 0; x--, world -= 32) {
|
|
|
|
attr = busReadBuffer(world, VB_U16);
|
|
|
|
|
|
|
|
/* Non-graphical world */
|
2024-10-20 03:56:23 +00:00
|
|
|
if (attr & 0x0040) /* END */
|
2024-10-19 22:01:36 +00:00
|
|
|
break; /* Control world */
|
|
|
|
if ((attr & 0xC000) == 0) /* LON, RON */
|
|
|
|
continue; /* Dummy world */
|
|
|
|
|
|
|
|
/* Object world */
|
|
|
|
if ((attr >> 12 & 3) == 3) {
|
|
|
|
vipDrawObjects(sim, group);
|
|
|
|
group = (group - 1) & 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Background world */
|
|
|
|
else if (attr & 0xC000) /* LON, RON */
|
|
|
|
vipDrawWorld(sim, world, attr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Supply filler timings for each group of 8 rows of pixels
|
|
|
|
Using 2.8ms experimental measurement for empty frame
|
|
|
|
= 125 clocks every 24 halfwords
|
|
|
|
*/
|
|
|
|
timing = &sim->vip.halfwords[(uint32_t) sim->vip.xp.sbcount * 384];
|
|
|
|
x = sim->vip.xp.column;
|
|
|
|
for (y = sim->vip.xp.sbcount; y < 28; y++, x = 0)
|
|
|
|
for (; x < 384; x++, timing++)
|
|
|
|
*timing = 5 + ((uint32_t) 0x210884 >> x % 24 & 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Transfer one halfword from shadow pixel memory to the frame buffer */
|
|
|
|
static void vipTransferHalfword(VB *sim) {
|
|
|
|
uint32_t bits0; /* Upper 4 pixels from shadow memory */
|
|
|
|
uint32_t bits1; /* Lower 4 pixels from shadow memory */
|
|
|
|
uint8_t *dest; /* Pointer to frame buffer data in host memory */
|
|
|
|
uint32_t offset; /* Position of halfword in shadow memory */
|
|
|
|
uint8_t *src; /* Pointer to pixel data in host memory */
|
|
|
|
int i; /* Iterator */
|
|
|
|
|
|
|
|
/* Determine the output address in frame buffer memory */
|
|
|
|
dest = &sim->vip.ram[
|
|
|
|
(uint32_t) (sim->vip.dp.buffer ^ 1) << 15 |
|
|
|
|
(uint32_t) sim->vip.xp.column << 6 |
|
|
|
|
(uint32_t) sim->vip.xp.sbcount << 1
|
|
|
|
];
|
|
|
|
|
|
|
|
/* Transfer halfwords to each eye's frame buffer */
|
|
|
|
offset = (uint32_t) sim->vip.xp.column * 224 +
|
|
|
|
((uint32_t) sim->vip.xp.sbcount << 3);
|
|
|
|
for (i = 0; i < 2; i++, dest += 0x10000) {
|
|
|
|
src = &sim->vip.shadow[i][offset];
|
|
|
|
bits0 = busReadBuffer(src , VB_S32);
|
|
|
|
bits1 = busReadBuffer(src + 4, VB_S32);
|
|
|
|
busWriteBuffer(dest, VB_U16,
|
|
|
|
(bits0 & 0x0003) |
|
|
|
|
(bits0 >> 6 & 0x000C) |
|
|
|
|
(bits0 >> 12 & 0x0030) |
|
|
|
|
(bits0 >> 18 & 0x00C0) |
|
|
|
|
(bits1 << 8 & 0x0300) |
|
|
|
|
(bits1 << 2 & 0x0C00) |
|
|
|
|
(bits1 >> 4 & 0x3000) |
|
|
|
|
(bits1 >> 10 & 0xC000)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-10-17 01:14:38 +00:00
|
|
|
}
|
|
|
|
|
2024-10-16 21:53:33 +00:00
|
|
|
/* Process sub-component */
|
|
|
|
static void vipEmulateDrawing(VB *sim, uint32_t clocks) {
|
2024-10-17 01:14:38 +00:00
|
|
|
|
|
|
|
/* Process all clocks */
|
|
|
|
for (;;) {
|
|
|
|
|
|
|
|
/* The next event is after the time remaining */
|
|
|
|
if (sim->vip.xp.clocks > clocks) {
|
|
|
|
sim->vip.xp.clocks -= clocks;
|
|
|
|
sim->vip.xp.until -= clocks;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Advance forward the component's number of clocks */
|
|
|
|
clocks -= sim->vip.xp.clocks;
|
|
|
|
sim->vip.xp.until -= sim->vip.xp.clocks;
|
|
|
|
sim->vip.xp.clocks = 0;
|
|
|
|
|
|
|
|
/* Processing by operation phase */
|
|
|
|
switch (sim->vip.xp.step) {
|
|
|
|
|
2024-10-19 22:01:36 +00:00
|
|
|
case 1: /* Initialization */
|
|
|
|
sim->vip.xp.halfword = 0;
|
|
|
|
sim->vip.xp.sbcount = 0;
|
|
|
|
if (sim->vip.dp.buffer == 0)
|
|
|
|
sim->vip.xp.f1bsy = 1;
|
|
|
|
else sim->vip.xp.f0bsy = 1;
|
|
|
|
vipRender(sim);
|
2024-10-17 01:14:38 +00:00
|
|
|
|
|
|
|
/* Fallthrough */
|
2024-10-19 22:01:36 +00:00
|
|
|
case 2: /* Begin drawing halfwords */
|
|
|
|
sim->vip.xp.column = 0;
|
|
|
|
sim->vip.xp.sbout = 2240; /* 112us */
|
|
|
|
sim->vip.xp.step = 3;
|
|
|
|
sim->vip.xp.until = 2000; /* 100us */
|
2024-10-17 01:14:38 +00:00
|
|
|
if (sim->vip.xp.sbcount == sim->vip.xp.sbcmp)
|
|
|
|
vipThrow(sim, 0x2000); /* SBHIT */
|
|
|
|
|
2024-10-19 22:01:36 +00:00
|
|
|
/* Fallthrough */
|
|
|
|
case 3: /* Draw one halfword */
|
|
|
|
vipTransferHalfword(sim);
|
|
|
|
sim->vip.xp.clocks = sim->vip.halfwords[sim->vip.xp.halfword];
|
|
|
|
sim->vip.xp.column ++;
|
|
|
|
sim->vip.xp.halfword++;
|
|
|
|
if (sim->vip.xp.column < 384)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Transition to a new line of halfwords */
|
|
|
|
sim->vip.xp.sbcount++;
|
|
|
|
sim->vip.xp.step = sim->vip.xp.sbcount == 28 ? 4 : 2;
|
2024-10-17 01:14:38 +00:00
|
|
|
break;
|
|
|
|
|
2024-10-19 22:01:36 +00:00
|
|
|
case 4: /* Drawing complete */
|
2024-10-24 01:35:40 +00:00
|
|
|
sim->vip.xp.enabled = 0;
|
|
|
|
sim->vip.xp.step = 0;
|
2024-10-19 22:01:36 +00:00
|
|
|
if (sim->vip.dp.buffer == 0)
|
|
|
|
sim->vip.xp.f1bsy = 0;
|
|
|
|
else sim->vip.xp.f0bsy = 0;
|
2024-10-17 01:14:38 +00:00
|
|
|
vipThrow(sim, 0x4000); /* XPEND */
|
2024-10-19 22:01:36 +00:00
|
|
|
return;
|
2024-10-17 01:14:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-10-19 22:01:36 +00:00
|
|
|
return; /* Unreachable */
|
2024-10-16 21:53:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************** Library Functions *****************************/
|
|
|
|
|
2024-10-15 19:11:29 +00:00
|
|
|
/* Process component */
|
|
|
|
static int vipEmulate(VB *sim, uint32_t clocks) {
|
2024-10-17 01:14:38 +00:00
|
|
|
|
|
|
|
/* Process sub-components */
|
2024-10-16 21:15:39 +00:00
|
|
|
int brk = vipEmulateDisplay(sim, clocks);
|
2024-10-19 22:01:36 +00:00
|
|
|
if (sim->vip.xp.step != 0)
|
|
|
|
vipEmulateDrawing(sim, clocks);
|
2024-10-17 01:14:38 +00:00
|
|
|
|
|
|
|
/* Process SBOUT */
|
|
|
|
if (sim->vip.xp.sbout != 0) {
|
|
|
|
if (sim->vip.xp.sbout <= clocks) {
|
2024-10-19 22:01:36 +00:00
|
|
|
sim->vip.xp.sbout = 0;
|
2024-10-17 01:14:38 +00:00
|
|
|
} else sim->vip.xp.sbout -= clocks;
|
|
|
|
}
|
|
|
|
|
2024-10-16 21:15:39 +00:00
|
|
|
return brk;
|
2024-10-15 19:11:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Read a typed value from the VIP bus */
|
|
|
|
static void vipRead(VB *sim, uint32_t address, int type, int32_t *value) {
|
|
|
|
|
|
|
|
/* Working variables */
|
|
|
|
address &= 0x0007FFFF;
|
|
|
|
|
2024-10-16 21:15:39 +00:00
|
|
|
/* RAM */
|
|
|
|
if (address < 0x40000)
|
|
|
|
*value = busReadBuffer(&sim->vip.ram[address], type);
|
2024-10-15 19:11:29 +00:00
|
|
|
|
|
|
|
/* Unmapped */
|
|
|
|
else if (address < 0x5E000)
|
|
|
|
*value = 0;
|
|
|
|
|
|
|
|
/* I/O register */
|
|
|
|
else if (address < 0x60000)
|
|
|
|
*value = vipReadIO(sim, address, type);
|
|
|
|
|
|
|
|
/* Unmapped */
|
2024-10-19 22:01:36 +00:00
|
|
|
else if (address < 0x78000)
|
2024-10-15 19:11:29 +00:00
|
|
|
*value = 0;
|
|
|
|
|
|
|
|
/* Mirrors of character memory */
|
|
|
|
else {
|
|
|
|
address = 0x06000 | (address << 2 & 0x18000) | (address & 0x01FFF);
|
2024-10-16 21:15:39 +00:00
|
|
|
*value = busReadBuffer(&sim->vip.ram[address], type);
|
2024-10-15 19:11:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Simulate a hardware reset */
|
|
|
|
static void vipReset(VB *sim) {
|
|
|
|
int x, y; /* Iterators */
|
|
|
|
|
|
|
|
/* Normal */
|
2024-10-17 01:14:38 +00:00
|
|
|
sim->vip.intenb = 0x0000;
|
2024-10-16 21:15:39 +00:00
|
|
|
sim->vip.dp.disp = 0;
|
|
|
|
sim->vip.dp.re = 0;
|
|
|
|
sim->vip.dp.synce = 0;
|
|
|
|
sim->vip.xp.xpen = 0;
|
2024-10-15 19:11:29 +00:00
|
|
|
|
|
|
|
/* Extra (the hardware does not do this) */
|
2024-10-17 01:14:38 +00:00
|
|
|
sim->vip.bkcol = 0;
|
|
|
|
sim->vip.cta.cta_l = 0xFA;
|
|
|
|
sim->vip.cta.cta_r = 0xFA;
|
|
|
|
sim->vip.frmcyc = 0;
|
|
|
|
sim->vip.intpnd = 0x0000;
|
|
|
|
for (x = 0; x < 0x40000; x++)
|
2024-10-16 21:15:39 +00:00
|
|
|
sim->vip.ram[x] = 0;
|
2024-10-15 19:11:29 +00:00
|
|
|
for (x = 0; x < 4; x++) {
|
|
|
|
sim->vip.brtRest[x] = 0;
|
|
|
|
sim->vip.spt [x] = 0;
|
|
|
|
for (y = 0; y < 4; y++) {
|
|
|
|
sim->vip.gplt[x][y] = 0;
|
|
|
|
sim->vip.jplt[x][y] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-17 01:14:38 +00:00
|
|
|
/* Display processor extra (the hardware does not do this) */
|
|
|
|
sim->vip.dp.fclk = 0;
|
|
|
|
sim->vip.dp.l0bsy = 0;
|
|
|
|
sim->vip.dp.l1bsy = 0;
|
|
|
|
sim->vip.dp.lock = 0;
|
|
|
|
sim->vip.dp.r0bsy = 0;
|
|
|
|
sim->vip.dp.r1bsy = 0;
|
2024-10-19 22:01:36 +00:00
|
|
|
sim->vip.dp.scanrdy = 1;
|
2024-10-17 01:14:38 +00:00
|
|
|
|
|
|
|
/* Pixel processor extra (the hardware does not do this) */
|
|
|
|
sim->vip.xp.f0bsy = 0;
|
|
|
|
sim->vip.xp.f1bsy = 0;
|
|
|
|
sim->vip.xp.overtime = 0;
|
|
|
|
sim->vip.xp.sbcmp = 0;
|
|
|
|
sim->vip.xp.sbcount = 0;
|
|
|
|
sim->vip.xp.sbout = 0;
|
|
|
|
|
|
|
|
/* Display processor other */
|
2024-10-16 21:15:39 +00:00
|
|
|
sim->vip.dp.brt[0] = 0;
|
2024-10-17 01:14:38 +00:00
|
|
|
sim->vip.dp.buffer = 0; /* TODO: Hardware might actually do this */
|
2024-10-16 21:15:39 +00:00
|
|
|
sim->vip.dp.clocks = 0;
|
|
|
|
sim->vip.dp.step = 0;
|
2024-10-17 01:14:38 +00:00
|
|
|
sim->vip.dp.until = 0;
|
|
|
|
|
|
|
|
/* Pixel processor other */
|
|
|
|
sim->vip.xp.clocks = 0;
|
|
|
|
sim->vip.xp.step = 0;
|
|
|
|
sim->vip.xp.until = 0;
|
2024-10-15 19:11:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine how many clocks are guaranteed to process */
|
|
|
|
static uint32_t vipUntil(VB *sim, uint32_t clocks) {
|
2024-10-16 23:19:31 +00:00
|
|
|
if (clocks > sim->vip.dp.until)
|
|
|
|
clocks = sim->vip.dp.until;
|
2024-10-19 22:01:36 +00:00
|
|
|
if (sim->vip.xp.step != 0 && clocks > sim->vip.xp.until)
|
|
|
|
clocks = sim->vip.xp.until;
|
2024-10-15 19:11:29 +00:00
|
|
|
return clocks;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write a typed value to the VIP bus */
|
|
|
|
static void vipWrite(VB*sim,uint32_t address,int type,int32_t value,int debug){
|
|
|
|
|
|
|
|
/* Working variables */
|
|
|
|
address &= 0x0007FFFF;
|
|
|
|
|
2024-10-16 21:15:39 +00:00
|
|
|
/* RAM */
|
2024-10-15 19:11:29 +00:00
|
|
|
if (address < 0x40000)
|
2024-10-16 21:15:39 +00:00
|
|
|
busWriteBuffer(&sim->vip.ram[address], type, value);
|
2024-10-15 19:11:29 +00:00
|
|
|
|
|
|
|
/* Unmapped */
|
|
|
|
else if (address < 0x5E000)
|
|
|
|
;
|
|
|
|
|
|
|
|
/* I/O register */
|
|
|
|
else if (address < 0x60000)
|
|
|
|
vipWriteIO(sim, address, type, value, debug);
|
|
|
|
|
|
|
|
/* Unmapped */
|
|
|
|
else if (address < 0x78000)
|
|
|
|
;
|
|
|
|
|
|
|
|
/* Mirrors of character memory */
|
|
|
|
else {
|
|
|
|
address = 0x06000 | (address << 2 & 0x18000) | (address & 0x01FFF);
|
2024-10-16 21:15:39 +00:00
|
|
|
busWriteBuffer(&sim->vip.ram[address], type, value);
|
2024-10-15 19:11:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* VBAPI */
|