Cooperative multitasking added.

This commit is contained in:
Scott Duensing 2021-10-18 18:52:31 -05:00
parent f507b0713b
commit 07dbada6e8
7 changed files with 323 additions and 24 deletions

View file

@ -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 \

View file

@ -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);
}

View file

@ -18,6 +18,9 @@
*/
// Inspired by https://github.com/JMarlin/wsbe
#include "gui.h"
#include "widget.h"
#include "desktop.h"

View file

@ -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
View 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
View 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

View file

@ -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;
}