/*************************************************************************** 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 "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 tmpbitmap[BITMAP_WIDTH * BITMAP_HEIGHT]; /* temporary bitmap used to hold the */ /* character mapped part of the screen */ unsigned char *scrbitmap; unsigned char dirtybuffer[VIDEO_RAM_SIZE]; /* keep track of modified portions of the screen */ /* to speed up video refresh */ unsigned char radardirtybuffer[VIDEO_RAM_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(0x1000)) == 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; } gfxdecode(chars,tmpstorage,gamevidinfo[i].charlayout); gfxdecode(sprites,tmpstorage,gamevidinfo[i].spritelayout); 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. In the case of Pengo, all we do is keep track of writes to Video or Color RAM, marking the location as 'dirty' so we can update the screen bitmap before redrawing it. ***************************************************************************/ 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 >= COLOR_RAM_START && A < COLOR_RAM_START + VIDEO_RAM_SIZE) { if (RAM[A] != V) { dirtybuffer[A - COLOR_RAM_START] = 1; RAM[A] = V; } return 1; } else if (A >= RADAR_VIDEO_RAM_START && A < RADAR_VIDEO_RAM_START + VIDEO_RAM_SIZE) { if (RAM[A] != V) { radardirtybuffer[A - RADAR_VIDEO_RAM_START] = 1; RAM[A] = V; } return 1; } else if (A >= RADAR_COLOR_RAM_START && A < RADAR_COLOR_RAM_START + VIDEO_RAM_SIZE) { if (RAM[A] != V) { radardirtybuffer[A - RADAR_COLOR_RAM_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 < SPRITE_FIRST_VISIBLE_COLUMN) sx = SPRITE_FIRST_VISIBLE_COLUMN; if (ex > SPRITE_LAST_VISIBLE_COLUMN) ex = SPRITE_LAST_VISIBLE_COLUMN; ey = sy + SPRITE_HEIGHT; if (sy < SPRITE_FIRST_VISIBLE_ROW) sy = SPRITE_FIRST_VISIBLE_ROW; if (ey > SPRITE_LAST_VISIBLE_ROW) ey = SPRITE_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++; } } } } } /*************************************************************************** Redraw the screen. ***************************************************************************/ void vh_screenrefresh(void) { int i,offs; int sx,sy,y,y1; /* 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++) { if (dirtybuffer[offs]) { dirtybuffer[offs] = 0; sx = offs % 32; sy = offs / 32; drawchar(tmpbitmap,RAM[VIDEO_RAM_START + offs], RAM[COLOR_RAM_START + offs] & 0x1f, !(RAM[COLOR_RAM_START + offs] & 0x40), RAM[COLOR_RAM_START + offs] & 0x80, sx,sy); } } /* update radar */ for (sy = 0;sy < 32;sy++) { for (sx = 0;sx < 8;sx++) { if (radardirtybuffer[sy * 32 + sx]) { radardirtybuffer[sy * 32 + sx] = 0; drawchar(tmpbitmap,RAM[RADAR_VIDEO_RAM_START + sy * 32 + sx], RAM[RADAR_COLOR_RAM_START + sy * 32 + sx] & 0x1f, !(RAM[RADAR_COLOR_RAM_START + sy * 32 + sx] & 0x40), RAM[RADAR_COLOR_RAM_START + sy * 32 + sx] & 0x80, (sx ^ 4) + 32,sy); } } } sx = RAM[SCROLL_X] - 4; if (sx < 0) sx += 256; sy = RAM[SCROLL_Y]; for (y = 8;y < BITMAP_HEIGHT-8;y++) { y1 = sy+y; if (y1 >= BITMAP_HEIGHT) y1 -= BITMAP_HEIGHT; if (sx > 16) { memcpy(scrbitmap + y * BITMAP_WIDTH,tmpbitmap + y1 * BITMAP_WIDTH + sx,256-sx); memcpy(scrbitmap + y * BITMAP_WIDTH + 256 - sx,tmpbitmap + y1 * BITMAP_WIDTH,sx-16); } else memcpy(scrbitmap + y * BITMAP_WIDTH,tmpbitmap + y1 * BITMAP_WIDTH + sx,240); memcpy(scrbitmap + y * BITMAP_WIDTH + 256,tmpbitmap + y * BITMAP_WIDTH + 256,64); } /* draw sprites */ for (i = 0;i < 6;i++) { // if (RAM[SPRITES_BASE_2 + 2*i+1] & 0x80) /* ?????? */ drawsprite(scrbitmap,RAM[SPRITES_BASE_1 + 2*i] >> 2, RAM[SPRITES_BASE_2 + 2*i+1], RAM[SPRITES_BASE_1 + 2*i] & 1,RAM[SPRITES_BASE_1 + 2*i] & 2, RAM[SPRITES_BASE_1 + 2*i+1],240 - RAM[SPRITES_BASE_2 + 2*i]); } 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(); }