/*************************************************************************** vidhrdw.c Functions to emulate the video hardware of the machine. ***************************************************************************/ #include #include #include #include "Z80.h" #include "machine.h" #include "vidhrdw.h" #include "roms.h" #include "romdecod.h" #include "memmap.h" #include "osdepend.h" unsigned char chars[8 * 8 * TOTAL_CHARS]; /* character bitmaps */ unsigned char sprites[SPRITE_WIDTH * SPRITE_HEIGHT * TOTAL_SPRITES]; /* sprite bitmaps */ unsigned char spritechars[0x4000]; /* 16k for bigsprite bitmaps */ unsigned char tmpbitmap[BITMAP_WIDTH * BITMAP_HEIGHT]; /* temporary bitmap used to hold the */ /* character mapped part of the screen */ unsigned char bigsprite[BIGSPRITE_WIDTH * BIGSPRITE_HEIGHT]; /* temp bitmap used for the big sprite */ unsigned char *scrbitmap; unsigned char dirtybuffer[VIDEO_RAM_SIZE]; /* keep track of modified portions of the screen */ /* to speed up video refresh */ unsigned char bsdirtybuffer[BIGSPRITE_SIZE]; const unsigned char *palette; const unsigned char *colortable; unsigned char remappedtable[4*COLOR_CODES]; unsigned char backgroundpen; /*************************************************************************** Initialize the video hardware. Returns 0 if successful. This usually involves no more than loading the graphics roms, decoding them and store the graphics somewhere. ***************************************************************************/ int vh_init(const char *gamename) { char *tmpstorage; int i; if ((tmpstorage = malloc(0x8000)) == 0) return 1; i = 0; while (gamevidinfo[i].name && stricmp(gamename,gamevidinfo[i].name) != 0) i++; if (readroms(tmpstorage,gamevidinfo[i].gfxrom,gamename) != 0) { free(tmpstorage); return 1; } decodechars(tmpstorage,chars,512); decodesprites(chars,sprites,128); decodechars(tmpstorage + 0x2000,chars + 0x8000,512); decodesprites(chars + 0x8000,sprites + 0x8000,128); decodechars(tmpstorage + 0x4000,spritechars,256); free(tmpstorage); palette = gamevidinfo[i].palette; colortable = gamevidinfo[i].colortable; return 0; } /*************************************************************************** Start the video hardware emulation, that is set up a gfx mode, load the appropriate palette, and return. Returns 0 if successful. ***************************************************************************/ int vh_start(void) { int i; unsigned char pens[TOTAL_COLORS]; if ((scrbitmap = osd_create_display(BITMAP_WIDTH,BITMAP_HEIGHT)) == 0) return 1; for (i = 0;i < TOTAL_COLORS;i++) pens[i] = osd_obtain_pen(palette[3*i],palette[3*i+1],palette[3*i+2]); backgroundpen = pens[0]; for (i = 0;i < 4*COLOR_CODES;i++) remappedtable[i] = pens[colortable[i]]; memset(scrbitmap,backgroundpen,BITMAP_WIDTH * BITMAP_HEIGHT); return 0; } /*************************************************************************** Stop the video hardware emulation. ***************************************************************************/ void vh_stop(void) { osd_close_display(); } /*************************************************************************** Handle a write to memory. This function is called when the emulated code writes to RAM. Arguments: dword A - Memory address to write to. byte V - Value to write into memory. If the address given concerns the video hardware, the write is performed (together with additional operations which might be required by the video hardware) and the function returns non zero. Otherwise, it returns 0. ***************************************************************************/ int vh_wrmem(dword A,byte V) { if (A >= VIDEO_RAM_START && A < VIDEO_RAM_START + VIDEO_RAM_SIZE) { if (RAM[A] != V) { dirtybuffer[A - VIDEO_RAM_START] = 1; RAM[A] = V; } return 1; } else if (A >= MIRROR_VIDEO_RAM_START && A < MIRROR_VIDEO_RAM_START + VIDEO_RAM_SIZE) { /* Crazy Climber's address decoder decodes to 2k blocks. Sometimes the */ /* game writes to a mirror address. */ A -= (MIRROR_VIDEO_RAM_START - VIDEO_RAM_START); if (RAM[A] != V) { dirtybuffer[A - VIDEO_RAM_START] = 1; RAM[A] = V; } return 1; } else if (A >= COLOR_RAM_START && A < COLOR_RAM_START + VIDEO_RAM_SIZE) { /* bit 5 of the address is not used for color memory. There is just */ /* 512k of memory; every two consecutive rows share the same memory */ /* region. */ if (RAM[A] != V) { A &=0xffdf; dirtybuffer[A - COLOR_RAM_START] = 1; dirtybuffer[A - COLOR_RAM_START + 0x20] = 1; RAM[A] = V; RAM[A + 0x20] = V; } return 1; } else if (A >= BIGSPRITE_START && A < BIGSPRITE_START + BIGSPRITE_SIZE) { if (RAM[A] != V) { bsdirtybuffer[A - BIGSPRITE_START] = 1; RAM[A] = V; } return 1; } else return 0; } void drawchar(unsigned char *bitmap,int charcode,int color,int flipx,int flipy,int sx,int sy) { int x,y; const unsigned char *chardata; const unsigned char *paldata; unsigned char *bm; if (sx < FIRST_VISIBLE_COLUMN || sx >= LAST_VISIBLE_COLUMN || sy < FIRST_VISIBLE_ROW || sy >= LAST_VISIBLE_ROW) return; chardata = &chars[8*8 * (charcode % TOTAL_CHARS)]; paldata = &remappedtable[4 * (color % COLOR_CODES)]; bm = &bitmap[8 * (BITMAP_WIDTH * sy + sx)]; if (flipx) { if (flipy) /* XY flip */ { bm += 7 * BITMAP_WIDTH + 8; for (y = 0;y < 8;y++) { for (x = 0;x < 8;x++) { *(--bm) = paldata[*(chardata++)]; } bm -= BITMAP_WIDTH - 8; } } else /* X flip */ { bm += 8; for (y = 0;y < 8;y++) { for (x = 0;x < 8;x++) { *(--bm) = paldata[*(chardata++)]; } bm += BITMAP_WIDTH + 8; } } } else { if (flipy) /* Y flip */ { bm += 7 * BITMAP_WIDTH; for (y = 0;y < 8;y++) { for (x = 0;x < 8;x++) { *(bm++) = paldata[*(chardata++)]; } bm -= BITMAP_WIDTH + 8; } } else /* normal */ { for (y = 0;y < 8;y++) { for (x = 0;x < 8;x++) { *(bm++) = paldata[*(chardata++)]; } bm += BITMAP_WIDTH - 8; } } } } void drawsprite(unsigned char *bitmap,int spritecode,int color,int flipx,int flipy,int sx,int sy) { int ox,oy,ex,ey,x,y; const unsigned char *spritedata,*sd; const unsigned char *paldata; unsigned char *bm; int col; ox = sx; oy = sy; ex = sx + SPRITE_WIDTH; if (sx < 8 * FIRST_VISIBLE_COLUMN) sx = 8 * FIRST_VISIBLE_COLUMN; if (ex > 8 * LAST_VISIBLE_COLUMN) ex = 8 * LAST_VISIBLE_COLUMN; ey = sy + SPRITE_HEIGHT; if (sy < 8 * FIRST_VISIBLE_ROW) sy = 8 * FIRST_VISIBLE_ROW; if (ey > 8 * LAST_VISIBLE_ROW) ey = 8 * LAST_VISIBLE_ROW; spritedata = &sprites[SPRITE_WIDTH * SPRITE_HEIGHT * (spritecode % TOTAL_SPRITES)]; paldata = &remappedtable[4 * (color % COLOR_CODES)]; if (flipx) { if (flipy) /* XY flip */ { for (y = sy;y < ey;y++) { bm = &bitmap[BITMAP_WIDTH * y + sx]; sd = &spritedata[SPRITE_WIDTH * (SPRITE_WIDTH-1-y+oy) + SPRITE_HEIGHT-1-sx+ox]; for (x = sx;x < ex;x++) { col = paldata[*(sd--)]; if (col != backgroundpen) *bm = col; bm++; } } } else /* X flip */ { for (y = sy;y < ey;y++) { bm = &bitmap[BITMAP_WIDTH * y + sx]; sd = &spritedata[SPRITE_WIDTH * (y-oy) + SPRITE_HEIGHT-1-sx+ox]; for (x = sx;x < ex;x++) { col = paldata[*(sd--)]; if (col != backgroundpen) *bm = col; bm++; } } } } else { if (flipy) /* Y flip */ { for (y = sy;y < ey;y++) { bm = &bitmap[BITMAP_WIDTH * y + sx]; sd = &spritedata[SPRITE_WIDTH * (SPRITE_WIDTH-1-y+oy) + sx-ox]; for (x = sx;x < ex;x++) { col = paldata[*(sd++)]; if (col != backgroundpen) *bm = col; bm++; } } } else /* normal */ { for (y = sy;y < ey;y++) { bm = &bitmap[BITMAP_WIDTH * y + sx]; sd = &spritedata[SPRITE_WIDTH * (y-oy) + sx-ox]; for (x = sx;x < ex;x++) { col = paldata[*(sd++)]; if (col != backgroundpen) *bm = col; bm++; } } } } } void updatebigsprite(int charcode,int color,int sx,int sy) { int x,y; const unsigned char *chardata; unsigned char *bm; charcode &= 0xff; chardata = &spritechars[8*8 * charcode]; bm = &bigsprite[8 * (BIGSPRITE_WIDTH * sy + sx)]; for (y = 0;y < 8;y++) { for (x = 0;x < 8;x++) { *(bm++) = *(chardata++); } bm += BIGSPRITE_WIDTH - 8; } } void drawbigsprite(unsigned char *bitmap,int color,int flipx,int flipy,int sx,int sy) { int ox,oy,ex,ey,x,y; const unsigned char *spritedata,*sd; const unsigned char *paldata; unsigned char *bm; int col; ox = sx; oy = sy; ex = sx + BIGSPRITE_WIDTH; if (sx < 8 * FIRST_VISIBLE_COLUMN) sx = 8 * FIRST_VISIBLE_COLUMN; if (ex > 8 * LAST_VISIBLE_COLUMN) ex = 8 * LAST_VISIBLE_COLUMN; ey = sy + BIGSPRITE_HEIGHT; if (sy < 8 * FIRST_VISIBLE_ROW) sy = 8 * FIRST_VISIBLE_ROW; if (ey > 8 * LAST_VISIBLE_ROW) ey = 8 * LAST_VISIBLE_ROW; spritedata = bigsprite; paldata = &remappedtable[4 * (color % COLOR_CODES)]; if (flipx) { if (flipy) /* XY flip */ { for (y = sy;y < ey;y++) { bm = &bitmap[BITMAP_WIDTH * y + sx]; sd = &spritedata[BIGSPRITE_WIDTH * (BIGSPRITE_WIDTH-1-y+oy) + BIGSPRITE_HEIGHT-1-sx+ox]; for (x = sx;x < ex;x++) { col = paldata[*(sd--)]; if (col != backgroundpen) *bm = col; bm++; } } } else /* X flip */ { for (y = sy;y < ey;y++) { bm = &bitmap[BITMAP_WIDTH * y + sx]; sd = &spritedata[BIGSPRITE_WIDTH * (y-oy) + BIGSPRITE_HEIGHT-1-sx+ox]; for (x = sx;x < ex;x++) { col = paldata[*(sd--)]; if (col != backgroundpen) *bm = col; bm++; } } } } else { if (flipy) /* Y flip */ { for (y = sy;y < ey;y++) { bm = &bitmap[BITMAP_WIDTH * y + sx]; sd = &spritedata[BIGSPRITE_WIDTH * (BIGSPRITE_WIDTH-1-y+oy) + sx-ox]; for (x = sx;x < ex;x++) { col = paldata[*(sd++)]; if (col != backgroundpen) *bm = col; bm++; } } } else /* normal */ { for (y = sy;y < ey;y++) { bm = &bitmap[BITMAP_WIDTH * y + sx]; sd = &spritedata[BIGSPRITE_WIDTH * (y-oy) + sx-ox]; for (x = sx;x < ex;x++) { col = paldata[*(sd++)]; if (col != backgroundpen) *bm = col; bm++; } } } } } /*************************************************************************** Redraw the screen. ***************************************************************************/ void vh_screenrefresh(void) { int i,offs,y; /* for every character in the Video RAM, check if it has been modified */ /* since last time and update it accordingly. */ for (offs = 0;offs < VIDEO_RAM_SIZE;offs++) { int sx,sy; if (dirtybuffer[offs]) { dirtybuffer[offs] = 0; sy = offs % 32; sx = 0x1f-offs / 32; drawchar(tmpbitmap,RAM[VIDEO_RAM_START + offs] + 32 * (RAM[COLOR_RAM_START + offs] & 0x10) +8 * (RAM[COLOR_RAM_START + offs] &0x20), RAM[COLOR_RAM_START + offs] & 0x0f, RAM[COLOR_RAM_START + offs] & 0x40,0, sx,sy); } } /* update the Big Sprite */ for (offs = 0;offs < BIGSPRITE_SIZE;offs++) { int sx,sy; if (bsdirtybuffer[offs]) { bsdirtybuffer[offs] = 0; sy = offs % 16; sx = 0x0f-offs / 16; updatebigsprite(RAM[BIGSPRITE_START + offs],1,sx,sy); } } /* copy the character mapped graphics */ i = 0; while (i < H_CHARS) { int cons,y1; unsigned char *src,*dest; /* count consecutive columns scrolled by the same amount */ cons = 1; while (i + cons < H_CHARS && RAM[COLUMN_SCROLL_BASE + i + cons] == RAM[COLUMN_SCROLL_BASE + i]) cons++; /* copy all visible lines */ y = 8 * FIRST_VISIBLE_ROW; y1 = (y + RAM[COLUMN_SCROLL_BASE + i]) % BITMAP_HEIGHT; dest = &scrbitmap[y * BITMAP_WIDTH + 8 * i]; src = &tmpbitmap[y1 * BITMAP_WIDTH + 8 * i]; while (y < 8 * LAST_VISIBLE_ROW) { memcpy(dest,src,8 * cons); y++; dest += BITMAP_WIDTH; y1++; src += BITMAP_WIDTH; if (y1 == BITMAP_HEIGHT) { y1 = 0; src -= BITMAP_WIDTH * BITMAP_HEIGHT; } } i += cons; } /* draw sprites (must be done after the "big sprite" to obtain the correct priority) */ /* for CK this should be the other way round */ for (i = SPRITES_BASE + SPRITES_SIZE - 4 ;i >= SPRITES_BASE ;i -= 4) { if (RAM[i+3]) drawsprite(scrbitmap,(RAM[i] & 0x3f) + 8 * (RAM[i+1] &0x10) + 2*(RAM[i+1] &0x20),RAM[i+1] & 0x0f, RAM[i] & 0x80,(RAM[i+1] & 0x80) | (RAM[i] &0x40), RAM[i+2]+1,RAM[i+3]); } /* draw the "big sprite" */ drawbigsprite(scrbitmap, 0x10 + (RAM[BIGSPRITE_CTRL_BASE + 1] & 0x0f), RAM[BIGSPRITE_CTRL_BASE + 1] & 0x20,!(RAM[BIGSPRITE_CTRL_BASE + 1] & 0x10), RAM[BIGSPRITE_CTRL_BASE + 2], RAM[BIGSPRITE_CTRL_BASE + 3]-8); osd_update_display(); } /*************************************************************************** Display text on the screen. If erase is 0, it superimposes the text on the last frame displayed. ***************************************************************************/ void displaytext(const struct DisplayText *dt,int erase) { if (erase) memset(scrbitmap,backgroundpen,BITMAP_WIDTH * BITMAP_HEIGHT); while (dt->text) { int x; const unsigned char *c; x = dt->x; c = dt->text; while (*c) { if (*c != ' ') { if (*c >= '0' && *c <= '9') drawchar(scrbitmap,*c - '0' + NUMBERS_START,dt->color,0,0,x,dt->y); else drawchar(scrbitmap,*c - 'A' + LETTERS_START,dt->color,0,0,x,dt->y); } x++; c++; } dt++; } osd_update_display(); }