/* * display.c * * This file is part of Emu48 * * Copyright (C) 1995 Sebastien Carlier * */ #include "pch.h" #include "resource.h" #include "Emu48.h" #include "io.h" #include "kml.h" #define LCD1_ROW 144 #define LCD2_ROW 288 #define LCD3_ROW 576 UINT nBackgroundX = 0; UINT nBackgroundY = 0; UINT nBackgroundW = 0; UINT nBackgroundH = 0; UINT nLcdX = 0; UINT nLcdY = 0; UINT nLcdDoubled = 1; LPBYTE pbyLcd; HDC hLcdDC = NULL; HDC hMainDC = NULL; static HBITMAP hLcdBitmap; static HBITMAP hMainBitmap; static HBITMAP hOldLcdBitmap; static HBITMAP hOldMainBitmap; #define B 0x00FFFFFF #define W 0x00000000 #define I 0xFFFFFFFF static struct { BITMAPINFOHEADER Lcd_bmih; DWORD dwColor[64]; } bmiLcd = { {0x28,0/*x*/,0/*y*/,1,8,BI_RGB,0,0,0,64,0}, { 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 } }; #undef B #undef W #undef I static DWORD Pattern[16]; VOID UpdateContrast(BYTE byContrast) { DWORD c = byContrast; DWORD b = byContrast + 0x20; if (bmiLcd.dwColor[b] == 0xFFFFFFFF) b = 0; _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) { Pattern[i] = (Pattern[i] << 8) | ((i&j) ? c : b); } } } c = (c<<8) | c; b = (b<<8) | b; if (nLcdDoubled == 2) { Pattern[0] = (b<<16)|b; Pattern[1] = (b<<16)|c; Pattern[2] = (c<<16)|b; Pattern[3] = (c<<16)|c; } c = (c<<16) | c; b = (b<<16) | b; if (nLcdDoubled == 4) { Pattern[0] = b; Pattern[1] = c; } return; } VOID SetLcdColor(UINT nId, UINT nRed, UINT nGreen, UINT nBlue) { bmiLcd.dwColor[nId&0x3F] = ((nRed&0xFF)<<16)|((nGreen&0xFF)<<8)|(nBlue&0xFF); return; } VOID CreateLcdBitmap() { // create LCD bitmap _ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4); bmiLcd.Lcd_bmih.biWidth = LCD1_ROW * nLcdDoubled; bmiLcd.Lcd_bmih.biHeight = -64 * nLcdDoubled; hLcdDC = CreateCompatibleDC(hWindowDC); _ASSERT(hLcdDC != NULL); hLcdBitmap = CreateDIBSection(hLcdDC, (BITMAPINFO*)&bmiLcd,DIB_RGB_COLORS, (LPVOID*)&pbyLcd, NULL, 0); _ASSERT(hLcdBitmap != NULL); hOldLcdBitmap = SelectObject(hLcdDC, hLcdBitmap); _ASSERT(hPalette != NULL); SelectPalette(hLcdDC, hPalette, FALSE); // set palette for LCD DC RealizePalette(hLcdDC); // realize palette UpdateContrast(Chipset.contrast); } VOID DestroyLcdBitmap() { WORD i; // clear background colors for (i=32; i< 64; ++i) bmiLcd.dwColor[i] = 0xFFFFFFFF; if (hLcdDC != NULL) { // destroy LCD bitmap SelectObject(hLcdDC, hOldLcdBitmap); DeleteObject(hLcdBitmap); DeleteDC(hLcdDC); hLcdDC = NULL; hLcdBitmap = NULL; hOldLcdBitmap = NULL; } return; } BOOL CreateMainBitmap(LPSTR szFilename) { HPALETTE hAssertPalette; _ASSERT(hWindowDC != NULL); hMainDC = CreateCompatibleDC(hWindowDC); _ASSERT(hMainDC != NULL); if (hMainDC == NULL) return FALSE; // quit if failed hMainBitmap = LoadBitmapFile(szFilename); if (hMainBitmap == NULL) { DeleteDC(hMainDC); return FALSE; } hOldMainBitmap = SelectObject(hMainDC, hMainBitmap); _ASSERT(hPalette != NULL); hAssertPalette = SelectPalette(hMainDC, hPalette, FALSE); _ASSERT(hAssertPalette != NULL); RealizePalette(hMainDC); return TRUE; } VOID DestroyMainBitmap() { if (hMainDC != NULL) { // destroy Main bitmap SelectObject(hMainDC, hOldMainBitmap); DeleteObject(hMainBitmap); DeleteDC(hMainDC); hMainDC = NULL; hMainBitmap = NULL; hOldMainBitmap = NULL; } return; } //**************** //* //* LCD functions //* //**************** VOID UpdateDisplayPointers() { // calculate display width Chipset.width = (34 + Chipset.loffset + (Chipset.boffset / 4) * 2) & 0xFFFFFFFE; Chipset.end1 = Chipset.start1 + (Chipset.lcounter + 1) * 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 + (63 - Chipset.lcounter) * 34; } static BYTE Buf[36]; static BOOL bScreenIsClean = FALSE; VOID UpdateMainDisplay() { UINT x, y; INT nLines; DWORD d = Chipset.start1; BYTE *p = pbyLcd; if (!Chipset.dispon) { nLines = 64; if (!bScreenIsClean) { bScreenIsClean = TRUE; ZeroMemory(pbyLcd, LCD1_ROW * nLcdDoubled * nLines * nLcdDoubled); } } else { nLines = Chipset.lcounter + 1; bScreenIsClean = FALSE; _ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4); if (nLcdDoubled == 4) { for (y=0; y<=Chipset.lcounter; y++) { Npeek(Buf,d,36); for (x=0; x<36; x++) { *(((DWORD*)p)++)=Pattern[Buf[x]&1]; *(((DWORD*)p)++)=Pattern[(Buf[x]>>1) & 1]; *(((DWORD*)p)++)=Pattern[(Buf[x]>>2) & 1]; *(((DWORD*)p)++)=Pattern[(Buf[x]>>3) & 1]; } CopyMemory(p, p-LCD3_ROW, LCD3_ROW); p+=LCD3_ROW; CopyMemory(p, p-LCD3_ROW*2, LCD3_ROW*2); p+=LCD3_ROW*2; d+=Chipset.width; } } if (nLcdDoubled == 2) { for (y=0; y<=Chipset.lcounter; y++) { Npeek(Buf,d,36); for (x=0; x<36; x++) { *(((DWORD*)p)++)=Pattern[Buf[x]&3]; *(((DWORD*)p)++)=Pattern[Buf[x]>>2]; } CopyMemory(p, p-LCD2_ROW, LCD2_ROW); p+=LCD2_ROW; d+=Chipset.width; } } if (nLcdDoubled == 1) { for (y=0; y<=Chipset.lcounter; y++) { Npeek(Buf,d,36); for (x=0; x<36; x++) *(((DWORD*)p)++)=Pattern[Buf[x]]; d+=Chipset.width; } } } EnterCriticalSection(&csGDILock); // solving NT GDI problems { BitBlt(hWindowDC, nLcdX, nLcdY, 131*nLcdDoubled, nLines*nLcdDoubled, hLcdDC, Chipset.boffset*nLcdDoubled, 0, SRCCOPY); GdiFlush(); } LeaveCriticalSection(&csGDILock); return; } VOID UpdateMenuDisplay() { UINT x, y; BYTE *p; DWORD d = Chipset.start2; if (!Chipset.dispon) return; if (Chipset.lcounter==0x3F) return; // menu disabled _ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4); // calculate offset once p = pbyLcd + ((Chipset.lcounter+1)*nLcdDoubled*LCD1_ROW*nLcdDoubled); if (nLcdDoubled == 4) { for (y=Chipset.lcounter+1; y<64; y++) { Npeek(Buf,d,34); // 34 nibbles are viewed for (x=0; x<36; x++) { *(((DWORD*)p)++)=Pattern[Buf[x]&1]; *(((DWORD*)p)++)=Pattern[(Buf[x]>>1) & 1]; *(((DWORD*)p)++)=Pattern[(Buf[x]>>2) & 1]; *(((DWORD*)p)++)=Pattern[(Buf[x]>>3) & 1]; } CopyMemory(p, p-LCD3_ROW, LCD3_ROW); p+=LCD3_ROW; CopyMemory(p, p-LCD3_ROW*2, LCD3_ROW*2); p+=LCD3_ROW*2; d+=34; } } if (nLcdDoubled == 2) { for (y=Chipset.lcounter+1; y<64; y++) { Npeek(Buf,d,34); // 34 nibbles are viewed for (x=0; x<36; x++) { *(((DWORD*)p)++)=Pattern[Buf[x]&3]; *(((DWORD*)p)++)=Pattern[Buf[x]>>2]; } CopyMemory(p, p-LCD2_ROW, LCD2_ROW); p+=LCD2_ROW; d+=34; } } if (nLcdDoubled == 1) { for (y=Chipset.lcounter+1; y<64; y++) { Npeek(Buf,d,34); // 34 nibbles are viewed for (x=0; x<36; x++) *(((DWORD*)p)++)=Pattern[Buf[x]]; d+=34; } } EnterCriticalSection(&csGDILock); // solving NT GDI problems { BitBlt(hWindowDC, nLcdX, nLcdY+(Chipset.lcounter+1)*nLcdDoubled, 131*nLcdDoubled, (63-Chipset.lcounter)*nLcdDoubled, hLcdDC, 0, (Chipset.lcounter+1)*nLcdDoubled, SRCCOPY); GdiFlush(); } LeaveCriticalSection(&csGDILock); return; } VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s) { INT x0, x; INT y0, y; DWORD *p; INT lWidth = abs(Chipset.width); // display width 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 _ASSERT(y0 >= 0 && y0 <= (INT) Chipset.lcounter); // illegal zoom factor _ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4); // calculate memory position in LCD bitmap p = (DWORD*) (pbyLcd + y0*LCD1_ROW*nLcdDoubled*nLcdDoubled + x0*sizeof(bmiLcd.dwColor[0])*nLcdDoubled); 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 if (y == (INT) Chipset.lcounter) break; x = 0; // first coloumn ++y; // next row // recalculate bitmap memory position of new line p = (DWORD*) (pbyLcd+y*LCD1_ROW*nLcdDoubled*nLcdDoubled); } else 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 _ASSERT(x >= x0); // can't draw negative number of pixel x -= x0; // number of pixels to update x0 -= Chipset.boffset; // adjust x-position with left margin if (x0 < 0) x0 = 0; 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); return; } VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s) { UINT x0, x; UINT y0, y; DWORD *p; if (Chipset.lcounter==0x3F) return; // menu disabled d -= Chipset.start2; y0 = y = (d / 34) + (Chipset.lcounter+1); x0 = x = d % 34; if (x0 > 32) return; // position out of viewed area _ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4); if (nLcdDoubled == 4) { p = (DWORD*)(pbyLcd + y0*LCD3_ROW*4 + x0*16); 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; p=(DWORD*)(pbyLcd+y*LCD3_ROW*4); } else p+=4; } EnterCriticalSection(&csGDILock); // solving NT GDI problems { 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); } GdiFlush(); } LeaveCriticalSection(&csGDILock); } if (nLcdDoubled == 2) { p = (DWORD*)(pbyLcd + y0*LCD2_ROW*2 + x0*8); 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; p=(DWORD*)(pbyLcd+y*LCD2_ROW*2); } else p+=2; } EnterCriticalSection(&csGDILock); // solving NT GDI problems { 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); } GdiFlush(); } LeaveCriticalSection(&csGDILock); } if (nLcdDoubled == 1) { p = (DWORD*)(pbyLcd + y0*LCD1_ROW + x0*4); while (s--) { if (x<34) *p = Pattern[*a]; a++; x++; if ((x==34)&&s) { x=0; y++; if (y==64) break; p=(DWORD*)(pbyLcd+y*LCD1_ROW); } else p++; } EnterCriticalSection(&csGDILock); // solving NT GDI problems { 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); } GdiFlush(); } LeaveCriticalSection(&csGDILock); } return; } VOID UpdateAnnunciators() { BYTE c; 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) c=0; DrawAnnunciator(1,c&LA1); DrawAnnunciator(2,c&LA2); DrawAnnunciator(3,c&LA3); DrawAnnunciator(4,c&LA4); DrawAnnunciator(5,c&LA5); DrawAnnunciator(6,c&LA6); return; } VOID ResizeWindow() { RECT rectWindow; RECT rectClient; if (hWnd == NULL) return; // return if window closed 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); } InvalidateRect(hWnd,NULL,TRUE); return; }