396 lines
8.3 KiB
C
396 lines
8.3 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Z-Machine for the Foenix F256 computers.
|
|
*
|
|
* F256 machines are 65c02 based systems with 512k of banked RAM.
|
|
* As such, this ZIP is going to load the story entirely into "high"
|
|
* memory (above the 64k accessable to the CPU) and page things in
|
|
* and out as needed.
|
|
*
|
|
* To keep library code at a minimum, we'll also statically allocate
|
|
* as much as possible and do other "bad" things now frowned upon in
|
|
* modern development. :-)
|
|
*/
|
|
|
|
|
|
#define WITHOUT_GRAPHICS
|
|
#include "f256.h"
|
|
|
|
#include "portme.h"
|
|
#include "lib.h"
|
|
|
|
#include "../../include/text.h"
|
|
|
|
|
|
#ifdef MESSAGES
|
|
#define MSG_PORT_UNABLE_TO_OPEN_STORY "Unable to open %s!"
|
|
#define MSG_PORT_UNABLE_TO_ALLOCATE_PATH "Unable to allocate path %d."
|
|
#else
|
|
#define MSG_PORT_UNABLE_TO_OPEN_STORY ""
|
|
#define MSG_PORT_UNABLE_TO_ALLOCATE_PATH ""
|
|
#endif
|
|
|
|
|
|
#define BASE_ADDRESS 0x10000
|
|
|
|
|
|
void portAttributeSet(byte attribute) {
|
|
switch (attribute) {
|
|
case TEXT_ATTR_NORMAL:
|
|
portColorSet(7, 1);
|
|
break;
|
|
|
|
case TEXT_ATTR_REVERSE:
|
|
portColorSet(1, 7);
|
|
break;
|
|
|
|
case TEXT_ATTR_BOLD:
|
|
portColorSet(15, 1);
|
|
break;
|
|
|
|
case TEXT_ATTR_EMPHASIS:
|
|
portColorSet(14, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
uint8_t portByteGet(uint32_t address) {
|
|
uint8_t r = FAR_PEEK(BASE_ADDRESS + address);
|
|
// libf256 leaves slot 5 wherever it lands - we need it put back because there is code there.
|
|
POKE(MMU_MEM_BANK_5, SWAP_SLOT - 8);
|
|
return r;
|
|
}
|
|
|
|
|
|
void portByteSet(uint32_t address, uint8_t value) {
|
|
FAR_POKE(BASE_ADDRESS + address, value);
|
|
POKE(MMU_MEM_BANK_5, SWAP_SLOT - 8);
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
textGetXY(&tx, &ty);
|
|
// ZIP uses 1-based coordinates.
|
|
*x = tx + 1;
|
|
*y = ty + 1;
|
|
}
|
|
|
|
|
|
void portCharPrint(char c) {
|
|
static char ch[2] = { 0, 0 };
|
|
ch[0] = c;
|
|
textPrint(ch);
|
|
}
|
|
|
|
|
|
void portCharSetPos(byte x, byte y) {
|
|
// ZIP uses 1-based coordinates.
|
|
textGotoXY(x - 1, y - 1);
|
|
}
|
|
|
|
|
|
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.
|
|
#else
|
|
textPrint("\n\n");
|
|
textPrint(fmt);
|
|
#endif
|
|
exit(1);
|
|
}
|
|
|
|
|
|
uint32_t portFClose(void *stream) {
|
|
return fclose((FILE *)stream);
|
|
}
|
|
|
|
|
|
void *portFOpen(char *pathname, char *mode) {
|
|
return fopen(pathname, mode);
|
|
}
|
|
|
|
|
|
uint32_t portFRead(void *ptr, uint32_t size, uint32_t nmemb, void *stream) {
|
|
return fread(ptr, size, nmemb, (FILE *)stream);
|
|
}
|
|
|
|
|
|
uint32_t portFWrite(void *ptr, uint32_t size, uint32_t nmemb, void *stream) {
|
|
return fwrite(ptr, size, nmemb, (FILE *)stream);
|
|
}
|
|
|
|
|
|
void portFileLister(char *path) {
|
|
DIR *dir;
|
|
struct dirent *dirent;
|
|
uint32_t l;
|
|
|
|
if ((dir = opendir(path))) {
|
|
for (;;) {
|
|
dirent = readdir(dir);
|
|
if (dirent == 0) break;
|
|
// Is this a file?
|
|
if (_DE_ISREG(dirent->d_type)) {
|
|
// Add it to the story chooser.
|
|
// ***TODO*** d_blocks * 256 isn't right.
|
|
uiFileAdd(dirent->d_name, dirent->d_blocks * 256);
|
|
}
|
|
}
|
|
closedir(dir);
|
|
}
|
|
}
|
|
|
|
|
|
void portFree(void *ptr) {
|
|
free(ptr);
|
|
}
|
|
|
|
|
|
void *portMalloc(uint32_t size) {
|
|
return malloc(size);
|
|
}
|
|
|
|
|
|
uint16_t portRandomGet(int16_t range) {
|
|
// If range is zero, randomize with "best" random seed.
|
|
// If range is negative, use the positive value to seed.
|
|
|
|
if (range == 0) return 0;
|
|
|
|
if (range < 0) {
|
|
randomSeed(-range);
|
|
return 0;
|
|
}
|
|
|
|
return (randomRead() % range) + 1;
|
|
}
|
|
|
|
|
|
void portScreenClear(void) {
|
|
textClear();
|
|
}
|
|
|
|
|
|
void portStoryLoad(char *story, uint32_t *length) {
|
|
FILE *in;
|
|
uint32_t i;
|
|
byte j;
|
|
byte r;
|
|
|
|
in = fopen(story, "rb");
|
|
if (!in) portDie(MSG_PORT_UNABLE_TO_OPEN_STORY, 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 = 0;
|
|
do {
|
|
r = fread(__memoryBuffer, 1, 255, in);
|
|
if (r > 0) {
|
|
for (j=0; j<r; j++) {
|
|
storyAddToChecksum(i, __memoryBuffer[j]);
|
|
ZPOKE(i++, __memoryBuffer[j]);
|
|
}
|
|
}
|
|
} while (r == 255);
|
|
|
|
fclose(in);
|
|
|
|
// Update story length.
|
|
*length = i;
|
|
}
|
|
|
|
|
|
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";
|
|
}
|
|
|
|
|
|
uint16_t portWordGet(uint32_t address) {
|
|
uint16_t r = FAR_PEEKW(BASE_ADDRESS + address);
|
|
POKE(MMU_MEM_BANK_5, SWAP_SLOT - 8);
|
|
return SWAP_UINT16(r);
|
|
}
|
|
|
|
|
|
void portWordSet(uint32_t address, uint16_t value) {
|
|
uint16_t v = SWAP_UINT16(value);
|
|
FAR_POKEW(BASE_ADDRESS + address, v);
|
|
POKE(MMU_MEM_BANK_5, SWAP_SLOT - 8);
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
byte c;
|
|
byte colors[16][3] = {
|
|
{ 0x00, 0x00, 0x00 },
|
|
{ 0x00, 0x00, 0xaa },
|
|
{ 0x00, 0xaa, 0x00 },
|
|
{ 0x00, 0xaa, 0xaa },
|
|
{ 0xaa, 0x00, 0x00 },
|
|
{ 0xaa, 0x00, 0xaa },
|
|
{ 0xaa, 0x55, 0x00 },
|
|
{ 0xaa, 0xaa, 0xaa },
|
|
{ 0x55, 0x55, 0x55 },
|
|
{ 0x55, 0x55, 0xff },
|
|
{ 0x55, 0xff, 0x55 },
|
|
{ 0x55, 0xff, 0xff },
|
|
{ 0xff, 0x55, 0x55 },
|
|
{ 0xff, 0x55, 0xff },
|
|
{ 0xff, 0xff, 0x55 },
|
|
{ 0xff, 0xff, 0xff }
|
|
};
|
|
byte x;
|
|
byte l;
|
|
char *noPath = "0:";
|
|
char *path = 0;
|
|
|
|
// No arguments provided. Assume drive 0:.
|
|
if (argc == 0) {
|
|
path = noPath;
|
|
}
|
|
|
|
// One argumet, the program name. Use same path as PGZ.
|
|
if (argc == 1) {
|
|
// Remove PGZ name from path.
|
|
l = libStrLen(argv[0]);
|
|
for (x=l; x>0; x--) {
|
|
if ((x == 1) && (argv[0][x] == ':')) break; // Drive number.
|
|
if (argv[0][x] == '/') break; // Path separator.
|
|
}
|
|
if (x > 0) {
|
|
path = (char *)malloc(x + 1);
|
|
if (path == 0) portDie(MSG_PORT_UNABLE_TO_ALLOCATE_PATH, 0);
|
|
for (l=0; l<=x; l++) path[l] = argv[0][l];
|
|
path[l] = 0;
|
|
} else {
|
|
path = noPath;
|
|
}
|
|
}
|
|
|
|
// User provided at least one command line argument. Use first one.
|
|
if (argc > 1) {
|
|
// Does it end with a '/'?
|
|
l = libStrLen(argv[1]);
|
|
if (argv[1][l-1] != '/') l++; // Add space for a slash.
|
|
path = (char *)malloc(l);
|
|
if (path == 0) portDie(MSG_PORT_UNABLE_TO_ALLOCATE_PATH, 1);
|
|
for (x=0; x<l; x++) path[x] = argv[1][x];
|
|
path[x-1] = '/';
|
|
path[x] = 0;
|
|
}
|
|
|
|
// Load EGA palette into text CLUT.
|
|
for (c=0; c<16; c++) {
|
|
textDefineForegroundColor(c, colors[c][0], colors[c][1], colors[c][2]);
|
|
textDefineBackgroundColor(c, colors[c][0], colors[c][1], colors[c][2]);
|
|
}
|
|
textEnableBackgroundColors(true);
|
|
|
|
/*
|
|
// Ugly font dump.
|
|
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();
|
|
*/
|
|
|
|
uiSizeSet(80, 30);
|
|
uiGameSelect(path);
|
|
|
|
if (path != noPath) free(path);
|
|
|
|
return 0;
|
|
}
|