Menuing system almost working.
This commit is contained in:
parent
c03bb427bc
commit
88b4a6850a
19 changed files with 764 additions and 205 deletions
|
@ -25,6 +25,7 @@ set(HEADERS
|
||||||
stddclmr.h
|
stddclmr.h
|
||||||
story.h
|
story.h
|
||||||
text.h
|
text.h
|
||||||
|
ui.h
|
||||||
variable.h
|
variable.h
|
||||||
window.h
|
window.h
|
||||||
zscii.h
|
zscii.h
|
||||||
|
@ -53,6 +54,7 @@ set(SOURCE
|
||||||
state.c
|
state.c
|
||||||
story.c
|
story.c
|
||||||
text.c
|
text.c
|
||||||
|
ui.c
|
||||||
variable.c
|
variable.c
|
||||||
window.c
|
window.c
|
||||||
zscii.c
|
zscii.c
|
||||||
|
|
|
@ -30,7 +30,9 @@
|
||||||
|
|
||||||
void libMemSet(byte *start, byte val, uint32_t len);
|
void libMemSet(byte *start, byte val, uint32_t len);
|
||||||
char *libStrChr(char *haystack, char needle);
|
char *libStrChr(char *haystack, char needle);
|
||||||
|
int8_t libStrICmp(char *a, char *b);
|
||||||
uint32_t libStrLen(char *str);
|
uint32_t libStrLen(char *str);
|
||||||
|
char libToLower(char c);
|
||||||
|
|
||||||
|
|
||||||
#endif // LIB_H
|
#endif // LIB_H
|
||||||
|
|
|
@ -42,11 +42,13 @@
|
||||||
#define MSG_INT_V12_SHIFT "Add V1/V2 shifting."
|
#define MSG_INT_V12_SHIFT "Add V1/V2 shifting."
|
||||||
#define MSG_INT_V12_SHIFT_LOCK "Add V1/V2 shift locking."
|
#define MSG_INT_V12_SHIFT_LOCK "Add V1/V2 shift locking."
|
||||||
#define MSG_MEM_BUFFER "Unable to allocate memory buffer."
|
#define MSG_MEM_BUFFER "Unable to allocate memory buffer."
|
||||||
|
#define MSG_STA_CANNOT_ALLOCATE_STACK "Cannot allocate stack!"
|
||||||
#define MSG_OP_CALL_TOO_MANY_LOCALS "Too many local variables! (%d)"
|
#define MSG_OP_CALL_TOO_MANY_LOCALS "Too many local variables! (%d)"
|
||||||
#define MSG_OP_CALL_STACK_OVERFLOW "Stack overflow!"
|
#define MSG_OP_CALL_STACK_OVERFLOW "Stack overflow!"
|
||||||
#define MSG_OP_INPUT_BUFFER_TOO_SMALL "Text buffer too small for reading."
|
#define MSG_OP_INPUT_BUFFER_TOO_SMALL "Text buffer too small for reading."
|
||||||
#define MSG_OP_OBJ_MISSING_PROPERTY "Missing object property."
|
#define MSG_OP_OBJ_MISSING_PROPERTY "Missing object property."
|
||||||
#define MSG_OP_WIN_NO_SPLITTING "Window splitting is not supported."
|
#define MSG_OP_WIN_NO_SPLITTING "Window splitting is not supported."
|
||||||
|
#define MSG_UI_CANNOT_ALLOCATE "Cannot allocate %l bytes!"
|
||||||
#else // DEBUGGING
|
#else // DEBUGGING
|
||||||
#define MSG_UNIMPLEMENTED ""
|
#define MSG_UNIMPLEMENTED ""
|
||||||
#define MSG_INT_INVALID_EXT_OPCODE ""
|
#define MSG_INT_INVALID_EXT_OPCODE ""
|
||||||
|
@ -61,11 +63,13 @@
|
||||||
#define MSG_INT_V12_SHIFT ""
|
#define MSG_INT_V12_SHIFT ""
|
||||||
#define MSG_INT_V12_SHIFT_LOCK ""
|
#define MSG_INT_V12_SHIFT_LOCK ""
|
||||||
#define MSG_MEM_BUFFER ""
|
#define MSG_MEM_BUFFER ""
|
||||||
|
#define MSG_STA_CANNOT_ALLOCATE_STACK ""
|
||||||
#define MSG_OP_CALL_TOO_MANY_LOCALS ""
|
#define MSG_OP_CALL_TOO_MANY_LOCALS ""
|
||||||
#define MSG_OP_CALL_STACK_OVERFLOW ""
|
#define MSG_OP_CALL_STACK_OVERFLOW ""
|
||||||
#define MSG_OP_INPUT_BUFFER_TOO_SMALL ""
|
#define MSG_OP_INPUT_BUFFER_TOO_SMALL ""
|
||||||
#define MSG_OP_OBJ_MISSING_PROPERTY ""
|
#define MSG_OP_OBJ_MISSING_PROPERTY ""
|
||||||
#define MSG_OP_WIN_NO_SPLITTING ""
|
#define MSG_OP_WIN_NO_SPLITTING ""
|
||||||
|
#define MSG_UI_CANNOT_ALLOCATE ""
|
||||||
#endif // DEBUGGING
|
#endif // DEBUGGING
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,11 +35,20 @@ char portCharGet(void);
|
||||||
void portCharGetPos(byte *x, byte *y);
|
void portCharGetPos(byte *x, byte *y);
|
||||||
void portCharPrint(char c);
|
void portCharPrint(char c);
|
||||||
void portCharSetPos(byte x, byte y);
|
void portCharSetPos(byte x, byte y);
|
||||||
|
void portColorSet(byte f, byte b);
|
||||||
|
void portCursorShow(bool s);
|
||||||
void portDie(char *fmt, ...);
|
void portDie(char *fmt, ...);
|
||||||
bool portFileRestore(void);
|
uint32_t portFClose(void *stream);
|
||||||
bool portFileSave(void);
|
void *portFOpen(char *pathname, char *mode);
|
||||||
|
uint32_t portFRead(void *ptr, uint32_t size, uint32_t nmemb, void *stream);
|
||||||
|
uint32_t portFWrite(void *ptr, uint32_t size, uint32_t nmemb, void *stream);
|
||||||
|
void portFileLister(void);
|
||||||
|
void portFree(void *ptr);
|
||||||
|
void *portMalloc(uint32_t size);
|
||||||
uint16_t portRandomGet(int16_t range);
|
uint16_t portRandomGet(int16_t range);
|
||||||
void portStoryLoad(void);
|
void portScreenClear(void);
|
||||||
|
void portStoryLoad(char *story, uint32_t *length);
|
||||||
|
char *portSymbolsGet(void);
|
||||||
uint16_t portWordGet(uint32_t address);
|
uint16_t portWordGet(uint32_t address);
|
||||||
void portWordSet(uint32_t address, uint16_t value);
|
void portWordSet(uint32_t address, uint16_t value);
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
|
|
||||||
typedef struct stateS {
|
typedef struct stateS {
|
||||||
uint16_t stack[STACK_SIZE];
|
uint16_t *stack;
|
||||||
bool quit;
|
bool quit;
|
||||||
uint32_t pc; // Program Counter
|
uint32_t pc; // Program Counter
|
||||||
uint16_t sp; // Stack Pointer
|
uint16_t sp; // Stack Pointer
|
||||||
|
|
|
@ -120,7 +120,7 @@
|
||||||
|
|
||||||
void storyChecksumCalculate(void);
|
void storyChecksumCalculate(void);
|
||||||
uint32_t storyLength(void);
|
uint32_t storyLength(void);
|
||||||
void storySetup(byte width, byte height);
|
void storySetup(void);
|
||||||
|
|
||||||
|
|
||||||
#endif // STORY_H
|
#endif // STORY_H
|
||||||
|
|
46
include/ui.h
Normal file
46
include/ui.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef UI_H
|
||||||
|
#define UI_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define UI_KEY_UP 255
|
||||||
|
#define UI_KEY_RIGHT 254
|
||||||
|
#define UI_KEY_DOWN 253
|
||||||
|
#define UI_KEY_LEFT 252
|
||||||
|
|
||||||
|
|
||||||
|
void uiFileAdd(char *filename, uint32_t size);
|
||||||
|
void uiGameSelect(void);
|
||||||
|
char *uiSaveGet(void);
|
||||||
|
void uiSizeGet(byte *w, byte *h);
|
||||||
|
void uiSizeSet(byte w, byte h);
|
||||||
|
char *uiStoryGet(void);
|
||||||
|
uint32_t uiStorySizeGet(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // UI_H
|
|
@ -27,6 +27,7 @@ set(HEADERS
|
||||||
stddclmr.h
|
stddclmr.h
|
||||||
story.h
|
story.h
|
||||||
text.h
|
text.h
|
||||||
|
ui.h
|
||||||
variable.h
|
variable.h
|
||||||
window.h
|
window.h
|
||||||
zscii.h
|
zscii.h
|
||||||
|
@ -52,6 +53,7 @@ set(SOURCE
|
||||||
state.c
|
state.c
|
||||||
story.c
|
story.c
|
||||||
text.c
|
text.c
|
||||||
|
ui.c
|
||||||
variable.c
|
variable.c
|
||||||
window.c
|
window.c
|
||||||
zscii.c
|
zscii.c
|
||||||
|
|
|
@ -25,12 +25,12 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
#include "portme.h"
|
#include "portme.h"
|
||||||
#include "story.h"
|
#include "story.h"
|
||||||
#include "state.h"
|
|
||||||
#include "interpreter.h"
|
|
||||||
#include "text.h"
|
#include "text.h"
|
||||||
|
#include "ui.h"
|
||||||
|
|
||||||
#pragma push_macro("bool")
|
#pragma push_macro("bool")
|
||||||
#undef bool
|
#undef bool
|
||||||
|
@ -39,35 +39,26 @@
|
||||||
#pragma pop_macro("bool")
|
#pragma pop_macro("bool")
|
||||||
|
|
||||||
|
|
||||||
static char *_storyFile = NULL;
|
|
||||||
static uint8_t *_RAM = NULL;
|
static uint8_t *_RAM = NULL;
|
||||||
static uint8_t _attr = (1 << 4) + 7;
|
static uint8_t _attr = 0;
|
||||||
|
|
||||||
|
|
||||||
void portAttributeSet(byte attribute) {
|
void portAttributeSet(byte attribute) {
|
||||||
switch (attribute) {
|
switch (attribute) {
|
||||||
case TEXT_ATTR_NORMAL:
|
case TEXT_ATTR_NORMAL:
|
||||||
textbackground(1);
|
portColorSet(7, 1);
|
||||||
textcolor(7);
|
|
||||||
_attr = (1 << 4) + 7;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_ATTR_REVERSE:
|
case TEXT_ATTR_REVERSE:
|
||||||
textbackground(7);
|
portColorSet(1, 7);
|
||||||
textcolor(1);
|
|
||||||
_attr = (7 << 4) + 1;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_ATTR_BOLD:
|
case TEXT_ATTR_BOLD:
|
||||||
textbackground(1);
|
portColorSet(15, 1);
|
||||||
textcolor(15);
|
|
||||||
_attr = (1 << 4) + 15;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_ATTR_EMPHASIS:
|
case TEXT_ATTR_EMPHASIS:
|
||||||
textbackground(1);
|
portColorSet(14, 1);
|
||||||
textcolor(14);
|
|
||||||
_attr = (1 << 4) + 14;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,6 +91,10 @@ char portCharGet(void) {
|
||||||
if (key == KEY_RETURN) c = 13;
|
if (key == KEY_RETURN) c = 13;
|
||||||
if (key == KEY_BACK) c = 127;
|
if (key == KEY_BACK) c = 127;
|
||||||
if (key == KEY_DELETE) c = 8;
|
if (key == KEY_DELETE) c = 8;
|
||||||
|
if (key == KEY_UP) c = (char)UI_KEY_UP;
|
||||||
|
if (key == KEY_RIGHT) c = (char)UI_KEY_RIGHT;
|
||||||
|
if (key == KEY_DOWN) c = (char)UI_KEY_DOWN;
|
||||||
|
if (key == KEY_LEFT) c = (char)UI_KEY_LEFT;
|
||||||
} while (c == 0);
|
} while (c == 0);
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
|
@ -139,6 +134,22 @@ void portCharSetPos(byte x, byte y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void portColorSet(byte f, byte b) {
|
||||||
|
textcolor(f);
|
||||||
|
textbackground(b);
|
||||||
|
_attr = (f << 4) + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void portCursorShow(bool s) {
|
||||||
|
if (s) {
|
||||||
|
curson();
|
||||||
|
} else {
|
||||||
|
cursoff();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void portDie(char *fmt, ...) {
|
void portDie(char *fmt, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
|
@ -152,56 +163,64 @@ void portDie(char *fmt, ...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool portFileRestore(void) {
|
uint32_t portFClose(void *stream) {
|
||||||
|
return fclose((FILE *)stream);
|
||||||
FILE *in;
|
|
||||||
bool ok = false;
|
|
||||||
|
|
||||||
in = fopen("save.dat", "rb");
|
|
||||||
if (in) {
|
|
||||||
ok = true;
|
|
||||||
|
|
||||||
// Read in PC.
|
|
||||||
ok &= (fread(&__state.pc, sizeof(__state.pc), 1, in) == 1);
|
|
||||||
// Read in SP.
|
|
||||||
ok &= (fread(&__state.sp, sizeof(__state.sp), 1, in) == 1);
|
|
||||||
// Read in BP.
|
|
||||||
ok &= (fread(&__state.bp, sizeof(__state.bp), 1, in) == 1);
|
|
||||||
// Read in dynamic game RAM.
|
|
||||||
ok &= (fread(_RAM, storyStaticMemoryBaseAddress(), 1, in) == 1);
|
|
||||||
// Read in stack.
|
|
||||||
ok &= (fread(__state.stack, sizeof(__state.stack[0]), __state.sp, in) == __state.sp);
|
|
||||||
|
|
||||||
fclose(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool portFileSave(void) {
|
void *portFOpen(char *pathname, char *mode) {
|
||||||
FILE *out;
|
return fopen(pathname, mode);
|
||||||
bool ok = false;
|
}
|
||||||
|
|
||||||
out = fopen("save.dat", "wb");
|
|
||||||
if (out) {
|
|
||||||
ok = true;
|
|
||||||
|
|
||||||
// Write out PC.
|
uint32_t portFRead(void *ptr, uint32_t size, uint32_t nmemb, void *stream) {
|
||||||
ok &= (fwrite(&__state.pc, sizeof(__state.pc), 1, out) == 1);
|
return fread(ptr, size, nmemb, (FILE *)stream);
|
||||||
// Write out SP.
|
}
|
||||||
ok &= (fwrite(&__state.sp, sizeof(__state.sp), 1, out) == 1);
|
|
||||||
// Write out BP.
|
|
||||||
ok &= (fwrite(&__state.bp, sizeof(__state.bp), 1, out) == 1);
|
|
||||||
// Write out dynamic game RAM.
|
|
||||||
ok &= (fwrite(_RAM, storyStaticMemoryBaseAddress(), 1, out) == 1);
|
|
||||||
// Write out stack.
|
|
||||||
ok &= (fwrite(__state.stack, sizeof(__state.stack[0]), __state.sp, out) == __state.sp);
|
|
||||||
|
|
||||||
fclose(out);
|
|
||||||
|
uint32_t portFWrite(void *ptr, uint32_t size, uint32_t nmemb, void *stream) {
|
||||||
|
return fwrite(ptr, size, nmemb, (FILE *)stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void portFileLister(void) {
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *dirent;
|
||||||
|
uint32_t l;
|
||||||
|
FILE *in;
|
||||||
|
|
||||||
|
if ((dir = opendir("."))) {
|
||||||
|
for (;;) {
|
||||||
|
if ((dirent = readdir(dir))) {
|
||||||
|
// Is this a file or symlink?
|
||||||
|
if ((dirent->d_type == DT_REG) || (dirent->d_type == DT_LNK)) {
|
||||||
|
// Get it's length.
|
||||||
|
in = fopen(dirent->d_name, "rb");
|
||||||
|
if (in) {
|
||||||
|
fseek(in, 0, SEEK_END);
|
||||||
|
l = ftell(in);
|
||||||
|
fseek(in, 0, SEEK_SET);
|
||||||
|
fclose(in);
|
||||||
|
// Add it to the story chooser.
|
||||||
|
uiFileAdd(dirent->d_name, l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ok;
|
|
||||||
|
void portFree(void *ptr) {
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void *portMalloc(uint32_t size) {
|
||||||
|
return malloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -218,32 +237,43 @@ uint16_t portRandomGet(int16_t range) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void portStoryLoad(void) {
|
void portScreenClear(void) {
|
||||||
|
clrscr();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void portStoryLoad(char *story, uint32_t *length) {
|
||||||
FILE *in;
|
FILE *in;
|
||||||
uint32_t length;
|
|
||||||
|
|
||||||
in = fopen(_storyFile, "rb");
|
in = fopen(story, "rb");
|
||||||
if (!in) portDie("Unable to open %s!\n", _storyFile);
|
if (!in) portDie("Unable to open %s!\n", story);
|
||||||
|
|
||||||
fseek(in, 0, SEEK_END);
|
_RAM = (byte *)malloc(*length);
|
||||||
length = ftell(in);
|
|
||||||
fseek(in, 0, SEEK_SET);
|
|
||||||
|
|
||||||
_RAM = (byte *)malloc(length);
|
|
||||||
if (!_RAM) {
|
if (!_RAM) {
|
||||||
fclose(in);
|
fclose(in);
|
||||||
portDie("Unable to allocate %u bytes!\n", length);
|
portDie("Unable to allocate %u bytes!\n", *length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fread(_RAM, length, 1, in) != 1) {
|
if (fread(_RAM, *length, 1, in) != 1) {
|
||||||
free(_RAM);
|
free(_RAM);
|
||||||
fclose(in);
|
fclose(in);
|
||||||
portDie("Unable to read %s!\n", _storyFile);
|
portDie("Unable to read %s!\n", story);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(in);
|
fclose(in);
|
||||||
|
}
|
||||||
|
|
||||||
storyChecksumCalculate();
|
|
||||||
|
char *portSymbolsGet(void) {
|
||||||
|
// Box drawing and other symbols needed by the UI.
|
||||||
|
//
|
||||||
|
// 0---1---2
|
||||||
|
// | |
|
||||||
|
// 7 3
|
||||||
|
// | |
|
||||||
|
// 6---5---4
|
||||||
|
|
||||||
|
return "\xda\xc4\xbf\xb3\xd9\xc4\xc0\xb3";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -258,24 +288,14 @@ void portWordSet(uint32_t address, uint16_t value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#include "unistd.h"
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
chdir("/home/scott/code/zip/stories");
|
||||||
if (argc != 2) portDie("Usage: %s [storyfile]\n", argv[0]);
|
|
||||||
|
|
||||||
_storyFile = argv[1];
|
|
||||||
|
|
||||||
setvideomode(videomode_80x25_9x16);
|
setvideomode(videomode_80x25_9x16);
|
||||||
curson();
|
|
||||||
textbackground(1);
|
|
||||||
textcolor(7);
|
|
||||||
clrscr();
|
|
||||||
gotoxy(0, screenheight() - 1);
|
|
||||||
|
|
||||||
stateReset();
|
uiSizeSet(80, 25);
|
||||||
portStoryLoad();
|
uiGameSelect();
|
||||||
opcodesSetup();
|
|
||||||
storySetup(80, 25);
|
|
||||||
interpreterRun();
|
|
||||||
|
|
||||||
free(_RAM);
|
free(_RAM);
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ set(HEADERS
|
||||||
stddclmr.h
|
stddclmr.h
|
||||||
story.h
|
story.h
|
||||||
text.h
|
text.h
|
||||||
|
ui.h
|
||||||
variable.h
|
variable.h
|
||||||
window.h
|
window.h
|
||||||
zscii.h
|
zscii.h
|
||||||
|
@ -52,6 +53,7 @@ set(SOURCE
|
||||||
state.c
|
state.c
|
||||||
story.c
|
story.c
|
||||||
text.c
|
text.c
|
||||||
|
ui.c
|
||||||
variable.c
|
variable.c
|
||||||
window.c
|
window.c
|
||||||
zscii.c
|
zscii.c
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
|
|
||||||
PROJECT=f256zip
|
PROJECT=f256zip
|
||||||
START=0x200
|
START=0x300
|
||||||
|
|
||||||
F256=$(pwd)/../../../f256
|
F256=$(pwd)/../../../f256
|
||||||
LLVM=${F256}/llvm-mos
|
LLVM=${F256}/llvm-mos
|
||||||
|
@ -50,8 +50,8 @@ ${F256}/header \
|
||||||
pgz 24 \
|
pgz 24 \
|
||||||
../${PROJECT}.pgz \
|
../${PROJECT}.pgz \
|
||||||
${START} \
|
${START} \
|
||||||
${PROJECT}.bin ${START} \
|
${PROJECT}.bin ${START}
|
||||||
../../../stories/zork1-r119-s880429.z3 0x10000
|
# ../../../stories/zork1-r119-s880429.z3 0x10000
|
||||||
# ../../../tests/testers/czech/czech.z3 0x10000
|
# ../../../tests/testers/czech/czech.z3 0x10000
|
||||||
|
|
||||||
#llvm-nm ${PROJECT}.elf > ${PROJECT}.lst
|
#llvm-nm ${PROJECT}.elf > ${PROJECT}.lst
|
||||||
|
|
|
@ -38,35 +38,31 @@
|
||||||
#include "f256.h"
|
#include "f256.h"
|
||||||
|
|
||||||
#include "portme.h"
|
#include "portme.h"
|
||||||
#include "story.h"
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "state.h"
|
#include "ui.h"
|
||||||
#include "interpreter.h"
|
|
||||||
#include "../../include/text.h"
|
#include "../../include/text.h"
|
||||||
|
|
||||||
|
|
||||||
#define BASE_ADDRESS 0x10000
|
#define BASE_ADDRESS 0x10000
|
||||||
|
|
||||||
|
|
||||||
static char *_saveName = 0;
|
|
||||||
|
|
||||||
|
|
||||||
void portAttributeSet(byte attribute) {
|
void portAttributeSet(byte attribute) {
|
||||||
switch (attribute) {
|
switch (attribute) {
|
||||||
case TEXT_ATTR_NORMAL:
|
case TEXT_ATTR_NORMAL:
|
||||||
textSetColor(7, 1);
|
portColorSet(7, 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_ATTR_REVERSE:
|
case TEXT_ATTR_REVERSE:
|
||||||
textSetColor(1, 7);
|
portColorSet(1, 7);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_ATTR_BOLD:
|
case TEXT_ATTR_BOLD:
|
||||||
textSetColor(15, 1);
|
portColorSet(15, 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_ATTR_EMPHASIS:
|
case TEXT_ATTR_EMPHASIS:
|
||||||
textSetColor(14, 1);
|
portColorSet(14, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,6 +78,26 @@ void portByteSet(uint32_t address, uint8_t value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char portCharGet(void) {
|
||||||
|
char c;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static char playback[] = "open mailbox\ntake leaflet\nread leaflet\ndrop leaflet\n";
|
||||||
|
static uint32_t pointer = 0;
|
||||||
|
|
||||||
|
if (pointer < libStrLen(playback)) return playback[pointer++];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
c = getchar();
|
||||||
|
if (c == 16) c = (char)UI_KEY_UP;
|
||||||
|
if (c == 6) c = (char)UI_KEY_RIGHT;
|
||||||
|
if (c == 14) c = (char)UI_KEY_DOWN;
|
||||||
|
if (c == 2) c = (char)UI_KEY_LEFT;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void portCharGetPos(byte *x, byte *y) {
|
void portCharGetPos(byte *x, byte *y) {
|
||||||
byte tx;
|
byte tx;
|
||||||
byte ty;
|
byte ty;
|
||||||
|
@ -106,6 +122,20 @@ void portCharSetPos(byte x, byte y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void portColorSet(byte f, byte b) {
|
||||||
|
textSetColor(f, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void portCursorShow(bool s) {
|
||||||
|
if (s) {
|
||||||
|
textSetCursor(199);
|
||||||
|
} else {
|
||||||
|
textSetCursor(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void portDie(char *fmt, ...) {
|
void portDie(char *fmt, ...) {
|
||||||
#ifdef DEBUGGING
|
#ifdef DEBUGGING
|
||||||
printf("%s\n", fmt); // Yeah, this isn't right.
|
printf("%s\n", fmt); // Yeah, this isn't right.
|
||||||
|
@ -114,78 +144,55 @@ void portDie(char *fmt, ...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool portFileRestore(void) {
|
uint32_t portFClose(void *stream) {
|
||||||
|
return fclose((FILE *)stream);
|
||||||
FILE *in;
|
|
||||||
bool ok = false;
|
|
||||||
uint32_t i;
|
|
||||||
byte b;
|
|
||||||
|
|
||||||
in = fopen(_saveName, "rb");
|
|
||||||
if (in) {
|
|
||||||
ok = true;
|
|
||||||
|
|
||||||
// Read in PC.
|
|
||||||
ok &= (fread(&__state.pc, sizeof(__state.pc), 1, in) == 1);
|
|
||||||
// Read in SP.
|
|
||||||
ok &= (fread(&__state.sp, sizeof(__state.sp), 1, in) == 1);
|
|
||||||
// Read in BP.
|
|
||||||
ok &= (fread(&__state.bp, sizeof(__state.bp), 1, in) == 1);
|
|
||||||
// Read in dynamic game RAM.
|
|
||||||
for (i=0; i<storyStaticMemoryBaseAddress(); i++) {
|
|
||||||
ok &= (fread(&b, 1, 1, in) == 1);
|
|
||||||
ZPOKE(i, b);
|
|
||||||
}
|
|
||||||
// Read in stack.
|
|
||||||
ok &= (fread(__state.stack, sizeof(__state.stack[0]), __state.sp, in) == __state.sp);
|
|
||||||
|
|
||||||
fclose(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool portFileSave(void) {
|
void *portFOpen(char *pathname, char *mode) {
|
||||||
FILE *out;
|
return fopen(pathname, mode);
|
||||||
bool ok = false;
|
|
||||||
uint32_t i;
|
|
||||||
byte b;
|
|
||||||
|
|
||||||
out = fopen(_saveName, "wb");
|
|
||||||
if (out) {
|
|
||||||
ok = true;
|
|
||||||
|
|
||||||
// Write out PC.
|
|
||||||
ok &= (fwrite(&__state.pc, sizeof(__state.pc), 1, out) == 1);
|
|
||||||
// Write out SP.
|
|
||||||
ok &= (fwrite(&__state.sp, sizeof(__state.sp), 1, out) == 1);
|
|
||||||
// Write out BP.
|
|
||||||
ok &= (fwrite(&__state.bp, sizeof(__state.bp), 1, out) == 1);
|
|
||||||
// Write out dynamic game RAM.
|
|
||||||
for (i=0; i<storyStaticMemoryBaseAddress(); i++) {
|
|
||||||
b = ZPEEK(i);
|
|
||||||
ok &= (fwrite(&b, 1, 1, out) == 1);
|
|
||||||
}
|
|
||||||
// Write out stack.
|
|
||||||
ok &= (fwrite(__state.stack, sizeof(__state.stack[0]), __state.sp, out) == __state.sp);
|
|
||||||
|
|
||||||
fclose(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char portCharGet(void) {
|
uint32_t portFRead(void *ptr, uint32_t size, uint32_t nmemb, void *stream) {
|
||||||
#if 0
|
return fread(ptr, size, nmemb, (FILE *)stream);
|
||||||
static char playback[] = "open mailbox\ntake leaflet\nread leaflet\ndrop leaflet\n";
|
}
|
||||||
static uint32_t pointer = 0;
|
|
||||||
|
|
||||||
if (pointer < libStrLen(playback)) return playback[pointer++];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return getchar();
|
uint32_t portFWrite(void *ptr, uint32_t size, uint32_t nmemb, void *stream) {
|
||||||
|
return fwrite(ptr, size, nmemb, (FILE *)stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void portFileLister(void) {
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *dirent;
|
||||||
|
uint32_t l;
|
||||||
|
|
||||||
|
if ((dir = opendir("0:"))) {
|
||||||
|
for (;;) {
|
||||||
|
if ((dirent = readdir(dir))) {
|
||||||
|
// Is this a file?
|
||||||
|
if (_DE_ISREG(dirent->d_type)) {
|
||||||
|
// Add it to the story chooser.
|
||||||
|
uiFileAdd(dirent->d_name, dirent->d_blocks * 256);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void portFree(void *ptr) {
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void *portMalloc(uint32_t size) {
|
||||||
|
return malloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -204,16 +211,58 @@ uint16_t portRandomGet(int16_t range) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void portStoryLoad(void) {
|
void portScreenClear(void) {
|
||||||
|
textClear();
|
||||||
|
}
|
||||||
|
|
||||||
// On the F256, the story is loaded into RAM along with the ZIP.
|
|
||||||
// ***TODO*** This likely breaks the "restart" command.
|
|
||||||
|
|
||||||
// Later, we probably want to see if the user has a memory expansion
|
void portStoryLoad(char *story, uint32_t *length) {
|
||||||
// installed. If they do, we can use it and then use the lower memory
|
FILE *in;
|
||||||
// for graphics and sound.
|
uint32_t i;
|
||||||
|
byte r;
|
||||||
|
byte b;
|
||||||
|
uint32_t newLength;
|
||||||
|
|
||||||
storyChecksumCalculate();
|
in = fopen(story, "rb");
|
||||||
|
if (!in) portDie("Unable to open %s!\n", story);
|
||||||
|
|
||||||
|
// The F256 only returns file sizes in blocks of 256.
|
||||||
|
// We load what we can until we get an error and then
|
||||||
|
// see if we're within 256 to determine success.
|
||||||
|
i = BASE_ADDRESS;
|
||||||
|
do {
|
||||||
|
r = fread(&b, 1, 1, in);
|
||||||
|
if (r == 1) ZPOKE(i++, b);
|
||||||
|
} while (r == 1);
|
||||||
|
|
||||||
|
fclose(in);
|
||||||
|
|
||||||
|
newLength = *length - (i - BASE_ADDRESS);
|
||||||
|
if (newLength > 256) portDie("Unable to read %s!\n", story);
|
||||||
|
|
||||||
|
// Update story length.
|
||||||
|
*length = newLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *portSymbolsGet(void) {
|
||||||
|
// Box drawing and other symbols needed by the UI.
|
||||||
|
//
|
||||||
|
// 0---1---2
|
||||||
|
// | |
|
||||||
|
// 7 3
|
||||||
|
// | |
|
||||||
|
// 6---5---4
|
||||||
|
|
||||||
|
// 160 a0
|
||||||
|
// 150 96
|
||||||
|
// 161 a1
|
||||||
|
// 130 82
|
||||||
|
// 163 a3
|
||||||
|
// 150 96
|
||||||
|
// 162 a2
|
||||||
|
// 130 82
|
||||||
|
return "\xa0\x96\xa1\x82\xa3\x96\xa2\x82";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -248,8 +297,6 @@ int main(void) {
|
||||||
{ 0xff, 0xff, 0xff }
|
{ 0xff, 0xff, 0xff }
|
||||||
};
|
};
|
||||||
|
|
||||||
_saveName = "zork1.sav";
|
|
||||||
|
|
||||||
f256Init();
|
f256Init();
|
||||||
|
|
||||||
// Load EGA palette into text CLUT.
|
// Load EGA palette into text CLUT.
|
||||||
|
@ -259,24 +306,30 @@ int main(void) {
|
||||||
}
|
}
|
||||||
textEnableBackgroundColors(true);
|
textEnableBackgroundColors(true);
|
||||||
|
|
||||||
textSetColor(15, 0);
|
/*
|
||||||
textPrint("Welcome to MUDDLE Beta 1, Another Z-Machine!\n\n");
|
uint16_t b;
|
||||||
textSetColor(7, 0);
|
c = 0;
|
||||||
textPrint("Copyright 2024 Scott Duensing, All Rights Reserved.\n");
|
char ch[2] = {0,0};
|
||||||
textPrint("Kangaroo Punch Studios https://kangaroopunch.com\n\n\n");
|
for (b=1; b<256; b++) {
|
||||||
textSetColor(8, 0);
|
ch[0] = (char)b; // (b == 13 ? ' ' : (char)b);
|
||||||
textPrint("Thinking...");
|
textPrint(ch);
|
||||||
|
textPrint("=");
|
||||||
|
textPrintInt(b);
|
||||||
|
c++;
|
||||||
|
if (c > 11) {
|
||||||
|
c = 0;
|
||||||
|
textPrint("\n");
|
||||||
|
} else {
|
||||||
|
if (b<100) textPrint(" ");
|
||||||
|
if (b<10) textPrint(" ");
|
||||||
|
textPrint(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
portCharGet();
|
||||||
|
*/
|
||||||
|
|
||||||
stateReset();
|
uiSizeSet(80, 30);
|
||||||
portStoryLoad();
|
uiGameSelect();
|
||||||
opcodesSetup();
|
|
||||||
storySetup(80, 30);
|
|
||||||
|
|
||||||
textSetColor(7, 1);
|
|
||||||
textClear();
|
|
||||||
textSetCursor(199);
|
|
||||||
|
|
||||||
interpreterRun();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "../src/state.c"
|
#include "../src/state.c"
|
||||||
#include "../src/story.c"
|
#include "../src/story.c"
|
||||||
#include "../src/text.c"
|
#include "../src/text.c"
|
||||||
|
#include "../src/ui.c"
|
||||||
#include "../src/variable.c"
|
#include "../src/variable.c"
|
||||||
#include "../src/window.c"
|
#include "../src/window.c"
|
||||||
#include "../src/zscii.c"
|
#include "../src/zscii.c"
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "variable.h"
|
#include "variable.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "lib.h"
|
#include "lib.h"
|
||||||
|
#include "text.h"
|
||||||
|
|
||||||
|
|
||||||
void interpreterDoBranch(int32_t truth) {
|
void interpreterDoBranch(int32_t truth) {
|
||||||
|
@ -136,10 +137,7 @@ void interpreterRun(void) {
|
||||||
opcodeT op;
|
opcodeT op;
|
||||||
bool eight;
|
bool eight;
|
||||||
|
|
||||||
__state.savedX = 1;
|
// Go!
|
||||||
__state.savedY = storyScreenHeightLines();
|
|
||||||
portCharSetPos(__state.savedX, __state.savedY);
|
|
||||||
|
|
||||||
while (__state.quit == 0) {
|
while (__state.quit == 0) {
|
||||||
opcode = ZPEEK(__state.pc++);
|
opcode = ZPEEK(__state.pc++);
|
||||||
extended = ((opcode == 190) && (storyVersion() >= 5)) ? true : false;
|
extended = ((opcode == 190) && (storyVersion() >= 5)) ? true : false;
|
||||||
|
|
17
src/lib.c
17
src/lib.c
|
@ -43,8 +43,25 @@ char *libStrChr(char *haystack, char needle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int8_t libStrICmp(char *a, char *b) {
|
||||||
|
char d;
|
||||||
|
for (;; a++, b++) {
|
||||||
|
d = libToLower((unsigned char)*a) - libToLower((unsigned char)*b);
|
||||||
|
if (d != 0 || !*a) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t libStrLen(char *str) {
|
uint32_t libStrLen(char *str) {
|
||||||
uint32_t l = 0;
|
uint32_t l = 0;
|
||||||
while (str[l] != 0) l++;
|
while (str[l] != 0) l++;
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char libToLower(char c) {
|
||||||
|
if ((c >= 'A') && (c <= 'Z')) c = c - ('A' - 'z');
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
|
@ -27,30 +27,76 @@
|
||||||
#include "portme.h"
|
#include "portme.h"
|
||||||
#include "story.h"
|
#include "story.h"
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
#include "memory.h"
|
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "ui.h"
|
||||||
|
|
||||||
|
|
||||||
void opcodes_save(void) {
|
void opcodes_save(void) {
|
||||||
interpreterDoBranch(portFileSave() ? 1 : 0);
|
void *out;
|
||||||
|
bool ok = false;
|
||||||
|
uint32_t i;
|
||||||
|
byte b;
|
||||||
|
|
||||||
|
out = portFOpen(uiSaveGet(), "wb");
|
||||||
|
if (out) {
|
||||||
|
ok = true;
|
||||||
|
// Write out PC.
|
||||||
|
ok &= (portFWrite(&__state.pc, sizeof(__state.pc), 1, out) == 1);
|
||||||
|
// Write out SP.
|
||||||
|
ok &= (portFWrite(&__state.sp, sizeof(__state.sp), 1, out) == 1);
|
||||||
|
// Write out BP.
|
||||||
|
ok &= (portFWrite(&__state.bp, sizeof(__state.bp), 1, out) == 1);
|
||||||
|
// Write out dynamic game RAM.
|
||||||
|
for (i=0; i<storyStaticMemoryBaseAddress(); i++) {
|
||||||
|
b = ZPEEK(i);
|
||||||
|
ok &= (portFWrite(&b, 1, 1, out) == 1);
|
||||||
|
}
|
||||||
|
// Write out stack.
|
||||||
|
ok &= (portFWrite(__state.stack, sizeof(__state.stack[0]), __state.sp, out) == __state.sp);
|
||||||
|
portFClose(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
interpreterDoBranch(ok ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void opcodes_restart(void) {
|
void opcodes_restart(void) {
|
||||||
byte width = ZPEEK(STORY_SCREEN_WIDTH_CHARACTERS);
|
|
||||||
byte height = ZPEEK(STORY_SCREEN_HEIGHT_LINES);
|
|
||||||
uint16_t checksum = __state.checksum;
|
uint16_t checksum = __state.checksum;
|
||||||
|
uint32_t length = uiStorySizeGet();
|
||||||
|
|
||||||
stateReset();
|
stateReset();
|
||||||
portStoryLoad();
|
portStoryLoad(uiStoryGet(), &length); // If they change the file size on the second load, they have more problems than us not updating it.
|
||||||
__state.checksum = checksum;
|
__state.checksum = checksum;
|
||||||
opcodesSetup();
|
opcodesSetup();
|
||||||
storySetup(width, height);
|
storySetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void opcodes_restore(void) {
|
void opcodes_restore(void) {
|
||||||
bool ok = portFileRestore();
|
void *in = 0;
|
||||||
|
bool ok = false;
|
||||||
|
uint32_t i;
|
||||||
|
byte b;
|
||||||
|
|
||||||
|
in = portFOpen(uiSaveGet(), "rb");
|
||||||
|
if (in) {
|
||||||
|
ok = true;
|
||||||
|
// Read in PC.
|
||||||
|
ok &= (portFRead(&__state.pc, sizeof(__state.pc), 1, in) == 1);
|
||||||
|
// Read in SP.
|
||||||
|
ok &= (portFRead(&__state.sp, sizeof(__state.sp), 1, in) == 1);
|
||||||
|
// Read in BP.
|
||||||
|
ok &= (portFRead(&__state.bp, sizeof(__state.bp), 1, in) == 1);
|
||||||
|
// Read in dynamic game RAM.
|
||||||
|
for (i=0; i<storyStaticMemoryBaseAddress(); i++) {
|
||||||
|
ok &= (portFRead(&b, 1, 1, in) == 1);
|
||||||
|
ZPOKE(i, b);
|
||||||
|
}
|
||||||
|
// Read in stack.
|
||||||
|
ok &= (portFRead(__state.stack, sizeof(__state.stack[0]), __state.sp, in) == __state.sp);
|
||||||
|
portFClose(in);
|
||||||
|
}
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
// Collapse upper window.
|
// Collapse upper window.
|
||||||
|
|
11
src/state.c
11
src/state.c
|
@ -23,12 +23,23 @@
|
||||||
|
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "lib.h"
|
#include "lib.h"
|
||||||
|
#include "portme.h"
|
||||||
|
#include "messages.h"
|
||||||
|
|
||||||
|
|
||||||
stateT __state;
|
stateT __state;
|
||||||
|
|
||||||
|
static bool _stackAllocated = false;
|
||||||
|
|
||||||
|
|
||||||
void stateReset(void) {
|
void stateReset(void) {
|
||||||
|
|
||||||
|
if (!_stackAllocated) {
|
||||||
|
__state.stack = (uint16_t *)portMalloc(sizeof(uint16_t) * STACK_SIZE);
|
||||||
|
if (__state.stack == 0) portDie(MSG_STA_CANNOT_ALLOCATE_STACK);
|
||||||
|
_stackAllocated = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Zero out all state data.
|
// Zero out all state data.
|
||||||
libMemSet((byte *)&__state, 0, sizeof(stateT));
|
libMemSet((byte *)&__state, 0, sizeof(stateT));
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "story.h"
|
#include "story.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "ui.h"
|
||||||
|
|
||||||
|
|
||||||
void storyChecksumCalculate(void) {
|
void storyChecksumCalculate(void) {
|
||||||
|
@ -55,11 +56,14 @@ uint32_t storyLength() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void storySetup(byte width, byte height) {
|
void storySetup(void) {
|
||||||
char *p;
|
char *p;
|
||||||
uint16_t i;
|
uint16_t i;
|
||||||
|
byte width;
|
||||||
|
byte height;
|
||||||
|
|
||||||
// Set display size.
|
// Set display size.
|
||||||
|
uiSizeGet(&width, &height);
|
||||||
ZPOKE(STORY_SCREEN_WIDTH_CHARACTERS, width);
|
ZPOKE(STORY_SCREEN_WIDTH_CHARACTERS, width);
|
||||||
ZPOKE(STORY_SCREEN_HEIGHT_LINES, height);
|
ZPOKE(STORY_SCREEN_HEIGHT_LINES, height);
|
||||||
|
|
||||||
|
|
342
src/ui.c
Normal file
342
src/ui.c
Normal file
|
@ -0,0 +1,342 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "ui.h"
|
||||||
|
#include "portme.h"
|
||||||
|
#include "lib.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "opcodes.h"
|
||||||
|
#include "story.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
#include "messages.h"
|
||||||
|
#include "text.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define uiStringPrintAt(x,y,s) portCharSetPos(x, y); uiStringPrint(s)
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct fileListS {
|
||||||
|
char *filename;
|
||||||
|
byte filenameLen;
|
||||||
|
uint32_t filesize;
|
||||||
|
struct fileListS *next;
|
||||||
|
} fileListT;
|
||||||
|
|
||||||
|
|
||||||
|
static void uiBox(byte x, byte y, byte w, byte h);
|
||||||
|
static void uiCenter(char *m);
|
||||||
|
static void uiDialog(byte fc, byte bc, byte tc, byte y, char *message);
|
||||||
|
static void uiStringPrint(char *s);
|
||||||
|
static void uiTitleDraw(void);
|
||||||
|
|
||||||
|
|
||||||
|
static byte _screenHeight;
|
||||||
|
static byte _screenWidth;
|
||||||
|
static char *_saveName = 0;
|
||||||
|
static fileListT *_games = 0;
|
||||||
|
static byte _longestName = 0;
|
||||||
|
static fileListT *_selected = 0;
|
||||||
|
static uint16_t _gameCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
static void uiBox(byte x, byte y, byte w, byte h) {
|
||||||
|
byte i;
|
||||||
|
byte j;
|
||||||
|
char *b = portSymbolsGet();
|
||||||
|
|
||||||
|
portCharSetPos(x, y);
|
||||||
|
portCharPrint(b[0]);
|
||||||
|
for (j=0; j<w-2; j++) portCharPrint(b[1]);
|
||||||
|
portCharPrint(b[2]);
|
||||||
|
|
||||||
|
for (i=y+1; i<y+h-1; i++) {
|
||||||
|
portCharSetPos(x, i);
|
||||||
|
portCharPrint(b[7]);
|
||||||
|
for (j=0; j<w-2; j++) portCharPrint(' ');
|
||||||
|
portCharPrint(b[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
portCharSetPos(x, y+h-1);
|
||||||
|
portCharPrint(b[6]);
|
||||||
|
for (j=0; j<w-2; j++) portCharPrint(b[5]);
|
||||||
|
portCharPrint(b[4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void uiCenter(char *m) {
|
||||||
|
byte x;
|
||||||
|
byte y;
|
||||||
|
byte w = 80;
|
||||||
|
byte l = libStrLen(m);
|
||||||
|
|
||||||
|
portCharGetPos(&x, &y);
|
||||||
|
portCharSetPos((w - l) * 0.5, y);
|
||||||
|
for (x=0; x<l; x++) portCharPrint(m[x]);
|
||||||
|
portCharSetPos(1, y + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void uiDialog(byte fc, byte bc, byte tc, byte y, char *message) {
|
||||||
|
byte l;
|
||||||
|
byte x;
|
||||||
|
|
||||||
|
l = libStrLen(message);
|
||||||
|
x = (_screenWidth - l) * 0.5 - 1;
|
||||||
|
portColorSet(fc, bc);
|
||||||
|
uiBox(x - 1, y, l + 4, 5);
|
||||||
|
portColorSet(tc, bc);
|
||||||
|
uiStringPrintAt(x + 1, y + 2, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uiFileAdd(char *filename, uint32_t size) {
|
||||||
|
uint32_t l;
|
||||||
|
byte v;
|
||||||
|
fileListT *newGame;
|
||||||
|
fileListT *currentGame;
|
||||||
|
|
||||||
|
l = libStrLen(filename);
|
||||||
|
|
||||||
|
// Right now, we only support V1-V3 games. Filter the rest.
|
||||||
|
if (l > 3) {
|
||||||
|
if ((filename[l - 2] == 'z') || (filename[l - 2] == 'Z')) {
|
||||||
|
if (filename[l - 3] == '.') {
|
||||||
|
v = filename[l - 1] - '0';
|
||||||
|
if (v <= 3) {
|
||||||
|
// Add game to chooser.
|
||||||
|
|
||||||
|
// Create new game entry.
|
||||||
|
newGame = (fileListT *)portMalloc(sizeof(fileListT));
|
||||||
|
if (newGame == 0) portDie(MSG_UI_CANNOT_ALLOCATE, sizeof(fileListT));
|
||||||
|
newGame->filename = (char *)portMalloc(l + 1);
|
||||||
|
if (newGame->filename == 0) portDie(MSG_UI_CANNOT_ALLOCATE, l + 1);
|
||||||
|
for (v=0; v<l+1; v++) newGame->filename[v] = filename[v];
|
||||||
|
newGame->filesize = size;
|
||||||
|
newGame->filenameLen = l;
|
||||||
|
newGame->next = 0;
|
||||||
|
|
||||||
|
// Insert into list, sorted by filename.
|
||||||
|
if ((_games == 0) || (libStrICmp(_games->filename, newGame->filename) >= 0)) {
|
||||||
|
newGame->next = _games;
|
||||||
|
_games = newGame;
|
||||||
|
} else {
|
||||||
|
currentGame = _games;
|
||||||
|
while ((currentGame->next != 0) && (libStrICmp(currentGame->next->filename, newGame->filename) < 0)) {
|
||||||
|
currentGame = currentGame->next;
|
||||||
|
}
|
||||||
|
newGame->next = currentGame->next;
|
||||||
|
currentGame->next = newGame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uiGameSelect(void) {
|
||||||
|
byte x;
|
||||||
|
byte y;
|
||||||
|
byte h;
|
||||||
|
byte w;
|
||||||
|
byte l;
|
||||||
|
uint16_t i;
|
||||||
|
fileListT *g;
|
||||||
|
byte c;
|
||||||
|
uint16_t top;
|
||||||
|
uint16_t selector;
|
||||||
|
|
||||||
|
// ***TODO*** This doesn't check if filenames are too long to fit on the screen.
|
||||||
|
|
||||||
|
portCursorShow(false);
|
||||||
|
uiTitleDraw();
|
||||||
|
portCharGetPos(&x, &y);
|
||||||
|
y += 2;
|
||||||
|
|
||||||
|
portFileLister();
|
||||||
|
|
||||||
|
// No games?
|
||||||
|
if (_games == 0) {
|
||||||
|
uiDialog(15, 4, 14, y, " No story files found! ");
|
||||||
|
portCharGet();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count games and find longest.
|
||||||
|
g = _games;
|
||||||
|
_gameCount = 0;
|
||||||
|
while (g) {
|
||||||
|
_gameCount++;
|
||||||
|
if (g->filenameLen > _longestName) _longestName = g->filenameLen;
|
||||||
|
g = g->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw chooser window.
|
||||||
|
x = (_screenWidth - _longestName - 4) * 0.5;
|
||||||
|
w = _longestName + 4;
|
||||||
|
h = _screenHeight - y;
|
||||||
|
portColorSet(15, 1);
|
||||||
|
uiBox(x, y, w, h);
|
||||||
|
|
||||||
|
// Adjust for future drawing.
|
||||||
|
x++;
|
||||||
|
y++;
|
||||||
|
h -= 2;
|
||||||
|
w -= 2;
|
||||||
|
|
||||||
|
// Run chooser.
|
||||||
|
top = 0;
|
||||||
|
selector = 0;
|
||||||
|
do {
|
||||||
|
g = _games;
|
||||||
|
// Skip games that have scrolled off the top.
|
||||||
|
for (i=0; i<top; i++) {
|
||||||
|
g = g->next;
|
||||||
|
}
|
||||||
|
// Draw list and selector.
|
||||||
|
for (i=0; i<h; i++) {
|
||||||
|
if (i == selector) {
|
||||||
|
portColorSet(1, 7);
|
||||||
|
_selected = g;
|
||||||
|
} else {
|
||||||
|
portColorSet(7, 1);
|
||||||
|
}
|
||||||
|
portCharSetPos(x, y + i);
|
||||||
|
portCharPrint(' ');
|
||||||
|
uiStringPrint(g->filename);
|
||||||
|
for (c=g->filenameLen+1; c<w; c++) portCharPrint(' ');
|
||||||
|
g = g->next;
|
||||||
|
}
|
||||||
|
// Get input.
|
||||||
|
c = portCharGet();
|
||||||
|
if (c == UI_KEY_DOWN) {
|
||||||
|
if ((selector < h-1) && (selector < _gameCount-1)) {
|
||||||
|
selector++;
|
||||||
|
} else {
|
||||||
|
if (top + h < _gameCount) {
|
||||||
|
top++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c == UI_KEY_UP) {
|
||||||
|
if (selector > 0) {
|
||||||
|
selector--;
|
||||||
|
} else {
|
||||||
|
if (top > 0) {
|
||||||
|
top--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (c != 13);
|
||||||
|
|
||||||
|
// Loading dialog.
|
||||||
|
uiDialog(15, 8, 15, y + 3, " Loading story! ");
|
||||||
|
|
||||||
|
// Generate save game name.
|
||||||
|
_saveName = (char *)portMalloc(_selected->filenameLen + 2);
|
||||||
|
if (_saveName == 0) portDie(MSG_UI_CANNOT_ALLOCATE, _selected->filenameLen + 2);
|
||||||
|
for (i=0; i<_selected->filenameLen; i++) _saveName[i] = _selected->filename[i];
|
||||||
|
_saveName[_selected->filenameLen - 2] = 's';
|
||||||
|
_saveName[_selected->filenameLen - 1] = 'a';
|
||||||
|
_saveName[_selected->filenameLen ] = 'v';
|
||||||
|
_saveName[_selected->filenameLen + 1] = 0;
|
||||||
|
|
||||||
|
// Get ready.
|
||||||
|
stateReset();
|
||||||
|
// uiStringPrint("stateReset()\n");
|
||||||
|
portStoryLoad(_selected->filename, &_selected->filesize);
|
||||||
|
// uiStringPrint("portStoryLoad()\n");
|
||||||
|
storyChecksumCalculate();
|
||||||
|
// uiStringPrint("storyChecksumCalculate()\n");
|
||||||
|
opcodesSetup();
|
||||||
|
storySetup();
|
||||||
|
|
||||||
|
// Set up display.
|
||||||
|
portAttributeSet(TEXT_ATTR_NORMAL);
|
||||||
|
portScreenClear();
|
||||||
|
__state.savedX = 1;
|
||||||
|
__state.savedY = storyScreenHeightLines();
|
||||||
|
portCharSetPos(__state.savedX, __state.savedY);
|
||||||
|
portCursorShow(true);
|
||||||
|
|
||||||
|
// Start the game!
|
||||||
|
interpreterRun();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *uiSaveGet(void) {
|
||||||
|
return _saveName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uiSizeGet(byte *w, byte *h) {
|
||||||
|
*w = _screenWidth;
|
||||||
|
*h = _screenHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uiSizeSet(byte w, byte h) {
|
||||||
|
_screenWidth = w;
|
||||||
|
_screenHeight = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *uiStoryGet(void) {
|
||||||
|
return _selected->filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t uiStorySizeGet(void) {
|
||||||
|
return _selected->filesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void uiStringPrint(char *s) {
|
||||||
|
byte i = 0;
|
||||||
|
while (s[i] != 0) portCharPrint(s[i++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void uiTitleDraw(void) {
|
||||||
|
|
||||||
|
portColorSet(9, 0);
|
||||||
|
portScreenClear();
|
||||||
|
portCharSetPos(1, 1);
|
||||||
|
|
||||||
|
uiCenter(" Welcome to... ");
|
||||||
|
portColorSet(11, 0);
|
||||||
|
uiCenter(" __ __ _ _ _ ");
|
||||||
|
uiCenter("| \\/ | | | | | | ");
|
||||||
|
uiCenter("| \\ / |_ _ __| | __| | | ___ ");
|
||||||
|
uiCenter("| |\\/| | | | |/ _` |/ _` | |/ _ \\");
|
||||||
|
uiCenter("| | | | |_| | (_| | (_| | | __/");
|
||||||
|
uiCenter("|_| |_|\\__,_|\\__,_|\\__,_|_|\\___|");
|
||||||
|
uiCenter("");
|
||||||
|
portColorSet(9, 0);
|
||||||
|
uiCenter(" Release 1.0 Beta 2 ");
|
||||||
|
uiCenter("");
|
||||||
|
portColorSet(12, 0);
|
||||||
|
uiCenter("Copyright 2024 Scott Duensing, All Rights Reserved");
|
||||||
|
portColorSet(4, 0);
|
||||||
|
uiCenter("2024 Kangaroo Punch Studios https://kangaroopunch.com");
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue