From 984f19915ecf92bc0cab216d34889d0bcb7b0832 Mon Sep 17 00:00:00 2001 From: jhhoward Date: Mon, 13 Dec 2021 22:50:38 +0000 Subject: [PATCH] Added experimental HP 95LX build --- project/DOS/DOS.vcxproj | 3 + project/DOS/dosbox-genimages.conf | 1 + project/DOS/hp95lx.mak | 72 +++ src/DOS/DOSNet.cpp | 13 +- src/DOS/DOSNet.h | 7 +- src/DOS/HP95LX.cpp | 942 ++++++++++++++++++++++++++++++ src/DOS/HP95LX.h | 74 +++ src/DOS/Platform.cpp | 41 +- src/Page.cpp | 2 +- src/Renderer.cpp | 6 + 10 files changed, 1148 insertions(+), 13 deletions(-) create mode 100644 project/DOS/hp95lx.mak create mode 100644 src/DOS/HP95LX.cpp create mode 100644 src/DOS/HP95LX.h diff --git a/project/DOS/DOS.vcxproj b/project/DOS/DOS.vcxproj index ee30f07..b57b375 100644 --- a/project/DOS/DOS.vcxproj +++ b/project/DOS/DOS.vcxproj @@ -101,6 +101,7 @@ + @@ -116,6 +117,7 @@ + @@ -135,6 +137,7 @@ + diff --git a/project/DOS/dosbox-genimages.conf b/project/DOS/dosbox-genimages.conf index bf5504d..ed0969d 100644 --- a/project/DOS/dosbox-genimages.conf +++ b/project/DOS/dosbox-genimages.conf @@ -8,6 +8,7 @@ c: imgmount A "../../floppy/freedos.boot.disk.720K.img" imgmount B "../../floppy/freedos.boot.disk.360K.img" d:LZEXE.EXE MICROWEB +d:LZEXE.EXE MWEB95 copy /Y MICROWEB.EXE A: copy /Y MICROWEB.EXE B: pause diff --git a/project/DOS/hp95lx.mak b/project/DOS/hp95lx.mak new file mode 100644 index 0000000..0d05ecb --- /dev/null +++ b/project/DOS/hp95lx.mak @@ -0,0 +1,72 @@ +bin = MWEB95.exe +SRC_PATH = ..\..\src +OBJDIR=obj +objects = MicroWeb.obj App.obj Parser.obj Renderer.obj Tags.obj Platform.obj HP95LX.obj Font.obj Interface.obj DOSInput.obj DOSNet.obj Page.obj +memory_model = -ml +CC = wpp +CFLAGS = -zq -0 -ot -bt=DOS -w2 $(memory_model) -dHP95LX +LD = wlink + +# begin mTCP stuff +tcp_h_dir = ..\..\lib\mTCP\TCPINC\ +tcp_c_dir = ..\..\lib\mTCP\TCPLIB\ + +tcpobjs = packet.obj arp.obj eth.obj ip.obj tcp.obj tcpsockm.obj udp.obj utils.obj dns.obj timer.obj ipasm.obj trace.obj + +tcp_compile_options = -0 $(memory_model) -DCFG_H="tcp.cfg" -oh -ok -ot -s -oa -ei -zp2 -zpw -we -ob -ol+ -oi+ +tcp_compile_options += -i=$(tcp_h_dir) + +.cpp : $(tcp_c_dir) + +.asm : $(tcp_c_dir) + +.asm.obj : + wasm -0 $(memory_model) $[* + +.cpp.obj : + wpp $[* $(tcp_compile_options) +# end mTCP stuff + +$(bin): $(objects) $(tcpobjs) + $(LD) system dos name $@ file { $(objects) $(tcpobjs) } + + +MicroWeb.obj: $(SRC_PATH)\MicroWeb.cpp + $(CC) -fo=$@ $(CFLAGS) $< + +App.obj: $(SRC_PATH)\App.cpp + $(CC) -fo=$@ $(CFLAGS) $< + +Parser.obj: $(SRC_PATH)\Parser.cpp + $(CC) -fo=$@ $(CFLAGS) $< + +Renderer.obj: $(SRC_PATH)\Renderer.cpp + $(CC) -fo=$@ $(CFLAGS) $< + +Tags.obj: $(SRC_PATH)\Tags.cpp + $(CC) -fo=$@ $(CFLAGS) $< + +Page.obj: $(SRC_PATH)\Page.cpp + $(CC) -fo=$@ $(CFLAGS) $< + +Platform.obj: $(SRC_PATH)\DOS\Platform.cpp + $(CC) -fo=$@ $(CFLAGS) $< + +Font.obj: $(SRC_PATH)\Font.cpp + $(CC) -fo=$@ $(CFLAGS) $< + +Interface.obj: $(SRC_PATH)\Interface.cpp + $(CC) -fo=$@ $(CFLAGS) $< + +HP95LX.obj: $(SRC_PATH)\DOS\HP95LX.cpp + $(CC) -fo=$@ $(CFLAGS) $< + +DOSInput.obj: $(SRC_PATH)\DOS\DOSInput.cpp + $(CC) -fo=$@ $(CFLAGS) $< + +DOSNet.obj: $(SRC_PATH)\DOS\DOSNet.cpp + $(CC) -fo=$@ $(CFLAGS) -i=$(tcp_h_dir) -DCFG_H="tcp.cfg" $< + +clean: .symbolic + del *.obj + del $(bin) \ No newline at end of file diff --git a/src/DOS/DOSNet.cpp b/src/DOS/DOSNet.cpp index 3d3fa61..2c0974a 100644 --- a/src/DOS/DOSNet.cpp +++ b/src/DOS/DOSNet.cpp @@ -64,6 +64,11 @@ void DOSNetworkDriver::Init() return; } + for (int n = 0; n < MAX_CONCURRENT_HTTP_REQUESTS; n++) + { + requests[n] = new DOSHTTPRequest(); + } + isConnected = true; } @@ -87,7 +92,7 @@ void DOSNetworkDriver::Update() for (int n = 0; n < MAX_CONCURRENT_HTTP_REQUESTS; n++) { - requests[n].Update(); + requests[n]->Update(); } } } @@ -98,10 +103,10 @@ HTTPRequest* DOSNetworkDriver::CreateRequest(char* url) { for (int n = 0; n < MAX_CONCURRENT_HTTP_REQUESTS; n++) { - if (requests[n].GetStatus() == HTTPRequest::Stopped) + if (requests[n]->GetStatus() == HTTPRequest::Stopped) { - requests[n].Open(url); - return &requests[n]; + requests[n]->Open(url); + return requests[n]; } } } diff --git a/src/DOS/DOSNet.h b/src/DOS/DOSNet.h index 37fe76f..cd9f9e6 100644 --- a/src/DOS/DOSNet.h +++ b/src/DOS/DOSNet.h @@ -18,8 +18,13 @@ #include "../Platform.h" #include "../URL.h" +#ifdef HP95LX +#define TCP_RECV_BUFFER_SIZE (8192) +#define MAX_CONCURRENT_HTTP_REQUESTS 1 +#else #define TCP_RECV_BUFFER_SIZE (16384) #define MAX_CONCURRENT_HTTP_REQUESTS 3 +#endif #define HOSTNAME_LEN (80) #define PATH_LEN (MAX_URL_LENGTH) #define LINE_BUFFER_SIZE 512 @@ -108,6 +113,6 @@ class DOSNetworkDriver : public NetworkDriver virtual void DestroyRequest(HTTPRequest* request); private: - DOSHTTPRequest requests[MAX_CONCURRENT_HTTP_REQUESTS]; + DOSHTTPRequest* requests[MAX_CONCURRENT_HTTP_REQUESTS]; bool isConnected; }; diff --git a/src/DOS/HP95LX.cpp b/src/DOS/HP95LX.cpp new file mode 100644 index 0000000..05b166f --- /dev/null +++ b/src/DOS/HP95LX.cpp @@ -0,0 +1,942 @@ +// +// Copyright (C) 2021 James Howard +// +// This program 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. +// +// This program 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. +// + +#include +#include +#include +#include +#include +#include +#include "../Image.h" +#include "HP95LX.h" +#include "DefData.h" +#include "../Interface.h" + +#define USE_EGA_PREVIS 0 + +#if USE_EGA_PREVIS +#define BASE_VRAM_ADDRESS (uint8_t*) MK_FP(0xA000, 0) +#else +#define BASE_VRAM_ADDRESS (uint8_t*) MK_FP(0xB000, 0) +#endif + +#define SCREEN_WIDTH 240 +#define SCREEN_HEIGHT 128 + +#define ADDRESS_BAR_HEIGHT 13 +#define TITLE_BAR_HEIGHT 8 +#define STATUS_BAR_HEIGHT 0 +#define STATUS_BAR_Y (SCREEN_HEIGHT - STATUS_BAR_HEIGHT) + +#define NAVIGATION_BUTTON_WIDTH 24 +#define NAVIGATION_BUTTON_HEIGHT ADDRESS_BAR_HEIGHT + +#define BACK_BUTTON_X 0 +#define FORWARD_BUTTON_X (BACK_BUTTON_X + NAVIGATION_BUTTON_WIDTH + 1) +#define ADDRESS_BAR_X (FORWARD_BUTTON_X + NAVIGATION_BUTTON_WIDTH + 1) +#define ADDRESS_BAR_Y (TITLE_BAR_HEIGHT + 1) +#define ADDRESS_BAR_WIDTH (SCREEN_WIDTH - ADDRESS_BAR_X - 1) + +#define WINDOW_TOP (TITLE_BAR_HEIGHT + ADDRESS_BAR_HEIGHT + 2) +#define WINDOW_HEIGHT (SCREEN_HEIGHT - WINDOW_TOP - STATUS_BAR_HEIGHT) +#define WINDOW_BOTTOM (WINDOW_TOP + WINDOW_HEIGHT) + +#define SCROLL_BAR_WIDTH 8 + +#define WINDOW_WIDTH (SCREEN_WIDTH - SCROLL_BAR_WIDTH) + +#if USE_EGA_PREVIS +#define BYTES_PER_LINE 80 +#else +#define BYTES_PER_LINE 30 +#endif + +#define WINDOW_VRAM_TOP (BYTES_PER_LINE * (WINDOW_TOP)) +#define WINDOW_VRAM_BOTTOM (BYTES_PER_LINE * (WINDOW_BOTTOM)) + + + +HP95LXVideoDriver::HP95LXVideoDriver() +{ + screenWidth = SCREEN_WIDTH; + screenHeight = SCREEN_HEIGHT; + windowWidth = SCREEN_WIDTH - SCROLL_BAR_WIDTH; + windowHeight = WINDOW_HEIGHT; + windowX = 0; + windowY = WINDOW_TOP; + scissorX1 = 0; + scissorY1 = 0; + scissorX2 = SCREEN_WIDTH; + scissorY2 = SCREEN_HEIGHT; + invertScreen = true; + clearMask = invertScreen ? 0 : 0xffff; + imageIcon = &Default_ImageIcon; + bulletImage = &Default_Bullet; + isTextMode = false; +} + +void HP95LXVideoDriver::Init() +{ + startingScreenMode = GetScreenMode(); +#if USE_EGA_PREVIS + SetScreenMode(0x10); +#else + SetScreenMode(0x20); +#endif +} + +void HP95LXVideoDriver::Shutdown() +{ + SetScreenMode(startingScreenMode); +} + +int HP95LXVideoDriver::GetScreenMode() +{ + union REGS inreg, outreg; + inreg.h.ah = 0xf; + + int86(0x10, &inreg, &outreg); + + return (int)outreg.h.al; +} + +void HP95LXVideoDriver::SetScreenMode(int screenMode) +{ + union REGS inreg, outreg; + inreg.h.ah = 0; + inreg.h.al = (unsigned char)screenMode; + + int86(0x10, &inreg, &outreg); +} + +static void FastMemSet(void far* mem, uint8_t value, unsigned int count); +#pragma aux FastMemSet = \ + "rep stosb" \ + modify [di cx] \ + parm[es di][al][cx]; + +void HP95LXVideoDriver::InvertScreen() +{ + int count = (SCREEN_HEIGHT * BYTES_PER_LINE); + unsigned char far* VRAM = BASE_VRAM_ADDRESS; + while (count--) + { + *VRAM ^= 0xff; + VRAM++; + } + + invertScreen = !invertScreen; + clearMask = invertScreen ? 0 : 0xffff; +} + +void HP95LXVideoDriver::ClearScreen() +{ + uint8_t clearValue = (uint8_t)(clearMask & 0xff); + FastMemSet(BASE_VRAM_ADDRESS, clearValue, (SCREEN_HEIGHT * BYTES_PER_LINE)); +} + +void HP95LXVideoDriver::DrawImage(Image* image, int x, int y) +{ + int imageHeight = image->height; + + if (x >= scissorX2) + { + return; + } + if (y >= scissorY2) + { + return; + } + if (y + imageHeight < scissorY1) + { + return; + } + if (y + imageHeight > scissorY2) + { + imageHeight = (scissorY2 - y); + } + + uint16_t firstLine = 0; + if (y < scissorY1) + { + firstLine += scissorY1 - y; + y += firstLine; + } + + uint8_t far* VRAM = (uint8_t far*) BASE_VRAM_ADDRESS; + VRAM += y * BYTES_PER_LINE; + + uint16_t imageWidthBytes = image->width >> 3; + + // Check if rounds to a byte, pad if necessary + if ((imageWidthBytes << 3) != image->width) + { + imageWidthBytes++; + } + + uint8_t* imageData = image->data + firstLine * imageWidthBytes; + uint8_t far* VRAMptr = VRAM + (x >> 3); + + for (uint8_t j = firstLine; j < imageHeight; j++) + { + uint8_t writeOffset = (uint8_t)(x) & 0x7; + + for (uint8_t i = 0; i < imageWidthBytes; i++) + { + uint8_t glyphPixels = *imageData++; + + VRAMptr[i] ^= (glyphPixels >> writeOffset); + VRAMptr[i + 1] ^= (glyphPixels << (8 - writeOffset)); + } + + VRAMptr += BYTES_PER_LINE; + } +} + +void HP95LXVideoDriver::DrawString(const char* text, int x, int y, int size, FontStyle::Type style) +{ + Font* font = GetFont(size, style); + + int startX = x; + uint8_t glyphHeight = font->glyphHeight; + if (x >= scissorX2) + { + return; + } + if (y >= scissorY2) + { + return; + } + if (y + glyphHeight > scissorY2) + { + glyphHeight = (uint8_t)(scissorY2 - y); + } + if (y + glyphHeight < scissorY1) + { + return; + } + + uint8_t firstLine = 0; + if (y < scissorY1) + { + firstLine += scissorY1 - y; + y += firstLine; + } + + uint8_t far* VRAM = (uint8_t far*) BASE_VRAM_ADDRESS; + + VRAM += y * BYTES_PER_LINE; + + while (*text) + { + char c = *text++; + if (c < 32 || c >= 128) + { + continue; + } + + char index = c - 32; + uint8_t glyphWidth = font->glyphWidth[index]; + + if (glyphWidth == 0) + { + continue; + } + + uint8_t* glyphData = font->glyphData + (font->glyphDataStride * index); + + glyphData += (firstLine * font->glyphWidthBytes); + + uint8_t far* VRAMptr = VRAM + (x >> 3); + + for (uint8_t j = firstLine; j < glyphHeight; j++) + { + uint8_t writeOffset = (uint8_t)(x) & 0x7; + + if ((style & FontStyle::Italic) && j < (font->glyphHeight >> 1)) + { + writeOffset++; + } + + for (uint8_t i = 0; i < font->glyphWidthBytes; i++) + { + uint8_t glyphPixels = *glyphData++; + + if (style & FontStyle::Bold) + { + glyphPixels |= (glyphPixels >> 1); + } + + VRAMptr[i] ^= (glyphPixels >> writeOffset); + VRAMptr[i + 1] ^= (glyphPixels << (8 - writeOffset)); + } + + VRAMptr += BYTES_PER_LINE; + } + + x += glyphWidth; + if (style & FontStyle::Bold) + { + x++; + } + + if (x >= scissorX2) + { + break; + } + } + + if ((style & FontStyle::Underline) && y - firstLine + font->glyphHeight - 1 < scissorY2) + { + HLine(startX, y - firstLine + font->glyphHeight - 1, x - startX); + } +} + +Font* HP95LXVideoDriver::GetFont(int fontSize, FontStyle::Type style) +{ + if (style & FontStyle::Monospace) + { + switch (fontSize) + { + default: + return &Default_SmallFont_Monospace; + case 2: + case 3: + case 4: + return &Default_RegularFont_Monospace; + } + } + + switch (fontSize) + { + default: + return &Default_SmallFont; + case 2: + case 3: + case 4: + return &Default_RegularFont; + } +} + +void HP95LXVideoDriver::HLine(int x, int y, int count) +{ + if (invertScreen) + { + ClearHLine(x, y, count); + } + else + { + HLineInternal(x, y, count); + } +} + +void HP95LXVideoDriver::HLineInternal(int x, int y, int count) +{ + if (x >= scissorX2) + { + return; + } + if (x + count >= scissorX2) + { + count = scissorX2 - x; + } + if (y < scissorY1 || y >= scissorY2) + return; + + uint8_t far* VRAMptr = (uint8_t far*) BASE_VRAM_ADDRESS; + + VRAMptr += y * BYTES_PER_LINE; + VRAMptr += (x >> 3); + + uint8_t data = *VRAMptr; + uint8_t mask = ~(0x80 >> (x & 7)); + + while (count--) + { + data &= mask; + x++; + mask = (mask >> 1) | 0x80; + if ((x & 7) == 0) + { + *VRAMptr++ = data; + while (count > 8) + { + *VRAMptr++ = 0; + count -= 8; + } + mask = ~0x80; + data = *VRAMptr; + } + } + + *VRAMptr = data; +} + +void HP95LXVideoDriver::ClearHLine(int x, int y, int count) +{ + if (x >= scissorX2) + { + return; + } + if (x + count >= scissorX2) + { + count = scissorX2 - x; + } + + uint8_t far* VRAMptr = (uint8_t far*) BASE_VRAM_ADDRESS; + + VRAMptr += y * BYTES_PER_LINE; + VRAMptr += (x >> 3); + + uint8_t data = *VRAMptr; + uint8_t mask = (0x80 >> (x & 7)); + + while (count--) + { + data |= mask; + x++; + mask >>= 1; + if ((x & 7) == 0) + { + *VRAMptr++ = data; + while (count > 8) + { + *VRAMptr++ = 0xff; + count -= 8; + } + mask = 0x80; + data = *VRAMptr; + } + } + + *VRAMptr = data; +} + +void HP95LXVideoDriver::ClearRect(int x, int y, int width, int height) +{ + if (!ApplyScissor(y, height)) + return; + + if (invertScreen) + { + for (int j = 0; j < height; j++) + { + HLineInternal(x, y + j, width); + } + } + else + { + for (int j = 0; j < height; j++) + { + ClearHLine(x, y + j, width); + } + } +} + +void HP95LXVideoDriver::InvertLine(int x, int y, int count) +{ + uint8_t far* VRAMptr = (uint8_t far*) BASE_VRAM_ADDRESS; + + VRAMptr += y * BYTES_PER_LINE; + VRAMptr += (x >> 3); + + uint8_t data = *VRAMptr; + uint8_t mask = (0x80 >> (x & 7)); + + while (count--) + { + data ^= mask; + x++; + mask >>= 1; + if ((x & 7) == 0) + { + *VRAMptr++ = data; + while (count > 8) + { + *VRAMptr++ ^= 0xff; + count -= 8; + } + mask = 0x80; + data = *VRAMptr; + } + } + + *VRAMptr = data; +} + +bool HP95LXVideoDriver::ApplyScissor(int& y, int& height) +{ + if (y + height < scissorY1) + return false; + if (y >= scissorY2) + return false; + if (y < scissorY1) + { + height -= (scissorY1 - y); + y = scissorY1; + } + if (y + height >= scissorY2) + { + height = scissorY2 - y; + } + return true; +} + +void HP95LXVideoDriver::InvertRect(int x, int y, int width, int height) +{ + if (!ApplyScissor(y, height)) + return; + + for (int j = 0; j < height; j++) + { + InvertLine(x, y + j, width); + } +} + +void HP95LXVideoDriver::FillRect(int x, int y, int width, int height) +{ + if (invertScreen) + { + for (int j = 0; j < height; j++) + { + ClearHLine(x, y + j, width); + } + } + else + { + for (int j = 0; j < height; j++) + { + HLineInternal(x, y + j, width); + } + } +} + +void HP95LXVideoDriver::VLine(int x, int y, int count) +{ + if (x >= scissorX2) + { + return; + } + if (y < scissorY1) + { + count -= (scissorY1 - y); + y = scissorY1; + } + if (y >= scissorY2) + { + return; + } + if (y + count >= scissorY2) + { + count = scissorY2 - y; + } + if (count <= 0) + { + return; + } + + uint8_t far* VRAMptr = (uint8_t far*) BASE_VRAM_ADDRESS; + uint8_t mask = ~(0x80 >> (x & 7)); + + VRAMptr += y * BYTES_PER_LINE; + VRAMptr += (x >> 3); + + if (invertScreen) + { + mask ^= 0xff; + + while (count--) + { + *VRAMptr |= mask; + VRAMptr += BYTES_PER_LINE; + } + } + else + { + while (count--) + { + *VRAMptr &= mask; + VRAMptr += BYTES_PER_LINE; + } + } +} + +MouseCursorData* HP95LXVideoDriver::GetCursorGraphic(MouseCursor::Type type) +{ + switch (type) + { + default: + case MouseCursor::Pointer: + return &Default_MouseCursor; + case MouseCursor::Hand: + return &Default_MouseCursorHand; + case MouseCursor::TextSelect: + return &Default_MouseCursorTextSelect; + } +} + +int HP95LXVideoDriver::GetGlyphWidth(char c, int fontSize, FontStyle::Type style) +{ + Font* font = GetFont(fontSize, style); + if (c >= 32 && c < 128) + { + int width = font->glyphWidth[c - 32]; + if (style & FontStyle::Bold) + { + width++; + } + return width; + } + return 0; +} + +int HP95LXVideoDriver::GetLineHeight(int fontSize, FontStyle::Type style) +{ + return GetFont(fontSize, style)->glyphHeight + 1; +} + +void DrawScrollBarBlock(uint8_t far* ptr, int top, int middle, int bottom); +void DrawScrollBarBlockInverted(uint8_t far* ptr, int top, int middle, int bottom); + +#if USE_EGA_PREVIS +#pragma aux DrawScrollBarBlock = \ + "cmp bx, 0" \ + "je _startMiddle" \ + "mov al, 0x7e" \ + "_loopTop:" \ + "stosb" \ + "add di, 79" \ + "dec bx"\ + "jnz _loopTop" \ + "_startMiddle:" \ + "mov al, 0x42" \ + "_loopMiddle:" \ + "stosb" \ + "add di, 79" \ + "dec cx"\ + "jnz _loopMiddle" \ + "mov al, 0x7e" \ + "cmp dx, 0" \ + "je _end" \ + "_loopBottom:" \ + "stosb" \ + "add di, 79" \ + "dec dx"\ + "jnz _loopBottom" \ + "_end:" \ + modify [di ax bx cx dx] \ +parm[es di][bx][cx][dx]; + +#pragma aux DrawScrollBarBlockInverted = \ + "cmp bx, 0" \ + "je _startMiddle" \ + "mov al, 0x81" \ + "_loopTop:" \ + "stosb" \ + "add di, 79" \ + "dec bx"\ + "jnz _loopTop" \ + "_startMiddle:" \ + "mov al, 0xbd" \ + "_loopMiddle:" \ + "stosb" \ + "add di, 79" \ + "dec cx"\ + "jnz _loopMiddle" \ + "mov ax, 0x81" \ + "cmp dx, 0" \ + "je _end" \ + "_loopBottom:" \ + "stosb" \ + "add di, 79" \ + "dec dx"\ + "jnz _loopBottom" \ + "_end:" \ + modify [di ax bx cx dx] \ +parm[es di][bx][cx][dx]; + +#else +#pragma aux DrawScrollBarBlock = \ + "cmp bx, 0" \ + "je _startMiddle" \ + "mov al, 0x7e" \ + "_loopTop:" \ + "stosb" \ + "add di, 29" \ + "dec bx"\ + "jnz _loopTop" \ + "_startMiddle:" \ + "mov al, 0x42" \ + "_loopMiddle:" \ + "stosb" \ + "add di, 29" \ + "dec cx"\ + "jnz _loopMiddle" \ + "mov al, 0x7e" \ + "cmp dx, 0" \ + "je _end" \ + "_loopBottom:" \ + "stosb" \ + "add di, 29" \ + "dec dx"\ + "jnz _loopBottom" \ + "_end:" \ + modify [di ax bx cx dx] \ +parm[es di][bx][cx][dx]; + +#pragma aux DrawScrollBarBlockInverted = \ + "cmp bx, 0" \ + "je _startMiddle" \ + "mov al, 0x81" \ + "_loopTop:" \ + "stosb" \ + "add di, 29" \ + "dec bx"\ + "jnz _loopTop" \ + "_startMiddle:" \ + "mov al, 0xbd" \ + "_loopMiddle:" \ + "stosb" \ + "add di, 29" \ + "dec cx"\ + "jnz _loopMiddle" \ + "mov al, 0x81" \ + "cmp dx, 0" \ + "je _end" \ + "_loopBottom:" \ + "stosb" \ + "add di, 29" \ + "dec dx"\ + "jnz _loopBottom" \ + "_end:" \ + modify [di ax bx cx dx] \ +parm[es di][bx][cx][dx]; +#endif + +void HP95LXVideoDriver::DrawScrollBar(int position, int size) +{ +// uint8_t* VRAM = BASE_VRAM_ADDRESS + WINDOW_TOP * BYTES_PER_LINE + (BYTES_PER_LINE - 2); + uint8_t* VRAM = BASE_VRAM_ADDRESS + WINDOW_TOP * BYTES_PER_LINE + ((SCREEN_WIDTH / 8) - 1); + + if (invertScreen) + { + DrawScrollBarBlockInverted(VRAM, position, size, WINDOW_HEIGHT - position - size); + } + else + { + DrawScrollBarBlock(VRAM, position, size, WINDOW_HEIGHT - position - size); + } +} + +void HP95LXVideoDriver::DrawRect(int x, int y, int width, int height) +{ + HLine(x, y, width); + HLine(x, y + height - 1, width); + VLine(x, y + 1, height - 2); + VLine(x + width - 1, y + 1, height - 2); +} + +void HP95LXVideoDriver::DrawButtonRect(int x, int y, int width, int height) +{ + HLine(x + 1, y, width - 2); + HLine(x + 1, y + height - 1, width - 2); + VLine(x, y + 1, height - 2); + VLine(x + width - 1, y + 1, height - 2); +} + +void ScrollRegionUp(int dest, int src, int count); +void ScrollRegionDown(int dest, int src, int count); +void ClearRegion(int offset, int count, uint16_t clearMask); + +#if USE_EGA_PREVIS +#pragma aux ScrollRegionUp = \ + "push ds" \ + "push es" \ + "mov ax, 0xa000" /* b000*/ \ + "mov ds, ax" \ + "mov es, ax" \ + "_loopLine:" \ + "mov cx, 29" \ + "rep movsb" \ + "add di, 51" /* 1 */ \ + "add si, 51" /* 1 */ \ + "dec dx" \ + "jnz _loopLine" \ + "pop es" \ + "pop ds" \ + modify [ax cx dx di si] \ + parm [di][si][dx] + +#pragma aux ScrollRegionDown = \ + "push ds" \ + "push es" \ + "mov ax, 0xa000" /* b000*/ \ + "mov ds, ax" \ + "mov es, ax" \ + "_loopLine:" \ + "mov cx, 29" \ + "rep movsb" \ + "sub di, 109" /* 160 - 2 = 158 -> 58 */ \ + "sub si, 109" \ + "dec dx" \ + "jnz _loopLine" \ + "pop es" \ + "pop ds" \ + modify [ax cx dx di si] \ + parm [di][si][dx] + +#pragma aux ClearRegion = \ + "push es" \ + "mov ax, 0xa000" /* b000 */ \ + "mov es, ax" \ + "mov ax, bx" \ + "_loopLine:" \ + "mov cx, 29" \ + "rep stosb" \ + "add di, 51" /* 2 */ \ + "dec dx" \ + "jnz _loopLine" \ + "pop es" \ + modify [cx di ax cx dx] \ + parm [di] [dx] [bx] +#else +#pragma aux ScrollRegionUp = \ + "push ds" \ + "push es" \ + "mov ax, 0xb000" \ + "mov ds, ax" \ + "mov es, ax" \ + "_loopLine:" \ + "mov cx, 29" \ + "rep movsb" \ + "add di, 1" \ + "add si, 1" \ + "dec dx" \ + "jnz _loopLine" \ + "pop es" \ + "pop ds" \ + modify [ax cx dx di si] \ + parm [di][si][dx] + +#pragma aux ScrollRegionDown = \ + "push ds" \ + "push es" \ + "mov ax, 0xb000" \ + "mov ds, ax" \ + "mov es, ax" \ + "_loopLine:" \ + "mov cx, 29" \ + "rep movsb" \ + "sub di, 59" \ + "sub si, 59" \ + "dec dx" \ + "jnz _loopLine" \ + "pop es" \ + "pop ds" \ + modify [ax cx dx di si] \ + parm [di][si][dx] + +#pragma aux ClearRegion = \ + "push es" \ + "mov ax, 0xb000" \ + "mov es, ax" \ + "mov ax, bx" \ + "_loopLine:" \ + "mov cx, 29" \ + "rep stosb" \ + "add di, 1" \ + "dec dx" \ + "jnz _loopLine" \ + "pop es" \ + modify [cx di ax cx dx] \ + parm [di] [dx] [bx] +#endif + +void HP95LXVideoDriver::ScrollWindow(int amount) +{ + if (amount > 0) + { + int lines = (WINDOW_HEIGHT - amount); + int offset = amount * BYTES_PER_LINE; + ScrollRegionUp(WINDOW_VRAM_TOP, WINDOW_VRAM_TOP + offset, lines); + + ClearRegion(WINDOW_VRAM_BOTTOM - offset, (WINDOW_HEIGHT) - lines, clearMask); + } + else if (amount < 0) + { + int lines = (WINDOW_HEIGHT + amount); + int offset = amount * BYTES_PER_LINE; + ScrollRegionDown(WINDOW_VRAM_BOTTOM - BYTES_PER_LINE, WINDOW_VRAM_BOTTOM - BYTES_PER_LINE + offset, lines); + + ClearRegion(WINDOW_VRAM_TOP, WINDOW_HEIGHT - lines, clearMask); + } +} + +void HP95LXVideoDriver::ClearWindow() +{ + ClearRegion(WINDOW_VRAM_TOP, WINDOW_HEIGHT, clearMask); +} + +void HP95LXVideoDriver::SetScissorRegion(int y1, int y2) +{ + scissorY1 = y1; + scissorY2 = y2; + scissorX1 = 0; + scissorX2 = WINDOW_WIDTH; +} + +void HP95LXVideoDriver::ClearScissorRegion() +{ + scissorY1 = 0; + scissorY2 = screenHeight; + scissorX1 = 0; + scissorX2 = SCREEN_WIDTH; +} + +void HP95LXVideoDriver::ArrangeAppInterfaceWidgets(AppInterface& app) +{ + app.addressBar.x = ADDRESS_BAR_X; + app.addressBar.y = ADDRESS_BAR_Y; + app.addressBar.width = ADDRESS_BAR_WIDTH; + app.addressBar.height = ADDRESS_BAR_HEIGHT; + + app.scrollBar.x = SCREEN_WIDTH - SCROLL_BAR_WIDTH; + app.scrollBar.y = WINDOW_TOP; + app.scrollBar.width = SCROLL_BAR_WIDTH; + app.scrollBar.height = WINDOW_HEIGHT; + + app.backButton.x = BACK_BUTTON_X; + app.backButton.y = ADDRESS_BAR_Y; + app.backButton.width = NAVIGATION_BUTTON_WIDTH; + app.backButton.height = NAVIGATION_BUTTON_HEIGHT; + + app.forwardButton.x = FORWARD_BUTTON_X; + app.forwardButton.y = ADDRESS_BAR_Y; + app.forwardButton.width = NAVIGATION_BUTTON_WIDTH; + app.forwardButton.height = NAVIGATION_BUTTON_HEIGHT; + + app.statusBar.x = 0; + app.statusBar.y = SCREEN_HEIGHT - STATUS_BAR_HEIGHT; + app.statusBar.width = SCREEN_WIDTH; + app.statusBar.height = STATUS_BAR_HEIGHT; + + app.titleBar.x = 0; + app.titleBar.y = 0; + app.titleBar.width = SCREEN_WIDTH; + app.titleBar.height = TITLE_BAR_HEIGHT; +} + +void HP95LXVideoDriver::ScaleImageDimensions(int& width, int& height) +{ +} diff --git a/src/DOS/HP95LX.h b/src/DOS/HP95LX.h new file mode 100644 index 0000000..98828b5 --- /dev/null +++ b/src/DOS/HP95LX.h @@ -0,0 +1,74 @@ +// +// Copyright (C) 2021 James Howard +// +// This program 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. +// +// This program 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. +// + +#pragma once + +#include "../Platform.h" + +struct Image; + +class HP95LXVideoDriver : public VideoDriver +{ +public: + HP95LXVideoDriver(); + + virtual void Init(); + virtual void Shutdown(); + virtual void ClearScreen(); + virtual void InvertScreen(); + + virtual void ArrangeAppInterfaceWidgets(class AppInterface& app); + + + virtual void ClearWindow(); + virtual void ScrollWindow(int delta); + virtual void SetScissorRegion(int y1, int y2); + virtual void ClearScissorRegion(); + + virtual void DrawString(const char* text, int x, int y, int size = 1, FontStyle::Type style = FontStyle::Regular); + virtual void DrawRect(int x, int y, int width, int height); + virtual void DrawButtonRect(int x, int y, int width, int height); + virtual void DrawImage(Image* image, int x, int y); + virtual void ClearRect(int x, int y, int width, int height); + virtual void FillRect(int x, int y, int width, int height); + virtual void InvertRect(int x, int y, int width, int height); + + virtual void HLine(int x, int y, int count); + virtual void VLine(int x, int y, int count); + virtual void ScaleImageDimensions(int& width, int& height); + + virtual int GetGlyphWidth(char c, int fontSize = 1, FontStyle::Type style = FontStyle::Regular); + virtual int GetLineHeight(int fontSize = 1, FontStyle::Type style = FontStyle::Regular); + virtual Font* GetFont(int fontSize, FontStyle::Type style = FontStyle::Regular); + + virtual void DrawScrollBar(int position, int size); + + virtual MouseCursorData* GetCursorGraphic(MouseCursor::Type type); + +private: + int GetScreenMode(); + void SetScreenMode(int screenMode); + + void ClearHLine(int x, int y, int count); + void HLineInternal(int x, int y, int count); + void InvertLine(int x, int y, int count); + bool ApplyScissor(int& y, int& height); + + bool invertScreen; + uint16_t clearMask; + int startingScreenMode; + int scissorX1, scissorY1, scissorX2, scissorY2; + +}; + diff --git a/src/DOS/Platform.cpp b/src/DOS/Platform.cpp index 62fc45c..19521db 100644 --- a/src/DOS/Platform.cpp +++ b/src/DOS/Platform.cpp @@ -14,10 +14,14 @@ #include #include "../Platform.h" +#ifdef HP95LX +#include "HP95LX.h" +#else #include "CGA.h" #include "EGA.h" #include "Hercules.h" #include "TextMode.h" +#endif #include "DOSInput.h" #include "DOSNet.h" @@ -92,7 +96,19 @@ bool DetectHercules(); static void AutoDetectVideoDriver() { union REGS inreg, outreg; +#ifdef HP95LX + inreg.x.ax = 0x4dd4; + int86(0x15, &inreg, &outreg); + if (outreg.x.bx == 0x4850) + { + Platform::video = new HP95LXVideoDriver(); + printf("Detected HP 95LX\n"); + return; + } + fprintf(stderr, "This build only works with the HP 95LX, 100LX and 200LX\n"); + exit(1); +#else // First try detect presence of VGA card inreg.x.ax = 0x1200; inreg.h.bl = 0x32; @@ -101,6 +117,7 @@ static void AutoDetectVideoDriver() if (outreg.h.al == 0x12) { Platform::video = new VGADriver(); + printf("Detected VGA\n"); return; } @@ -112,27 +129,33 @@ static void AutoDetectVideoDriver() if (outreg.h.bl < 4) { Platform::video = new EGADriver(); + printf("Detected EGA\n"); return; } - bool isMono = Find6845(0x3b4); - // Attempt to detect Hercules - if (isMono && DetectHercules()) + // Attempt to detect CGA + // Note we are preferring CGA over Hercules because some CGA devices are errorneously reporting Hercules support + if (Find6845(0x3d4)) { - Platform::video = new HerculesDriver(); + Platform::video = new CGADriver(); + printf("Detected CGA\n"); return; } - // Attempt to detect CGA - if (Find6845(0x3d4)) + bool isMono = Find6845(0x3b4); + + // Attempt to detect Hercules + if (isMono && DetectHercules()) { - Platform::video = new CGADriver(); + Platform::video = new HerculesDriver(); + printf("Detected Hercules\n"); return; } if (isMono) { Platform::video = new MDATextModeDriver(); + printf("Detected MDA\n"); return; //fprintf(stderr, "MDA cards not supported!\n"); } @@ -141,14 +164,17 @@ static void AutoDetectVideoDriver() fprintf(stderr, "Could not detect video card!\n"); } exit(1); +#endif } void Platform::Init(int argc, char* argv[]) { bool inverse = false; + video = NULL; for (int n = 1; n < argc; n++) { +#ifndef HP95LX if (!stricmp(argv[n], "-v") && !video) { video = new VGADriver(); @@ -173,6 +199,7 @@ void Platform::Init(int argc, char* argv[]) { video = new MDATextModeDriver(); } +#endif if (!stricmp(argv[n], "-i")) { inverse = true; diff --git a/src/Page.cpp b/src/Page.cpp index 1baf14d..09d68de 100644 --- a/src/Page.cpp +++ b/src/Page.cpp @@ -155,7 +155,7 @@ void Page::AddTextField(char* text, int bufferLength, char* name) } widget->textField->bufferLength = bufferLength; - widget->width = 200; + widget->width = Platform::video->screenWidth / 3; widget->height = Platform::video->GetFont(1)->glyphHeight + 4; //widget->width = Platform::video->GetFont(1)->CalculateWidth(text) + 4; } diff --git a/src/Renderer.cpp b/src/Renderer.cpp index 9955f8c..18c9add 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -512,6 +512,12 @@ void Renderer::SetTitle(const char* title) void Renderer::SetStatus(const char* status) { + if (app.ui.statusBar.height == 0) + { + // For video modes that hide the status bar + return; + } + if(!status && statusMessage[0] != '\0' || strncmp(status, statusMessage, MAX_STATUS_LENGTH)) { Platform::input->HideMouse();