Logging, surface, platform, and memory debugging added. Issues in DYN loading order.

This commit is contained in:
Scott Duensing 2025-12-26 19:43:21 -06:00
parent 9caff407f4
commit 3a1aedf14b
15 changed files with 2080 additions and 120 deletions

View file

@ -32,8 +32,8 @@ DXE3GEN := dxe3gen
EXE2COFF := exe2coff EXE2COFF := exe2coff
# Compiler Settings # Compiler Settings
CFLAGS := -DPLATFORM_DOS -Wall -MD override CFLAGS += -Wall -MD
LDFLAGS := override LDFLAGS +=
# Output Directories # Output Directories
OBJ := obj OBJ := obj
@ -47,30 +47,40 @@ FIN := font/in
# Include Paths # Include Paths
INC := include 3rdparty 3rdparty/pthreads/include roo_e 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 Compiler Source and Target - NOTE: This is a Linux binary!
FONT_ELF := font 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_OBJ := $(FONT_SRC:%=$(OBJ)/%.o)
FONT_LIB := -lm FONT_LIB := -lm
# Roo/E Source and Target # Roo/E Source and Target
ROOE_EXE := roo_e.exe 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_OBJ := $(ROOE_SRC:%=$(OBJ)/%.o)
ROOE_LIB := 3rdparty/pthreads/lib/libgthreads.a -lgcc -lm ROOE_LIB := 3rdparty/pthreads/lib/libgthreads.a -lgcc -lm
# MultiPlayer Game Client # MultiPlayer Game Client
MPGC_EXE := kpsmpgc.app 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_OBJ := $(MPGC_SRC:%=$(OBJ)/%.o)
MPGC_LIB := MPGC_LIB :=
DYNS := kpsvideo stbimage DYNS := kpssurf kpsplatf stbimage
FONTS := vga4x8 vga8x8 vga8x14 vga8x16 FONTS := vga4x8 vga8x8 vga8x14 vga8x16
# Wiring # Wiring
INC_FLAGS := $(addprefix -I,$(INC)) override CFLAGS += $(INC_FLAGS)
CFLAGS += $(INC_FLAGS) INC_FLAGS := $(addprefix -I,$(INC)) $(foreach DIR,$(DYNS),-Idyn/$(DIR))
APP_OBJS := $(ROOE_OBJ) $(MPGC_OBJ) APP_OBJS := $(ROOE_OBJ) $(MPGC_OBJ)
FONTS := $(addprefix $(FNT)/,$(addsuffix .fnt,$(FONTS))) FONTS := $(addprefix $(FNT)/,$(addsuffix .fnt,$(FONTS)))
FONT_PNGS := $(subst .fnt,.png,$(subst $(FNT),$(FIN),$(FONTS))) FONT_PNGS := $(subst .fnt,.png,$(subst $(FNT),$(FIN),$(FONTS)))
@ -96,6 +106,7 @@ clean:
# Font Compiler Target # Font Compiler Target
$(BIN)/$(FONT_ELF): $(FONT_OBJ) Makefile $(BIN)/$(FONT_ELF): $(FONT_OBJ) Makefile
@echo "\n---------- $@"
mkdir -p $(dir $@) $(BIN)/fonts mkdir -p $(dir $@) $(BIN)/fonts
$(CC) $(FONT_OBJ) -o $@ $(LDFLAGS) $(FONT_LIB) $(CC) $(FONT_OBJ) -o $@ $(LDFLAGS) $(FONT_LIB)
@ -104,6 +115,7 @@ $(FONTS) &: $(FONT_PNGS) $(BIN)/$(FONT_ELF)
# Roo/E Target # Roo/E Target
$(BIN)/$(ROOE_EXE): $(ROOE_OBJ) Makefile $(BIN)/$(ROOE_EXE): $(ROOE_OBJ) Makefile
@echo "\n---------- $@"
mkdir -p $(dir $@) mkdir -p $(dir $@)
$(CC) $(ROOE_OBJ) -o $@ $(LDFLAGS) $(ROOE_LIB) $(CC) $(ROOE_OBJ) -o $@ $(LDFLAGS) $(ROOE_LIB)
# Embed the DPMI Server # Embed the DPMI Server
@ -113,16 +125,17 @@ $(BIN)/$(ROOE_EXE): $(ROOE_OBJ) Makefile
# MPGC Target # MPGC Target
$(APP)/$(MPGC_EXE): $(MPGC_OBJ) Makefile $(APP)/$(MPGC_EXE): $(MPGC_OBJ) Makefile
@echo "\n---------- $@"
mkdir -p $(dir $@) mkdir -p $(dir $@)
$(DXE3GEN) -o $@ $(MPGC_OBJ) -U $(LDFLAGS) $(MPGC_LIB) $(DXE3GEN) -o $@ $(MPGC_OBJ) -U $(LDFLAGS) $(MPGC_LIB)
# DYN Targets - This is NOT how make should be used. # DYN Targets - This is NOT how make should be used.
.PHONY: dyns .PHONY: dyns
$(DYN_FILES): $(DYN_SOURCE) Makefile $(DYN_FILES): $(DYN_SOURCE) Makefile
echo $(DYN_SOURCE) @echo "\n---------- $@"
mkdir -p $(DYN) $(OBJ)/dyn/$(basename $(notdir $@)) $(SDK)/lib $(SDK)/include 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 $(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) $(DXE3GEN) -o $@ -Y $@.a --whole-archive -U $(OBJ)/dyn/$(basename $(notdir $@)).a $(LDFLAGS)
mv $@.a $(SDK)/lib/. 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 if [ ! -z "$(shell find dyn/$(basename $(notdir $@)) -name '*.h')" ]; then cp $(shell find dyn/$(basename $(notdir $@)) -name '*.h') $(SDK)/include/.; fi

