emu48-mirror/sources/Emu48/DISPLAY.C

845 lines
20 KiB
C
Raw Normal View History

2024-03-19 22:24:30 +01:00
/*
* display.c
*
* This file is part of Emu48
*
* Copyright (C) 1995 Sebastien Carlier
2024-03-19 22:37:54 +01:00
* Copyright (C) 2002 Christoph Gie<EFBFBD>elink
2024-03-19 22:24:30 +01:00
*
*/
#include "pch.h"
#include "resource.h"
#include "Emu48.h"
2024-03-19 22:36:03 +01:00
#include "io.h"
2024-03-19 22:24:30 +01:00
#include "kml.h"
2024-03-19 22:37:03 +01:00
// #define DEBUG_DISPLAY // switch for DISPLAY debug purpose
2024-03-19 22:37:54 +01:00
#if defined GRAYSCALE
#define NOCOLORS 8
#else
#define NOCOLORS 2
#endif
#define B 0x00000000 // black
#define W 0x00FFFFFF // white
#define I 0xFFFFFFFF // ignore
#define LCD_ROW (36*4) // max. pixel per line
2024-03-19 22:24:30 +01:00
2024-03-19 22:37:03 +01:00
// main display lines, handle zero lines exception
2024-03-19 22:37:54 +01:00
#define LINES(n) (((n) == 0) ? 64 : ((n)+1))
#define GRAYMASK (((((NOCOLORS)-1)>>1)<<24) \
|((((NOCOLORS)-1)>>1)<<16) \
|((((NOCOLORS)-1)>>1)<<8) \
|((((NOCOLORS)-1)>>1)))
#define DIBPIXEL(d,p) *(((DWORD*)(d))++) = ((*((DWORD*)(d)) & GRAYMASK) << 1) | (p)
2024-03-19 22:37:03 +01:00
2024-03-19 22:24:30 +01:00
UINT nBackgroundX = 0;
UINT nBackgroundY = 0;
UINT nBackgroundW = 0;
UINT nBackgroundH = 0;
UINT nLcdX = 0;
UINT nLcdY = 0;
2024-03-19 22:25:45 +01:00
UINT nLcdDoubled = 1;
2024-03-19 22:24:30 +01:00
LPBYTE pbyLcd;
2024-03-19 22:25:45 +01:00
HDC hLcdDC = NULL;
HDC hMainDC = NULL;
2024-03-19 22:37:54 +01:00
2024-03-19 22:24:30 +01:00
static HBITMAP hLcdBitmap;
static HBITMAP hMainBitmap;
static HBITMAP hOldLcdBitmap;
static HBITMAP hOldMainBitmap;
2024-03-19 22:37:54 +01:00
static DWORD Pattern[16];
static DWORD dwKMLColor[64] = // color table loaded by KML script
{
W,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,
B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I
};
2024-03-19 22:24:30 +01:00
static struct
{
BITMAPINFOHEADER Lcd_bmih;
2024-03-19 22:37:54 +01:00
RGBQUAD bmiColors[NOCOLORS];
2024-03-19 22:24:30 +01:00
} bmiLcd =
{
2024-03-19 22:37:54 +01:00
{0x28,0/*x*/,0/*y*/,1,8,BI_RGB,0,0,0,NOCOLORS,0}
2024-03-19 22:24:30 +01:00
};
2024-03-19 22:37:54 +01:00
static __inline VOID BuildPattern(VOID)
2024-03-19 22:24:30 +01:00
{
_ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4);
if (nLcdDoubled == 1)
{
WORD i,j;
for (i=0; i<16; ++i)
{
Pattern[i] = 0;
for (j=8; j>0; j>>=1)
{
2024-03-19 22:37:54 +01:00
Pattern[i] = (Pattern[i] << 8) | ((i&j) != 0);
2024-03-19 22:24:30 +01:00
}
}
2024-03-19 22:37:03 +01:00
return;
2024-03-19 22:24:30 +01:00
}
if (nLcdDoubled == 2)
{
2024-03-19 22:37:54 +01:00
Pattern[0] = 0x00000000;
Pattern[1] = 0x00000101;
Pattern[2] = 0x01010000;
Pattern[3] = 0x01010101;
2024-03-19 22:37:03 +01:00
return;
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:37:54 +01:00
if (nLcdDoubled == 4)
{
Pattern[0] = 0x00000000;
Pattern[1] = 0x01010101;
}
return;
}
2024-03-19 22:24:30 +01:00
2024-03-19 22:37:54 +01:00
VOID UpdateContrast(BYTE byContrast)
{
#if defined GRAYSCALE
INT i;
2024-03-19 22:24:30 +01:00
2024-03-19 22:37:54 +01:00
RGBQUAD c = *(RGBQUAD*)&dwKMLColor[byContrast]; // pixel on color
RGBQUAD b = *(RGBQUAD*)&dwKMLColor[byContrast+32]; // pixel off color
if ((Chipset.IORam[BITOFFSET] & DON) == 0 || I == *(DWORD*)&b)
b = *(RGBQUAD*)&dwKMLColor[0];
// fill color palette of bitmap
for (i = 0; i < ARRAYSIZEOF(bmiLcd.bmiColors); ++i)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:54 +01:00
const INT nCAdj[] = { 0, 1, 1, 2, 1, 2, 2, 3 };
_ASSERT(i < ARRAYSIZEOF(bmiLcd.bmiColors));
_ASSERT(i < ARRAYSIZEOF(nCAdj));
bmiLcd.bmiColors[i] = b;
bmiLcd.bmiColors[i].rgbRed += (((INT) c.rgbRed - (INT) b.rgbRed) * nCAdj[i] / 3);
bmiLcd.bmiColors[i].rgbGreen += (((INT) c.rgbGreen - (INT) b.rgbGreen) * nCAdj[i] / 3);
bmiLcd.bmiColors[i].rgbBlue += (((INT) c.rgbBlue - (INT) b.rgbBlue) * nCAdj[i] / 3);
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:37:54 +01:00
#else
bmiLcd.bmiColors[1] = *(RGBQUAD*)&dwKMLColor[byContrast]; // pixel on color
bmiLcd.bmiColors[0] = *(RGBQUAD*)&dwKMLColor[byContrast+32]; // pixel off color
if ((Chipset.IORam[BITOFFSET] & DON) == 0 || I == *(DWORD*)&bmiLcd.bmiColors[0])
bmiLcd.bmiColors[0] = *(RGBQUAD*)&dwKMLColor[0];
#endif
// update palette information
_ASSERT(hLcdDC);
SetDIBColorTable(hLcdDC,0,ARRAYSIZEOF(bmiLcd.bmiColors),bmiLcd.bmiColors);
2024-03-19 22:24:30 +01:00
return;
}
VOID SetLcdColor(UINT nId, UINT nRed, UINT nGreen, UINT nBlue)
{
2024-03-19 22:37:54 +01:00
dwKMLColor[nId&0x3F] = ((nRed&0xFF)<<16)|((nGreen&0xFF)<<8)|(nBlue&0xFF);
2024-03-19 22:24:30 +01:00
return;
}
2024-03-19 22:37:03 +01:00
VOID CreateLcdBitmap(VOID)
2024-03-19 22:24:30 +01:00
{
// create LCD bitmap
_ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4);
2024-03-19 22:37:54 +01:00
bmiLcd.Lcd_bmih.biWidth = LCD_ROW * nLcdDoubled;
2024-03-19 22:24:30 +01:00
bmiLcd.Lcd_bmih.biHeight = -64 * nLcdDoubled;
2024-03-19 22:37:54 +01:00
_ASSERT(hLcdDC == NULL);
2024-03-19 22:24:30 +01:00
hLcdDC = CreateCompatibleDC(hWindowDC);
2024-03-19 22:36:03 +01:00
_ASSERT(hLcdDC != NULL);
2024-03-19 22:24:30 +01:00
hLcdBitmap = CreateDIBSection(hLcdDC, (BITMAPINFO*)&bmiLcd,DIB_RGB_COLORS, (LPVOID*)&pbyLcd, NULL, 0);
2024-03-19 22:36:03 +01:00
_ASSERT(hLcdBitmap != NULL);
2024-03-19 22:24:30 +01:00
hOldLcdBitmap = SelectObject(hLcdDC, hLcdBitmap);
2024-03-19 22:36:03 +01:00
_ASSERT(hPalette != NULL);
SelectPalette(hLcdDC, hPalette, FALSE); // set palette for LCD DC
RealizePalette(hLcdDC); // realize palette
2024-03-19 22:37:54 +01:00
BuildPattern(); // build Nibble -> DIB mask pattern
2024-03-19 22:25:45 +01:00
UpdateContrast(Chipset.contrast);
2024-03-19 22:37:54 +01:00
return;
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:37:03 +01:00
VOID DestroyLcdBitmap(VOID)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:54 +01:00
// set contrast palette to startup colors
WORD i = 0; dwKMLColor[i++] = W;
while(i < 32) dwKMLColor[i++] = B;
while(i < 64) dwKMLColor[i++] = I;
2024-03-19 22:24:30 +01:00
if (hLcdDC != NULL)
{
// destroy LCD bitmap
SelectObject(hLcdDC, hOldLcdBitmap);
DeleteObject(hLcdBitmap);
DeleteDC(hLcdDC);
hLcdDC = NULL;
hLcdBitmap = NULL;
hOldLcdBitmap = NULL;
}
return;
}
2024-03-19 22:37:03 +01:00
BOOL CreateMainBitmap(LPCTSTR szFilename)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:36:03 +01:00
HPALETTE hAssertPalette;
_ASSERT(hWindowDC != NULL);
2024-03-19 22:24:30 +01:00
hMainDC = CreateCompatibleDC(hWindowDC);
2024-03-19 22:36:03 +01:00
_ASSERT(hMainDC != NULL);
if (hMainDC == NULL) return FALSE; // quit if failed
2024-03-19 22:24:30 +01:00
hMainBitmap = LoadBitmapFile(szFilename);
if (hMainBitmap == NULL)
{
DeleteDC(hMainDC);
return FALSE;
}
hOldMainBitmap = SelectObject(hMainDC, hMainBitmap);
2024-03-19 22:36:03 +01:00
_ASSERT(hPalette != NULL);
hAssertPalette = SelectPalette(hMainDC, hPalette, FALSE);
_ASSERT(hAssertPalette != NULL);
RealizePalette(hMainDC);
2024-03-19 22:24:30 +01:00
return TRUE;
}
2024-03-19 22:37:03 +01:00
VOID DestroyMainBitmap(VOID)
2024-03-19 22:24:30 +01:00
{
if (hMainDC != NULL)
{
// destroy Main bitmap
SelectObject(hMainDC, hOldMainBitmap);
DeleteObject(hMainBitmap);
DeleteDC(hMainDC);
hMainDC = NULL;
hMainBitmap = NULL;
hOldMainBitmap = NULL;
}
return;
}
//****************
//*
//* LCD functions
//*
//****************
2024-03-19 22:37:03 +01:00
VOID UpdateDisplayPointers(VOID)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:54 +01:00
GRAYON(EnterCriticalSection(&csLcdLock));
2024-03-19 22:37:03 +01:00
{
2024-03-19 22:37:54 +01:00
UINT nLines = LINES(Chipset.lcounter);
2024-03-19 22:37:03 +01:00
2024-03-19 22:37:54 +01:00
#if defined DEBUG_DISPLAY
{
TCHAR buffer[256];
wsprintf(buffer,_T("%.5lx: Update Display Pointer\n"),Chipset.pc);
OutputDebugString(buffer);
}
#endif
// calculate display width
Chipset.width = (34 + Chipset.loffset + (Chipset.boffset / 4) * 2) & 0xFFFFFFFE;
Chipset.end1 = Chipset.start1 + nLines * Chipset.width;
if (Chipset.end1 < Chipset.start1)
{
// calculate first address of main display
Chipset.start12 = Chipset.end1 - Chipset.width;
// calculate last address of main display
Chipset.end1 = Chipset.start1 - Chipset.width;
}
else
{
Chipset.start12 = Chipset.start1;
}
Chipset.end2 = Chipset.start2 + (64 - nLines) * 34;
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:37:54 +01:00
GRAYON(LeaveCriticalSection(&csLcdLock));
return;
2024-03-19 22:24:30 +01:00
}
static BYTE Buf[36];
2024-03-19 22:37:03 +01:00
VOID UpdateMainDisplay(VOID)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:03 +01:00
UINT x, y, nLines;
2024-03-19 22:24:30 +01:00
DWORD d = Chipset.start1;
BYTE *p = pbyLcd;
2024-03-19 22:37:03 +01:00
#if defined DEBUG_DISPLAY
{
TCHAR buffer[256];
wsprintf(buffer,_T("%.5lx: Update Main Display\n"),Chipset.pc);
OutputDebugString(buffer);
}
#endif
_ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4);
2024-03-19 22:37:54 +01:00
if (!(Chipset.IORam[BITOFFSET]&DON))
2024-03-19 22:24:30 +01:00
{
nLines = 64;
2024-03-19 22:37:54 +01:00
ZeroMemory(pbyLcd, LCD_ROW * nLcdDoubled * nLines * nLcdDoubled);
2024-03-19 22:24:30 +01:00
}
else
{
2024-03-19 22:37:03 +01:00
nLines = LINES(Chipset.lcounter); // main display lines
2024-03-19 22:24:30 +01:00
if (nLcdDoubled == 4)
{
2024-03-19 22:37:03 +01:00
for (y = 0; y < nLines; ++y)
2024-03-19 22:24:30 +01:00
{
Npeek(Buf,d,36);
for (x=0; x<36; x++)
{
2024-03-19 22:37:54 +01:00
DIBPIXEL(p,Pattern[Buf[x]&1]);
DIBPIXEL(p,Pattern[(Buf[x]>>1) & 1]);
DIBPIXEL(p,Pattern[(Buf[x]>>2) & 1]);
DIBPIXEL(p,Pattern[(Buf[x]>>3) & 1]);
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:37:54 +01:00
CopyMemory(p, p-LCD_ROW*4, LCD_ROW*4);
p+=LCD_ROW*4;
CopyMemory(p, p-LCD_ROW*8, LCD_ROW*8);
p+=LCD_ROW*8;
2024-03-19 22:24:30 +01:00
d+=Chipset.width;
}
}
2024-03-19 22:25:45 +01:00
if (nLcdDoubled == 2)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:03 +01:00
for (y = 0; y < nLines; ++y)
2024-03-19 22:24:30 +01:00
{
Npeek(Buf,d,36);
for (x=0; x<36; x++)
{
2024-03-19 22:37:54 +01:00
DIBPIXEL(p,Pattern[Buf[x]&3]);
DIBPIXEL(p,Pattern[Buf[x]>>2]);
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:37:54 +01:00
CopyMemory(p, p-LCD_ROW*2, LCD_ROW*2);
p+=LCD_ROW*2;
2024-03-19 22:24:30 +01:00
d+=Chipset.width;
}
}
2024-03-19 22:25:45 +01:00
if (nLcdDoubled == 1)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:03 +01:00
for (y = 0; y < nLines; ++y)
2024-03-19 22:24:30 +01:00
{
Npeek(Buf,d,36);
2024-03-19 22:37:54 +01:00
for (x=0; x<36; x++) // every 4 pixel
{
DIBPIXEL(p,Pattern[Buf[x]]);
}
2024-03-19 22:24:30 +01:00
d+=Chipset.width;
}
}
}
2024-03-19 22:25:45 +01:00
EnterCriticalSection(&csGDILock); // solving NT GDI problems
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:03 +01:00
BitBlt(hWindowDC, nLcdX, nLcdY, 131*nLcdDoubled, nLines*nLcdDoubled,
hLcdDC, Chipset.boffset*nLcdDoubled, 0, SRCCOPY);
2024-03-19 22:25:45 +01:00
GdiFlush();
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
LeaveCriticalSection(&csGDILock);
2024-03-19 22:24:30 +01:00
return;
}
2024-03-19 22:37:03 +01:00
VOID UpdateMenuDisplay(VOID)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:03 +01:00
UINT x, y, nLines;
2024-03-19 22:24:30 +01:00
BYTE *p;
DWORD d = Chipset.start2;
2024-03-19 22:37:03 +01:00
#if defined DEBUG_DISPLAY
{
TCHAR buffer[256];
wsprintf(buffer,_T("%.5lx: Update Menu Display\n"),Chipset.pc);
OutputDebugString(buffer);
}
#endif
nLines = LINES(Chipset.lcounter);
2024-03-19 22:37:54 +01:00
if (!(Chipset.IORam[BITOFFSET]&DON)) return;
2024-03-19 22:37:03 +01:00
if (nLines == 64) return; // menu disabled
2024-03-19 22:24:30 +01:00
_ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4);
2024-03-19 22:37:03 +01:00
// calculate bitmap offset
2024-03-19 22:37:54 +01:00
p = pbyLcd + (nLines*nLcdDoubled*LCD_ROW*nLcdDoubled);
2024-03-19 22:24:30 +01:00
if (nLcdDoubled == 4)
{
2024-03-19 22:37:03 +01:00
for (y = nLines; y < 64; ++y)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
Npeek(Buf,d,34); // 34 nibbles are viewed
2024-03-19 22:37:03 +01:00
for (x = 0; x < 36; ++x)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:54 +01:00
DIBPIXEL(p,Pattern[Buf[x]&1]);
DIBPIXEL(p,Pattern[(Buf[x]>>1) & 1]);
DIBPIXEL(p,Pattern[(Buf[x]>>2) & 1]);
DIBPIXEL(p,Pattern[(Buf[x]>>3) & 1]);
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:37:54 +01:00
CopyMemory(p, p-LCD_ROW*4, LCD_ROW*4);
p+=LCD_ROW*4;
CopyMemory(p, p-LCD_ROW*8, LCD_ROW*8);
p+=LCD_ROW*8;
2024-03-19 22:24:30 +01:00
d+=34;
}
}
2024-03-19 22:25:45 +01:00
if (nLcdDoubled == 2)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:03 +01:00
for (y = nLines; y < 64; ++y)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
Npeek(Buf,d,34); // 34 nibbles are viewed
2024-03-19 22:37:03 +01:00
for (x = 0; x < 36; ++x)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:54 +01:00
DIBPIXEL(p,Pattern[Buf[x]&3]);
DIBPIXEL(p,Pattern[Buf[x]>>2]);
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:37:54 +01:00
CopyMemory(p, p-LCD_ROW*2, LCD_ROW*2);
p+=LCD_ROW*2;
2024-03-19 22:24:30 +01:00
d+=34;
}
}
2024-03-19 22:25:45 +01:00
if (nLcdDoubled == 1)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:03 +01:00
for (y = nLines; y < 64; ++y)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
Npeek(Buf,d,34); // 34 nibbles are viewed
2024-03-19 22:37:54 +01:00
for (x=0; x<36; x++) // every 4 pixel
{
DIBPIXEL(p,Pattern[Buf[x]]);
}
2024-03-19 22:24:30 +01:00
d+=34;
}
}
2024-03-19 22:25:45 +01:00
EnterCriticalSection(&csGDILock); // solving NT GDI problems
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:03 +01:00
BitBlt(hWindowDC, nLcdX, nLcdY+nLines*nLcdDoubled,
131*nLcdDoubled, (64-nLines)*nLcdDoubled,
hLcdDC, 0, nLines*nLcdDoubled, SRCCOPY);
2024-03-19 22:25:45 +01:00
GdiFlush();
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
LeaveCriticalSection(&csGDILock);
2024-03-19 22:24:30 +01:00
return;
}
VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s)
{
2024-03-19 22:37:54 +01:00
#if !defined GRAYSCALE
2024-03-19 22:37:03 +01:00
INT x0, x;
INT y0, y;
DWORD *p;
INT lWidth = abs(Chipset.width); // display width
UINT nLines = LINES(Chipset.lcounter); // main display lines
2024-03-19 22:24:30 +01:00
2024-03-19 22:37:03 +01:00
#if defined DEBUG_DISPLAY
{
TCHAR buffer[256];
wsprintf(buffer,_T("%.5lx: Write Main Display %x,%u\n"),Chipset.pc,d,s);
OutputDebugString(buffer);
}
#endif
2024-03-19 22:24:30 +01:00
d -= Chipset.start1; // nibble offset to DISPADDR (start of display)
d += 64 * lWidth; // make positive offset
y0 = abs((INT) d / lWidth - 64); // bitmap row
x0 = (INT) d % lWidth; // bitmap coloumn
y = y0; x = x0; // load loop variables
// outside main display area
2024-03-19 22:37:03 +01:00
_ASSERT(y0 >= 0 && y0 < (INT) nLines);
2024-03-19 22:24:30 +01:00
// illegal zoom factor
_ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4);
// calculate memory position in LCD bitmap
2024-03-19 22:37:54 +01:00
p = (DWORD*) (pbyLcd + y0*LCD_ROW*nLcdDoubled*nLcdDoubled
+ x0*sizeof(*p)*nLcdDoubled);
2024-03-19 22:24:30 +01:00
while (s--) // loop for nibbles to write
{
if (x<36) // only fill visible area
{
if (nLcdDoubled == 4)
{
p[432] = p[288] = p[144] = p[0] = Pattern[(*a)&1];
p[433] = p[289] = p[145] = p[1] = Pattern[((*a)>>1) &1];
p[434] = p[290] = p[146] = p[2] = Pattern[((*a)>>2) &1];
p[435] = p[291] = p[147] = p[3] = Pattern[((*a)>>3) &1];
}
if (nLcdDoubled == 2)
{
p[72] = p[0] = Pattern[(*a)&3];
p[73] = p[1] = Pattern[(*a)>>2];
}
if (nLcdDoubled == 1)
{
*p = Pattern[*a];
}
}
++a; // next value to write
++x; // next x position
if ((x==lWidth)&&s) // end of display line
{
// end of main display area
2024-03-19 22:37:03 +01:00
if (y == (INT) nLines - 1) break;
2024-03-19 22:24:30 +01:00
x = 0; // first coloumn
++y; // next row
// recalculate bitmap memory position of new line
2024-03-19 22:37:54 +01:00
p = (DWORD*) (pbyLcd+y*LCD_ROW*nLcdDoubled*nLcdDoubled);
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:37:54 +01:00
else
2024-03-19 22:24:30 +01:00
p += nLcdDoubled; // next x position in bitmap
}
// update window region
if (y0 != y) // changed more than one line
{
x0 = 0; // no x-position offset
x = 131; // redraw complete lines
++y; // redraw this line as well
}
else
{
x0 <<= 2; x <<= 2; // x-position in pixel
2024-03-19 22:37:54 +01:00
_ASSERT(x >= x0); // can't draw negative number of pixel
2024-03-19 22:24:30 +01:00
x -= x0; // number of pixels to update
x0 -= Chipset.boffset; // adjust x-position with left margin
if (x0 < 0) x0 = 0;
2024-03-19 22:37:54 +01:00
2024-03-19 22:24:30 +01:00
if (x0 > 131) x0 = 131; // cut right borders
if (x+x0 > 131) x = 131 - x0;
y = y0 + 1; // draw one line
}
x0 <<= nLcdDoubled / 2; // adjust dimensions to pixel size
x <<= nLcdDoubled / 2;
y0 <<= nLcdDoubled / 2;
y <<= nLcdDoubled / 2;
EnterCriticalSection(&csGDILock);
{
BitBlt(hWindowDC, nLcdX+x0, nLcdY+y0, x, y-y0, hLcdDC, x0+Chipset.boffset*nLcdDoubled, y0, SRCCOPY);
GdiFlush();
}
LeaveCriticalSection(&csGDILock);
2024-03-19 22:37:54 +01:00
#endif
2024-03-19 22:24:30 +01:00
return;
}
VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s)
{
2024-03-19 22:37:54 +01:00
#if !defined GRAYSCALE
2024-03-19 22:24:30 +01:00
UINT x0, x;
UINT y0, y;
DWORD *p;
2024-03-19 22:37:03 +01:00
UINT nLines = LINES(Chipset.lcounter); // main display lines
#if defined DEBUG_DISPLAY
{
TCHAR buffer[256];
wsprintf(buffer,_T("%.5lx: Write Menu Display %x,%u\n"),Chipset.pc,d,s);
OutputDebugString(buffer);
}
#endif
if (nLines == 64) return; // menu disabled
2024-03-19 22:24:30 +01:00
d -= Chipset.start2;
2024-03-19 22:37:03 +01:00
y0 = y = (d / 34) + nLines;
2024-03-19 22:24:30 +01:00
x0 = x = d % 34;
2024-03-19 22:25:45 +01:00
if (x0 > 32) return; // position out of viewed area
2024-03-19 22:24:30 +01:00
_ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4);
if (nLcdDoubled == 4)
{
2024-03-19 22:37:54 +01:00
p = (DWORD*)(pbyLcd + y0*LCD_ROW*16 + x0*16);
2024-03-19 22:24:30 +01:00
while (s--)
{
if (x<34)
{
p[432] = p[288] = p[144] = p[0] = Pattern[(*a)&1];
p[433] = p[289] = p[145] = p[1] = Pattern[((*a)>>1) &1];
p[434] = p[290] = p[146] = p[2] = Pattern[((*a)>>2) &1];
p[435] = p[291] = p[147] = p[3] = Pattern[((*a)>>3) &1];
}
a++;
x++;
if ((x==34)&&s)
{
x=0;
y++;
if (y==64) break;
2024-03-19 22:37:54 +01:00
p=(DWORD*)(pbyLcd+y*LCD_ROW*16);
2024-03-19 22:24:30 +01:00
} else p+=4;
}
2024-03-19 22:25:45 +01:00
EnterCriticalSection(&csGDILock); // solving NT GDI problems
2024-03-19 22:24:30 +01:00
{
if (y0!=y)
{
y0<<=2; y<<=2;
BitBlt(hWindowDC, nLcdX, nLcdY+y0, 524, y-y0+4, hLcdDC, 0, y0, SRCCOPY);
}
else
{
x0<<=4; x<<=4;
y0<<=2; y<<=2;
if (x>524) x=524;
BitBlt(hWindowDC, nLcdX+x0, nLcdY+y0, x-x0, y-y0+4, hLcdDC, x0, y0, SRCCOPY);
}
2024-03-19 22:25:45 +01:00
GdiFlush();
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
LeaveCriticalSection(&csGDILock);
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
if (nLcdDoubled == 2)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:54 +01:00
p = (DWORD*)(pbyLcd + y0*LCD_ROW*4 + x0*8);
2024-03-19 22:24:30 +01:00
while (s--)
{
if (x<34)
{
p[72] = p[0] = Pattern[(*a)&3];
p[73] = p[1] = Pattern[(*a)>>2];
}
a++;
x++;
if ((x==34)&&s)
{
x=0;
y++;
if (y==64) break;
2024-03-19 22:37:54 +01:00
p=(DWORD*)(pbyLcd+y*LCD_ROW*4);
2024-03-19 22:24:30 +01:00
} else p+=2;
}
2024-03-19 22:25:45 +01:00
EnterCriticalSection(&csGDILock); // solving NT GDI problems
2024-03-19 22:24:30 +01:00
{
if (y0!=y)
{
y0<<=1; y<<=1;
BitBlt(hWindowDC, nLcdX, nLcdY+y0, 262, y-y0+2, hLcdDC, 0, y0, SRCCOPY);
}
else
{
x0<<=3; x<<=3;
y0<<=1; y<<=1;
if (x>262) x=262;
BitBlt(hWindowDC, nLcdX+x0, nLcdY+y0, x-x0, y-y0+2, hLcdDC, x0, y0, SRCCOPY);
}
2024-03-19 22:25:45 +01:00
GdiFlush();
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
LeaveCriticalSection(&csGDILock);
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
if (nLcdDoubled == 1)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:37:54 +01:00
p = (DWORD*)(pbyLcd + y0*LCD_ROW + x0*4);
2024-03-19 22:24:30 +01:00
while (s--)
{
if (x<34) *p = Pattern[*a];
a++;
x++;
if ((x==34)&&s)
{
x=0;
y++;
if (y==64) break;
2024-03-19 22:37:54 +01:00
p=(DWORD*)(pbyLcd+y*LCD_ROW);
2024-03-19 22:24:30 +01:00
} else p++;
}
2024-03-19 22:25:45 +01:00
EnterCriticalSection(&csGDILock); // solving NT GDI problems
2024-03-19 22:24:30 +01:00
{
if (y0!=y)
{
BitBlt(hWindowDC, nLcdX, nLcdY+y0, 131, y-y0+1, hLcdDC, 0, y0, SRCCOPY);
}
else
{
x0<<=2; x<<=2;
if (x>131) x=131;
BitBlt(hWindowDC, nLcdX+x0, nLcdY+y0, x-x0, y-y0+1, hLcdDC, x0, y0, SRCCOPY);
}
2024-03-19 22:25:45 +01:00
GdiFlush();
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
LeaveCriticalSection(&csGDILock);
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:37:54 +01:00
#endif
2024-03-19 22:24:30 +01:00
return;
}
2024-03-19 22:37:03 +01:00
VOID UpdateAnnunciators(VOID)
2024-03-19 22:24:30 +01:00
{
BYTE c;
2024-03-19 22:37:54 +01:00
2024-03-19 22:36:03 +01:00
c = (BYTE)(Chipset.IORam[ANNCTRL] | (Chipset.IORam[ANNCTRL+1]<<4));
// switch annunciators off if timer stopped
if ((c & AON) == 0 || (Chipset.IORam[TIMER2_CTRL] & RUN) == 0)
2024-03-19 22:37:03 +01:00
c = 0;
2024-03-19 22:36:03 +01:00
DrawAnnunciator(1,c&LA1);
DrawAnnunciator(2,c&LA2);
DrawAnnunciator(3,c&LA3);
DrawAnnunciator(4,c&LA4);
DrawAnnunciator(5,c&LA5);
DrawAnnunciator(6,c&LA6);
2024-03-19 22:24:30 +01:00
return;
}
2024-03-19 22:37:03 +01:00
VOID ResizeWindow(VOID)
2024-03-19 22:24:30 +01:00
{
RECT rectWindow;
RECT rectClient;
2024-03-19 22:25:45 +01:00
if (hWnd == NULL) return; // return if window closed
2024-03-19 22:24:30 +01:00
rectWindow.left = 0;
rectWindow.top = 0;
rectWindow.right = nBackgroundW;
rectWindow.bottom = nBackgroundH;
AdjustWindowRect(&rectWindow, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, TRUE);
SetWindowPos (hWnd, (HWND)NULL, 0, 0,
rectWindow.right - rectWindow.left,
rectWindow.bottom - rectWindow.top,
SWP_NOMOVE | SWP_NOZORDER);
GetClientRect(hWnd, &rectClient);
AdjustWindowRect(&rectClient, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, TRUE);
if (rectClient.bottom < rectWindow.bottom)
{
rectWindow.bottom += (rectWindow.bottom - rectClient.bottom);
SetWindowPos (hWnd, (HWND)NULL, 0, 0,
rectWindow.right - rectWindow.left,
rectWindow.bottom - rectWindow.top,
SWP_NOMOVE | SWP_NOZORDER);
}
2024-03-19 22:37:54 +01:00
_ASSERT(hWindowDC); // move destination window
SetWindowOrgEx(hWindowDC, nBackgroundX, nBackgroundY, NULL);
2024-03-19 22:24:30 +01:00
InvalidateRect(hWnd,NULL,TRUE);
return;
2024-03-19 22:37:54 +01:00
}
#if defined GRAYSCALE
#define DISPLAY_FREQ 19 // display update 1/frequency (1/64) in ms
static LARGE_INTEGER lLcdRef; // reference time for VBL counter
static UINT uLcdTimerId = 0;
static VOID CALLBACK LcdProc(UINT uEventId, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
EnterCriticalSection(&csLcdLock);
{
UpdateMainDisplay(); // update display
UpdateMenuDisplay();
}
LeaveCriticalSection(&csLcdLock);
QueryPerformanceCounter(&lLcdRef); // actual time
return;
UNREFERENCED_PARAMETER(uEventId);
UNREFERENCED_PARAMETER(uMsg);
UNREFERENCED_PARAMETER(dwUser);
UNREFERENCED_PARAMETER(dw1);
UNREFERENCED_PARAMETER(dw2);
}
// LCD line counter calculation
BYTE GetLineCounter(VOID)
{
LARGE_INTEGER lLC;
BYTE byTime;
if (uLcdTimerId == 0) // display off
return ((Chipset.IORam[LINECOUNT+1] & (LC5|LC4)) << 4) | Chipset.IORam[LINECOUNT];
QueryPerformanceCounter(&lLC); // get elapsed time since display update
// elapsed ticks so far
byTime = (BYTE) (((lLC.QuadPart - lLcdRef.QuadPart) << 12) / lFreq.QuadPart);
if (byTime > 0x3F) byTime = 0x3F; // all counts made
return (0x7E - byTime) & 0x3F; // update display between VBL counter 0x3F-0x3E
}
VOID StartDisplay(BYTE byInitial)
{
if (uLcdTimerId) // LCD update timer running
return; // -> quit
if (Chipset.IORam[BITOFFSET]&DON) // display on?
{
QueryPerformanceCounter(&lLcdRef); // actual time of top line
// adjust startup counter to get the right VBL value
lLcdRef.QuadPart -= ((LONGLONG) ((0x7E - byInitial) & 0x3F) * lFreq.QuadPart) >> 12;
uLcdTimerId = timeSetEvent(DISPLAY_FREQ,0,(LPTIMECALLBACK)&LcdProc,0,TIME_PERIODIC);
_ASSERT(uLcdTimerId); // test if display update timer started
}
return;
}
VOID StopDisplay(VOID)
{
BYTE a[2];
ReadIO(a,LINECOUNT,2); // update VBL at display off time
if (uLcdTimerId == 0) // timer stopped
return; // -> quit
timeKillEvent(uLcdTimerId); // stop display update
uLcdTimerId = 0; // set flag display update stopped
EnterCriticalSection(&csLcdLock); // update to last condition
{
UpdateMainDisplay(); // update display
UpdateMenuDisplay();
}
LeaveCriticalSection(&csLcdLock);
return;
}
#else
static BYTE byVblRef = 0; // VBL stop reference
// LCD line counter calculation
static BYTE F4096Hz(VOID) // get a 6 bit 4096Hz down counter value
{
LARGE_INTEGER lLC;
QueryPerformanceCounter(&lLC); // get counter value
// calculate 4096 Hz frequency down counter value
return -(BYTE)(((lLC.QuadPart - lAppStart.QuadPart) << 12) / lFreq.QuadPart) & 0x3F;
}
BYTE GetLineCounter(VOID) // get line counter value
{
_ASSERT(byVblRef < 0x40);
return (0x40 + F4096Hz() - byVblRef) & 0x3F;
}
VOID StartDisplay(BYTE byInitial)
{
// get positive VBL difference between now and stop time
byVblRef = (0x40 + F4096Hz() - byInitial) & 0x3F;
return;
}
VOID StopDisplay(VOID)
{
BYTE a[2];
ReadIO(a,LINECOUNT,2); // update VBL at display off time
return;
}
#endif