Cooperative multitasking added.
This commit is contained in:
parent
f507b0713b
commit
07dbada6e8
7 changed files with 323 additions and 24 deletions
|
@ -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 \
|
||||
|
|
|
@ -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 <scott@kangaroopunch.com>\n");
|
||||
printf("Based on: VBE 2.0 driver v1.0 (c) 1999, Tobias Koch <tobias.koch@gmail.com>\n\n");
|
||||
//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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
*/
|
||||
|
||||
|
||||
// Inspired by https://github.com/JMarlin/wsbe
|
||||
|
||||
|
||||
#include "gui.h"
|
||||
#include "widget.h"
|
||||
#include "desktop.h"
|
||||
|
|
|
@ -22,9 +22,6 @@
|
|||
#define GUI_H
|
||||
|
||||
|
||||
// Inspired by https://github.com/JMarlin/wsbe
|
||||
|
||||
|
||||
#include "os.h"
|
||||
#include "vesa.h"
|
||||
#include "array.h"
|
||||
|
|
222
client/src/gui/task.c
Normal file
222
client/src/gui/task.c
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// 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 <setjmp.h>
|
||||
|
||||
#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; x<len; x++) {
|
||||
task = _private.taskList[x];
|
||||
if (task->status == 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; x<len; x++) {
|
||||
if (task == _private.taskList[x]) {
|
||||
// Remove us from the list.
|
||||
arrdel(_private.taskList, x);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't free the task or we won't have a stack.
|
||||
// Defer until we longjmp back into the old stack.
|
||||
longjmp(_private.buf, EXIT_TASK);
|
||||
|
||||
// Execution doesn't get to here.
|
||||
}
|
||||
|
||||
|
||||
static void taskFree(void) {
|
||||
TaskT *task = _private.current;
|
||||
|
||||
_private.current = NULL;
|
||||
free(task->stackBottom);
|
||||
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);
|
||||
}
|
||||
}
|
36
client/src/gui/task.h
Normal file
36
client/src/gui/task.h
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue