singe/video/tms9128nl.cpp
2019-11-11 14:53:02 -06:00

959 lines
26 KiB
C++

/*
* 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 <SDL.h>
#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 <stdio.h>
#include <string.h>
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;
}