Menuing system almost working.

This commit is contained in:
Scott Duensing 2024-02-07 18:58:53 -06:00
parent c03bb427bc
commit 88b4a6850a
19 changed files with 764 additions and 205 deletions

View file

@ -25,6 +25,7 @@ set(HEADERS
stddclmr.h
story.h
text.h
ui.h
variable.h
window.h
zscii.h
@ -53,6 +54,7 @@ set(SOURCE
state.c
story.c
text.c
ui.c
variable.c
window.c
zscii.c

View file

@ -30,7 +30,9 @@
void libMemSet(byte *start, byte val, uint32_t len);
char *libStrChr(char *haystack, char needle);
int8_t libStrICmp(char *a, char *b);
uint32_t libStrLen(char *str);
char libToLower(char c);
#endif // LIB_H

View file

@ -42,11 +42,13 @@
#define MSG_INT_V12_SHIFT "Add V1/V2 shifting."
#define MSG_INT_V12_SHIFT_LOCK "Add V1/V2 shift locking."
#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_STACK_OVERFLOW "Stack overflow!"
#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_WIN_NO_SPLITTING "Window splitting is not supported."
#define MSG_UI_CANNOT_ALLOCATE "Cannot allocate %l bytes!"
#else // DEBUGGING
#define MSG_UNIMPLEMENTED ""
#define MSG_INT_INVALID_EXT_OPCODE ""
@ -61,11 +63,13 @@
#define MSG_INT_V12_SHIFT ""
#define MSG_INT_V12_SHIFT_LOCK ""
#define MSG_MEM_BUFFER ""
#define MSG_STA_CANNOT_ALLOCATE_STACK ""
#define MSG_OP_CALL_TOO_MANY_LOCALS ""
#define MSG_OP_CALL_STACK_OVERFLOW ""
#define MSG_OP_INPUT_BUFFER_TOO_SMALL ""
#define MSG_OP_OBJ_MISSING_PROPERTY ""
#define MSG_OP_WIN_NO_SPLITTING ""
#define MSG_UI_CANNOT_ALLOCATE ""
#endif // DEBUGGING

View file

@ -35,11 +35,20 @@ char portCharGet(void);
void portCharGetPos(byte *x, byte *y);
void portCharPrint(char c);
void portCharSetPos(byte x, byte y);
void portColorSet(byte f, byte b);
void portCursorShow(bool s);
void portDie(char *fmt, ...);
bool portFileRestore(void);
bool portFileSave(void);
uint32_t portFClose(void *stream);
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);
void portStoryLoad(void);
void portScreenClear(void);
void portStoryLoad(char *story, uint32_t *length);
char *portSymbolsGet(void);
uint16_t portWordGet(uint32_t address);
void portWordSet(uint32_t address, uint16_t value);

View file

@ -30,7 +30,7 @@
typedef struct stateS {
uint16_t stack[STACK_SIZE];
uint16_t *stack;
bool quit;
uint32_t pc; // Program Counter
uint16_t sp; // Stack Pointer

View file

@ -120,7 +120,7 @@
void storyChecksumCalculate(void);
uint32_t storyLength(void);
void storySetup(byte width, byte height);
void storySetup(void);
#endif // STORY_H

46
include/ui.h Normal file
View 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

View file

@ -27,6 +27,7 @@ set(HEADERS
stddclmr.h
story.h
text.h
ui.h
variable.h
window.h
zscii.h
@ -52,6 +53,7 @@ set(SOURCE
state.c
story.c
text.c
ui.c
variable.c
window.c
zscii.c

View file

@ -25,12 +25,12 @@
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <dirent.h>
#include "portme.h"
#include "story.h"
#include "state.h"
#include "interpreter.h"
#include "text.h"
#include "ui.h"
#pragma push_macro("bool")
#undef bool
@ -39,35 +39,26 @@
#pragma pop_macro("bool")
static char *_storyFile = NULL;
static uint8_t *_RAM = NULL;
static uint8_t _attr = (1 << 4) + 7;
static uint8_t _attr = 0;
void portAttributeSet(byte attribute) {
switch (attribute) {
case TEXT_ATTR_NORMAL:
textbackground(1);
textcolor(7);
_attr = (1 << 4) + 7;
portColorSet(7, 1);
break;
case TEXT_ATTR_REVERSE:
textbackground(7);
textcolor(1);
_attr = (7 << 4) + 1;
portColorSet(1, 7);
break;
case TEXT_ATTR_BOLD:
textbackground(1);
textcolor(15);
_attr = (1 << 4) + 15;
portColorSet(15, 1);
break;
case TEXT_ATTR_EMPHASIS:
textbackground(1);
textcolor(14);
_attr = (1 << 4) + 14;
portColorSet(14, 1);
break;
}
}
@ -100,6 +91,10 @@ char portCharGet(void) {
if (key == KEY_RETURN) c = 13;
if (key == KEY_BACK) c = 127;
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);
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, ...) {
va_list ap;
@ -152,56 +163,64 @@ void portDie(char *fmt, ...) {
}
bool portFileRestore(void) {
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;
uint32_t portFClose(void *stream) {
return fclose((FILE *)stream);
}
bool portFileSave(void) {
FILE *out;
bool ok = false;
void *portFOpen(char *pathname, char *mode) {
return fopen(pathname, mode);
}
out = fopen("save.dat", "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.
ok &= (fwrite(_RAM, storyStaticMemoryBaseAddress(), 1, out) == 1);
// Write out stack.
ok &= (fwrite(__state.stack, sizeof(__state.stack[0]), __state.sp, out) == __state.sp);
uint32_t portFRead(void *ptr, uint32_t size, uint32_t nmemb, void *stream) {
return fread(ptr, size, nmemb, (FILE *)stream);
}
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;
uint32_t length;
in = fopen(_storyFile, "rb");
if (!in) portDie("Unable to open %s!\n", _storyFile);
in = fopen(story, "rb");
if (!in) portDie("Unable to open %s!\n", story);
fseek(in, 0, SEEK_END);
length = ftell(in);
fseek(in, 0, SEEK_SET);
_RAM = (byte *)malloc(length);
_RAM = (byte *)malloc(*length);
if (!_RAM) {
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);
fclose(in);
portDie("Unable to read %s!\n", _storyFile);
portDie("Unable to read %s!\n", story);
}
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[]) {
if (argc != 2) portDie("Usage: %s [storyfile]\n", argv[0]);
_storyFile = argv[1];
chdir("/home/scott/code/zip/stories");
setvideomode(videomode_80x25_9x16);
curson();
textbackground(1);
textcolor(7);
clrscr();
gotoxy(0, screenheight() - 1);
stateReset();
portStoryLoad();
opcodesSetup();
storySetup(80, 25);
interpreterRun();
uiSizeSet(80, 25);
uiGameSelect();
free(_RAM);

View file

@ -25,6 +25,7 @@ set(HEADERS
stddclmr.h
story.h
text.h
ui.h
variable.h
window.h
zscii.h
@ -52,6 +53,7 @@ set(SOURCE
state.c
story.c
text.c
ui.c
variable.c
window.c
zscii.c

View file

@ -24,7 +24,7 @@
PROJECT=f256zip
START=0x200
START=0x300
F256=$(pwd)/../../../f256
LLVM=${F256}/llvm-mos
@ -50,8 +50,8 @@ ${F256}/header \
pgz 24 \
../${PROJECT}.pgz \
${START} \
${PROJECT}.bin ${START} \
../../../stories/zork1-r119-s880429.z3 0x10000
${PROJECT}.bin ${START}
# ../../../stories/zork1-r119-s880429.z3 0x10000
# ../../../tests/testers/czech/czech.z3 0x10000
#llvm-nm ${PROJECT}.elf > ${PROJECT}.lst

View file

@ -38,35 +38,31 @@
#include "f256.h"
#include "portme.h"
#include "story.h"
#include "memory.h"
#include "state.h"
#include "interpreter.h"
#include "ui.h"
#include "../../include/text.h"
#define BASE_ADDRESS 0x10000
static char *_saveName = 0;
void portAttributeSet(byte attribute) {
switch (attribute) {
case TEXT_ATTR_NORMAL:
textSetColor(7, 1);
portColorSet(7, 1);
break;
case TEXT_ATTR_REVERSE:
textSetColor(1, 7);
portColorSet(1, 7);
break;
case TEXT_ATTR_BOLD:
textSetColor(15, 1);
portColorSet(15, 1);
break;
case TEXT_ATTR_EMPHASIS:
textSetColor(14, 1);
portColorSet(14, 1);
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) {
byte tx;
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, ...) {
#ifdef DEBUGGING
printf("%s\n", fmt); // Yeah, this isn't right.
@ -114,78 +144,55 @@ void portDie(char *fmt, ...) {
}
bool portFileRestore(void) {
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;
uint32_t portFClose(void *stream) {
return fclose((FILE *)stream);
}
bool portFileSave(void) {
FILE *out;
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;
void *portFOpen(char *pathname, char *mode) {
return fopen(pathname, mode);
}
char portCharGet(void) {
#if 0
static char playback[] = "open mailbox\ntake leaflet\nread leaflet\ndrop leaflet\n";
static uint32_t pointer = 0;
uint32_t portFRead(void *ptr, uint32_t size, uint32_t nmemb, void *stream) {
return fread(ptr, size, nmemb, (FILE *)stream);
}
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
// installed. If they do, we can use it and then use the lower memory
// for graphics and sound.
void portStoryLoad(char *story, uint32_t *length) {
FILE *in;
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 }
};
_saveName = "zork1.sav";
f256Init();
// Load EGA palette into text CLUT.
@ -259,24 +306,30 @@ int main(void) {
}
textEnableBackgroundColors(true);
textSetColor(15, 0);
textPrint("Welcome to MUDDLE Beta 1, Another Z-Machine!\n\n");
textSetColor(7, 0);
textPrint("Copyright 2024 Scott Duensing, All Rights Reserved.\n");
textPrint("Kangaroo Punch Studios https://kangaroopunch.com\n\n\n");
textSetColor(8, 0);
textPrint("Thinking...");
/*
uint16_t b;
c = 0;
char ch[2] = {0,0};
for (b=1; b<256; b++) {
ch[0] = (char)b; // (b == 13 ? ' ' : (char)b);
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();
portStoryLoad();
opcodesSetup();
storySetup(80, 30);
textSetColor(7, 1);
textClear();
textSetCursor(199);
interpreterRun();
uiSizeSet(80, 30);
uiGameSelect();
return 0;
}

View file

@ -40,6 +40,7 @@
#include "../src/state.c"
#include "../src/story.c"
#include "../src/text.c"
#include "../src/ui.c"
#include "../src/variable.c"
#include "../src/window.c"
#include "../src/zscii.c"

View file

@ -31,6 +31,7 @@
#include "variable.h"
#include "memory.h"
#include "lib.h"
#include "text.h"
void interpreterDoBranch(int32_t truth) {
@ -136,10 +137,7 @@ void interpreterRun(void) {
opcodeT op;
bool eight;
__state.savedX = 1;
__state.savedY = storyScreenHeightLines();
portCharSetPos(__state.savedX, __state.savedY);
// Go!
while (__state.quit == 0) {
opcode = ZPEEK(__state.pc++);
extended = ((opcode == 190) && (storyVersion() >= 5)) ? true : false;

View file

@ -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 l = 0;
while (str[l] != 0) l++;
return l;
}
char libToLower(char c) {
if ((c >= 'A') && (c <= 'Z')) c = c - ('A' - 'z');
return c;
}

View file

@ -27,30 +27,76 @@
#include "portme.h"
#include "story.h"
#include "opcodes.h"
#include "memory.h"
#include "window.h"
#include "memory.h"
#include "ui.h"
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) {
byte width = ZPEEK(STORY_SCREEN_WIDTH_CHARACTERS);
byte height = ZPEEK(STORY_SCREEN_HEIGHT_LINES);
uint16_t checksum = __state.checksum;
uint32_t length = uiStorySizeGet();
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;
opcodesSetup();
storySetup(width, height);
storySetup();
}
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) {
// Collapse upper window.

View file

@ -23,12 +23,23 @@
#include "state.h"
#include "lib.h"
#include "portme.h"
#include "messages.h"
stateT __state;
static bool _stackAllocated = false;
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.
libMemSet((byte *)&__state, 0, sizeof(stateT));

View file

@ -24,6 +24,7 @@
#include "story.h"
#include "state.h"
#include "memory.h"
#include "ui.h"
void storyChecksumCalculate(void) {
@ -55,11 +56,14 @@ uint32_t storyLength() {
}
void storySetup(byte width, byte height) {
void storySetup(void) {
char *p;
uint16_t i;
byte width;
byte height;
// Set display size.
uiSizeGet(&width, &height);
ZPOKE(STORY_SCREEN_WIDTH_CHARACTERS, width);
ZPOKE(STORY_SCREEN_HEIGHT_LINES, height);

342
src/ui.c Normal file
View 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");
}