Implement VIP, fix interrupts
This commit is contained in:
parent
3c2c9bb938
commit
37b9a94338
|
@ -376,7 +376,7 @@ static uint32_t cpuSetSystemRegister(VB*sim,
|
|||
case VB_FEPSW: return sim->cpu.fepsw = value & 0x000FF3FF;
|
||||
case VB_PIR : return 0x00005346;
|
||||
case VB_PSW :
|
||||
sim->cpu.psw.i = value >> 16 & 0xF;
|
||||
sim->cpu.psw.i = value >> 16 & 15;
|
||||
sim->cpu.psw.np = value >> 15 & 1;
|
||||
sim->cpu.psw.ep = value >> 14 & 1;
|
||||
sim->cpu.psw.ae = value >> 13 & 1;
|
||||
|
@ -414,7 +414,7 @@ static int cpuIRQ(VB *sim) {
|
|||
if (sim->cpu.psw.id | sim->cpu.psw.ep | sim->cpu.psw.np)
|
||||
return 0;
|
||||
level = IRQ_LEVELS[sim->cpu.irq];
|
||||
if (level == -1 || level > sim->cpu.psw.i)
|
||||
if (level < sim->cpu.psw.i)
|
||||
return 0;
|
||||
cpuThrow(sim, 0xFE00 | level << 4);
|
||||
return 1;
|
||||
|
@ -476,6 +476,9 @@ static int cpuException(VB *sim) {
|
|||
/* Regular exception */
|
||||
else {
|
||||
|
||||
/* All exceptions */
|
||||
sim->cpu.eipsw = cpuGetSystemRegister(sim, VB_PSW);
|
||||
|
||||
/* Interrupts only */
|
||||
if ((cause & 0xFF00) == 0xFE00) {
|
||||
sim->cpu.psw.i = (cause >> 4 & 7) + 1;
|
||||
|
@ -487,7 +490,6 @@ static int cpuException(VB *sim) {
|
|||
|
||||
/* All exceptions */
|
||||
sim->cpu.ecr.eicc = cause;
|
||||
sim->cpu.eipsw = cpuGetSystemRegister(sim, VB_PSW);
|
||||
sim->cpu.eipc = sim->cpu.pc;
|
||||
sim->cpu.psw.ep = 1;
|
||||
sim->cpu.nextPC = 0xFFFF0000 |
|
||||
|
|
14
core/vb.c
14
core/vb.c
|
@ -8,6 +8,9 @@
|
|||
|
||||
/*********************************** Types ***********************************/
|
||||
|
||||
/* Output image */
|
||||
typedef uint8_t Pixels[2][384*224];
|
||||
|
||||
/* Simulation state */
|
||||
struct VB {
|
||||
|
||||
|
@ -126,6 +129,7 @@ struct VB {
|
|||
|
||||
/* Pixel processor */
|
||||
struct {
|
||||
|
||||
/* Hardware state */
|
||||
uint8_t f0bsy; /* Drawing into frame buffer 0 */
|
||||
uint8_t f1bsy; /* Drawing into frame buffer 1 */
|
||||
|
@ -137,7 +141,9 @@ struct VB {
|
|||
|
||||
/* Simulation state */
|
||||
uint32_t clocks; /* Master clocks to wait */
|
||||
int column; /* Current horizontal output position */
|
||||
int frame; /* FRMCYC counter */
|
||||
int32_t halfword; /* Current output halfword offset */
|
||||
int step; /* Processing phase */
|
||||
uint32_t until; /* Clocks until interrupt condition */
|
||||
} xp;
|
||||
|
@ -152,8 +158,10 @@ struct VB {
|
|||
uint8_t jplt[4][4]; /* Object palettes */
|
||||
uint16_t spt[4]; /* Object control */
|
||||
|
||||
/* Output frame buffers [buffer index][0=left, 1=right][pixel index] */
|
||||
uint8_t frames[2][2][384*224];
|
||||
/* Rendering shadow memory */
|
||||
uint16_t halfwords[384*28]; /* Output timing by 1x8 halfword */
|
||||
Pixels output[2]; /* Output images, row-major */
|
||||
Pixels shadow; /* Drawing shadow image, column-major */
|
||||
|
||||
/* Other state */
|
||||
uint8_t ram[0x40000]; /* Video memory */
|
||||
|
@ -319,7 +327,7 @@ VBAPI void vbGetPixels(VB *sim, void *left, int leftStrideX, int leftStrideY,
|
|||
continue;
|
||||
|
||||
/* Transfer pixels to the destination */
|
||||
src = sim->vip.frames[sim->vip.dp.buffer][i];
|
||||
src = sim->vip.output[sim->vip.dp.buffer ^ 1][i];
|
||||
for (y = 0; y < 224; y++, dest += yStride)
|
||||
for (x = offset = 0; x < 384; x++, offset += xStride)
|
||||
dest[offset] = *src++;
|
||||
|
|
523
core/vip.c
523
core/vip.c
|
@ -8,10 +8,47 @@
|
|||
/* Compute how many clocks are in some number of milliseconds */
|
||||
#define vipClocksMs(x) x * 20000
|
||||
|
||||
/* 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 }
|
||||
};
|
||||
|
||||
|
||||
|
||||
/***************************** Module Functions ******************************/
|
||||
|
||||
/* 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)];
|
||||
}
|
||||
|
||||
/* Read a palette */
|
||||
static int32_t vipReadPalette(uint8_t *entries) {
|
||||
return entries[3] << 6 | entries[2] << 4 | entries[1] << 2;
|
||||
|
@ -36,8 +73,8 @@ static void vipWritePalette(uint8_t *entries, int32_t mask, int32_t value) {
|
|||
|
||||
/* Read a typed value from an I/O register */
|
||||
static int32_t vipReadIO(VB *sim, uint32_t address, int type) {
|
||||
int32_t mask; /* Byte access mask */
|
||||
int32_t value; /* Return value */
|
||||
int32_t mask = 0; /* Byte access mask */
|
||||
int32_t value; /* Return value */
|
||||
|
||||
/* Adjustments by type */
|
||||
switch (type) {
|
||||
|
@ -211,7 +248,7 @@ static void vipWriteIO(
|
|||
break;
|
||||
case 0x5F804>>1: /* INTCLR */
|
||||
sim->vip.intpnd &= ~value;
|
||||
if (sim->vip.intpnd == 0)
|
||||
if ((sim->vip.intpnd & sim->vip.intenb) == 0)
|
||||
sim->cpu.irq &= ~0x0010;
|
||||
break;
|
||||
|
||||
|
@ -223,7 +260,16 @@ static void vipWriteIO(
|
|||
}
|
||||
if ((mask & 0x00FF) == 0) {
|
||||
sim->vip.dp.disp = value >> 1 & 1;
|
||||
/* if (value & 1) {} TODO: DPRST */
|
||||
|
||||
/* 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 */
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -256,7 +302,16 @@ static void vipWriteIO(
|
|||
sim->vip.xp.sbcmp = value >> 8 & 31;
|
||||
if ((mask & 0x00FF) == 0) {
|
||||
sim->vip.xp.xpen = value >> 1 & 1;
|
||||
/* if (value & 1) TODO: XPRST */
|
||||
|
||||
/* 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 */
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -350,7 +405,7 @@ static void vipTransferColumn(VB *sim, int32_t eye) {
|
|||
int y, z; /* Iterators */
|
||||
|
||||
/* Select destination address in host memory */
|
||||
dest = &sim->vip.frames
|
||||
dest = &sim->vip.output
|
||||
[sim->vip.dp.buffer][eye][sim->vip.dp.column];
|
||||
|
||||
/* Output is disabled */
|
||||
|
@ -407,23 +462,26 @@ static int vipEmulateDisplay(VB *sim, uint32_t clocks) {
|
|||
vipThrow(sim, 0x0010); /* FRAMESTART */
|
||||
|
||||
/* Game frame */
|
||||
if (sim->vip.xp.frame >= sim->vip.frmcyc) {
|
||||
sim->vip.xp.frame = 0;
|
||||
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.xp.overtime = 0;
|
||||
sim->vip.xp.step = 1;
|
||||
vipThrow(sim, 0x0008); /* GAMESTART */
|
||||
}
|
||||
/* Initiate drawing procedure */
|
||||
if (sim->vip.xp.step == 0) {
|
||||
sim->vip.dp.buffer ^= 1;
|
||||
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 */
|
||||
}
|
||||
/* Drawing procedure has run into overtime */
|
||||
else {
|
||||
sim->vip.xp.overtime = 1;
|
||||
vipThrow(sim, 0x8000); /* TIMEERR */
|
||||
}
|
||||
|
||||
} else sim->vip.xp.frame++;
|
||||
} else sim->vip.xp.frame++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
@ -518,9 +576,375 @@ static int vipEmulateDisplay(VB *sim, uint32_t clocks) {
|
|||
|
||||
/****************************** Pixel Processor ******************************/
|
||||
|
||||
/* Draw every world into a group of 8 rows of pixels */
|
||||
static void vipDrawWorlds(VB *sim) {
|
||||
(void) sim;
|
||||
/* 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;
|
||||
for (o = stop;; o = (o - 1) & 1023) {
|
||||
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 */
|
||||
} /* x */
|
||||
|
||||
} /* y */
|
||||
|
||||
} /* i */
|
||||
|
||||
/* All objects have been drawn */
|
||||
if (o == start)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Draw a background world into shadow memory */
|
||||
static void vipDrawWorld(VB *sim, uint8_t *world, uint16_t attr) {
|
||||
uint8_t *bg; /* Background arrangement */
|
||||
uint16_t bgAttr; /* BG map cell attributes */
|
||||
int32_t bgh; /* Height of background in BG maps, minus 1 */
|
||||
int32_t bgw; /* Width of background in BG maps, minus 1 */
|
||||
int32_t bottom; /* Window bottom edge position */
|
||||
int32_t bp; /* Eye BG map source parallax */
|
||||
int32_t bx; /* BG horizontal map source position */
|
||||
int32_t by; /* BG vertical map source position */
|
||||
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 horizontal pixel source position */
|
||||
int32_t cy; /* BG cell/character vertical pixel source position */
|
||||
uint8_t *dest; /* Pointer to output in shadow memory */
|
||||
int32_t left; /* Window left edge position */
|
||||
uint32_t param; /* Current line parameter address */
|
||||
int8_t pixel; /* Character pixel value */
|
||||
int32_t right; /* Window right edge position */
|
||||
int32_t top; /* Window top edge position */
|
||||
int32_t wx; /* Eye horizontal position */
|
||||
int32_t i, x, y; /* Iterators */
|
||||
|
||||
/* World attributes */
|
||||
int32_t base; /* BG map base index */
|
||||
int bgm; /* BG mode */
|
||||
int32_t dx; /* Affine per-pixel X delta */
|
||||
int32_t dy; /* Affine per-pixel Y delta */
|
||||
int16_t gp; /* Window stereo parallax */
|
||||
int16_t gx; /* Window base horizontal position */
|
||||
int16_t gy; /* Window vertical position */
|
||||
int16_t h; /* Window height */
|
||||
int16_t hofst; /* H-bias shift */
|
||||
int32_t mp; /* BG map source stereo parallax */
|
||||
int32_t mx = 0; /* BG map horizontal source */
|
||||
int32_t my = 0; /* BG map vertical source */
|
||||
int32_t scx; /* Background width shift amount */
|
||||
int32_t scy; /* Background height shift amount */
|
||||
uint8_t *over; /* Pointer to overplane "cell" in host memory */
|
||||
uint32_t params = 0; /* Base address of line parameters */
|
||||
int16_t w; /* Window width */
|
||||
|
||||
/* Validate the world isn't below the frame */
|
||||
gy = busReadBuffer(world + 6, VB_S16);
|
||||
if (gy > 223)
|
||||
return;
|
||||
|
||||
/* Validate the world isn't above the frame, and height is positive */
|
||||
bgm = attr >> 12 & 3;
|
||||
y = bgm == 2 ? 0 : 8;
|
||||
h = busReadBuffer(world + 16, VB_S16) + 1;
|
||||
if (h < y)
|
||||
h = y;
|
||||
if (h == 0)
|
||||
return;
|
||||
bottom = gy + h - 1;
|
||||
top = (int32_t) sim->vip.xp.sbcount << 3;
|
||||
if (bottom < top)
|
||||
return;
|
||||
if (bottom > 223)
|
||||
bottom = 223;
|
||||
if (top < gy)
|
||||
top = gy;
|
||||
|
||||
/* Validate width is positive */
|
||||
w = SignExtend(busReadBuffer(world + 14, VB_U16), bgm == 2 ? 10 : 13) + 1;
|
||||
if (w < 1)
|
||||
return;
|
||||
|
||||
/* Parse attributes */
|
||||
bg = (uint8_t *) BG_TEMPLATES[attr >> 8 & 15];
|
||||
gx = SignExtend(busReadBuffer(world + 2, VB_U16), 10);
|
||||
gp = SignExtend(busReadBuffer(world + 4, VB_U16), 10);
|
||||
over = (attr & 0x0080) ? world + 20 : NULL;
|
||||
scx = attr >> 10 & 3;
|
||||
scy = attr >> 8 & 3;
|
||||
bgw = (1 << scx) - 1;
|
||||
bgh = (1 << scy) - 1;
|
||||
base = attr & 15 & ~((1 << (scx + scy < 3 ? scx + scy : 3)) - 1);
|
||||
if (bgm != 2) {
|
||||
mx = SignExtend(busReadBuffer(world + 8, VB_U16), 13);
|
||||
mp = SignExtend(busReadBuffer(world + 10, VB_U16), 15);
|
||||
my = SignExtend(busReadBuffer(world + 12, VB_U16), 13);
|
||||
}
|
||||
if (bgm != 0) {
|
||||
params = 0x20000 + (top - gy) * (bgm == 1 ? 4 : 16) +
|
||||
((uint32_t) busReadBuffer(world + 18, VB_U16) << 1);
|
||||
}
|
||||
else hofst = 0;
|
||||
|
||||
/* Draw the world */
|
||||
for (i = 0; i < 2; i++) {
|
||||
|
||||
/* Validate world is enabled */
|
||||
if ((attr & (int32_t) 0x8000 >> i) == 0) /* LON, RON */
|
||||
continue;
|
||||
|
||||
/* Validate the world isn't right of frame */
|
||||
wx = gx - (i == 0 ? gp : -gp);
|
||||
if (wx > 383)
|
||||
return;
|
||||
left = wx < 0 ? 0 : wx;
|
||||
|
||||
/* Validate the world isn't left of frame */
|
||||
right = wx + w - 1;
|
||||
if (right < 0)
|
||||
return;
|
||||
if (right > 383)
|
||||
right = 383;
|
||||
|
||||
/* BG source parallax */
|
||||
if (bgm != 2)
|
||||
bp = i == 0 ? -mp : mp;
|
||||
|
||||
/* Draw all rows */
|
||||
for (y = top, param = params; y <= bottom; y++) {
|
||||
|
||||
/* Parse line parameters */
|
||||
switch (bgm) {
|
||||
case 1: /* H-bias */
|
||||
hofst = SignExtend(vipParam(param | i << 1), 13);
|
||||
param += 4;
|
||||
break;
|
||||
case 2: /* Affine */
|
||||
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);
|
||||
mx = ((int32_t) (int16_t) vipParam(param ) << 13) +
|
||||
dx * (left - ((mp < 0) ^ i ? mp : 0));
|
||||
my = ((int32_t) (int16_t) vipParam(param | 4) << 13) +
|
||||
dy * (left - ((mp < 0) ^ i ? mp : 0));
|
||||
param += 16;
|
||||
}
|
||||
|
||||
/* Select output in shadow memory */
|
||||
dest = &sim->vip.shadow[i][left * 224 + y];
|
||||
|
||||
/* Draw all columns */
|
||||
for (x = left; x <= right; x++, dest += 224) {
|
||||
|
||||
/* Locate the pixel in the background */
|
||||
if (bgm != 2) { /* Normal, H-bias */
|
||||
cx = x - wx + mx + bp + hofst;
|
||||
cy = y - gy + my;
|
||||
} 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 */
|
||||
if (bx < 0 || bx > bgw || by < 0 || by > bgh) {
|
||||
if (over == NULL) {
|
||||
bx &= bgw;
|
||||
by &= bgh;
|
||||
}
|
||||
else cell = over;
|
||||
}
|
||||
|
||||
/* Locate the cell in the BG map */
|
||||
if (cell == NULL) {
|
||||
cell = &sim->vip.ram[
|
||||
0x20000 |
|
||||
(base + bg[by << scx | bx]) << 13 |
|
||||
(cy << 4 & 0x1F80) |
|
||||
(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 */
|
||||
if (attr & 0x0020) /* END */
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Process sub-component */
|
||||
|
@ -544,29 +968,49 @@ static void vipEmulateDrawing(VB *sim, uint32_t clocks) {
|
|||
/* Processing by operation phase */
|
||||
switch (sim->vip.xp.step) {
|
||||
|
||||
case 1: /* Initialize */
|
||||
sim->vip.xp.sbcount = 0;
|
||||
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);
|
||||
|
||||
/* Fallthrough */
|
||||
case 2: /* Transition to a new row of halfwords */
|
||||
sim->vip.xp.sbout = 2240; /* 112us */
|
||||
sim->vip.xp.step = 3;
|
||||
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 */
|
||||
if (sim->vip.xp.sbcount == sim->vip.xp.sbcmp)
|
||||
vipThrow(sim, 0x2000); /* SBHIT */
|
||||
|
||||
/* 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;
|
||||
break;
|
||||
|
||||
case 3: /* Draw halfwords */
|
||||
vipDrawWorlds(sim);
|
||||
sim->vip.xp.until = 2000; /* 100us */
|
||||
break;
|
||||
|
||||
case 4: /* Finalize */
|
||||
case 4: /* Drawing complete */
|
||||
sim->vip.xp.step = 0;
|
||||
if (sim->vip.dp.buffer == 0)
|
||||
sim->vip.xp.f1bsy = 0;
|
||||
else sim->vip.xp.f0bsy = 0;
|
||||
vipThrow(sim, 0x4000); /* XPEND */
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return; /* Unreachable */
|
||||
}
|
||||
|
||||
|
||||
|
@ -578,12 +1022,13 @@ static int vipEmulate(VB *sim, uint32_t clocks) {
|
|||
|
||||
/* Process sub-components */
|
||||
int brk = vipEmulateDisplay(sim, clocks);
|
||||
vipEmulateDrawing(sim, clocks);
|
||||
if (sim->vip.xp.step != 0)
|
||||
vipEmulateDrawing(sim, clocks);
|
||||
|
||||
/* Process SBOUT */
|
||||
if (sim->vip.xp.sbout != 0) {
|
||||
if (sim->vip.xp.sbout <= clocks) {
|
||||
sim->vip.xp.sbout = 0;
|
||||
sim->vip.xp.sbout = 0;
|
||||
} else sim->vip.xp.sbout -= clocks;
|
||||
}
|
||||
|
||||
|
@ -609,7 +1054,7 @@ static void vipRead(VB *sim, uint32_t address, int type, int32_t *value) {
|
|||
*value = vipReadIO(sim, address, type);
|
||||
|
||||
/* Unmapped */
|
||||
if (address < 0x78000)
|
||||
else if (address < 0x78000)
|
||||
*value = 0;
|
||||
|
||||
/* Mirrors of character memory */
|
||||
|
@ -655,7 +1100,7 @@ static void vipReset(VB *sim) {
|
|||
sim->vip.dp.lock = 0;
|
||||
sim->vip.dp.r0bsy = 0;
|
||||
sim->vip.dp.r1bsy = 0;
|
||||
sim->vip.dp.scanrdy = 0;
|
||||
sim->vip.dp.scanrdy = 1;
|
||||
|
||||
/* Pixel processor extra (the hardware does not do this) */
|
||||
sim->vip.xp.f0bsy = 0;
|
||||
|
@ -682,6 +1127,8 @@ static void vipReset(VB *sim) {
|
|||
static uint32_t vipUntil(VB *sim, uint32_t clocks) {
|
||||
if (clocks > sim->vip.dp.until)
|
||||
clocks = sim->vip.dp.until;
|
||||
if (sim->vip.xp.step != 0 && clocks > sim->vip.xp.until)
|
||||
clocks = sim->vip.xp.until;
|
||||
return clocks;
|
||||
}
|
||||
|
||||
|
|
16
makefile
16
makefile
|
@ -20,21 +20,21 @@ clean:
|
|||
.PHONY: core
|
||||
core:
|
||||
# GCC generic
|
||||
@gcc core/vb.c -I core -c -o /dev/null \
|
||||
-Werror -std=c90 -Wall -Wextra -Wpedantic
|
||||
@gcc core/vb.c -I core -c -o /dev/null -O3 \
|
||||
-Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing
|
||||
# GCC compilation control
|
||||
@gcc core/vb.c -I core -c -o /dev/null \
|
||||
-Werror -std=c90 -Wall -Wextra -Wpedantic \
|
||||
@gcc core/vb.c -I core -c -o /dev/null -O3 \
|
||||
-Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing \
|
||||
-D VB_LITTLE_ENDIAN -D VB_SIGNED_PROPAGATE -D VB_DIV_GENERIC \
|
||||
-D VB_DIRECT_EXCEPTION=textException -D VB_DIRECT_EXECUTE=textExecute \
|
||||
-D VB_DIRECT_FETCH=testFetch -D VB_DIRECT_FRAME=testFrame \
|
||||
-D VB_DIRECT_WRITE=testWrite -D VB_DIRECT_READ=testRead
|
||||
# Clang generic
|
||||
@emcc core/vb.c -I core -c -o /dev/null \
|
||||
-Werror -std=c90 -Wall -Wextra -Wpedantic
|
||||
@emcc core/vb.c -I core -c -o /dev/null -O3 \
|
||||
-Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing
|
||||
# Clang compilation control
|
||||
@emcc core/vb.c -I core -c -o /dev/null \
|
||||
-Werror -std=c90 -Wall -Wextra -Wpedantic \
|
||||
@emcc core/vb.c -I core -c -o /dev/null -O3 \
|
||||
-Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing \
|
||||
-D VB_LITTLE_ENDIAN -D VB_SIGNED_PROPAGATE -D VB_DIV_GENERIC \
|
||||
-D VB_DIRECT_EXCEPTION=textException -D VB_DIRECT_EXECUTE=textExecute \
|
||||
-D VB_DIRECT_FETCH=testFetch -D VB_DIRECT_FRAME=testFrame \
|
||||
|
|
Loading…
Reference in New Issue