View file

@ -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. # 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. # Now do the DOS part.
eval "$(../toolchains/toolchains.sh use x86 dos)" eval "$(../toolchains/toolchains.sh use x86 dos)"
make dos CFLAGS="-DMEMWATCH" make dos

785
dyn/kpsplatf/djgpp.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#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, &regs, &regs);
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, &regs, &regs);
// 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 <scott@kangaroopunch.com>\n");
//printf("Based on: VBE 2.0 driver v1.0 (c) 1999, Tobias Koch <tobias.koch@gmail.com>\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__

71
dyn/kpsplatf/kpsplatf.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#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

230
dyn/kpsplatf/sdl2.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#ifdef __linux__
#include <SDL2/SDL.h>
#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__

570
dyn/kpssurf/kpssurf.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#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; i<height; i++) {
memcpy(&__surfaceActive->buffer.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; y<y2; y++) {
for (x=x1; x<x2; x++) {
pixel = surfacePixelGet(source, x, y);
if (transparent != pixel) {
surfacePixelSet(targetX + x, targetY + y, pixel);
}
}
}
}
void surfaceClear(ColorT color) {
uint16_t x;
size_t offsetTarget;
// Draw the top line.
surfaceLineH(0, __surfaceActive->width - 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;
}

95
dyn/kpssurf/kpssurf.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#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

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#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;
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#ifndef KPSVIDEO_H
#define KPSVIDEO_H
#endif // KPSVIDEO_H

3
env.sh
View file

@ -25,4 +25,5 @@
eval "$(../toolchains/toolchains.sh use x86 dos)" 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"

View file

@ -23,8 +23,7 @@
#include <stdio.h> #include <stdio.h>
#ifdef PLATFORM_LINUX #ifdef MEMWATCH
#define MEMWATCH
#include "memwatch/memwatch.h" #include "memwatch/memwatch.h"
#endif #endif
@ -90,7 +89,6 @@ int main(int argc, char *argv[]) {
(void)argc; (void)argc;
(void)argv; (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/vga4x8.png", "fonts/vga4x8.fnt", 4, 8, 16, 256);
makeFont("../font/in/vga8x8.png", "fonts/vga8x8.fnt", 8, 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); makeFont("../font/in/vga8x14.png", "fonts/vga8x14.fnt", 8, 14, 16, 256);

View file

@ -27,12 +27,21 @@
static RooAppThreadT **_appThreadList = NULL; // NOLINT 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); static void *rooStartAppThread(void *arg);
#ifdef PLATFORM_DOS #ifdef MEMWATCH
void mwLogW(FILE *p);
#endif
#ifdef __DJGPP__
#include <sys/dxe.h> #include <sys/dxe.h>
void *dxeResolver(const char *symbol); void *dxeResolver(const char *symbol);
static int lastResort(void); static int lastResort(void);
@ -53,15 +62,58 @@ struct __emutls_object // NOLINT
void *__emutls_get_address(struct __emutls_object *); // NOLINT void *__emutls_get_address(struct __emutls_object *); // NOLINT
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
DXE_EXPORT_TABLE(exports) 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
DXE_EXPORT(dlclose) DXE_EXPORT(dlclose)
DXE_EXPORT(dlopen) DXE_EXPORT(dlopen)
DXE_EXPORT(dlregsym) DXE_EXPORT(dlregsym)
DXE_EXPORT(dlstatbind) DXE_EXPORT(dlstatbind)
DXE_EXPORT(dlstatunbind) DXE_EXPORT(dlstatunbind)
// farptr
DXE_EXPORT(_farpokeb)
// go32
DXE_EXPORT(_go32_info_block)
// math // math
DXE_EXPORT(ldexp) DXE_EXPORT(ldexp)
DXE_EXPORT(pow) 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 // stb_ds
DXE_EXPORT(stbds_arrfreef) DXE_EXPORT(stbds_arrfreef)
DXE_EXPORT(stbds_arrgrowf) DXE_EXPORT(stbds_arrgrowf)
@ -81,26 +133,47 @@ DXE_EXPORT_TABLE(exports)
DXE_EXPORT(fclose) DXE_EXPORT(fclose)
DXE_EXPORT(feof) DXE_EXPORT(feof)
DXE_EXPORT(ferror) DXE_EXPORT(ferror)
DXE_EXPORT(fflush)
DXE_EXPORT(fgetc) DXE_EXPORT(fgetc)
DXE_EXPORT(fgets)
DXE_EXPORT(fopen) DXE_EXPORT(fopen)
DXE_EXPORT(fprintf)
DXE_EXPORT(fputc)
DXE_EXPORT(fread) DXE_EXPORT(fread)
DXE_EXPORT(fseek) DXE_EXPORT(fseek)
DXE_EXPORT(ftell) DXE_EXPORT(ftell)
DXE_EXPORT(fwrite)
DXE_EXPORT(printf) DXE_EXPORT(printf)
DXE_EXPORT(puts) DXE_EXPORT(puts)
DXE_EXPORT(sprintf)
DXE_EXPORT(ungetc) DXE_EXPORT(ungetc)
DXE_EXPORT(vsprintf)
// stdlib // stdlib
DXE_EXPORT(abort)
DXE_EXPORT(atexit)
DXE_EXPORT(exit)
DXE_EXPORT(free) DXE_EXPORT(free)
DXE_EXPORT(calloc)
DXE_EXPORT(malloc) DXE_EXPORT(malloc)
DXE_EXPORT(realloc) DXE_EXPORT(realloc)
// string // string
DXE_EXPORT(memcpy) DXE_EXPORT(memcpy)
DXE_EXPORT(memset) DXE_EXPORT(memset)
DXE_EXPORT(strcat)
DXE_EXPORT(strcmp) DXE_EXPORT(strcmp)
DXE_EXPORT(strcpy)
DXE_EXPORT(strlen)
DXE_EXPORT(strncmp) DXE_EXPORT(strncmp)
DXE_EXPORT(strncpy)
DXE_EXPORT(strtol) DXE_EXPORT(strtol)
// time
DXE_EXPORT(ctime)
DXE_EXPORT(time)
// Compiler stuff. // Compiler stuff.
DXE_EXPORT(__dj_assert) 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(__emutls_get_address)
DXE_EXPORT_END DXE_EXPORT_END
@ -120,7 +193,101 @@ static int lastResort(void) {
printf("ROO/E: Last resort function called!\n"); printf("ROO/E: Last resort function called!\n");
return 0; 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[]) { RooAppThreadT *rooStartApp(const char *appName, const int argc, char *argv[]) {
@ -161,9 +328,9 @@ static void *rooStartAppThread(void *arg) {
appThread->running = true; appThread->running = true;
appPtrCast.from = dlsym(appThread->namedPointer->pointer, "_appMain"); appPtrCast.from = dlsym(appThread->namedPointer->pointer, "_appMain");
appMain = appPtrCast.to; // NOLINT appMain = appPtrCast.to; // NOLINT
debug("ROO/E: Calling appMain.\n"); logWrite("ROO/E: Calling appMain.\n");
appThread->result = appMain(appThread->argc, appThread->argv); appThread->result = appMain(appThread->argc, appThread->argv);
debug("ROO/E: Back from appMain.\n"); logWrite("ROO/E: Back from appMain.\n");
appThread->running = false; appThread->running = false;
return NULL; return NULL;
@ -199,7 +366,8 @@ int main(const int argc, char *argv[]) {
RooAppThreadT *appThread = NULL;; RooAppThreadT *appThread = NULL;;
RooNamedPointerT **dynList = NULL; RooNamedPointerT **dynList = NULL;
RooNamedPointerT *namedPointer = NULL; RooNamedPointerT *namedPointer = NULL;
int result; char *logName = utilFileExtensionChange("roo_e.exe", "log");
int result = -1;
// RooAbortReasonT abort; // RooAbortReasonT abort;
int x; int x;
int (*dynInitStop)(void); int (*dynInitStop)(void);
@ -212,15 +380,26 @@ int main(const int argc, char *argv[]) {
(void)argc; (void)argc;
(void)argv; (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. // Set the error callback function.
_dlsymresolver = dxeResolver; _dlsymresolver = dxeResolver;
// Register the symbols exported into dynamic modules. // Register the symbols exported into dynamic modules.
dlregsym(exports); dlregsym(exports);
#endif // PLATFORM_DOS #endif // __DJGPP__
// Load libraries. // Load libraries.
debug("ROO/E: Loading dynamic libraries.\n"); logWrite("ROO/E: Loading dynamic libraries.\n");
if ((dir = opendir("dyn/")) == NULL) { if ((dir = opendir("dyn/")) == NULL) {
printf("ROO/E: Unable to open dyn directory!\n"); printf("ROO/E: Unable to open dyn directory!\n");
exit(1); 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()); printf("ROO/E: Unable to load %s! %s\n", namedPointer->name, dlerror());
exit(1); exit(1);
} }
debug("ROO/E: Loaded %s.\n", namedPointer->name); logWrite("ROO/E: Loaded %s.\n", namedPointer->name);
dynPtrCast.from = dlsym(namedPointer->pointer, "_dynStart"); dynPtrCast.from = dlsym(namedPointer->pointer, "_dynStart");
if (dynPtrCast.from != NULL) { if (dynPtrCast.from != NULL) {
dynInitStop = dynPtrCast.to; // NOLINT dynInitStop = dynPtrCast.to; // NOLINT
debug("ROO/E: Starting %s.\n", namedPointer->name); logWrite("ROO/E: Starting %s.\n", namedPointer->name);
result = dynInitStop(); result = dynInitStop();
if (result != 0) { if (result != 0) {
printf("ROO/E: %s failed to start!\n", namedPointer->name); printf("ROO/E: %s failed to start!\n", namedPointer->name);
@ -253,7 +432,7 @@ int main(const int argc, char *argv[]) {
} }
closedir(dir); 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. // Load the Shell. This needs to be configurable.
rooStartApp("kpsmpgc", 0, NULL); rooStartApp("kpsmpgc", 0, NULL);
@ -263,13 +442,13 @@ int main(const int argc, char *argv[]) {
// Wait for all apps to exit. // Wait for all apps to exit.
for (x=0; x<arrlen(_appThreadList); x++) { for (x=0; x<arrlen(_appThreadList); x++) {
appThread = _appThreadList[x]; // NOLINT appThread = _appThreadList[x]; // NOLINT
debug("ROO/E: App %d of %d running == %d\n", x, arrlen(_appThreadList), (int)appThread->running); logWrite("ROO/E: App %d of %d running == %d\n", x, arrlen(_appThreadList), (int)appThread->running);
if (appThread->running == false) { if (appThread->running == false) {
debug("ROO/E: Joining.\n"); logWrite("ROO/E: Joining.\n");
pthread_join(appThread->thread, NULL); pthread_join(appThread->thread, NULL);
debug("ROO/E: Joined.\n"); logWrite("ROO/E: Joined.\n");
dlclose(appThread->namedPointer->pointer); 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->name);
DEL(appThread->namedPointer); DEL(appThread->namedPointer);
//***TODO*** Deal with argv cleanup here? //***TODO*** Deal with argv cleanup here?
@ -297,10 +476,10 @@ int main(const int argc, char *argv[]) {
*/ */
// Unload DYNs // Unload DYNs
debug("ROO/E: Unloading %d libraries.\n", arrlen(dynList)); logWrite("ROO/E: Unloading %d libraries.\n", arrlen(dynList));
while (arrlen(dynList) > 0) { while (arrlen(dynList) > 0) {
namedPointer = dynList[0]; // NOLINT 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"); dynPtrCast.from = dlsym(namedPointer->pointer, "_dynStop");
if (dynPtrCast.from != NULL) { if (dynPtrCast.from != NULL) {
dynInitStop = dynPtrCast.to; // NOLINT dynInitStop = dynPtrCast.to; // NOLINT
@ -316,5 +495,12 @@ int main(const int argc, char *argv[]) {
DEL(namedPointer); DEL(namedPointer);
} }
#ifdef MEMWATCH
unlink("memwatch.log"); // It just insists on creating this!
mwTerm();
#endif
logClose();
return result; return result;
} }

View file

@ -28,15 +28,35 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <dirent.h> #include <dirent.h>
#include <math.h> #include <math.h>
#include <ctype.h>
#include "pthread.h" #include "pthread.h"
#include "stbds.h" #include "stbds.h"
#include "util.h" #include "util.h"
#include "stddclmr.h" #include "stddclmr.h"
// This is weird being here, but we have to expose a lot of it via DXE_EXPORT.
#ifdef __DJGPP__
#include <dos.h>
#include <dpmi.h>
#include <go32.h>
#include <bios.h>
#include <conio.h>
#include <sys/farptr.h>
#include <sys/nearptr.h>
#endif
#ifdef MEMWATCH
#include "memwatch/memwatch.h"
#endif
#define ROO_CONSTRUCTOR void __attribute__((constructor)) #define ROO_CONSTRUCTOR void __attribute__((constructor))
#define ROO_DESTRUCTOR void __attribute__((destructor)) #define ROO_DESTRUCTOR void __attribute__((destructor))
@ -45,6 +65,23 @@
#define NEW(t,v) (v)=(t*)malloc(sizeof(t)) #define NEW(t,v) (v)=(t*)malloc(sizeof(t))
#define DEL(v) {if(v) {free(v); v=NULL;}} #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 { typedef struct RooNamedPointerS {
char *name; char *name;
@ -74,6 +111,12 @@ typedef enum {
} RooAbortReasonT; } 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[]); RooAppThreadT *rooStartApp(const char *appName, int argc, char *argv[]);
RooAbortReasonT rooStopApp(RooAppThreadT **threadPointer, RooStopReasonT reason); RooAbortReasonT rooStopApp(RooAppThreadT **threadPointer, RooStopReasonT reason);

View file

@ -21,14 +21,8 @@
*/ */
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include "util.h" #include "util.h"
#include <ctype.h>
char *utilCreateString(char *format, ...) { char *utilCreateString(char *format, ...) {
va_list args; va_list args;
@ -78,3 +72,46 @@ bool utilEndsWith(const char *haystack, const char *needle, const bool caseSensi
return true; 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;
}

View file

@ -25,25 +25,14 @@
#define UTIL_H #define UTIL_H
#include <stdbool.h> #include "roo_e.h"
#include <stdio.h>
#include <stdint.h>
#define debug printf char *utilCreateString(char *format, ...);
char *utilCreateStringVArgs(char *format, va_list args);
#ifndef va_copy bool utilEndsWith(const char *haystack, const char *needle, bool caseSensitive);
#if defined(__GNUC__) || defined(__clang__) char *utilFileExtensionChange(char *appName, char *extension);
#define va_copy(dest, src) __builtin_va_copy(dest, src) uint8_t utilFileExists(char *filename);
#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);
#endif // UTIL_H #endif // UTIL_H