AppleWin/AppleWin/source/Video.cpp

1715 lines
55 KiB
C++

/*
AppleWin : An Apple //e emulator for Windows
Copyright (C) 1994-1996, Michael O'Brien
Copyright (C) 1999-2001, Oliver Schmidt
Copyright (C) 2002-2005, Tom Charlesworth
Copyright (C) 2006, Tom Charlesworth, Michael Pohoreski
AppleWin 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.
AppleWin 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 AppleWin; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Description: Emulation of video modes
*
* Author: Various
*/
#include "StdAfx.h"
#pragma hdrstop
#include "..\resource\resource.h"
/* reference: technote tn-iigs-063 "Master Color Values"
Color Color Register LR HR DHR Master Color R,G,B
Name Value # # # Value
----------------------------------------------------
Black 0 0 0,4 0 $0000 (0,0,0) -> (00,00,00) Windows
(Magenta) Deep Red 1 1 1 $0D03 (D,0,3) -> (D0,00,30) Custom
Dark Blue 2 2 8 $0009 (0,0,9) -> (00,00,80) Windows
(Violet) Purple 3 3 2 9 $0D2D (D,2,D) -> (FF,00,FF) Windows
Dark Green 4 4 4 $0072 (0,7,2) -> (00,80,00) Windows
(Gray 1) Dark Gray 5 5 5 $0555 (5,5,5) -> (80,80,80) Windows
(Blue) Medium Blue 6 6 6 C $022F (2,2,F) -> (00,00,FF) Windows
(Cyan) Light Blue 7 7 D $06AF (6,A,F) -> (60,A0,FF) Custom
Brown 8 8 2 $0850 (8,5,0) -> (80,50,00) Custom
Orange 9 9 5 3 $0F60 (F,6,0) -> (FF,80,00) Custom (modified to match better with the other Hi-Res Colors)
(Gray 2) Light Gray A A A $0AAA (A,A,A) -> (C0,C0,C0) Windows
Pink B B B $0F98 (F,9,8) -> (FF,90,80) Custom
(Green) Light Green C C 1 6 $01D0 (1,D,0) -> (00,FF,00) Windows
Yellow D D 7 $0FF0 (F,F,0) -> (FF,FF,00) Windows
(Aqua) Aquamarine E E E $04F9 (4,F,9) -> (40,FF,90) Custom
White F F 3,7 F $0FFF (F,F,F) -> (FF,FF,FF) Windows
LR: Lo-Res HR: Hi-Res DHR: Double Hi-Res */
// STANDARD WINDOWS COLORS
#define BLACK 0x00
#define DARK_RED 0x01
#define DARK_GREEN 0x02
#define DARK_YELLOW 0x03
#define DARK_BLUE 0x04
#define DARK_MAGENTA 0x05
#define DARK_CYAN 0x06
#define LIGHT_GRAY 0x07
#define MONEY_GREEN 0x08
#define SKY_BLUE 0x09
#define CREAM 0xF6
#define MEDIUM_GRAY 0xF7
#define DARK_GRAY 0xF8
#define RED 0xF9
#define GREEN 0xFA
#define YELLOW 0xFB
#define BLUE 0xFC
#define MAGENTA 0xFD
#define CYAN 0xFE
#define WHITE 0xFF
// OUR CUSTOM COLORS
#define DEEP_RED 0x0A
#define LIGHT_BLUE 0x0B
#define BROWN 0x0C
#define ORANGE 0X0D
#define PINK 0x0E
#define AQUA 0x0F
// CUSTOM HGR COLORS (don't change values) - For tv emulation mode
#define HGR_BLACK 0x10
#define HGR_WHITE 0x11
#define HGR_BLUE 0x12
#define HGR_RED 0x13
#define HGR_GREEN 0x14
#define HGR_MAGENTA 0x15
#define HGR_GREY1 0x16
#define HGR_GREY2 0x17
#define HGR_YELLOW 0x18
#define HGR_AQUA 0x19
#define HGR_PURPLE 0x1A
#define HGR_PINK 0x1B
// USER CUSTOMIZABLE COLOR
#define MONOCHROME 0x20
#define SRCOFFS_40COL 0
#define SRCOFFS_80COL (SRCOFFS_40COL + 256)
#define SRCOFFS_LORES (SRCOFFS_80COL + 128)
#define SRCOFFS_HIRES (SRCOFFS_LORES + 16)
#define SRCOFFS_DHIRES (SRCOFFS_HIRES + 512)
#define SRCOFFS_TOTAL (SRCOFFS_DHIRES + 2560)
#define VF_80COL 0x00000001
#define VF_DHIRES 0x00000002
#define VF_HIRES 0x00000004
#define VF_MASK2 0x00000008
#define VF_MIXED 0x00000010
#define VF_PAGE2 0x00000020
#define VF_TEXT 0x00000040
#define SW_80COL (vidmode & VF_80COL)
#define SW_DHIRES (vidmode & VF_DHIRES)
#define SW_HIRES (vidmode & VF_HIRES)
#define SW_MASK2 (vidmode & VF_MASK2)
#define SW_MIXED (vidmode & VF_MIXED)
#define SW_PAGE2 (vidmode & VF_PAGE2)
#define SW_TEXT (vidmode & VF_TEXT)
#define SETSOURCEPIXEL(x,y,c) sourceoffsettable[(y)][(x)] = (c)
#define SETFRAMECOLOR(i,r,g,b) framebufferinfo->bmiColors[i].rgbRed = r; \
framebufferinfo->bmiColors[i].rgbGreen = g; \
framebufferinfo->bmiColors[i].rgbBlue = b;
#define HGR_MATRIX_YOFFSET 2 // For tv emulation mode
typedef BOOL (*updatetype)(int,int,int,int,int);
static BYTE celldirty[40][32];
static COLORREF customcolors[MONOCHROME]; // MONOCHROME is last custom color
static HBITMAP devicebitmap;
static HDC devicedc;
static LPBYTE framebufferbits;
static LPBITMAPINFO framebufferinfo;
static LPBYTE frameoffsettable[384];
static LPBYTE hiresauxptr;
static LPBYTE hiresmainptr;
static HBITMAP logobitmap;
static HPALETTE palette;
static HBITMAP sourcebitmap;
static LPBYTE sourcebits;
static LPBITMAPINFO sourceinfo;
static LPBYTE sourceoffsettable[512];
static LPBYTE textauxptr;
static LPBYTE textmainptr;
// For tv emulation mode
static BYTE hgrpixelmatrix[280][192 + 2 * HGR_MATRIX_YOFFSET];
static BYTE colormixbuffer[6];
static WORD colormixmap[6][6][6];
//
static int charoffs = 0;
static BOOL displaypage2 = 0;
static LPBYTE framebufferaddr = (LPBYTE)0;
static LONG framebufferpitch = 0;
BOOL graphicsmode = 0;
static BOOL hasrefreshed = 0;
static DWORD lastpageflip = 0;
COLORREF monochrome = RGB(0xFF,0xFF,0xFF);
static BOOL rebuiltsource = 0;
static BOOL redrawfull = 1;
static DWORD dwVBlCounter = 0;
static LPBYTE vidlastmem = NULL;
static DWORD vidmode = VF_TEXT;
DWORD videotype = VT_COLOR_TVEMU;
static bool g_bTextFlashState = false;
static bool g_bTextFlashFlag = false;
//-------------------------------------
// Video consts:
const UINT nVBlStop_NTSC = 21;
const UINT nVBlStop_PAL = 29;
//-------------------------------------
void DrawDHiResSource ();
void DrawHiResSource ();
void DrawLoResSource ();
void DrawMonoDHiResSource ();
void DrawMonoHiResSource ();
void DrawMonoLoResSource ();
void DrawMonoTextSource (HDC dc);
void DrawTextSource (HDC dc);
//===========================================================================
void __stdcall CopySource (int destx, int desty,
int xsize, int ysize,
int sourcex, int sourcey)
{
LPBYTE currdestptr = frameoffsettable [desty] +destx;
LPBYTE currsourceptr = sourceoffsettable[sourcey]+sourcex;
int bytesleft;
while (ysize--)
{
bytesleft = xsize;
while (bytesleft & 3)
{
--bytesleft;
*(currdestptr+bytesleft) = *(currsourceptr+bytesleft);
}
while (bytesleft)
{
bytesleft -= 4;
*(LPDWORD)(currdestptr+bytesleft) = *(LPDWORD)(currsourceptr+bytesleft);
}
currdestptr -= framebufferpitch;
currsourceptr -= SRCOFFS_TOTAL;
}
}
//===========================================================================
void CreateFrameOffsetTable (LPBYTE addr, LONG pitch) {
if (framebufferaddr == addr &&
framebufferpitch == pitch)
return;
framebufferaddr = addr;
framebufferpitch = pitch;
// CREATE THE OFFSET TABLE FOR EACH SCAN LINE IN THE FRAME BUFFER
for (int loop = 0; loop < 384; loop++)
frameoffsettable[loop] = framebufferaddr+framebufferpitch*(383-loop);
}
//===========================================================================
void CreateIdentityPalette () {
if (palette)
DeleteObject(palette);
// SET FRAME BUFFER TABLE ENTRIES TO CUSTOM COLORS
SETFRAMECOLOR(DEEP_RED, 0xD0,0x00,0x30);
SETFRAMECOLOR(LIGHT_BLUE,0x60,0xA0,0xFF);
SETFRAMECOLOR(BROWN, 0x80,0x50,0x00);
SETFRAMECOLOR(ORANGE, 0xFF,0x80,0x00);
SETFRAMECOLOR(PINK, 0xFF,0x90,0x80);
SETFRAMECOLOR(AQUA, 0x40,0xFF,0x90);
SETFRAMECOLOR(HGR_BLACK, 0x00,0x00,0x00); // For tv emulation mode
SETFRAMECOLOR(HGR_WHITE, 0xFF,0xFF,0xFE);
SETFRAMECOLOR(HGR_BLUE, 0x00,0x80,0xFF);
SETFRAMECOLOR(HGR_RED, 0xF0,0x50,0x00);
SETFRAMECOLOR(HGR_GREEN, 0x20,0xC0,0x00);
SETFRAMECOLOR(HGR_MAGENTA,0xA0,0x00,0xFF);
SETFRAMECOLOR(HGR_GREY1, 0x80,0x80,0x80);
SETFRAMECOLOR(HGR_GREY2, 0x80,0x80,0x80);
SETFRAMECOLOR(HGR_YELLOW, 0xD0,0xB0,0x10);
SETFRAMECOLOR(HGR_AQUA, 0x20,0xB0,0xB0);
SETFRAMECOLOR(HGR_PURPLE, 0x60,0x50,0xE0);
SETFRAMECOLOR(HGR_PINK, 0xD0,0x40,0xA0);
SETFRAMECOLOR(MONOCHROME,GetRValue(monochrome),
GetGValue(monochrome),
GetBValue(monochrome));
// IF WE ARE IN A PALETTIZED VIDEO MODE, CREATE AN IDENTITY PALETTE
HWND window = GetDesktopWindow();
HDC dc = GetDC(window);
int colors = GetDeviceCaps(dc,SIZEPALETTE);
int system = GetDeviceCaps(dc,NUMCOLORS);
if ((GetDeviceCaps(dc,RASTERCAPS) & RC_PALETTE) && (colors <= 256)) {
// GET THE PALETTE ENTRIES OF THE LOGO
RGBQUAD logotable[256];
ZeroMemory(logotable,sizeof(logotable));
if (logobitmap) {
HDC memdc = CreateCompatibleDC(dc);
SelectObject(memdc,logobitmap);
GetDIBColorTable(memdc,0,colors,logotable);
DeleteDC(memdc);
}
// CREATE A PALETTE ENTRY ARRAY
LOGPALETTE *paldata = (LOGPALETTE *)VirtualAlloc(NULL,
sizeof(LOGPALETTE)
+256*sizeof(PALETTEENTRY),
MEM_COMMIT,
PAGE_READWRITE);
paldata->palVersion = 0x300;
paldata->palNumEntries = colors;
GetSystemPaletteEntries(dc,0,colors,paldata->palPalEntry);
// FILL IN THE PALETTE ENTRIES
int paletteindex = 0;
int logoindex = 0;
int halftoneindex = 0;
// COPY THE SYSTEM PALETTE ENTRIES AT THE BEGINNING OF THE PALETTE
for (; paletteindex < system/2; paletteindex++)
paldata->palPalEntry[paletteindex].peFlags = 0;
// FILL IN THE MIDDLE PORTION OF THE PALETTE WITH OUR OWN COLORS
for (int ourindex = DEEP_RED; ourindex <= MONOCHROME; ourindex++) {
paldata->palPalEntry[paletteindex].peRed = framebufferinfo->bmiColors[ourindex].rgbRed;
paldata->palPalEntry[paletteindex].peGreen = framebufferinfo->bmiColors[ourindex].rgbGreen;
paldata->palPalEntry[paletteindex].peBlue = framebufferinfo->bmiColors[ourindex].rgbBlue;
paldata->palPalEntry[paletteindex].peFlags = PC_NOCOLLAPSE;
paletteindex++;
}
for (; paletteindex < colors-system/2; paletteindex++) {
// IF THIS PALETTE ENTRY IS NEEDED FOR THE LOGO, COPY IN THE LOGO COLOR
if (logotable[logoindex].rgbRed &&
logotable[logoindex].rgbGreen &&
logotable[logoindex].rgbBlue) {
paldata->palPalEntry[paletteindex].peRed = logotable[logoindex].rgbRed;
paldata->palPalEntry[paletteindex].peGreen = logotable[logoindex].rgbGreen;
paldata->palPalEntry[paletteindex].peBlue = logotable[logoindex].rgbBlue;
}
// OTHERWISE, ADD A HALFTONING COLOR, SO THAT OTHER APPLICATIONS
// RUNNING IN THE BACKGROUND WILL HAVE SOME REASONABLE COLORS TO USE
else {
static BYTE halftonetable[6] = {32,64,96,160,192,224};
paldata->palPalEntry[paletteindex].peRed = halftonetable[halftoneindex % 6];
paldata->palPalEntry[paletteindex].peGreen = halftonetable[halftoneindex/6 % 6];
paldata->palPalEntry[paletteindex].peBlue = halftonetable[halftoneindex/36 % 6];
++halftoneindex;
}
++logoindex;
paldata->palPalEntry[paletteindex].peFlags = PC_NOCOLLAPSE;
}
// COPY THE SYSTEM PALETTE ENTRIES AT THE END OF THE PALETTE
for (; paletteindex < colors; paletteindex++)
paldata->palPalEntry[paletteindex].peFlags = 0;
// FILL THE FRAME BUFFER TABLE WITH COLORS FROM OUR PALETTE
for (int loop = 0; loop < colors; loop++) {
framebufferinfo->bmiColors[loop].rgbRed = paldata->palPalEntry[loop].peRed;
framebufferinfo->bmiColors[loop].rgbGreen = paldata->palPalEntry[loop].peGreen;
framebufferinfo->bmiColors[loop].rgbBlue = paldata->palPalEntry[loop].peBlue;
}
// CREATE THE PALETTE
palette = CreatePalette(paldata);
VirtualFree(paldata,0,MEM_RELEASE);
}
// OTHERWISE, FILL THE FRAME BUFFER TABLE WITH THE STANDARD WINDOWS COLORS
else {
SETFRAMECOLOR(BLACK, 0x00,0x00,0x00);
SETFRAMECOLOR(DARK_RED, 0x80,0x00,0x00);
SETFRAMECOLOR(DARK_GREEN, 0x00,0x80,0x00);
SETFRAMECOLOR(DARK_YELLOW, 0x80,0x80,0x00);
SETFRAMECOLOR(DARK_BLUE, 0x00,0x00,0x80);
SETFRAMECOLOR(DARK_MAGENTA,0x80,0x00,0x80);
SETFRAMECOLOR(DARK_CYAN, 0x00,0x80,0x80);
SETFRAMECOLOR(LIGHT_GRAY, 0xC0,0xC0,0xC0);
SETFRAMECOLOR(MONEY_GREEN, 0xC0,0xDC,0xC0);
SETFRAMECOLOR(SKY_BLUE, 0xA6,0xCA,0xF0);
SETFRAMECOLOR(CREAM, 0xFF,0xFB,0xF0);
SETFRAMECOLOR(MEDIUM_GRAY, 0xA0,0xA0,0xA4);
SETFRAMECOLOR(DARK_GRAY, 0x80,0x80,0x80);
SETFRAMECOLOR(RED, 0xFF,0x00,0x00);
SETFRAMECOLOR(GREEN, 0x00,0xFF,0x00);
SETFRAMECOLOR(YELLOW, 0xFF,0xFF,0x00);
SETFRAMECOLOR(BLUE, 0x00,0x00,0xFF);
SETFRAMECOLOR(MAGENTA, 0xFF,0x00,0xFF);
SETFRAMECOLOR(CYAN, 0x00,0xFF,0xFF);
SETFRAMECOLOR(WHITE, 0xFF,0xFF,0xFF);
palette = (HPALETTE)0;
}
ReleaseDC(window,dc);
}
//===========================================================================
void CreateDIBSections () {
CopyMemory(sourceinfo->bmiColors,framebufferinfo->bmiColors,256*sizeof(RGBQUAD));
// CREATE THE DEVICE CONTEXT
HWND window = GetDesktopWindow();
HDC dc = GetDC(window);
if (devicedc)
DeleteDC(devicedc);
devicedc = CreateCompatibleDC(dc);
// CREATE THE FRAME BUFFER DIB SECTION
if (devicebitmap)
DeleteObject(devicebitmap);
devicebitmap = CreateDIBSection(dc,framebufferinfo,DIB_RGB_COLORS,
(LPVOID *)&framebufferbits,0,0);
SelectObject(devicedc,devicebitmap);
// CREATE THE SOURCE IMAGE DIB SECTION
HDC sourcedc = CreateCompatibleDC(dc);
ReleaseDC(window,dc);
if (sourcebitmap)
DeleteObject(sourcebitmap);
sourcebitmap = CreateDIBSection(sourcedc,sourceinfo,DIB_RGB_COLORS,
(LPVOID *)&sourcebits,0,0);
SelectObject(sourcedc,sourcebitmap);
// CREATE THE OFFSET TABLE FOR EACH SCAN LINE IN THE SOURCE IMAGE
for (int loop = 0; loop < 512; loop++)
sourceoffsettable[loop] = sourcebits+SRCOFFS_TOTAL*(511-loop);
// DRAW THE SOURCE IMAGE INTO THE SOURCE BIT BUFFER
ZeroMemory(sourcebits,SRCOFFS_TOTAL*512);
if (videotype != VT_MONO) {
DrawTextSource(sourcedc);
DrawLoResSource();
DrawHiResSource();
DrawDHiResSource();
}
else {
DrawMonoTextSource(sourcedc);
DrawMonoLoResSource();
DrawMonoHiResSource();
DrawMonoDHiResSource();
}
DeleteDC(sourcedc);
}
//===========================================================================
void DrawDHiResSource () {
BYTE colorval[16] = {BLACK, DARK_BLUE, DARK_GREEN,BLUE,
BROWN, LIGHT_GRAY,GREEN, AQUA,
DEEP_RED,MAGENTA, DARK_GRAY, LIGHT_BLUE,
ORANGE, PINK, YELLOW, WHITE};
#define OFFSET 3
#define SIZE 10
for (int column = 0; column < 256; column++) {
int coloffs = SIZE * column;
for (unsigned byteval = 0; byteval < 256; byteval++) {
int color[SIZE];
ZeroMemory(color,sizeof(color));
unsigned pattern = MAKEWORD(byteval,column);
int pixel;
for (pixel = 1; pixel < 15; pixel++) {
if (pattern & (1 << pixel)) {
int pixelcolor = 1 << ((pixel-OFFSET) & 3);
if ((pixel >= OFFSET+2) && (pixel < SIZE+OFFSET+2) && (pattern & (0x7 << (pixel-4))))
color[pixel-(OFFSET+2)] |= pixelcolor;
if ((pixel >= OFFSET+1) && (pixel < SIZE+OFFSET+1) && (pattern & (0xF << (pixel-4))))
color[pixel-(OFFSET+1)] |= pixelcolor;
if ((pixel >= OFFSET+0) && (pixel < SIZE+OFFSET+0))
color[pixel-(OFFSET+0)] |= pixelcolor;
if ((pixel >= OFFSET-1) && (pixel < SIZE+OFFSET-1) && (pattern & (0xF << (pixel+1))))
color[pixel-(OFFSET-1)] |= pixelcolor;
if ((pixel >= OFFSET-2) && (pixel < SIZE+OFFSET-2) && (pattern & (0x7 << (pixel+2))))
color[pixel-(OFFSET-2)] |= pixelcolor;
}
}
if (videotype == VT_COLOR_TEXT_OPTIMIZED)
{
/***
activate for fringe reduction on white hgr text
drawback: loss of color mix patterns in hgr mode.
select videotype by index
***/
for (pixel = 0; pixel < 13; pixel++)
{
if ((pattern & (0xF << pixel)) == (unsigned)(0xF << pixel))
for (int pos = pixel; pos < pixel + 4; pos++)
if (pos >= OFFSET && pos < SIZE+OFFSET)
color[pos-OFFSET] = 15;
}
}
int y = byteval << 1;
for (int x = 0; x < SIZE; x++) {
SETSOURCEPIXEL(SRCOFFS_DHIRES+coloffs+x,y ,colorval[color[x]]);
SETSOURCEPIXEL(SRCOFFS_DHIRES+coloffs+x,y+1,colorval[color[x]]);
}
}
}
#undef SIZE
#undef OFFSET
}
//===========================================================================
void DrawHiResSource () {
// BYTE colorval[6] = {MAGENTA,BLUE,GREEN,ORANGE,BLACK,WHITE};
BYTE colorval[6] = {HGR_MAGENTA,HGR_BLUE,HGR_GREEN,HGR_RED,HGR_BLACK,HGR_WHITE};
for (int column = 0; column < 16; column++) {
int coloffs = column << 5;
for (unsigned byteval = 0; byteval < 256; byteval++) {
BOOL pixelon[11];
pixelon[ 0] = column & 4;
pixelon[ 1] = column & 8;
pixelon[ 9] = column & 1;
pixelon[10] = column & 2;
int bitval = 1;
int pixel;
for (pixel = 2; pixel < 9; pixel++) {
pixelon[pixel] = ((byteval & bitval) != 0);
bitval <<= 1;
}
int hibit = ((byteval & 0x80) != 0);
int x = 0;
int y = byteval << 1;
while (x < 28) {
int adj = (x >= 14) << 1;
int odd = (x >= 14);
for (pixel = 2; pixel < 9; pixel++) {
int color = 4;
if (pixelon[pixel])
{
if (pixelon[pixel-1] || pixelon[pixel+1])
color = 5;
else
color = ((odd ^ (pixel&1)) << 1) | hibit;
}
else if (pixelon[pixel-1] && pixelon[pixel+1])
{
/***
activate for fringe reduction on white hgr text -
drawback: loss of color mix patterns in hgr mode.
select videotype by index exclusion
***/
if ((videotype == VT_COLOR_STANDARD) || (videotype == VT_COLOR_TVEMU) || !(pixelon[pixel-2] && pixelon[pixel+2]))
color = ((odd ^ !(pixel&1)) << 1) | hibit; // // No white HGR text optimization
}
SETSOURCEPIXEL(SRCOFFS_HIRES+coloffs+x+adj ,y ,colorval[color]);
SETSOURCEPIXEL(SRCOFFS_HIRES+coloffs+x+adj+1,y ,colorval[color]);
SETSOURCEPIXEL(SRCOFFS_HIRES+coloffs+x+adj ,y+1,colorval[color]);
SETSOURCEPIXEL(SRCOFFS_HIRES+coloffs+x+adj+1,y+1,colorval[color]);
x += 2;
}
}
}
}
}
//===========================================================================
void DrawLoResSource () {
BYTE colorval[16] = {BLACK, DEEP_RED, DARK_BLUE, MAGENTA,
DARK_GREEN,DARK_GRAY,BLUE, LIGHT_BLUE,
BROWN, ORANGE, LIGHT_GRAY,PINK,
GREEN, YELLOW, AQUA, WHITE};
for (int color = 0; color < 16; color++)
for (int x = 0; x < 16; x++)
for (int y = 0; y < 16; y++)
SETSOURCEPIXEL(SRCOFFS_LORES+x,(color << 4)+y,colorval[color]);
}
//===========================================================================
void DrawMonoDHiResSource () {
for (int column = 0; column < 256; column++) {
int coloffs = 10 * column;
for (unsigned byteval = 0; byteval < 256; byteval++) {
unsigned pattern = MAKEWORD(byteval,column);
int y = byteval << 1;
for (int x = 0; x < 10; x++) {
BYTE colorval = pattern & (1 << (x+3)) ? MONOCHROME : BLACK;
SETSOURCEPIXEL(SRCOFFS_DHIRES+coloffs+x,y ,colorval);
SETSOURCEPIXEL(SRCOFFS_DHIRES+coloffs+x,y+1,colorval);
}
}
}
}
//===========================================================================
void DrawMonoHiResSource () {
for (int column = 0; column < 512; column += 16) {
for (int y = 0; y < 512; y += 2) {
unsigned val = (y >> 1);
for (int x = 0; x < 16; x += 2) {
BYTE colorval = (val & 1) ? MONOCHROME : BLACK;
val >>= 1;
SETSOURCEPIXEL(SRCOFFS_HIRES+column+x ,y ,colorval);
SETSOURCEPIXEL(SRCOFFS_HIRES+column+x+1,y ,colorval);
SETSOURCEPIXEL(SRCOFFS_HIRES+column+x ,y+1,colorval);
SETSOURCEPIXEL(SRCOFFS_HIRES+column+x+1,y+1,colorval);
}
}
}
}
//===========================================================================
void DrawMonoLoResSource () {
for (int color = 0; color < 16; color++)
for (int x = 0; x < 16; x++)
for (int y = 0; y < 16; y++) {
BYTE colorval = (color >> (x & 3) & 1) ? MONOCHROME : BLACK;
SETSOURCEPIXEL(SRCOFFS_LORES+x,(color << 4)+y,colorval);
}
}
//===========================================================================
void DrawMonoTextSource (HDC dc) {
HDC memdc = CreateCompatibleDC(dc);
HBITMAP bitmap = LoadBitmap(instance,TEXT("CHARSET40"));
HBRUSH brush = CreateSolidBrush(monochrome);
SelectObject(memdc,bitmap);
SelectObject(dc,brush);
BitBlt(dc,SRCOFFS_40COL,0,256,512,memdc,0,0,MERGECOPY);
StretchBlt(dc,SRCOFFS_80COL,0,128,512,memdc,0,0,256,512,MERGECOPY);
SelectObject(dc,GetStockObject(NULL_BRUSH));
DeleteObject(brush);
DeleteDC(memdc);
DeleteObject(bitmap);
}
//===========================================================================
void DrawTextSource (HDC dc) {
HDC memdc = CreateCompatibleDC(dc);
HBITMAP bitmap = LoadBitmap(instance,TEXT("CHARSET40"));
SelectObject(memdc,bitmap);
BitBlt(dc,SRCOFFS_40COL,0,256,512,memdc,0,0,SRCCOPY);
StretchBlt(dc,SRCOFFS_80COL,0,128,512,memdc,0,0,256,512,SRCCOPY);
DeleteDC(memdc);
DeleteObject(bitmap);
}
//===========================================================================
void SetLastDrawnImage () {
memcpy(vidlastmem+0x400,textmainptr,0x400);
if (SW_HIRES)
memcpy(vidlastmem+0x2000,hiresmainptr,0x2000);
if (SW_DHIRES && SW_HIRES)
memcpy(vidlastmem,hiresauxptr,0x2000);
if (SW_80COL && !SW_HIRES)
memcpy(vidlastmem,textauxptr,0x400);
int loop;
for (loop = 0; loop < 256; loop++)
*(memdirty+loop) &= ~2;
}
//===========================================================================
BOOL Update40ColCell (int x, int y, int xpixel, int ypixel, int offset)
{
BYTE ch = *(textmainptr+offset);
bool bCharChanged = (ch != *(vidlastmem+offset+0x400) || redrawfull);
// FLASHing chars:
// - FLASHing if:Alt Char Set is OFF && 0x40<=char<=0x7F
// - The inverse of this char is located at: char+0x40
bool bCharFlashing = (charoffs == 0) && (ch >= 0x40) && (ch <= 0x7F);
if(bCharChanged || (bCharFlashing && g_bTextFlashFlag))
{
bool bInvert = bCharFlashing ? g_bTextFlashState : false;
CopySource(xpixel,ypixel,
14,16,
SRCOFFS_40COL+((ch & 0x0F) << 4),
(ch & 0xF0)+charoffs + (bInvert?0x40:0x00));
return 1;
}
else
{
return 0;
}
}
//===========================================================================
BOOL Update80ColCell (int x, int y, int xpixel, int ypixel, int offset) {
BYTE auxval = *(textauxptr +offset);
BYTE mainval = *(textmainptr+offset);
if ((auxval != *(vidlastmem+offset)) ||
(mainval != *(vidlastmem+offset+0x400)) ||
redrawfull) {
CopySource(xpixel,ypixel,
7,16,
SRCOFFS_80COL+((auxval & 15)<<3),((auxval>>4)<<4)+charoffs);
CopySource(xpixel+7,ypixel,
7,16,
SRCOFFS_80COL+((mainval & 15)<<3),((mainval>>4)<<4)+charoffs);
return 1;
}
else
return 0;
}
//===========================================================================
BOOL UpdateDHiResCell (int x, int y, int xpixel, int ypixel, int offset) {
BOOL dirty = 0;
int yoffset = 0;
while (yoffset < 0x2000) {
BYTE byteval1 = (x > 0) ? *(hiresmainptr+offset+yoffset-1) : 0;
BYTE byteval2 = *(hiresauxptr +offset+yoffset);
BYTE byteval3 = *(hiresmainptr+offset+yoffset);
BYTE byteval4 = (x < 39) ? *(hiresauxptr +offset+yoffset+1) : 0;
if ((byteval2 != *(vidlastmem+offset+yoffset)) ||
(byteval3 != *(vidlastmem+offset+yoffset+0x2000)) ||
((x > 0) && ((byteval1 & 0x70) != (*(vidlastmem+offset+yoffset+0x1FFF) & 0x70))) ||
((x < 39) && ((byteval4 & 0x07) != (*(vidlastmem+offset+yoffset+ 1) & 0x07))) ||
redrawfull) {
DWORD dwordval = (byteval1 & 0x70) | ((byteval2 & 0x7F) << 7) |
((byteval3 & 0x7F) << 14) | ((byteval4 & 0x07) << 21);
#define PIXEL 0
#define COLOR ((xpixel + PIXEL) & 3)
#define VALUE (dwordval >> (4 + PIXEL - COLOR))
CopySource(xpixel+PIXEL,ypixel+(yoffset >> 9),7,2,
SRCOFFS_DHIRES+10*HIBYTE(VALUE)+COLOR,LOBYTE(VALUE)<<1);
#undef PIXEL
#define PIXEL 7
CopySource(xpixel+PIXEL,ypixel+(yoffset >> 9),7,2,
SRCOFFS_DHIRES+10*HIBYTE(VALUE)+COLOR,LOBYTE(VALUE)<<1);
#undef PIXEL
#undef COLOR
#undef VALUE
dirty = 1;
}
yoffset += 0x400;
}
return dirty;
}
//===========================================================================
BYTE MixColors(BYTE c1, BYTE c2) { // For tv emulation mode
#define COMBINATION(c1,c2,ref1,ref2) (((c1)==(ref1)&&(c2)==(ref2)) || ((c1)==(ref2)&&(c2)==(ref1)))
if (c1 == c2)
return c1;
if (COMBINATION(c1,c2,HGR_BLUE,HGR_RED))
return HGR_GREY1;
else if (COMBINATION(c1,c2,HGR_GREEN,HGR_MAGENTA))
return HGR_GREY2;
else if (COMBINATION(c1,c2,HGR_RED,HGR_GREEN))
return HGR_YELLOW;
else if (COMBINATION(c1,c2,HGR_BLUE,HGR_GREEN))
return HGR_AQUA;
else if (COMBINATION(c1,c2,HGR_BLUE,HGR_MAGENTA))
return HGR_PURPLE;
else if (COMBINATION(c1,c2,HGR_RED,HGR_MAGENTA))
return HGR_PINK;
else
return MONOCHROME; // visible failure indicator
#undef COMBINATION
}
//===========================================================================
void CreateColorMixMap() { // For tv emulation mode
#define FROM_NEIGHBOUR 0x00
int t,m,b;
BYTE cTop, cMid, cBot;
WORD mixTop, mixBot;
for (t=0; t<6; t++)
for (m=0; m<6; m++)
for (b=0; b<6; b++) {
cTop = t | 0x10;
cMid = m | 0x10;
cBot = b | 0x10;
if (cMid < HGR_BLUE) {
mixTop = mixBot = cMid;
} else {
if (cTop < HGR_BLUE) {
mixTop = FROM_NEIGHBOUR;
} else {
mixTop = MixColors(cMid,cTop);
}
if (cBot < HGR_BLUE) {
mixBot = FROM_NEIGHBOUR;
} else {
mixBot = MixColors(cMid,cBot);
}
if (mixTop == FROM_NEIGHBOUR && mixBot != FROM_NEIGHBOUR) {
mixTop = mixBot;
} else if (mixBot == FROM_NEIGHBOUR && mixTop != FROM_NEIGHBOUR) {
mixBot = mixTop;
} else if (mixBot == FROM_NEIGHBOUR && mixTop == FROM_NEIGHBOUR) {
mixBot = mixTop = cMid;
}
}
colormixmap[t][m][b] = (mixTop << 8) | mixBot;
}
#undef FROM_NEIGHBOUR
}
//===========================================================================
void __stdcall MixColorsVertical(int matx, int maty) { // For tv emulation mode
WORD twoHalfPixel;
int bot1idx, bot2idx;
if (SW_MIXED && maty > 159) {
if (maty < 161) {
bot1idx = hgrpixelmatrix[matx][maty+1] & 0x0F;
bot2idx = 0;
} else {
bot1idx = bot2idx = 0;
}
} else {
bot1idx = hgrpixelmatrix[matx][maty+1] & 0x0F;
bot2idx = hgrpixelmatrix[matx][maty+2] & 0x0F;
}
twoHalfPixel = colormixmap[hgrpixelmatrix[matx][maty-2] & 0x0F]
[hgrpixelmatrix[matx][maty-1] & 0x0F]
[hgrpixelmatrix[matx][maty ] & 0x0F];
colormixbuffer[0] = (twoHalfPixel & 0xFF00) >> 8;
colormixbuffer[1] = twoHalfPixel & 0x00FF;
twoHalfPixel = colormixmap[hgrpixelmatrix[matx][maty-1] & 0x0F]
[hgrpixelmatrix[matx][maty ] & 0x0F]
[bot1idx];
colormixbuffer[2] = (twoHalfPixel & 0xFF00) >> 8;
colormixbuffer[3] = twoHalfPixel & 0x00FF;
twoHalfPixel = colormixmap[hgrpixelmatrix[matx][maty ] & 0x0F]
[bot1idx]
[bot2idx];
colormixbuffer[4] = (twoHalfPixel & 0xFF00) >> 8;
colormixbuffer[5] = twoHalfPixel & 0x00FF;
}
//===========================================================================
void __stdcall CopyMixedSource (int x, int y, int sourcex, int sourcey) { // For tv emulation mode
LPBYTE currsourceptr = sourceoffsettable[sourcey]+sourcex;
LPBYTE currdestptr = frameoffsettable[y<<1] + (x<<1);
LPBYTE currptr;
int matx = x;
int maty = HGR_MATRIX_YOFFSET + y;
int count;
int bufxoffset;
int hgrlinesabove = (y > 0)? 1 : 0;
int hgrlinesbelow = SW_MIXED ? ((y < 159)? 1:0) : ((y < 191)? 1:0);
int i;
int istart = 2 - (hgrlinesabove << 1);
int iend = 3 + (hgrlinesbelow << 1);
// transfer 7 pixels (i.e. the visible part of an apple hgr-byte) from row to pixelmatrix
for (count = 0, bufxoffset = 0;
count < 7;
count++, bufxoffset += 2) {
hgrpixelmatrix[matx+count][maty] = *(currsourceptr+bufxoffset);
// color mixing between adjacent scanlines at current x position
MixColorsVertical(matx+count, maty);
// transfer up to 6 mixed (half-)pixels of current column to framebuffer
currptr = currdestptr+bufxoffset;
if (hgrlinesabove)
currptr += framebufferpitch << 1;
for (i = istart;
i <= iend;
currptr -= framebufferpitch, i++) {
*currptr = *(currptr+1) = colormixbuffer[i];
}
}
}
//===========================================================================
BOOL UpdateHiResCell (int x, int y, int xpixel, int ypixel, int offset) {
BOOL dirty = 0;
int yoffset = 0;
while (yoffset < 0x2000) {
BYTE byteval1 = (x > 0) ? *(hiresmainptr+offset+yoffset-1) : 0;
BYTE byteval2 = *(hiresmainptr+offset+yoffset);
BYTE byteval3 = (x < 39) ? *(hiresmainptr+offset+yoffset+1) : 0;
if ((byteval2 != *(vidlastmem+offset+yoffset+0x2000)) ||
((x > 0) && ((byteval1 & 0x60) != (*(vidlastmem+offset+yoffset+0x1FFF) & 0x60))) ||
((x < 39) && ((byteval3 & 0x03) != (*(vidlastmem+offset+yoffset+0x2001) & 0x03))) ||
redrawfull) {
#define COLOFFS (((byteval1 & 0x60) << 2) | \
((byteval3 & 0x03) << 5))
if (videotype == VT_COLOR_TVEMU)
{
CopyMixedSource(xpixel >> 1, (ypixel+(yoffset >> 9)) >> 1,
SRCOFFS_HIRES+COLOFFS+((x & 1) << 4),(((int)byteval2) << 1));
}
else
{
CopySource(xpixel,ypixel+(yoffset >> 9),
14,2,
SRCOFFS_HIRES+COLOFFS+((x & 1) << 4),(((int)byteval2) << 1));
}
#undef COLOFFS
dirty = 1;
}
yoffset += 0x400;
}
return dirty;
}
//===========================================================================
BOOL UpdateLoResCell (int x, int y, int xpixel, int ypixel, int offset) {
BYTE val = *(textmainptr+offset);
if ((val != *(vidlastmem+offset+0x400)) || redrawfull) {
CopySource(xpixel,ypixel,
14,8,
SRCOFFS_LORES+((x & 1) << 1),((val & 0xF) << 4));
CopySource(xpixel,ypixel+8,
14,8,
SRCOFFS_LORES+((x & 1) << 1),(val & 0xF0));
return 1;
}
else
return 0;
}
//===========================================================================
BOOL UpdateDLoResCell (int x, int y, int xpixel, int ypixel, int offset)
{
BYTE auxval = *(textauxptr +offset);
BYTE mainval = *(textmainptr+offset);
if ( (auxval != *(vidlastmem+offset)) ||
(mainval != *(vidlastmem+offset+0x400)) ||
redrawfull
)
{
CopySource( xpixel,ypixel,
7,8,
SRCOFFS_LORES+((x & 1) << 1),((auxval & 0xF) << 4));
CopySource( xpixel,ypixel+8,
7,8,
SRCOFFS_LORES+((x & 1) << 1),(auxval & 0xF0));
//
CopySource( xpixel+7,ypixel,
7,8,
SRCOFFS_LORES+((x & 1) << 1),((mainval & 0xF) << 4));
CopySource( xpixel+7,ypixel+8,
7,8,
SRCOFFS_LORES+((x & 1) << 1),(mainval & 0xF0));
return 1;
}
else
{
return 0;
}
}
//
// ----- ALL GLOBALLY ACCESSIBLE FUNCTIONS ARE BELOW THIS LINE -----
//
//===========================================================================
BOOL VideoApparentlyDirty ()
{
if (SW_MIXED || redrawfull)
return 1;
DWORD address = (SW_HIRES && !SW_TEXT) ? (0x20 << displaypage2) : (0x4 << displaypage2);
DWORD length = (SW_HIRES && !SW_TEXT) ? 0x20 : 0x4;
while (length--)
if (*(memdirty+(address++)) & 2)
return 1;
//
bool bCharFlashing = false;
// Scan visible text page for any flashing chars
if((SW_TEXT || SW_MIXED) && (charoffs == 0))
{
BYTE* pnMemText = MemGetMainPtr(0x400 << displaypage2);
// Scan 8 long-lines of 120 chars (at 128 char offsets):
// . Skip 8-char holes in TEXT
for(UINT y=0; y<8; y++)
{
for(UINT x=0; x<40*3; x++)
{
BYTE ch = pnMemText[y*128+x];
if((ch >= 0x40) && (ch <= 0x7F))
{
bCharFlashing = true;
break;
}
}
}
}
if(bCharFlashing)
return 1;
return 0;
}
//===========================================================================
void VideoBenchmark () {
Sleep(500);
// PREPARE TWO DIFFERENT FRAME BUFFERS, EACH OF WHICH HAVE HALF OF THE
// BYTES SET TO 0x14 AND THE OTHER HALF SET TO 0xAA
int loop;
LPDWORD mem32 = (LPDWORD)mem;
for (loop = 4096; loop < 6144; loop++)
*(mem32+loop) = ((loop & 1) ^ ((loop & 0x40) >> 6)) ? 0x14141414
: 0xAAAAAAAA;
for (loop = 6144; loop < 8192; loop++)
*(mem32+loop) = ((loop & 1) ^ ((loop & 0x40) >> 6)) ? 0xAAAAAAAA
: 0x14141414;
// SEE HOW MANY TEXT FRAMES PER SECOND WE CAN PRODUCE WITH NOTHING ELSE
// GOING ON, CHANGING HALF OF THE BYTES IN THE VIDEO BUFFER EACH FRAME TO
// SIMULATE THE ACTIVITY OF AN AVERAGE GAME
DWORD totaltextfps = 0;
vidmode = VF_TEXT;
FillMemory(mem+0x400,0x400,0x14);
VideoRedrawScreen();
DWORD milliseconds = GetTickCount();
while (GetTickCount() == milliseconds) ;
milliseconds = GetTickCount();
DWORD cycle = 0;
do {
if (cycle & 1)
FillMemory(mem+0x400,0x400,0x14);
else
CopyMemory(mem+0x400,mem+((cycle & 2) ? 0x4000 : 0x6000),0x400);
VideoRefreshScreen();
if (cycle++ >= 3)
cycle = 0;
totaltextfps++;
} while (GetTickCount() - milliseconds < 1000);
// SEE HOW MANY HIRES FRAMES PER SECOND WE CAN PRODUCE WITH NOTHING ELSE
// GOING ON, CHANGING HALF OF THE BYTES IN THE VIDEO BUFFER EACH FRAME TO
// SIMULATE THE ACTIVITY OF AN AVERAGE GAME
DWORD totalhiresfps = 0;
vidmode = VF_HIRES;
FillMemory(mem+0x2000,0x2000,0x14);
VideoRedrawScreen();
milliseconds = GetTickCount();
while (GetTickCount() == milliseconds) ;
milliseconds = GetTickCount();
cycle = 0;
do {
if (cycle & 1)
FillMemory(mem+0x2000,0x2000,0x14);
else
CopyMemory(mem+0x2000,mem+((cycle & 2) ? 0x4000 : 0x6000),0x2000);
VideoRefreshScreen();
if (cycle++ >= 3)
cycle = 0;
totalhiresfps++;
} while (GetTickCount() - milliseconds < 1000);
// DETERMINE HOW MANY 65C02 CLOCK CYCLES WE CAN EMULATE PER SECOND WITH
// NOTHING ELSE GOING ON
CpuSetupBenchmark();
DWORD totalmhz10 = 0;
milliseconds = GetTickCount();
while (GetTickCount() == milliseconds) ;
milliseconds = GetTickCount();
cycle = 0;
do {
CpuExecute(100000);
totalmhz10++;
} while (GetTickCount() - milliseconds < 1000);
// IF THE PROGRAM COUNTER IS NOT IN THE EXPECTED RANGE AT THE END OF THE
// CPU BENCHMARK, REPORT AN ERROR AND OPTIONALLY TRACK IT DOWN
if ((regs.pc < 0x300) || (regs.pc > 0x400))
if (MessageBox(framewindow,
TEXT("The emulator has detected a problem while running ")
TEXT("the CPU benchmark. Would you like to gather more ")
TEXT("information?"),
TEXT("Benchmarks"),
MB_ICONQUESTION | MB_YESNO | MB_SETFOREGROUND) == IDYES) {
BOOL error = 0;
WORD lastpc = 0x300;
int loop = 0;
while ((loop < 10000) && !error) {
CpuSetupBenchmark();
CpuExecute(loop);
if ((regs.pc < 0x300) || (regs.pc > 0x400))
error = 1;
else {
lastpc = regs.pc;
++loop;
}
}
if (error) {
TCHAR outstr[256];
wsprintf(outstr,
TEXT("The emulator experienced an error %u clock cycles ")
TEXT("into the CPU benchmark. Prior to the error, the ")
TEXT("program counter was at $%04X. After the error, it ")
TEXT("had jumped to $%04X."),
(unsigned)loop,
(unsigned)lastpc,
(unsigned)regs.pc);
MessageBox(framewindow,
outstr,
TEXT("Benchmarks"),
MB_ICONINFORMATION | MB_SETFOREGROUND);
}
else
MessageBox(framewindow,
TEXT("The emulator was unable to locate the exact ")
TEXT("point of the error. This probably means that ")
TEXT("the problem is external to the emulator, ")
TEXT("happening asynchronously, such as a problem in ")
TEXT("a timer interrupt handler."),
TEXT("Benchmarks"),
MB_ICONINFORMATION | MB_SETFOREGROUND);
}
// DO A REALISTIC TEST OF HOW MANY FRAMES PER SECOND WE CAN PRODUCE
// WITH FULL EMULATION OF THE CPU, JOYSTICK, AND DISK HAPPENING AT
// THE SAME TIME
DWORD realisticfps = 0;
FillMemory(mem+0x2000,0x2000,0xAA);
VideoRedrawScreen();
milliseconds = GetTickCount();
while (GetTickCount() == milliseconds) ;
milliseconds = GetTickCount();
cycle = 0;
do {
if (realisticfps < 10) {
int cycles = 100000;
while (cycles > 0) {
DWORD executedcycles = CpuExecute(103);
cycles -= executedcycles;
DiskUpdatePosition(executedcycles);
JoyUpdatePosition();
VideoUpdateVbl(0);
}
}
if (cycle & 1)
FillMemory(mem+0x2000,0x2000,0xAA);
else
CopyMemory(mem+0x2000,mem+((cycle & 2) ? 0x4000 : 0x6000),0x2000);
VideoRefreshScreen();
if (cycle++ >= 3)
cycle = 0;
realisticfps++;
} while (GetTickCount() - milliseconds < 1000);
// DISPLAY THE RESULTS
VideoDisplayLogo();
TCHAR outstr[256];
wsprintf(outstr,
TEXT("Pure Video FPS:\t%u hires, %u text\n")
TEXT("Pure CPU MHz:\t%u.%u%s\n\n")
TEXT("EXPECTED AVERAGE VIDEO GAME\n")
TEXT("PERFORMANCE: %u FPS"),
(unsigned)totalhiresfps,
(unsigned)totaltextfps,
(unsigned)(totalmhz10/10),
(unsigned)(totalmhz10 % 10),
(LPCTSTR)(apple2e ? TEXT("") : TEXT(" (6502)")),
(unsigned)realisticfps);
MessageBox(framewindow,
outstr,
TEXT("Benchmarks"),
MB_ICONINFORMATION | MB_SETFOREGROUND);
}
//===========================================================================
BYTE __stdcall VideoCheckMode (WORD, BYTE address, BYTE, BYTE, ULONG) {
if (address == 0x7F)
return MemReturnRandomData(SW_DHIRES != 0);
else {
BOOL result = 0;
switch (address) {
case 0x1A: result = SW_TEXT; break;
case 0x1B: result = SW_MIXED; break;
case 0x1D: result = SW_HIRES; break;
case 0x1E: result = charoffs; break;
case 0x1F: result = SW_80COL; break;
case 0x7F: result = SW_DHIRES; break;
}
return KeybGetKeycode() | (result ? 0x80 : 0);
}
}
//===========================================================================
void VideoCheckPage (BOOL force) {
if ((displaypage2 != (SW_PAGE2 != 0)) &&
(force || (emulmsec-lastpageflip > 500))) {
displaypage2 = (SW_PAGE2 != 0);
VideoRefreshScreen();
hasrefreshed = 1;
lastpageflip = emulmsec;
}
}
//===========================================================================
BYTE __stdcall VideoCheckVbl (WORD, BYTE, BYTE, BYTE, ULONG)
{
/*
// Drol expects = 80
68DE A5 02 LDX #02
68E0 AD 50 C0 LDA TXTCLR
68E3 C9 80 CMP #80
68E5 D0 F7 BNE $68DE
6957 A5 02 LDX #02
6959 AD 50 C0 LDA TXTCLR
695C C9 80 CMP #80
695E D0 F7 BNE $68DE
69D3 A5 02 LDX #02
69D5 AD 50 C0 LDA TXTCLR
69D8 C9 80 CMP #80
69DA D0 F7 BNE $68DE
// Karateka expects < 80
// Returns 00 or FF on Apple ][
07DE AD 19 C0 LDA RDVBLBAR
07E1 30 FB BMI $7DE
*/
// return MemReturnRandomData(dwVBlCounter <= nVBlStop_NTSC);
if (dwVBlCounter <= nVBlStop_NTSC)
return 0x00;
else
return 0x80;
}
//===========================================================================
void VideoChooseColor () {
CHOOSECOLOR cc;
ZeroMemory(&cc,sizeof(CHOOSECOLOR));
cc.lStructSize = sizeof(CHOOSECOLOR);
cc.hwndOwner = framewindow;
cc.rgbResult = monochrome;
cc.lpCustColors = customcolors;
cc.Flags = CC_RGBINIT | CC_SOLIDCOLOR;
if (ChooseColor(&cc)) {
monochrome = cc.rgbResult;
VideoReinitialize();
if ((mode != MODE_LOGO) && (mode != MODE_DEBUG))
VideoRedrawScreen();
RegSaveValue(TEXT("Configuration"),TEXT("Monochrome Color"),1,monochrome);
}
}
//===========================================================================
void VideoDestroy () {
// DESTROY BUFFERS
VirtualFree(framebufferinfo,0,MEM_RELEASE);
VirtualFree(sourceinfo ,0,MEM_RELEASE);
VirtualFree(vidlastmem ,0,MEM_RELEASE);
framebufferinfo = NULL;
sourceinfo = NULL;
vidlastmem = NULL;
// DESTROY FRAME BUFFER
DeleteDC(devicedc);
DeleteObject(devicebitmap);
devicedc = (HDC)0;
devicebitmap = (HBITMAP)0;
// DESTROY SOURCE IMAGE
DeleteObject(sourcebitmap);
sourcebitmap = (HBITMAP)0;
// DESTROY LOGO
if (logobitmap) {
DeleteObject(logobitmap);
logobitmap = (HBITMAP)0;
}
// DESTROY PALETTE
if (palette) {
DeleteObject(palette);
palette = (HPALETTE)0;
}
}
//===========================================================================
void VideoDisplayLogo () {
HDC framedc = FrameGetDC();
// DRAW THE LOGO
HBRUSH brush = CreateSolidBrush(PALETTERGB(0x70,0x30,0xE0));
if (logobitmap) {
HDC memdc = CreateCompatibleDC(framedc);
SelectObject(memdc,logobitmap);
BitBlt(framedc,0,0,560,384,memdc,0,0,SRCCOPY);
DeleteDC(memdc);
}
else {
SelectObject(framedc,brush);
SelectObject(framedc,GetStockObject(NULL_PEN));
Rectangle(framedc,0,0,560+1,384+1);
}
// DRAW THE VERSION NUMBER
HFONT font = CreateFont(-20,0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET,
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,
VARIABLE_PITCH | 4 | FF_SWISS,
TEXT("Arial"));
SelectObject(framedc,font);
SetTextAlign(framedc,TA_RIGHT | TA_TOP);
SetBkMode(framedc,TRANSPARENT);
#define VERSION_TXT "Version "
char* szVersion = new char[strlen(VERSION_TXT) + strlen(VERSIONSTRING) + 1];
strcpy(&szVersion[0], VERSION_TXT);
strcpy(&szVersion[strlen(VERSION_TXT)], VERSIONSTRING);
szVersion[strlen(szVersion)] = 0x00;
#define DRAWVERSION(x,y,c) SetTextColor(framedc,c); \
TextOut(framedc, \
540+x,358+y, \
szVersion, \
strlen(szVersion));
if (GetDeviceCaps(framedc,PLANES) * GetDeviceCaps(framedc,BITSPIXEL) <= 4) {
DRAWVERSION( 2, 2,RGB(0x00,0x00,0x00));
DRAWVERSION( 1, 1,RGB(0x00,0x00,0x00));
DRAWVERSION( 0, 0,RGB(0xFF,0x00,0xFF));
}
else {
DRAWVERSION( 1, 1,PALETTERGB(0x30,0x30,0x70));
DRAWVERSION(-1,-1,PALETTERGB(0xC0,0x70,0xE0));
DRAWVERSION( 0, 0,PALETTERGB(0x70,0x30,0xE0));
}
delete [] szVersion;
#undef DRAWVERSION
FrameReleaseDC();
DeleteObject(brush);
DeleteObject(font);
}
//===========================================================================
BOOL VideoHasRefreshed () {
BOOL result = hasrefreshed;
hasrefreshed = 0;
return result;
}
//===========================================================================
void VideoInitialize () {
// CREATE A BUFFER FOR AN IMAGE OF THE LAST DRAWN MEMORY
vidlastmem = (LPBYTE)VirtualAlloc(NULL,0x10000,MEM_COMMIT,PAGE_READWRITE);
ZeroMemory(vidlastmem,0x10000);
// LOAD THE LOGO
logobitmap = (HBITMAP)LoadImage(instance, MAKEINTRESOURCE(IDB_APPLEWIN), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
// CREATE A BITMAPINFO STRUCTURE FOR THE FRAME BUFFER
framebufferinfo = (LPBITMAPINFO)VirtualAlloc(NULL,
sizeof(BITMAPINFOHEADER)
+256*sizeof(RGBQUAD),
MEM_COMMIT,
PAGE_READWRITE);
ZeroMemory(framebufferinfo,sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD));
framebufferinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
framebufferinfo->bmiHeader.biWidth = 560;
framebufferinfo->bmiHeader.biHeight = 384;
framebufferinfo->bmiHeader.biPlanes = 1;
framebufferinfo->bmiHeader.biBitCount = 8;
framebufferinfo->bmiHeader.biClrUsed = 256;
// CREATE A BITMAPINFO STRUCTURE FOR THE SOURCE IMAGE
sourceinfo = (LPBITMAPINFO)VirtualAlloc(NULL,
sizeof(BITMAPINFOHEADER)
+256*sizeof(RGBQUAD),
MEM_COMMIT,
PAGE_READWRITE);
ZeroMemory(sourceinfo,sizeof(BITMAPINFOHEADER));
sourceinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
sourceinfo->bmiHeader.biWidth = SRCOFFS_TOTAL;
sourceinfo->bmiHeader.biHeight = 512;
sourceinfo->bmiHeader.biPlanes = 1;
sourceinfo->bmiHeader.biBitCount = 8;
sourceinfo->bmiHeader.biClrUsed = 256;
// CREATE AN IDENTITY PALETTE AND FILL IN THE CORRESPONDING COLORS IN
// THE BITMAPINFO STRUCTURE
CreateIdentityPalette();
// PREFILL THE 16 CUSTOM COLORS AND MAKE SURE TO INCLUDE THE CURRENT MONOCHROME COLOR
for (int index = DARK_RED; index <= MONOCHROME; index++)
customcolors[index-DARK_RED] = RGB(framebufferinfo->bmiColors[index].rgbRed,
framebufferinfo->bmiColors[index].rgbGreen,
framebufferinfo->bmiColors[index].rgbBlue);
// CREATE THE FRAME BUFFER DIB SECTION AND DEVICE CONTEXT,
// CREATE THE SOURCE IMAGE DIB SECTION AND DRAW INTO THE SOURCE BIT BUFFER
CreateDIBSections();
// RESET THE VIDEO MODE SWITCHES AND THE CHARACTER SET OFFSET
VideoResetState();
}
//===========================================================================
void VideoRealizePalette (HDC dc) {
if (palette) {
SelectPalette(dc,palette,0);
RealizePalette(dc);
}
}
//===========================================================================
void VideoRedrawScreen () {
redrawfull = 1;
VideoRefreshScreen();
}
//===========================================================================
void VideoRefreshScreen () {
LPBYTE addr = framebufferbits;
LONG pitch = 560;
HDC framedc = FrameGetVideoDC(&addr,&pitch);
CreateFrameOffsetTable(addr,pitch);
// CHECK EACH CELL FOR CHANGED BYTES. REDRAW PIXELS FOR THE CHANGED BYTES
// IN THE FRAME BUFFER. MARK CELLS IN WHICH REDRAWING HAS TAKEN PLACE AS
// DIRTY.
hiresauxptr = MemGetAuxPtr (0x2000 << displaypage2);
hiresmainptr = MemGetMainPtr(0x2000 << displaypage2);
textauxptr = MemGetAuxPtr (0x400 << displaypage2);
textmainptr = MemGetMainPtr(0x400 << displaypage2);
ZeroMemory(celldirty,40*32);
updatetype update = SW_TEXT ? SW_80COL ? Update80ColCell
: Update40ColCell
: SW_HIRES ? (SW_DHIRES && SW_80COL) ? UpdateDHiResCell
: UpdateHiResCell
: (SW_DHIRES && SW_80COL) ? UpdateDLoResCell
: UpdateLoResCell;
BOOL anydirty = 0;
int y = 0;
int ypixel = 0;
while (y < 20) {
int offset = ((y & 7) << 7) + ((y >> 3) * 40);
int x = 0;
int xpixel = 0;
while (x < 40) {
anydirty |= celldirty[x][y] = update(x,y,xpixel,ypixel,offset+x);
++x;
xpixel += 14;
}
++y;
ypixel += 16;
}
if (SW_MIXED)
update = SW_80COL ? Update80ColCell
: Update40ColCell;
while (y < 24) {
int offset = ((y & 7) << 7) + ((y >> 3) * 40);
int x = 0;
int xpixel = 0;
while (x < 40) {
anydirty |= celldirty[x][y] = update(x,y,xpixel,ypixel,offset+x);
++x;
xpixel += 14;
}
++y;
ypixel += 16;
}
// Clear this flag after TEXT screen has been updated
g_bTextFlashFlag = false;
#if 0
// New simpified code:
// . Oliver Schmidt gets a flickering mouse cursor with this code
if (framedc && anydirty)
{
BitBlt(framedc,0,0,560,384,devicedc,0,0,SRCCOPY);
GdiFlush();
}
#else
// Original code:
if (!framedc || !anydirty)
{
FrameReleaseVideoDC();
SetLastDrawnImage();
redrawfull = 0;
return;
}
// COPY DIRTY CELLS FROM THE DEVICE DEPENDENT BITMAP ONTO THE SCREEN
// IN LONG HORIZONTAL RECTANGLES
BOOL remainingdirty = 0;
y = 0;
ypixel = 0;
while (y < 24) {
int start = -1;
int startx = 0;
int x = 0;
int xpixel = 0;
while (x < 40) {
if ((x == 39) && celldirty[x][y])
if (start >= 0) {
xpixel += 14;
celldirty[x][y] = 0;
}
else
remainingdirty = 1;
if ((start >= 0) && !celldirty[x][y]) {
if ((x - startx > 1) || ((x == 39) && (xpixel == 560))) {
int height = 1;
while ((y+height < 24)
&& celldirty[startx][y+height]
&& celldirty[x-1][y+height]
&& celldirty[(startx+x-1) >> 1][y+height])
height++;
BitBlt(framedc,start,ypixel,xpixel-start,height << 4,
devicedc,start,ypixel,SRCCOPY);
while (height--) {
int loop = startx;
while (loop < x+(xpixel == 560))
celldirty[loop++][y+height] = 0;
}
start = -1;
}
else
remainingdirty = 1;
start = -1;
}
else if ((start == -1) && celldirty[x][y] && (x < 39)) {
start = xpixel;
startx = x;
}
x++;
xpixel += 14;
}
y++;
ypixel += 16;
}
// COPY ANY REMAINING DIRTY CELLS FROM THE DEVICE DEPENDENT BITMAP
// ONTO THE SCREEN IN VERTICAL RECTANGLES
if (remainingdirty) {
int x = 0;
int xpixel = 0;
while (x < 40) {
int start = -1;
int y = 0;
int ypixel = 0;
while (y < 24) {
if ((y == 23) && celldirty[x][y]) {
if (start == -1)
start = ypixel;
ypixel += 16;
celldirty[x][y] = 0;
}
if ((start >= 0) && !celldirty[x][y]) {
BitBlt(framedc,xpixel,start,14,ypixel-start,
devicedc,xpixel,start,SRCCOPY);
start = -1;
}
else if ((start == -1) && celldirty[x][y])
start = ypixel;
y++;
ypixel += 16;
}
x++;
xpixel += 14;
}
}
GdiFlush();
#endif
FrameReleaseVideoDC();
SetLastDrawnImage();
redrawfull = 0;
}
//===========================================================================
void VideoReinitialize () {
CreateIdentityPalette();
CreateDIBSections();
}
//===========================================================================
void VideoResetState () {
charoffs = 0;
displaypage2 = 0;
vidmode = VF_TEXT;
redrawfull = 1;
}
//===========================================================================
BYTE __stdcall VideoSetMode (WORD, BYTE address, BYTE write, BYTE, ULONG) {
DWORD oldpage2 = SW_PAGE2;
int oldvalue = charoffs+(int)(vidmode & ~(VF_MASK2 | VF_PAGE2));
switch (address) {
case 0x00: vidmode &= ~VF_MASK2; break;
case 0x01: vidmode |= VF_MASK2; break;
case 0x0C: vidmode &= ~VF_80COL; break;
case 0x0D: vidmode |= VF_80COL; break;
case 0x0E: charoffs = 0; break; // Alternate char set off
case 0x0F: charoffs = 256; break; // Alternate char set on
case 0x50: vidmode &= ~VF_TEXT; break;
case 0x51: vidmode |= VF_TEXT; break;
case 0x52: vidmode &= ~VF_MIXED; break;
case 0x53: vidmode |= VF_MIXED; break;
case 0x54: vidmode &= ~VF_PAGE2; break;
case 0x55: vidmode |= VF_PAGE2; break;
case 0x56: vidmode &= ~VF_HIRES; break;
case 0x57: vidmode |= VF_HIRES; break;
case 0x5E: vidmode |= VF_DHIRES; break;
case 0x5F: vidmode &= ~VF_DHIRES; break;
}
if (SW_MASK2)
vidmode &= ~VF_PAGE2;
if (oldvalue != charoffs+(int)(vidmode & ~(VF_MASK2 | VF_PAGE2))) {
graphicsmode = !SW_TEXT;
redrawfull = 1;
}
if (g_bFullSpeed && oldpage2 && !SW_PAGE2) {
static DWORD lasttime = 0;
DWORD currtime = GetTickCount();
if (currtime-lasttime >= 20)
lasttime = currtime;
else
oldpage2 = SW_PAGE2;
}
if (oldpage2 != SW_PAGE2) {
static DWORD lastrefresh = 0;
BOOL fastvideoslowcpu = 0;
if ((cpuemtype == CPU_FASTPAGING) && (emulmsec-lastrefresh >= 20))
fastvideoslowcpu = 1;
if ((displaypage2 && !SW_PAGE2) || (!behind) || fastvideoslowcpu) {
displaypage2 = (SW_PAGE2 != 0);
if (!redrawfull) {
VideoRefreshScreen();
hasrefreshed = 1;
lastrefresh = emulmsec;
}
}
else if ((!SW_PAGE2) && (!redrawfull) && (emulmsec-lastrefresh >= 20)) {
displaypage2 = 0;
VideoRefreshScreen();
hasrefreshed = 1;
lastrefresh = emulmsec;
}
lastpageflip = emulmsec;
}
if (address == 0x50)
return VideoCheckVbl(0,0,0,0,0);
else
return MemReturnRandomData(1);
}
//===========================================================================
void VideoUpdateVbl (DWORD dwCyclesThisFrame)
{
dwVBlCounter = (DWORD) ((double)dwCyclesThisFrame / (double)uCyclesPerLine);
}
//===========================================================================
// Called at 60Hz (every 16.666ms)
void VideoUpdateFlash()
{
static UINT nTextFlashCnt = 0;
nTextFlashCnt++;
if(nTextFlashCnt == 60/4) // Flash rate = 4Hz (every 250ms)
{
nTextFlashCnt = 0;
g_bTextFlashState = !g_bTextFlashState;
// Redraw any FLASHing chars if any text showing. NB. No FLASH mode for 80 cols
if((SW_TEXT || SW_MIXED) && !SW_80COL)
g_bTextFlashFlag = true;
}
}
//===========================================================================
bool VideoGetSW80COL()
{
return SW_80COL ? true : false;
}
//===========================================================================
DWORD VideoGetSnapshot(SS_IO_Video* pSS)
{
pSS->bAltCharSet = !(charoffs == 0);
pSS->dwVidMode = vidmode;
return 0;
}
DWORD VideoSetSnapshot(SS_IO_Video* pSS)
{
charoffs = !pSS->bAltCharSet ? 0 : 256;
vidmode = pSS->dwVidMode;
//
graphicsmode = !SW_TEXT;
displaypage2 = (SW_PAGE2 != 0);
return 0;
}