/* * tms9128nl.cpp * * Copyright (C) 2001 Matt Ownby * * This file is part of DAPHNE, a laserdisc arcade game emulator * * DAPHNE is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * DAPHNE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // undefine this to get rid of debug messages, or 1 to view them //#define TMS_DEBUG 1 #include #include "SDL_DrawText.h" #include "tms9128nl.h" #include "palette.h" #include "video.h" #include "../game/game.h" #include "../io/conout.h" #include "../ldp-out/ldp.h" // to check to see if blitting is allowed #include #include static unsigned char g_vidbuf[TMS9128NL_OVERLAY_W * TMS9128NL_OVERLAY_H]; // video buffer needed because we clobber the SDL_Surface buffer when we do real-time scaling static unsigned char vidmem[32767] = { 0 }; // video memory static unsigned char lowbyte = 0; static unsigned char highbyte = 0; static unsigned int rvidindex; static unsigned int wvidindex; static int toggleflag = 0; //keep track of low / high byte static int g_vidmode = 0; //keep track of video mode static int viddisp = 1; //enable video display 0=off 1=on static int vidreg[8] = { 0 }; //registers 0-7 static int rowdiv = 40; //text mode unsigned char g_tms_pnt_addr = 0; // pattern name table address unsigned char g_tms_ct_addr = 0; // color table address unsigned char g_tms_pgt_addr = 0; // pattern generation table address unsigned char g_tms_sat_addr = 0; // sprite attribute table address unsigned char g_tms_sgt_addr = 0; // sprite generation table address unsigned char g_tms_foreground_color = 0xF; // white (3 bit) unsigned char g_tms_background_color = 0; // black (3 bit) bool g_tms_interrupt_enabled = false; // whether NMI is on or off int g_transparency_enabled = 0; int g_transparency_latch = 0; // BARBADEL: Added int introHack = 0; int prevg_vidmode = 0; void tms9128nl_clear_overlay(); //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // resets all variables to their initial state void tms9128nl_reset() { memset(vidmem, 0, sizeof(vidmem)); lowbyte = 0; highbyte = 0; rvidindex = 0; wvidindex = 0; toggleflag = 0; g_vidmode = 0; viddisp = 1; memset(vidreg, 0, sizeof(vidreg)); rowdiv = 40; g_tms_pnt_addr = 0; g_tms_ct_addr = 0; g_tms_pgt_addr = 0; g_tms_sat_addr = 0; g_tms_sgt_addr = 0; g_tms_foreground_color = 0xF; g_tms_background_color = 0; g_tms_interrupt_enabled = false; g_transparency_enabled = 0; g_transparency_latch = 0; introHack = 0; prevg_vidmode = 0; } bool tms9128nl_int_enabled() { return(g_tms_interrupt_enabled); } void tms9128nl_writechar(unsigned char value) //write a character on the screen, using 40*24 mode emulation // this is the same as writing to register #0 on a TMS chip { unsigned int row = 0,col = 0; int base = 0; if (viddisp==0) return; //video display is off //if (g_vidmode!=1) return; //only text mode 1 for now // MODE 1 (bitmapped text) if (g_vidmode == 1) { // if the coordinates requested are within the viewable area if ((wvidindex >= 0) && (wvidindex <= 0x3c0)) { base=0; rowdiv=40; //40*24 row = (wvidindex-base-1) / rowdiv; col = (wvidindex-base-1) % rowdiv; if ((col==31) && (rowdiv==32)) return; //problems with col31? or bug in fancyclearscreen routine? tms9128nl_drawchar(value, col, row); } // else, they're outside the viewable area, so draw nothing } // end mode 1 // MODE 2 (Cliff Hanger graphical logo) else if (g_vidmode == 2) { if ((wvidindex>=0x3c00) && (wvidindex<=0x3f00)) //pattern name table - graphics mode { base=0x3c01; rowdiv=32; //32*24 row = (wvidindex-base-1) / rowdiv; col = (wvidindex-base-1) % rowdiv; if ((col==31) && (rowdiv==32)) return; //problems with col31? or bug in fancyclearscreen routine? tms9128nl_drawchar(value, col, row); } // BARBADEL: Added else { g_tms_foreground_color = (unsigned char) ((value & 0xF0) >> 4); g_tms_background_color = (unsigned char) (value & 0x0F); tms9128nl_palette_update(); // MATT } } } //end writechar //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// unsigned char tms9128nl_getvidmem(void) { return vidmem[rvidindex++]; } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// void tms9128nl_write_port1(unsigned char value) //port 54 is the index into video memory. First a low byte is sent, then a highbyte // this is the same as writing to port #1 for a TMS chip (the same rules apply) { static int tempindex = 0; #ifdef TMS_DEBUG char s[81] = { 0 }; // just a temp string #endif if (toggleflag==0) { wvidindex=0; rvidindex=0; lowbyte=value; // printf("lowbyte: %d\n",lowbyte); } // if toggleflag is not zero else { highbyte=value; // if bit 7 is set, it means a TMS register is being written to if (highbyte & 0x80) { // AND'ing by 0x7F strips off the high bit, it has nothing to do with TMS_TRANSPARENT_COLOR int which_reg = highbyte & 0x7F; // the register that is being written to vidreg[which_reg] = lowbyte; // set the register in memory in case we need to refer to it later // handle register access switch (which_reg) { // register 0 (select screen mode 2 or Black & White) case 0: // if only bit 1 is set, it means to go to graphics mode 2 if (lowbyte & 2) { g_vidmode = 2; if(prevg_vidmode != g_vidmode) // Barbadel: if we're switching video modes, clear the overlay { #ifdef TMS_DEBUG printline("TMS: mode 2"); #endif tms9128nl_clear_overlay(); prevg_vidmode = g_vidmode; } } // if the B&W bit is set, flag an error if (lowbyte & 1) { #ifdef TMS_DEBUG printline("TMS: B&W bit set, unsupported"); #endif } else { #ifdef TMS_DEBUG printline("TMS : B&W bit cleared"); #endif } break; // register 1 (4/16K, blank screen, GINT, mode1, mode3, sprite mode, sprite scale) case 1: // if bit 0 is set, MAG if (lowbyte & 1) { #ifdef TMS_DEBUG printline("TMS: double sprite size not supported"); #endif } // if bit 1 is set, 16x16 sprites is what we use, else 8x8 if (lowbyte & 2) { #ifdef TMS_DEBUG printline("TMS: 16x16 sprites requested"); #endif } else { #ifdef TMS_DEBUG //printline("TMS: 8x8 sprites requested"); // don't print anything since this is what we expect #endif } // bit 3 set == mode 3 if (lowbyte & 0x08) { g_vidmode=3; if(prevg_vidmode != g_vidmode) // Barbadel: if we're switching video modes, clear the overlay { #ifdef TMS_DEBUG printline("TMS: mode 3"); #endif tms9128nl_clear_overlay(); prevg_vidmode = g_vidmode; } } // bit 4 set == mode 1 else if (lowbyte & 0x10) //bit 4 set - text mode 1 { g_vidmode=1; if(prevg_vidmode != g_vidmode) // Barbadel: if we're switching video modes, clear the overlay { #ifdef TMS_DEBUG printline("TMS: mode 1"); #endif tms9128nl_clear_overlay(); prevg_vidmode = g_vidmode; } } // if all vid bits have been cleared, then set g_vidmode to 0 // we need to check this somewhere, here is as good a place as any I suppose else if (!(vidreg[0]&0x02)) //no vid bits set { g_vidmode=0; if(prevg_vidmode != g_vidmode) // Barbadel: if we're switching video modes, clear the overlay { #ifdef TMS_DEBUG printline("TMS: g_vidmode 0 special"); #endif tms9128nl_clear_overlay(); prevg_vidmode = g_vidmode; } } // bit 5 set == generate interrupts if (lowbyte & 0x20) { #ifdef TMS_DEBUG // if they weren't previously enabled if (!g_tms_interrupt_enabled) { printline("TMS: Generate interrupts enabled"); } #endif // don't print anything since this is what we expect g_tms_interrupt_enabled = 1; } else { #ifdef TMS_DEBUG // only notify us if they weren't already disabled if (g_tms_interrupt_enabled) { printline("TMS: Generate interrupts disabled"); } #endif g_tms_interrupt_enabled = 0; } // don't blank screen if (lowbyte & 0x40) { viddisp=1; // enable video display } // blank screen, leaving only backdrop else { viddisp=0; // disable video display (blank screen) tms9128nl_clear_overlay(); #ifdef TMS_DEBUG printline("TMS: VIDEO DISPLAY TO BE BLANKED!"); #endif } // bit 7 set == select 16K ram, else 4K ram if (lowbyte & 0x80) { #ifdef TMS_DEBUG // printline("TMS: 16K ram selected"); #endif // don't print anything since this is what we expect } else { #ifdef TMS_DEBUG printline("TMS: 4k ram selected, unsupported"); #endif } break; // register #2 sets address for pattern name table case 2: #ifdef TMS_DEBUG { unsigned char temp = (unsigned char) (lowbyte & 0xF); // only lowest 4 bits if (temp != g_tms_pnt_addr) { sprintf(s, "TMS: Pattern Name Table Address changed to %x", g_tms_pnt_addr); printline(s); } } #endif g_tms_pnt_addr = (unsigned char) (lowbyte & 0xF); // only lowest 4 bits break; // register #3 sets address for color table case 3: #ifdef TMS_DEBUG if (lowbyte != g_tms_ct_addr) { sprintf(s, "TMS: Color Table Address changed to %x", g_tms_ct_addr); printline(s); } #endif g_tms_ct_addr = lowbyte; break; // register #4 sets address for pattern generation table case 4: #ifdef TMS_DEBUG { unsigned char temp = (unsigned char) (lowbyte & 7); if (temp != g_tms_pgt_addr) { sprintf(s, "TMS: Pattern Generation Table changed to %x", g_tms_pgt_addr); printline(s); } } #endif g_tms_pgt_addr = (unsigned char) (lowbyte & 7); // only lowest 3 bits break; // register #5 sets address for sprite attribute table case 5: #ifdef TMS_DEBUG { unsigned char temp = (unsigned char) (lowbyte & 0x7F); if (temp != g_tms_sat_addr) { sprintf(s, "TMS: Sprite Attribute Table address changed to %x", g_tms_sat_addr); printline(s); } } #endif g_tms_sat_addr = (unsigned char) (lowbyte & 0x7F); // discard high bit break; // register #6 sets address for sprite generation table case 6: #ifdef TMS_DEBUG { unsigned char temp = (unsigned char) (lowbyte & 0x7); if (temp != g_tms_sgt_addr) { sprintf(s, "TMS: Sprite Generator Table address changed to %x", g_tms_sgt_addr); printline(s); } } #endif g_tms_sgt_addr = (unsigned char) (lowbyte & 0x7); // only lowest 3 bits break; // register #7 sets foreground and background colors case 7: #ifdef TMS_DEBUG { unsigned char t1 = (unsigned char) ((lowbyte & 0xF0) >> 4); unsigned char t2 = (unsigned char) (lowbyte & 0x0F); if (t1 != g_tms_foreground_color) { sprintf(s, "TMS : Foreground color changed to %x", g_tms_foreground_color); printline(s); } if (t2 != g_tms_background_color) { sprintf(s, "TMS : Background color changed to %x", g_tms_background_color); printline(s); } } #endif g_tms_foreground_color = (unsigned char) ((lowbyte & 0xF0) >> 4); g_tms_background_color = (unsigned char) (lowbyte & 0x0F); tms9128nl_palette_update(); break; default: #ifdef TMS_DEBUG sprintf(s,"TMS: Register %d was written to (unsupported)", which_reg); printline(s); #endif break; } //end switch } //end if bit 7 is set (and we're writing to a register) // bit 7 is clear so we're not writing to a register // instead, we are modifying the video memory address else { #ifdef TMS_DEBUG // NOTE : we don't currently keep track of whether we're in memory read/write mode // this seems to work fine for now but it may be an issue later // if bit 6 is set it means we're in memory write mode if (highbyte & 0x40) { // printline("Memory write mode requested"); } // otherwise we're in memory read mode else { printline("Memory read mode requested"); } #endif highbyte = (unsigned char) (highbyte & 0x3f); //strip top 2 bits tempindex = (highbyte << 8) | lowbyte; wvidindex = tempindex; rvidindex = tempindex; } //end if high bit not set } //end if toggleflag is not zero toggleflag=toggleflag^1; //flip bit 1 } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // writes a byte to port 0 of the TMS chip void tms9128nl_write_port0(unsigned char Value) //return a 1 if the screen needs updating { vidmem[wvidindex] = Value; wvidindex++; // the index always advances when we write tms9128nl_writechar(Value); // update the screen } void tms9128nl_convert_color(unsigned char color_src, SDL_Color *color) { char s[81] = { 0 }; switch(color_src) { case 0: // transparent color->r = 0; color->g = 0; color->b = 0; break; case 1: // black color->r = 0; color->g = 0; color->b = 0; break; case 2: // medium green; color->r = 36; color->g = 219; color->b = 36; break; case 3: // light green color->r = 109; color->g = 255; color->b = 109; break; case 4: // dark blue color->r = 36; color->g = 36; color->b = 255; break; case 5: // light blue color->r = 73; color->g = 109; color->b = 255; break; case 6: // dark red if (!g_transparency_latch) { color->r = 182; color->g = 36; color->b = 36; } // if we're in transparency mode, the move prompts else { // these colors aren't correct for the TMS chip but they make Cliffy look a bit more // authentic (the move prompts are more purplish than red) color->r = 125; color->g = 0; color->b = 128; //BARBADEL: 128 makes it more purple than 50 } break; case 7: // light cyan color->r = 73; color->g = 219; color->b = 255; break; case 8: // medium red color->r = 255; color->g = 36; color->b = 36; break; case 9: // light red color->r = 255; color->g = 109; color->b = 109; break; case 10: // dark yellow color->r = 219; color->g = 219; color->b = 36; break; case 11: // light yellow color->r = 219; color->g = 219; color->b = 146; break; case 12: // dark green color->r = 36; color->g = 146; color->b = 36; break; case 13: // magenta color->r = 219; color->g = 73; color->b = 182; break; case 14: // grey color->r = 182; color->g = 182; color->b = 182; break; case 15: // white color->r = 255; color->g = 255; color->b = 255; break; default: sprintf(s, "UNSUPPORTED COLOR passed into convert color : %d", color_src); printline(s); break; } } // draws a character to the Cliff video display // 40 columns by 24 rows // uses Cliffy's video memory to retrieve 8x8 character bitmap void tms9128nl_drawchar(unsigned char ch, int col, int row) { const int CHAR_WIDTH = 8; const int CHAR_HEIGHT = 8; int bmp_index = (ch * 8) + (g_tms_pgt_addr << 11); // index in vid mem where bmp data is located int i = 0, j = 0; // temp indices int x = col * CHAR_WIDTH; int y = row * CHAR_HEIGHT; unsigned char line = 0; unsigned char background_color = TMS_BG_COLOR; // if character is 0 and we're in transparency mode, make bitmap transparent if (g_transparency_latch) { static bool latched = false; if ((ch == 0) || (ch == 0xFF)) { // we need two 0's in a row to make a transparent block if (!latched) { latched = true; return; } else { background_color = TMS_TRANSPARENT_COLOR; } } else { latched = false; } } // colors are special in g_vidmode 2, we don't handle them properly yet if (g_vidmode == 2) { // MODIFIED SECTION BY BARBADEL if (ch != 255) { x += 4; // MATT : even scaled, it is still 1 character off-center. Perhaps it has a 1 character border? // major hack : If the row is less than 12 we know it's the Cliff Hanger graphic, hence we hard-code the // correct values in here. Someone should fix this sometime :) if (row <= 11) { g_tms_foreground_color = 0x5; // Light Blue g_tms_background_color = 0x1; // Black tms9128nl_palette_update(); bmp_index = (ch * 8) + 8*256 * (row > 7); } else { // MATT : these color hacks don't seem to be necessary anymore // g_tms_foreground_color = 0x1; // Black // g_tms_background_color = 0x5; // Light Blue bmp_index = (ch * 8) + 0x3800; } } else { x += 4; g_tms_foreground_color = 0x0; // Black g_tms_background_color = 0x5; // Light Blue tms9128nl_palette_update(); if(col != 0) { // g_transparent_color = 0xff; bmp_index = (ch * 8) + 8*256 * (row > 7); } else { // g_transparent_color = 0x7f; // background_color = 0; bmp_index = (ch * 8) + 8*256 * (row > 7); } } } // draw each line of character into new surface for (i = 0; i < CHAR_HEIGHT; i++) { line = vidmem[bmp_index + i]; // get a line // handle each pixel across for (j = CHAR_WIDTH - 1; j >= 0; j--) { // if rightmost bit is 1, it means draw the pixel if (line & 1) { *((Uint8 *) g_vidbuf + ((y+i+TMS_VERTICAL_OFFSET) * TMS9128NL_OVERLAY_W) + (x+j)) = TMS_FG_COLOR; } // else draw the background else { *((Uint8 *) g_vidbuf + ((y+i+TMS_VERTICAL_OFFSET) * TMS9128NL_OVERLAY_W) + (x+j)) = background_color; } line = (unsigned char) (line >> 1); } } // end for loop // In transparency mode, if we draw a solid character, we need to make the character after it non-transparent // This seems to be how Cliff Hanger behaves. I haven't found it documented anywhere though. if ((g_transparency_latch) && (ch != 0) && (ch != 0xFF)) { int row, col; Uint8 *ptr = ((Uint8 *) g_vidbuf) + ((y + TMS_VERTICAL_OFFSET) * TMS9128NL_OVERLAY_W) + x + CHAR_WIDTH; for (row = 0; row < CHAR_HEIGHT; row++) { for (col = 0; col < CHAR_WIDTH; col++) { // make it non-transparent if it is if (*ptr == TMS_TRANSPARENT_COLOR) { *ptr = TMS_BG_COLOR; } ptr++; } ptr += (TMS9128NL_OVERLAY_W - CHAR_WIDTH); // move to the next line } } g_game->set_video_overlay_needs_update(true); } void tms9128nl_outcommand(char *s,int col,int row) { // gp2x doesn't have enough resolution to display this schlop anyway... #ifndef GP2X SDL_Rect dest; dest.x = (short) ((col*6) + 200); dest.y = (short) ((row*13) + 100); dest.w = (unsigned short) (6 * strlen(s)); // width of rectangle area to draw (width of font * length of string) dest.h = 13; // height of area (height of font) // VLDP freaks out if it's not the only thing drawing to the screen if (!g_ldp->is_vldp()) { vid_blank(); SDLDrawText(s, get_screen_blitter(), FONT_SMALL, dest.x, dest.y); // TODO : get this working again under the new video scheme } #endif } // called everytime the color palette changes // We don't make this part of the 'palette_update()' function because that function also // does some stuff with the transparent color, which is only a one-time initialization requirement void tms9128nl_palette_update() { SDL_Color fore, back; // the foreground and background colors tms9128nl_convert_color(g_tms_foreground_color, &fore); tms9128nl_convert_color(g_tms_background_color, &back); palette_set_color(0, back); palette_set_color(255, fore); // if we should do extra calculations for stretching if (g_vidmode == 2) { SDL_Color fore75back25, fore5back5, fore25back75; // mixtures of the foreground and background colors (for stretching) MIX_COLORS_75_25(fore75back25, fore, back); // 3/4, 1/4 MIX_COLORS_50(fore5back5, fore, back); // average MIX_COLORS_75_25(fore25back75, back, fore); // 1/4, 3/4 palette_set_color(1, fore25back75); palette_set_color(2, fore5back5); palette_set_color(3, fore75back25); } palette_finalize(); g_game->set_video_overlay_needs_update(true); } // calculates the initial color palette (basically our main routine is in palette_update) void tms9128nl_palette_calculate() { // transparent color set to a light grey color so we can debug more effectively in 'noldp' mode SDL_Color color; color.r = color.g = color.b = TMS_TRANSPARENT_COLOR; palette_set_transparency(0, false); // change default to non-transparent palette_set_transparency(TMS_TRANSPARENT_COLOR, true); // make transparent color transparent :) palette_set_color(TMS_TRANSPARENT_COLOR, color); tms9128nl_palette_update(); tms9128nl_reset(); } void tms9128nl_video_repaint() { // if the transparency state has changed if (g_transparency_enabled != g_transparency_latch) { int i = 0; Uint8 *ptr = (Uint8 *) g_vidbuf + (TMS9128NL_OVERLAY_W * TMS_VERTICAL_OFFSET); // I don't believe we want to do the stretched overlay here // if transparency was off and is now on and we're in vidmode 1 (HACK) if ((g_transparency_enabled) && (g_vidmode == 1)) { for (i = 0; i < TMS9128NL_OVERLAY_W * (TMS9128NL_OVERLAY_H - (TMS_VERTICAL_OFFSET << 1)); i++) { // if color is a background color, make it 0x7F (transparency color) if (*ptr == 0) *ptr = TMS_TRANSPARENT_COLOR; ptr++; } } else { for (i = 0; i < TMS9128NL_OVERLAY_W * (TMS9128NL_OVERLAY_H - (TMS_VERTICAL_OFFSET << 1)); i++) { // if color is transparent (0x7F), make it a background color if (*ptr == TMS_TRANSPARENT_COLOR) *ptr = 0; ptr++; } } g_transparency_latch = g_transparency_enabled; } g_transparency_enabled = 0; // apparently this has to be set to true every pulse of the NMI in order // to maintain the transparency. The Cliff ROM does this. // if we're in video mode 2, we have to display our stretched overlay instead of our regular one if (g_vidmode == 2) { tms9128nl_video_repaint_stretched(); } // if we're not in mode 2, display our non-stretched overlay else { memcpy(g_game->get_active_video_overlay()->pixels, g_vidbuf, TMS9128NL_OVERLAY_W * TMS9128NL_OVERLAY_H); } } // creates the stretched overlay, using the contents of the normal overlay as its source // the stretched overlay is simply a 256x192 window scaled to 320x192 using a hard-coded algorithm (hopefully it's fast) void tms9128nl_video_repaint_stretched() { int x256 = 0; int y = 0; Uint8 *ptr256 = (Uint8 *) g_vidbuf; // source ... Uint8 *ptr320 = (Uint8 *) g_game->get_active_video_overlay()->pixels; // destination ... // these values correspond to colors in the color palette unsigned char blend[4][2] = { { 0, 0 }, { 3, 1 }, { 2, 2 }, { 1, 3 }, }; // do every row for (y = 0; y < TMS9128NL_OVERLAY_H; y++) { // do each pixel, but divide it up into smallest integer sections so we can use a hard-coded algorithm // there is a 4:5 correspondance between the 256 and 320 surfaces for (x256 = 0; x256 < 256; x256 += 4) { // PIXEL +0 *(ptr320) = *(ptr256); // PIXEL +1 to PIXEL +3 (blending possibly required) for (int i = 1; i < 4; i++) { // if prev pixel is not the same as cur pixel, blending is required if (*(ptr256+i-1) != *(ptr256+i)) { // if prev pixel is background color, cur pixel must be foreground if (*(ptr256+i-1) == 0) { *(ptr320+i) = blend[i][0]; } // else prev pixel is foreground, and therefore cur pixel must be background else { *(ptr320+i) = blend[i][1]; } } else *(ptr320+i) = *(ptr256+i); // else no blending required } // PIXEL +4 *(ptr320+4) = *(ptr256+3); ptr320 += 5; ptr256 += 4; } ptr256 += (320-256); // move to the next line, ptr320 is already at the next line } } void tms9128nl_clear_overlay() { Uint8 clear_color = TMS_BG_COLOR; int i = 0; Uint8 *ptr = g_vidbuf; // printf("Overlay is being cleared, transparency is %d, latch is %d\n", g_transparency_enabled, g_transparency_latch); // if transparency mode is on ... if (g_transparency_latch) { clear_color = TMS_TRANSPARENT_COLOR; } // top area always gets border color and is never transparent for (i = 0; i < TMS9128NL_OVERLAY_W * TMS_VERTICAL_OFFSET; i++) { *ptr = 0; ptr++; } // erase viewable area with either the background color or the transparent color for (i = 0; i < TMS9128NL_OVERLAY_W * (TMS9128NL_OVERLAY_H - (TMS_VERTICAL_OFFSET << 1)); i++) { *ptr = clear_color; ptr++; } // bottom area always gets border color and is never transparent for (i = 0; i < TMS9128NL_OVERLAY_W * TMS_VERTICAL_OFFSET; i++) { *ptr = 0; } g_game->set_video_overlay_needs_update(true); } // kind of a hack for Cliff Hanger, not sure if this is part of the TMS9128NL chip or not // sets the "transparency value" for one NMI tick (it gets cleared at each NMI tick) void tms9128nl_set_transparency() { g_transparency_enabled = 1; }