diff --git a/Makefile b/Makefile index 60d5cb5..dbb0b08 100644 --- a/Makefile +++ b/Makefile @@ -32,8 +32,8 @@ DXE3GEN := dxe3gen EXE2COFF := exe2coff # Compiler Settings -CFLAGS := -DPLATFORM_DOS -Wall -MD -LDFLAGS := +override CFLAGS += -Wall -MD +override LDFLAGS += # Output Directories OBJ := obj @@ -47,30 +47,40 @@ FIN := font/in # Include Paths INC := include 3rdparty 3rdparty/pthreads/include roo_e +# Use MEMWATCH? +ifneq (,$(findstring MEMWATCH,$(CFLAGS))) + MEMWATCH_SRC := 3rdparty/memwatch/memwatch.c + MEMWATCH_OBJ := $(OBJ)/$(MEMWATCH_SRC).o + INC += 3rdparty/memwatch +else + MEMWATCH_SRC := + MEMWATCH_OBJ := +endif + # Font Compiler Source and Target - NOTE: This is a Linux binary! FONT_ELF := font -FONT_SRC := $(shell find font/src -name '*.c') +FONT_SRC := $(shell find font/src -name '*.c') $(MEMWATCH_SRC) FONT_OBJ := $(FONT_SRC:%=$(OBJ)/%.o) FONT_LIB := -lm # Roo/E Source and Target ROOE_EXE := roo_e.exe -ROOE_SRC := $(shell find roo_e -name '*.c') +ROOE_SRC := $(shell find roo_e -name '*.c') $(MEMWATCH_SRC) ROOE_OBJ := $(ROOE_SRC:%=$(OBJ)/%.o) ROOE_LIB := 3rdparty/pthreads/lib/libgthreads.a -lgcc -lm # MultiPlayer Game Client MPGC_EXE := kpsmpgc.app -MPGC_SRC := $(shell find kpsmpgc -name '*.c') +MPGC_SRC := $(shell find kpsmpgc -name '*.c') $(MEMWATCH_SRC) MPGC_OBJ := $(MPGC_SRC:%=$(OBJ)/%.o) MPGC_LIB := -DYNS := kpsvideo stbimage +DYNS := kpssurf kpsplatf stbimage FONTS := vga4x8 vga8x8 vga8x14 vga8x16 # Wiring -INC_FLAGS := $(addprefix -I,$(INC)) -CFLAGS += $(INC_FLAGS) +override CFLAGS += $(INC_FLAGS) +INC_FLAGS := $(addprefix -I,$(INC)) $(foreach DIR,$(DYNS),-Idyn/$(DIR)) APP_OBJS := $(ROOE_OBJ) $(MPGC_OBJ) FONTS := $(addprefix $(FNT)/,$(addsuffix .fnt,$(FONTS))) FONT_PNGS := $(subst .fnt,.png,$(subst $(FNT),$(FIN),$(FONTS))) @@ -96,6 +106,7 @@ clean: # Font Compiler Target $(BIN)/$(FONT_ELF): $(FONT_OBJ) Makefile + @echo "\n---------- $@" mkdir -p $(dir $@) $(BIN)/fonts $(CC) $(FONT_OBJ) -o $@ $(LDFLAGS) $(FONT_LIB) @@ -104,6 +115,7 @@ $(FONTS) &: $(FONT_PNGS) $(BIN)/$(FONT_ELF) # Roo/E Target $(BIN)/$(ROOE_EXE): $(ROOE_OBJ) Makefile + @echo "\n---------- $@" mkdir -p $(dir $@) $(CC) $(ROOE_OBJ) -o $@ $(LDFLAGS) $(ROOE_LIB) # Embed the DPMI Server @@ -113,16 +125,17 @@ $(BIN)/$(ROOE_EXE): $(ROOE_OBJ) Makefile # MPGC Target $(APP)/$(MPGC_EXE): $(MPGC_OBJ) Makefile + @echo "\n---------- $@" mkdir -p $(dir $@) $(DXE3GEN) -o $@ $(MPGC_OBJ) -U $(LDFLAGS) $(MPGC_LIB) # DYN Targets - This is NOT how make should be used. .PHONY: dyns $(DYN_FILES): $(DYN_SOURCE) Makefile - echo $(DYN_SOURCE) + @echo "\n---------- $@" mkdir -p $(DYN) $(OBJ)/dyn/$(basename $(notdir $@)) $(SDK)/lib $(SDK)/include $(foreach CFILE,$(shell find dyn/$(basename $(notdir $@)) -name '*.c'),$(CC) $(CFLAGS) -o $(OBJ)/dyn/$(basename $(notdir $@))/$(subst dyn/$(basename $(notdir $@))/,,$(CFILE).o) -c $(CFILE) && ) true - $(AR) rcs $(OBJ)/dyn/$(basename $(notdir $@)).a $(OBJ)/dyn/$(basename $(notdir $@))/*.o + $(AR) rcs $(OBJ)/dyn/$(basename $(notdir $@)).a $(OBJ)/dyn/$(basename $(notdir $@))/*.o $(MEMWATCH_OBJ) $(DXE3GEN) -o $@ -Y $@.a --whole-archive -U $(OBJ)/dyn/$(basename $(notdir $@)).a $(LDFLAGS) mv $@.a $(SDK)/lib/. if [ ! -z "$(shell find dyn/$(basename $(notdir $@)) -name '*.h')" ]; then cp $(shell find dyn/$(basename $(notdir $@)) -name '*.h') $(SDK)/include/.; fi diff --git a/build.sh b/build.sh index dda3402..f1a4f7f 100755 --- a/build.sh +++ b/build.sh @@ -24,9 +24,15 @@ # +# Delete things that get built for Linux and also need built for DOS. +rm -rf obj/3rdparty/memwatch + # Linux side first. -make linux +CFLAGS="-DMEMWATCH" make linux + +# Delete things that get built for Linux and also need built for DOS. +rm -rf obj/3rdparty/memwatch # Now do the DOS part. eval "$(../toolchains/toolchains.sh use x86 dos)" -make dos +CFLAGS="-DMEMWATCH" make dos diff --git a/dyn/kpsplatf/djgpp.c b/dyn/kpsplatf/djgpp.c new file mode 100644 index 0000000..94ad6ef --- /dev/null +++ b/dyn/kpsplatf/djgpp.c @@ -0,0 +1,785 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2026 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E 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 Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#ifdef __DJGPP__ + + +#include "kpsplatf.h" + + +// These are all we support +#define VBE_MM_PACKED 4 +#define VBE_MM_DCOLOR 6 + + +// Based on http://www.brackeen.com/vga/source/djgpp20/mouse.c.html +#define MOUSE_INT 0x33 + +#define MOUSE_RESET 0x00 +#define MOUSE_STATUS 0x03 +#define MOUSE_GETMOTION 0x0B + +#define MOUSE_LEFT_BUTTON 0x01 +#define MOUSE_RIGHT_BUTTON 0x02 +#define MOUSE_MIDDLE_BUTTON 0x04 + + +#define KEYBOARD_READ_EXTENDED 0x10 +#define KEYBOARD_CHECK_EXTENDED 0x11 +#define KEYBOARD_META_EXTENDED 0x12 + + +enum MetaBitsE { + KEY_META_SHIFT_RIGHT = 0, + KEY_META_SHIFT_LEFT, + KEY_META_CONTROL, + KEY_META_ALT, + KEY_META_SCROLL_LOCKED, + KEY_META_NUM_LOCKED, + KEY_META_CAPS_LOCKED, + KEY_META_INSERT_LOCKED, + KEY_META_CONTROL_LEFT, + KEY_META_ALT_LEFT, + KEY_META_CONTROL_RIGHT, + KEY_META_ALT_RIGHT, + KEY_META_SCROLL_LOCK, + KEY_META_NUM_LOCK, + KEY_META_CAPS_LOCK, + KEY_META_SYSREQ +}; + + +typedef struct VBEInfoS { + char vbeSignature[4]; // 'VESA' 4 byte signature + int16_t vbeVersion; // VBE version number + char *oemStringPtr; // Pointer to OEM string + uint32_t capabilities; // Capabilities of video card + uint16_t *videoModePtr; // Pointer to supported modes + uint16_t totalMemory; // Number of 64kb memory blocks + // added for VBE 2.0 + uint16_t oemSoftwareRev; // OEM Software revision number + char *oemVendorNamePtr; // Pointer to Vendor Name string + char *oemProductNamePtr; // Pointer to Product Name string + char *oemProductRevPtr; // Pointer to Product Revision str + char reserved[222]; // Pad to 256 byte block size + char oemData[256]; // Scratch pad for OEM data +} __attribute__ ((packed)) VBEInfoT; + + +typedef struct VBEModeInfoS { + // Mandatory information for all VBE revisions: + uint16_t modeAttributes; // mode attributes + uint8_t winAAttributes; // window A attributes + uint8_t winBAttributes; // window B attributes + uint16_t winGranularity; // window granularity + uint16_t winSize; // window size + uint16_t winASegment; // window A start segment + uint16_t winBSegment; // window B start segment + uint32_t winFuncPtr; // pointer to window function + uint16_t bytesPerScanLine; // bytes per scan line + // Mandatory information for VBE 1.2 and above: + uint16_t xResolution; // horizontal resolution in pixels or chars + uint16_t yResolution; // vertical resolution in pixels or chars + uint8_t xCharSize; // character cell width in pixels + uint8_t yCharSize; // character cell height in pixels + uint8_t numberOfPlanes; // number of memory planes + uint8_t bitsPerPixel; // bits per pixel + uint8_t numberOfBanks; // number of banks + uint8_t memoryModel; // memory model type + uint8_t bankSize; // bank size in KB + uint8_t numberOfImagePages; // number of images + uint8_t reserved; // reserved for page function + // Direct Color fields (required for direct/6 and YUV/7 memory models) + uint8_t redMaskSize; // size of direct color red mask in bits + uint8_t redFieldPosition; // bit position of lsb of red mask + uint8_t greenMaskSize; // size of direct color green mask in bits + uint8_t greenFieldPosition; // bit position of lsb of green mask + uint8_t blueMaskSize; // size of direct color blue mask in bits + uint8_t blueFieldPosition; // bit position of lsb of blue mask + uint8_t rsvdMaskSize; // size of direct color reserved mask in bits + uint8_t rsvdFieldPosition; // bit position of lsb of reserved mask + uint8_t directColorModeInfo; // direct color mode attributes + // Mandatory information for VBE 2.0 and above: + uint32_t physBasePtr; // physical address for flat frame buffer + uint32_t offScreenMemOffset; // pointer to start of off screen memory + uint16_t offScreenMemSize; // amount of off screen memory in 1k units + char reservedBuf[206]; +} __attribute__ ((packed)) VBEModeInfoT; + + +typedef struct PModeInterfaceS { + int16_t setWindow; + int16_t setDisplayStart; + int16_t setPalette; + int16_t ioInfo; +} __attribute__ ((packed)) PModeInterfaceT; + + +typedef struct VBESurfaceS { + uint32_t lfbLinearAddress; + uint16_t lfbSelector; + void *lfbNearPtr; + uint32_t lfbMemSize; + uint16_t xResolution; + uint16_t yResolution; + uint32_t bitsPerPixel; + uint16_t virtualXResolution; + uint32_t bytesPerPixel; + uint32_t screenBytes; + uint32_t screenDWords; + uint32_t centerX; + uint32_t centerY; + uint16_t numberOfOffscreens; + uint8_t vbeBoolean; + uint8_t vbeInitBoolean; + int32_t ioSegment; + uint32_t ioLinear; + uint32_t rMask; + uint32_t gMask; + uint32_t bMask; + uint32_t aMask; + uint8_t rShift; + uint8_t gShift; + uint8_t bShift; + uint8_t aShift; + uint8_t rPos; + uint8_t gPos; + uint8_t bPos; + uint8_t aPos; +} VBESurfaceT; + + +static VBESurfaceT _vbeSurface; +static VBEInfoT _vbeInfo; +static VBEModeInfoT _vbeModeInfo; +static PModeInterfaceT *_pmodeInterfacePtr; +static uint32_t *_yTable; + + +static void vbeCreatePalette(void); +static VBEInfoT *vbeGetInfo(void); +static VBEModeInfoT *vbeGetModeInfo(uint16_t vbeModeNumber); +static void *vbeGetPmodeInterface(void); +static uint8_t vbeIsDesiredMode(void); +static uint16_t vbeSelectModeNumber(uint16_t xRes, uint16_t yRes, uint8_t bpp); +static VBESurfaceT *vbeSetMode(uint16_t vbeModeNumber); +static uint16_t vbeSetScanlineLength(uint16_t pixelLength); + + +static void (*pmVBESetDisplayStart)(void); + + +void platformEventGet(PlatformEventT *event) { + int32_t x; + int32_t y; + int16_t dx; + int16_t dy; + int16_t h = videoDisplayHeightGet(); + int16_t w = videoDisplayWidthGet(); + int16_t ext = bioskey(KEYBOARD_CHECK_EXTENDED); + int16_t meta = bioskey(KEYBOARD_META_EXTENDED); + int16_t key = 0; + union REGS regs; + static int32_t lastX = 0; + static int32_t lastY = 0; + static int32_t lastButtons = 0; + + event->buttons = lastButtons; + + // Read mouse motion. + regs.x.ax = MOUSE_GETMOTION; + int86(MOUSE_INT, ®s, ®s); + dx = regs.x.cx; // Temporary assignment changes values to signed. + dy = regs.x.dx; // Don't skip this step. :-) + x = lastX + dx; + y = lastY + dy; + if (x < 0) x = 0; + if (x > w - 1) x = w - 1; + if (y < 0) y = 0; + if (y > h - 1) y = h - 1; + lastX = x; + lastY = y; + event->x = x; + event->y = y; + + // Read mouse buttons. + regs.x.ax = MOUSE_STATUS; + int86(MOUSE_INT, ®s, ®s); + + // Was the left button down? + if (event->buttons & BUTTON_LEFT) { + // Yes. Is it still down? + if ((regs.x.bx & MOUSE_LEFT_BUTTON) > 0) { + // Yes. Do nothing. + } else { + // No! Clear it and set UP event. + event->buttons &= ~BUTTON_LEFT; + event->flags |= EVENT_FLAG_LEFT_UP; + } + } else { + // No. Is it down now? + if ((regs.x.bx & MOUSE_LEFT_BUTTON) > 0) { + // Yes! Set bit and DOWN event. + event->buttons |= BUTTON_LEFT; + event->flags |= EVENT_FLAG_LEFT_DOWN; + } else { + // No. Do nothing. + } + } + + // Was the right button down? + if (event->buttons & BUTTON_RIGHT) { + // Yes. Is it still down? + if ((regs.x.bx & MOUSE_RIGHT_BUTTON) > 0) { + // Yes. Do nothing. + } else { + // No! Clear it and set UP event. + event->buttons &= ~BUTTON_RIGHT; + event->flags |= EVENT_FLAG_RIGHT_UP; + } + } else { + // No. Is it down now? + if ((regs.x.bx & MOUSE_RIGHT_BUTTON) > 0) { + // Yes! Set bit and DOWN event. + event->buttons |= BUTTON_RIGHT; + event->flags |= EVENT_FLAG_RIGHT_DOWN; + } else { + // No. Do nothing. + } + } + + // Read keyboard. + event->key = 0; + event->kbstat = 0; + if (ext > 0) { + key = bioskey(KEYBOARD_READ_EXTENDED); + // The value returned is a combination of the key's scan code in the high 8 bits + // and its ASCII code in the low 8 bits. For non-alphanumeric keys, such as the + // arrow keys, the low 8 bits are zeroed. + // Extended keys have the E0h prefix in the low 8 bits. + event->flags |= EVENT_FLAG_KEYPRESS; + if (LOW_BYTE(key) == 0xE0) { + //_extended = 1; + event->key = HIGH_BYTE(key); + } else { + //_extended = 0; + event->key = LOW_BYTE(key); + } + if ((meta & (1 << KEY_META_ALT)) + (meta & (1 << KEY_META_ALT_LEFT)) + (meta & (1 << KEY_META_ALT_RIGHT))) event->kbstat |= META_ALT; + if ((meta & (1 << KEY_META_CONTROL)) + (meta & (1 << KEY_META_CONTROL_LEFT)) + (meta & (1 << KEY_META_CONTROL_RIGHT))) event->kbstat |= META_CTRL; + if ((meta & (1 << KEY_META_SHIFT_LEFT)) + (meta & (1 << KEY_META_SHIFT_RIGHT))) event->kbstat |= META_SHIFT; + } + + lastButtons = event->buttons; +} + + +void platformShutdown(void) { + __dpmi_regs r; + __dpmi_meminfo m; + + surfaceShutdown(); + + if (_vbeSurface.vbeInitBoolean == 0) { + r.x.ax = 0x03; // make sure we're in 3h + __dpmi_int(0x10, &r); // for buggy vesa implementations + //return(1); + } + + if (_yTable) free(_yTable); + + // free mapping etc. + m.size = (_vbeInfo.totalMemory * 64 * 1024); + m.address = _vbeSurface.lfbLinearAddress; + __dpmi_unlock_linear_region(&m); + __dpmi_free_physical_address_mapping(&m); + __dpmi_free_ldt_descriptor(_vbeSurface.lfbSelector); + + // get rid of PMI interface + if (_pmodeInterfacePtr) free(_pmodeInterfacePtr); + if (_vbeSurface.ioSegment) { + m.address = _vbeSurface.ioLinear; + __dpmi_free_physical_address_mapping(&m); + __dpmi_free_ldt_descriptor(_vbeSurface.ioSegment); + } + + // return do DOS + r.x.ax = 0x03; + __dpmi_int(0x10, &r); + + // deinit VBE surface + _vbeSurface.vbeInitBoolean = 0; + _vbeSurface.vbeBoolean = 0; + + // dealloc mode list + free(_vbeInfo.videoModePtr); +} + + +uint8_t platformStartup(int16_t width, int16_t height, int16_t depth) { + uint16_t vbeModeNumber; + + if (vbeGetInfo() == NULL) { + logWrite("No VESA BIOS Extensions found.\n"); + platformShutdown(); + return FAIL; + } + + if (_vbeInfo.vbeVersion < 0x0200) { + logWrite("VBE Version 2.0 or better required.\n"); + platformShutdown(); + return FAIL; + } + + vbeGetPmodeInterface(); + + if ((vbeModeNumber = vbeSelectModeNumber(width, height, depth)) == 0) { + logWrite("No appropriate video mode available.\n"); + platformShutdown(); + return FAIL; + } + + if (vbeSetMode(vbeModeNumber) == NULL) { + platformShutdown(); + return FAIL; + } + + surfaceStartup(_vbeSurface.bitsPerPixel); + + return SUCCESS; +} + + +static void vbeCreatePalette(void) { + uint8_t color = 0; + uint8_t red; + uint8_t green; + uint8_t blue; + + // Create palette for 3:3:2 true color emulation + for (red = 4; red < 68; red += 8) { + for (green = 4; green < 68; green += 8) { + for (blue = 4; blue < 68; blue += 16) { + outportb(0x3C8, color); + outportb(0x3C9, red); + outportb(0x3C9, green); + outportb(0x3C9, blue); + color++; + } + } + } +} + + +static VBEInfoT *vbeGetInfo(void) { + uint16_t counter = 0; + uint16_t offset = 0; + uint16_t vbeMode = 0xFFFF; + uint16_t *vbeModeList = 0; + __dpmi_regs r; + + // we want VBE 2.0+ info + memcpy(_vbeInfo.vbeSignature, "VBE2", 4); + + // request info + r.x.ax = 0x4F00; + r.x.di = __tb & 0x0F; + r.x.es = (__tb >> 4) & 0xFFFF; + dosmemput(&_vbeInfo, sizeof(VBEInfoT), __tb); + __dpmi_int(0x10, &r); + if (r.x.ax != 0x004F) return NULL; + dosmemget(__tb, sizeof(VBEInfoT), &_vbeInfo); + + if (strncmp(_vbeInfo.vbeSignature, "VESA", 4) != 0) return NULL; // VESA ? + + // get the videomode list + do { + dosmemget(((((long)_vbeInfo.videoModePtr >> 16) & 0xFFFF) << 4) + ((long)_vbeInfo.videoModePtr & 0xFFFF) + (counter * sizeof(short)), sizeof(short), &vbeMode); + counter++; + } while (vbeMode != 0xFFFF); + vbeModeList = malloc((counter + 1) * sizeof(short)); + + counter = 0; + do { + dosmemget(((((long)_vbeInfo.videoModePtr >> 16) & 0xFFFF) << 4) + ((long)_vbeInfo.videoModePtr & 0xFFFF) + (counter * sizeof(short)), sizeof(short), &vbeModeList[counter]); + counter++; + } while (vbeModeList[counter - 1] != 0xFFFF); + _vbeInfo.videoModePtr = vbeModeList; + + // get the OEM string + counter = 0; + do { + dosmemget(((((long)_vbeInfo.oemStringPtr >> 16) & 0xFFFF) << 4) + ((long)_vbeInfo.oemStringPtr & 0xFFFF) + counter, sizeof(char), &_vbeInfo.oemData[counter]); + counter++; + } while (_vbeInfo.oemData[counter - 1] != 0); + _vbeInfo.oemStringPtr = &_vbeInfo.oemData[0]; + offset = counter; + + if (_vbeInfo.vbeVersion >= 0x0200) { + // get the vendor name + counter = 0; + do { + dosmemget(((((long)_vbeInfo.oemVendorNamePtr >> 16) & 0xFFFF) << 4) + ((long)_vbeInfo.oemVendorNamePtr & 0xFFFF) + counter, sizeof(char), &_vbeInfo.oemData[counter + offset]); + counter++; + } while (_vbeInfo.oemData[counter + offset - 1] != 0); + _vbeInfo.oemVendorNamePtr = &_vbeInfo.oemData[offset]; + offset = offset + counter; + + // get the product name + counter = 0; + do { + dosmemget(((((long)_vbeInfo.oemProductNamePtr >> 16) & 0xFFFF) << 4) + ((long)_vbeInfo.oemProductNamePtr & 0xFFFF) + counter, sizeof(char), &_vbeInfo.oemData[counter + offset]); + counter++; + } while (_vbeInfo.oemData[counter + offset - 1] != 0); + _vbeInfo.oemProductNamePtr = &_vbeInfo.oemData[offset]; + offset = offset + counter; + + //get the product revision + counter = 0; + do { + dosmemget(((((long)_vbeInfo.oemProductRevPtr >> 16) & 0xFFFF) << 4) + ((long)_vbeInfo.oemProductRevPtr & 0xFFFF) + counter, sizeof(char), &_vbeInfo.oemData[counter + offset]); + counter++; + } while (_vbeInfo.oemData[counter + offset - 1] != 0); + _vbeInfo.oemProductRevPtr = &_vbeInfo.oemData[offset]; + } + + _vbeSurface.vbeBoolean = 1; + + return(&_vbeInfo); +} + + +static VBEModeInfoT *vbeGetModeInfo(uint16_t vbeModeNumber) { + __dpmi_regs r; + + if (_vbeSurface.vbeBoolean == 0) return NULL; + + r.x.ax = 0x4F01; + r.x.cx = vbeModeNumber; + r.x.di = __tb & 0x0F; + r.x.es = (__tb >> 4) & 0xFFFF; + __dpmi_int(0x10, &r); + + if (r.x.ax != 0x004F) return NULL; + + dosmemget(__tb, sizeof(VBEModeInfoT), &_vbeModeInfo); + + // Fake 3:3:2 true color for packed modes. + if (_vbeModeInfo.bitsPerPixel == 8) { + _vbeModeInfo.blueMaskSize = 2; + _vbeModeInfo.greenMaskSize = 3; + _vbeModeInfo.redMaskSize = 3; + } + + if (_vbeModeInfo.bitsPerPixel == 16) { + // for buggy VBE implementations + _vbeModeInfo.bitsPerPixel = _vbeModeInfo.redMaskSize + _vbeModeInfo.greenMaskSize + _vbeModeInfo.blueMaskSize; + } + + return(&_vbeModeInfo); +} + + +static void *vbeGetPmodeInterface(void) { + __dpmi_regs r; + __dpmi_meminfo m; + uint16_t *ptr; + + // only available in VBE 2.0+ + if (_vbeInfo.vbeVersion < 0x200) return NULL; + + r.x.ax = 0x4F0A; + r.x.bx = 0; + __dpmi_int(0x10, &r); + if (r.x.ax != 0x004F) return NULL; + + // copy interface + _pmodeInterfacePtr = (PModeInterfaceT *)malloc(r.x.cx); + dosmemget((r.x.es * 16) + r.x.di, r.x.cx, _pmodeInterfacePtr); + _go32_dpmi_lock_data(_pmodeInterfacePtr, r.x.cx); + + // need memory-mapped IO? + if (_pmodeInterfacePtr->ioInfo) { + ptr = (uint16_t *)((char *)_pmodeInterfacePtr + _pmodeInterfacePtr->ioInfo); + // skip the port table... + while (*ptr != 0xFFFF) + ptr++; + ptr++; + // ...and get descriptor + if (*ptr != 0xFFFF) { + m.address = *((uint32_t *)ptr); + m.size = *(ptr + 2); + if (__dpmi_physical_address_mapping(&m) != 0) return NULL; + _vbeSurface.ioLinear = m.address; + __dpmi_lock_linear_region(&m); + _vbeSurface.ioSegment = __dpmi_allocate_ldt_descriptors(1); + if (_vbeSurface.ioSegment < 0) { + __dpmi_unlock_linear_region(&m); + __dpmi_free_physical_address_mapping(&m); + return NULL; + } + __dpmi_set_segment_base_address(_vbeSurface.ioSegment, _vbeSurface.ioLinear); + __dpmi_set_segment_limit(_vbeSurface.ioSegment, m.size - 1); + } + } + + pmVBESetDisplayStart = (void *)((char *)_pmodeInterfacePtr + _pmodeInterfacePtr->setDisplayStart); + + return pmVBESetDisplayStart; +} + + +static uint8_t vbeIsDesiredMode(void) { + // This is a custom filter to remove modes not supported by the calling application. + + // Must be linear. + if (((_vbeModeInfo.modeAttributes) & (1<<7)) >> 7) { + // Packed or Direct Color mode. + if (_vbeModeInfo.memoryModel == VBE_MM_PACKED || _vbeModeInfo.memoryModel == VBE_MM_DCOLOR) { + // We only handle these bit depths. + if ((_vbeModeInfo.bitsPerPixel == 8) || (_vbeModeInfo.bitsPerPixel == 16) || (_vbeModeInfo.bitsPerPixel == 32)) { + // Resolution minimum of 640x480. + if (_vbeModeInfo.xResolution >= 640 && _vbeModeInfo.yResolution >= 480) { + // Multiple of 8 + if (DIVISIBLE_BY_EIGHT(_vbeModeInfo.xResolution) && DIVISIBLE_BY_EIGHT(_vbeModeInfo.yResolution)) { + // Valid mode! + return 1; + } + } + } + } + } + + // Rejected! + return 0; +} + + +static uint16_t vbeSelectModeNumber(uint16_t xRes, uint16_t yRes, uint8_t bpp) { + uint16_t counter; + + if (_vbeSurface.vbeBoolean == 0) return(0); + + for (counter = 0; ; counter++) { + if (_vbeInfo.videoModePtr[counter] == 0xFFFF) return(0); + vbeGetModeInfo(_vbeInfo.videoModePtr[counter]); + if (vbeIsDesiredMode()) { + if (_vbeModeInfo.xResolution == xRes && _vbeModeInfo.yResolution == yRes && _vbeModeInfo.bitsPerPixel == bpp) + break; + } + } + + return(_vbeInfo.videoModePtr[counter]); +} + + +static VBESurfaceT *vbeSetMode(uint16_t vbeModeNumber) { + __dpmi_regs r; + __dpmi_meminfo m; + uint32_t counter; + + if (_vbeSurface.vbeBoolean == 0) return NULL; + if (_vbeSurface.vbeInitBoolean == 1) return NULL; + if (vbeGetModeInfo(vbeModeNumber) == 0) return NULL; + + if (_yTable) free(_yTable); + if ((_yTable = malloc(4 * (_vbeModeInfo.yResolution + 1))) == 0) return NULL; + for (counter = 0; counter <= _vbeModeInfo.yResolution; counter++) { + _yTable[counter] = _vbeModeInfo.xResolution * counter;// * ((_vbeModeInfo.bitsPerPixel + 1) / 8); + } + + // request frame buffer + r.x.ax = 0x4F02; + r.x.bx = (vbeModeNumber | 0x4000); + __dpmi_int(0x10, &r); + if (r.x.ax != 0x004F) return(0); + + m.size = (_vbeInfo.totalMemory * 64 * 1024); + m.address = _vbeModeInfo.physBasePtr; + __dpmi_physical_address_mapping(&m); + __dpmi_lock_linear_region(&m); + + _vbeSurface.lfbLinearAddress = m.address; + _vbeSurface.lfbSelector = __dpmi_allocate_ldt_descriptors(1); + + __dpmi_set_segment_base_address(_vbeSurface.lfbSelector, _vbeSurface.lfbLinearAddress); + __dpmi_set_segment_limit(_vbeSurface.lfbSelector, (_vbeInfo.totalMemory * 64 * 1024) - 1); + + _vbeSurface.lfbNearPtr = (void *)(_vbeSurface.lfbLinearAddress + __djgpp_conventional_base); + _vbeSurface.xResolution = _vbeModeInfo.xResolution; + _vbeSurface.yResolution = _vbeModeInfo.yResolution; + _vbeSurface.bitsPerPixel = _vbeModeInfo.bitsPerPixel; + _vbeSurface.virtualXResolution = _vbeModeInfo.xResolution; + _vbeSurface.bytesPerPixel = (_vbeModeInfo.bitsPerPixel + 1) / 8; + _vbeSurface.centerX = _vbeSurface.virtualXResolution / 2; + _vbeSurface.centerY = _vbeSurface.yResolution / 2; + _vbeSurface.numberOfOffscreens = vbeSetScanlineLength(_vbeModeInfo.xResolution) / _vbeModeInfo.yResolution; + _vbeSurface.vbeInitBoolean = 1; + _vbeSurface.screenBytes = (_vbeSurface.xResolution * _vbeSurface.yResolution * _vbeSurface.bytesPerPixel); + _vbeSurface.screenDWords = _vbeSurface.screenBytes / 4; + _vbeSurface.lfbMemSize = (_vbeSurface.xResolution * _vbeSurface.yResolution * _vbeSurface.bytesPerPixel) / 1024 + _vbeModeInfo.offScreenMemSize; + + for (counter = 0; counter < (_vbeSurface.lfbMemSize * 1024); counter++) { + _farpokeb(_vbeSurface.lfbSelector, counter, 0x0); // clear Lfb + } + + if (_vbeModeInfo.memoryModel == VBE_MM_PACKED) { + vbeCreatePalette(); + + _vbeModeInfo.redFieldPosition = 5; + _vbeModeInfo.greenFieldPosition = 2; + _vbeModeInfo.blueFieldPosition = 0; + _vbeModeInfo.rsvdFieldPosition = 8; + + _vbeModeInfo.redMaskSize = 3; + _vbeModeInfo.greenMaskSize = 3; + _vbeModeInfo.blueMaskSize = 2; + _vbeModeInfo.rsvdMaskSize = 0; + } + + _vbeSurface.rMask = ((1UL << _vbeModeInfo.redMaskSize) - 1) << _vbeModeInfo.redFieldPosition; + _vbeSurface.gMask = ((1UL << _vbeModeInfo.greenMaskSize) - 1) << _vbeModeInfo.greenFieldPosition; + _vbeSurface.bMask = ((1UL << _vbeModeInfo.blueMaskSize) - 1) << _vbeModeInfo.blueFieldPosition; + _vbeSurface.aMask = ((1UL << _vbeModeInfo.rsvdMaskSize) - 1) << _vbeModeInfo.rsvdFieldPosition; + + _vbeSurface.rShift = 8 - _vbeModeInfo.redMaskSize; + _vbeSurface.gShift = 8 - _vbeModeInfo.greenMaskSize; + _vbeSurface.bShift = 8 - _vbeModeInfo.blueMaskSize; + _vbeSurface.aShift = 8 - _vbeModeInfo.rsvdMaskSize; + + _vbeSurface.rPos = _vbeModeInfo.redFieldPosition; + _vbeSurface.gPos = _vbeModeInfo.greenFieldPosition; + _vbeSurface.bPos = _vbeModeInfo.blueFieldPosition; + _vbeSurface.aPos = _vbeModeInfo.rsvdFieldPosition; + + /* + logWrite("VESA Red Mask %u Shift %u Loss %u\n", _vbeSurface.rMask, _vbeSurface.rPos, _vbeSurface.rShift); + logWrite("VESA Green Mask %u Shift %u Loss %u\n", _vbeSurface.gMask, _vbeSurface.gPos, _vbeSurface.gShift); + logWrite("VESA Blue Mask %u Shift %u Loss %u\n", _vbeSurface.bMask, _vbeSurface.bPos, _vbeSurface.bShift); + logWrite("VESA Alpha Mask %u Shift %u Loss %u\n\n", _vbeSurface.aMask, _vbeSurface.aPos, _vbeSurface.aShift); + */ + + return(&_vbeSurface); +} + + +static uint16_t vbeSetScanlineLength(uint16_t pixelLength) { + __dpmi_regs r; + + if (_vbeSurface.vbeBoolean == 0) return(0); + + r.x.ax = 0x4F06; + r.x.bx = 0x0000; + r.x.cx = pixelLength; + __dpmi_int(0x10, &r); + if (r.h.ah != 0) return(0); + if (r.x.cx != pixelLength) return(0); + + _vbeSurface.virtualXResolution = pixelLength; + + return(r.x.dx); +} + + +void videoBlit(int16_t targetX, int16_t targetY, SurfaceT *source) { + + //***TODO*** Does not handle partial blits at this time. + (void)targetX; + (void)targetY; + + _movedatal(_my_ds(), (int32_t)source->buffer.bits32, _vbeSurface.lfbSelector, 0x0, _vbeSurface.screenDWords); +} + + +uint16_t videoDisplayHeightGet(void) { + return _vbeSurface.yResolution; +} + + +uint16_t videoDisplayWidthGet(void) { + return _vbeSurface.xResolution; +} + + +void videoModesShow(void) { + int8_t counter; + + // 0 1 2 3 4 5 6 7 8 + // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 + //printf("VBE 2.0 driver v1.0 (c) 2021, Scott Duensing \n"); + //printf("Based on: VBE 2.0 driver v1.0 (c) 1999, Tobias Koch \n\n"); + + if (vbeGetInfo() == NULL) { + logWrite("No VESA BIOS Extensions found.\n"); + platformShutdown(); + return; + } + logWrite( + "Video Memory - %d KB\n" + "VBE Version - %d.%d detected\n" + "OEM Specification - %s\n", + _vbeInfo.totalMemory * 64, + _vbeInfo.vbeVersion >> 8, _vbeInfo.vbeVersion & 0x00FF, + _vbeInfo.oemStringPtr); + + if (_vbeInfo.vbeVersion >= 0x0200) { + logWrite( + "OEM Software Revision - %d.%d\n" + "OEM Vendor Name - %s\n" + "OEM Product Name - %s\n" + "OEM Product Revision - %s\n" + "Protected Mode Interface - %s\n\n", + _vbeInfo.oemSoftwareRev >> 8, _vbeInfo.oemSoftwareRev & 0x00FF, + _vbeInfo.oemVendorNamePtr, + _vbeInfo.oemProductNamePtr, + _vbeInfo.oemProductRevPtr, + vbeGetPmodeInterface() ? "Found" : "Missing"); + } else { + logWrite("VESA BIOS Extension 2.0 or better required!\n"); + platformShutdown(); + return; + } + + for (counter=0; ; counter++) { + if (_vbeInfo.videoModePtr[counter] == 0xFFFF) break; + vbeGetModeInfo(_vbeInfo.videoModePtr[counter]); + if (vbeIsDesiredMode()) { + logWrite("Mode %Xh - %4d x %4d x %2d - RGB %d:%d:%d - %s\n", + _vbeInfo.videoModePtr[counter], + _vbeModeInfo.xResolution, + _vbeModeInfo.yResolution, + _vbeModeInfo.bitsPerPixel, + _vbeModeInfo.redMaskSize, + _vbeModeInfo.greenMaskSize, + _vbeModeInfo.blueMaskSize, + ((_vbeModeInfo.memoryModel / 2) - 2) ? "DCOLOR" : "PACKED"); + } + } + + platformShutdown(); +} + + +#endif // __DJGPP__ diff --git a/dyn/kpsplatf/kpsplatf.h b/dyn/kpsplatf/kpsplatf.h new file mode 100644 index 0000000..c2ddcad --- /dev/null +++ b/dyn/kpsplatf/kpsplatf.h @@ -0,0 +1,71 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2026 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E 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 Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#ifndef KPSPLATF_H +#define KPSPLATF_H + + +#include "roo_e.h" +#include "kpssurf.h" + + +#define EVENT_FLAG_KEYPRESS 1 +#define EVENT_FLAG_LEFT_DOWN 2 +#define EVENT_FLAG_LEFT_UP 4 +#define EVENT_FLAG_RIGHT_DOWN 8 +#define EVENT_FLAG_RIGHT_UP 16 + +#define BUTTON_LEFT 1 +#define BUTTON_RIGHT 2 + +#define META_ALT 1 +#define META_CTRL 2 +#define META_SHIFT 4 + +#define KEY_ESC 27 + + +typedef struct PlatformEventS { + int32_t flags; + int32_t x; + int32_t y; + int32_t buttons; + int32_t key; + int32_t kbstat; + int32_t dtime; +} PlatformEventT; + + +void platformEventGet(PlatformEventT *event); +void platformShutdown(void); +uint8_t platformStartup(int16_t width, int16_t height, int16_t depth); + +void videoBlit(int16_t targetX, int16_t targetY, SurfaceT *source); +uint16_t videoDisplayHeightGet(void); +uint16_t videoDisplayWidthGet(void); +void videoModesShow(void); + + +#endif // KPSPLATF_H diff --git a/dyn/kpsplatf/sdl2.c b/dyn/kpsplatf/sdl2.c new file mode 100644 index 0000000..52c8c59 --- /dev/null +++ b/dyn/kpsplatf/sdl2.c @@ -0,0 +1,230 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2026 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E 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 Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#ifdef __linux__ + + +#include +#include "kpsplatf.h" + + +static SDL_Window *_window = NULL; +static SDL_Renderer *_renderer = NULL; +static SDL_Texture *_texture = NULL; +static uint16_t _width = 0; +static uint16_t _height = 0; +static uint8_t _windowScale = 1; + + +void platformEventGet(PlatformEventT *event) { + SDL_Event e; + static uint8_t ASCII = 0; + static uint8_t debounce = 0; + static uint8_t buttons = 0; + static int16_t x = 0; + static int16_t y = 0; + + memset(event, 0, sizeof(EventT)); + + while (SDL_PollEvent(&e)) { + switch (e.type) { + case SDL_MOUSEMOTION: + x = e.motion.x; + y = e.motion.y; + break; + + case SDL_MOUSEBUTTONUP: + if (e.button.button == SDL_BUTTON_LEFT) { + buttons &= ~BUTTON_LEFT; + event->flags |= EVENT_FLAG_LEFT_UP; + } + if (e.button.button == SDL_BUTTON_RIGHT) { + buttons &= ~BUTTON_RIGHT; + event->flags |= EVENT_FLAG_RIGHT_UP; + } + break; + + case SDL_MOUSEBUTTONDOWN: + if (e.button.button == SDL_BUTTON_LEFT) { + buttons |= BUTTON_LEFT; + event->flags |= EVENT_FLAG_LEFT_DOWN; + } + if (e.button.button == SDL_BUTTON_RIGHT) { + buttons |= BUTTON_RIGHT; + event->flags |= EVENT_FLAG_RIGHT_DOWN; + } + break; + + case SDL_KEYUP: + ASCII = 0; + debounce = 0; + break; + + case SDL_KEYDOWN: + if (debounce == 0) { + if (e.key.keysym.scancode != 0) { + // Not a meta key + debounce = 1; + ASCII = e.key.keysym.sym; + //if (e.key.keysym.scancode == SDL_SCANCODE_ESCAPE) ASCII = 27; + event->key = ASCII; + event->flags |= EVENT_FLAG_KEYPRESS; + } + } + break; + } + } + + event->x = x; + event->y = y; + event->buttons = buttons; + if ((SDL_GetModState() & KMOD_ALT) != 0) event->kbstat |= META_ALT; + if ((SDL_GetModState() & KMOD_CTRL) != 0) event->kbstat |= META_CTRL; + if ((SDL_GetModState() & KMOD_SHIFT) != 0) event->kbstat |= META_SHIFT; +} + + +void platformShutdown(void) { + + SDL_ShowCursor(SDL_ENABLE); + + surfaceShutdown(); + + if (_texture) { + SDL_DestroyTexture(_texture); + _texture = NULL; + } + + if (_renderer) { + SDL_DestroyRenderer(_renderer); + _renderer = NULL; + } + + if (_window) { + SDL_DestroyWindow(_window); + _window = NULL; + } + + SDL_Quit(); +} + + +uint8_t platformStartup(int16_t width, int16_t height, int16_t depth) { + SDL_PixelFormatEnum pixelFormat = SDL_PIXELFORMAT_ARGB8888; + + (void)depth; + + /* + SDL_Surface *bits8 = SDL_CreateRGBSurfaceWithFormat(0, 320, 200, 8, SDL_PIXELFORMAT_RGB332); + SDL_Surface *bits16 = SDL_CreateRGBSurfaceWithFormat(0, 320, 200, 8, SDL_PIXELFORMAT_RGB565); + SDL_Surface *bits32 = SDL_CreateRGBSurfaceWithFormat(0, 320, 200, 8, SDL_PIXELFORMAT_ARGB8888); + + logWrite("8 Red Mask %u Shift %u Loss %u\n", bits8->format->Rmask, bits8->format->Rshift, bits8->format->Rloss); + logWrite("8 Green Mask %u Shift %u Loss %u\n", bits8->format->Gmask, bits8->format->Gshift, bits8->format->Gloss); + logWrite("8 Blue Mask %u Shift %u Loss %u\n", bits8->format->Bmask, bits8->format->Bshift, bits8->format->Bloss); + logWrite("8 Alpha Mask %u Shift %u Loss %u\n\n", bits8->format->Amask, bits8->format->Ashift, bits8->format->Aloss); + + logWrite("16 Red Mask %u Shift %u Loss %u\n", bits16->format->Rmask, bits16->format->Rshift, bits16->format->Rloss); + logWrite("16 Green Mask %u Shift %u Loss %u\n", bits16->format->Gmask, bits16->format->Gshift, bits16->format->Gloss); + logWrite("16 Blue Mask %u Shift %u Loss %u\n", bits16->format->Bmask, bits16->format->Bshift, bits16->format->Bloss); + logWrite("16 Alpha Mask %u Shift %u Loss %u\n\n", bits16->format->Amask, bits16->format->Ashift, bits16->format->Aloss); + + logWrite("32 Red Mask %u Shift %u Loss %u\n", bits32->format->Rmask, bits32->format->Rshift, bits32->format->Rloss); + logWrite("32 Green Mask %u Shift %u Loss %u\n", bits32->format->Gmask, bits32->format->Gshift, bits32->format->Gloss); + logWrite("32 Blue Mask %u Shift %u Loss %u\n", bits32->format->Bmask, bits32->format->Bshift, bits32->format->Bloss); + logWrite("32 Alpha Mask %u Shift %u Loss %u\n\n", bits32->format->Amask, bits32->format->Ashift, bits32->format->Aloss); + */ + + switch (depth) { + case 8: + pixelFormat = SDL_PIXELFORMAT_RGB332; + break; + + case 16: + pixelFormat = SDL_PIXELFORMAT_RGB565; + break; + + case 32: + pixelFormat = SDL_PIXELFORMAT_ARGB8888; + break; + } + + SDL_Init(SDL_INIT_EVERYTHING); + + _windowScale = 1; + + _window = SDL_CreateWindow("GUI Debug", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_ALLOW_HIGHDPI); + _renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED); + _texture = SDL_CreateTexture(_renderer, pixelFormat, SDL_TEXTUREACCESS_STREAMING, width, height); + + SDL_RenderSetLogicalSize(_renderer, width, height); + SDL_SetWindowSize(_window, width * _windowScale, height * _windowScale); + + SDL_ShowCursor(SDL_DISABLE); + + _width = width; + _height = height; + + surfaceStartup(depth); + + return SUCCESS; +} + + +void videoBlit(int16_t targetX, int16_t targetY, SurfaceT *source) { + void *pixels; + int temp; + + //***TODO*** Does not handle partial blits at this time. + (void)targetX; + (void)targetY; + + SDL_LockTexture(_texture, NULL, &pixels, &temp); + memcpy(pixels, source->buffer.bits8, source->bytes); + SDL_UnlockTexture(_texture); + SDL_RenderCopy(_renderer, _texture, NULL, NULL); + SDL_RenderPresent(_renderer); + + // Throttle this to some sane frame rate. + SDL_Delay(32); +} + + +uint16_t videoDisplayHeightGet(void) { + return _height; +} + + +uint16_t videoDisplayWidthGet(void) { + return _width; +} + + +void videoModesShow(void) { + // Nada +} + + +#endif // __linux__ diff --git a/dyn/kpssurf/kpssurf.c b/dyn/kpssurf/kpssurf.c new file mode 100644 index 0000000..dc8caad --- /dev/null +++ b/dyn/kpssurf/kpssurf.c @@ -0,0 +1,570 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2026 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E 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 Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#include "kpssurf.h" + + +SurfaceT *__surfaceActive = NULL; +uint8_t __surfaceBitsPerPixel = 0; +uint8_t __surfaceBytesPerPixel = 0; +SurfaceFormatT __surfaceFormat = { 0 }; + + +void (*surfaceLineH)(int16_t x1, int16_t x2, int16_t y, ColorT c); +void (*surfaceLineV)(int16_t x, int16_t y1, int16_t y2, ColorT c); +ColorT (*surfacePixelGet)(SurfaceT *surface, int16_t x, int16_t y); +void (*surfacePixelSet)(uint16_t x, uint16_t y, ColorT color); + + +static void surfaceLineH8(int16_t x1, int16_t x2, int16_t y, ColorT c); +static void surfaceLineH16(int16_t x1, int16_t x2, int16_t y, ColorT c); +static void surfaceLineH32(int16_t x1, int16_t x2, int16_t y, ColorT c); + +static void surfaceLineV8(int16_t x, int16_t y1, int16_t y2, ColorT c); +static void surfaceLineV16(int16_t x, int16_t y1, int16_t y2, ColorT c); +static void surfaceLineV32(int16_t x, int16_t y1, int16_t y2, ColorT c); + +static ColorT surfacePixelGet8(SurfaceT *surface, int16_t x, int16_t y); +static ColorT surfacePixelGet16(SurfaceT *surface, int16_t x, int16_t y); +static ColorT surfacePixelGet32(SurfaceT *surface, int16_t x, int16_t y); + +static void surfacePixelSet8(uint16_t x, uint16_t y, ColorT color); +static void surfacePixelSet16(uint16_t x, uint16_t y, ColorT color); +static void surfacePixelSet32(uint16_t x, uint16_t y, ColorT color); + + +void surfaceBlit(int16_t targetX, int16_t targetY, int16_t offsetX, int16_t offsetY, int16_t width, int16_t height, SurfaceT *source) { + int16_t i; + int16_t x = 0; + int16_t y = 0; + size_t bytes; + size_t offsetTarget; + size_t offsetSource; + + // Did they provide a valid width/height? + if (width <= 0 || width > source->width) width = source->width; + if (height <= 0 || height > source->height) height = source->height; + + // Clip on top and left. x1 & y1 are pixel locations inside the source bitmap. + if (targetX < 0) x = -targetX; + if (targetY < 0) y = -targetY; + + // Clip on right and bottom. + if (targetX + width - offsetX > __surfaceActive->width) width -= targetX + width - offsetX - __surfaceActive->width; + if (targetY + height - offsetY > __surfaceActive->height) height -= targetY + height - offsetY - __surfaceActive->height; + + // Are we still on the screen? + if (x < 0 || y < 0 || width < x || height < y) return; + + if (targetX == 0 && targetY == 0 && __surfaceActive->width == source->width && __surfaceActive->height == source->height) { + // Direct blit of entire surface. + memcpy(__surfaceActive->buffer.bits8, source->buffer.bits8, source->bytes); + } else { + // Blit into larger surface. + offsetTarget = (targetY + y) * __surfaceActive->scanline + (targetX + x) * __surfaceBytesPerPixel; + offsetSource = (y + offsetY) * source->scanline + (x + offsetX) * __surfaceBytesPerPixel; + bytes = (width - x) * __surfaceBytesPerPixel; + for (i=y; ibuffer.bits8[offsetTarget], &source->buffer.bits8[offsetSource], bytes); + offsetTarget += __surfaceActive->scanline; + offsetSource += source->scanline; + } + } +} + + +void surfaceBlitWithTransparency(int16_t targetX, int16_t targetY, SurfaceT *source, ColorT transparent) { + int16_t x; + int16_t y; + int16_t x1 = 0; + int16_t y1 = 0; + int16_t x2 = source->width; + int16_t y2 = source->height; + ColorT pixel; + + // Clip on top and left. x1 & y1 are pixel locations inside the source bitmap. ox & oy offset those into screen coordinates. + if (targetX < 0) x1 = -targetX; + if (targetY < 0) y1 = -targetY; + + // Clip on right and bottom. + if (targetX + x2 > __surfaceActive->width) x2 -= targetX + x2 - __surfaceActive->width; + if (targetY + y2 > __surfaceActive->height) y2 -= targetY + y2 - __surfaceActive->height; + + // Are we still on the screen? + if (x1 < 0 || y1 < 0 || x2 < x1 || y2 < y1) return; + + // Blit. + for (y=y1; ywidth - 1, 0, color); + + // Copy it to the other lines. + offsetTarget = __surfaceActive->scanline; + for (x=1; x<__surfaceActive->height; x++) { + memcpy(&__surfaceActive->buffer.bits8[offsetTarget], &__surfaceActive->buffer.bits8[0], __surfaceActive->scanline); + offsetTarget += __surfaceActive->scanline; + } +} + + +ColorT surfaceColorMake(uint8_t r, uint8_t g, uint8_t b) { + return + (r >> __surfaceFormat.rLoss) << __surfaceFormat.rShift | + (g >> __surfaceFormat.gLoss) << __surfaceFormat.gShift | + (b >> __surfaceFormat.bLoss) << __surfaceFormat.bShift | + ((255 >> __surfaceFormat.aLoss) << __surfaceFormat.aShift & __surfaceFormat.aMask); +} + + +SurfaceT *surfaceCreate(int16_t width, int16_t height) { + SurfaceT *surface = NULL; + + NEW(SurfaceT, surface); + if (!surface) return NULL; + + surface->width = width; + surface->height = height; + surface->scanline = width * __surfaceBytesPerPixel; + surface->bytes = surface->scanline * height; + + surface->buffer.bits8 = malloc(surface->bytes); + if (!surface->buffer.bits8) { + free(surface); + return NULL; + } + + return surface; +} + + +void surfaceBox(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT c) { + surfaceLineH(x1, x2, y1, c); + surfaceLineH(x1, x2, y2, c); + surfaceLineV(x1, y1, y2, c); + surfaceLineV(x2, y1, y2, c); +} + + +void surfaceBoxFilled(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT c) { + int16_t i; + size_t offsetTarget; + size_t offsetSource; + uint16_t width; + + if (x1 > x2) { + i = x1; + x1 = x2; + x2 = i; + } + + if (y1 > y2) { + i = y1; + y1 = y2; + y2 = i; + } + + width = (x2 - x1 + 1) * __surfaceBytesPerPixel; + + // Draw the top line. + surfaceLineH(x1, x2, y1, c); + + // Copy it to the other lines. + offsetTarget = __surfaceActive->scanline * (y1 + 1) + (x1 * __surfaceBytesPerPixel); + offsetSource = __surfaceActive->scanline * y1 + (x1 * __surfaceBytesPerPixel); + for (i=y1 + 1; i<=y2; i++) { + memcpy(&__surfaceActive->buffer.bits8[offsetTarget], &__surfaceActive->buffer.bits8[offsetSource], width); + offsetTarget += __surfaceActive->scanline; + } +} + + +void surfaceBoxHighlight(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT highlight, ColorT shadow) { + surfaceLineH(x1, x2, y1, highlight); + surfaceLineV(x1, y1, y2, highlight); + surfaceLineH(x1, x2, y2, shadow); + surfaceLineV(x2, y1, y2, shadow); +} + + +void surfaceDestroy(SurfaceT **surface) { + SurfaceT *s = *surface; + DEL(s->buffer.bits8); + DEL(s); + *surface = NULL; +} + + +SurfaceT *surfaceGet(void) { + return __surfaceActive; +} + + +int16_t surfaceHeightGet(SurfaceT *surface) { + return surface->height; +} + + +void surfaceLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color) { + int16_t x; + int16_t y; + int16_t dx; + int16_t dy; + int16_t incX; + int16_t incY; + int16_t balance; + + if (x2 >= x1) { + dx = x2 - x1; + incX = 1; + } else { + dx = x1 - x2; + incX = -1; + } + + if (y2 >= y1) { + dy = y2 - y1; + incY = 1; + } else { + dy = y1 - y2; + incY = -1; + } + + x = x1; + y = y1; + + if (dx >= dy) { + dy <<= 1; + balance = dy - dx; + dx <<= 1; + while (x != x2) { + surfacePixelSet(x, y, color); + if (balance >= 0) { + y += incY; + balance -= dx; + } + balance += dy; + x += incX; + } + surfacePixelSet(x, y, color); + } else { + dx <<= 1; + balance = dx - dy; + dy <<= 1; + while (y != y2) { + surfacePixelSet(x, y, color); + if (balance >= 0) { + x += incX; + balance -= dy; + } + balance += dx; + y += incY; + } + surfacePixelSet(x, y, color); + } +} + + +static void surfaceLineH8(int16_t x1, int16_t x2, int16_t y, ColorT c) { + int16_t i; + size_t offset; + + if (x1 > x2) { + i = x2; + x2 = x1; + x1 = i; + } + + offset = y * __surfaceActive->width + x1; + for (i=x1; i<=x2; i++) __surfaceActive->buffer.bits8[offset++] = (uint8_t)c; +} + + +static void surfaceLineH16(int16_t x1, int16_t x2, int16_t y, ColorT c) { + int16_t i; + size_t offset; + + if (x1 > x2) { + i = x2; + x2 = x1; + x1 = i; + } + + offset = y * __surfaceActive->width + x1; + for (i=x1; i<=x2; i++) __surfaceActive->buffer.bits16[offset++] = (uint16_t)c; +} + + +static void surfaceLineH32(int16_t x1, int16_t x2, int16_t y, ColorT c) { + int16_t i; + size_t offset; + + if (x1 > x2) { + i = x2; + x2 = x1; + x1 = i; + } + + offset = y * __surfaceActive->width + x1; + for (i=x1; i<=x2; i++) __surfaceActive->buffer.bits32[offset++] = (uint32_t)c; +} + + +static void surfaceLineV8(int16_t x, int16_t y1, int16_t y2, ColorT c) { + int16_t i; + size_t offset; + + if (y1 > y2) { + i = y2; + y2 = y1; + y1 = i; + } + + offset = y1 * __surfaceActive->width + x; + for (i=y1; i<=y2; i++) { + __surfaceActive->buffer.bits8[offset] = (uint8_t)c; + offset += __surfaceActive->width; + } +} + + +static void surfaceLineV16(int16_t x, int16_t y1, int16_t y2, ColorT c) { + int16_t i; + size_t offset; + + if (y1 > y2) { + i = y2; + y2 = y1; + y1 = i; + } + + offset = y1 * __surfaceActive->width + x; + for (i=y1; i<=y2; i++) { + __surfaceActive->buffer.bits16[offset] = (uint16_t)c; + offset += __surfaceActive->width; + } +} + + +static void surfaceLineV32(int16_t x, int16_t y1, int16_t y2, ColorT c) { + int16_t i; + size_t offset; + + if (y1 > y2) { + i = y2; + y2 = y1; + y1 = i; + } + + offset = y1 * __surfaceActive->width + x; + for (i=y1; i<=y2; i++) { + __surfaceActive->buffer.bits32[offset] = (uint32_t)c; + offset += __surfaceActive->width; + } +} + + +SurfaceT *surfaceLoad(char *filename) { + char ext[5] = { 0 }; + char *name = NULL; + FILE *in = NULL; + SurfaceT *i = NULL; + + sprintf(ext, "S%d", __surfaceBitsPerPixel); + name = utilFileExtensionChange(filename, ext); + + if (!utilFileExists(name)) { + in = fopen(name, "rb"); + if (in) { + NEW(SurfaceT, i); + if (!i) return NULL; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-result" + fread(&i->width, sizeof(uint16_t), 1, in); + fread(&i->height, sizeof(uint16_t), 1, in); + fread(&i->scanline, sizeof(size_t), 1, in); + fread(&i->bytes, sizeof(size_t), 1, in); + i->buffer.bits8 = (uint8_t *)malloc(i->bytes); + fread(i->buffer.bits8, i->bytes, 1, in); +#pragma GCC diagnostic pop + fclose(in); + } + } + + DEL(name); + + return i; +} + + +static ColorT surfacePixelGet8(SurfaceT *surface, int16_t x, int16_t y) { + return surface->buffer.bits8[y * surface->width + x]; +} + + +static ColorT surfacePixelGet16(SurfaceT *surface, int16_t x, int16_t y) { + return surface->buffer.bits16[y * surface->width + x]; +} + + +static ColorT surfacePixelGet32(SurfaceT *surface, int16_t x, int16_t y) { + return surface->buffer.bits32[y * surface->width + x]; +} + + +static void surfacePixelSet8(uint16_t x, uint16_t y, ColorT color) { + __surfaceActive->buffer.bits8[y * __surfaceActive->width + x] = (uint8_t)color; +} + + +static void surfacePixelSet16(uint16_t x, uint16_t y, ColorT color) { + __surfaceActive->buffer.bits16[y * __surfaceActive->width + x] = (uint16_t)color; +} + + +static void surfacePixelSet32(uint16_t x, uint16_t y, ColorT color) { + __surfaceActive->buffer.bits32[y * __surfaceActive->width + x] = color; +} + + +void surfaceSave(SurfaceT *surface, char *filename) { + char ext[5] = { 0 }; + char *name = NULL; + FILE *out = NULL; + + sprintf(ext, "S%d", __surfaceBitsPerPixel); + name = utilFileExtensionChange(filename, ext); + + out = fopen(name, "wb"); + if (out) { + fwrite(&surface->width, sizeof(uint16_t), 1, out); + fwrite(&surface->height, sizeof(uint16_t), 1, out); + fwrite(&surface->scanline, sizeof(size_t), 1, out); + fwrite(&surface->bytes, sizeof(size_t), 1, out); + fwrite(surface->buffer.bits8, surface->bytes, 1, out); + fclose(out); + } + + DEL(name); +} + + +void surfaceSet(SurfaceT *surface) { + __surfaceActive = surface; +} + + +void surfaceShutdown(void) { + // Nada +} + + +void surfaceStartup(uint8_t bits) { + uint8_t redMaskSize; + uint8_t greenMaskSize; + uint8_t blueMaskSize; + uint8_t alphaMaskSize; + + __surfaceBitsPerPixel = bits; + __surfaceBytesPerPixel = bits >> 3; + + switch (bits) { + case 8: + // xxx 3:3:2 + alphaMaskSize = 0; + redMaskSize = 3; + greenMaskSize = 3; + blueMaskSize = 2; + surfaceLineH = surfaceLineH8; + surfaceLineV = surfaceLineV8; + surfacePixelSet = surfacePixelSet8; + surfacePixelGet = surfacePixelGet8; + break; + + case 16: + // xx 5:6:5 + alphaMaskSize = 0; + redMaskSize = 5; + greenMaskSize = 6; + blueMaskSize = 5; + surfaceLineH = surfaceLineH16; + surfaceLineV = surfaceLineV16; + surfacePixelSet = surfacePixelSet16; + surfacePixelGet = surfacePixelGet16; + break; + + default: + // x 8:8:8 + alphaMaskSize = 8; + redMaskSize = 8; + greenMaskSize = 8; + blueMaskSize = 8; + surfaceLineH = surfaceLineH32; + surfaceLineV = surfaceLineV32; + surfacePixelSet = surfacePixelSet32; + surfacePixelGet = surfacePixelGet32; + break; + } + + __surfaceFormat.bShift = 0; + __surfaceFormat.gShift = __surfaceFormat.bShift + blueMaskSize; + __surfaceFormat.rShift = __surfaceFormat.gShift + greenMaskSize; + __surfaceFormat.aShift = __surfaceFormat.rShift + redMaskSize; + + __surfaceFormat.rMask = ((1UL << redMaskSize) - 1) << __surfaceFormat.rShift; + __surfaceFormat.gMask = ((1UL << greenMaskSize) - 1) << __surfaceFormat.gShift; + __surfaceFormat.bMask = ((1UL << blueMaskSize) - 1) << __surfaceFormat.bShift; + __surfaceFormat.aMask = ((1UL << alphaMaskSize) - 1) << __surfaceFormat.aShift; + + __surfaceFormat.rLoss = 8 - redMaskSize; + __surfaceFormat.gLoss = 8 - greenMaskSize; + __surfaceFormat.bLoss = 8 - blueMaskSize; + __surfaceFormat.aLoss = 8 - alphaMaskSize; + + /* + logWrite("Surface Red Mask %u Shift %u Loss %u\n", __surfaceFormat.rMask, __surfaceFormat.rShift, __surfaceFormat.rLoss); + logWrite("Surface Green Mask %u Shift %u Loss %u\n", __surfaceFormat.gMask, __surfaceFormat.gShift, __surfaceFormat.gLoss); + logWrite("Surface Blue Mask %u Shift %u Loss %u\n", __surfaceFormat.bMask, __surfaceFormat.bShift, __surfaceFormat.bLoss); + logWrite("Surface Alpha Mask %u Shift %u Loss %u\n\n", __surfaceFormat.aMask, __surfaceFormat.aShift, __surfaceFormat.aLoss); + */ +} + + +int16_t surfaceWidthGet(SurfaceT *surface) { + return surface->width; +} diff --git a/dyn/kpssurf/kpssurf.h b/dyn/kpssurf/kpssurf.h new file mode 100644 index 0000000..328c0f3 --- /dev/null +++ b/dyn/kpssurf/kpssurf.h @@ -0,0 +1,95 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2026 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E 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 Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#ifndef KPSSURF_H +#define KPSSURF_H + + +#include "roo_e.h" + + +typedef uint32_t ColorT; + +typedef struct SurfaceS { + uint16_t width; + uint16_t height; + size_t scanline; + size_t bytes; + union { + uint8_t *bits8; + uint16_t *bits16; + uint32_t *bits32; + } buffer; +} SurfaceT; + +typedef struct SurfaceFormatS { + uint32_t rMask; + uint32_t gMask; + uint32_t bMask; + uint32_t aMask; + uint8_t rShift; + uint8_t gShift; + uint8_t bShift; + uint8_t aShift; + uint8_t rLoss; + uint8_t gLoss; + uint8_t bLoss; + uint8_t aLoss; +} SurfaceFormatT; + + +extern SurfaceT *__surfaceActive; +extern uint8_t __surfaceBitsPerPixel; +extern uint8_t __surfaceBytesPerPixel; +extern SurfaceFormatT __surfaceFormat; + + +extern void (*surfaceLineH)(int16_t x1, int16_t x2, int16_t y, ColorT c); +extern void (*surfaceLineV)(int16_t x, int16_t y1, int16_t y2, ColorT c); +extern ColorT (*surfacePixelGet)(SurfaceT *surface, int16_t x, int16_t y); +extern void (*surfacePixelSet)(uint16_t x, uint16_t y, ColorT color); + + +void surfaceBlit(int16_t targetX1, int16_t targetY1, int16_t offsetX, int16_t offsetY, int16_t width, int16_t height, SurfaceT *source); +void surfaceBlitWithTransparency(int16_t targetX, int16_t targetY, SurfaceT *source, ColorT transparent); +void surfaceClear(ColorT color); +ColorT surfaceColorMake(uint8_t r, uint8_t g, uint8_t b); +SurfaceT *surfaceCreate(int16_t width, int16_t height); +void surfaceBox(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT c); +void surfaceBoxFilled(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT c); +void surfaceBoxHighlight(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT highlight, ColorT shadow); +void surfaceDestroy(SurfaceT **surface); +SurfaceT *surfaceGet(void); +int16_t surfaceHeightGet(SurfaceT *surface); +void surfaceLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color); +SurfaceT *surfaceLoad(char *filename); +void surfaceSave(SurfaceT *surface, char *filename); +void surfaceSet(SurfaceT *surface); +void surfaceShutdown(void); +void surfaceStartup(uint8_t bits); +int16_t surfaceWidthGet(SurfaceT *surface); + + +#endif // KPSSURF_H diff --git a/dyn/kpsvideo/kpsvideo.c b/dyn/kpsvideo/kpsvideo.c deleted file mode 100644 index d003d00..0000000 --- a/dyn/kpsvideo/kpsvideo.c +++ /dev/null @@ -1,37 +0,0 @@ -/* Roo/E, the Kangaroo Punch Portable GUI Toolkit - * Copyright (C) 2026 Scott Duensing - * - * http://kangaroopunch.com - * - * - * This file is part of Roo/E. - * - * Roo/E is free software: you can redistribute it and/or modify it under the - * terms of the GNU Affero General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Roo/E 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 Affero General Public License - * along with Roo/E. If not, see . - */ - - -#include "roo_e.h" -#include "kpsvideo.h" - - -int dynStart(void) { - printf("kpsvideo starting!\n"); - return 0; -} - - -int dynStop(void) { - printf("kpsvideo stopping!\n"); - return 0; -} diff --git a/dyn/kpsvideo/kpsvideo.h b/dyn/kpsvideo/kpsvideo.h deleted file mode 100644 index 9d54738..0000000 --- a/dyn/kpsvideo/kpsvideo.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Roo/E, the Kangaroo Punch Portable GUI Toolkit - * Copyright (C) 2026 Scott Duensing - * - * http://kangaroopunch.com - * - * - * This file is part of Roo/E. - * - * Roo/E is free software: you can redistribute it and/or modify it under the - * terms of the GNU Affero General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Roo/E 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 Affero General Public License - * along with Roo/E. If not, see . - */ - - -#ifndef KPSVIDEO_H -#define KPSVIDEO_H - -#endif // KPSVIDEO_H diff --git a/env.sh b/env.sh index c323055..ddae6ba 100755 --- a/env.sh +++ b/env.sh @@ -25,4 +25,5 @@ eval "$(../toolchains/toolchains.sh use x86 dos)" -export C_INCLUDE_PATH=${HOME}/code/toolchains/x-tools/djgpp/i586-pc-msdosdjgpp/sys-include:roo_e +export CFLAGS="-DMEMWATCH" +export C_INCLUDE_PATH="${HOME}/code/toolchains/x-tools/djgpp/i586-pc-msdosdjgpp/sys-include:roo_e" diff --git a/font/src/main.c b/font/src/main.c index ad10c0f..737c04e 100644 --- a/font/src/main.c +++ b/font/src/main.c @@ -23,8 +23,7 @@ #include -#ifdef PLATFORM_LINUX -#define MEMWATCH +#ifdef MEMWATCH #include "memwatch/memwatch.h" #endif @@ -90,7 +89,6 @@ int main(int argc, char *argv[]) { (void)argc; (void)argv; - // Run this in DOS from the BIN folder. makeFont("../font/in/vga4x8.png", "fonts/vga4x8.fnt", 4, 8, 16, 256); makeFont("../font/in/vga8x8.png", "fonts/vga8x8.fnt", 8, 8, 16, 256); makeFont("../font/in/vga8x14.png", "fonts/vga8x14.fnt", 8, 14, 16, 256); diff --git a/roo_e/main.c b/roo_e/main.c index 4175daf..465f98f 100644 --- a/roo_e/main.c +++ b/roo_e/main.c @@ -27,12 +27,21 @@ static RooAppThreadT **_appThreadList = NULL; // NOLINT +static FILE *_memoryLog = NULL; // NOLINT +static FILE *_log = NULL; // NOLINT +static uint8_t _ourHandle = 0; // NOLINT +static char *_logBuffer = NULL; // NOLINT +static uint16_t _logBufferSize = 0; // NOLINT static void *rooStartAppThread(void *arg); -#ifdef PLATFORM_DOS +#ifdef MEMWATCH +void mwLogW(FILE *p); +#endif + +#ifdef __DJGPP__ #include void *dxeResolver(const char *symbol); static int lastResort(void); @@ -53,15 +62,58 @@ struct __emutls_object // NOLINT void *__emutls_get_address(struct __emutls_object *); // NOLINT #pragma GCC diagnostic pop DXE_EXPORT_TABLE(exports) + // bios + DXE_EXPORT(bioskey) + // ctype + DXE_EXPORT(toupper) + // dos + DXE_EXPORT(int86) + // dpmi + DXE_EXPORT(__dpmi_allocate_ldt_descriptors) + DXE_EXPORT(__dpmi_free_ldt_descriptor) + DXE_EXPORT(__dpmi_free_physical_address_mapping) + DXE_EXPORT(__dpmi_int) + DXE_EXPORT(__dpmi_lock_linear_region) + DXE_EXPORT(__dpmi_physical_address_mapping) + DXE_EXPORT(__dpmi_set_segment_base_address) + DXE_EXPORT(__dpmi_set_segment_limit) + DXE_EXPORT(__dpmi_unlock_linear_region) + DXE_EXPORT(_go32_dpmi_lock_data) // dxe DXE_EXPORT(dlclose) DXE_EXPORT(dlopen) DXE_EXPORT(dlregsym) DXE_EXPORT(dlstatbind) DXE_EXPORT(dlstatunbind) + // farptr + DXE_EXPORT(_farpokeb) + // go32 + DXE_EXPORT(_go32_info_block) // math DXE_EXPORT(ldexp) DXE_EXPORT(pow) + // movedata + DXE_EXPORT(_movedatal) + DXE_EXPORT(dosmemget) + DXE_EXPORT(dosmemput) + // pc + DXE_EXPORT(outportb) + // Roo/E + DXE_EXPORT(logClose) + DXE_EXPORT(logOpen) + DXE_EXPORT(logOpenByHandle) + DXE_EXPORT(logWrite) + DXE_EXPORT(logWriteToFileOnly) + DXE_EXPORT(utilCreateString) + DXE_EXPORT(utilCreateStringVArgs) + DXE_EXPORT(utilEndsWith) + DXE_EXPORT(utilFileExists) + DXE_EXPORT(utilFileExtensionChange) + // setjmp + DXE_EXPORT(longjmp) + DXE_EXPORT(setjmp) + // signal + DXE_EXPORT(signal) // stb_ds DXE_EXPORT(stbds_arrfreef) DXE_EXPORT(stbds_arrgrowf) @@ -81,26 +133,47 @@ DXE_EXPORT_TABLE(exports) DXE_EXPORT(fclose) DXE_EXPORT(feof) DXE_EXPORT(ferror) + DXE_EXPORT(fflush) DXE_EXPORT(fgetc) + DXE_EXPORT(fgets) DXE_EXPORT(fopen) + DXE_EXPORT(fprintf) + DXE_EXPORT(fputc) DXE_EXPORT(fread) DXE_EXPORT(fseek) DXE_EXPORT(ftell) + DXE_EXPORT(fwrite) DXE_EXPORT(printf) DXE_EXPORT(puts) + DXE_EXPORT(sprintf) DXE_EXPORT(ungetc) + DXE_EXPORT(vsprintf) // stdlib + DXE_EXPORT(abort) + DXE_EXPORT(atexit) + DXE_EXPORT(exit) DXE_EXPORT(free) + DXE_EXPORT(calloc) DXE_EXPORT(malloc) DXE_EXPORT(realloc) // string DXE_EXPORT(memcpy) DXE_EXPORT(memset) + DXE_EXPORT(strcat) DXE_EXPORT(strcmp) + DXE_EXPORT(strcpy) + DXE_EXPORT(strlen) DXE_EXPORT(strncmp) + DXE_EXPORT(strncpy) DXE_EXPORT(strtol) + // time + DXE_EXPORT(ctime) + DXE_EXPORT(time) // Compiler stuff. DXE_EXPORT(__dj_assert) + DXE_EXPORT(__dj_ctype_toupper) + DXE_EXPORT(__dj_stdin) + DXE_EXPORT(__djgpp_base_address) DXE_EXPORT(__emutls_get_address) DXE_EXPORT_END @@ -120,7 +193,101 @@ static int lastResort(void) { printf("ROO/E: Last resort function called!\n"); return 0; } -#endif // PLATFORM_DOS +#endif // __DJGPP__ + + +void logClose(void) { + if (_log && _ourHandle) { + fclose(_log); + _ourHandle = 0; + } + DEL(_logBuffer); +} + + +uint8_t logOpen(char *filename, uint8_t append) { + _log = fopen(filename, append ? "a" : "w"); + if (_log) { + _ourHandle = 1; + return 1; + } + return 0; +} + + +void logOpenByHandle(FILE *handle) { + _log = handle; +} + + +void logWrite(char *format, ...) { + va_list args = { 0 }; + va_list args2 = { 0 }; + uint16_t length = 0; + + if (_log) { + va_start(args, format); + va_copy(args2, args); + + // Attempt to write into current log buffer. + length = vsnprintf(_logBuffer, _logBufferSize, format, args); + + // Did it fit? + if (length >= _logBufferSize) { + // Nope. Resize buffer to fit plus a bit more. + if (_logBuffer) free(_logBuffer); + _logBufferSize = length + 32; + _logBuffer = (char *)malloc(_logBufferSize); + // Do it again. + vsnprintf(_logBuffer, _logBufferSize, format, args2); + } + +#ifdef __linux__ + // Also output to stdout on Linux. + fprintf(stdout, "%s", _logBuffer); + fflush(stdout); +#endif + + fprintf(_log, "%s", _logBuffer); + fflush(_log); + + va_end(args2); + va_end(args); + } +} + + +void logWriteToFileOnly(char *format, ...) { + va_list args; + + va_start(args, format); + + if (_log) { + vfprintf(_log, format, args); + fflush(_log); + } + + va_end(args); +} + + +FILE *memoryLogHandleGet(void) { + return _memoryLog; +} + + +#ifdef MEMWATCH +void memoryOutput(int c) { + fputc(c, _memoryLog); + fflush(_memoryLog); + + #ifdef __linux__ + // Also output to stdout on Linux. + fputc(c, stdout); + fflush(stdout); + #endif +} +#endif RooAppThreadT *rooStartApp(const char *appName, const int argc, char *argv[]) { @@ -161,9 +328,9 @@ static void *rooStartAppThread(void *arg) { appThread->running = true; appPtrCast.from = dlsym(appThread->namedPointer->pointer, "_appMain"); appMain = appPtrCast.to; // NOLINT - debug("ROO/E: Calling appMain.\n"); + logWrite("ROO/E: Calling appMain.\n"); appThread->result = appMain(appThread->argc, appThread->argv); - debug("ROO/E: Back from appMain.\n"); + logWrite("ROO/E: Back from appMain.\n"); appThread->running = false; return NULL; @@ -199,7 +366,8 @@ int main(const int argc, char *argv[]) { RooAppThreadT *appThread = NULL;; RooNamedPointerT **dynList = NULL; RooNamedPointerT *namedPointer = NULL; - int result; + char *logName = utilFileExtensionChange("roo_e.exe", "log"); + int result = -1; // RooAbortReasonT abort; int x; int (*dynInitStop)(void); @@ -212,15 +380,26 @@ int main(const int argc, char *argv[]) { (void)argc; (void)argv; -#ifdef PLATFORM_DOS + _memoryLog = fopen(logName, "w"); // This used to be encapsulated inside memory.c. Kinda just dangling here now. + logOpenByHandle(memoryLogHandleGet()); + + #ifdef MEMWATCH + mwLogW(_memoryLog); + mwSetOutFunc(memoryOutput); + mwInit(); + #endif + + free(logName); + +#ifdef __DJGPP__ // Set the error callback function. _dlsymresolver = dxeResolver; // Register the symbols exported into dynamic modules. dlregsym(exports); -#endif // PLATFORM_DOS +#endif // __DJGPP__ // Load libraries. - debug("ROO/E: Loading dynamic libraries.\n"); + logWrite("ROO/E: Loading dynamic libraries.\n"); if ((dir = opendir("dyn/")) == NULL) { printf("ROO/E: Unable to open dyn directory!\n"); exit(1); @@ -236,11 +415,11 @@ int main(const int argc, char *argv[]) { printf("ROO/E: Unable to load %s! %s\n", namedPointer->name, dlerror()); exit(1); } - debug("ROO/E: Loaded %s.\n", namedPointer->name); + logWrite("ROO/E: Loaded %s.\n", namedPointer->name); dynPtrCast.from = dlsym(namedPointer->pointer, "_dynStart"); if (dynPtrCast.from != NULL) { dynInitStop = dynPtrCast.to; // NOLINT - debug("ROO/E: Starting %s.\n", namedPointer->name); + logWrite("ROO/E: Starting %s.\n", namedPointer->name); result = dynInitStop(); if (result != 0) { printf("ROO/E: %s failed to start!\n", namedPointer->name); @@ -253,7 +432,7 @@ int main(const int argc, char *argv[]) { } closedir(dir); - debug("ROO/E: Loaded %d libraries.\n", arrlen(dynList)); + logWrite("ROO/E: Loaded %d libraries.\n", arrlen(dynList)); // Load the Shell. This needs to be configurable. rooStartApp("kpsmpgc", 0, NULL); @@ -263,13 +442,13 @@ int main(const int argc, char *argv[]) { // Wait for all apps to exit. for (x=0; xrunning); + logWrite("ROO/E: App %d of %d running == %d\n", x, arrlen(_appThreadList), (int)appThread->running); if (appThread->running == false) { - debug("ROO/E: Joining.\n"); + logWrite("ROO/E: Joining.\n"); pthread_join(appThread->thread, NULL); - debug("ROO/E: Joined.\n"); + logWrite("ROO/E: Joined.\n"); dlclose(appThread->namedPointer->pointer); - debug("ROO/E: %s exited.\n", appThread->namedPointer->name); + logWrite("ROO/E: %s exited.\n", appThread->namedPointer->name); DEL(appThread->namedPointer->name); DEL(appThread->namedPointer); //***TODO*** Deal with argv cleanup here? @@ -297,10 +476,10 @@ int main(const int argc, char *argv[]) { */ // Unload DYNs - debug("ROO/E: Unloading %d libraries.\n", arrlen(dynList)); + logWrite("ROO/E: Unloading %d libraries.\n", arrlen(dynList)); while (arrlen(dynList) > 0) { namedPointer = dynList[0]; // NOLINT - debug("ROO/E: Stopping %s\n", namedPointer->name); + logWrite("ROO/E: Stopping %s\n", namedPointer->name); dynPtrCast.from = dlsym(namedPointer->pointer, "_dynStop"); if (dynPtrCast.from != NULL) { dynInitStop = dynPtrCast.to; // NOLINT @@ -316,5 +495,12 @@ int main(const int argc, char *argv[]) { DEL(namedPointer); } + #ifdef MEMWATCH + unlink("memwatch.log"); // It just insists on creating this! + mwTerm(); + #endif + + logClose(); + return result; } diff --git a/roo_e/roo_e.h b/roo_e/roo_e.h index 8535e29..8c98034 100644 --- a/roo_e/roo_e.h +++ b/roo_e/roo_e.h @@ -28,15 +28,35 @@ #include #include +#include +#include +#include +#include #include #include #include +#include #include "pthread.h" #include "stbds.h" #include "util.h" #include "stddclmr.h" +// This is weird being here, but we have to expose a lot of it via DXE_EXPORT. +#ifdef __DJGPP__ +#include +#include +#include +#include +#include +#include +#include +#endif + +#ifdef MEMWATCH +#include "memwatch/memwatch.h" +#endif + #define ROO_CONSTRUCTOR void __attribute__((constructor)) #define ROO_DESTRUCTOR void __attribute__((destructor)) @@ -45,6 +65,23 @@ #define NEW(t,v) (v)=(t*)malloc(sizeof(t)) #define DEL(v) {if(v) {free(v); v=NULL;}} +// Some helper defines. +#define DIVISIBLE_BY_EIGHT(x) ((((x) >> 3) << 3) == (x)) +#define HIGH_BYTE(b) ((uint8_t)(((b) & 0xFF00) >> 8)) +#define LOW_BYTE(b) ((uint8_t)((b) & 0x00FF)) + +// Return codes. +#define SUCCESS 0 +#define FAIL 1 + +#ifndef va_copy +#if defined(__GNUC__) || defined(__clang__) +#define va_copy(dest, src) __builtin_va_copy(dest, src) +#else +#define va_copy(dest, src) (dest = src) +#endif +#endif + typedef struct RooNamedPointerS { char *name; @@ -74,6 +111,12 @@ typedef enum { } RooAbortReasonT; +void logClose(void); +uint8_t logOpen(char *filename, uint8_t append); +void logOpenByHandle(FILE *handle); +void logWrite(char *format, ...); +void logWriteToFileOnly(char *format, ...); + RooAppThreadT *rooStartApp(const char *appName, int argc, char *argv[]); RooAbortReasonT rooStopApp(RooAppThreadT **threadPointer, RooStopReasonT reason); diff --git a/roo_e/util.c b/roo_e/util.c index 0605a4f..49b11b3 100644 --- a/roo_e/util.c +++ b/roo_e/util.c @@ -21,14 +21,8 @@ */ -#include -#include -#include - #include "util.h" -#include - char *utilCreateString(char *format, ...) { va_list args; @@ -78,3 +72,46 @@ bool utilEndsWith(const char *haystack, const char *needle, const bool caseSensi return true; } + + +uint8_t utilFileExists(char *filename) { + FILE *f = fopen(filename, "rb"); + + if (f) { + fclose(f); + return SUCCESS; + } + + return FAIL; +} + + +char *utilFileExtensionChange(char *appName, char *extension) { + char *c = NULL; + char *newName = NULL; + int16_t x = strlen(appName); + uint16_t len = 2 + strlen(extension); // 2 = dot in extension and 0 terminator. + + // Find last portion of filename. + while (x > 0) { + if (appName[x] == '/' || appName[x] == '\\') { x++; break; } + x--; + len++; + } + x--; + + // We use this + length of new extension for new string length. + newName = (char *)malloc(len); + if (newName) { + if (strlen(appName) - x < len) { + // Replace extension + strncpy(newName, &appName[x + 1], len - 1); + c = strstr(newName, "."); + if (c) *c = 0; + strncat(newName, ".", len - 1); + strncat(newName, extension, len - 1); + } + } + + return newName; +} diff --git a/roo_e/util.h b/roo_e/util.h index c93e488..9eb2a06 100644 --- a/roo_e/util.h +++ b/roo_e/util.h @@ -25,25 +25,14 @@ #define UTIL_H -#include -#include -#include +#include "roo_e.h" -#define debug printf - -#ifndef va_copy -#if defined(__GNUC__) || defined(__clang__) -#define va_copy(dest, src) __builtin_va_copy(dest, src) -#else -#define va_copy(dest, src) (dest = src) -#endif -#endif - - -char *utilCreateString(char *format, ...); -char *utilCreateStringVArgs(char *format, va_list args); -bool utilEndsWith(const char *haystack, const char *needle, bool caseSensitive); +char *utilCreateString(char *format, ...); +char *utilCreateStringVArgs(char *format, va_list args); +bool utilEndsWith(const char *haystack, const char *needle, bool caseSensitive); +char *utilFileExtensionChange(char *appName, char *extension); +uint8_t utilFileExists(char *filename); #endif // UTIL_H