diff --git a/client/client.pro b/client/client.pro index dcc4d17..5bfd2af 100644 --- a/client/client.pro +++ b/client/client.pro @@ -44,6 +44,7 @@ INCLUDEPATH += \ HEADERS = \ $$LINUX_HEADERS \ src/gui/button.h \ + src/gui/task.h \ src/thirdparty/stb_ds.h \ src/thirdparty/stb_leakcheck.h \ src/thirdparty/stb_image.h \ @@ -67,6 +68,7 @@ SOURCES = \ src/gui/font.c \ src/gui/desktop.c \ src/gui/gui.c \ + src/gui/task.c \ src/gui/widget.c \ src/gui/window.c \ src/gui/image.c \ diff --git a/client/src/dos/vesa.c b/client/src/dos/vesa.c index cf9f397..9b0c85d 100644 --- a/client/src/dos/vesa.c +++ b/client/src/dos/vesa.c @@ -377,7 +377,7 @@ static uint8_t vbeIsDesiredMode(void) { // Packed or Direct Color mode. if (_vbeModeInfo.memoryModel == VBE_MM_PACKED || _vbeModeInfo.memoryModel == VBE_MM_DCOLOR) { // Resolution minimum of 640x480. - if (_vbeModeInfo.xResolution >= 640 && _vbeModeInfo.yResolution >= 480) { + if (_vbeModeInfo.xResolution >= 800 && _vbeModeInfo.yResolution >= 600) { // Multiple of 8 if (DIVISIBLE_BY_EIGHT(_vbeModeInfo.xResolution) && DIVISIBLE_BY_EIGHT(_vbeModeInfo.yResolution)) { // Valid mode! @@ -601,8 +601,8 @@ uint16_t vbeSetScanlineLength(uint16_t pixelLength) { 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); + if (r.h.ah != 0) return(0); + if (r.x.cx != pixelLength) return(0); _vbeSurface.virtualXResolution = pixelLength; @@ -615,11 +615,11 @@ int16_t vbeShowInfo(void) { // 0 1 2 3 4 5 6 7 8 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 - printf("VBE 2.0 driver v1.0 (c) 2021, Scott Duensing \n"); - printf("Based on: VBE 2.0 driver v1.0 (c) 1999, Tobias Koch \n\n"); + //printf("VBE 2.0 driver v1.0 (c) 2021, Scott Duensing \n"); + //printf("Based on: VBE 2.0 driver v1.0 (c) 1999, Tobias Koch \n\n"); if (vbeGetInfo() == NULL) { - printf("No VESA BIOS Extensions found.\n\n"); + printf("No VESA BIOS Extensions found.\n"); return(1); } printf( @@ -643,7 +643,7 @@ int16_t vbeShowInfo(void) { _vbeInfo.oemProductRevPtr, vbeGetPmodeInterface() ? "Found" : "Missing"); } else { - printf("VESA BIOS Extension 2.0 or better required!\n\n"); + printf("VESA BIOS Extension 2.0 or better required!\n"); return(1); } diff --git a/client/src/gui/gui.c b/client/src/gui/gui.c index 9ba4ea5..c2786d5 100644 --- a/client/src/gui/gui.c +++ b/client/src/gui/gui.c @@ -18,6 +18,9 @@ */ +// Inspired by https://github.com/JMarlin/wsbe + + #include "gui.h" #include "widget.h" #include "desktop.h" diff --git a/client/src/gui/gui.h b/client/src/gui/gui.h index 0f163b8..f4519fd 100644 --- a/client/src/gui/gui.h +++ b/client/src/gui/gui.h @@ -22,9 +22,6 @@ #define GUI_H -// Inspired by https://github.com/JMarlin/wsbe - - #include "os.h" #include "vesa.h" #include "array.h" diff --git a/client/src/gui/task.c b/client/src/gui/task.c new file mode 100644 index 0000000..d38a8be --- /dev/null +++ b/client/src/gui/task.c @@ -0,0 +1,222 @@ +/* + * Kangaroo Punch Multi Player Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +// Based on https://brennan.io/2020/05/24/userspace-cooperative-multitasking/ + + +// https://sourceforge.net/p/predef/wiki/Home/ +#if !defined(__i386__) && !defined(__i486__) && !defined(__i586__) && !defined(__i686__) && !defined(__x86_64__) +#error "Only Intel 386 and later processors are supported." +#endif + +#if !defined(__DJGPP__) && !defined(__GNUC__) +#error "Requires GCC or DJGPP." +#endif + + +#include + +#include "os.h" +#include "task.h" +#include "array.h" + + +typedef struct TaskS { + enum { + STATE_CREATED, + STATE_RUNNING, + STATE_WAITING + } status; + uint32_t id; + jmp_buf buf; + void (*function)(void *); + void *data; + void *stackBottom; + void *stackTop; + uint32_t stackSize; +} TaskT; + +enum { + INIT = 0, + SCHEDULE, + EXIT_TASK, +}; + +typedef struct PrivateS { + jmp_buf buf; + TaskT *current; + TaskT **taskList; +} PrivateT; + + +static PrivateT _private; +static uint16_t _stackSizeInK; + + +static TaskT *taskChoose(void); +static void taskFree(void); +static void taskSchedule(void); + + +static TaskT *taskChoose(void) { + TaskT *task = NULL; + uint32_t len = arrlenu(_private.taskList); + uint32_t x; + + // Find the next task to run. + for (x=0; xstatus == STATE_CREATED || task->status == STATE_RUNNING) { + // Move it to the end of the list. + arrdel(_private.taskList, x); + arrput(_private.taskList, task); + break; + } + } + + return task; +} + + +void taskCreate(void (*function)(void *), void *data) { + static uint32_t id = 1; + TaskT *task = (TaskT *)malloc(sizeof(TaskT)); + + task->status = STATE_CREATED; + task->function = function; + task->data = data; + task->id = id++; + task->stackSize = _stackSizeInK * 1024; + task->stackBottom = malloc(task->stackSize); + task->stackTop = task->stackBottom + task->stackSize; + + arrput(_private.taskList, task); +} + + +void taskExit(void) { + TaskT *task = _private.current; + uint32_t len = arrlenu(_private.taskList); + uint32_t x; + + // Find our task. + for (x=0; xstackBottom); + free(task); +} + + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +void taskRun(void) { + // This is the exit path for the scheduler! + switch (setjmp(_private.buf)) { + case EXIT_TASK: + taskFree(); + + case INIT: + case SCHEDULE: + taskSchedule(); + // If we get here, there's nothing else to do so exit + return; + + default: + fprintf(stderr, "Task scheduler error!\n"); + return; + } +} +#pragma GCC diagnostic pop + + +static void taskSchedule(void) { + TaskT *next = taskChoose(); + + if (!next) return; + + _private.current = next; + + if (next->status == STATE_CREATED) { + // This task has not been started yet. + // Assign a new stack pointer, run the task, and exit it at the end. + register void *top = next->stackTop; +#ifdef __x86_64__ + // 64 bit. + asm volatile( + "mov %[rs], %%rsp \n" + : [ rs ] "+r" (top) :: + ); +#else + // 32 bit. + asm volatile( + "mov %[rs], %%esp \n" + : [ rs ] "+r" (top) :: + ); +#endif + // Run the task. + next->status = STATE_RUNNING; + next->function(next->data); + + // The stack pointer should be back where we set it. Returning would be a very, very bad idea. Let's instead exit. + taskExit(); + } else { + longjmp(next->buf, 1); + } + + // Execution doesn't get to here. +} + + +void taskShutdown(void) { + // Nothing yet. +} + + +void taskStartup(uint16_t stackInK) { + _stackSizeInK = stackInK; + _private.current = NULL; + _private.taskList = NULL; +} + + +void taskYield(void) { + if (setjmp(_private.current->buf)) { + return; + } else { + longjmp(_private.buf, SCHEDULE); + } +} diff --git a/client/src/gui/task.h b/client/src/gui/task.h new file mode 100644 index 0000000..190fed6 --- /dev/null +++ b/client/src/gui/task.h @@ -0,0 +1,36 @@ +/* + * Kangaroo Punch Multi Player Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#ifndef TASK_H +#define TASK_H + + +#include "os.h" + + +void taskCreate(void (*function)(void *), void *data); +void taskExit(void); +void taskRun(void); +void taskShutdown(void); +void taskStartup(uint16_t stackInK); +void taskYield(void); + + +#endif // TASK_H diff --git a/client/src/main.c b/client/src/main.c index 180f457..d6b999f 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -18,8 +18,10 @@ */ +#include "os.h" #include "vesa.h" #include "mouse.h" +#include "task.h" #include "image.h" #include "font.h" #include "gui.h" @@ -34,19 +36,17 @@ void buttonClick(WidgetT *widget) { } -void test(void) { +void test(void *data) { MouseT *mouse = NULL; ImageT *pointer = NULL; PixelT alpha; - DesktopT *desktop = NULL; + DesktopT *desktop = (DesktopT *)guiRootGet(); WindowT *w1 = NULL; WindowT *w2 = NULL; WindowT *w3 = NULL; ButtonT *b1 = NULL; - vbeStartup(800, 600, 16); - mouseStartup(); - desktop = guiStartup(); + (void)data; pointer = imageLoad("mouse.png"); alpha = imagePixelGet(pointer, 5, 0); @@ -71,30 +71,69 @@ void test(void) { } while (!(!mouse->buttonRight && mouse->buttonRightWasDown)); // Exit on release of right-click. imageUnload(&pointer); - - guiShutdown(); - mouseShutdown(); - vbeShutdown(); } int main(int argc, char *argv[]) { - (void)argv; - if (argc > 1) { + uint16_t xResolution = 0; + uint16_t yResolution = 0; + uint16_t colorDepth = 0; + char logName[32] = { "log.log" }; + char *c = NULL; + int16_t x = strlen(argv[0]); + + // 0 1 2 3 4 5 6 7 8 + // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 + printf("Kangaroo Punch Multi Player DOS Game Client Mark II\n"); + printf("Copyright (C) 2020-2021 Scott Duensing scott@kangaroopunch.com\n\n"); + + // Find last portion of filename. + while (x > 0) { + if (argv[0][x] == '/' || argv[0][x] == '\\') break; + x--; + } + if (strlen(argv[0]) - x < 32) { + // Replace any extension with ".log" + strncpy(logName, &argv[0][x + 1], 31); + c = strstr(logName, "."); + if (c) *c = 0; + strncat(logName, ".log", 31); + } + + logOpen(logName, 0); + + // Command line needs to have the desired resolution and color depth on it. + if (argc != 4) { vbeShowInfo(); return 0; } + xResolution = atoi(argv[1]); + yResolution = atoi(argv[2]); + colorDepth = atoi(argv[3]); - logOpen("test.log", 0); + // Do we have the video mode they asked for? + if (vbeStartup(xResolution, yResolution, colorDepth)) { + return 1; + } - test(); + mouseStartup(); + guiStartup(); + taskStartup(64); - logClose(); + taskCreate(test, NULL); + taskRun(); + + taskShutdown(); + guiShutdown(); + mouseShutdown(); + vbeShutdown(); #ifdef memoryLeakShow memoryLeaksShow(); #endif + logClose(); + return 0; }