diff --git a/core/vb.c b/core/vb.c index d424a00..042c14c 100644 --- a/core/vb.c +++ b/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 */ - uint8_t ram[0x40000]; /* Video memory */ + 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 */ diff --git a/core/vip.c b/core/vip.c index 1cf834b..f2e96f8 100644 --- a/core/vip.c +++ b/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) { - vipDrawObjects(sim, group); - group = (group - 1) & 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,7 +1326,11 @@ static void vipRead(VB *sim, uint32_t address, int type, int32_t *value) { /* Simulate a hardware reset */ static void vipReset(VB *sim) { - int x, y; /* Iterators */ + Cell *cell; /* BG map cell state */ + Character *chr; /* Character state */ + Object *obj; /* Object state */ + World *world; /* World state */ + int x, y; /* Iterators */ /* Normal */ sim->vip.intenb = 0x0000; @@ -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); } diff --git a/core/vsu.c b/core/vsu.c index 10c4777..9e4b23a 100644 --- a/core/vsu.c +++ b/core/vsu.c @@ -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); } }