Entry 762
STIC video chip emulation
Submitted by JZbiciak
on May 10, 2008 at 1:15 a.m.
Language: C. Code size: 99.2 KB.
/* ======================================================================== */ /* STIC.C -- New, complete, hopefully fast STIC implementation. */ /* ======================================================================== */ #include "config.h" #include "periph/periph.h" #include "mem/mem.h" #include "cp1600/cp1600.h" #include "demo/demo.h" #include "gfx/gfx.h" #include "stic.h" #include "speed/speed.h" #include "debug/debug_.h" static const char rcs_id[] UNUSED = "$Id$"; LOCAL void stic_draw_fgbg(stic_t *stic); LOCAL void stic_draw_cstk(stic_t *stic); #ifdef __GNUC__ #define ALIGN __attribute__((aligned(128))) #else #define ALIGN #endif /* ======================================================================== */ /* STIC Register Masks */ /* Only certain bits in each STIC register are writeable. The bits that */ /* are not implemented return a fixed pattern of 0s and 1s. The table */ /* below encodes this information in the form of an "AND / OR" mask pair. */ /* The data is first ANDed with the AND mask. This mask effectively */ /* indicates the implemented bits. The data is then ORed with the OR */ /* mask. This second mask effectively indicates which of the bits always */ /* read as 1. */ /* ======================================================================== */ struct stic_reg_mask_t { uint_32 and_mask; uint_32 or_mask; }; LOCAL const struct stic_reg_mask_t stic_reg_mask[0x40] ALIGN = { /* MOB X Registers 0x00 - 0x07 */ {0x07FF,0x3800}, {0x07FF,0x3800}, {0x07FF,0x3800}, {0x07FF,0x3800}, {0x07FF,0x3800}, {0x07FF,0x3800}, {0x07FF,0x3800}, {0x07FF,0x3800}, /* MOB Y Registers 0x08 - 0x0F */ {0x0FFF,0x3000}, {0x0FFF,0x3000}, {0x0FFF,0x3000}, {0x0FFF,0x3000}, {0x0FFF,0x3000}, {0x0FFF,0x3000}, {0x0FFF,0x3000}, {0x0FFF,0x3000}, /* MOB A Registers 0x10 - 0x17 */ {0x3FFF,0x0000}, {0x3FFF,0x0000}, {0x3FFF,0x0000}, {0x3FFF,0x0000}, {0x3FFF,0x0000}, {0x3FFF,0x0000}, {0x3FFF,0x0000}, {0x3FFF,0x0000}, /* MOB C Registers 0x18 - 0x1F */ {0x03FE,0x3C00}, {0x03FD,0x3C00}, {0x03FB,0x3C00}, {0x03F7,0x3C00}, {0x03EF,0x3C00}, {0x03DF,0x3C00}, {0x03BF,0x3C00}, {0x037F,0x3C00}, /* Display enable, Mode select 0x20 - 0x21 */ {0x0000,0x3FFF}, {0x0000,0x3FFF}, /* Unimplemented registers 0x22 - 0x27 */ {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, /* Color stack, border color 0x28 - 0x2C */ {0x000F,0x3FF0}, {0x000F,0x3FF0}, {0x000F,0x3FF0}, {0x000F,0x3FF0}, {0x000F,0x3FF0}, /* Unimplemented registers 0x2D - 0x2F */ {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, /* Horiz delay, vertical delay, border extension 0x30 - 0x32 */ {0x0007,0x3FF8}, {0x0007,0x3FF8}, {0x0003,0x3FFC}, /* Unimplemented registers 0x33 - 0x3F */ {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF}, {0x0000,0x3FFF} }; /* ======================================================================== */ /* MOB height secret decoder ring. */ /* ======================================================================== */ LOCAL const int stic_mob_hgt[8] = { 8, 16, 16, 32, 32, 64, 64, 128 }; /* ======================================================================== */ /* STIC Color Nibble Masks -- For generating packed-nibble pixels. */ /* ======================================================================== */ LOCAL const uint_32 stic_color_mask[16] ALIGN = { 0x00000000, 0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, 0x66666666, 0x77777777, 0x88888888, 0x99999999, 0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD, 0xEEEEEEEE, 0xFFFFFFFF }; /* ======================================================================== */ /* STIC Color Mask and Bit Manipulation Lookup Tables */ /* -- b2n expands 8 bits to 8 nibbles. */ /* -- b2n_d expands 8 bits to 4 nibbles, pixel-doubling as it goes. */ /* -- b2n_r expands 8 bits to 4 nibbles, reversing bit order as it goes. */ /* -- b2n_rd expands 8 bits to 4 nibbles, reversing and pixel-doubleling. */ /* -- n2b expands 2 nibbles to 2 bytes. */ /* -- bit_r reverses the bit order in an 8-bit byte. */ /* -- bit_d doubles the bits in an 8-bit byte to a 16-bit int. */ /* -- bit_rd doubles the bits and reverses them. */ /* These are computed at runtime for now. */ /* ======================================================================== */ LOCAL uint_16 stic_n2b [256] ALIGN; LOCAL uint_32 stic_b2n [256] ALIGN, stic_b2n_r [256] ALIGN; LOCAL uint_32 stic_b2n_d[16] ALIGN, stic_b2n_rd[16] ALIGN; LOCAL uint_16 stic_bit [256] ALIGN, stic_bit_r [256] ALIGN; LOCAL uint_16 stic_bit_d[256] ALIGN, stic_bit_rd[256] ALIGN; /* ======================================================================== */ /* STIC_CTRL_RD -- Read from a STIC control register (addr <= 0x3F) */ /* ======================================================================== */ uint_32 stic_ctrl_rd(periph_t *per, periph_t *req, uint_32 addr, uint_32 data) { stic_t *stic = (stic_t *)per->parent; uint_64 access_time = req && req->req ? req->req->now + 4 : 0; (void)data; #if 1 /* -------------------------------------------------------------------- */ /* Is this access after the Bus-Copy -> Bus-Isolation transition */ /* or before the Bus-Isolation -> Bus-Copy transition? */ /* -------------------------------------------------------------------- */ if (access_time > stic->stic_accessible || access_time < stic->req_bus->intak) { /* ---------------------------------------------------------------- */ /* Yes: Return garbage. */ /* ---------------------------------------------------------------- */ return addr < 0x80 ? 0x000E & addr : 0xFFFF; } #endif /* -------------------------------------------------------------------- */ /* If reading location 0x21, put the display into Color-Stack mode. */ /* -------------------------------------------------------------------- */ if ((addr & 0x7F) == 0x0021) { if (stic->mode != 0) stic->bt_dirty = 1; stic->mode = 0; //jzp_printf("COLORSTACK\n"); stic->upd = stic_draw_cstk; } /* -------------------------------------------------------------------- */ /* If we're accessing a STIC CR alias, just return 0xFFFF. */ /* -------------------------------------------------------------------- */ if (addr >= 0x4000) return 0xFFFF; /* -------------------------------------------------------------------- */ /* If we're reading 0x40-0x7F, just sample GRAM and return. */ /* -------------------------------------------------------------------- */ if (addr >= 0x0040) return stic->gmem[addr + 0x800] & 0xFF; /* -------------------------------------------------------------------- */ /* Now just return the raw value from our internal register file, */ /* appropriately conditioned by the read/write masks. */ /* -------------------------------------------------------------------- */ return (stic->raw[addr] & stic_reg_mask[addr].and_mask) | stic_reg_mask[addr].or_mask; } /* ======================================================================== */ /* STIC_CTRL_PEEK -- Like read, except w/out side effects or restrictions */ /* ======================================================================== */ uint_32 stic_ctrl_peek(periph_t *per, periph_t *req, uint_32 addr, uint_32 data) { stic_t *stic = (stic_t *)per->parent; (void)req; (void)data; if (addr > 0x40) return 0xFFFF; /* -------------------------------------------------------------------- */ /* Just return the raw value from our internal register file, */ /* appropriately conditioned by the read/write masks. */ /* -------------------------------------------------------------------- */ return (stic->raw[addr] & stic_reg_mask[addr].and_mask) | stic_reg_mask[addr].or_mask; } /* ======================================================================== */ /* STIC_CTRL_WR -- Write to a STIC control register (addr <= 0x3F) */ /* ======================================================================== */ void stic_ctrl_wr(periph_t *per, periph_t *req, uint_32 addr, uint_32 data) { stic_t *stic = (stic_t *)per->parent; uint_64 access_time = req && req->req ? req->req->now + 4 : 0; uint_32 old = 0; addr &= 0x7F; /* -------------------------------------------------------------------- */ /* Ignore writes to the strange GROM visibility window at $40 and up. */ /* -------------------------------------------------------------------- */ if (addr >= 0x40) return; #if 1 /* -------------------------------------------------------------------- */ /* Is this access after the Bus-Copy -> Bus-Isolation transition */ /* or before the Bus-Isolation -> Bus-Copy transition? */ /* -------------------------------------------------------------------- */ if (access_time > stic->stic_accessible || access_time < stic->req_bus->intak) { /* ---------------------------------------------------------------- */ /* Yes: Drop the write. */ /* ---------------------------------------------------------------- */ //jzp_printf("access_time = %llu accessible = %llu intak = %llu\n", access_time, stic->stic_accessible, stic->req_bus->intak); return; } #endif /* -------------------------------------------------------------------- */ /* If writing location 0x20, enable the display. */ /* -------------------------------------------------------------------- */ if (addr == 0x0020) { //jzp_printf("got ve post, stic->phase = %d\n", stic->phase); stic->ve_post = 1; } /* -------------------------------------------------------------------- */ /* If writing location 0x21, put the display into FGBG mode. */ /* -------------------------------------------------------------------- */ if (addr == 0x0021) { if (stic->mode != 1) stic->bt_dirty = 1; stic->mode = 1; //jzp_printf("FOREGROUND/BACKGROUND\n"); stic->upd = stic_draw_fgbg; } /* -------------------------------------------------------------------- */ /* Now capture the write and store it in its raw, encoded form (after */ /* adjusting for the and/or masks). If the old != new, mark the frame */ /* as 'dirty'. */ /* -------------------------------------------------------------------- */ old = stic->raw[addr]; data &= stic_reg_mask[addr].and_mask; data |= stic_reg_mask[addr].or_mask; stic->raw[addr] = data; if (old != data) { stic->bt_dirty = 1; if (addr == 0x2C) { gfx_set_bord(stic->gfx, data & 0xF); } } } /* ======================================================================== */ /* STIC_RESET -- Reset state internal to the STIC */ /* ======================================================================== */ void stic_reset(periph_t *per) { stic_t *stic = (stic_t*)per->parent; int a; /* -------------------------------------------------------------------- */ /* Fill all the STIC registers with 1. */ /* -------------------------------------------------------------------- */ memset(stic->raw, 0, sizeof(stic->raw)); /* first, zero it */ /* then fill it with 1s via ctrl writes */ for (a = 0x00; a < 0x40; a++) { if (a != 0x20) stic_ctrl_wr(per, per->parent, a, 0xFFFF); } /* -------------------------------------------------------------------- */ /* Resync the internal state machine. */ /* -------------------------------------------------------------------- */ stic->fifo_ptr = 0; stic->stic_accessible = 0; stic->gmem_accessible = 0; stic->req_bus->intak = ~0ULL; stic->req_bus->next_busrq = ~0ULL; stic->req_bus->next_intrq = ~0ULL; /* stic->stic_cr.min_tick = 1; stic->stic_cr.max_tick = phase_len; stic->next_phase += phase_len; stic->phase = new_phase; */ } /* ======================================================================== */ /* STIC_BTAB_WR -- Capture writes to the background cards. */ /* ======================================================================== */ void stic_btab_wr(periph_t *per, periph_t *req, uint_32 addr, uint_32 data) { stic_t *stic = (stic_t*)per->parent; /* -------------------------------------------------------------------- */ /* Note -- this architecture has problems if I want to accurately */ /* model the incremental sampling of BACKTAB that the STIC performs */ /* throughout display time. To do it correctly w/ this setup, I need */ /* to double-buffer here, which shouldn't be too bad. What's 240 */ /* words, really? */ /* -------------------------------------------------------------------- */ (void)req; /* this will become un-ignored later. */ data &= 0x3FFF; /* only lower 14 bits seen by STIC. */ if (addr < 0xF0) { if (data != stic->btab_sr[addr]) stic->bt_dirty = 1; stic->btab_sr[addr] = data; } } /* ======================================================================== */ /* STIC_GMEM_WR -- Capture writes to the Graphics RAM. */ /* ======================================================================== */ void stic_gmem_wr(periph_t *per, periph_t *req, uint_32 addr, uint_32 data) { stic_t *stic = (stic_t*)per->parent; uint_64 access_time = req && req->req ? req->req->now + 4 : 0; #if 1 /* -------------------------------------------------------------------- */ /* Drop the write if in Bus Isolation mode. */ /* -------------------------------------------------------------------- */ if (access_time > stic->gmem_accessible || access_time < stic->req_bus->intak) { return; } #endif /* -------------------------------------------------------------------- */ /* We're mapped into the entire 4K address space for GRAM/GROM. Drop */ /* all writes for GROM addresses. */ /* -------------------------------------------------------------------- */ if ((addr & 0x0FFF) < 0x0800) return; /* -------------------------------------------------------------------- */ /* Mask according to what the GRAM will actually see address and data */ /* wise. As a result, this should even correctly work for the many */ /* GRAM write-aliases. */ /* -------------------------------------------------------------------- */ addr = (addr & 0x01FF) + 0x0800; data &= 0x00FF; /* Only the lower 8 bits of a GRAM write matter. */ if (data != stic->gmem[addr]) stic->gr_dirty = 1; stic->gmem[addr] = data; } /* ======================================================================== */ /* STIC_GMEM_POKE -- Same as GMEM_WR, except ignores bus isolation. */ /* ======================================================================== */ void stic_gmem_poke(periph_t *per, periph_t *req, uint_32 addr, uint_32 data) { stic_t *stic = (stic_t*)per->parent; (void)req; /* -------------------------------------------------------------------- */ /* Don't allow pokes to GROM. */ /* -------------------------------------------------------------------- */ if ((addr & 0x0FFF) < 0x0800) return; /* -------------------------------------------------------------------- */ /* Mask according to what the GRAM will actually see address and data */ /* wise. As a result, this should even correctly work for the many */ /* GRAM write-aliases. */ /* -------------------------------------------------------------------- */ addr = (addr & 0x01FF) + 0x0800; data &= 0x00FF; /* Only the lower 8 bits of a GRAM write matter. */ if (data != stic->gmem[addr]) stic->gr_dirty = 1; stic->gmem[addr] = data; } /* ======================================================================== */ /* STIC_GMEM_RD -- Read values out of GRAM, GROM, taking into account */ /* when GRAM/GROM are visible. */ /* ======================================================================== */ uint_32 stic_gmem_rd(periph_t *per, periph_t *req, uint_32 addr, uint_32 data) { stic_t *stic = (stic_t*)per->parent; uint_64 access_time = req && req->req ? req->req->now + 4 : 0; (void)data; #if 1 /* -------------------------------------------------------------------- */ /* Disallow access to graphics memory if in Bus Isolation. System */ /* Memory will return $FFFF for these reads. */ /* -------------------------------------------------------------------- */ if (access_time > stic->gmem_accessible || access_time < stic->req_bus->intak) { //jzp_printf("access_time = %llu gmem_accessible = %llu intak = %llu\n", access_time, stic->gmem_accessible, stic->req_bus->intak); return 0x3FFF & addr; } #endif /* -------------------------------------------------------------------- */ /* If this is a GRAM address, adjust it for aliases. */ /* -------------------------------------------------------------------- */ if (addr & 0x0800) addr = (addr & 0x09FF); /* -------------------------------------------------------------------- */ /* Return the data. */ /* -------------------------------------------------------------------- */ return stic->gmem[addr] & 0xFF; } /* ======================================================================== */ /* STIC_GMEM_PEEK -- Like gmem_rd, except always works. */ /* ======================================================================== */ uint_32 stic_gmem_peek(periph_t *per, periph_t *req, uint_32 addr, uint_32 data) { stic_t *stic = (stic_t*)per->parent; (void)req; (void)data; /* -------------------------------------------------------------------- */ /* If this is a GRAM address, adjust it for aliases. */ /* -------------------------------------------------------------------- */ if (addr & 0x0800) addr = (addr & 0x09FF); /* -------------------------------------------------------------------- */ /* Return the data. */ /* -------------------------------------------------------------------- */ return stic->gmem[addr] & 0xFF; } /* ======================================================================== */ /* STIC_INIT -- Initialize this ugly ass peripheral. Booyah! */ /* ======================================================================== */ int stic_init ( stic_t *RESTRICT stic, uint_16 *RESTRICT grom_img, req_bus_t *RESTRICT req_bus, gfx_t *RESTRICT gfx, demo_t *RESTRICT demo ) { int i, j; /* -------------------------------------------------------------------- */ /* First, zero out the STIC structure to get rid of anything that */ /* might be dangling. */ /* -------------------------------------------------------------------- */ memset((void*)stic, 0, sizeof(stic_t)); /* -------------------------------------------------------------------- */ /* Set our graphics subsystem pointers. */ /* -------------------------------------------------------------------- */ stic->gfx = gfx; stic->disp = gfx->vid; /* -------------------------------------------------------------------- */ /* Register the demo recorder, if there is one. */ /* -------------------------------------------------------------------- */ stic->demo = demo; /* -------------------------------------------------------------------- */ /* Initialize the bit/nibble expansion tables. */ /* -------------------------------------------------------------------- */ /* Calculate bit-to-nibble masks b2n, b2n_r */ for (i = 0; i < 256; i++) { uint_32 b2n, b2n_r; b2n = b2n_r = 0; for (j = 0; j < 8; j++) if ((i >> j) & 1) { b2n |= 0xF << (j * 4); b2n_r |= 0xF << (28 - j*4); } stic_b2n [i] = b2n; stic_b2n_r[i] = b2n_r; } /* Calculate bit-to-byte masks b2n_d, b2n_rd */ for (i = 0; i < 16; i++) { uint_32 b2n_d, b2n_rd; b2n_d = b2n_rd = 0; for (j = 0; j < 4; j++) if ((i >> j) & 1) { b2n_d |= 0xFF << (j * 8); b2n_rd |= 0xFF << (24 - j*8); } stic_b2n_d [i] = b2n_d; stic_b2n_rd[i] = b2n_rd; } /* Calculate n2b */ #ifdef BYTE_LE for (i = 0; i < 16; i++) for (j = 0; j < 16; j++) stic_n2b[16*j + i] = 256*i + j; #else for (i = 0; i < 16; i++) for (j = 0; j < 16; j++) stic_n2b[16*j + i] = i + 256*j; #endif /* Calculate bit_r 8-bit bit-reverse table */ for (i = 0; i < 256; i++) { uint_32 bit_r = i; bit_r = ((bit_r & 0xAA) >> 1) | ((bit_r & 0x55) << 1); bit_r = ((bit_r & 0xCC) >> 2) | ((bit_r & 0x33) << 2); bit_r = ((bit_r & 0xF0) >> 4) | ((bit_r & 0x0F) << 4); stic_bit [i] = i << 8; stic_bit_r[i] = bit_r << 8; } /* Calculate bit-doubling tables bit_d, bit_rd */ for (i = 0; i < 256; i++) { uint_32 bit_d = i, bit_rd = stic_bit_r[i] >> 8; for (j = 7; j > 0; j--) { bit_d += bit_d & (~0U << j); bit_rd += bit_rd & (~0U << j); } stic_bit_d [i] = 3 * bit_d; stic_bit_rd[i] = 3 * bit_rd; } /* -------------------------------------------------------------------- */ /* Initialize graphics memory. */ /* -------------------------------------------------------------------- */ for (i = 0; i < 2048; i++) stic->gmem[i] = grom_img[i]; /* -------------------------------------------------------------------- */ /* Set up our internal flags. */ /* -------------------------------------------------------------------- */ stic->phase = 0; stic->mode = 0; stic->upd = stic_draw_cstk; /* -------------------------------------------------------------------- */ /* Record our INTRQ/BUSRQ request bus pointer. Usually points us to */ /* cp1600->req_bus. */ /* -------------------------------------------------------------------- */ stic->req_bus = req_bus; /* -------------------------------------------------------------------- */ /* Now, set up our peripheral functions for the main STIC peripheral. */ /* -------------------------------------------------------------------- */ stic->stic_cr.read = stic_ctrl_rd; stic->stic_cr.write = stic_ctrl_wr; stic->stic_cr.peek = stic_ctrl_peek; stic->stic_cr.poke = stic_ctrl_wr; stic->stic_cr.tick = stic_tick; stic->stic_cr.reset = stic_reset; stic->stic_cr.min_tick = 57; /* to get started. stic_tick will reset. */ stic->stic_cr.max_tick = 57; stic->stic_cr.addr_base = 0x00000000; stic->stic_cr.addr_mask = 0x0000FFFF; stic->stic_cr.parent = (void*) stic; stic->phase = 0; stic->next_phase = 57; /* -------------------------------------------------------------------- */ /* Lastly, set up the 'snooping' STIC peripherals. */ /* -------------------------------------------------------------------- */ stic->snoop_btab.read = NULL; stic->snoop_btab.write = stic_btab_wr; stic->snoop_btab.peek = NULL; stic->snoop_btab.poke = stic_btab_wr; stic->snoop_btab.tick = NULL; stic->snoop_btab.min_tick = ~0U; stic->snoop_btab.max_tick = ~0U; stic->snoop_btab.addr_base = 0x00000200; stic->snoop_btab.addr_mask = 0x000000FF; stic->snoop_btab.parent = (void*) stic; stic->snoop_gram.read = stic_gmem_rd; stic->snoop_gram.write = stic_gmem_wr; stic->snoop_gram.peek = stic_gmem_peek; stic->snoop_gram.poke = stic_gmem_poke; stic->snoop_gram.tick = NULL; stic->snoop_gram.min_tick = ~0U; stic->snoop_gram.max_tick = ~0U; stic->snoop_gram.addr_base = 0x00003000; stic->snoop_gram.addr_mask = 0x0000FFFF; stic->snoop_gram.parent = (void*) stic; return 0; } /* ======================================================================== */ /* STIC BACKTAB display list architecture: */ /* */ /* There are two main BACKTAB renderers for the STIC: DRAW_CSTK and */ /* DRAW_FGBG. These correspond to the two primary STIC modes. Each of */ /* these renderers produce a pair of display lists that feed into the */ /* rest of the STIC display computation. */ /* */ /* The two lists correspond to the colors of the displayed pixels for */ /* each card, and the bitmap of "foreground vs. background" pixels for */ /* each card. What's important to note about these lists is that they */ /* are in card order, and are not rasterized onto the 160x96 background */ /* yet. */ /* */ /* The display color list stores a list of 32-bit ints, each containing */ /* 8 4-bit pixels packed as nibbles within each word. By packing pixels */ /* in this manner, pixel color computation becomes exceedingly efficient. */ /* Indeed, it's just a couple ANDs and an OR to merge foreground and */ /* background colors for an entire 8-pixel row of a card. */ /* */ /* The foreground bitmap list stores a list of bytes, each containing */ /* bits indicating which pixels are foreground and which pixels are */ /* background. A '1' bit in this bitmap indicates a foreground pixel. */ /* This secondary bitmap will be used to compute MOB collisions later, */ /* using nice simple bitwise ANDs to detect coincidence. */ /* */ /* The two lists are stored as lists of cards to limit the amount of */ /* addressing work that the display computation loops must do. By */ /* tightly limiting the focus of these loops and by constructing a nice */ /* linear output pattern, this code should be fairly efficient. */ /* */ /* In addition to the two display lists, the BACKTAB renderers produce */ /* a third, short list containing the "last background color" associated */ /* with each row of cards. This information will be used to render the */ /* pixels to the left and above the BACKTAB image later in the engine. */ /* */ /* These lists will feed into a unified render engine which will merge */ /* the MOB images and the BACKTAB image into the final frame buffer. */ /* ======================================================================== */ /* ======================================================================== */ /* STIC_DO_MOB -- Render a given MOB. */ /* ======================================================================== */ LOCAL void stic_do_mob(stic_t *stic, int mob) { int y, yy, y_flip, gr_idx, y_res = 8; uint_32 x_reg, y_reg, a_reg; uint_32 fg_clr, fg_msk; uint_16 * RESTRICT bit_remap; uint_32 *const RESTRICT mob_img = stic->mob_img; uint_16 *const RESTRICT mob_bmp = stic->mob_bmp[mob]; /* -------------------------------------------------------------------- */ /* Grab the MOB's information. */ /* -------------------------------------------------------------------- */ x_reg = stic->raw[mob + 0x00]; y_reg = stic->raw[mob + 0x08]; a_reg = stic->raw[mob + 0x10]; /* -------------------------------------------------------------------- */ /* Decode the various control bits from the MOB's registers. */ /* -------------------------------------------------------------------- */ fg_clr = ((a_reg >> 9) & 0x08) | (a_reg & 0x07); fg_msk = stic_color_mask[fg_clr]; if (x_reg & 0x0400) /* --- double width --- */ { /* x-flip */ /* normal */ bit_remap = (y_reg & 0x0400) ? stic_bit_rd : stic_bit_d; } else /* --- single width --- */ { /* x-flip */ /* normal */ bit_remap = (y_reg & 0x0400) ? stic_bit_r : stic_bit; } if (y_reg & 0x80) y_res = 16; y_flip = y_reg & 0x0800 ? y_res - 1 : 0; /* y-flip vs. normal */ /* -------------------------------------------------------------------- */ /* Decode the GROM/GRAM index. Bits 9 and 10 are ignored if the card */ /* is from GRAM, or if the display is in Foreground/Background mode. */ /* -------------------------------------------------------------------- */ gr_idx = a_reg & 0xFF8; if (stic->mode == 1 || (gr_idx & 0x800)) gr_idx &= 0x9F8; if (y_res == 16) gr_idx &= 0xFF0; /* -------------------------------------------------------------------- */ /* Generate the MOB's bitmap from its color and GRAM/GROM image. */ /* Each MOB is generated to a 16x16 bitmap, regardless of its actual */ /* size. We handle x-flip, y-flip and x-size here. We handle y-size */ /* later when compositing the MOBs into a single bitmap. */ /* -------------------------------------------------------------------- */ for (y = 0; y < y_res; y++) { uint_32 row = stic->gmem[gr_idx + y]; uint_16 bit = bit_remap[row]; uint_32 lpix = stic_b2n[bit >> 8 ] & fg_msk; uint_32 rpix = stic_b2n[bit & 0xFF] & fg_msk; yy = y ^ y_flip; mob_img[2*yy + 0] = lpix; mob_img[2*yy + 1] = rpix; mob_bmp[yy] = bit; } for (y = y_res; y < 16; y++) { mob_img[2*y + 0] = 0; mob_img[2*y + 1] = 0; mob_bmp[y] = 0; } } /* ======================================================================== */ /* STIC_DRAW_MOBS -- Draw all 8 MOBs onto the 256x96 bitplane. */ /* ======================================================================== */ LOCAL void stic_draw_mobs(stic_t *stic) { int i, j; uint_32 *const RESTRICT mpl_img = stic->mpl_img; uint_32 *const RESTRICT mpl_pri = stic->mpl_pri; uint_32 *const RESTRICT mpl_vsb = stic->mpl_vsb; uint_32 *const RESTRICT mob_img = stic->mob_img; /* -------------------------------------------------------------------- */ /* First, clear the MOB plane. We only need to clear the visibility */ /* and priority bits, not the color plane. This is because we ignore */ /* the contents of the color plane wherever the visibility bit is 0. */ /* -------------------------------------------------------------------- */ memset(mpl_pri, 0, 192 * 224 / 8); memset(mpl_vsb, 0, 192 * 224 / 8); /* -------------------------------------------------------------------- */ /* Generate the bitmaps for the 8 MOBs if they're active, and put */ /* together the MOB plane. */ /* -------------------------------------------------------------------- */ for (i = 7; i >= 0; i--) { uint_32 x_reg = stic->raw[i + 0x00]; uint_32 y_reg = stic->raw[i + 0x08]; uint_32 x_pos = x_reg & 0xFF; uint_32 y_pos = (y_reg & 0x7F) * 2; uint_32 prio = (stic->raw[i + 0x10] & 0x2000) ? ~0U : 0; uint_32 visb = x_reg & 0x200; uint_32 y_shf = (y_reg >> 8) & 3; int y_stp = 1 << y_shf; int y_hgt = stic_mob_hgt[(y_reg >> 7) & 7]; uint_32 x_rad = (x_pos & 7) * 4; uint_32 x_lad = 32 - x_rad; uint_32 x_ofs = (x_pos >> 3); uint_32 x_ofb = (x_pos & 31); uint_16 *const RESTRICT mob_bmp = stic->mob_bmp[i]; int y, y_res; /* ---------------------------------------------------------------- */ /* Compute bounding box for MOB and tell gfx about it. We can */ /* use this information to draw debug boxes around MOBs and other */ /* nice things. Bounding box is inclusive on all four edges. */ /* ---------------------------------------------------------------- */ stic->gfx->bbox[i][0] = x_pos; stic->gfx->bbox[i][1] = y_pos; stic->gfx->bbox[i][2] = x_pos + (x_reg & 0x400 ? 15 : 7); stic->gfx->bbox[i][3] = y_pos + y_hgt - 1; /* ---------------------------------------------------------------- */ /* Skip this MOB if it is off-screen or is both not-visible and */ /* non-interacting. */ /* ---------------------------------------------------------------- */ if (x_pos == 0 || x_pos >= 167 || (x_reg & 0x300) == 0 || y_pos >= 208) continue; /* ---------------------------------------------------------------- */ /* Generate the bitmap information for this MOB. */ /* ---------------------------------------------------------------- */ stic_do_mob(stic, i); /* ---------------------------------------------------------------- */ /* If this MOB is visible, put it into the color display image. */ /* ---------------------------------------------------------------- */ if (!visb || stic->drop_frame > 0) continue; y_res = y_reg & 0x80 ? 16 : 8; if (y_pos + (y_res << y_shf) > 208) y_res = (207 - y_pos + (1 << y_shf)) >> y_shf; for (y = 0; y < y_res; y++) { uint_32 l_pix, m_pix, r_pix; /* colors for 16-pixel MOB */ uint_32 l_msk, m_msk, r_msk; /* visibility masks */ uint_32 l_old, m_old, r_old; /* previous colors in disp */ uint_32 l_new, m_new, r_new; /* merged old and MOB */ uint_32 l_pri, r_pri; /* priority bit masks. */ int b_idx, v_idx; /* index into BMP and VISB */ uint_32 l_bmp, r_bmp; /* 1-bpp bitmaps of MOB */ /* ------------------------------------------------------------ */ /* Get the 4-bpp images of the MOB into l_pix, m_pix. */ /* ------------------------------------------------------------ */ l_pix = mob_img[2*y + 0]; m_pix = mob_img[2*y + 1]; l_msk = stic_b2n[mob_bmp[y] >> 8 ]; m_msk = stic_b2n[mob_bmp[y] & 0xFF]; /* ------------------------------------------------------------ */ /* Shift these right according to the X position of MOB. */ /* A 16-pixel wide MOB will straddle up to 3 32-bit words */ /* at 4-bpp. (x_rad == "x position right adjust") */ /* ------------------------------------------------------------ */ if (x_rad) { r_pix = (m_pix << x_lad); m_pix = (l_pix << x_lad) | (m_pix >> x_rad); l_pix = (l_pix >> x_rad); r_msk = (m_msk << x_lad); m_msk = (l_msk << x_lad) | (m_msk >> x_rad); l_msk = (l_msk >> x_rad); } else { r_pix = 0; r_msk = 0; } /* ------------------------------------------------------------ */ /* Similarly, shift the 1-bpp masks right according to the X */ /* position of the MOB. */ /* ------------------------------------------------------------ */ if (x_ofb <= 16) { l_bmp = mob_bmp[y] << (16 - x_ofb); r_bmp = 0; } else if (x_ofb <= 32) { l_bmp = mob_bmp[y] >> (x_ofb - 16); r_bmp = mob_bmp[y] << (48 - x_ofb); } else { l_bmp = 0; r_bmp = mob_bmp[y] << (48 - x_ofb); } /* ------------------------------------------------------------ */ /* Take the computed values and merge them into the image. */ /* This is where we replicate pixels to account for y-size. */ /* ------------------------------------------------------------ */ b_idx = (y_pos + (y << y_shf)) * 24 + x_ofs; v_idx = b_idx >> 2; for (j = 0; j < y_stp; j++, b_idx += 24, v_idx += 6) { /* -------------------------------------------------------- */ /* Now, merge the colors into the MOB color plane. */ /* -------------------------------------------------------- */ l_old = mpl_img[b_idx + 0]; m_old = mpl_img[b_idx + 1]; r_old = mpl_img[b_idx + 2]; l_new = (l_old &