/***************************************************************************
Sega 16-bit common hardware
****************************************************************************
Hang On
-------
Control Board (834-5668):
315-5011 -- sprite line comparitor
315-5012 -- sprite generator control
315-5049 (x2) -- tilemaps
315-5107 (PAL x2) -- horizontal timing control
315-5108 -- vertical timing control
315-5122 (PAL) -- timing
Enduro Racer
------------
CPU Side (171-5319):
315-5164 (PAL)
315-5165 (PAL)
315-5166 (PAL)
315-5167 (PAL)
Video Side (171-5320):
315-5049 (x2) -- tilemaps
315-5011 -- sprite line comparitor
315-5012 -- sprite generator control
315-5106 (PAL)
315-5107 (PAL)
315-5108 (PAL)
315-5168 (PAL)
315-5170 (PAL)
315-5171 (PAL)
315-5172 (PAL)
Pre-System 16
-------------
Main Board (171-5335):
315-5011 -- sprite line comparitor
315-5012 -- sprite generator control
315-5049 (x2) -- tilemaps
315-5107 (PAL) -- display timing
315-5108 (PAL) -- display timing
315-5141 (PAL) -- Z80 address decoding
315-5143 (PAL) -- sprite-related?
315-5144 (PAL) -- sprite-related?
315-5147 (PAL) -- unknown, DTACK-related
315-5149 (PAL) -- video mixing
315-5193 (PAL) -- 68000/MCU interface & address decoding
315-5202 (PAL) -- 68000/MCU interface & address decoding
Sega System 16A
---------------
Bottom Board (171-5307):
315-5011 -- sprite line comparitor
315-5012 -- sprite generator control
315-5049 (x2) -- tilemaps
315-5107 (PAL) -- display timing
315-5108 (PAL) -- display timing
315-5143 (PAL) -- sprite-related?
315-5144 (PAL) -- sprite-related?
315-5145 (PAL)
Top Board (171-5306):
315-5141 (PAL) -- Z80 address decoding
315-5142 (PAL)
315-5149 (PAL) -- video mixing
315-5150 (PAL)
Sega System 16B
---------------
Main Board (171-5357):
315-5195 -- memory mapper
315-5196 -- sprite generator
315-5197 -- tilemap generator
315-5213 (PAL) -- sprite-related
315-5214 (PAL) -- unknown
ROM Board (171-5521):
315-5298 (PAL)
ROM Board (171-5704):
315-5298 (PAL)
ROM Board (171-5797):
315-5248 -- hardware multiplier
315-5250 -- compare/timer
315-5298 (PAL)
Sega System 18
--------------
Main Board (171-5873B):
315-5242 -- color encoder
315-5296 -- I/O chip
315-5313 -- VDP
315-5360 -- memory mapper?
315-5361 -- sprite generator
315-5362 -- tilemap generator
315-5373 (PAL) -- video mixing
315-5374 (PAL) -- sprite timing
315-5375 (PAL) -- system timing
315-5389 (PAL) -- VDP sync
315-5390 (PAL)
315-5391 (PAL) -- Z80 address decoding
Main Board (171-5873-02B):
315-5242 -- color encoder
315-5296 -- I/O chip
315-5313 -- VDP
315-5360 -- memory mapper?
315-5361 -- sprite generator
315-5362 -- tilemap generator
315-5374 (PAL) -- sprite timing
315-5375 (PAL) -- system timing
315-5389 (PAL) -- VDP sync
315-5391 (PAL) -- Z80 address decoding
315-5430 (PAL) -- video mixing
ROM Board (171-5987A):
315-5436 -- tile/sprite banking
Sega System C
-------------
Main Board:
315-5242 -- color encoder
315-5296 -- I/O chip
315-5313 -- VDP
315-5393 (PAL)
315-5394 (PAL)
315-5395 (PAL)
Super Hang On
-------------
CPU Board 171-5376-01:
315-5195 -- memory mapper
315-5218 -- PCM sound controller
315-5155 (PAL x2) -- road bit extraction
315-5222 (PAL) -- road mixing
315-5223a (PAL)
315-5224 (PAL)
315-5225 (PAL)
315-5226 (PAL)
VIDEO Board: (not the same as out run !) 171-5480
315-5196 -- sprite generator
315-5197 -- tilemap generator
315-5213 (PAL) -- sprite-related
315-5242 -- color encoder
315-5251 (PAL)
Out Run
-------
CPU Board 837-6063-01:
315-5195 -- memory mapper
315-5218 -- PCM sound controller
315-5155 (PAL x2) -- road bit extraction
315-5222 (PAL) -- road mixing
315-5223a (PAL)
315-5224 (PAL)
315-5225 (PAL)
315-5226 (PAL)
VIDEO Board: 837-6064, 171-5377-01
315-5197 -- tilemap generator
315-5211 -- sprite generator
315-5227a (PAL)
315-5228 (PAL)
315-5242 -- color encoder
Sega System 32
--------------
Main Board (317-5964):
315-5242 -- color encoder
315-5296 -- I/O chip
315-5385
315-5386 -- tilemap generator
315-5387 -- sprite generator
315-5388 -- video mixing
315-5441 (PAL)
315-5476
X-Board
-------
Main Board:
315-5197 -- tilemap generator
315-5211A -- sprite generator
315-5218 -- PCM sound controller
315-5242 -- color encoder
315-5248 (x2) -- hardware multiplier
315-5249 (x2) -- hardware divider
315-5250 (x2) -- compare/timer
315-5275 -- road generator
315-5278 (PAL) -- sprite ROM bank control
315-5279 (PAL) -- video mixing (Afterburner)
315-5280 (PAL) -- Z80 address decoding
315-5290 (PAL) -- main CPU address decoding
315-5291 (PAL) -- main CPU address decoding
315-5304 (PAL) -- video mixing (Line of Fire)
Y-Board
-------
Main Board (837-6565):
315-5218 -- PCM sound controller
315-5248 (x3) -- hardware multiplier
315-5249 (x3) -- hardware divider
315-5280 (PAL) -- Z80 address decoding
315-5296 -- I/O chip
315-5314 (PAL)
315-5315 (PAL)
315-5316 (PAL)
315-5317 (PAL)
315-5318 (PAL)
315-5328 (PAL)
Video Board (837-6566):
315-5196 -- sprite generator
315-5213 (PAL) -- sprite-related
315-5242 -- color encoder
315-5305 -- sprite generator
315-5306 (x2) -- video sync and rotation
315-5312 -- video mixing
315-5319 (PAL)
315-5325 (PAL)
Custom parts
------------
SYS1 SYS2 HANG ENDU PR16 S16A S16B SY18 SHNG ORUN XBRD YBRD SYSC SY24 SY32
315-5011: xx xx xx xx xx xx -- sprite line comparitor
315-5012: xx xx xx xx xx xx -- sprite generator control
315-5049: xx x2 x2 x2 x2 -- tilemap generator
315-5195: xx xx xx -- memory mapper
315-5196: xx xx xx -- sprite genereator
315-5197: xx xx xx xx -- tilemap generator
315-5211: xx -- sprite generator
315-5211A: xx -- sprite generator
315-5218: xx xx xx xx -- PCM sound controller
315-5242: xx xx xx xx xx xx xx xx -- color encoder
315-5248: xx x2 x3 -- hardware multiplier
315-5249: x2 x3 -- hardware divider
315-5250: xx x2 -- compare/timer
315-5275: xx -- road generator
315-5296: xx xx xx xx -- I/O chip
315-5305: xx --
315-5312: xx -- video mixing
315-5313: xx xx -- VDP
315-5360: xx -- memory mapper
315-5361: xx -- sprite generator
315-5362: xx -- tilemap generator
315-5385: xx -- ???
315-5386: xx -- tilemap generator
315-5387: xx -- sprite generator
315-5388: xx -- video mixing
315-5436: xx -- sprite/tile banking
315-5476: xx -- ????
****************************************************************************
Sega system16 and friends hardware
CPU Tiles Sprites Priority Color SCPU Sound Other
System C 68000 315-5313 315-5242 z80 ym3438 315-5296(IO)
Space Harrier 68000x2 (c) z80 ym2203 pcm(b)
System 16B 68000 315-5197 315-5196 GAL (c) z80 ym2151 upd7759 315-5195
After Burner 68000x2 315-5197 315-5211A GAL 315-5242 z80 ym2151 315-5218 315-5250(a) 315-5248(x2) 315-5249(x2) 315-5275(road)
System 18 68000 315-536x 315-536x 315-5242 z80 ym3834(x2) RF5c68(d) 315-3296(IO) 315-5313(vdp)
System 24 68000x2 315-5292 315-5293 315-5294 315-5242 ym2151 dac 315-5195(x3) 315-5296(IO)
Galaxy Force 68000x3 315-5296+ 315-5312 315-5242 z80 ym2151 315-5218 315-5296(IO)
System 32 V60 315-5386A 315-5387 315-5388 315-5242 z80 ym3834(x2) RF5c68(d) 315-5296(IO)
a) 315-5250: 68000 glue and address decoding
b) 8x8-bit voices entirely in TTL. The 315-5218 is believed to be the
integrated version of that
c) Resistor network and latches believed to be equivalent to the 315-5242
d) Also seen as 315-5476A and ASSP 5c105 and ASSP 5c68a
Quick review of the system16 hardware:
Hang-on hardware:
The first one. Two tilemap planes, one sprite plane, one road
plane. The shadow capability doesn't seem to be used, the
highlight/shadow switch in the 5242-equivalent is global for all
colors.
Space harrier hardware:
Similar to hang-on, with per-color highlight/shadow selection, and
the shadows are used.
System16a / Pre-system16:
Space harrier without the road generator.
System16b:
4-layer tilemap hardware in two pairs, with selection between each
members on the pairs on a 8-lines basis. Slightly better sprites.
System18
System 16b plus a genesis vdp.
Outrun:
System 16b tilemaps, frame buffered sprites with better zooming
capabilities, and a road generator able to handle two roads
simultaneously.
Super hang-on:
Outrun lite, with System 16b sprites instead of the frame buffered
sprites, and only one of the two roads is actually used.
X-Board:
Outrun with a better fillrate and an even more flexible road
generator.
Y-Board:
New design, with two sprite planes and no tilemaps. The back
sprite plane has a huge fillrate and the capability to have its
frame buffer completely rotated. Also, it has a palette
indirection capability to allows for easier palette rotations.
The front sprite plane is System 16b.
System24:
The odd one out. Medium resolution. Entirely ram-based, no
graphics roms. 4-layer tilemap hardware in two pairs, selection
on a 8-pixels basis. Tile-based sprites(!) organised as a linked
list. The tilemap chip has been reused for model1 and model2,
probably because they had it handy and it handles medium res.
System32:
5-layer tilemap hardware consisting of 4 independant rom-based
layers with linescroll, lineselection, linezoom and window
clipping capability and one simpler ram-based text plane. Mixed
ram/rom sprite engine with palette indirection, per-color priority
(thankfully not actually used). The sprite list includes jumping
and clipping capabilities, and advanced hot-spot positioning. The
mixer chip adds totally dynamic priorities, alpha-blending of the
tilemaps, per-component color control, and some other funnies we
have not been able to decypher.
ST-V (also know as Titan or the Saturn console):
The ultimate 2D system. Even more advanced tilemaps, with 6-dof
roz support, alpha up to the wazoo and other niceties, known as
the vdp2. Ths sprite engine, vdp1, allows for any 4-point
streching of the sprites, actually giving polygonal 3D
capabilities. Interestingly, the mixer capabilities took a hit,
with no real per-sprite mixer priority, which could be considered
annoying for a 2D system. It still allowed some beauties like
Radiant Silvergun.
***************************************************************************/
#include "driver.h"
#include "segaic16.h"
#include "video/resnet.h"
/*************************************
*
* Debugging
*
*************************************/
#define PRINT_UNUSUAL_MODES (0)
/*************************************
*
* Type definitions
*
*************************************/
struct palette_info
{
INT32 entries; /* number of entries (not counting shadows) */
UINT8 normal[32]; /* RGB translations for normal pixels */
UINT8 shadow[32]; /* RGB translations for shadowed pixels */
UINT8 hilight[32]; /* RGB translations for hilighted pixels */
};
struct tilemap_callback_info
{
UINT16 * rambase; /* base of RAM for this tilemap page */
const UINT8 * bank; /* pointer to bank array */
UINT16 banksize; /* size of banks */
};
struct tilemap_info
{
UINT8 index; /* index of this structure */
UINT8 type; /* type of tilemap (see segaic16.h for details) */
UINT8 numpages; /* number of allocated pages */
UINT8 flip; /* screen flip? */
UINT8 rowscroll, colscroll; /* are rowscroll/colscroll enabled (if external enables are used) */
UINT8 bank[8]; /* indexes of the tile banks */
UINT16 banksize; /* number of tiles per bank */
UINT16 latched_xscroll[4]; /* latched X scroll values */
UINT16 latched_yscroll[4]; /* latched Y scroll values */
UINT16 latched_pageselect[4]; /* latched page select values */
INT32 xoffs; /* X scroll offset */
tilemap *tilemaps[16]; /* up to 16 tilemap pages */
tilemap *textmap; /* a single text tilemap */
struct tilemap_callback_info tilemap_info[16]; /* callback info for 16 tilemap pages */
struct tilemap_callback_info textmap_info; /* callback info for a single textmap page */
void (*reset)(struct tilemap_info *info);/* reset callback */
void (*draw_layer)(running_machine *machine, struct tilemap_info *info, mame_bitmap *bitmap, const rectangle *cliprect, int which, int flags, int priority);
UINT16 * textram; /* pointer to textram pointer */
UINT16 * tileram; /* pointer to tileram pointer */
};
struct sprite_info
{
UINT8 index; /* index of this structure */
UINT8 type; /* type of sprite system (see segaic16.h for details) */
UINT8 flip; /* screen flip? */
UINT8 shadow; /* shadow or hilight? */
UINT8 bank[16]; /* banking redirection */
UINT16 colorbase; /* base color index */
INT32 ramsize; /* size of sprite RAM */
INT32 xoffs; /* X scroll offset */
void (*draw)(struct sprite_info *info, mame_bitmap *bitmap, const rectangle *cliprect);
UINT16 * spriteram; /* pointer to spriteram pointer */
UINT16 * buffer; /* buffered spriteram for those that use it */
};
struct road_info
{
UINT8 index; /* index of this structure */
UINT8 type; /* type of road system (see segaic16.h for details) */
UINT8 control; /* control register value */
UINT16 colorbase1; /* color base for road ROM data */
UINT16 colorbase2; /* color base for road background data */
UINT16 colorbase3; /* color base for sky data */
INT32 xoffs; /* X scroll offset */
void (*draw)(struct road_info *info, mame_bitmap *bitmap, const rectangle *cliprect, int priority);
UINT16 * roadram; /* pointer to roadram pointer */
UINT16 * buffer; /* buffered roadram pointer */
UINT8 * gfx; /* expanded road graphics */
};
struct rotate_info
{
UINT8 index; /* index of this structure */
UINT8 type; /* type of rotate system (see segaic16.h for details) */
UINT16 colorbase; /* base color index */
INT32 ramsize; /* size of rotate RAM */
UINT16 * rotateram; /* pointer to rotateram pointer */
UINT16 * buffer; /* buffered data */
};
/*************************************
*
* Globals
*
*************************************/
UINT8 segaic16_display_enable;
UINT16 *segaic16_tileram_0;
UINT16 *segaic16_textram_0;
UINT16 *segaic16_spriteram_0;
UINT16 *segaic16_spriteram_1;
UINT16 *segaic16_roadram_0;
UINT16 *segaic16_rotateram_0;
/*************************************
*
* Statics
*
*************************************/
static struct palette_info palette;
static struct tilemap_info bg_tilemap[SEGAIC16_MAX_TILEMAPS];
static struct sprite_info sprites[SEGAIC16_MAX_SPRITES];
static struct road_info road[SEGAIC16_MAX_ROADS];
static struct rotate_info rotate[SEGAIC16_MAX_ROTATE];
/*************************************
*
* Misc functions
*
*************************************/
void segaic16_set_display_enable(int enable)
{
enable = (enable != 0);
if (segaic16_display_enable != enable)
{
video_screen_update_partial(0, video_screen_get_vpos(0));
segaic16_display_enable = enable;
}
}
/*************************************
*
* Palette computation
*
*************************************/
/*
Color generation details
Each color is made up of 5 bits, connected through one or more resistors like so:
Bit 0 = 1 x 3.9K ohm
Bit 1 = 1 x 2.0K ohm
Bit 2 = 1 x 1.0K ohm
Bit 3 = 2 x 1.0K ohm
Bit 4 = 4 x 1.0K ohm
Another data bit is connected by a tristate buffer to the color output through a
470 ohm resistor. The buffer allows the resistor to have no effect (tristate),
halve brightness (pull-down) or double brightness (pull-up). The data bit source
is bit 15 of each color RAM entry.
*/
void segaic16_palette_init(int entries)
{
static const int resistances_normal[6] = { 3900, 2000, 1000, 1000/2, 1000/4, 0 };
static const int resistances_sh[6] = { 3900, 2000, 1000, 1000/2, 1000/4, 470 };
double weights[2][6];
int i;
/* compute the number of palette entries */
palette.entries = entries;
/* compute weight table for regular palette entries */
compute_resistor_weights(0, 255, -1.0,
6, resistances_normal, weights[0], 0, 0,
0, NULL, NULL, 0, 0,
0, NULL, NULL, 0, 0);
/* compute weight table for shadow/hilight palette entries */
compute_resistor_weights(0, 255, -1.0,
6, resistances_sh, weights[1], 0, 0,
0, NULL, NULL, 0, 0,
0, NULL, NULL, 0, 0);
/* compute R, G, B for each weight */
for (i = 0; i < 32; i++)
{
int i4 = (i >> 4) & 1;
int i3 = (i >> 3) & 1;
int i2 = (i >> 2) & 1;
int i1 = (i >> 1) & 1;
int i0 = (i >> 0) & 1;
palette.normal[i] = combine_6_weights(weights[0], i0, i1, i2, i3, i4, 0);
palette.shadow[i] = combine_6_weights(weights[1], i0, i1, i2, i3, i4, 0);
palette.hilight[i] = combine_6_weights(weights[1], i0, i1, i2, i3, i4, 1);
}
}
/*************************************
*
* Palette accessors
*
*************************************/
WRITE16_HANDLER( segaic16_paletteram_w )
{
UINT16 newval;
int r, g, b;
/* get the new value */
newval = paletteram16[offset];
COMBINE_DATA(&newval);
paletteram16[offset] = newval;
/* byte 0 byte 1 */
/* sBGR BBBB GGGG RRRR */
/* x000 4321 4321 4321 */
r = ((newval >> 12) & 0x01) | ((newval << 1) & 0x1e);
g = ((newval >> 13) & 0x01) | ((newval >> 3) & 0x1e);
b = ((newval >> 14) & 0x01) | ((newval >> 7) & 0x1e);
/* normal colors */
palette_set_color_rgb(Machine, offset + 0 * palette.entries, palette.normal[r], palette.normal[g], palette.normal[b]);
palette_set_color_rgb(Machine, offset + 1 * palette.entries, palette.shadow[r], palette.shadow[g], palette.shadow[b]);
palette_set_color_rgb(Machine, offset + 2 * palette.entries, palette.hilight[r], palette.hilight[g], palette.hilight[b]);
}
/*************************************
*
* Draw a split tilemap in up to
* four pieces
*
*************************************/
static void segaic16_draw_virtual_tilemap(running_machine *machine, struct tilemap_info *info, mame_bitmap *bitmap, const rectangle *cliprect, UINT16 pages, UINT16 xscroll, UINT16 yscroll, UINT32 flags, UINT32 priority)
{
int leftmin = -1, leftmax = -1, rightmin = -1, rightmax = -1;
int topmin = -1, topmax = -1, bottommin = -1, bottommax = -1;
rectangle pageclip;
int page;
/* which half/halves of the virtual tilemap do we intersect in the X direction? */
if (xscroll < 64*8 - machine->screen[0].width)
{
leftmin = 0;
leftmax = machine->screen[0].width - 1;
rightmin = -1;
}
else if (xscroll < 64*8)
{
leftmin = 0;
leftmax = 64*8 - xscroll - 1;
rightmin = leftmax + 1;
rightmax = machine->screen[0].width - 1;
}
else if (xscroll < 128*8 - machine->screen[0].width)
{
rightmin = 0;
rightmax = machine->screen[0].width - 1;
leftmin = -1;
}
else
{
rightmin = 0;
rightmax = 128*8 - xscroll - 1;
leftmin = rightmax + 1;
leftmax = machine->screen[0].width - 1;
}
/* which half/halves of the virtual tilemap do we intersect in the Y direction? */
if (yscroll < 32*8 - machine->screen[0].height)
{
topmin = 0;
topmax = machine->screen[0].height - 1;
bottommin = -1;
}
else if (yscroll < 32*8)
{
topmin = 0;
topmax = 32*8 - yscroll - 1;
bottommin = topmax + 1;
bottommax = machine->screen[0].height - 1;
}
else if (yscroll < 64*8 - machine->screen[0].height)
{
bottommin = 0;
bottommax = machine->screen[0].height - 1;
topmin = -1;
}
else
{
bottommin = 0;
bottommax = 64*8 - yscroll - 1;
topmin = bottommax + 1;
topmax = machine->screen[0].height - 1;
}
/* if the tilemap is flipped, we need to flip our sense within each quadrant */
if (info->flip)
{
if (leftmin != -1)
{
int temp = leftmin;
leftmin = machine->screen[0].width - 1 - leftmax;
leftmax = machine->screen[0].width - 1 - temp;
}
if (rightmin != -1)
{
int temp = rightmin;
rightmin = machine->screen[0].width - 1 - rightmax;
rightmax = machine->screen[0].width - 1 - temp;
}
if (topmin != -1)
{
int temp = topmin;
topmin = machine->screen[0].height - 1 - topmax;
topmax = machine->screen[0].height - 1 - temp;
}
if (bottommin != -1)
{
int temp = bottommin;
bottommin = machine->screen[0].height - 1 - bottommax;
bottommax = machine->screen[0].height - 1 - temp;
}
}
/* draw the upper-left chunk */
if (leftmin != -1 && topmin != -1)
{
pageclip.min_x = (leftmin < cliprect->min_x) ? cliprect->min_x : leftmin;
pageclip.max_x = (leftmax > cliprect->max_x) ? cliprect->max_x : leftmax;
pageclip.min_y = (topmin < cliprect->min_y) ? cliprect->min_y : topmin;
pageclip.max_y = (topmax > cliprect->max_y) ? cliprect->max_y : topmax;
if (pageclip.min_x <= pageclip.max_x && pageclip.min_y <= pageclip.max_y)
{
page = (pages >> 0) & 0xf;
tilemap_set_scrollx(info->tilemaps[page], 0, xscroll);
tilemap_set_scrolly(info->tilemaps[page], 0, yscroll);
tilemap_draw(bitmap, &pageclip, info->tilemaps[page], flags, priority);
}
}
/* draw the upper-right chunk */
if (rightmin != -1 && topmin != -1)
{
pageclip.min_x = (rightmin < cliprect->min_x) ? cliprect->min_x : rightmin;
pageclip.max_x = (rightmax > cliprect->max_x) ? cliprect->max_x : rightmax;
pageclip.min_y = (topmin < cliprect->min_y) ? cliprect->min_y : topmin;
pageclip.max_y = (topmax > cliprect->max_y) ? cliprect->max_y : topmax;
if (pageclip.min_x <= pageclip.max_x && pageclip.min_y <= pageclip.max_y)
{
page = (pages >> 4) & 0xf;
tilemap_set_scrollx(info->tilemaps[page], 0, xscroll);
tilemap_set_scrolly(info->tilemaps[page], 0, yscroll);
tilemap_draw(bitmap, &pageclip, info->tilemaps[page], flags, priority);
}
}
/* draw the lower-left chunk */
if (leftmin != -1 && bottommin != -1)
{
pageclip.min_x = (leftmin < cliprect->min_x) ? cliprect->min_x : leftmin;
pageclip.max_x = (leftmax > cliprect->max_x) ? cliprect->max_x : leftmax;
pageclip.min_y = (bottommin < cliprect->min_y) ? cliprect->min_y : bottommin;
pageclip.max_y = (bottommax > cliprect->max_y) ? cliprect->max_y : bottommax;
if (pageclip.min_x <= pageclip.max_x && pageclip.min_y <= pageclip.max_y)
{
page = (pages >> 8) & 0xf;
tilemap_set_scrollx(info->tilemaps[page], 0, xscroll);
tilemap_set_scrolly(info->tilemaps[page], 0, yscroll);
tilemap_draw(bitmap, &pageclip, info->tilemaps[page], flags, priority);
}
}
/* draw the lower-right chunk */
if (rightmin != -1 && bottommin != -1)
{
pageclip.min_x = (rightmin < cliprect->min_x) ? cliprect->min_x : rightmin;
pageclip.max_x = (rightmax > cliprect->max_x) ? cliprect->max_x : rightmax;
pageclip.min_y = (bottommin < cliprect->min_y) ? cliprect->min_y : bottommin;
pageclip.max_y = (bottommax > cliprect->max_y) ? cliprect->max_y : bottommax;
if (pageclip.min_x <= pageclip.max_x && pageclip.min_y <= pageclip.max_y)
{
page = (pages >> 12) & 0xf;
tilemap_set_scrollx(info->tilemaps[page], 0, xscroll);
tilemap_set_scrolly(info->tilemaps[page], 0, yscroll);
tilemap_draw(bitmap, &pageclip, info->tilemaps[page], flags, priority);
}
}
}
/*******************************************************************************************
*
* Hang On/System 16A-style tilemaps
*
* 4 total pages (Hang On)
* 8 total pages (System 16A)
* Column/rowscroll enabled via external signals
*
* Tile format:
* Bits Usage
* ??------ -------- Unknown
* --b----- -------- Tile bank select
* ---p---- -------- Tile priority versus sprites
* ----cccc ccc----- Tile color palette
* ----nnnn nnnnnnnn Tile index
*
* Text format:
* Bits Usage
* ????---- -------- Unknown
* ----p--- -------- Priority
* -----ccc -------- Tile color palette
* -------- nnnnnnnn Tile index
*
* Text RAM:
* Offset Bits Usage
* E8C -aaa-bbb -ccc-ddd Background tilemap page select (screen flipped)
* E8E -aaa-bbb -ccc-ddd Foreground tilemap page select (screen flipped)
* E9C -aaa-bbb -ccc-ddd Background tilemap page select
* E9E -aaa-bbb -ccc-ddd Foreground tilemap page select
* F24 -------- vvvvvvvv Foreground tilemap vertical scroll
* F26 -------- vvvvvvvv Background tilemap vertical scroll
* F30-F7D -------- vvvvvvvv Foreground tilemap per-16-pixel-column vertical scroll (every 2 words)
* F32-F7F -------- vvvvvvvv Background tilemap per-16-pixel-column vertical scroll (every 2 words)
* F80-FED -------h hhhhhhhh Foreground tilemap per-8-pixel-row horizontal scroll (every 2 words)
* F82-FEF -------h hhhhhhhh Background tilemap per-8-pixel-row horizontal scroll (every 2 words)
* FF8 -------h hhhhhhhh Foreground tilemap horizontal scroll
* FFA -------h hhhhhhhh Background tilemap horizontal scroll
*
*******************************************************************************************/
static TILE_GET_INFO( segaic16_tilemap_16a_tile_info )
{
const struct tilemap_callback_info *info = param;
UINT16 data = info->rambase[tile_index];
int code = ((data >> 1) & 0x1000) | (data & 0xfff);
int color = (data >> 5) & 0x7f;
SET_TILE_INFO(0, code, color, 0);
tileinfo->category = (data >> 12) & 1;
}
static TILE_GET_INFO( segaic16_tilemap_16a_text_info )
{
const struct tilemap_callback_info *info = param;
UINT16 data = info->rambase[tile_index];
int color = (data >> 8) & 0x07;
int code = data & 0xff;
SET_TILE_INFO(0, code, color, 0);
tileinfo->category = (data >> 11) & 1;
}
static void segaic16_tilemap_16a_draw_layer(running_machine *machine, struct tilemap_info *info, mame_bitmap *bitmap, const rectangle *cliprect, int which, int flags, int priority)
{
UINT16 *textram = info->textram;
/* note that the scrolling for these games can only scroll as much as the top-left */
/* page; in order to scroll beyond that they swap pages and reset the scroll value */
UINT16 xscroll = textram[0xff8/2 + which] & 0x1ff;
UINT16 yscroll = textram[0xf24/2 + which] & 0x0ff;
UINT16 pages = textram[(info->flip ? 0xe8e/2 : 0xe9e/2) - which];
int x, y;
/* pages are swapped along the X direction, and there are only 8 of them */
pages = ((pages >> 4) & 0x0707) | ((pages << 4) & 0x7070);
if (info->numpages == 4)
pages &= 0x3333;
/* column AND row scroll */
if (info->colscroll && info->rowscroll)
{
if (PRINT_UNUSUAL_MODES) mame_printf_debug("Column AND row scroll\n");
/* loop over row chunks */
for (y = cliprect->min_y & ~7; y <= cliprect->max_y; y += 8)
{
int rowscrollindex = (info->flip ? (216 - y) : y) / 8;
rectangle rowcolclip;
/* adjust to clip this row only */
rowcolclip.min_y = (y < cliprect->min_y) ? cliprect->min_y : y;
rowcolclip.max_y = (y + 7 > cliprect->max_y) ? cliprect->max_y : y + 7;
/* loop over column chunks */
for (x = cliprect->min_x & ~15; x <= cliprect->max_x; x += 16)
{
UINT16 effxscroll, effyscroll;
/* adjust to clip this column only */
rowcolclip.min_x = (x < cliprect->min_x) ? cliprect->min_x : x;
rowcolclip.max_x = (x + 15 > cliprect->max_x) ? cliprect->max_x : x + 15;
/* get the effective scroll values */
effxscroll = textram[0xf80/2 + rowscrollindex * 2 + which] & 0x1ff;
effyscroll = textram[0xf30/2 + (x/16) * 2 + which] & 0x0ff;
/* adjust the xscroll for flipped screen */
if (info->flip)
effxscroll += 17;
/* draw the chunk */
effxscroll = (0xc8 - effxscroll + info->xoffs) & 0x3ff;
effyscroll = effyscroll & 0x1ff;
segaic16_draw_virtual_tilemap(machine, info, bitmap, &rowcolclip, pages, effxscroll, effyscroll, flags, priority);
}
}
}
else if (info->colscroll)
{
if (PRINT_UNUSUAL_MODES) mame_printf_debug("Column scroll\n");
/* loop over column chunks */
for (x = cliprect->min_x & ~15; x <= cliprect->max_x; x += 16)
{
rectangle colclip = *cliprect;
UINT16 effxscroll, effyscroll;
/* adjust to clip this row only */
colclip.min_x = (x < cliprect->min_x) ? cliprect->min_x : x;
colclip.max_x = (x + 15 > cliprect->max_x) ? cliprect->max_x : x + 15;
/* get the effective scroll values */
effxscroll = xscroll;
effyscroll = textram[0xf30/2 + (x/16) * 2 + which] & 0x0ff;
/* adjust the xscroll for flipped screen */
if (info->flip)
effxscroll += 17;
/* draw the chunk */
effxscroll = (0xc8 - effxscroll + info->xoffs) & 0x3ff;
effyscroll = effyscroll & 0x1ff;
segaic16_draw_virtual_tilemap(machine, info, bitmap, &colclip, pages, effxscroll, effyscroll, flags, priority);
}
}
else if (info->rowscroll)
{
if (PRINT_UNUSUAL_MODES) mame_printf_debug("Row scroll\n");
/* loop over row chunks */
for (y = cliprect->min_y & ~7; y <= cliprect->max_y; y += 8)
{
int rowscrollindex = (info->flip ? (216 - y) : y) / 8;
rectangle rowclip = *cliprect;
UINT16 effxscroll, effyscroll;
/* adjust to clip this row only */
rowclip.min_y = (y < cliprect->min_y) ? cliprect->min_y : y;
rowclip.max_y = (y + 7 > cliprect->max_y) ? cliprect->max_y : y + 7;
/* get the effective scroll values */
effxscroll = textram[0xf80/2 + rowscrollindex * 2 + which] & 0x1ff;
effyscroll = yscroll;
/* adjust the xscroll for flipped screen */
if (info->flip)
effxscroll += 17;
/* draw the chunk */
effxscroll = (0xc8 - effxscroll + info->xoffs) & 0x3ff;
effyscroll = effyscroll & 0x1ff;
segaic16_draw_virtual_tilemap(machine, info, bitmap, &rowclip, pages, effxscroll, effyscroll, flags, priority);
}
}
else
{
/* adjust the xscroll for flipped screen */
if (info->flip)
xscroll += 17;
xscroll = (0xc8 - xscroll + info->xoffs) & 0x3ff;
yscroll = yscroll & 0x1ff;
segaic16_draw_virtual_tilemap(machine, info, bitmap, cliprect, pages, xscroll, yscroll, flags, priority);
}
}
/*******************************************************************************************
*
* System 16B-style tilemaps
*
* 16 total pages
* Column/rowscroll enabled via bits in text layer
* Alternate tilemap support
*
* Tile format:
* Bits Usage
* p------- -------- Tile priority versus sprites
* -??----- -------- Unknown
* ---ccccc cc------ Tile color palette
* ---nnnnn nnnnnnnn Tile index
*
* Text format:
* Bits Usage
* p------- -------- Tile priority versus sprites
* -???---- -------- Unknown
* ----ccc- -------- Tile color palette
* -------n nnnnnnnn Tile index
*
* Alternate tile format:
* Bits Usage
* p------- -------- Tile priority versus sprites
* -??----- -------- Unknown
* ----cccc ccc----- Tile color palette
* ---nnnnn nnnnnnnn Tile index
*
* Alternate text format:
* Bits Usage
* p------- -------- Tile priority versus sprites
* -???---- -------- Unknown
* -----ccc -------- Tile color palette
* -------- nnnnnnnn Tile index
*
* Text RAM:
* Offset Bits Usage
* E80 aaaabbbb ccccdddd Foreground tilemap page select
* E82 aaaabbbb ccccdddd Background tilemap page select
* E84 aaaabbbb ccccdddd Alternate foreground tilemap page select
* E86 aaaabbbb ccccdddd Alternate background tilemap page select
* E90 c------- -------- Foreground tilemap column scroll enable
* -------v vvvvvvvv Foreground tilemap vertical scroll
* E92 c------- -------- Background tilemap column scroll enable
* -------v vvvvvvvv Background tilemap vertical scroll
* E94 -------v vvvvvvvv Alternate foreground tilemap vertical scroll
* E96 -------v vvvvvvvv Alternate background tilemap vertical scroll
* E98 r------- -------- Foreground tilemap row scroll enable
* ------hh hhhhhhhh Foreground tilemap horizontal scroll
* E9A r------- -------- Background tilemap row scroll enable
* ------hh hhhhhhhh Background tilemap horizontal scroll
* E9C ------hh hhhhhhhh Alternate foreground tilemap horizontal scroll
* E9E ------hh hhhhhhhh Alternate background tilemap horizontal scroll
* F16-F3F -------- vvvvvvvv Foreground tilemap per-16-pixel-column vertical scroll
* F56-F7F -------- vvvvvvvv Background tilemap per-16-pixel-column vertical scroll
* F80-FB7 a------- -------- Foreground tilemap per-8-pixel-row alternate tilemap enable
* -------h hhhhhhhh Foreground tilemap per-8-pixel-row horizontal scroll
* FC0-FF7 a------- -------- Background tilemap per-8-pixel-row alternate tilemap enable
* -------h hhhhhhhh Background tilemap per-8-pixel-row horizontal scroll
*
*******************************************************************************************/
static TILE_GET_INFO( segaic16_tilemap_16b_tile_info )
{
const struct tilemap_callback_info *info = param;
UINT16 data = info->rambase[tile_index];
int color = (data >> 6) & 0x7f;
int code = data & 0x1fff;
code = info->bank[code / info->banksize] * info->banksize + code % info->banksize;
SET_TILE_INFO(0, code, color, 0);
tileinfo->category = (data >> 15) & 1;
}
static TILE_GET_INFO( segaic16_tilemap_16b_text_info )
{
const struct tilemap_callback_info *info = param;
UINT16 data = info->rambase[tile_index];
int bank = info->bank[0];
int color = (data >> 9) & 0x07;
int code = data & 0x1ff;
SET_TILE_INFO(0, bank * info->banksize + code, color, 0);
tileinfo->category = (data >> 15) & 1;
}
static TILE_GET_INFO( segaic16_tilemap_16b_alt_tile_info )
{
const struct tilemap_callback_info *info = param;
UINT16 data = info->rambase[tile_index];
int color = (data >> 5) & 0x7f;
int code = data & 0x1fff;
code = info->bank[code / info->banksize] * info->banksize + code % info->banksize;
SET_TILE_INFO(0, code, color, 0);
tileinfo->category = (data >> 15) & 1;
}
static TILE_GET_INFO( segaic16_tilemap_16b_alt_text_info )
{
const struct tilemap_callback_info *info = param;
UINT16 data = info->rambase[tile_index];
int bank = info->bank[0];
int color = (data >> 8) & 0x07;
int code = data & 0xff;
SET_TILE_INFO(0, bank * info->banksize + code, color, 0);
tileinfo->category = (data >> 15) & 1;
}
static void segaic16_tilemap_16b_draw_layer(running_machine *machine, struct tilemap_info *info, mame_bitmap *bitmap, const rectangle *cliprect, int which, int flags, int priority)
{
UINT16 *textram = info->textram;
UINT16 xscroll, yscroll, pages;
int x, y;
/* get global values */
xscroll = info->latched_xscroll[which];
yscroll = info->latched_yscroll[which];
pages = info->latched_pageselect[which];
/* column scroll? */
if (yscroll & 0x8000)
{
if (PRINT_UNUSUAL_MODES) mame_printf_debug("Column AND row scroll\n");
/* loop over row chunks */
for (y = cliprect->min_y & ~7; y <= cliprect->max_y; y += 8)
{
int rowscrollindex = (info->flip ? (216 - y) : y) / 8;
rectangle rowcolclip;
/* adjust to clip this row only */
rowcolclip.min_y = (y < cliprect->min_y) ? cliprect->min_y : y;
rowcolclip.max_y = (y + 7 > cliprect->max_y) ? cliprect->max_y : y + 7;
/* loop over column chunks */
for (x = ((cliprect->min_x + 8) & ~15) - 8; x <= cliprect->max_x; x += 16)
{
UINT16 effxscroll, effyscroll, rowscroll;
UINT16 effpages = pages;
/* adjust to clip this column only */
rowcolclip.min_x = (x < cliprect->min_x) ? cliprect->min_x : x;
rowcolclip.max_x = (x + 15 > cliprect->max_x) ? cliprect->max_x : x + 15;
/* get the effective scroll values */
rowscroll = textram[0xf80/2 + 0x40/2 * which + rowscrollindex];
effxscroll = (xscroll & 0x8000) ? rowscroll : xscroll;
effyscroll = textram[0xf16/2 + 0x40/2 * which + (x+8)/16];
/* are we using an alternate? */
if (rowscroll & 0x8000)
{
effxscroll = info->latched_xscroll[which + 2];
effyscroll = info->latched_yscroll[which + 2];
effpages = info->latched_pageselect[which + 2];
}
/* draw the chunk */
effxscroll = (0xc0 - effxscroll + info->xoffs) & 0x3ff;
effyscroll = effyscroll & 0x1ff;
segaic16_draw_virtual_tilemap(machine, info, bitmap, &rowcolclip, effpages, effxscroll, effyscroll, flags, priority);
}
}
}
else
{
if (PRINT_UNUSUAL_MODES) mame_printf_debug("Row scroll\n");
/* loop over row chunks */
for (y = cliprect->min_y & ~7; y <= cliprect->max_y; y += 8)
{
int rowscrollindex = (info->flip ? (216 - y) : y) / 8;
rectangle rowclip = *cliprect;
UINT16 effxscroll, effyscroll, rowscroll;
UINT16 effpages = pages;
/* adjust to clip this row only */
rowclip.min_y = (y < cliprect->min_y) ? cliprect->min_y : y;
rowclip.max_y = (y + 7 > cliprect->max_y) ? cliprect->max_y : y + 7;
/* get the effective scroll values */
rowscroll = textram[0xf80/2 + 0x40/2 * which + rowscrollindex];
effxscroll = (xscroll & 0x8000) ? rowscroll : xscroll;
effyscroll = yscroll;
/* are we using an alternate? */
if (rowscroll & 0x8000)
{
effxscroll = info->latched_xscroll[which + 2];
effyscroll = info->latched_yscroll[which + 2];
effpages = info->latched_pageselect[which + 2];
}
/* draw the chunk */
effxscroll = (0xc0 - effxscroll + info->xoffs) & 0x3ff;
effyscroll = effyscroll & 0x1ff;
segaic16_draw_virtual_tilemap(machine, info, bitmap, &rowclip, effpages, effxscroll, effyscroll, flags, priority);
}
}
}
static TIMER_CALLBACK( segaic16_tilemap_16b_latch_values )
{
struct tilemap_info *info = &bg_tilemap[param];
UINT16 *textram = info->textram;
int i;
/* latch the scroll and page select values */
for (i = 0; i < 4; i++)
{
info->latched_pageselect[i] = textram[0xe80/2 + i];
info->latched_yscroll[i] = textram[0xe90/2 + i];
info->latched_xscroll[i] = textram[0xe98/2 + i];
}
/* set a timer to do this again next frame */
timer_set(video_screen_get_time_until_pos(0, 261, 0), param, segaic16_tilemap_16b_latch_values);
}
static void segaic16_tilemap_16b_reset(struct tilemap_info *info)
{
/* set a timer to latch values on scanline 261 */
timer_set(video_screen_get_time_until_pos(0, 261, 0), info->index, segaic16_tilemap_16b_latch_values);
}
/*************************************
*
* General tilemap initialization
*
*************************************/
void segaic16_tilemap_init(int which, int type, int colorbase, int xoffs, int numbanks)
{
struct tilemap_info *info = &bg_tilemap[which];
tile_get_info_callback get_text_info;
tile_get_info_callback get_tile_info;
int pagenum;
int i;
/* reset the tilemap info */
memset(info, 0, sizeof(*info));
info->index = which;
info->type = type;
for (i = 0; i < numbanks; i++)
info->bank[i] = i;
info->banksize = 0x2000 / numbanks;
info->xoffs = xoffs;
/* set up based on which tilemap */
switch (which)
{
case 0:
info->textram = segaic16_textram_0;
info->tileram = segaic16_tileram_0;
break;
default:
fatalerror("Invalid tilemap index specified in segaic16_tilemap_init");
}
/* determine the parameters of the tilemaps */
switch (type)
{
case SEGAIC16_TILEMAP_HANGON:
get_text_info = segaic16_tilemap_16a_text_info;
get_tile_info = segaic16_tilemap_16a_tile_info;
info->numpages = 4;
info->draw_layer = segaic16_tilemap_16a_draw_layer;
info->reset = NULL;
break;
case SEGAIC16_TILEMAP_16A:
get_text_info = segaic16_tilemap_16a_text_info;
get_tile_info = segaic16_tilemap_16a_tile_info;
info->numpages = 8;
info->draw_layer = segaic16_tilemap_16a_draw_layer;
info->reset = NULL;
break;
case SEGAIC16_TILEMAP_16B:
get_text_info = segaic16_tilemap_16b_text_info;
get_tile_info = segaic16_tilemap_16b_tile_info;
info->numpages = 16;
info->draw_layer = segaic16_tilemap_16b_draw_layer;
info->reset = segaic16_tilemap_16b_reset;
break;
case SEGAIC16_TILEMAP_16B_ALT:
get_text_info = segaic16_tilemap_16b_alt_text_info;
get_tile_info = segaic16_tilemap_16b_alt_tile_info;
info->numpages = 16;
info->draw_layer = segaic16_tilemap_16b_draw_layer;
info->reset = segaic16_tilemap_16b_reset;
break;
default:
fatalerror("Invalid tilemap type specified in segaic16_tilemap_init");
}
/* create the tilemap for the text layer */
info->textmap = tilemap_create(get_text_info, tilemap_scan_rows, TILEMAP_TYPE_PEN, 8,8, 64,28);
/* configure it */
info->textmap_info.rambase = info->textram;
info->textmap_info.bank = info->bank;
info->textmap_info.banksize = info->banksize;
tilemap_set_user_data(info->textmap, &info->textmap_info);
tilemap_set_palette_offset(info->textmap, colorbase);
tilemap_set_transparent_pen(info->textmap, 0);
tilemap_set_scrolldx(info->textmap, -192 + xoffs, -170 + xoffs);
tilemap_set_scrolldy(info->textmap, 0, 38);
/* create the tilemaps for the tile pages */
for (pagenum = 0; pagenum < info->numpages; pagenum++)
{
/* each page is 64x32 */
info->tilemaps[pagenum] = tilemap_create(get_tile_info, tilemap_scan_rows, TILEMAP_TYPE_PEN, 8,8, 64,32);
/* configure the tilemap */
info->tilemap_info[pagenum].rambase = info->tileram + pagenum * 64*32;
info->tilemap_info[pagenum].bank = info->bank;
info->tilemap_info[pagenum].banksize = info->banksize;
tilemap_set_user_data(info->tilemaps[pagenum], &info->tilemap_info[pagenum]);
tilemap_set_palette_offset(info->tilemaps[pagenum], colorbase);
tilemap_set_transparent_pen(info->tilemaps[pagenum], 0);
tilemap_set_scrolldx(info->tilemaps[pagenum], 0, 22);
tilemap_set_scrolldy(info->tilemaps[pagenum], 0, 38);
}
}
/*************************************
*
* General tilemap rendering
*
*************************************/
void segaic16_tilemap_draw(running_machine *machine, int which, mame_bitmap *bitmap, const rectangle *cliprect, int map, int priority, int priority_mark)
{
struct tilemap_info *info = &bg_tilemap[which];
/* text layer is a special common case */
if (map == SEGAIC16_TILEMAP_TEXT)
tilemap_draw(bitmap, cliprect, info->textmap, priority, priority_mark);
/* other layers are handled differently per-system */
else
(*info->draw_layer)(machine, info, bitmap, cliprect, map, priority, priority_mark);
}
/*************************************
*
* General tilemap reset
*
*************************************/
void segaic16_tilemap_reset(int which)
{
struct tilemap_info *info = &bg_tilemap[which];
if (info->reset)
(*info->reset)(info);
}
/*************************************
*
* General tilemap banking
*
*************************************/
void segaic16_tilemap_set_bank(int which, int banknum, int offset)
{
struct tilemap_info *info = &bg_tilemap[which];
if (info->bank[banknum] != offset)
{
video_screen_update_partial(0, video_screen_get_vpos(0));
info->bank[banknum] = offset;
tilemap_mark_all_tiles_dirty(NULL);
}
}
/*************************************
*
* General tilemap screen flipping
*
*************************************/
void segaic16_tilemap_set_flip(int which, int flip)
{
struct tilemap_info *info = &bg_tilemap[which];
int pagenum;
flip = (flip != 0);
if (info->flip != flip)
{
video_screen_update_partial(0, video_screen_get_vpos(0));
info->flip = flip;
tilemap_set_flip(info->textmap, flip ? (TILEMAP_FLIPX | TILEMAP_FLIPY) : 0);
for (pagenum = 0; pagenum < info->numpages; pagenum++)
tilemap_set_flip(info->tilemaps[pagenum], flip ? (TILEMAP_FLIPX | TILEMAP_FLIPY) : 0);
}
}
/*************************************
*
* General tilemap row scroll enable
*
*************************************/
void segaic16_tilemap_set_rowscroll(int which, int enable)
{
struct tilemap_info *info = &bg_tilemap[which];
enable = (enable != 0);
if (info->rowscroll != enable)
{
video_screen_update_partial(0, video_screen_get_vpos(0));
info->rowscroll = enable;
}
}
/*************************************
*
* General tilemap column scroll enable
*
*************************************/
void segaic16_tilemap_set_colscroll(int which, int enable)
{
struct tilemap_info *info = &bg_tilemap[which];
enable = (enable != 0);
if (info->colscroll != enable)
{
video_screen_update_partial(0, video_screen_get_vpos(0));
info->colscroll = enable;
}
}
/*************************************
*
* General tilemap write handlers
*
*************************************/
WRITE16_HANDLER( segaic16_tileram_0_w )
{
COMBINE_DATA(&segaic16_tileram_0[offset]);
tilemap_mark_tile_dirty(bg_tilemap[0].tilemaps[offset / (64*32)], offset % (64*32));
}
WRITE16_HANDLER( segaic16_textram_0_w )
{
/* certain ranges need immediate updates */
if (offset >= 0xe80/2)
video_screen_update_partial(0, video_screen_get_vpos(0));
COMBINE_DATA(&segaic16_textram_0[offset]);
tilemap_mark_tile_dirty(bg_tilemap[0].textmap, offset);
}
/*******************************************************************************************
*
* Hang On-style sprites
*
* Offs Bits Usage
* +0 bbbbbbbb -------- Bottom scanline of sprite - 1
* +0 -------- tttttttt Top scanline of sprite - 1
* +2 bbbb---- -------- Sprite bank
* +2 -------x xxxxxxxx X position of sprite (position $BD is screen position 0)
* +4 pppppppp pppppppp Signed 16-bit pitch value between scanlines
* +6 -ooooooo oooooooo Offset within selected sprite bank
* +6 f------- -------- Horizontal flip: read the data backwards if set
* +8 --cccccc -------- Sprite color palette
* +8 -------- zzzzzz-- Zoom factor
* +8 -------- ------pp Sprite priority
* +E dddddddd dddddddd Scratch space for current address
*
* Special notes:
*
* There is an interaction between the horizonal flip bit and the offset.
* The offset is maintained as a 16-bit value, even though only the lower
* 15 bits are used for the address. The top bit is used to control flipping.
* This means that if the low 15 bits overflow during rendering, the sprite
* data will be read backwards after the overflow. This is important to
* emulate correctly as many games make use of this feature to render sprites
* at the beginning of a bank.
*
*******************************************************************************************/
#define hangon_draw_pixel() \
/* only draw if onscreen, not 0 or 15 */ \
if (pix != 0 && pix != 15) \
{ \
/* are we high enough priority to be visible? */ \
if (sprpri > pri[x]) \
{ \
/* shadow/hilight mode? */ \
if (color == info->colorbase + (0x3f << 4)) \
dest[x] += info->shadow ? palette.entries*2 : palette.entries; \
\
/* regular draw */ \
else \
dest[x] = pix | color; \
} \
\
/* always mark priority so no one else draws here */ \
pri[x] = 0xff; \
} \
static void segaic16_sprites_hangon_draw(struct sprite_info *info, mame_bitmap *bitmap, const rectangle *cliprect)
{
UINT8 numbanks = memory_region_length(REGION_GFX2) / 0x10000;
const UINT16 *spritebase = (const UINT16 *)memory_region(REGION_GFX2);
const UINT8 *zoom = (const UINT8 *)memory_region(REGION_PROMS);
UINT16 *data;
/* first scan forward to find the end of the list */
for (data = info->spriteram; data < info->spriteram + info->ramsize/2; data += 8)
if ((data[0] >> 8) > 0xf0)
break;
/* now scan backwards and render the sprites in order */
for (data -= 8; data >= info->spriteram; data -= 8)
{
int bottom = (data[0] >> 8) + 1;
int top = (data[0] & 0xff) + 1;
int bank = info->bank[(data[1] >> 12) & 0xf];
int xpos = (data[1] & 0x1ff) - 0xbd;
int pitch = (INT16)data[2];
UINT16 addr = data[3];
int color = info->colorbase + (((data[4] >> 8) & 0x3f) << 4);
int vzoom = (data[4] >> 2) & 0x3f;
int hzoom = vzoom << 1;
int sprpri = 1 << ((data[4] >> 0) & 0x3);
int x, y, pix, zaddr, zmask;
const UINT16 *spritedata;
/* initialize the end address to the start address */
data[7] = addr;
/* if hidden, or top greater than/equal to bottom, or invalid bank, punt */
if ((top >= bottom) || bank == 255)
continue;
/* clamp to within the memory region size */
if (numbanks)
bank %= numbanks;
spritedata = spritebase + 0x8000 * bank;
/* determine the starting zoom address and mask */
zaddr = (vzoom & 0x38) << 5;
zmask = 1 << (vzoom & 7);
/* loop from top to bottom */
for (y = top; y < bottom; y++)
{
/* advance a row */
addr += pitch;
/* if the zoom bit says so, add pitch a second time */
if (zoom[zaddr++] & zmask)
addr += pitch;
/* skip drawing if not within the cliprect */
if (y >= cliprect->min_y && y <= cliprect->max_y)
{
UINT16 *dest = BITMAP_ADDR16(bitmap, y, 0);
UINT8 *pri = BITMAP_ADDR8(priority_bitmap, y, 0);
int xacc = 0x00;
/* note that the System 16A sprites have a design flaw that allows the address */
/* to carry into the flip flag, which is the topmost bit -- it is very important */
/* to emulate this as the games compensate for it */
/* non-flipped case */
if (!(addr & 0x8000))
{
/* start at the word before because we preincrement below */
data[7] = addr - 1;
for (x = xpos; x <= cliprect->max_x; )
{
UINT16 pixels = spritedata[++data[7] & 0x7fff];
/* draw four pixels */
pix = (pixels >> 12) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) hangon_draw_pixel(); x++; }
pix = (pixels >> 8) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) hangon_draw_pixel(); x++; }
pix = (pixels >> 4) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) hangon_draw_pixel(); x++; }
pix = (pixels >> 0) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) hangon_draw_pixel(); x++; }
/* stop if the last pixel in the group was 0xf */
if (pix == 15)
break;
}
}
/* flipped case */
else
{
/* start at the word after because we predecrement below */
data[7] = addr + 1;
for (x = xpos; x <= cliprect->max_x; )
{
UINT16 pixels = spritedata[--data[7] & 0x7fff];
/* draw four pixels */
pix = (pixels >> 0) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) hangon_draw_pixel(); x++; }
pix = (pixels >> 4) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) hangon_draw_pixel(); x++; }
pix = (pixels >> 8) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) hangon_draw_pixel(); x++; }
pix = (pixels >> 12) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) hangon_draw_pixel(); x++; }
/* stop if the last pixel in the group was 0xf */
if (pix == 15)
break;
}
}
}
}
}
}
/*******************************************************************************************
*
* Space Harrier-style sprites
*
* Offs Bits Usage
* +0 bbbbbbbb -------- Bottom scanline of sprite - 1
* +0 -------- tttttttt Top scanline of sprite - 1
* +2 bbbb---- -------- Sprite bank
* +2 -------x xxxxxxxx X position of sprite (position $BD is screen position 0)
* +4 s------- -------- Sprite shadow enable (0=enable, 1=disable)
* +4 -p------ -------- Sprite priority
* +4 --cccccc -------- Sprite color palette
* +4 -------- -ppppppp Signed 7-bit pitch value between scanlines
* +6 f------- -------- Horizontal flip: read the data backwards if set
* +6 -ooooooo oooooooo Offset within selected sprite bank
* +8 --zzzzzz -------- Horizontal zoom factor
* +8 -------- --zzzzzz Vertical zoom factor
* +E dddddddd dddddddd Scratch space for current address
*
* Special notes:
*
* There is an interaction between the horizonal flip bit and the offset.
* The offset is maintained as a 16-bit value, even though only the lower
* 15 bits are used for the address. The top bit is used to control flipping.
* This means that if the low 15 bits overflow during rendering, the sprite
* data will be read backwards after the overflow. This is important to
* emulate correctly as many games make use of this feature to render sprites
* at the beginning of a bank.
*
*******************************************************************************************/
#define sharrier_draw_pixel() \
/* only draw if onscreen, not 0 or 15 */ \
if (pix != 0 && pix != 15) \
{ \
/* are we high enough priority to be visible? */ \
if (sprpri > pri[x]) \
{ \
/* shadow/hilight mode? */ \
if (shadow && pix == 0xa) \
dest[x] += (paletteram16[dest[x]] & 0x8000) ? palette.entries*2 : palette.entries; \
\
/* regular draw */ \
else \
dest[x] = pix | color; \
} \
\
/* always mark priority so no one else draws here */ \
pri[x] = 0xff; \
} \
static void segaic16_sprites_sharrier_draw(struct sprite_info *info, mame_bitmap *bitmap, const rectangle *cliprect)
{
UINT8 numbanks = memory_region_length(REGION_GFX2) / 0x20000;
const UINT32 *spritebase = (const UINT32 *)memory_region(REGION_GFX2);
const UINT8 *zoom = (const UINT8 *)memory_region(REGION_PROMS);
UINT16 *data;
/* first scan forward to find the end of the list */
for (data = info->spriteram; data < info->spriteram + info->ramsize/2; data += 8)
if ((data[0] >> 8) > 0xf0)
break;
/* now scan backwards and render the sprites in order */
for (data -= 8; data >= info->spriteram; data -= 8)
{
int bottom = (data[0] >> 8) + 1;
int top = (data[0] & 0xff) + 1;
int bank = info->bank[(data[1] >> 12) & 0x7];
int xpos = (data[1] & 0x1ff) - 0xbd;
int shadow = (~data[2] >> 15) & 1;
int sprpri = ((data[2] >> 14) & 1) ? (1<<3) : (1<<1);
int color = info->colorbase + (((data[2] >> 8) & 0x3f) << 4);
int pitch = (INT16)(data[2] << 9) >> 9;
UINT16 addr = data[3];
int hzoom = ((data[4] >> 8) & 0x3f) << 1;
int vzoom = (data[4] >> 0) & 0x3f;
int x, y, pix, zaddr, zmask;
const UINT32 *spritedata;
/* initialize the end address to the start address */
data[7] = addr;
/* if hidden, or top greater than/equal to bottom, or invalid bank, punt */
if ((top >= bottom) || bank == 255)
continue;
/* clamp to within the memory region size */
if (numbanks)
bank %= numbanks;
spritedata = spritebase + 0x8000 * bank;
/* determine the starting zoom address and mask */
zaddr = (vzoom & 0x38) << 5;
zmask = 1 << (vzoom & 7);
/* loop from top to bottom */
for (y = top; y < bottom; y++)
{
/* advance a row */
addr += pitch;
/* if the zoom bit says so, add pitch a second time */
if (zoom[zaddr++] & zmask)
addr += pitch;
/* skip drawing if not within the cliprect */
if (y >= cliprect->min_y && y <= cliprect->max_y)
{
UINT16 *dest = BITMAP_ADDR16(bitmap, y, 0);
UINT8 *pri = BITMAP_ADDR8(priority_bitmap, y, 0);
int xacc = 0x00;
/* note that the System 16A sprites have a design flaw that allows the address */
/* to carry into the flip flag, which is the topmost bit -- it is very important */
/* to emulate this as the games compensate for it */
/* non-flipped case */
if (!(addr & 0x8000))
{
/* start at the word before because we preincrement below */
data[7] = addr - 1;
for (x = xpos; x <= cliprect->max_x; )
{
UINT32 pixels = spritedata[++data[7] & 0x7fff];
/* draw 8 pixels */
pix = (pixels >> 28) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 24) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 20) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 16) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 12) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 8) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 4) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 0) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
/* stop if the last pixel in the group was 0xf */
if (pix == 15)
break;
}
}
/* flipped case */
else
{
/* start at the word after because we predecrement below */
data[7] = addr + 1;
for (x = xpos; x <= cliprect->max_x; )
{
UINT32 pixels = spritedata[--data[7] & 0x7fff];
/* draw 8 pixels */
pix = (pixels >> 0) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 4) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 8) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 12) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 16) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 20) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 24) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
pix = (pixels >> 28) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect->min_x) sharrier_draw_pixel(); x++; }
/* stop if the last pixel in the group was 0xf */
if (pix == 15)
break;
}
}
}
}
}
}
/*******************************************************************************************
*
* System 16A-style sprites
*
* Offs Bits Usage
* +0 bbbbbbbb -------- Bottom scanline of sprite - 1
* +0 -------- tttttttt Top scanline of sprite - 1
* +2 -------x xxxxxxxx X position of sprite (position $BD is screen position 0)
* +4 pppppppp pppppppp Signed 16-bit pitch value between scanlines
* +6 -ooooooo oooooooo Offset within selected sprite bank
* +6 f------- -------- Horizontal flip: read the data backwards if set
* +8 --cccccc -------- Sprite color palette
* +8 -------- -bbb---- Sprite bank
* +8 -------- ------pp Sprite priority
* +E dddddddd dddddddd Scratch space for current address
*
* Special notes:
*
* There is an interaction between the horizonal flip bit and the offset.
* The offset is maintained as a 16-bit value, even though only the lower
* 15 bits are used for the address. The top bit is used to control flipping.
* This means that if the low 15 bits overflow during rendering, the sprite
* data will be read backwards after the overflow. This is important to
* emulate correctly as many games make use of this feature to render sprites
* at the beginning of a bank.
*
*******************************************************************************************/
#define system16a_draw_pixel() \
/* only draw if onscreen, not 0 or 15 */ \
if (x >= cliprect->min_x && x <= cliprect->max_x && pix != 0 && pix != 15) \
{ \
/* are we high enough priority to be visible? */ \
if (sprpri > pri[x]) \
{ \
/* shadow/hilight mode? */ \
if (color == info->colorbase + (0x3f << 4)) \
dest[x] += (paletteram16[dest[x]] & 0x8000) ? palette.entries*2 : palette.entries; \
\
/* regular draw */ \
else \
dest[x] = pix | color; \
} \
\
/* always mark priority so no one else draws here */ \
pri[x] = 0xff; \
} \
static void segaic16_sprites_16a_draw(struct sprite_info *info, mame_bitmap *bitmap, const rectangle *cliprect)
{
UINT8 numbanks = memory_region_length(REGION_GFX2) / 0x10000;
const UINT16 *spritebase = (const UINT16 *)memory_region(REGION_GFX2);
UINT16 *data;
/* first scan forward to find the end of the list */
for (data = info->spriteram; data < info->spriteram + info->ramsize/2; data += 8)
if ((data[0] >> 8) > 0xf0)
break;
/* now scan backwards and render the sprites in order */
for (data -= 8; data >= info->spriteram; data -= 8)
{
int bottom = (data[0] >> 8) + 1;
int top = (data[0] & 0xff) + 1;
int xpos = (data[1] & 0x1ff) - 0xbd;
int pitch = (INT16)data[2];
UINT16 addr = data[3];
int color = info->colorbase + (((data[4] >> 8) & 0x3f) << 4);
int bank = info->bank[(data[4] >> 4) & 0x7];
int sprpri = 1 << ((data[4] >> 0) & 0x3);
const UINT16 *spritedata;
int x, y, pix, xdelta = 1;
/* initialize the end address to the start address */
data[7] = addr;
/* if hidden, or top greater than/equal to bottom, or invalid bank, punt */
if ((top >= bottom) || bank == 255)
continue;
/* clamp to within the memory region size */
if (numbanks)
bank %= numbanks;
spritedata = spritebase + 0x8000 * bank;
/* adjust positions for screen flipping */
if (info->flip)
{
int temp = top;
top = 224 - bottom;
bottom = 224 - temp;
xpos = 320 - xpos;
xdelta = -1;
}
/* loop from top to bottom */
for (y = top; y < bottom; y++)
{
/* advance a row */
addr += pitch;
/* skip drawing if not within the cliprect */
if (y >= cliprect->min_y && y <= cliprect->max_y)
{
UINT16 *dest = BITMAP_ADDR16(bitmap, y, 0);
UINT8 *pri = BITMAP_ADDR8(priority_bitmap, y, 0);
/* note that the System 16A sprites have a design flaw that allows the address */
/* to carry into the flip flag, which is the topmost bit -- it is very important */
/* to emulate this as the games compensate for it */
/* non-flipped case */
if (!(addr & 0x8000))
{
/* start at the word before because we preincrement below */
data[7] = addr - 1;
for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
{
UINT16 pixels = spritedata[++data[7] & 0x7fff];
/* draw four pixels */
pix = (pixels >> 12) & 0xf; system16a_draw_pixel(); x += xdelta;
pix = (pixels >> 8) & 0xf; system16a_draw_pixel(); x += xdelta;
pix = (pixels >> 4) & 0xf; system16a_draw_pixel(); x += xdelta;
pix = (pixels >> 0) & 0xf; system16a_draw_pixel(); x += xdelta;
/* stop if the last pixel in the group was 0xf */
if (pix == 15)
break;
}
}
/* flipped case */
else
{
/* start at the word after because we predecrement below */
data[7] = addr + 1;
for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
{
UINT16 pixels = spritedata[--data[7] & 0x7fff];
/* draw four pixels */
pix = (pixels >> 0) & 0xf; system16a_draw_pixel(); x += xdelta;
pix = (pixels >> 4) & 0xf; system16a_draw_pixel(); x += xdelta;
pix = (pixels >> 8) & 0xf; system16a_draw_pixel(); x += xdelta;
pix = (pixels >> 12) & 0xf; system16a_draw_pixel(); x += xdelta;
/* stop if the last pixel in the group was 0xf */
if (pix == 15)
break;
}
}
}
}
}
}
/*******************************************************************************************
*
* System 16B-style sprites
*
* Offs Bits Usage
* +0 bbbbbbbb -------- Bottom scanline of sprite - 1
* +0 -------- tttttttt Top scanline of sprite - 1
* +2 -------x xxxxxxxx X position of sprite (position $BD is screen position 0)
* +4 e------- -------- Signify end of sprite list
* +4 -h------ -------- Hide this sprite
* +4 -------f -------- Horizontal flip: read the data backwards if set
* +4 -------- pppppppp Signed 8-bit pitch value between scanlines
* +6 oooooooo oooooooo Offset within selected sprite bank
* +8 ----bbbb -------- Sprite bank
* +8 -------- pp------ Sprite priority, relative to tilemaps
* +8 -------- --cccccc Sprite color palette
* +A ------vv vvv----- Vertical zoom factor (0 = full size, 0x10 = half size)
* +A -------- ---hhhhh Horizontal zoom factor (0 = full size, 0x10 = half size)
* +E dddddddd dddddddd Scratch space for current address
*
* Note that the zooming described below is 100% accurate to the real board.
*
*******************************************************************************************/
#define system16b_draw_pixel() \
/* only draw if onscreen, not 0 or 15 */ \
if (x >= cliprect->min_x && x <= cliprect->max_x && pix != 0 && pix != 15) \
{ \
/* are we high enough priority to be visible? */ \
if (sprpri > pri[x]) \
{ \
/* shadow/hilight mode? */ \
if (color == info->colorbase + (0x3f << 4)) \
dest[x] += (paletteram16[dest[x]] & 0x8000) ? palette.entries*2 : palette.entries; \
\
/* regular draw */ \
else \
dest[x] = pix | color; \
} \
\
/* always mark priority so no one else draws here */ \
pri[x] = 0xff; \
} \
static void segaic16_sprites_16b_draw(struct sprite_info *info, mame_bitmap *bitmap, const rectangle *cliprect)
{
UINT8 numbanks;
const UINT16 *spritebase;
UINT16 *data;
if (!memory_region(REGION_GFX2))
return;
spritebase = (const UINT16 *)memory_region(REGION_GFX2);
numbanks = memory_region_length(REGION_GFX2) / 0x20000;
/* first scan forward to find the end of the list */
for (data = info->spriteram; data < info->spriteram + info->ramsize/2; data += 8)
if (data[2] & 0x8000)
break;
/* now scan backwards and render the sprites in order */
for (data -= 8; data >= info->spriteram; data -= 8)
{
int bottom = data[0] >> 8;
int top = data[0] & 0xff;
int xpos = (data[1] & 0x1ff) - 0xb8;
int hide = data[2] & 0x4000;
int flip = data[2] & 0x100;
int pitch = (INT8)(data[2] & 0xff);
UINT16 addr = data[3];
int bank = info->bank[(data[4] >> 8) & 0xf];
int sprpri = 1 << ((data[4] >> 6) & 0x3);
int color = info->colorbase + ((data[4] & 0x3f) << 4);
int vzoom = (data[5] >> 5) & 0x1f;
int hzoom = data[5] & 0x1f;
const UINT16 *spritedata;
int x, y, pix, xdelta = 1;
/* initialize the end address to the start address */
data[7] = addr;
/* if hidden, or top greater than/equal to bottom, or invalid bank, punt */
if (hide || (top >= bottom) || bank == 255)
continue;
/* clamp to within the memory region size */
if (numbanks)
bank %= numbanks;
spritedata = spritebase + 0x10000 * bank;
/* reset the yzoom counter */
data[5] &= 0x03ff;
/* adjust positions for screen flipping */
if (info->flip)
{
int temp = top;
top = 224 - bottom;
bottom = 224 - temp;
xpos = 320 - xpos;
xdelta = -1;
}
/* loop from top to bottom */
for (y = top; y < bottom; y++)
{
/* advance a row */
addr += pitch;
/* accumulate zoom factors; if we carry into the high bit, skip an extra row */
data[5] += vzoom << 10;
if (data[5] & 0x8000)
{
addr += pitch;
data[5] &= ~0x8000;
}
/* skip drawing if not within the cliprect */
if (y >= cliprect->min_y && y <= cliprect->max_y)
{
UINT16 *dest = BITMAP_ADDR16(bitmap, y, 0);
UINT8 *pri = BITMAP_ADDR8(priority_bitmap, y, 0);
int xacc;
/* compute the initial X zoom accumulator; this is verified on the real PCB */
xacc = 4 * hzoom;
/* non-flipped case */
if (!flip)
{
/* start at the word before because we preincrement below */
data[7] = addr - 1;
for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
{
UINT16 pixels = spritedata[++data[7]];
/* draw four pixels */
pix = (pixels >> 12) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { system16b_draw_pixel(); x += xdelta; }
pix = (pixels >> 8) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { system16b_draw_pixel(); x += xdelta; }
pix = (pixels >> 4) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { system16b_draw_pixel(); x += xdelta; }
pix = (pixels >> 0) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { system16b_draw_pixel(); x += xdelta; }
/* stop if the last pixel in the group was 0xf */
if (pix == 15)
break;
}
}
/* flipped case */
else
{
/* start at the word after because we predecrement below */
data[7] = addr + 1;
for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
{
UINT16 pixels = spritedata[--data[7]];
/* draw four pixels */
pix = (pixels >> 0) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { system16b_draw_pixel(); x += xdelta; }
pix = (pixels >> 4) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { system16b_draw_pixel(); x += xdelta; }
pix = (pixels >> 8) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { system16b_draw_pixel(); x += xdelta; }
pix = (pixels >> 12) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { system16b_draw_pixel(); x += xdelta; }
/* stop if the last pixel in the group was 0xf */
if (pix == 15)
break;
}
}
}
}
}
}
/*******************************************************************************************
*
* The Y-board variant has different mixing properties. The sprite implementation itself
* is identical, however.
*
*******************************************************************************************/
#define yboard_16b_draw_pixel() \
/* only draw if onscreen, not 0 or 15 */ \
if (x >= cliprect->min_x && x <= cliprect->max_x && pix != 0 && pix != 15) \
{ \
/* are we high enough priority to be visible? */ \
if ((sprpri & 0x1f) < (pri[x] & 0x1f)) \
{ \
/* shadow/hilight mode? */ \
if (pix == 14) \
dest[x] += (paletteram16[dest[x]] & 0x8000) ? palette.entries*2 : palette.entries; \
\
/* regular draw */ \
else \
dest[x] = pix | color; \
} \
\
/* always mark priority so no one else draws here */ \
pri[x] = 0; \
} \
static void segaic16_sprites_yboard_16b_draw(struct sprite_info *info, mame_bitmap *bitmap, const rectangle *cliprect)
{
UINT8 numbanks = memory_region_length(REGION_GFX2) / 0x20000;
const UINT16 *spritebase = (const UINT16 *)memory_region(REGION_GFX2);
UINT16 *data;
/* first scan forward to find the end of the list */
for (data = info->spriteram; data < info->spriteram + info->ramsize/2; data += 8)
if (data[2] & 0x8000)
break;
/* now scan backwards and render the sprites in order */
for (data -= 8; data >= info->spriteram; data -= 8)
{
int bottom = data[0] >> 8;
int top = data[0] & 0xff;
int xpos = (data[1] & 0x1ff) - 0xb8;
int sprpri = (data[1] >> 8) & 0x1e; // 0x00 = high, 0x7f = low -- 0x71 = ship in gforce, 0x31 = strike fighter logo
int hide = data[2] & 0x4000;
int flip = data[2] & 0x100;
int pitch = (INT8)(data[2] & 0xff);
UINT16 addr = data[3];
int bank = info->bank[(data[4] >> 8) & 0xf];
int color = info->colorbase + ((data[4] & 0x7f) << 4);
int vzoom = (data[5] >> 5) & 0x1f;
int hzoom = data[5] & 0x1f;
const UINT16 *spritedata;
int x, y, pix, xdelta = 1;
/* initialize the end address to the start address */
data[7] = addr;
/* if hidden, or top greater than/equal to bottom, or invalid bank, punt */
if (hide || (top >= bottom) || bank == 255)
continue;
/* clamp to within the memory region size */
if (numbanks)
bank %= numbanks;
spritedata = spritebase + 0x10000 * bank;
/* reset the yzoom counter */
data[5] &= 0x03ff;
/* adjust positions for screen flipping */
if (info->flip)
{
int temp = top;
top = 224 - bottom;
bottom = 224 - temp;
xpos = 320 - xpos;
xdelta = -1;
}
/* loop from top to bottom */
for (y = top; y < bottom; y++)
{
/* advance a row */
addr += pitch;
/* accumulate zoom factors; if we carry into the high bit, skip an extra row */
data[5] += vzoom << 10;
if (data[5] & 0x8000)
{
addr += pitch;
data[5] &= ~0x8000;
}
/* skip drawing if not within the cliprect */
if (y >= cliprect->min_y && y <= cliprect->max_y)
{
UINT16 *dest = BITMAP_ADDR16(bitmap, y, 0);
UINT8 *pri = BITMAP_ADDR8(priority_bitmap, y, 0);
int xacc;
/* compute the initial X zoom accumulator; this is verified on the real PCB */
xacc = 4 * hzoom;
/* non-flipped case */
if (!flip)
{
/* start at the word before because we preincrement below */
data[7] = addr - 1;
for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
{
UINT16 pixels = spritedata[++data[7]];
/* draw four pixels */
pix = (pixels >> 12) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { yboard_16b_draw_pixel(); x += xdelta; }
pix = (pixels >> 8) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { yboard_16b_draw_pixel(); x += xdelta; }
pix = (pixels >> 4) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { yboard_16b_draw_pixel(); x += xdelta; }
pix = (pixels >> 0) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { yboard_16b_draw_pixel(); x += xdelta; }
/* stop if the last pixel in the group was 0xf */
if (pix == 15)
break;
}
}
/* flipped case */
else
{
/* start at the word after because we predecrement below */
data[7] = addr + 1;
for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
{
UINT16 pixels = spritedata[--data[7]];
/* draw four pixels */
pix = (pixels >> 0) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { yboard_16b_draw_pixel(); x += xdelta; }
pix = (pixels >> 4) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { yboard_16b_draw_pixel(); x += xdelta; }
pix = (pixels >> 8) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { yboard_16b_draw_pixel(); x += xdelta; }
pix = (pixels >> 12) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { yboard_16b_draw_pixel(); x += xdelta; }
/* stop if the last pixel in the group was 0xf */
if (pix == 15)
break;
}
}
}
}
}
}
/*******************************************************************************************
*
* Out Run/X-Board-style sprites
*
* Offs Bits Usage
* +0 e------- -------- Signify end of sprite list
* +0 -h-h---- -------- Hide this sprite if either bit is set
* +0 ----bbb- -------- Sprite bank
* +0 -------t tttttttt Top scanline of sprite + 256
* +2 oooooooo oooooooo Offset within selected sprite bank
* +4 ppppppp- -------- Signed 7-bit pitch value between scanlines
* +4 -------x xxxxxxxx X position of sprite (position $BE is screen position 0)
* +6 -s------ -------- Enable shadows
* +6 --pp---- -------- Sprite priority, relative to tilemaps
* +6 ------vv vvvvvvvv Vertical zoom factor (0x200 = full size, 0x100 = half size, 0x300 = 2x size)
* +8 y------- -------- Render from top-to-bottom (1) or bottom-to-top (0) on screen
* +8 -f------ -------- Horizontal flip: read the data backwards if set
* +8 --x----- -------- Render from left-to-right (1) or right-to-left (0) on screen
* +8 ------hh hhhhhhhh Horizontal zoom factor (0x200 = full size, 0x100 = half size, 0x300 = 2x size)
* +E dddddddd dddddddd Scratch space for current address
*
* Out Run only:
* +A hhhhhhhh -------- Height in scanlines - 1
* +A -------- -ccccccc Sprite color palette
*
* X-Board only:
* +A ----hhhh hhhhhhhh Height in scanlines - 1
* +C -------- cccccccc Sprite color palette
*
*******************************************************************************************/
#define outrun_draw_pixel() \
/* only draw if onscreen, not 0 or 15 */ \
if (x >= cliprect->min_x && x <= cliprect->max_x && pix != 0 && pix != 15) \
{ \
/* are we high enough priority to be visible? */ \
if (sprpri > pri[x]) \
{ \
/* shadow/hilight mode? */ \
if (shadow && pix == 0xa) \
dest[x] += (paletteram16[dest[x]] & 0x8000) ? palette.entries*2 : palette.entries; \
\
/* regular draw */ \
else \
dest[x] = pix | color; \
} \
\
/* always mark priority so no one else draws here */ \
pri[x] = 0xff; \
} \
static void segaic16_sprites_outrun_draw(struct sprite_info *info, mame_bitmap *bitmap, const rectangle *cliprect)
{
UINT8 numbanks = memory_region_length(REGION_GFX2) / 0x40000;
const UINT32 *spritebase = (const UINT32 *)memory_region(REGION_GFX2);
UINT16 *data;
/* first scan forward to find the end of the list */
for (data = info->buffer; data < info->buffer + info->ramsize/2; data += 8)
if (data[0] & 0x8000)
break;
/* now scan backwards and render the sprites in order */
for (data -= 8; data >= info->buffer; data -= 8)
{
int hide = (data[0] & 0x5000);
int bank = (data[0] >> 9) & 7;
int top = (data[0] & 0x1ff) - 0x100;
UINT16 addr = data[1];
int pitch = (INT16)((data[2] >> 1) | ((data[4] & 0x1000) << 3)) >> 8;
int xpos = data[2] & 0x1ff;
int shadow = (data[3] >> 14) & 1;
int sprpri = 1 << ((data[3] >> 12) & 3);
int vzoom = data[3] & 0x7ff;
int ydelta = (data[4] & 0x8000) ? 1 : -1;
int flip = (~data[4] >> 14) & 1;
int xdelta = (data[4] & 0x2000) ? 1 : -1;
int hzoom = data[4] & 0x7ff;
int height = ((info->type == SEGAIC16_SPRITES_OUTRUN) ? (data[5] >> 8) : (data[5] & 0xfff)) + 1;
int color = info->colorbase + (((info->type == SEGAIC16_SPRITES_OUTRUN) ? (data[5] & 0x7f) : (data[6] & 0xff)) << 4);
int x, y, ytarget, yacc = 0, pix;
const UINT32 *spritedata;
/* adjust X coordinate */
/* note: the threshhold below is a guess. If it is too high, rachero will draw garbage */
/* If it is too low, smgp won't draw the bottom part of the road */
if (xpos < 0x80 && xdelta < 0)
xpos += 0x200;
xpos -= 0xbe;
/* initialize the end address to the start address */
data[7] = addr;
/* if hidden, or top greater than/equal to bottom, or invalid bank, punt */
if (hide || height == 0)
continue;
/* clamp to within the memory region size */
if (numbanks)
bank %= numbanks;
spritedata = spritebase + 0x10000 * bank;
/* clamp to a maximum of 8x (not 100% confirmed) */
if (vzoom < 0x40) vzoom = 0x40;
if (hzoom < 0x40) hzoom = 0x40;
/* loop from top to bottom */
ytarget = top + ydelta * height;
for (y = top; y != ytarget; y += ydelta)
{
/* skip drawing if not within the cliprect */
if (y >= cliprect->min_y && y <= cliprect->max_y)
{
UINT16 *dest = BITMAP_ADDR16(bitmap, y, 0);
UINT8 *pri = BITMAP_ADDR8(priority_bitmap, y, 0);
int xacc = 0;
/* non-flipped case */
if (!flip)
{
/* start at the word before because we preincrement below */
data[7] = addr - 1;
for (x = xpos; (xdelta > 0 && x <= cliprect->max_x) || (xdelta < 0 && x >= cliprect->min_x); )
{
UINT32 pixels = spritedata[++data[7]];
/* draw four pixels */
pix = (pixels >> 28) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 24) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 20) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 16) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 12) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 8) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 4) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 0) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
/* stop if the second-to-last pixel in the group was 0xf */
if ((pixels & 0x000000f0) == 0x000000f0)
break;
}
}
/* flipped case */
else
{
/* start at the word after because we predecrement below */
data[7] = addr + 1;
for (x = xpos; (xdelta > 0 && x <= cliprect->max_x) || (xdelta < 0 && x >= cliprect->min_x); )
{
UINT32 pixels = spritedata[--data[7]];
/* draw four pixels */
pix = (pixels >> 0) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 4) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 8) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 12) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 16) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 20) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 24) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
pix = (pixels >> 28) & 0xf; while (xacc < 0x200) { outrun_draw_pixel(); x += xdelta; xacc += hzoom; } xacc -= 0x200;
/* stop if the second-to-last pixel in the group was 0xf */
if ((pixels & 0x0f000000) == 0x0f000000)
break;
}
}
}
/* accumulate zoom factors; if we carry into the high bit, skip an extra row */
yacc += vzoom;
addr += pitch * (yacc >> 9);
yacc &= 0x1ff;
}
}
}
/*******************************************************************************************
*
* Y-Board-style sprites
*
* Offs Bits Usage
* +0 e------- -------- Signify end of sprite list
* +0 -----iii iiiiiiii Address of indirection table (/16)
* +2 bbbb---- -------- Upper 4 bits of bank index
* +2 ----xxxx xxxxxxxx X position of sprite (position $600 is screen position 0)
* +4 bbbb---- -------- Lower 4 bits of bank index
* +4 ----yyyy yyyyyyyy Y position of sprite (position $600 is screen position 0)
* +6 oooooooo oooooooo Offset within selected sprite bank
* +8 hhhhhhhh hhhhhhhh Height of sprite
* +A -y------ -------- Render from top-to-bottom (1) or bottom-to-top (0) on screen
* +A --f----- -------- Horizontal flip: read the data backwards if set
* +A ---x---- -------- Render from left-to-right (1) or right-to-left (0) on screen
* +A -----zzz zzzzzzzz Zoom factor
* +C -ccc---- -------- Sprite color
* +C ----rrrr -------- Sprite priority
* +C -------- pppppppp Signed 8-bit pitch value between scanlines
* +E ----nnnn nnnnnnnn Index of next sprite
*
* In addition to these parameters, the sprite area is clipped using scanline extents
* stored for every pair of scanlines in the rotation RAM. It's a bit of a cheat for us
* to poke our nose into the rotation structure, but there are no known cases of Y-board
* sprites without rotation RAM.
*
*******************************************************************************************/
#define yboard_draw_pixel() \
/* only draw if onscreen */ \
if (x >= minx && x <= maxx && ind < 0x1fe) \
dest[x] = ind | colorpri; \
static void segaic16_sprites_yboard_draw(struct sprite_info *info, mame_bitmap *bitmap, const rectangle *cliprect)
{
UINT8 numbanks = memory_region_length(REGION_GFX1) / 0x80000;
const UINT64 *spritebase = (const UINT64 *)memory_region(REGION_GFX1);
const UINT16 *rotatebase = rotate[0].buffer ? rotate[0].buffer : rotate[0].rotateram;
UINT8 visited[0x1000];
UINT16 *data;
int next = 0;
int y;
/* reset the visited list */
memset(visited, 0, sizeof(visited));
/* clear out any scanlines we might be using */
for (y = cliprect->min_y; y <= cliprect->max_y; y++)
if (!(rotatebase[y & ~1] & 0xc000))
memset(BITMAP_ADDR16(bitmap, y, cliprect->min_x), 0xff, (cliprect->max_x - cliprect->min_x + 1) * sizeof(UINT16));
/* now scan backwards and render the sprites in order */
for (data = info->spriteram; !(data[0] & 0x8000) && !visited[next]; data = info->spriteram + next * 8)
{
int hide = (data[0] & 0x5000);
UINT16 *indirect = info->spriteram + ((data[0] & 0x7ff) << 4);
int bank = ((data[1] >> 8) & 0x10) | ((data[2] >> 12) & 0x0f);
int xpos = (data[1] & 0xfff) - 0x600;
int top = (data[2] & 0xfff) - 0x600;
UINT16 addr = data[3];
int height = data[4];
int ydelta = (data[5] & 0x4000) ? 1 : -1;
int flip = (~data[5] >> 13) & 1;
int xdelta = (data[5] & 0x1000) ? 1 : -1;
int zoom = data[5] & 0x7ff;
int colorpri= (data[6] << 1) & 0xfe00;
int pitch = (INT8)data[6];
int x, y, ytarget, yacc = 0, pix, ind;
const UINT64 *spritedata;
UINT16 offs;
/* note that we've visited this entry and get the offset of the next one */
visited[next] = 1;
next = data[7] & 0xfff;
/* if hidden, or top greater than/equal to bottom, or invalid bank, punt */
if (hide || height == 0)
continue;
/* clamp to within the memory region size */
if (numbanks)
bank %= numbanks;
spritedata = spritebase + 0x10000 * bank;
/* clamp to a maximum of 8x (not 100% confirmed) */
if (zoom == 0) zoom = 1;
/* loop from top to bottom */
ytarget = top + ydelta * height;
for (y = top; y != ytarget; y += ydelta)
{
/* skip drawing if not within the cliprect */
if (y >= cliprect->min_y && y <= cliprect->max_y)
{
UINT16 *dest = BITMAP_ADDR16(bitmap, y, 0);
int minx = rotatebase[y & ~1];
int maxx = rotatebase[y | 1];
int xacc = 0;
/* bit 0x8000 from rotate RAM means that Y is above the top of the screen */
if ((minx & 0x8000) && ydelta < 0)
break;
/* bit 0x4000 from rotate RAM means that Y is below the bottom of the screen */
if ((minx & 0x4000) && ydelta > 0)
break;
/* if either bit is set, skip the rest for this scanline */
if (!(minx & 0xc000))
{
/* clamp min/max to the cliprect */
minx -= 0x600;
maxx -= 0x600;
if (minx < cliprect->min_x)
minx = cliprect->min_x;
if (maxx > cliprect->max_x)
maxx = cliprect->max_x;
/* non-flipped case */
if (!flip)
{
/* start at the word before because we preincrement below */
offs = addr - 1;
for (x = xpos; (xdelta > 0 && x <= maxx) || (xdelta < 0 && x >= minx); )
{
UINT64 pixels = spritedata[++offs];
/* draw four pixels */
pix = (pixels >> 60) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 56) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 52) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 48) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 44) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 40) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 36) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 32) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 28) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 24) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 20) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 16) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 12) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 8) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 4) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 0) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
/* stop if the second-to-last pixel in the group was 0xf */
if (pix == 0x0f)
break;
}
}
/* flipped case */
else
{
/* start at the word after because we predecrement below */
offs = addr + 1;
for (x = xpos; (xdelta > 0 && x <= maxx) || (xdelta < 0 && x >= minx); )
{
UINT64 pixels = spritedata[--offs];
/* draw four pixels */
pix = (pixels >> 0) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 4) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 8) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 12) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 16) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 20) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 24) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 28) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 32) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 36) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 40) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 44) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 48) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 52) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 56) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
pix = (pixels >> 60) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { yboard_draw_pixel(); x += xdelta; xacc += zoom; } xacc -= 0x200;
/* stop if the second-to-last pixel in the group was 0xf */
if (pix == 0x0f)
break;
}
}
}
}
/* accumulate zoom factors; if we carry into the high bit, skip an extra row */
yacc += zoom;
addr += pitch * (yacc >> 9);
yacc &= 0x1ff;
}
}
}
/*************************************
*
* General sprite initialization
*
*************************************/
void segaic16_sprites_init(int which, int type, int colorbase, int xoffs)
{
struct sprite_info *info = &sprites[which];
int i, buffer = 0;
/* reset the sprite info */
memset(info, 0, sizeof(*info));
info->index = which;
info->type = type;
for (i = 0; i < 16; i++)
info->bank[i] = i;
info->colorbase = colorbase;
info->xoffs = xoffs;
/* set up based on which sprite system */
switch (which)
{
case 0:
info->spriteram = segaic16_spriteram_0;
break;
case 1:
info->spriteram = segaic16_spriteram_1;
break;
default:
fatalerror("Invalid sprite index specified in segaic16_sprites_init");
}
/* determine the parameters of the sprites */
switch (type)
{
case SEGAIC16_SPRITES_HANGON:
info->draw = segaic16_sprites_hangon_draw;
info->ramsize = 0x800;
break;
case SEGAIC16_SPRITES_16A:
info->draw = segaic16_sprites_16a_draw;
info->ramsize = 0x800;
break;
case SEGAIC16_SPRITES_16B:
info->draw = segaic16_sprites_16b_draw;
info->ramsize = 0x800;
break;
case SEGAIC16_SPRITES_SHARRIER:
info->draw = segaic16_sprites_sharrier_draw;
info->ramsize = 0x1000;
break;
case SEGAIC16_SPRITES_OUTRUN:
case SEGAIC16_SPRITES_XBOARD:
info->draw = segaic16_sprites_outrun_draw;
info->ramsize = 0x1000;
buffer = 1;
break;
case SEGAIC16_SPRITES_YBOARD:
info->draw = segaic16_sprites_yboard_draw;
info->ramsize = 0x10000;
break;
case SEGAIC16_SPRITES_YBOARD_16B:
info->draw = segaic16_sprites_yboard_16b_draw;
info->ramsize = 0x800;
break;
default:
fatalerror("Invalid sprite system specified in segaic16_sprites_init");
}
/* if the sprites need buffering, allocate memory for the buffer */
if (buffer)
info->buffer = auto_malloc(info->ramsize);
state_save_register_item("segaic16_sp", which, info->flip);
state_save_register_item("segaic16_sp", which, info->shadow);
state_save_register_item_array("segaic16_sp", which, info->bank);
state_save_register_item("segaic16_sp", which, info->colorbase);
state_save_register_item("segaic16_sp", which, info->xoffs);
if (buffer)
state_save_register_item_pointer("segaic16_sp", which, ((UINT8 *) info->buffer), info->ramsize);
}
/*************************************
*
* General sprite drawing
*
*************************************/
void segaic16_sprites_draw(int which, mame_bitmap *bitmap, const rectangle *cliprect)
{
struct sprite_info *info = &sprites[which];
(*info->draw)(info, bitmap, cliprect);
}
/*************************************
*
* General sprite banking
*
*************************************/
void segaic16_sprites_set_bank(int which, int banknum, int offset)
{
struct sprite_info *info = &sprites[which];
if (info->bank[banknum] != offset)
{
video_screen_update_partial(0, video_screen_get_vpos(0));
info->bank[banknum] = offset;
}
}
/*************************************
*
* General sprite screen flipping
*
*************************************/
void segaic16_sprites_set_flip(int which, int flip)
{
struct sprite_info *info = &sprites[which];
flip = (flip != 0);
if (info->flip != flip)
{
video_screen_update_partial(0, video_screen_get_vpos(0));
info->flip = flip;
}
}
/*************************************
*
* General sprite shadows
*
*************************************/
void segaic16_sprites_set_shadow(int which, int shadow)
{
struct sprite_info *info = &sprites[which];
shadow = (shadow != 0);
if (info->shadow != shadow)
{
video_screen_update_partial(0, video_screen_get_vpos(0));
info->shadow = shadow;
}
}
/*************************************
*
* General sprite buffer control
*
*************************************/
static void segaic16_sprites_buffer(struct sprite_info *info)
{
if (info->buffer)
{
UINT32 *src = (UINT32 *)info->spriteram;
UINT32 *dst = (UINT32 *)info->buffer;
int i;
/* swap the halves of the sprite RAM */
for (i = 0; i < info->ramsize/4; i++)
{
UINT32 temp = *src;
*src++ = *dst;
*dst++ = temp;
}
/* hack for thunderblade */
*info->spriteram = 0xffff;
}
/* we will render the sprites when the video update happens */
}
WRITE16_HANDLER( segaic16_sprites_draw_0_w )
{
segaic16_sprites_buffer(&sprites[0]);
}
WRITE16_HANDLER( segaic16_sprites_draw_1_w )
{
segaic16_sprites_buffer(&sprites[1]);
}
/*******************************************************************************************
*
* Hang On/Space Harrier-style road chip
*
* Road RAM:
* Offset Bits Usage
* 000-1FF ----pp-- -------- road priority versus tilemaps and sprites
* ------s- -------- (Hang On only) Stripe coloring enable (1=enable)
* ------s- -------- (Space Harrier only) Solid color fill (1=solid, 0=from ROM)
* -------m -------- mirror enable (1=enable)
* -------- iiiiiiii index for other tables
* -------- rrrrrrrr road ROM line select
* 200-3FF ----hhhh hhhhhhhh horizontal scroll
* 400-5FF --bbbbbb -------- background color (colorset 0)
* -------- --bbbbbb background color (colorset 1)
* 600-7FF -------- s------- stripe color index (colorset 1)
* -------- -s------ stripe color index (colorset 0)
* -------- --a----- pixel value 2 color index (colorset 1)
* -------- ---a---- pixel value 2 color index (colorset 0)
* -------- ----b--- pixel value 1 color index (colorset 1)
* -------- -----b-- pixel value 1 color index (colorset 0)
* -------- ------c- pixel value 0 color index (colorset 1)
* -------- -------c pixel value 0 color index (colorset 0)
*
* Logic:
* First, the scanline is used to index into the table at 000-1FF
*
* The index is taken from the low 8 bits of the table value from 000-1FF
*
* The horizontal scroll value is looked up using the index in the table at
* 200-3FF
*
* The background color information is looked up using the index in the table at 400-5FF.
*
* The pixel color information is looked up using the index in the table at 600-7FF.
*
*******************************************************************************************/
static void segaic16_road_hangon_decode(struct road_info *info)
{
int x, y;
/* allocate memory for the unpacked road data */
info->gfx = auto_malloc(256 * 512);
/* loop over rows */
for (y = 0; y < 256; y++)
{
UINT8 *src = memory_region(REGION_GFX3) + ((y & 0xff) * 0x40) % memory_region_length(REGION_GFX3);
UINT8 *dst = info->gfx + y * 512;
/* loop over columns */
for (x = 0; x < 512; x++)
dst[x] = (((src[x/8] >> (~x & 7)) & 1) << 0) | (((src[x/8 + 0x4000] >> (~x & 7)) & 1) << 1);
}
}
static void segaic16_road_hangon_draw(struct road_info *info, mame_bitmap *bitmap, const rectangle *cliprect, int priority)
{
UINT16 *roadram = info->roadram;
int x, y;
/* loop over scanlines */
for (y = cliprect->min_y; y <= cliprect->max_y; y++)
{
UINT16 *dest = BITMAP_ADDR16(bitmap, y, 0);
int control = roadram[0x000 + y];
int hpos = roadram[0x100 + (control & 0xff)];
int color0 = roadram[0x200 + (control & 0xff)];
int color1 = roadram[0x300 + (control & 0xff)];
int ff9j1, ff9j2, ctr9m, ctr9n9p, ctr9n9p_ena, ss8j, plycont;
UINT8 *src;
/* the PLYCONT signal controls the road layering */
plycont = (control >> 10) & 3;
/* skip layers we aren't supposed to be drawing */
if ((plycont == 0 && priority != SEGAIC16_ROAD_BACKGROUND) ||
(plycont != 0 && priority != SEGAIC16_ROAD_FOREGROUND))
continue;
/* compute the offset of the road graphics for this line */
src = info->gfx + (0x000 + (control & 0xff)) * 512;
/* initialize the 4-bit counter at 9M, which counts bits within each road byte */
ctr9m = hpos & 7;
/* initialize the two 4-bit counters at 9P (low) and 9N (high), which count road data bytes */
ctr9n9p = (hpos >> 3) & 0xff;
/* initialize the flip-flop at 9J (lower half), which controls the counting direction */
ff9j1 = (hpos >> 11) & 1;
/* initialize the flip-flop at 9J (upper half), which controls the background color */
ff9j2 = 1;
/* initialize the serial shifter at 8S, which delays several signals after we flip */
ss8j = 0;
/* draw this scanline from the beginning */
for (x = -24; x <= cliprect->max_x; x++)
{
int md, color, select;
/* ---- the following logic all happens constantly ---- */
/* the enable is controlled by the value in the counter at 9M */
ctr9n9p_ena = (ctr9m == 7);
/* if we carried out of the 9P/9N counters, we will forcibly clear the flip-flop at 9J (lower half) */
if ((ctr9n9p & 0xff) == 0xff)
ff9j1 = 0;
/* if the control word bit 8 is clear, we will forcibly set the flip-flop at 9J (lower half) */
if (!(control & 0x100))
ff9j1 = 1;
/* for the Hang On/Super Hang On case only: if the control word bit 9 is clear, we will forcibly */
/* set the flip-flip at 9J (upper half) */
if (info->type == SEGAIC16_ROAD_HANGON && !(control & 0x200))
ff9j2 = 1;
/* ---- now process the pixel ---- */
md = 3;
/* the Space Harrier/Enduro Racer hardware has a tweak that maps the control word bit 9 to the */
/* /CE line on the road ROM; use this to effectively disable the road data */
if (info->type != SEGAIC16_ROAD_SHARRIER || !(control & 0x200))
/* the /OE line on the road ROM is linked to the AND of bits 2 & 3 of the counter at 9N */
if ((ctr9n9p & 0xc0) == 0xc0)
{
/* note that the pixel logic is hidden in a custom at 9S; this is just a guess */
if (ss8j & 1)
md = src[((ctr9n9p & 0x3f) << 3) | ctr9m];
else
md = src[((ctr9n9p & 0x3f) << 3) | (ctr9m ^ 7)];
}
/* "select" is a made-up signal that comes from bit 3 of the serial shifter and is */
/* used in several places for color selection */
select = (ss8j >> 3) & 1;
/* check the flip-flop at 9J (upper half) to determine if we should use the background color; */
/* the output of this is ANDed with M0 and M1 so it only affects pixels with a value of 3; */
/* this is done by the AND gates at 9L and 7K */
if (ff9j2 && md == 3)
{
/* in this case, the "select" signal is used to select which background color to use */
/* since the color0 control word contains two selections */
color = (color0 >> (select ? 0 : 8)) & 0x3f;
color |= info->colorbase2;
}
/* if we're not using the background color, we select pixel data from an alternate path */
else
{
/* the AND gates at 7L, 9K, and 7K clamp the pixel value to 0 if bit 7 of the color 1 */
/* signal is 1 and if the pixel value is 3 (both M0 and M1 == 1) */
if ((color1 & 0x80) && md == 3)
md = 0;
/* the pixel value plus the "select" line combine to form a mux into the low 8 bits of color1 */
color = (color1 >> ((md << 1) | select)) & 1;
/* this value becomes the low bit of the final color; the "select" line itself and the pixel */
/* value form the other bits */
color |= select << 3;
color |= md << 1;
color |= info->colorbase1;
}
/* write the pixel if we're past the minimum clip */
if (x >= cliprect->min_x)
dest[x] = color;
/* ---- the following logic all happens on the 6M clock ---- */
/* clock the counter at 9M */
ctr9m = (ctr9m + 1) & 7;
/* if enabled, clock on the two cascaded 4-bit counters at 9P and 9N */
if (ctr9n9p_ena)
{
if (ff9j1)
ctr9n9p++;
else
ctr9n9p--;
}
/* clock the flip-flop at 9J (upper half) */
ff9j2 = !(!ff9j1 && (ss8j & 0x80));
/* clock the serial shift register at 8J */
ss8j = (ss8j << 1) | ff9j1;
}
}
}
/*******************************************************************************************
*
* Out Run/X-Board-style road chip
*
* Road control register:
* Bits Usage
* -------- -----d-- (X-board only) Direct scanline mode (1) or indirect mode (0)
* -------- ------pp Road enable/priorities:
* 0 = road 0 only visible
* 1 = both roads visible, road 0 has priority
* 2 = both roads visible, road 1 has priority
* 3 = road 1 only visible
*
* Road RAM:
* Offset Bits Usage
* 000-1FF ----s--- -------- Road 0: Solid fill (1) or ROM fill
* -------- -ccccccc Road 0: Solid color (if solid fill)
* -------i iiiiiiii Road 0: Index for other tables (if in indirect mode)
* -------r rrrrrrr- Road 0: Road ROM line select
* 200-3FF ----s--- -------- Road 1: Solid fill (1) or ROM fill
* -------- -ccccccc Road 1: Solid color (if solid fill)
* -------i iiiiiiii Road 1: Index for other tables (if in indirect mode)
* -------r rrrrrrr- Road 1: Road ROM line select
* 400-7FF ----hhhh hhhhhhhh Road 0: horizontal scroll
* 800-BFF ----hhhh hhhhhhhh Road 1: horizontal scroll
* C00-FFF ----bbbb -------- Background color index
* -------- s------- Road 1: stripe color index
* -------- -a------ Road 1: pixel value 2 color index
* -------- --b----- Road 1: pixel value 1 color index
* -------- ---c---- Road 1: pixel value 0 color index
* -------- ----s--- Road 0: stripe color index
* -------- -----a-- Road 0: pixel value 2 color index
* -------- ------b- Road 0: pixel value 1 color index
* -------- -------c Road 0: pixel value 0 color index
*
* Logic:
* First, the scanline is used to index into the tables at 000-1FF/200-3FF
* - if solid fill, the background is filled with the specified color index
* - otherwise, the remaining tables are used
*
* If indirect mode is selected, the index is taken from the low 9 bits of the
* table value from 000-1FF/200-3FF
* If direct scanline mode is selected, the index is set equal to the scanline
* for road 0, or the scanline + 256 for road 1
*
* The horizontal scroll value is looked up using the index in the tables at
* 400-7FF/800-BFF
*
* The color information is looked up using the index in the table at C00-FFF. Note
* that the same table is used for both roads.
*
*
* Out Run road priorities are controlled by a PAL that maps as indicated below.
* This was used to generate the priority_map. It is assumed that X-board is the
* same, though this logic is locked inside a Sega custom.
*
* RRC0 = CENTA & (RDA == 3) & !RRC2
* | CENTB & (RDB == 3) & RRC2
* | (RDA == 1) & !RRC2
* | (RDB == 1) & RRC2
*
* RRC1 = CENTA & (RDA == 3) & !RRC2
* | CENTB & (RDB == 3) & RRC2
* | (RDA == 2) & !RRC2
* | (RDB == 2) & RRC2
*
* RRC2 = !/HSYNC & IIQ
* | (CTRL == 3)
* | !CENTA & (RDA == 3) & !CENTB & (RDB == 3) & (CTRL == 2)
* | CENTB & (RDB == 3) & (CTRL == 2)
* | !CENTA & (RDA == 3) & !M2 & (CTRL == 2)
* | !CENTA & (RDA == 3) & !M3 & (CTRL == 2)
* | !M0 & (RDB == 0) & (CTRL == 2)
* | !M1 & (RDB == 0) & (CTRL == 2)
* | !CENTA & (RDA == 3) & CENTB & (RDB == 3) & (CTRL == 1)
* | !M0 & CENTB & (RDB == 3) & (CTRL == 1)
* | !M1 & CENTB & (RDB == 3) & (CTRL == 1)
* | !CENTA & M0 & (RDB == 0) & (CTRL == 1)
* | !CENTA & M1 & (RDB == 0) & (CTRL == 1)
* | !CENTA & (RDA == 3) & (RDB == 1) & (CTRL == 1)
* | !CENTA & (RDA == 3) & (RDB == 2) & (CTRL == 1)
*
* RRC3 = VA11 & VB11
* | VA11 & (CTRL == 0)
* | (CTRL == 3) & VB11
*
* RRC4 = !CENTA & (RDA == 3) & !CENTB & (RDB == 3)
* | VA11 & VB11
* | VA11 & (CTRL == 0)
* | (CTRL == 3) & VB11
* | !CENTB & (RDB == 3) & (CTRL == 3)
* | !CENTA & (RDA == 3) & (CTRL == 0)
*
*******************************************************************************************/
static void segaic16_road_outrun_decode(struct road_info *info)
{
int x, y;
/* allocate memory for the unpacked road data */
info->gfx = auto_malloc((256 * 2 + 1) * 512);
/* loop over rows */
for (y = 0; y < 256 * 2; y++)
{
UINT8 *src = memory_region(REGION_GFX3) + ((y & 0xff) * 0x40 + (y >> 8) * 0x8000) % memory_region_length(REGION_GFX3);
UINT8 *dst = info->gfx + y * 512;
/* loop over columns */
for (x = 0; x < 512; x++)
{
dst[x] = (((src[x/8] >> (~x & 7)) & 1) << 0) | (((src[x/8 + 0x4000] >> (~x & 7)) & 1) << 1);
/* pre-mark road data in the "stripe" area with a high bit */
if (x >= 256-8 && x < 256 && dst[x] == 3)
dst[x] |= 4;
}
}
/* set up a dummy road in the last entry */
memset(info->gfx + 256 * 2 * 512, 3, 512);
}
static void segaic16_road_outrun_draw(struct road_info *info, mame_bitmap *bitmap, const rectangle *cliprect, int priority)
{
UINT16 *roadram = info->buffer;
int x, y;
/* loop over scanlines */
for (y = cliprect->min_y; y <= cliprect->max_y; y++)
{
static const UINT8 priority_map[2][8] =
{
{ 0x80,0x81,0x81,0x87,0,0,0,0x00 },
{ 0x81,0x81,0x81,0x8f,0,0,0,0x80 }
//
// Original guesses from X-board priorities:
// { 0x80,0x81,0x81,0x83,0,0,0,0x00 },
// { 0x81,0x87,0x87,0x8f,0,0,0,0x00 }
};
UINT16 *dest = BITMAP_ADDR16(bitmap, y, 0);
int data0 = roadram[0x000 + y];
int data1 = roadram[0x100 + y];
/* background case: look for solid fill scanlines */
if (priority == SEGAIC16_ROAD_BACKGROUND)
{
int color = -1;
/* based on the info->control, we can figure out which sky to draw */
switch (info->control & 3)
{
case 0:
if (data0 & 0x800)
color = data0 & 0x7f;
break;
case 1:
if (data0 & 0x800)
color = data0 & 0x7f;
else if (data1 & 0x800)
color = data1 & 0x7f;
break;
case 2:
if (data1 & 0x800)
color = data1 & 0x7f;
else if (data0 & 0x800)
color = data0 & 0x7f;
break;
case 3:
if (data1 & 0x800)
color = data1 & 0x7f;
break;
}
/* fill the scanline with color */
if (color != -1)
{
color |= info->colorbase3;
for (x = cliprect->min_x; x <= cliprect->max_x; x++)
dest[x] = color;
}
}
/* foreground case: render from ROM */
else
{
int hpos0, hpos1, color0, color1;
int control = info->control & 3;
UINT16 color_table[32];
UINT8 *src0, *src1;
UINT8 bgcolor;
/* if both roads are low priority, skip */
if ((data0 & 0x800) && (data1 & 0x800))
continue;
/* get road 0 data */
src0 = (data0 & 0x800) ? info->gfx + 256 * 2 * 512 : (info->gfx + (0x000 + ((data0 >> 1) & 0xff)) * 512);
hpos0 = (roadram[0x200 + ((info->control & 4) ? y : (data0 & 0x1ff))]) & 0xfff;
color0 = roadram[0x600 + ((info->control & 4) ? y : (data0 & 0x1ff))];
/* get road 1 data */
src1 = (data1 & 0x800) ? info->gfx + 256 * 2 * 512 : (info->gfx + (0x100 + ((data1 >> 1) & 0xff)) * 512);
hpos1 = (roadram[0x400 + ((info->control & 4) ? (0x100 + y) : (data1 & 0x1ff))]) & 0xfff;
color1 = roadram[0x600 + ((info->control & 4) ? (0x100 + y) : (data1 & 0x1ff))];
/* determine the 5 colors for road 0 */
color_table[0x00] = info->colorbase1 ^ 0x00 ^ ((color0 >> 0) & 1);
color_table[0x01] = info->colorbase1 ^ 0x02 ^ ((color0 >> 1) & 1);
color_table[0x02] = info->colorbase1 ^ 0x04 ^ ((color0 >> 2) & 1);
bgcolor = (color0 >> 8) & 0xf;
color_table[0x03] = (data0 & 0x200) ? color_table[0x00] : (info->colorbase2 ^ 0x00 ^ bgcolor);
color_table[0x07] = info->colorbase1 ^ 0x06 ^ ((color0 >> 3) & 1);
/* determine the 5 colors for road 1 */
color_table[0x10] = info->colorbase1 ^ 0x08 ^ ((color1 >> 4) & 1);
color_table[0x11] = info->colorbase1 ^ 0x0a ^ ((color1 >> 5) & 1);
color_table[0x12] = info->colorbase1 ^ 0x0c ^ ((color1 >> 6) & 1);
bgcolor = (color1 >> 8) & 0xf;
color_table[0x13] = (data1 & 0x200) ? color_table[0x10] : (info->colorbase2 ^ 0x10 ^ bgcolor);
color_table[0x17] = info->colorbase1 ^ 0x0e ^ ((color1 >> 7) & 1);
/* draw the road */
switch (control)
{
case 0:
if (data0 & 0x800)
continue;
hpos0 = (hpos0 - (0x5f8 + info->xoffs)) & 0xfff;
for (x = cliprect->min_x; x <= cliprect->max_x; x++)
{
int pix0 = (hpos0 < 0x200) ? src0[hpos0] : 3;
dest[x] = color_table[0x00 + pix0];
hpos0 = (hpos0 + 1) & 0xfff;
}
break;
case 1:
hpos0 = (hpos0 - (0x5f8 + info->xoffs)) & 0xfff;
hpos1 = (hpos1 - (0x5f8 + info->xoffs)) & 0xfff;
for (x = cliprect->min_x; x <= cliprect->max_x; x++)
{
int pix0 = (hpos0 < 0x200) ? src0[hpos0] : 3;
int pix1 = (hpos1 < 0x200) ? src1[hpos1] : 3;
if ((priority_map[0][pix0] >> pix1) & 1)
dest[x] = color_table[0x10 + pix1];
else
dest[x] = color_table[0x00 + pix0];
hpos0 = (hpos0 + 1) & 0xfff;
hpos1 = (hpos1 + 1) & 0xfff;
}
break;
case 2:
hpos0 = (hpos0 - (0x5f8 + info->xoffs)) & 0xfff;
hpos1 = (hpos1 - (0x5f8 + info->xoffs)) & 0xfff;
for (x = cliprect->min_x; x <= cliprect->max_x; x++)
{
int pix0 = (hpos0 < 0x200) ? src0[hpos0] : 3;
int pix1 = (hpos1 < 0x200) ? src1[hpos1] : 3;
if ((priority_map[1][pix0] >> pix1) & 1)
dest[x] = color_table[0x10 + pix1];
else
dest[x] = color_table[0x00 + pix0];
hpos0 = (hpos0 + 1) & 0xfff;
hpos1 = (hpos1 + 1) & 0xfff;
}
break;
case 3:
if (data1 & 0x800)
continue;
hpos1 = (hpos1 - (0x5f8 + info->xoffs)) & 0xfff;
for (x = cliprect->min_x; x <= cliprect->max_x; x++)
{
int pix1 = (hpos1 < 0x200) ? src1[hpos1] : 3;
dest[x] = color_table[0x10 + pix1];
hpos1 = (hpos1 + 1) & 0xfff;
}
break;
}
}
}
}
/*************************************
*
* General road initialization
*
*************************************/
void segaic16_road_init(int which, int type, int colorbase1, int colorbase2, int colorbase3, int xoffs)
{
struct road_info *info = &road[which];
/* reset the tilemap info */
memset(info, 0, sizeof(*info));
info->index = which;
info->type = type;
info->colorbase1 = colorbase1;
info->colorbase2 = colorbase2;
info->colorbase3 = colorbase3;
info->xoffs = xoffs;
/* set up based on which road generator */
switch (which)
{
case 0:
info->roadram = segaic16_roadram_0;
break;
default:
fatalerror("Invalid road index specified in segaic16_road_init");
}
/* determine the parameters of the road */
switch (type)
{
case SEGAIC16_ROAD_HANGON:
case SEGAIC16_ROAD_SHARRIER:
info->draw = segaic16_road_hangon_draw;
segaic16_road_hangon_decode(info);
break;
case SEGAIC16_ROAD_OUTRUN:
case SEGAIC16_ROAD_XBOARD:
info->buffer = auto_malloc(0x1000);
info->draw = segaic16_road_outrun_draw;
segaic16_road_outrun_decode(info);
break;
default:
fatalerror("Invalid road system specified in segaic16_road_init");
}
}
/*************************************
*
* General road drawing
*
*************************************/
void segaic16_road_draw(int which, mame_bitmap *bitmap, const rectangle *cliprect, int priority)
{
struct road_info *info = &road[which];
(*info->draw)(info, bitmap, cliprect, priority);
}
/*************************************
*
* General road control read/write
*
*************************************/
READ16_HANDLER( segaic16_road_control_0_r )
{
struct road_info *info = &road[0];
if (info->buffer)
{
UINT32 *src = (UINT32 *)info->roadram;
UINT32 *dst = (UINT32 *)info->buffer;
int i;
/* swap the halves of the road RAM */
for (i = 0; i < 0x1000/4; i++)
{
UINT32 temp = *src;
*src++ = *dst;
*dst++ = temp;
}
}
return 0xffff;
}
WRITE16_HANDLER( segaic16_road_control_0_w )
{
if (ACCESSING_LSB)
{
road[0].control = data & ((road[0].type == SEGAIC16_ROAD_OUTRUN) ? 3 : 7);
}
}
/*************************************
*
* General rotation initialization
*
*************************************/
void segaic16_rotate_init(int which, int type, int colorbase)
{
struct rotate_info *info = &rotate[which];
/* reset the tilemap info */
memset(info, 0, sizeof(*info));
info->index = which;
info->type = type;
info->colorbase = colorbase;
/* set up based on which road generator */
switch (which)
{
case 0:
info->rotateram = segaic16_rotateram_0;
break;
default:
fatalerror("Invalid rotate index specified in segaic16_rotate_init");
}
/* determine the parameters of the rotate */
switch (type)
{
case SEGAIC16_ROTATE_YBOARD:
info->ramsize = 0x800;
break;
default:
fatalerror("Invalid rotate system specified in segaic16_rotate_init");
}
/* allocate a buffer for swapping */
info->buffer = auto_malloc(info->ramsize);
state_save_register_item("segaic16_rot", which, info->colorbase);
state_save_register_item_pointer("segaic16_rot", which, ((UINT8 *) info->buffer), info->ramsize);
}
/*************************************
*
* General rotation drawing
*
*************************************/
void segaic16_rotate_draw(int which, mame_bitmap *bitmap, const rectangle *cliprect, mame_bitmap *srcbitmap)
{
struct rotate_info *info = &rotate[which];
INT32 currx = (info->buffer[0x3f0] << 16) | info->buffer[0x3f1];
INT32 curry = (info->buffer[0x3f2] << 16) | info->buffer[0x3f3];
INT32 dyy = (info->buffer[0x3f4] << 16) | info->buffer[0x3f5];
INT32 dxx = (info->buffer[0x3f6] << 16) | info->buffer[0x3f7];
INT32 dxy = (info->buffer[0x3f8] << 16) | info->buffer[0x3f9];
INT32 dyx = (info->buffer[0x3fa] << 16) | info->buffer[0x3fb];
int x, y;
/* advance forward based on the clip rect */
currx += dxx * (cliprect->min_x + 27) + dxy * cliprect->min_y;
curry += dyx * (cliprect->min_x + 27) + dyy * cliprect->min_y;
/* loop over screen Y coordinates */
for (y = cliprect->min_y; y <= cliprect->max_y; y++)
{
UINT16 *dest = BITMAP_ADDR16(bitmap, y, 0);
UINT16 *src = (UINT16 *)srcbitmap->base;
UINT8 *pri = BITMAP_ADDR8(priority_bitmap, y, 0);
INT32 tx = currx;
INT32 ty = curry;
/* loop over screen X coordinates */
for (x = cliprect->min_x; x <= cliprect->max_x; x++)
{
/* fetch the pixel from the source bitmap */
int sx = (tx >> 14) & 0x1ff;
int sy = (ty >> 14) & 0x1ff;
int pix = src[sy * srcbitmap->rowpixels + sx];
/* non-zero pixels get written; everything else is the scanline color */
if (pix != 0xffff)
{
*dest++ = (pix & 0x1ff) | ((pix >> 6) & 0x200) | ((pix >> 3) & 0xc00) | 0x1000;
*pri++ = (pix >> 8) | 1;
}
else
{
*dest++ = info->colorbase + sy;
*pri++ = 0xff;
}
/* advance the source X/Y pointers */
tx += dxx;
ty += dyx;
}
/* advance the source X/Y pointers */
currx += dxy;
curry += dyy;
}
}
/*************************************
*
* General road control read/write
*
*************************************/
READ16_HANDLER( segaic16_rotate_control_0_r )
{
struct rotate_info *info = &rotate[0];
if (info->buffer)
{
UINT32 *src = (UINT32 *)info->rotateram;
UINT32 *dst = (UINT32 *)info->buffer;
int i;
/* swap the halves of the rotation RAM */
for (i = 0; i < info->ramsize/4; i++)
{
UINT32 temp = *src;
*src++ = *dst;
*dst++ = temp;
}
}
return 0xffff;
}