/* * 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 "f256.c" #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; j0; 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