/* Sega system24 hardware System 24 68000x2 315-5292 315-5293 315-5294 315-5242 ym2151 dac 315-5195(x3) 315-5296(IO) 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. */ #include "emu.h" #include "segaic24.h" const device_type S24TILE = &device_creator; const device_type S24SPRITE = &device_creator; const device_type S24MIXER = &device_creator; segas24_tile::segas24_tile(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, S24TILE, "S24TILE", tag, owner, clock) { } void segas24_tile::static_set_tile_mask(device_t &device, UINT16 _tile_mask) { segas24_tile &dev = downcast(device); dev.tile_mask = _tile_mask; } #define XOR(a) WORD_XOR_BE(a) const gfx_layout segas24_tile::char_layout = { 8, 8, SYS24_TILES, 4, { 0, 1, 2, 3 }, { XOR(0)*4, XOR(1)*4, XOR(2)*4, XOR(3)*4, XOR(4)*4, XOR(5)*4, XOR(6)*4, XOR(7)*4 }, { 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32 }, 8*32 }; void segas24_tile::tile_info(int offset, tile_data *tileinfo, tilemap_memory_index tile_index) { UINT16 val = tile_ram[tile_index|offset]; tileinfo->category = (val & 0x8000) != 0; tileinfo_set(machine(), tileinfo, char_gfx_index, val & tile_mask, (val >> 7) & 0xff, 0); } TILE_GET_INFO_DEVICE( segas24_tile::tile_info_0s ) { downcast(device)->tile_info(0x0000, tileinfo, tile_index); } TILE_GET_INFO_DEVICE( segas24_tile::tile_info_0w ) { downcast(device)->tile_info(0x1000, tileinfo, tile_index); } TILE_GET_INFO_DEVICE( segas24_tile::tile_info_1s ) { downcast(device)->tile_info(0x2000, tileinfo, tile_index); } TILE_GET_INFO_DEVICE( segas24_tile::tile_info_1w ) { downcast(device)->tile_info(0x3000, tileinfo, tile_index); } void segas24_tile::device_start() { for(char_gfx_index = 0; char_gfx_index < MAX_GFX_ELEMENTS; char_gfx_index++) if (machine().gfx[char_gfx_index] == 0) break; assert(char_gfx_index != MAX_GFX_ELEMENTS); char_ram = auto_alloc_array(machine(), UINT16, 0x80000/2); tile_ram = auto_alloc_array(machine(), UINT16, 0x10000/2); tile_layer[0] = tilemap_create_device(this, tile_info_0s, tilemap_scan_rows, 8, 8, 64, 64); tile_layer[1] = tilemap_create_device(this, tile_info_0w, tilemap_scan_rows, 8, 8, 64, 64); tile_layer[2] = tilemap_create_device(this, tile_info_1s, tilemap_scan_rows, 8, 8, 64, 64); tile_layer[3] = tilemap_create_device(this, tile_info_1w, tilemap_scan_rows, 8, 8, 64, 64); tilemap_set_transparent_pen(tile_layer[0], 0); tilemap_set_transparent_pen(tile_layer[1], 0); tilemap_set_transparent_pen(tile_layer[2], 0); tilemap_set_transparent_pen(tile_layer[3], 0); memset(char_ram, 0, 0x80000); memset(tile_ram, 0, 0x10000); machine().gfx[char_gfx_index] = gfx_element_alloc(machine(), &char_layout, (UINT8 *)char_ram, machine().total_colors() / 16, 0); save_pointer(NAME(tile_ram), 0x10000/2); save_pointer(NAME(char_ram), 0x80000/2); } void segas24_tile::draw_rect(bitmap_t *bm, bitmap_t *tm, bitmap_t *dm, const UINT16 *mask, UINT16 tpri, UINT8 lpri, int win, int sx, int sy, int xx1, int yy1, int xx2, int yy2) { int y; const UINT16 *source = ((UINT16 *)bm->base) + sx + sy*bm->rowpixels; const UINT8 *trans = ((UINT8 *) tm->base) + sx + sy*tm->rowpixels; UINT8 *prib = (UINT8 *)machine().priority_bitmap->base; UINT16 *dest = (UINT16 *)dm->base; tpri |= TILEMAP_PIXEL_LAYER0; dest += yy1*dm->rowpixels + xx1; prib += yy1*machine().priority_bitmap->rowpixels + xx1; mask += yy1*4; yy2 -= yy1; while(xx1 >= 128) { xx1 -= 128; xx2 -= 128; mask++; } for(y=0; y 0) { UINT16 m = *mask1++; if(win) m = ~m; if(!cur_x && llx>=128) { // Fast paths for the 128-pixels without side clipping case if(!m) { // 1- 128 pixels from this layer int x; for(x=0; x<128; x++) { if(*srct++ == tpri) { *dst = *src; *pr |= lpri; } src++; dst++; pr++; } } else if(m == 0xffff) { // 2- 128 pixels from the other layer src += 128; srct += 128; dst += 128; pr += 128; } else { // 3- 128 pixels from both layers int x; for(x=0; x<128; x+=8) { if(!(m & 0x8000)) { int xx; for(xx=0; xx<8; xx++) if(srct[xx] == tpri) { dst[xx] = src[xx]; pr[xx] |= lpri; } } src += 8; srct += 8; dst += 8; pr += 8; m <<= 1; } } } else { // Clipped path int llx1 = llx >= 128 ? 128 : llx; if(!m) { // 1- 128 pixels from this layer int x; for(x = cur_x; x> (x >> 3)))) { *dst = *src; *pr |= lpri; } src++; dst++; pr++; } } } llx -= 128; cur_x = 0; } source += bm->rowpixels; trans += tm->rowpixels; dest += dm->rowpixels; prib += machine().priority_bitmap->rowpixels; mask += 4; } } // The rgb version is used by model 1 & 2 which do not need to care // about sprite priority hence the lack of support for the // priority_bitmap void segas24_tile::draw_rect_rgb(bitmap_t *bm, bitmap_t *tm, bitmap_t *dm, const UINT16 *mask, UINT16 tpri, UINT8 lpri, int win, int sx, int sy, int xx1, int yy1, int xx2, int yy2) { int y; const UINT16 *source = ((UINT16 *)bm->base) + sx + sy*bm->rowpixels; const UINT8 *trans = ((UINT8 *) tm->base) + sx + sy*tm->rowpixels; UINT16 *dest = (UINT16 *)dm->base; const pen_t *pens = machine().pens; tpri |= TILEMAP_PIXEL_LAYER0; dest += yy1*dm->rowpixels + xx1; mask += yy1*4; yy2 -= yy1; while(xx1 >= 128) { xx1 -= 128; xx2 -= 128; mask++; } for(y=0; y 0) { UINT16 m = *mask1++; if(win) m = ~m; if(!cur_x && llx>=128) { // Fast paths for the 128-pixels without side clipping case if(!m) { // 1- 128 pixels from this layer int x; for(x=0; x<128; x++) { if(*srct++ == tpri) *dst = pens[*src]; src++; dst++; } } else if(m == 0xffff) { // 2- 128 pixels from the other layer src += 128; srct += 128; dst += 128; } else { // 3- 128 pixels from both layers int x; for(x=0; x<128; x+=8) { if(!(m & 0x8000)) { int xx; for(xx=0; xx<8; xx++) if(srct[xx] == tpri) dst[xx] = pens[src[xx]]; } src += 8; srct += 8; dst += 8; m <<= 1; } } } else { // Clipped path int llx1 = llx >= 128 ? 128 : llx; if(!m) { // 1- 128 pixels from this layer int x; for(x = cur_x; x> (x >> 3)))) *dst = pens[*src]; src++; dst++; } } } llx -= 128; cur_x = 0; } source += bm->rowpixels; trans += tm->rowpixels; dest += dm->rowpixels; mask += 4; } } void segas24_tile::draw(bitmap_t *bitmap, const rectangle *cliprect, int layer, int lpri, int flags) { UINT16 hscr = tile_ram[0x5000+(layer >> 1)]; UINT16 vscr = tile_ram[0x5004+(layer >> 1)]; UINT16 ctrl = tile_ram[0x5004+((layer >> 1) & 2)]; UINT16 *mask = tile_ram + (layer & 4 ? 0x6800 : 0x6000); UINT16 tpri = layer & 1; lpri = 1 << lpri; layer >>= 1; // Layer disable if(vscr & 0x8000) return; if(ctrl & 0x6000) { // Special window/scroll modes if(layer & 1) return; tilemap_set_scrolly(tile_layer[layer], 0, vscr & 0x1ff); tilemap_set_scrolly(tile_layer[layer|1], 0, vscr & 0x1ff); if(hscr & 0x8000) { UINT16 *hscrtb = tile_ram + 0x4000 + 0x200*layer; switch((ctrl & 0x6000) >> 13) { case 1: { int y; UINT16 v = (-vscr) & 0x1ff; if(!((-vscr) & 0x200)) layer ^= 1; for(y=cliprect->min_y; y<=cliprect->max_y; y++) { UINT16 h; rectangle c = *cliprect; int l1 = layer; if(y >= v) l1 ^= 1; c.min_y = c.max_y = y; hscr = hscrtb[y]; h = hscr & 0x1ff; tilemap_set_scrollx(tile_layer[l1], 0, -h); tilemap_draw(bitmap, &c, tile_layer[l1], tpri, lpri); } break; } case 2: case 3: { int y; for(y=cliprect->min_y; y<=cliprect->max_y; y++) { UINT16 h; rectangle c1 = *cliprect; rectangle c2 = *cliprect; int l1 = layer; hscr = hscrtb[y]; h = hscr & 0x1ff; tilemap_set_scrollx(tile_layer[layer], 0, -h); tilemap_set_scrollx(tile_layer[layer|1], 0, -h); if(c1.max_x >= h) c1.max_x = h-1; if(c2.min_x < h) c2.min_x = h; if(!(hscr & 0x200)) l1 ^= 1; c1.min_y = c1.max_y = c2.min_y = c2.max_y = y; tilemap_draw(bitmap, &c1, tile_layer[l1], tpri, lpri); tilemap_draw(bitmap, &c2, tile_layer[l1^1], tpri, lpri); } break; } } } else { tilemap_set_scrollx(tile_layer[layer], 0, -(hscr & 0x1ff)); tilemap_set_scrollx(tile_layer[layer|1], 0, -(hscr & 0x1ff)); switch((ctrl & 0x6000) >> 13) { case 1: { rectangle c1 = *cliprect; rectangle c2 = *cliprect; UINT16 v; v = (-vscr) & 0x1ff; if(c1.max_y >= v) c1.max_y = v-1; if(c2.min_y < v) c2.min_y = v; if(!((-vscr) & 0x200)) layer ^= 1; tilemap_draw(bitmap, &c1, tile_layer[layer], tpri, lpri); tilemap_draw(bitmap, &c2, tile_layer[layer^1], tpri, lpri); break; } case 2: case 3: { rectangle c1 = *cliprect; rectangle c2 = *cliprect; UINT16 h; h = (+hscr) & 0x1ff; if(c1.max_x >= h) c1.max_x = h-1; if(c2.min_x < h) c2.min_x = h; if(!((+hscr) & 0x200)) layer ^= 1; tilemap_draw(bitmap, &c1, tile_layer[layer], tpri, lpri); tilemap_draw(bitmap, &c2, tile_layer[layer^1], tpri, lpri); break; } } } } else { bitmap_t *bm, *tm; void (segas24_tile::*draw)(bitmap_t *, bitmap_t *, bitmap_t *, const UINT16 *, UINT16, UINT8, int, int, int, int, int, int, int); int win = layer & 1; if(bitmap->format != BITMAP_FORMAT_INDEXED16) draw = &segas24_tile::draw_rect_rgb; else draw = &segas24_tile::draw_rect; bm = tilemap_get_pixmap(tile_layer[layer]); tm = tilemap_get_flagsmap(tile_layer[layer]); if(hscr & 0x8000) { int y; UINT16 *hscrtb = tile_ram + 0x4000 + 0x200*layer; vscr &= 0x1ff; for(y=0; y<384; y++) { hscr = (-hscrtb[y]) & 0x1ff; if(hscr + 496 <= 512) { // Horizontal split unnecessary (this->*draw)(bm, tm, bitmap, mask, tpri, lpri, win, hscr, vscr, 0, y, 496, y+1); } else { // Horizontal split necessary (this->*draw)(bm, tm, bitmap, mask, tpri, lpri, win, hscr, vscr, 0, y, 512-hscr, y+1); (this->*draw)(bm, tm, bitmap, mask, tpri, lpri, win, 0, vscr, 512-hscr, y, 496, y+1); } vscr = (vscr + 1) & 0x1ff; } } else { hscr = (-hscr) & 0x1ff; vscr = (+vscr) & 0x1ff; if(hscr + 496 <= 512) { // Horizontal split unnecessary if(vscr + 384 <= 512) { // Vertical split unnecessary (this->*draw)(bm, tm, bitmap, mask, tpri, lpri, win, hscr, vscr, 0, 0, 496, 384); } else { // Vertical split necessary (this->*draw)(bm, tm, bitmap, mask, tpri, lpri, win, hscr, vscr, 0, 0, 496, 512-vscr); (this->*draw)(bm, tm, bitmap, mask, tpri, lpri, win, hscr, 0, 0, 512-vscr, 496, 384); } } else { // Horizontal split necessary if(vscr + 384 <= 512) { // Vertical split unnecessary (this->*draw)(bm, tm, bitmap, mask, tpri, lpri, win, hscr, vscr, 0, 0, 512-hscr, 384); (this->*draw)(bm, tm, bitmap, mask, tpri, lpri, win, 0, vscr, 512-hscr, 0, 496, 384); } else { // Vertical split necessary (this->*draw)(bm, tm, bitmap, mask, tpri, lpri, win, hscr, vscr, 0, 0, 512-hscr, 512-vscr); (this->*draw)(bm, tm, bitmap, mask, tpri, lpri, win, 0, vscr, 512-hscr, 0, 496, 512-vscr); (this->*draw)(bm, tm, bitmap, mask, tpri, lpri, win, hscr, 0, 0, 512-vscr, 512-hscr, 384); (this->*draw)(bm, tm, bitmap, mask, tpri, lpri, win, 0, 0, 512-hscr, 512-vscr, 496, 384); } } } } } READ16_MEMBER(segas24_tile::tile_r) { return tile_ram[offset]; } READ16_MEMBER(segas24_tile::char_r) { return char_ram[offset]; } WRITE16_MEMBER(segas24_tile::tile_w) { COMBINE_DATA(tile_ram + offset); if(offset < 0x4000) tilemap_mark_tile_dirty(tile_layer[offset >> 12], offset & 0xfff); } WRITE16_MEMBER(segas24_tile::char_w) { UINT16 old = char_ram[offset]; COMBINE_DATA(char_ram + offset); if(old != char_ram[offset]) gfx_element_mark_dirty(machine().gfx[char_gfx_index], offset / 16); } READ32_MEMBER(segas24_tile::tile32_r) { return tile_r(space, offset*2, mem_mask&0xffff) | tile_r(space, (offset*2)+1, mem_mask>>16)<<16; } READ32_MEMBER(segas24_tile::char32_r) { return char_r(space, offset*2, mem_mask&0xffff) | char_r(space, (offset*2)+1, mem_mask>>16)<<16; } WRITE32_MEMBER(segas24_tile::tile32_w) { tile_w(space, offset*2, data&0xffff, mem_mask&0xffff); tile_w(space, (offset*2)+1, data>>16, mem_mask>>16); } WRITE32_MEMBER(segas24_tile::char32_w) { char_w(space, offset*2, data&0xffff, mem_mask&0xffff); char_w(space, (offset*2)+1, data>>16, mem_mask>>16); } segas24_sprite::segas24_sprite(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, S24SPRITE, "S24SPRITE", tag, owner, clock) { } void segas24_sprite::device_start() { sprite_ram = auto_alloc_array(machine(), UINT16, 0x40000/2); save_pointer(NAME(sprite_ram), 0x40000/2); } /* System24 sprites Normal sprite: 0 00Z--nnn nnnnnnnn zoom mode (1 = separate x and y), next sprite 1 xxxxxxxx yyyyyyyy zoom x, zoom y (zoom y is used for both when mode = 0) 2 --TTTTTT TTTTTTTT sprite number 3 PPPPCCCC CCCCCCCC priority, indirect palette base 4 FSSSYYYY YYYYYYYY flipy, y size, top 5 FSSSXXXX XXXXXXXX flipx, x size, left Clip? 0 01---nnn nnnnnnnn next sprite 1 hVH----- -------- hide/vflip/hflip 2 -------y yyyyyyyy Clip top 2 -------x xxxxxxxx Clip left 2 -------y yyyyyyyy Clip bottom 2 -------x xxxxxxxx Clip right Skipped entry 0 10---nnn nnnnnnnn next sprite End of sprite list 0 11------ -------- */ void segas24_sprite::draw(bitmap_t *bitmap, const rectangle *cliprect, const int *spri) { UINT16 curspr = 0; int countspr = 0; int seen; UINT8 pmt[4]; int i; UINT16 *sprd[0x2000], *clip[0x2000]; UINT16 *cclip = 0; for(i=0; i<4; i++) pmt[i] = 0xff << (1+spri[3-i]); for(seen = 0; seen < 0x2000; seen++) { UINT16 *source; UINT16 type; source = sprite_ram + (curspr << 3); if(curspr == 0 && source[0] == 0) break; curspr = source[0]; type = curspr & 0xc000; curspr &= 0x1fff; if(type == 0xc000) break; if(type == 0x8000) continue; if(type == 0x4000) { cclip = source; continue; } sprd[countspr] = source; clip[countspr] = cclip; countspr++; if(!curspr) break; } for(countspr--; countspr >= 0; countspr--) { UINT16 *source, *pix; int x, y, sx, sy; int px, py; UINT16 colors[16]; int flipx, flipy; int zoomx, zoomy; UINT8 pm[16]; // int dump; int xmod, ymod; int min_x, min_y, max_x, max_y; UINT32 addoffset; UINT32 newoffset; UINT32 offset; source = sprd[countspr]; cclip = clip[countspr]; if(cclip) { min_y = (cclip[2] & 511); min_x = (cclip[3] & 511) - 8; max_y = (cclip[4] & 511); max_x = (cclip[5] & 511) - 8; } else { min_x = 0; max_x = 495; min_y = 0; max_y = 383; } if(min_x < cliprect->min_x) min_x = cliprect->min_x; if(min_y < cliprect->min_y) min_y = cliprect->min_y; if(max_x > cliprect->max_x) max_x = cliprect->max_x; if(max_y > cliprect->max_y) max_y = cliprect->max_y; if(!(source[0] & 0x2000)) zoomx = zoomy = source[1] & 0xff; else { zoomx = source[1] >> 8; zoomy = source[1] & 0xff; } if(!zoomx) zoomx = 0x3f; if(!zoomy) zoomy = 0x3f; zoomx++; zoomy++; x = source[5] & 0xfff; flipx = source[5] & 0x8000; if(x & 0x800) x -= 0x1000; sx = 1 << ((source[5] & 0x7000) >> 12); x -= 8; y = source[4] & 0xfff; if(y & 0x800) y -= 0x1000; flipy = source[4] & 0x8000; sy = 1 << ((source[4] & 0x7000) >> 12); pix = &sprite_ram[(source[3] & 0x3fff)* 0x8]; for(px=0; px<8; px++) { int c; c = pix[px] >> 8; pm[px*2] = pmt[c>>6]; if(c>1) c |= 0x1000; colors[px*2] = c; c = pix[px] & 0xff; pm[px*2+1] = pmt[c>>6]; if(c>1) c |= 0x1000; colors[px*2+1] = c; } offset = (source[2] & 0x7fff) * 0x10; xmod = 0x20; ymod = 0x20; for(py=0; py= 0x40) { if(ypos1 >= min_y && ypos1 <= max_y) { int zx; xmod2 = xmod1; xpos2 = xpos1; for(zx=0; zx<8; zx++) { xmod2 += zoomx; while(xmod2 >= 0x40) { if(xpos2 >= min_x && xpos2 <= max_x) { int zx1 = flipx ? 7-zx : zx; UINT32 neweroffset = (newoffset+(zx1>>2))&0x1ffff; // crackdown sometimes attempts to use data past the end of spriteram int c = (sprite_ram[neweroffset] >> (((~zx1) & 3) << 2)) & 0xf; UINT8 *pri = BITMAP_ADDR8(machine().priority_bitmap, ypos1, xpos2); if(!(*pri & pm[c])) { c = colors[c]; if(c) { UINT16 *dst = BITMAP_ADDR16(bitmap, ypos1, xpos2); if(c==1) *dst = (*dst) | 0x2000; else *dst = c; *pri = 0xff; } } } xmod2 -= 0x40; xpos2++; } } } ymod1 -= 0x40; ypos1++; } if(flipy) newoffset -= 2; else newoffset += 2; } xpos1 = xpos2; xmod1 = xmod2; } y = ypos1; ymod = ymod1; } } } WRITE16_MEMBER(segas24_sprite::write) { COMBINE_DATA(sprite_ram + offset); } READ16_MEMBER(segas24_sprite::read) { return sprite_ram[offset]; } segas24_mixer::segas24_mixer(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, S24MIXER, "S24MIXER", tag, owner, clock) { } void segas24_mixer::device_start() { memset(mixer_reg, 0, sizeof(mixer_reg)); save_item(NAME(mixer_reg)); } WRITE16_MEMBER(segas24_mixer::write) { COMBINE_DATA(mixer_reg + offset); } READ16_MEMBER(segas24_mixer::read) { return mixer_reg[offset]; } UINT16 segas24_mixer::get_reg(int reg) { return mixer_reg[reg]; }