/*************************************************************************** atarirle.c RLE sprite handling for early-to-mid 90's Atari raster games. **************************************************************************** Description: Beginning with Hydra, and continuing through to Primal Rage, Atari used RLE-compressed sprites. These sprites were decoded, colored, and scaled on the fly using an AMD 29C101 ALU unit. The instructions for the ALU were read from 3 512-byte PROMs and fed into the instruction input. See the bottom of the source for more details on the operation of these components. ***************************************************************************/ #include "emu.h" #include "atarirle.h" /*************************************************************************** TYPES & STRUCTURES ***************************************************************************/ /* internal structure containing a word index, shift and mask */ typedef struct atarirle_mask atarirle_mask; struct atarirle_mask { int word; /* word index */ int shift; /* shift amount */ int mask; /* final mask */ }; /* internal structure for sorting the motion objects */ typedef struct mo_sort_entry mo_sort_entry; struct mo_sort_entry { mo_sort_entry * next; int entry; }; /* internal structure describing each object in the ROMs */ typedef struct atarirle_info atarirle_info; struct atarirle_info { INT16 width; INT16 height; INT16 xoffs; INT16 yoffs; UINT8 bpp; const UINT16 * table; const UINT16 * data; }; /* internal structure containing the state of the motion objects */ typedef struct atarirle_data atarirle_data; struct atarirle_data { int bitmapwidth; /* width of the full playfield bitmap */ int bitmapheight; /* height of the full playfield bitmap */ int bitmapxmask; /* x coordinate mask for the playfield bitmap */ int bitmapymask; /* y coordinate mask for the playfield bitmap */ int spriterammask; /* combined mask when accessing sprite RAM with raw addresses */ int spriteramsize; /* total size of sprite RAM, in entries */ int palettebase; /* base palette entry */ int maxcolors; /* maximum number of colors */ rectangle cliprect; /* clipping rectangle */ atarirle_mask codemask; /* mask for the code index */ atarirle_mask colormask; /* mask for the color */ atarirle_mask xposmask; /* mask for the X position */ atarirle_mask yposmask; /* mask for the Y position */ atarirle_mask scalemask; /* mask for the scale factor */ atarirle_mask hflipmask; /* mask for the horizontal flip */ atarirle_mask ordermask; /* mask for the order */ atarirle_mask prioritymask; /* mask for the priority */ atarirle_mask vrammask; /* mask for the VRAM target */ const UINT16 * rombase; /* pointer to the base of the GFX ROM */ int romlength; /* length of the GFX ROM */ int objectcount; /* number of objects in the ROM */ atarirle_info * info; /* list of info records */ atarirle_entry *spriteram; /* pointer to sprite RAM */ bitmap_t * vram[2][2]; /* pointers to VRAM bitmaps and backbuffers */ int partial_scanline; /* partial update scanline */ UINT8 control_bits; /* current control bits */ UINT8 command; /* current command */ UINT8 is32bit; /* 32-bit or 16-bit? */ UINT16 checksums[256]; /* checksums for each 0x40000 bytes */ UINT16 ram[0x1000/2]; }; /*************************************************************************** MACROS ***************************************************************************/ /* data extraction */ #define EXTRACT_DATA(_input, _mask) (((_input)->data[(_mask).word] >> (_mask).shift) & (_mask).mask) enum { atarirle_hilite_index = -1 }; /*************************************************************************** STATIC VARIABLES ***************************************************************************/ static UINT8 rle_bpp[8]; static UINT16 *rle_table[8]; /*************************************************************************** STATIC FUNCTION DECLARATIONS ***************************************************************************/ static void build_rle_tables(running_machine &machine); static int count_objects(const UINT16 *base, int length); static void prescan_rle(const atarirle_data *mo, int which); static void sort_and_render(running_machine &machine, atarirle_data *mo); static void compute_checksum(atarirle_data *mo); static void draw_rle(atarirle_data *mo, bitmap_t *bitmap, int code, int color, int hflip, int vflip, int x, int y, int xscale, int yscale, const rectangle *clip); static void draw_rle_zoom(bitmap_t *bitmap, const atarirle_info *gfx, UINT32 palette, int sx, int sy, int scalex, int scaley, const rectangle *clip); static void draw_rle_zoom_hflip(bitmap_t *bitmap, const atarirle_info *gfx, UINT32 palette, int sx, int sy, int scalex, int scaley, const rectangle *clip); /*************************************************************************** INLINE FUNCTIONS ***************************************************************************/ INLINE atarirle_data *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == ATARIRLE); return (atarirle_data *)downcast(device)->token(); } /*--------------------------------------------------------------- compute_log: Computes the number of bits necessary to hold a given value. The input must be an even power of two. ---------------------------------------------------------------*/ #ifdef UNUSED_FUNCTION INLINE int compute_log(int value) { int log = 0; if (value == 0) return -1; while (!(value & 1)) log++, value >>= 1; if (value != 1) return -1; return log; } #endif /*--------------------------------------------------------------- round_to_powerof2: Rounds a number up to the nearest power of 2. Even powers of 2 are rounded up to the next greatest power (e.g., 4 returns 8). ---------------------------------------------------------------*/ INLINE int round_to_powerof2(int value) { int log = 0; if (value == 0) return 1; while ((value >>= 1) != 0) log++; return 1 << (log + 1); } /*--------------------------------------------------------------- collapse_bits: Moving right-to-left, for each 1 bit in the mask, copy the corresponding bit from the input value into the result, packing the bits along the way. ---------------------------------------------------------------*/ #ifdef UNUSED_FUNCTION INLINE int collapse_bits(int value, int mask) { int testmask, ormask; int result = 0; for (testmask = ormask = 1; testmask != 0; testmask <<= 1) if (mask & testmask) { if (value & testmask) result |= ormask; ormask <<= 1; } return result; } #endif /*--------------------------------------------------------------- convert_mask: Converts a 4-word mask into a word index, shift, and adjusted mask. Returns 0 if invalid. ---------------------------------------------------------------*/ INLINE int convert_mask(const atarirle_entry *input, atarirle_mask *result) { int i, temp; /* determine the word and make sure it's only 1 */ result->word = -1; for (i = 0; i < 8; i++) if (input->data[i]) { if (result->word == -1) result->word = i; else return 0; } /* if all-zero, it's valid */ if (result->word == -1) { result->word = result->shift = result->mask = 0; return 1; } /* determine the shift and final mask */ result->shift = 0; temp = input->data[result->word]; while (!(temp & 1)) { result->shift++; temp >>= 1; } result->mask = temp; return 1; } /*************************************************************************** GLOBAL FUNCTIONS ***************************************************************************/ /*--------------------------------------------------------------- atarirle_init: Configures the motion objects using the input description. Allocates all memory necessary and generates the attribute lookup table. ---------------------------------------------------------------*/ static DEVICE_START( atarirle ) { atarirle_data *mo = get_safe_token(device); running_machine &machine = device->machine(); const atarirle_desc *desc = (const atarirle_desc *)device->static_config(); const UINT16 *base = (const UINT16 *)machine.region(desc->region)->base(); int i, width, height; /* build and allocate the generic tables */ build_rle_tables(machine); /* determine the masks first */ convert_mask(&desc->codemask, &mo->codemask); convert_mask(&desc->colormask, &mo->colormask); convert_mask(&desc->xposmask, &mo->xposmask); convert_mask(&desc->yposmask, &mo->yposmask); convert_mask(&desc->scalemask, &mo->scalemask); convert_mask(&desc->hflipmask, &mo->hflipmask); convert_mask(&desc->ordermask, &mo->ordermask); convert_mask(&desc->prioritymask, &mo->prioritymask); convert_mask(&desc->vrammask, &mo->vrammask); /* copy in the basic data */ mo->bitmapwidth = round_to_powerof2(mo->xposmask.mask); mo->bitmapheight = round_to_powerof2(mo->yposmask.mask); mo->bitmapxmask = mo->bitmapwidth - 1; mo->bitmapymask = mo->bitmapheight - 1; mo->spriteramsize = desc->spriteramentries; mo->spriterammask = desc->spriteramentries - 1; mo->palettebase = desc->palettebase; mo->maxcolors = desc->maxcolors / 16; mo->rombase = base; mo->romlength = machine.region(desc->region)->bytes(); mo->objectcount = count_objects(base, mo->romlength); mo->cliprect = machine.primary_screen->visible_area(); if (desc->rightclip) { mo->cliprect.min_x = desc->leftclip; mo->cliprect.max_x = desc->rightclip; } /* compute the checksums */ memset(mo->checksums, 0, sizeof(mo->checksums)); for (i = 0; i < mo->romlength / 0x20000; i++) { const UINT16 *csbase = &mo->rombase[0x10000 * i]; int cursum = 0, j; for (j = 0; j < 0x10000; j++) cursum += *csbase++; mo->checksums[i] = cursum; } /* allocate the object info */ mo->info = auto_alloc_array_clear(machine, atarirle_info, mo->objectcount); /* fill in the data */ for (i = 0; i < mo->objectcount; i++) prescan_rle(mo, i); /* allocate the spriteram */ mo->spriteram = auto_alloc_array_clear(machine, atarirle_entry, mo->spriteramsize); /* allocate bitmaps */ width = machine.primary_screen->width(); height = machine.primary_screen->height(); mo->vram[0][0] = auto_bitmap_alloc(machine, width, height, BITMAP_FORMAT_INDEXED16); mo->vram[0][1] = auto_bitmap_alloc(machine, width, height, BITMAP_FORMAT_INDEXED16); bitmap_fill(mo->vram[0][0], NULL, 0); bitmap_fill(mo->vram[0][1], NULL, 0); /* allocate alternate bitmaps if needed */ if (mo->vrammask.mask != 0) { mo->vram[1][0] = auto_bitmap_alloc(machine, width, height, BITMAP_FORMAT_INDEXED16); mo->vram[1][1] = auto_bitmap_alloc(machine, width, height, BITMAP_FORMAT_INDEXED16); bitmap_fill(mo->vram[1][0], NULL, 0); bitmap_fill(mo->vram[1][1], NULL, 0); } mo->partial_scanline = -1; /* register for save states */ device->save_pointer(NAME(mo->spriteram[0].data), ARRAY_LENGTH(mo->spriteram[0].data) * mo->spriteramsize); device->save_item(NAME(*mo->vram[0][0])); device->save_item(NAME(*mo->vram[0][1])); if (mo->vrammask.mask != 0) { device->save_item(NAME(*mo->vram[1][0])); device->save_item(NAME(*mo->vram[1][1])); } device->save_item(NAME(mo->partial_scanline)); device->save_item(NAME(mo->control_bits)); device->save_item(NAME(mo->command)); device->save_item(NAME(mo->is32bit)); device->save_item(NAME(mo->checksums)); } DEVICE_GET_INFO( atarirle ) { switch (state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(atarirle_data); break; /* --- the following bits of info are returned as pointers to data or functions --- */ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(atarirle); break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case DEVINFO_STR_NAME: strcpy(info->s, "Atari RLE"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Atari RLE Video IC"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright MAME Team"); break; } } DEFINE_LEGACY_DEVICE(ATARIRLE, atarirle); /*--------------------------------------------------------------- atarirle_control_w: Write handler for MO control bits. ---------------------------------------------------------------*/ void atarirle_control_w(device_t *device, UINT8 bits) { atarirle_data *mo = get_safe_token(device); int scanline = device->machine().primary_screen->vpos(); int oldbits = mo->control_bits; //logerror("atarirle_control_w(%d)\n", bits); /* do nothing if nothing changed */ if (oldbits == bits) return; /* force a partial update first */ device->machine().primary_screen->update_partial(scanline); /* if the erase flag was set, erase the front map */ if (oldbits & ATARIRLE_CONTROL_ERASE) { rectangle cliprect = mo->cliprect; /* compute the top and bottom of the rect */ if (mo->partial_scanline + 1 > cliprect.min_y) cliprect.min_y = mo->partial_scanline + 1; if (scanline < cliprect.max_y) cliprect.max_y = scanline; //logerror(" partial erase %d-%d (frame %d)\n", cliprect.min_y, cliprect.max_y, (oldbits & ATARIRLE_CONTROL_FRAME) >> 2); /* erase the bitmap */ bitmap_fill(mo->vram[0][(oldbits & ATARIRLE_CONTROL_FRAME) >> 2], &cliprect, 0); if (mo->vrammask.mask != 0) bitmap_fill(mo->vram[1][(oldbits & ATARIRLE_CONTROL_FRAME) >> 2], &cliprect, 0); } /* update the bits */ mo->control_bits = bits; /* if mogo is set, do a render on the rising edge */ if (!(oldbits & ATARIRLE_CONTROL_MOGO) && (bits & ATARIRLE_CONTROL_MOGO)) { if (mo->command == ATARIRLE_COMMAND_DRAW) sort_and_render(device->machine(), mo); else if (mo->command == ATARIRLE_COMMAND_CHECKSUM) compute_checksum(mo); } /* remember where we left off */ mo->partial_scanline = scanline; } /*--------------------------------------------------------------- atarirle_command_w: Write handler for MO command bits. ---------------------------------------------------------------*/ void atarirle_command_w(device_t *device, UINT8 command) { atarirle_data *mo = get_safe_token(device); mo->command = command; } /*--------------------------------------------------------------- atarirle_eof: Flush remaining changes. ---------------------------------------------------------------*/ void atarirle_eof(device_t *device) { { atarirle_data *mo = get_safe_token(device); /* if the erase flag is set, erase to the end of the screen */ if (mo->control_bits & ATARIRLE_CONTROL_ERASE) { rectangle cliprect = mo->cliprect; /* compute top only; bottom is equal to visible_area */ if (mo->partial_scanline + 1 > cliprect.min_y) cliprect.min_y = mo->partial_scanline + 1; //logerror(" partial erase %d-%d (frame %d)\n", cliprect.min_y, cliprect.max_y, (mo->control_bits & ATARIRLE_CONTROL_FRAME) >> 2); /* erase the bitmap */ bitmap_fill(mo->vram[0][(mo->control_bits & ATARIRLE_CONTROL_FRAME) >> 2], &cliprect, 0); if (mo->vrammask.mask != 0) bitmap_fill(mo->vram[1][(mo->control_bits & ATARIRLE_CONTROL_FRAME) >> 2], &cliprect, 0); } /* reset the partial scanline to -1 so we can detect full updates */ mo->partial_scanline = -1; } } /*--------------------------------------------------------------- atarirle_spriteram_r: Read handler for the spriteram. ---------------------------------------------------------------*/ READ16_DEVICE_HANDLER( atarirle_spriteram_r ) { atarirle_data *mo = get_safe_token(device); return mo->ram[offset]; } /*--------------------------------------------------------------- atarirle_spriteram_w: Write handler for the spriteram. ---------------------------------------------------------------*/ WRITE16_DEVICE_HANDLER( atarirle_spriteram_w ) { atarirle_data *mo = get_safe_token(device); int entry = (offset >> 3) & mo->spriterammask; int idx = offset & 7; /* combine raw data */ COMBINE_DATA(&mo->ram[offset]); /* store a copy in our local spriteram */ mo->spriteram[entry].data[idx] = mo->ram[offset]; mo->is32bit = 0; } /*--------------------------------------------------------------- atarirle_spriteram32_r: Read handler for the spriteram. ---------------------------------------------------------------*/ READ32_DEVICE_HANDLER( atarirle_spriteram32_r ) { atarirle_data *mo = get_safe_token(device); UINT32 *ram = (UINT32 *)mo->ram; return ram[offset]; } /*--------------------------------------------------------------- atarirle_spriteram32_w: Write handler for the spriteram. ---------------------------------------------------------------*/ WRITE32_DEVICE_HANDLER( atarirle_spriteram32_w ) { atarirle_data *mo = get_safe_token(device); UINT32 *ram = (UINT32 *)mo->ram; int entry = (offset >> 2) & mo->spriterammask; int idx = 2 * (offset & 3); /* combine raw data */ COMBINE_DATA(&ram[offset]); /* store a copy in our local spriteram */ mo->spriteram[entry].data[idx+0] = ram[offset] >> 16; mo->spriteram[entry].data[idx+1] = ram[offset]; mo->is32bit = 1; } /*--------------------------------------------------------------- atarirle_get_vram: Return the VRAM bitmap. ---------------------------------------------------------------*/ bitmap_t *atarirle_get_vram(device_t *device, int idx) { atarirle_data *mo = get_safe_token(device); //logerror("atarirle_get_vram (frame %d)\n", (mo->control_bits & ATARIRLE_CONTROL_FRAME) >> 2); return mo->vram[idx][(mo->control_bits & ATARIRLE_CONTROL_FRAME) >> 2]; } /*--------------------------------------------------------------- build_rle_tables: Builds internal table for RLE mapping. ---------------------------------------------------------------*/ static void build_rle_tables(running_machine &machine) { UINT16 *base; int i; /* allocate all 5 tables */ base = auto_alloc_array(machine, UINT16, 0x500); /* assign the tables */ rle_table[0] = &base[0x000]; rle_table[1] = &base[0x100]; rle_table[2] = rle_table[3] = &base[0x200]; rle_table[4] = rle_table[6] = &base[0x300]; rle_table[5] = rle_table[7] = &base[0x400]; /* set the bpps */ rle_bpp[0] = 4; rle_bpp[1] = rle_bpp[2] = rle_bpp[3] = 5; rle_bpp[4] = rle_bpp[5] = rle_bpp[6] = rle_bpp[7] = 6; /* build the 4bpp table */ for (i = 0; i < 256; i++) rle_table[0][i] = (((i & 0xf0) + 0x10) << 4) | (i & 0x0f); /* build the 5bpp table */ for (i = 0; i < 256; i++) rle_table[2][i] = (((i & 0xe0) + 0x20) << 3) | (i & 0x1f); /* build the special 5bpp table */ for (i = 0; i < 256; i++) { if ((i & 0x0f) == 0) rle_table[1][i] = (((i & 0xf0) + 0x10) << 4) | (i & 0x0f); else rle_table[1][i] = (((i & 0xe0) + 0x20) << 3) | (i & 0x1f); } /* build the 6bpp table */ for (i = 0; i < 256; i++) rle_table[5][i] = (((i & 0xc0) + 0x40) << 2) | (i & 0x3f); /* build the special 6bpp table */ for (i = 0; i < 256; i++) { if ((i & 0x0f) == 0) rle_table[4][i] = (((i & 0xf0) + 0x10) << 4) | (i & 0x0f); else rle_table[4][i] = (((i & 0xc0) + 0x40) << 2) | (i & 0x3f); } } /*--------------------------------------------------------------- count_objects: Determines the number of objects in the motion object ROM. ---------------------------------------------------------------*/ int count_objects(const UINT16 *base, int length) { int lowest_address = length; int i; /* first determine the lowest address of all objects */ for (i = 0; i < lowest_address; i += 4) { int offset = ((base[i + 2] & 0xff) << 16) | base[i + 3]; //logerror("count_objects: i=%d offset=%08X\n", i, offset); if (offset > i && offset < lowest_address) lowest_address = offset; } /* that determines how many objects */ return lowest_address / 4; } /*--------------------------------------------------------------- prescan_rle: Prescans an RLE object, computing the width, height, and other goodies. ---------------------------------------------------------------*/ static void prescan_rle(const atarirle_data *mo, int which) { atarirle_info *rledata = &mo->info[which]; UINT16 *base = (UINT16 *)&mo->rombase[which * 4]; const UINT16 *end = mo->rombase + mo->romlength / 2; int width = 0, height, flags, offset; const UINT16 *table; /* look up the offset */ rledata->xoffs = (INT16)base[0]; rledata->yoffs = (INT16)base[1]; /* determine the depth and table */ flags = base[2]; rledata->bpp = rle_bpp[(flags >> 8) & 7]; table = rledata->table = rle_table[(flags >> 8) & 7]; /* determine the starting offset */ offset = ((base[2] & 0xff) << 16) | base[3]; rledata->data = base = (UINT16 *)&mo->rombase[offset]; /* make sure it's valid */ if (offset < which * 4 || offset >= mo->romlength) { memset(rledata, 0, sizeof(*rledata)); return; } /* first pre-scan to determine the width and height */ for (height = 0; height < 1024 && base < end; height++) { int tempwidth = 0; int entry_count = *base++; /* if the high bit is set, assume we're inverted */ if (entry_count & 0x8000) { entry_count ^= 0xffff; /* also change the ROM data so we don't have to do this again at runtime */ base[-1] ^= 0xffff; } /* we're done when we hit 0 */ if (entry_count == 0) break; /* track the width */ while (entry_count-- && base < end) { int word = *base++; int count/*, value*/; /* decode the low byte first */ count = table[word & 0xff]; //value = count & 0xff; tempwidth += count >> 8; /* decode the upper byte second */ count = table[word >> 8]; //value = count & 0xff; tempwidth += count >> 8; } /* only remember the max */ if (tempwidth > width) width = tempwidth; } /* fill in the data */ rledata->width = width; rledata->height = height; } /*--------------------------------------------------------------- compute_checksum: Compute the checksum values on the ROMs. ---------------------------------------------------------------*/ static void compute_checksum(atarirle_data *mo) { int reqsums = mo->spriteram[0].data[0] + 1; int i; /* number of checksums is in the first word */ if (reqsums > 256) reqsums = 256; /* stuff them back */ if (!mo->is32bit) { for (i = 0; i < reqsums; i++) mo->ram[i] = mo->checksums[i]; } else { UINT32 *ram = (UINT32 *)mo->ram; for (i = 0; i < reqsums; i++) if (i & 1) ram[i/2] = (ram[i/2] & 0xffff0000) | mo->checksums[i]; else ram[i/2] = (ram[i/2] & 0x0000ffff) | (mo->checksums[i] << 16); } } /*--------------------------------------------------------------- sort_and_render: Render all motion objects in order. ---------------------------------------------------------------*/ static void sort_and_render(running_machine &machine, atarirle_data *mo) { bitmap_t *bitmap1 = mo->vram[0][(~mo->control_bits & ATARIRLE_CONTROL_FRAME) >> 2]; bitmap_t *bitmap2 = mo->vram[1][(~mo->control_bits & ATARIRLE_CONTROL_FRAME) >> 2]; atarirle_entry *obj = mo->spriteram; mo_sort_entry sort_entry[256]; mo_sort_entry *list_head[256]; mo_sort_entry *current; int i; atarirle_entry *hilite = NULL; int count = 0; /* sort the motion objects into their proper priorities */ memset(list_head, 0, sizeof(list_head)); for (i = 0; i < 256; i++, obj++) { int order = EXTRACT_DATA(obj, mo->ordermask); sort_entry[i].entry = i; sort_entry[i].next = list_head[order]; list_head[order] = &sort_entry[i]; } /* now loop back and process */ count = 0; for (i = 1; i < 256; i++) for (current = list_head[i]; current; current = current->next) { int scale, code; /* extract scale and code */ obj = &mo->spriteram[current->entry]; scale = EXTRACT_DATA(obj, mo->scalemask); code = EXTRACT_DATA(obj, mo->codemask); /* make sure they are in range */ if (scale > 0 && code < mo->objectcount) { int hflip = EXTRACT_DATA(obj, mo->hflipmask); int color = EXTRACT_DATA(obj, mo->colormask); int priority = EXTRACT_DATA(obj, mo->prioritymask); int x = EXTRACT_DATA(obj, mo->xposmask); int y = EXTRACT_DATA(obj, mo->yposmask); int which = EXTRACT_DATA(obj, mo->vrammask); if (count++ == atarirle_hilite_index) hilite = obj; if (x & ((mo->xposmask.mask + 1) >> 1)) x = (INT16)(x | ~mo->xposmask.mask); if (y & ((mo->yposmask.mask + 1) >> 1)) y = (INT16)(y | ~mo->yposmask.mask); x += mo->cliprect.min_x; /* merge priority and color */ color = (color << 4) | (priority << ATARIRLE_PRIORITY_SHIFT); /* render to one or both bitmaps */ if (which == 0) draw_rle(mo, bitmap1, code, color, hflip, 0, x, y, scale, scale, &mo->cliprect); if (bitmap2 && which != 0) draw_rle(mo, bitmap2, code, color, hflip, 0, x, y, scale, scale, &mo->cliprect); } } if (hilite) { int scale, code/*, which*/; /* extract scale and code */ obj = hilite; scale = EXTRACT_DATA(obj, mo->scalemask); code = EXTRACT_DATA(obj, mo->codemask); //which = EXTRACT_DATA(obj, mo->vrammask); /* make sure they are in range */ if (scale > 0 && code < mo->objectcount) { int hflip = EXTRACT_DATA(obj, mo->hflipmask); int color = EXTRACT_DATA(obj, mo->colormask); int priority = EXTRACT_DATA(obj, mo->prioritymask); int x = EXTRACT_DATA(obj, mo->xposmask); int y = EXTRACT_DATA(obj, mo->yposmask); int scaled_xoffs, scaled_yoffs; const atarirle_info *info; if (x & ((mo->xposmask.mask + 1) >> 1)) x = (INT16)(x | ~mo->xposmask.mask); if (y & ((mo->yposmask.mask + 1) >> 1)) y = (INT16)(y | ~mo->yposmask.mask); x += mo->cliprect.min_x; /* merge priority and color */ color = (color << 4) | (priority << ATARIRLE_PRIORITY_SHIFT); info = &mo->info[code]; scaled_xoffs = (scale * info->xoffs) >> 12; scaled_yoffs = (scale * info->yoffs) >> 12; /* we're hflipped, account for it */ if (hflip) scaled_xoffs = ((scale * info->width) >> 12) - scaled_xoffs; /* adjust for the x and y offsets */ x -= scaled_xoffs; y -= scaled_yoffs; do { const rectangle &visarea = machine.primary_screen->visible_area(); int scaled_width = (scale * info->width + 0x7fff) >> 12; int scaled_height = (scale * info->height + 0x7fff) >> 12; int ex, ey, sx = x, sy = y, tx, ty; /* make sure we didn't end up with 0 */ if (scaled_width == 0) scaled_width = 1; if (scaled_height == 0) scaled_height = 1; /* compute the remaining parameters */ ex = sx + scaled_width - 1; ey = sy + scaled_height - 1; /* left edge clip */ if (sx < visarea.min_x) sx = visarea.min_x; if (sx > visarea.max_x) break; /* right edge clip */ if (ex > visarea.max_x) ex = visarea.max_x; else if (ex < visarea.min_x) break; /* top edge clip */ if (sy < visarea.min_y) sy = visarea.min_y; else if (sy > visarea.max_y) break; /* bottom edge clip */ if (ey > visarea.max_y) ey = visarea.max_y; else if (ey < visarea.min_y) break; for (ty = sy; ty <= ey; ty++) { *BITMAP_ADDR16(bitmap1, ty, sx) = machine.rand() & 0xff; *BITMAP_ADDR16(bitmap1, ty, ex) = machine.rand() & 0xff; } for (tx = sx; tx <= ex; tx++) { *BITMAP_ADDR16(bitmap1, sy, tx) = machine.rand() & 0xff; *BITMAP_ADDR16(bitmap1, ey, tx) = machine.rand() & 0xff; } } while (0); fprintf(stderr, " Sprite: c=%04X l=%04X h=%d X=%4d (o=%4d w=%3d) Y=%4d (o=%4d h=%d) s=%04X\n", code, color, hflip, x, -scaled_xoffs, (scale * info->width) >> 12, y, -scaled_yoffs, (scale * info->height) >> 12, scale); } } } /*--------------------------------------------------------------- draw_rle: Render a single RLE-compressed motion object. ---------------------------------------------------------------*/ void draw_rle(atarirle_data *mo, bitmap_t *bitmap, int code, int color, int hflip, int vflip, int x, int y, int xscale, int yscale, const rectangle *clip) { UINT32 palettebase = mo->palettebase + color; const atarirle_info *info = &mo->info[code]; int scaled_xoffs = (xscale * info->xoffs) >> 12; int scaled_yoffs = (yscale * info->yoffs) >> 12; /* we're hflipped, account for it */ if (hflip) scaled_xoffs = ((xscale * info->width) >> 12) - scaled_xoffs; //if (clip->min_y == Machine->primary_screen->visible_area().min_y) //logerror(" Sprite: c=%04X l=%04X h=%d X=%4d (o=%4d w=%3d) Y=%4d (o=%4d h=%d) s=%04X\n", // code, color, hflip, // x, -scaled_xoffs, (xscale * info->width) >> 12, // y, -scaled_yoffs, (yscale * info->height) >> 12, xscale); /* adjust for the x and y offsets */ x -= scaled_xoffs; y -= scaled_yoffs; /* bail on a NULL object */ if (!info->data) return; /* 16-bit case */ assert(bitmap->bpp == 16); if (!hflip) draw_rle_zoom(bitmap, info, palettebase, x, y, xscale << 4, yscale << 4, clip); else draw_rle_zoom_hflip(bitmap, info, palettebase, x, y, xscale << 4, yscale << 4, clip); } /*--------------------------------------------------------------- draw_rle_zoom: Draw an RLE-compressed object to a 16-bit bitmap. ---------------------------------------------------------------*/ void draw_rle_zoom(bitmap_t *bitmap, const atarirle_info *gfx, UINT32 palette, int sx, int sy, int scalex, int scaley, const rectangle *clip) { const UINT16 *row_start = gfx->data; const UINT16 *table = gfx->table; volatile int current_row = 0; int scaled_width = (scalex * gfx->width + 0x7fff) >> 16; int scaled_height = (scaley * gfx->height + 0x7fff) >> 16; int pixels_to_skip = 0, xclipped = 0; int dx, dy, ex, ey; int y, sourcey; /* make sure we didn't end up with 0 */ if (scaled_width == 0) scaled_width = 1; if (scaled_height == 0) scaled_height = 1; /* compute the remaining parameters */ dx = (gfx->width << 16) / scaled_width; dy = (gfx->height << 16) / scaled_height; ex = sx + scaled_width - 1; ey = sy + scaled_height - 1; sourcey = dy / 2; /* left edge clip */ if (sx < clip->min_x) pixels_to_skip = clip->min_x - sx, xclipped = 1; if (sx > clip->max_x) return; /* right edge clip */ if (ex > clip->max_x) ex = clip->max_x, xclipped = 1; else if (ex < clip->min_x) return; /* top edge clip */ if (sy < clip->min_y) { sourcey += (clip->min_y - sy) * dy; sy = clip->min_y; } else if (sy > clip->max_y) return; /* bottom edge clip */ if (ey > clip->max_y) ey = clip->max_y; else if (ey < clip->min_y) return; /* loop top to bottom */ for (y = sy; y <= ey; y++, sourcey += dy) { UINT16 *dest = BITMAP_ADDR16(bitmap, y, sx); int j, sourcex = dx / 2, rle_end = 0; const UINT16 *base; int entry_count; /* loop until we hit the row we're on */ for ( ; current_row != (sourcey >> 16); current_row++) row_start += 1 + *row_start; /* grab our starting parameters from this row */ base = row_start; entry_count = *base++; /* non-clipped case */ if (!xclipped) { /* decode the pixels */ for (j = 0; j < entry_count; j++) { int word = *base++; int count, value; /* decode the low byte first */ count = table[word & 0xff]; value = count & 0xff; rle_end += (count & 0xff00) << 8; /* store copies of the value until we pass the end of this chunk */ if (value) { value += palette; while (sourcex < rle_end) *dest++ = value, sourcex += dx; } else { while (sourcex < rle_end) dest++, sourcex += dx; } /* decode the upper byte second */ count = table[word >> 8]; value = count & 0xff; rle_end += (count & 0xff00) << 8; /* store copies of the value until we pass the end of this chunk */ if (value) { value += palette; while (sourcex < rle_end) *dest++ = value, sourcex += dx; } else { while (sourcex < rle_end) dest++, sourcex += dx; } } } /* clipped case */ else { const UINT16 *end = BITMAP_ADDR16(bitmap, y, ex); int to_be_skipped = pixels_to_skip; /* decode the pixels */ for (j = 0; j < entry_count && dest <= end; j++) { int word = *base++; int count, value; /* decode the low byte first */ count = table[word & 0xff]; value = count & 0xff; rle_end += (count & 0xff00) << 8; /* store copies of the value until we pass the end of this chunk */ if (to_be_skipped) { while (to_be_skipped && sourcex < rle_end) dest++, sourcex += dx, to_be_skipped--; if (to_be_skipped) goto next3; } if (value) { value += palette; while (sourcex < rle_end && dest <= end) *dest++ = value, sourcex += dx; } else { while (sourcex < rle_end) dest++, sourcex += dx; } next3: /* decode the upper byte second */ count = table[word >> 8]; value = count & 0xff; rle_end += (count & 0xff00) << 8; /* store copies of the value until we pass the end of this chunk */ if (to_be_skipped) { while (to_be_skipped && sourcex < rle_end) dest++, sourcex += dx, to_be_skipped--; if (to_be_skipped) goto next4; } if (value) { value += palette; while (sourcex < rle_end && dest <= end) *dest++ = value, sourcex += dx; } else { while (sourcex < rle_end) dest++, sourcex += dx; } next4: ; } } } } /*--------------------------------------------------------------- draw_rle_zoom_hflip: Draw an RLE-compressed object to a 16-bit bitmap with horizontal flip. ---------------------------------------------------------------*/ void draw_rle_zoom_hflip(bitmap_t *bitmap, const atarirle_info *gfx, UINT32 palette, int sx, int sy, int scalex, int scaley, const rectangle *clip) { const UINT16 *row_start = gfx->data; const UINT16 *table = gfx->table; volatile int current_row = 0; int scaled_width = (scalex * gfx->width + 0x7fff) >> 16; int scaled_height = (scaley * gfx->height + 0x7fff) >> 16; int pixels_to_skip = 0, xclipped = 0; int dx, dy, ex, ey; int y, sourcey; /* make sure we didn't end up with 0 */ if (scaled_width == 0) scaled_width = 1; if (scaled_height == 0) scaled_height = 1; /* compute the remaining parameters */ dx = (gfx->width << 16) / scaled_width; dy = (gfx->height << 16) / scaled_height; ex = sx + scaled_width - 1; ey = sy + scaled_height - 1; sourcey = dy / 2; /* left edge clip */ if (sx < clip->min_x) sx = clip->min_x, xclipped = 1; if (sx > clip->max_x) return; /* right edge clip */ if (ex > clip->max_x) pixels_to_skip = ex - clip->max_x, xclipped = 1; else if (ex < clip->min_x) return; /* top edge clip */ if (sy < clip->min_y) { sourcey += (clip->min_y - sy) * dy; sy = clip->min_y; } else if (sy > clip->max_y) return; /* bottom edge clip */ if (ey > clip->max_y) ey = clip->max_y; else if (ey < clip->min_y) return; /* loop top to bottom */ for (y = sy; y <= ey; y++, sourcey += dy) { UINT16 *dest = BITMAP_ADDR16(bitmap, y, ex); int j, sourcex = dx / 2, rle_end = 0; const UINT16 *base; int entry_count; /* loop until we hit the row we're on */ for ( ; current_row != (sourcey >> 16); current_row++) row_start += 1 + *row_start; /* grab our starting parameters from this row */ base = row_start; entry_count = *base++; /* non-clipped case */ if (!xclipped) { /* decode the pixels */ for (j = 0; j < entry_count; j++) { int word = *base++; int count, value; /* decode the low byte first */ count = table[word & 0xff]; value = count & 0xff; rle_end += (count & 0xff00) << 8; /* store copies of the value until we pass the end of this chunk */ if (value) { value += palette; while (sourcex < rle_end) *dest-- = value, sourcex += dx; } else { while (sourcex < rle_end) dest--, sourcex += dx; } /* decode the upper byte second */ count = table[word >> 8]; value = count & 0xff; rle_end += (count & 0xff00) << 8; /* store copies of the value until we pass the end of this chunk */ if (value) { value += palette; while (sourcex < rle_end) *dest-- = value, sourcex += dx; } else { while (sourcex < rle_end) dest--, sourcex += dx; } } } /* clipped case */ else { const UINT16 *start = BITMAP_ADDR16(bitmap, y, sx); int to_be_skipped = pixels_to_skip; /* decode the pixels */ for (j = 0; j < entry_count && dest >= start; j++) { int word = *base++; int count, value; /* decode the low byte first */ count = table[word & 0xff]; value = count & 0xff; rle_end += (count & 0xff00) << 8; /* store copies of the value until we pass the end of this chunk */ if (to_be_skipped) { while (to_be_skipped && sourcex < rle_end) dest--, sourcex += dx, to_be_skipped--; if (to_be_skipped) goto next3; } if (value) { value += palette; while (sourcex < rle_end && dest >= start) *dest-- = value, sourcex += dx; } else { while (sourcex < rle_end) dest--, sourcex += dx; } next3: /* decode the upper byte second */ count = table[word >> 8]; value = count & 0xff; rle_end += (count & 0xff00) << 8; /* store copies of the value until we pass the end of this chunk */ if (to_be_skipped) { while (to_be_skipped && sourcex < rle_end) dest--, sourcex += dx, to_be_skipped--; if (to_be_skipped) goto next4; } if (value) { value += palette; while (sourcex < rle_end && dest >= start) *dest-- = value, sourcex += dx; } else { while (sourcex < rle_end) dest--, sourcex += dx; } next4: ; } } } } /*************************************************************************** The mapping of the bits from the PROMs is like this: D23 -> D22 -> D21 -> D20 -> D19 -> D18 -> D17 -> instruction bit 8 D16 -> instruction bit 7 D15 -> instruction bit 6 D14 -> instruction bit 5 D13 -> instruction bit 4 D12 -> instruction bit 3 D11 -> instruction bit 2 D10 -> instruction bit 1 (modified via a PAL) D9 -> instruction bit 0 D8 -> carry in D7 -> A bit 3 D6 -> A bit 2 D5 -> A bit 1 D4 -> A bit 0 D3 -> B bit 3 D2 -> B bit 2 D1 -> B bit 1 D0 -> B bit 0 Although much of the logic is contained in the ALU, the program counter is fed externally. Jumps are decoded like this: if (D13 && D14) { switch (D11 | D10 | D9) { case 0: condition = true; case 1: condition = ALU.LT; case 2: condition = ALU.Z; case 3: condition = ALU.N; case 4: condition = BLT.HFLIP; case 5: condition = /BLT.SCAN; case 6: condition = ALU.C; case 7: condition = MOTIMEP; } condition ^= D12; if (condition) PC = D8 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0; } Here is the code from Guardians of the Hood: I C A B --- - - - 000: A0 C8 00 -> 064 0 0 0 (latch UC.RASCAS) 001: 01 C8 66 -> 0E4 0 6 6 002: 01 89 66 -> 0C4 1 6 6 003: 03 82 66 -> 1C1 0 6 6 004: 01 B8 65 -> 0DC 0 6 5 005: 01 82 66 -> 0C1 0 6 6 006: 01 89 55 -> 0C4 1 5 5 007: 01 C8 77 -> 0E4 0 7 7 008: 01 89 77 -> 0C4 1 7 7 009: 03 82 77 -> 1C1 0 7 7 00A: 03 82 77 -> 1C1 0 7 7 00B: 03 82 77 -> 1C1 0 7 7 00C: 03 82 77 -> 1C1 0 7 7 00D: 01 C8 88 -> 0E4 0 8 8 00E: 01 B8 79 -> 0DC 0 7 9 00F: 03 82 99 -> 1C1 0 9 9 010: 01 82 99 -> 0C1 0 9 9 011: 01 B8 61 -> 0DC 0 6 1 012: 01 82 11 -> 0C1 0 1 1 013: 01 96 11 -> 0CB 0 1 1 014: 03 82 11 -> 1C1 0 1 1 015: 03 82 11 -> 1C1 0 1 1 016: 03 82 11 -> 1C1 0 1 1 017: 03 82 11 -> 1C1 0 1 1 018: 00 EE 18 -> 077 0 1 8 if /MOTIMEP JUMP 018 019: 84 B8 11 -> 05C 0 1 1 01A: 01 B8 72 -> 0DC 0 7 2 01B: 00 C8 00 -> 064 0 0 0 01C: 9C B8 88 -> 05C 0 8 8 01D: 98 B8 88 -> 05C 0 8 8 01E: 01 96 22 -> 0CB 0 2 2 01F: 00 F4 1C -> 07A 0 1 C if NZ JUMP 01C 020: 00 38 11 -> 01C 0 1 1 021: 03 34 00 -> 19A 0 0 0 022: 03 34 00 -> 19A 0 0 0 023: 03 34 00 -> 19A 0 0 0 024: 00 11 99 -> 008 1 9 9 025: 02 34 00 -> 11A 0 0 0 026: 84 B4 00 -> 05A 0 0 0 027: 00 C8 00 -> 064 0 0 0 028: 00 C8 00 -> 064 0 0 0 029: 10 3E 00 -> 01F 0 0 0 02A: 00 E5 3B -> 072 1 3 B if Z JUMP 13B 02B: 00 05 00 -> 002 1 0 0 02C: 9C B4 00 -> 05A 0 0 0 02D: 98 B4 00 -> 05A 0 0 0 02E: 00 EE 2E -> 077 0 2 E if /MOTIMEP JUMP 02E 02F: 10 3E 00 -> 01F 0 0 0 030: 00 05 00 -> 002 1 0 0 031: 00 F4 35 -> 07A 0 3 5 if NZ JUMP 035 032: A8 B8 55 -> 05C 0 5 5 (latch UC.HFLIP) 033: A8 FE 35 -> 07F 0 3 5 if MOTIMEP JUMP 035 (latch UC.HFLIP) 034: 00 E1 47 -> 070 1 4 7 JUMP 147 035: 01 B8 72 -> 0DC 0 7 2 036: 01 B8 8F -> 0DC 0 8 F 037: 01 83 5F -> 0C1 1 5 F 038: 84 B8 FF -> 05C 0 F F 039: 00 EE 39 -> 077 0 3 9 if /MOTIMEP JUMP 039 03A: 19 BE FF -> 0DF 0 F F 03B: 00 E4 68 -> 072 0 6 8 if Z JUMP 068 03C: 84 B8 88 -> 05C 0 8 8 03D: 00 EE 3D -> 077 0 3 D if /MOTIMEP JUMP 03D 03E: 19 BE AA -> 0DF 0 A A 03F: 00 EE 3F -> 077 0 3 F if /MOTIMEP JUMP 03F 040: 19 BE BB -> 0DF 0 B B 041: 00 EE 41 -> 077 0 4 1 if /MOTIMEP JUMP 041 042: 19 BE CC -> 0DF 0 C C 043: 00 EE 43 -> 077 0 4 3 if /MOTIMEP JUMP 043 044: 19 BE DD -> 0DF 0 D D 045: 00 EE 45 -> 077 0 4 5 if /MOTIMEP JUMP 045 046: 19 BE EE -> 0DF 0 E E 047: 00 C8 00 -> 064 0 0 0 048: 01 82 1F -> 0C1 0 1 F 049: 84 B8 FF -> 05C 0 F F 04A: 11 BE FF -> 0DF 0 F F 04B: 00 C8 00 -> 064 0 0 0 04C: 00 C8 00 -> 064 0 0 0 04D: 9C B8 99 -> 05C 0 9 9 04E: 90 B8 99 -> 05C 0 9 9 04F: 00 C8 00 -> 064 0 0 0 050: 84 B8 99 -> 05C 0 9 9 051: 9C B8 AA -> 05C 0 A A 052: 98 B8 AA -> 05C 0 A A 053: 00 C8 00 -> 064 0 0 0 054: 00 C8 00 -> 064 0 0 0 055: 9C B8 BB -> 05C 0 B B 056: 98 B8 BB -> 05C 0 B B 057: 00 C8 00 -> 064 0 0 0 058: 00 C8 00 -> 064 0 0 0 059: 9C B8 CC -> 05C 0 C C 05A: 98 B8 CC -> 05C 0 C C 05B: 00 C8 00 -> 064 0 0 0 05C: 00 C8 00 -> 064 0 0 0 05D: 9C B8 DD -> 05C 0 D D 05E: 98 B8 DD -> 05C 0 D D 05F: 00 C8 00 -> 064 0 0 0 060: 00 C8 00 -> 064 0 0 0 061: 9C B8 EE -> 05C 0 E E 062: 98 B8 EE -> 05C 0 E E 063: 00 C8 00 -> 064 0 0 0 064: 00 C8 00 -> 064 0 0 0 065: 9C B8 FF -> 05C 0 F F 066: 98 B8 FF -> 05C 0 F F 067: 01 82 69 -> 0C1 0 6 9 068: 01 82 68 -> 0C1 0 6 8 069: 01 96 22 -> 0CB 0 2 2 06A: 00 F4 36 -> 07A 0 3 6 if NZ JUMP 036 06B: 01 B8 72 -> 0DC 0 7 2 06C: 01 96 22 -> 0CB 0 2 2 06D: 01 C8 44 -> 0E4 0 4 4 06E: 01 96 44 -> 0CB 0 4 4 06F: 01 B8 43 -> 0DC 0 4 3 070: 02 B8 44 -> 15C 0 4 4 071: 02 B8 44 -> 15C 0 4 4 072: 02 B8 44 -> 15C 0 4 4 073: 02 B8 44 -> 15C 0 4 4 074: 02 B8 33 -> 15C 0 3 3 075: 01 93 43 -> 0C9 1 4 3 076: 02 B8 33 -> 15C 0 3 3 077: 00 38 33 -> 01C 0 3 3 078: 01 C8 33 -> 0E4 0 3 3 079: 01 89 33 -> 0C4 1 3 3 07A: 01 82 33 -> 0C1 0 3 3 07B: 01 C8 00 -> 0E4 0 0 0 07C: 00 B8 00 -> 05C 0 0 0 07D: 00 F4 87 -> 07A 0 8 7 if NZ JUMP 087 07E: 01 89 11 -> 0C4 1 1 1 07F: 84 B8 11 -> 05C 0 1 1 080: 00 EE 80 -> 077 0 8 0 if /MOTIMEP JUMP 080 081: 11 BE 00 -> 0DF 0 0 0 082: A4 B8 22 -> 05C 0 2 2 (latch UC.COLOR) 083: 01 96 22 -> 0CB 0 2 2 084: 00 F6 7C -> 07B 0 7 C if P JUMP 07C 085: 8C B8 22 -> 05C 0 2 2 086: 00 E0 86 -> 070 0 8 6 JUMP 086 087: 84 B8 00 -> 05C 0 0 0 088: 00 EE 88 -> 077 0 8 8 if /MOTIMEP JUMP 088 089: 19 BE 55 -> 0DF 0 5 5 08A: A8 B8 55 -> 05C 0 5 5 (latch UC.HFLIP) 08B: 03 82 55 -> 1C1 0 5 5 08C: 00 C8 00 -> 064 0 0 0 08D: 19 BE FF -> 0DF 0 F F 08E: A4 B8 FF -> 05C 0 F F (latch UC.COLOR) 08F: 00 C8 00 -> 064 0 0 0 090: 00 C8 00 -> 064 0 0 0 091: 19 BE 66 -> 0DF 0 6 6 092: B0 C8 00 -> 064 0 0 0 (latch UC.FORMAT) 093: 00 C8 00 -> 064 0 0 0 094: 00 C8 00 -> 064 0 0 0 095: 19 BE 77 -> 0DF 0 7 7 096: 00 C8 00 -> 064 0 0 0 097: 00 C8 00 -> 064 0 0 0 098: 00 C8 00 -> 064 0 0 0 099: 19 BE 88 -> 0DF 0 8 8 09A: 00 C8 00 -> 064 0 0 0 09B: 00 C8 00 -> 064 0 0 0 09C: 00 C8 00 -> 064 0 0 0 09D: 19 BE 00 -> 0DF 0 0 0 09E: 84 B8 55 -> 05C 0 5 5 09F: 00 C8 00 -> 064 0 0 0 0A0: 00 C8 00 -> 064 0 0 0 0A1: 09 BE DD -> 0DF 0 D D 0A2: 00 C8 00 -> 064 0 0 0 0A3: 00 C8 00 -> 064 0 0 0 0A4: 09 BE EE -> 0DF 0 E E 0A5: 01 C8 AA -> 0E4 0 A A 0A6: 01 C8 BB -> 0E4 0 B B 0A7: 09 BE 99 -> 0DF 0 9 9 0A8: 01 B8 8F -> 0DC 0 8 F 0A9: 01 82 FF -> 0C1 0 F F 0AA: 09 BE 55 -> 0DF 0 5 5 0AB: 01 C8 CC -> 0E4 0 C C 0AC: 00 B8 DD -> 05C 0 D D 0AD: 00 F6 B1 -> 07B 0 B 1 if P JUMP 0B1 0AE: 01 A9 DD -> 0D4 1 D D 0AF: 01 82 4C -> 0C1 0 4 C 0B0: 01 89 CC -> 0C4 1 C C 0B1: 01 82 CC -> 0C1 0 C C 0B2: 00 B8 EE -> 05C 0 E E 0B3: 00 F6 B7 -> 07B 0 B 7 if P JUMP 0B7 0B4: 01 A9 EE -> 0D4 1 E E 0B5: 01 82 4C -> 0C1 0 4 C 0B6: 01 89 CC -> 0C4 1 C C 0B7: 03 82 CC -> 1C1 0 C C 0B8: 01 82 FF -> 0C1 0 F F 0B9: 43 86 DA -> 1C3 0 D A 0BA: 43 86 EB -> 1C3 0 E B 0BB: 01 82 FF -> 0C1 0 F F 0BC: 43 86 DA -> 1C3 0 D A 0BD: 43 86 EB -> 1C3 0 E B 0BE: 01 82 FF -> 0C1 0 F F 0BF: 43 86 DA -> 1C3 0 D A 0C0: 43 86 EB -> 1C3 0 E B 0C1: 01 82 FF -> 0C1 0 F F 0C2: 43 86 DA -> 1C3 0 D A 0C3: 43 86 EB -> 1C3 0 E B 0C4: 01 82 FF -> 0C1 0 F F 0C5: 43 86 DA -> 1C3 0 D A 0C6: 43 86 EB -> 1C3 0 E B 0C7: 01 82 FF -> 0C1 0 F F 0C8: 43 86 DA -> 1C3 0 D A 0C9: 43 86 EB -> 1C3 0 E B 0CA: 01 82 FF -> 0C1 0 F F 0CB: 43 86 DA -> 1C3 0 D A 0CC: 43 86 EB -> 1C3 0 E B 0CD: 01 82 FF -> 0C1 0 F F 0CE: 41 86 DA -> 0C3 0 D A 0CF: 41 86 EB -> 0C3 0 E B 0D0: 02 B8 DD -> 15C 0 D D 0D1: 02 B8 EE -> 15C 0 E E 0D2: 01 82 FF -> 0C1 0 F F 0D3: 41 86 DA -> 0C3 0 D A 0D4: 41 86 EB -> 0C3 0 E B 0D5: 02 B8 DD -> 15C 0 D D 0D6: 02 B8 EE -> 15C 0 E E 0D7: 01 82 FF -> 0C1 0 F F 0D8: 41 86 DA -> 0C3 0 D A 0D9: 41 86 EB -> 0C3 0 E B 0DA: 02 B8 DD -> 15C 0 D D 0DB: 02 B8 EE -> 15C 0 E E 0DC: 01 82 FF -> 0C1 0 F F 0DD: 41 86 DA -> 0C3 0 D A 0DE: 41 86 EB -> 0C3 0 E B 0DF: 00 B8 CC -> 05C 0 C C 0E0: 00 F6 E2 -> 07B 0 E 2 if P JUMP 0E2 0E1: 01 A9 AA -> 0D4 1 A A 0E2: 01 82 CC -> 0C1 0 C C 0E3: 00 F6 E5 -> 07B 0 E 5 if P JUMP 0E5 0E4: 01 A9 BB -> 0D4 1 B B 0E5: 01 C8 CC -> 0E4 0 C C 0E6: 01 89 CC -> 0C4 1 C C 0E7: 03 82 CC -> 1C1 0 C C 0E8: 03 82 CC -> 1C1 0 C C 0E9: 03 82 CC -> 1C1 0 C C 0EA: 01 B8 4F -> 0DC 0 4 F 0EB: 03 82 FF -> 1C1 0 F F 0EC: 00 D2 48 -> 069 0 4 8 0ED: 00 E5 19 -> 072 1 1 9 if Z JUMP 119 0EE: 02 B8 88 -> 15C 0 8 8 0EF: 02 B8 88 -> 15C 0 8 8 0F0: 02 B8 88 -> 15C 0 8 8 0F1: 02 B8 88 -> 15C 0 8 8 0F2: 01 80 88 -> 0C0 0 8 8 0F3: 84 B8 88 -> 05C 0 8 8 0F4: 00 EE F4 -> 077 0 F 4 if /MOTIMEP JUMP 0F4 0F5: 19 BE 88 -> 0DF 0 8 8 0F6: AC B8 88 -> 05C 0 8 8 (latch UC.RATE) 0F7: 01 82 88 -> 0C1 0 8 8 0F8: 02 B8 88 -> 15C 0 8 8 0F9: 00 F8 FC -> 07C 0 F C if /BLT.HFLIP JUMP 0FC 0FA: 01 82 A6 -> 0C1 0 A 6 0FB: 00 E0 FD -> 070 0 F D JUMP 0FD 0FC: 01 93 A6 -> 0C9 1 A 6 0FD: 01 93 B7 -> 0C9 1 B 7 0FE: B0 B8 99 -> 05C 0 9 9 (latch UC.FORMAT) 0FF: 84 B8 55 -> 05C 0 5 5 100: 01 C8 EE -> 0E4 0 E E 101: 00 C8 00 -> 064 0 0 0 102: 09 BE DD -> 0DF 0 D D 103: 00 E6 7C -> 073 0 7 C if N JUMP 07C 104: 00 93 F7 -> 049 1 F 7 105: 00 ED 0B -> 076 1 0 B if C JUMP 10B 106: B4 B8 DD -> 05C 0 D D (latch UC.TRANS) 107: B8 B8 77 -> 05C 0 7 7 (latch UC.XYPOS) 108: A0 B8 33 -> 05C 0 3 3 (latch UC.RASCAS) 109: B8 B8 66 -> 05C 0 6 6 (latch UC.XYPOS) 10A: BC C8 00 -> 064 0 0 0 (latch UC.SCAN) 10B: 01 82 C7 -> 0C1 0 C 7 10C: 01 82 8E -> 0C1 0 8 E 10D: 00 D2 4E -> 069 0 4 E 10E: 00 E5 14 -> 072 1 1 4 if Z JUMP 114 10F: 01 93 4E -> 0C9 1 4 E 110: 01 96 EE -> 0CB 0 E E 111: 01 83 D5 -> 0C1 1 D 5 112: 00 FD 14 -> 07E 1 1 4 if NC JUMP 114 113: 01 89 99 -> 0C4 1 9 9 114: 00 FB 14 -> 07D 1 1 4 if BLT.SCAN JUMP 114 115: A0 C8 00 -> 064 0 0 0 (latch UC.RASCAS) 116: 84 B8 55 -> 05C 0 5 5 117: B0 B8 99 -> 05C 0 9 9 (latch UC.FORMAT) 118: 00 E1 02 -> 070 1 0 2 JUMP 102 119: AC B8 88 -> 05C 0 8 8 (latch UC.RATE) 11A: 00 F9 1D -> 07C 1 1 D if /BLT.HFLIP JUMP 11D 11B: 01 82 A6 -> 0C1 0 A 6 11C: 00 E1 1E -> 070 1 1 E JUMP 11E 11D: 01 93 A6 -> 0C9 1 A 6 11E: 01 93 B7 -> 0C9 1 B 7 11F: B0 B8 99 -> 05C 0 9 9 (latch UC.FORMAT) 120: 84 B8 55 -> 05C 0 5 5 121: 01 B8 4E -> 0DC 0 4 E 122: 01 89 EE -> 0C4 1 E E 123: 02 B8 EE -> 15C 0 E E 124: 09 BE DD -> 0DF 0 D D 125: 00 E6 7C -> 073 0 7 C if N JUMP 07C 126: 01 82 8E -> 0C1 0 8 E 127: 00 D2 4E -> 069 0 4 E 128: 00 E5 33 -> 072 1 3 3 if Z JUMP 133 129: 01 93 4E -> 0C9 1 4 E 12A: 01 96 EE -> 0CB 0 E E 12B: 00 93 F7 -> 049 1 F 7 12C: 00 ED 32 -> 076 1 3 2 if C JUMP 132 12D: B4 B8 DD -> 05C 0 D D (latch UC.TRANS) 12E: B8 B8 77 -> 05C 0 7 7 (latch UC.XYPOS) 12F: A0 B8 33 -> 05C 0 3 3 (latch UC.RASCAS) 130: B8 B8 66 -> 05C 0 6 6 (latch UC.XYPOS) 131: BC C8 00 -> 064 0 0 0 (latch UC.SCAN) 132: 01 82 C7 -> 0C1 0 C 7 133: 01 83 D5 -> 0C1 1 D 5 134: 00 FD 36 -> 07E 1 3 6 if NC JUMP 136 135: 01 89 99 -> 0C4 1 9 9 136: 00 FB 36 -> 07D 1 3 6 if BLT.SCAN JUMP 136 137: A0 C8 00 -> 064 0 0 0 (latch UC.RASCAS) 138: 84 B8 55 -> 05C 0 5 5 139: B0 B8 99 -> 05C 0 9 9 (latch UC.FORMAT) 13A: 00 E1 24 -> 070 1 2 4 JUMP 124 13B: 08 3E 00 -> 01F 0 0 0 13C: 00 EF 3C -> 077 1 3 C if /MOTIMEP JUMP 13C 13D: 18 3E 00 -> 01F 0 0 0 13E: 00 E5 75 -> 072 1 7 5 if Z JUMP 175 13F: 00 C8 00 -> 064 0 0 0 140: 9C B8 44 -> 05C 0 4 4 141: 98 B8 44 -> 05C 0 4 4 142: 00 C8 00 -> 064 0 0 0 143: 00 C8 00 -> 064 0 0 0 144: 9C B8 11 -> 05C 0 1 1 145: 98 B8 11 -> 05C 0 1 1 146: 00 E1 5A -> 070 1 5 A JUMP 15A 147: 00 38 11 -> 01C 0 1 1 148: 03 34 00 -> 19A 0 0 0 149: 03 34 00 -> 19A 0 0 0 14A: 03 34 00 -> 19A 0 0 0 14B: 00 11 99 -> 008 1 9 9 14C: 00 EF 4C -> 077 1 4 C if /MOTIMEP JUMP 14C 14D: 02 34 00 -> 11A 0 0 0 14E: 84 B4 00 -> 05A 0 0 0 14F: 01 C8 00 -> 0E4 0 0 0 150: 9C B8 00 -> 05C 0 0 0 151: 98 B8 00 -> 05C 0 0 0 152: 00 C8 00 -> 064 0 0 0 153: 00 C8 00 -> 064 0 0 0 154: 9C B8 11 -> 05C 0 1 1 155: 98 B8 11 -> 05C 0 1 1 156: 00 C8 00 -> 064 0 0 0 157: 00 C8 00 -> 064 0 0 0 158: 9C B8 44 -> 05C 0 4 4 159: 98 B8 44 -> 05C 0 4 4 15A: 09 BE DD -> 0DF 0 D D 15B: 01 82 8E -> 0C1 0 8 E 15C: 00 D2 4E -> 069 0 4 E 15D: 00 E5 67 -> 072 1 6 7 if Z JUMP 167 15E: 01 93 4E -> 0C9 1 4 E 15F: 01 96 EE -> 0CB 0 E E 160: 00 93 F7 -> 049 1 F 7 161: 00 ED 67 -> 076 1 6 7 if C JUMP 167 162: B4 B8 DD -> 05C 0 D D (latch UC.TRANS) 163: B8 B8 77 -> 05C 0 7 7 (latch UC.XYPOS) 164: A0 B8 33 -> 05C 0 3 3 (latch UC.RASCAS) 165: B8 B8 66 -> 05C 0 6 6 (latch UC.XYPOS) 166: BC C8 00 -> 064 0 0 0 (latch UC.SCAN) 167: 01 82 C7 -> 0C1 0 C 7 168: 01 83 D5 -> 0C1 1 D 5 169: 00 FD 6C -> 07E 1 6 C if NC JUMP 16C 16A: 01 89 99 -> 0C4 1 9 9 16B: 01 C8 00 -> 0E4 0 0 0 16C: 01 96 00 -> 0CB 0 0 0 16D: 00 F5 6C -> 07A 1 6 C if NZ JUMP 16C 16E: A0 C8 00 -> 064 0 0 0 (latch UC.RASCAS) 16F: 84 B8 55 -> 05C 0 5 5 170: B0 B8 99 -> 05C 0 9 9 (latch UC.FORMAT) 171: 01 C8 00 -> 0E4 0 0 0 172: 01 96 00 -> 0CB 0 0 0 173: 00 F5 72 -> 07A 1 7 2 if NZ JUMP 172 174: 00 E1 5A -> 070 1 5 A JUMP 15A 175: 84 B8 88 -> 05C 0 8 8 176: 00 EF 76 -> 077 1 7 6 if /MOTIMEP JUMP 176 177: 19 BE AA -> 0DF 0 A A 178: B0 B8 AA -> 05C 0 A A (latch UC.FORMAT) 179: 01 C8 77 -> 0E4 0 7 7 17A: 84 B8 77 -> 05C 0 7 7 17B: 01 C8 BB -> 0E4 0 B B 17C: 00 C8 00 -> 064 0 0 0 17D: 00 C8 00 -> 064 0 0 0 17E: 00 C8 00 -> 064 0 0 0 17F: 08 3E 00 -> 01F 0 0 0 180: 01 80 BB -> 0C0 0 B B 181: 01 89 77 -> 0C4 1 7 7 182: 00 F5 7F -> 07A 1 7 F if NZ JUMP 17F 183: 84 B8 AA -> 05C 0 A A 184: 00 C8 00 -> 064 0 0 0 185: 00 C8 00 -> 064 0 0 0 186: 9C B8 BB -> 05C 0 B B 187: 98 B8 BB -> 05C 0 B B 188: 01 96 AA -> 0CB 0 A A 189: 00 F7 8B -> 07B 1 8 B if P JUMP 18B 18A: 00 E1 8A -> 070 1 8 A JUMP 18A 18B: 00 C8 00 -> 064 0 0 0 18C: 00 E1 78 -> 070 1 7 8 JUMP 178 ***************************************************************************/