VIP optimizations
This commit is contained in:
parent
bc864644f7
commit
c90b8137de
58
core/vb.c
58
core/vb.c
|
@ -8,8 +8,11 @@
|
|||
|
||||
/*********************************** Types ***********************************/
|
||||
|
||||
/* Output image */
|
||||
typedef uint8_t Pixels[2][384*224];
|
||||
/* VIP BG map cell */
|
||||
typedef struct {
|
||||
uint8_t *palette; /* Points to a VB.vip.gplt */
|
||||
uint8_t *pixels; /* Points to a VB.vip.characters */
|
||||
} Cell;
|
||||
|
||||
/* VSU channel */
|
||||
typedef struct {
|
||||
|
@ -69,6 +72,53 @@ typedef struct {
|
|||
uint32_t until; /* Clocks until channel component update */
|
||||
} Channel;
|
||||
|
||||
/* VIP character (hflp << 1 | vflp) */
|
||||
typedef uint8_t Character[4][64];
|
||||
|
||||
/* VIP object */
|
||||
typedef struct {
|
||||
Cell cell; /* BG map-like attributes */
|
||||
int16_t jp; /* Parallax */
|
||||
int16_t jx; /* Screen base X */
|
||||
int16_t jy; /* Screen Y */
|
||||
uint8_t lron; /* Visible in left and right images */
|
||||
} Object;
|
||||
|
||||
/* Output image */
|
||||
typedef uint8_t Pixels[2][384*224];
|
||||
|
||||
/* VIP world */
|
||||
typedef struct {
|
||||
|
||||
/* Attributes */
|
||||
uint8_t bgm; /* Mode */
|
||||
uint8_t end; /* Control world */
|
||||
int16_t gp; /* World parallax */
|
||||
int16_t gx; /* World base X */
|
||||
int16_t gy; /* World Y */
|
||||
int16_t mx; /* Scroll base X */
|
||||
int16_t mp; /* Scroll parallax */
|
||||
int16_t my; /* Scroll Y */
|
||||
uint8_t over; /* Use overplane */
|
||||
Cell *overplane; /* Overplane cell */
|
||||
|
||||
/* Non-attributes */
|
||||
Cell *bg[64]; /* Background arrangement */
|
||||
int32_t bgHeight; /* Height of background in pixels */
|
||||
uint32_t bgMaskX; /* Horizontal background mask */
|
||||
uint32_t bgMaskY; /* Vertical background mask */
|
||||
uint32_t bgShift; /* Bits to shift when selecting a BG map */
|
||||
int32_t bgWidth; /* Width of background in pixels */
|
||||
uint32_t hAffine; /* H for affine mode */
|
||||
uint32_t hNaffine; /* H for not affine mode */
|
||||
uint32_t height; /* Height of world in pixels */
|
||||
uint8_t lron; /* Visible in left and right images */
|
||||
uint32_t paramBase; /* H-bias and affine parameter address */
|
||||
uint32_t wAffine; /* W for affine mode */
|
||||
uint32_t wNaffine; /* W for not affine mode */
|
||||
uint32_t width; /* Width of world in pixels */
|
||||
} World;
|
||||
|
||||
/* Simulation state */
|
||||
struct VB {
|
||||
|
||||
|
@ -280,7 +330,11 @@ struct VB {
|
|||
Pixels shadow; /* Drawing shadow image, column-major */
|
||||
|
||||
/* Other state */
|
||||
Cell cells[0x10000]; /* Pre-computed BG map attributes */
|
||||
Character characters[2048]; /* Pre-computed character pixels */
|
||||
Object objects[1024]; /* Pre-computed object attributes */
|
||||
uint8_t ram[0x40000]; /* Video memory */
|
||||
World worlds[32]; /* Pre-computed world attributes */
|
||||
} vip;
|
||||
|
||||
/* VSU */
|
||||
|
|
889
core/vip.c
889
core/vip.c
|
@ -56,37 +56,18 @@ static const uint8_t BRIGHT8[] = {
|
|||
|
||||
/*********************************** Types ***********************************/
|
||||
|
||||
/* World attribtues per eye */
|
||||
/* Intersection of the screen and a world rectangle */
|
||||
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 */
|
||||
uint8_t *over; /* Overplane character */
|
||||
uint32_t params; /* Line parameters address */
|
||||
int32_t scx, scy; /* Background dimensions */
|
||||
int32_t w, h; /* Window size */
|
||||
} WorldAttribs;
|
||||
int32_t x1; /* Left edge inclusive */
|
||||
int32_t x2; /* Right edge exclusive */
|
||||
int32_t y1; /* Top edge inclusive */
|
||||
int32_t y2; /* Bottom edge exclusive */
|
||||
} Window;
|
||||
|
||||
|
||||
|
||||
/***************************** 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;
|
||||
|
@ -244,6 +225,178 @@ static int32_t vipReadIO(VB *sim, uint32_t address, int type) {
|
|||
value;
|
||||
}
|
||||
|
||||
/* Write into BG map cell memory */
|
||||
static void vipWriteCell(VB *sim, uint32_t offset, int type, int32_t value) {
|
||||
Cell *cell;
|
||||
|
||||
/* Adjustments for byte and word writes */
|
||||
switch (type) {
|
||||
case VB_S8:
|
||||
case VB_U8:
|
||||
value = offset & 1 ?
|
||||
value << 8 | sim->vip.ram[0x20000 | offset] :
|
||||
value | (int32_t) sim->vip.ram[0x20001 | offset] << 8
|
||||
;
|
||||
break;
|
||||
case VB_S32:
|
||||
vipWriteCell(sim, offset + 2, VB_U16, value >> 16);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update pixel pointer */
|
||||
cell = &sim->vip.cells[offset >> 1];
|
||||
cell->palette = sim->vip.gplt[value >> 14 & 3];
|
||||
cell->pixels = sim->vip.characters[value & 0x07FF][value >> 12 & 3];
|
||||
}
|
||||
|
||||
/* Write into character memory */
|
||||
static void vipWriteChr(VB *sim, uint32_t offset, int type, int32_t value) {
|
||||
Character *chr; /* Pixel state */
|
||||
int b, l, r, t; /* Pixel bounds */
|
||||
int x, y, X, Y; /* Iterators */
|
||||
|
||||
/* Byte advance by data type */
|
||||
static const uint8_t HORZ[] = { 4, 4, 8, 8, 8 };
|
||||
static const uint8_t VERT[] = { 8, 8, 8, 8, 16 };
|
||||
|
||||
/* Working variables */
|
||||
chr = &sim->vip.characters[offset >> 4];
|
||||
l = (offset & 0x1) << 2;
|
||||
t = (offset & 0xE) << 2;
|
||||
r = l + HORZ[type];
|
||||
b = t + VERT[type];
|
||||
|
||||
/* Process all pixels */
|
||||
for (y = t, Y = 56 - y; y < b; y += 8, Y -= 8)
|
||||
for (x = l, X = 7 - x; x < r; x += 1, X -= 1, value >>= 2) {
|
||||
(*chr)[0][y | x] = (*chr)[1][Y | x] =
|
||||
(*chr)[2][y | X] = (*chr)[3][Y | X] = value & 3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Write into object attribute memory */
|
||||
static void vipWriteObject(VB *sim, uint32_t offset, int type, int32_t value) {
|
||||
Object *obj; /* Object state */
|
||||
|
||||
/* Adjustments for byte and word writes */
|
||||
switch (type) {
|
||||
case VB_S8:
|
||||
case VB_U8:
|
||||
value = offset & 1 ?
|
||||
value << 8 | sim->vip.ram[0x3E000 | offset] :
|
||||
value | (int32_t) sim->vip.ram[0x3E001 | offset] << 8
|
||||
;
|
||||
break;
|
||||
case VB_S32:
|
||||
vipWriteObject(sim, offset + 2, VB_U16, value >> 16);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Processing by offset */
|
||||
obj = &sim->vip.objects[offset >> 3];
|
||||
switch (offset >> 1 & 3) {
|
||||
case 0: obj->jx = SignExtend(value, 10); break;
|
||||
case 1:
|
||||
obj->jp = SignExtend(value, 10);
|
||||
obj->lron = value >> 14 & 3;
|
||||
break;
|
||||
case 2:
|
||||
value = (int8_t) value;
|
||||
obj->jy = value > -8 ? value : value & 0xFF;
|
||||
break;
|
||||
case 3:
|
||||
obj->cell.palette = sim->vip.jplt[value >> 14 & 3];
|
||||
obj->cell.pixels =
|
||||
sim->vip.characters[value & 0x07FF][value >> 12 & 3];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Write into world attribute memory */
|
||||
static void vipWriteWorld(VB *sim, uint32_t offset, int type, int32_t value) {
|
||||
uint32_t base; /* Base BG map index */
|
||||
uint8_t *bg; /* Background template */
|
||||
uint32_t count; /* Number of BG maps in background */
|
||||
World *world; /* World state */
|
||||
uint32_t z; /* Iterator */
|
||||
|
||||
/* Adjustments for byte and word writes */
|
||||
switch (type) {
|
||||
case VB_S8:
|
||||
case VB_U8:
|
||||
value = offset & 1 ?
|
||||
value << 8 | sim->vip.ram[0x3D800 | offset] :
|
||||
value | (int32_t) sim->vip.ram[0x3D801 | offset] << 8
|
||||
;
|
||||
break;
|
||||
case VB_S32:
|
||||
vipWriteWorld(sim, offset + 2, VB_U16, value >> 16);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Processing by offset */
|
||||
world = &sim->vip.worlds[offset >> 5];
|
||||
switch (offset >> 1 & 15) {
|
||||
case 0:
|
||||
|
||||
/* Parse attributes */
|
||||
world->lron = value >> 14 & 3;
|
||||
world->bgm = value >> 12 & 3;
|
||||
world->over = value >> 7 & 1;
|
||||
world->end = value >> 6 & 1;
|
||||
|
||||
/* Update background fields */
|
||||
z = value >> 10 & 3;
|
||||
world->bgMaskX = (1 << z) - 1;
|
||||
world->bgWidth = 512 << z;
|
||||
world->bgShift = z;
|
||||
z = value >> 8 & 3;
|
||||
world->bgMaskY = (1 << z) - 1;
|
||||
world->bgHeight = 512 << z;
|
||||
|
||||
/* Update world dimensions */
|
||||
if (world->bgm != 2) {
|
||||
world->height = world->hNaffine;
|
||||
world->width = world->wNaffine;
|
||||
} else {
|
||||
world->height = world->hAffine;
|
||||
world->width = world->wAffine;
|
||||
}
|
||||
|
||||
/* Update background arrangement */
|
||||
count = (world->bgMaskX + 1) * (world->bgMaskY + 1);
|
||||
base = value & 15 & ~(count > 8 ? 7 : count - 1);
|
||||
bg = (uint8_t *) BG_TEMPLATES[value >> 8 & 15];
|
||||
for (z = 0; z < count; z++)
|
||||
world->bg[z] = &sim->vip.cells[(base + bg[z]) << 12];
|
||||
break;
|
||||
|
||||
case 1: world->gx = SignExtend(value, 10); break;
|
||||
case 2: world->gp = SignExtend(value, 10); break;
|
||||
case 3: world->gy = (int16_t) value; break;
|
||||
case 4: world->mx = SignExtend(value, 13); break;
|
||||
case 5: world->mp = SignExtend(value, 15); break;
|
||||
case 6: world->my = SignExtend(value, 13); break;
|
||||
case 7: /* W */
|
||||
value = SignExtend(value, 13) + 1;
|
||||
world->wNaffine = value < 0 ? 0 : value;
|
||||
value = SignExtend(value, 10) + 1;
|
||||
world->wAffine = value < 0 ? 0 : value;
|
||||
world->width = world->bgm != 2 ? world->wNaffine : world->wAffine;
|
||||
break;
|
||||
case 8: /* H */
|
||||
value = (int16_t) value + 1;
|
||||
world->hNaffine = value < 8 ? 8 : value;
|
||||
world->hAffine = value < 0 ? 0 : value;
|
||||
world->height = world->bgm != 2 ? world->hNaffine : world->hAffine;
|
||||
break;
|
||||
case 9: world->paramBase = 0x20000|(uint32_t)(uint16_t)value<<1;break;
|
||||
case 10: world->overplane = &sim->vip.cells[(uint16_t) value]; break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Write a typed value to an I/O register */
|
||||
static void vipWriteIO(
|
||||
VB *sim, uint32_t address, int type, int32_t value, int debug) {
|
||||
|
@ -627,313 +780,352 @@ static int vipEmulateDisplay(VB *sim, uint32_t clocks) {
|
|||
|
||||
/****************************** Pixel Processor ******************************/
|
||||
|
||||
/* Parse eye attributes and test window bounds */
|
||||
static int vipParseEye(WorldAttribs *wttr, EyeAttribs *ettr, int eye) {
|
||||
/* Draw a cell into shadow memory */
|
||||
static void vipDrawCell(uint8_t *shadow, Window *wnd,
|
||||
Cell *cell, int32_t x, int32_t y) {
|
||||
uint8_t *col, *row; /* Output pixel */
|
||||
uint8_t pixel; /* Character pixel value */
|
||||
int32_t px1, px2, py1, py2; /* Pixel bounds in cell */
|
||||
int32_t px, py; /* Pixel origin in cell */
|
||||
|
||||
/* 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;
|
||||
/* Identify the visible pixels */
|
||||
py1 = y > wnd->y1 ? y : wnd->y1;
|
||||
py2 = y + 8 < wnd->y2 ? y + 8 : wnd->y2;
|
||||
px1 = x > wnd->x1 ? x : wnd->x1;
|
||||
px2 = x + 8 < wnd->x2 ? x + 8 : wnd->x2;
|
||||
|
||||
/* 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;
|
||||
/* Process all columns of pixels */
|
||||
col = &shadow[px1 * 224 + py1];
|
||||
for (px = px1; px < px2; px++, col += 224)
|
||||
for (py = py1, row = col; py < py2; py++, row++) {
|
||||
pixel = cell->pixels[(py - y) << 3 | (px - x)];
|
||||
if (pixel != 0)
|
||||
*row = cell->palette[pixel];
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
/* Draw a BG map into shadow memory */
|
||||
static void vipDrawBGMap(uint8_t *shadow, Window *wnd,
|
||||
uint8_t over, Cell *map, int32_t x, int32_t y) {
|
||||
Cell *col, *row; /* Source cell */
|
||||
int32_t cx1, cx2, cy1, cy2; /* Cell bounds in BG map pixels */
|
||||
int32_t cx, cy; /* Cell origin in BG map pixels */
|
||||
|
||||
/* Validate the world isn't below the frame */
|
||||
attr->gy = busReadBuffer(world + 6, VB_S16);
|
||||
if (attr->gy > 223)
|
||||
return 1;
|
||||
/* Identify the visible cells */
|
||||
cy1 = (wnd->y1-y ) & ~7; if (cy1 < 0) cy1 = 0; if (cy1 > 512) cy1 = 512;
|
||||
cy2 = (wnd->y2-y+7) & ~7; if (cy2 < 0) cy2 = 0; if (cy2 > 512) cy2 = 512;
|
||||
cx1 = (wnd->x1-x ) & ~7; if (cx1 < 0) cx1 = 0; if (cx1 > 512) cx1 = 512;
|
||||
cx2 = (wnd->x2-x+7) & ~7; if (cx2 < 0) cx2 = 0; if (cx2 > 512) cx2 = 512;
|
||||
|
||||
/* 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 & 0x0080) == 0 ? NULL :
|
||||
&sim->vip.ram[0x00020000 | busReadBuffer(world + 10, VB_U16) << 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)
|
||||
;
|
||||
/* Process all cells as overplane */
|
||||
if (over) {
|
||||
for (cy = cy1; cy < cy2; cy += 8)
|
||||
for (cx = cx1; cx < cx2; cx += 8)
|
||||
vipDrawCell(shadow, wnd, map, x + cx, y + cy);
|
||||
return;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* Process all cells normally */
|
||||
row = &map[cy1 << 3 | cx1 >> 3];
|
||||
for (cy = cy1 ; cy < cy2; cy += 8, row += 64)
|
||||
for (cx = cx1, col = row; cx < cx2; cx += 8, col++)
|
||||
vipDrawCell(shadow, wnd, col, x + cx, y + cy);
|
||||
}
|
||||
|
||||
/* Draw a background into shadow memory */
|
||||
static void vipDrawBackground(uint8_t *shadow, Window *wnd,
|
||||
World *world, int32_t x, int32_t y) {
|
||||
int32_t bx1, bx2, by1, by2; /* BG map bounds in background pixels */
|
||||
int32_t bx, by; /* BG map origin in background pixels */
|
||||
|
||||
/* Identify the visible BG maps */
|
||||
by1 = (wnd->y1 - y ) & ~511;
|
||||
by2 = (wnd->y2 - y - 1) & ~511;
|
||||
bx1 = (wnd->x1 - x ) & ~511;
|
||||
bx2 = (wnd->x2 - x - 1) & ~511;
|
||||
|
||||
/* Process all rows of BG maps */
|
||||
for (by = by1; by <= by2; by += 512) {
|
||||
|
||||
/* Row is out of bounds */
|
||||
if (world->over && (by < 0 || by >= world->bgHeight)) {
|
||||
for (bx = bx1; bx <= bx2; bx++)
|
||||
vipDrawBGMap(shadow, wnd, 1, world->overplane, x + bx, y + by);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Process all columns of BG maps */
|
||||
for (bx = bx1; bx <= bx2; bx += 512) {
|
||||
|
||||
/* Column is out of bounds */
|
||||
if (world->over && (bx < 0 || bx >= world->bgWidth)) {
|
||||
vipDrawBGMap(shadow, wnd, 1, world->overplane, x + bx, y + by);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Column is in bounds */
|
||||
vipDrawBGMap(shadow, wnd, 0, world->bg[
|
||||
(by >> 9 & world->bgMaskY) << world->bgShift |
|
||||
(bx >> 9 & world->bgMaskX)
|
||||
], x + bx, y + by);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Draw a Normal world into shadow memory */
|
||||
static void vipDrawNormal(VB *sim, World *world) {
|
||||
int32_t mx, my; /* Background origin */
|
||||
Window wnd; /* Window bounds in pixels */
|
||||
int i; /* Iterator */
|
||||
|
||||
/* No visible content */
|
||||
if (world->width == 0)
|
||||
return;
|
||||
|
||||
/* Vertical window bounds */
|
||||
wnd.y1 = (int32_t) sim->vip.xp.sbcount << 3;
|
||||
if (world->gy > wnd.y1)
|
||||
wnd.y1 = world->gy;
|
||||
if (wnd.y1 >= 224)
|
||||
return;
|
||||
wnd.y2 = wnd.y1 + world->height;
|
||||
if (wnd.y2 <= 0)
|
||||
return;
|
||||
my = wnd.y1 - world->my;
|
||||
if (wnd.y1 < 0)
|
||||
wnd.y1 = 0;
|
||||
if (wnd.y2 > 224)
|
||||
wnd.y2 = 224;
|
||||
|
||||
/* Process both eyes */
|
||||
for (i = 0; i < 2; i++) {
|
||||
|
||||
/* Check visibility */
|
||||
if (!(world->lron >> (i ^ 1) & 1))
|
||||
continue;
|
||||
|
||||
/* Horizontal window bounds */
|
||||
wnd.x1 = world->gx + (i == 0 ? -world->gp : world->gp);
|
||||
if (wnd.x1 >= 384)
|
||||
continue;
|
||||
wnd.x2 = wnd.x1 + world->width;
|
||||
if (wnd.x2 <= 0)
|
||||
continue;
|
||||
mx = wnd.x1 - world->mx - (i == 0 ? -world->mp : world->mp);
|
||||
if (wnd.x1 < 0)
|
||||
wnd.x1 = 0;
|
||||
if (wnd.x2 > 384)
|
||||
wnd.x2 = 384;
|
||||
|
||||
/* Draw the background into the window */
|
||||
vipDrawBackground(sim->vip.shadow[i], &wnd, world, mx, my);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Draw an H-bias world into shadow memory */
|
||||
static void vipDrawHBias(VB *sim, World *world) {
|
||||
int32_t mx, my; /* Background origin */
|
||||
uint8_t *param; /* World parameter memory */
|
||||
Window wnd; /* Window bounds in pixels */
|
||||
int32_t y1, y2; /* Vertical window bounds */
|
||||
int i, y; /* Iterators */
|
||||
|
||||
/* No visible content */
|
||||
if (world->width == 0)
|
||||
return;
|
||||
|
||||
/* Vertical window bounds */
|
||||
y1 = (int32_t) sim->vip.xp.sbcount << 3;
|
||||
if (world->gy > y1)
|
||||
y1 = world->gy;
|
||||
if (y1 >= 224)
|
||||
return;
|
||||
y2 = y1 + world->height;
|
||||
if (y2 <= 0)
|
||||
return;
|
||||
my = y1 - world->my;
|
||||
if (y1 < 0)
|
||||
y1 = 0;
|
||||
if (y2 > 224)
|
||||
y2 = 224;
|
||||
|
||||
/* Process both eyes */
|
||||
for (i = 0; i < 2; i++) {
|
||||
|
||||
/* Check visibility */
|
||||
if (!(world->lron >> (i ^ 1) & 1))
|
||||
continue;
|
||||
|
||||
/* Horizontal window bounds */
|
||||
wnd.x1 = world->gx + (i == 0 ? -world->gp : world->gp);
|
||||
if (wnd.x1 >= 384)
|
||||
continue;
|
||||
wnd.x2 = wnd.x1 + world->width;
|
||||
if (wnd.x2 <= 0)
|
||||
continue;
|
||||
mx = wnd.x1 - world->mx - (i == 0 ? -world->mp : world->mp);
|
||||
if (wnd.x1 < 0)
|
||||
wnd.x1 = 0;
|
||||
if (wnd.x2 > 384)
|
||||
wnd.x2 = 384;
|
||||
|
||||
/* Process all visible lines */
|
||||
param = &sim->vip.ram[world->paramBase +
|
||||
((y1 - world->gy) << 2) + (i << 1)];
|
||||
for (y = y1; y < y2; y++, param += 2) {
|
||||
|
||||
/* Configure window bounds */
|
||||
wnd.y1 = y;
|
||||
wnd.y2 = y + 1;
|
||||
|
||||
/* Draw the background into the window */
|
||||
vipDrawBackground(sim->vip.shadow[i], &wnd, world,
|
||||
mx + busReadBuffer(param, VB_S16), my);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Draw an Affine world into shadow memory */
|
||||
static void vipDrawAffine(VB *sim, World *world) {
|
||||
Cell *cell; /* Source cell */
|
||||
uint8_t *col, *row; /* Output pixel */
|
||||
uint8_t pixel; /* Character pixel value */
|
||||
uint8_t *param; /* World parameter memory */
|
||||
int32_t px, py; /* Pixel source coordinates */
|
||||
int32_t dx, dy, mp, mx, my; /* Affine parameters */
|
||||
int32_t wx; /* Base world X coordinate */
|
||||
int32_t x1, x2, y1, y2; /* Window bounds */
|
||||
int i, x, y; /* Iterators */
|
||||
|
||||
/* No visible content */
|
||||
if (world->width == 0 || world->height == 0)
|
||||
return;
|
||||
|
||||
/* Vertical window bounds */
|
||||
y1 = (int32_t) sim->vip.xp.sbcount << 3;
|
||||
if (world->gy > y1)
|
||||
y1 = world->gy;
|
||||
if (y1 >= 224)
|
||||
return;
|
||||
y2 = y1 + world->height;
|
||||
if (y2 <= 0)
|
||||
return;
|
||||
if (y1 < 0)
|
||||
y1 = 0;
|
||||
if (y2 > 224)
|
||||
y2 = 224;
|
||||
|
||||
/* Process both eyes */
|
||||
for (i = 0; i < 2; i++) {
|
||||
|
||||
/* Check visibility */
|
||||
if (!(world->lron >> (i ^ 1) & 1))
|
||||
continue;
|
||||
|
||||
/* Horizontal window bounds */
|
||||
x1 = world->gx + (i == 0 ? -world->gp : world->gp);
|
||||
if (x1 >= 384)
|
||||
continue;
|
||||
x2 = x1 + world->width;
|
||||
if (x2 <= 0)
|
||||
continue;
|
||||
wx = x1;
|
||||
if (x1 < 0)
|
||||
x1 = 0;
|
||||
if (x2 > 384)
|
||||
x2 = 384;
|
||||
wx = x1 - wx;
|
||||
|
||||
/* Process all visible rows */
|
||||
param = &sim->vip.ram[world->paramBase | (y1 - world->gy) << 4];
|
||||
row = &sim->vip.shadow[i][x1 * 224 + y1];
|
||||
for (y = y1; y < y2; y++, param += 16, row++) {
|
||||
|
||||
/* Parse line parameters */
|
||||
mx = (int32_t) busReadBuffer(param + 0, VB_S16) << 6;
|
||||
mp = (int32_t) busReadBuffer(param + 2, VB_S16);
|
||||
my = (int32_t) busReadBuffer(param + 4, VB_S16) << 6;
|
||||
dx = (int32_t) busReadBuffer(param + 6, VB_S16);
|
||||
dy = (int32_t) busReadBuffer(param + 8, VB_S16);
|
||||
|
||||
/* Adjust left-edge parameters */
|
||||
if ((mp < 0) ^ i) {
|
||||
mx += dx * (wx - mp);
|
||||
my += dy * (wx - mp);
|
||||
} else {
|
||||
mx += dx * wx;
|
||||
my += dy * wx;
|
||||
}
|
||||
|
||||
/* Process all visible columns */
|
||||
col = row;
|
||||
for (x = x1; x < x2; x++, mx += dx, my += dy, col += 224) {
|
||||
px = (int16_t) (mx >> 9);
|
||||
py = (int16_t) (my >> 9);
|
||||
|
||||
/* Overplane */
|
||||
if (world->over && (
|
||||
px < 0 || px >= world->bgWidth ||
|
||||
py < 0 || py >= world->bgHeight
|
||||
)) cell = world->overplane;
|
||||
|
||||
/* Regular */
|
||||
else cell = &world->bg[
|
||||
(py >> 9 & world->bgMaskY) << world->bgShift |
|
||||
(px >> 9 & world->bgMaskX)
|
||||
][(py << 3 & 0xFC0) | (px >> 3 & 63)];
|
||||
|
||||
/* Sample the pixel */
|
||||
pixel = cell->pixels[(py & 7) << 3 | (px & 7)];
|
||||
if (pixel != 0)
|
||||
*col = cell->palette[pixel];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 *obj; /* Object state */
|
||||
int start; /* First object in the group */
|
||||
int stop; /* Last object in the group */
|
||||
Window wnd; /* Window bounds in pixels */
|
||||
int i, o; /* 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 */
|
||||
/* Establish the viewing window */
|
||||
wnd.x1 = 0;
|
||||
wnd.y1 = (int32_t) sim->vip.xp.sbcount << 3;
|
||||
wnd.x2 = 384;
|
||||
wnd.y2 = 224;
|
||||
|
||||
/* 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 != -1; o = o == start ? -1 : (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 */
|
||||
obj = &sim->vip.objects[o];
|
||||
for (i = 0; i < 2; i++) {
|
||||
if ((attr1 >> (15 - i) & 1) == 0) /* J*ON */
|
||||
if (!(obj->lron >> (i ^ 1) & 1))
|
||||
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 */
|
||||
} /* y */
|
||||
|
||||
} /* x */
|
||||
|
||||
} /* i */
|
||||
|
||||
vipDrawCell(sim->vip.shadow[i], &wnd, &obj->cell,
|
||||
obj->jx + (i == 0 ? -obj->jp : obj->jp), obj->jy);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Draw a background world into shadow memory */
|
||||
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 */
|
||||
|
||||
/* World attributes */
|
||||
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 */
|
||||
|
||||
/* Working variables */
|
||||
ettr.bp = wttr.mp = wttr.mx = wttr.my = wttr.params = 0;
|
||||
|
||||
/* Parse attributes */
|
||||
if (vipParseWorld(sim, world, bits, &wttr))
|
||||
return; /* Window not in frame */
|
||||
|
||||
/* Draw the world */
|
||||
for (i = 0; i < 2; i++) {
|
||||
|
||||
/* World is not visible */
|
||||
if ((bits & (int32_t) 0x8000 >> i) == 0) /* LON, RON */
|
||||
continue;
|
||||
|
||||
/* Process attributes */
|
||||
if (vipParseEye(&wttr, &ettr, i))
|
||||
continue;
|
||||
|
||||
/* Draw all rows */
|
||||
for (y = wttr.top, param = wttr.params; y <= wttr.bottom; y++) {
|
||||
|
||||
/* Parse line parameters */
|
||||
hofst = 0;
|
||||
mp = wttr.mp;
|
||||
mx = wttr.mx;
|
||||
my = wttr.my;
|
||||
switch (wttr.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);
|
||||
mp = ettr.left - ettr.wx - ((mp < 0) ^ i ? mp : 0);
|
||||
mx = ((int32_t) (int16_t) vipParam(param ) << 13) + dx*mp;
|
||||
my = ((int32_t) (int16_t) vipParam(param|4) << 13) + dy*mp;
|
||||
param += 16;
|
||||
}
|
||||
|
||||
/* Select output in shadow memory */
|
||||
dest = &sim->vip.shadow[i][ettr.left * 224 + y];
|
||||
|
||||
/* Draw all columns */
|
||||
for (x = ettr.left; x <= ettr.right; x++, dest += 224) {
|
||||
|
||||
/* Locate the pixel in the background */
|
||||
if (wttr.bgm != 2) { /* Normal, H-bias */
|
||||
cx = x - ettr.wx + mx + ettr.bp + hofst;
|
||||
cy = y - wttr.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 > wttr.bgw ||
|
||||
by < 0 || by > wttr.bgh
|
||||
) {
|
||||
if (wttr.over == NULL) {
|
||||
bx &= wttr.bgw;
|
||||
by &= wttr.bgh;
|
||||
} else cell = wttr.over;
|
||||
}
|
||||
|
||||
/* Locate the cell in the BG map */
|
||||
if (cell == NULL) {
|
||||
cell = &sim->vip.ram[
|
||||
0x20000 | ((
|
||||
wttr.base +
|
||||
wttr.bg[by << wttr.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 */
|
||||
World *world; /* World attribute source in host memory */
|
||||
int32_t x, y; /* Iterators */
|
||||
|
||||
/* Erase all pixels */
|
||||
|
@ -943,25 +1135,25 @@ static void vipRender(VB *sim) {
|
|||
|
||||
/* 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);
|
||||
for (x = 31; x >= 0; x--) {
|
||||
world = &sim->vip.worlds[x];
|
||||
|
||||
/* Non-graphical world */
|
||||
if (attr & 0x0040) /* END */
|
||||
if (world->end)
|
||||
break; /* Control world */
|
||||
if ((attr & 0xC000) == 0) /* LON, RON */
|
||||
if (world->lron == 0)
|
||||
continue; /* Dummy world */
|
||||
|
||||
/* Object world */
|
||||
if ((attr >> 12 & 3) == 3) {
|
||||
/* Processing by world type */
|
||||
switch (world->bgm) {
|
||||
case 0: vipDrawNormal(sim, world); break;
|
||||
case 1: vipDrawHBias (sim, world); break;
|
||||
case 2: vipDrawAffine(sim, world); break;
|
||||
case 3:
|
||||
vipDrawObjects(sim, group);
|
||||
group = (group - 1) & 3;
|
||||
}
|
||||
|
||||
/* Background world */
|
||||
else if (attr & 0xC000) /* LON, RON */
|
||||
vipDrawWorld(sim, world, attr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1134,6 +1326,10 @@ static void vipRead(VB *sim, uint32_t address, int type, int32_t *value) {
|
|||
|
||||
/* Simulate a hardware reset */
|
||||
static void vipReset(VB *sim) {
|
||||
Cell *cell; /* BG map cell state */
|
||||
Character *chr; /* Character state */
|
||||
Object *obj; /* Object state */
|
||||
World *world; /* World state */
|
||||
int x, y; /* Iterators */
|
||||
|
||||
/* Normal */
|
||||
|
@ -1159,6 +1355,53 @@ static void vipReset(VB *sim) {
|
|||
sim->vip.jplt[x][y] = 0;
|
||||
}
|
||||
}
|
||||
for (x = 0; x < 0x10000; x++) {
|
||||
cell = &sim->vip.cells[x];
|
||||
cell->palette = sim->vip.gplt[0];
|
||||
cell->pixels = sim->vip.characters[0][0];
|
||||
}
|
||||
for (x = 0; x < 2048; x++) {
|
||||
chr = &sim->vip.characters[x];
|
||||
for (y = 0; y < 64; y++)
|
||||
(*chr)[0][y] = (*chr)[1][y] = (*chr)[2][y] = (*chr)[3][y] = 0;
|
||||
}
|
||||
for (x = 0; x < 32; x++) {
|
||||
world = &sim->vip.worlds[x];
|
||||
world->bgm = 0;
|
||||
world->end = 0;
|
||||
world->gp = 0;
|
||||
world->gx = 0;
|
||||
world->gy = 0;
|
||||
world->mx = 0;
|
||||
world->mp = 0;
|
||||
world->my = 0;
|
||||
world->over = 0;
|
||||
world->overplane = &sim->vip.cells[0];
|
||||
for (y = 0; y < 64; y++)
|
||||
world->bg[0] = world->overplane;
|
||||
world->bgHeight = 512;
|
||||
world->bgMaskX = 0;
|
||||
world->bgMaskY = 0;
|
||||
world->bgShift = 0;
|
||||
world->bgWidth = 512;
|
||||
world->hAffine = 0;
|
||||
world->hNaffine = 0;
|
||||
world->height = 8;
|
||||
world->lron = 0;
|
||||
world->paramBase = 0;
|
||||
world->wAffine = 0;
|
||||
world->wNaffine = 0;
|
||||
world->width = 0;
|
||||
}
|
||||
for (x = 0; x < 1024; x++) {
|
||||
obj = &sim->vip.objects[x];
|
||||
obj->cell.palette = sim->vip.jplt[0];
|
||||
obj->cell.pixels = sim->vip.characters[0][0];
|
||||
obj->jp = 0;
|
||||
obj->jx = 0;
|
||||
obj->jy = 0;
|
||||
obj->lron = 0;
|
||||
}
|
||||
|
||||
/* Display processor extra (the hardware does not do this) */
|
||||
sim->vip.dp.fclk = 0;
|
||||
|
@ -1206,9 +1449,28 @@ static void vipWrite(VB*sim,uint32_t address,int type,int32_t value,int debug){
|
|||
address &= 0x0007FFFF;
|
||||
|
||||
/* RAM */
|
||||
if (address < 0x40000)
|
||||
if (address < 0x40000) {
|
||||
busWriteBuffer(&sim->vip.ram[address], type, value);
|
||||
|
||||
/* Character memory */
|
||||
if ((address & 0x26000) == 0x06000) {
|
||||
address = (address & 0x18000) >> 2 | (address & 0x01FFF);
|
||||
vipWriteChr(sim, address, type, value);
|
||||
}
|
||||
|
||||
/* BG map memory */
|
||||
if (address >= 0x20000)
|
||||
vipWriteCell(sim, address & 0x1FFFF, type, value);
|
||||
|
||||
/* World memory */
|
||||
if ((address & 0x3FC00) == 0x3D800)
|
||||
vipWriteWorld(sim, address &0x003FF, type, value);
|
||||
|
||||
/* Object memory */
|
||||
if (address >= 0x3E000)
|
||||
vipWriteObject(sim, address & 0x01FFF, type, value);
|
||||
}
|
||||
|
||||
/* Unmapped */
|
||||
else if (address < 0x5E000)
|
||||
;
|
||||
|
@ -1223,6 +1485,7 @@ static void vipWrite(VB*sim,uint32_t address,int type,int32_t value,int debug){
|
|||
|
||||
/* Mirrors of character memory */
|
||||
else {
|
||||
vipWriteChr(sim, address & 0x07FFF, type, value);
|
||||
address = 0x06000 | (address << 2 & 0x18000) | (address & 0x01FFF);
|
||||
busWriteBuffer(&sim->vip.ram[address], type, value);
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ static void vsuNextFreqMod(VB *sim, Channel *chan) {
|
|||
}
|
||||
|
||||
/* Process one channel */
|
||||
static void vsuEmulateChannel(VB *sim, Channel *chan, uint32_t clocks) {
|
||||
static void vsuEmulateChannel(VB *sim, Channel *chan) {
|
||||
uint32_t bit; /* Pseudorandom bit */
|
||||
|
||||
/* Automatic shutoff */
|
||||
|
@ -343,7 +343,7 @@ static void vsuEmulate(VB *sim, uint32_t clocks) {
|
|||
|
||||
/* Update channel state */
|
||||
if (chan->until == 0)
|
||||
vsuEmulateChannel(sim, chan, chan->until);
|
||||
vsuEmulateChannel(sim, chan);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue