Logging, surface, platform, and memory debugging added. Issues in DYN loading order.
This commit is contained in:
parent
9caff407f4
commit
3a1aedf14b
15 changed files with 2080 additions and 120 deletions
33
Makefile
33
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
|
||||
|
|
|
|||
10
build.sh
10
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
|
||||
|
|
|
|||
785
dyn/kpsplatf/djgpp.c
Normal file
785
dyn/kpsplatf/djgpp.c
Normal 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, ®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 <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
71
dyn/kpsplatf/kpsplatf.h
Normal 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
230
dyn/kpsplatf/sdl2.c
Normal 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
570
dyn/kpssurf/kpssurf.c
Normal 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
95
dyn/kpssurf/kpssurf.h
Normal 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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
3
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"
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
#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);
|
||||
|
|
|
|||
220
roo_e/main.c
220
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 <sys/dxe.h>
|
||||
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; x<arrlen(_appThreadList); x++) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,15 +28,35 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
#include <dirent.h>
|
||||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#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 <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_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);
|
||||
|
||||
|
|
|
|||
49
roo_e/util.c
49
roo_e/util.c
|
|
@ -21,14 +21,8 @@
|
|||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
23
roo_e/util.h
23
roo_e/util.h
|
|
@ -25,25 +25,14 @@
|
|||
#define UTIL_H
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue