diff --git a/CMakeLists.txt b/CMakeLists.txt index e29070d..99e9790 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ set(HEADERS story.h text.h variable.h + window.h zscii.h # czech.z3.h zork1.h @@ -53,6 +54,7 @@ set(SOURCE story.c text.c variable.c + window.c zscii.c ) list(TRANSFORM SOURCE PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/src/") @@ -60,11 +62,10 @@ list(TRANSFORM SOURCE PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/src/") add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCE} - src/text.c - include/text.h ) target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) -#target_link_libraries(${CMAKE_PROJECT_NAME} -#) +target_link_libraries(${CMAKE_PROJECT_NAME} + -lcurses +) diff --git a/include/interpreter.h b/include/interpreter.h index 71e54b9..9827625 100644 --- a/include/interpreter.h +++ b/include/interpreter.h @@ -34,7 +34,6 @@ bool interpreterParseOperand(uint8_t opType, uint8_t operandId); uint8_t interpreterParseVariableOperands(uint8_t index); void interpreterRun(void); void interpreterTokenizeInput(void); -void interpreterUpdateStatusBar(void); #endif // INTERPRETER_H diff --git a/include/messages.h b/include/messages.h index 37c089e..d42141d 100644 --- a/include/messages.h +++ b/include/messages.h @@ -46,6 +46,7 @@ #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." #else // DEBUGGING #define MSG_UNIMPLEMENTED "" #define MSG_INT_INVALID_EXT_OPCODE "" @@ -64,6 +65,7 @@ #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 "" #endif // DEBUGGING diff --git a/include/oc_window.h b/include/oc_window.h index 2a6d0b0..c0da476 100644 --- a/include/oc_window.h +++ b/include/oc_window.h @@ -28,4 +28,9 @@ #include "common.h" +void opcodes_set_window(void); +void opcodes_show_status(void); +void opcodes_split_window(void); + + #endif // OC_WINDOW_H diff --git a/include/portme.h b/include/portme.h index c1893b5..a1db399 100644 --- a/include/portme.h +++ b/include/portme.h @@ -30,9 +30,9 @@ uint8_t portByteGet(uint32_t address); void portByteSet(uint32_t address, uint8_t value); -void portCharPrint(char c); char portCharGet(void); void portCharGetPos(byte *x, byte *y); +void portCharPrint(char c); void portCharSetPos(byte x, byte y); void portDie(char *fmt, ...); bool portFileRestore(void); diff --git a/include/state.h b/include/state.h index 3098acf..fb147a7 100644 --- a/include/state.h +++ b/include/state.h @@ -40,6 +40,9 @@ typedef struct stateS { uint8_t operandCount; uint16_t operands[8]; char alphabetTable[78]; + uint16_t statusBarLen; + uint16_t currentWindow; + uint16_t upperWindowLineCount; // If 0, there is no window split. #ifdef DEBUGGING char *opcodesName[256]; char *extOpcodesName[30]; diff --git a/include/window.h b/include/window.h new file mode 100644 index 0000000..cf2437c --- /dev/null +++ b/include/window.h @@ -0,0 +1,36 @@ +/* + * 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 WINDOW_H +#define WINDOW_H + + +#include "common.h" + + +void windowSet(uint16_t oldWindow, uint16_t newWindow); +void windowSplit(uint16_t oldLines, uint16_t newLines); +void windowUpdateStatusBar(void); + + +#endif // WINDOW_H diff --git a/ports/f256/CMakeLists.txt b/ports/f256/CMakeLists.txt index 20aed0a..6ddd875 100644 --- a/ports/f256/CMakeLists.txt +++ b/ports/f256/CMakeLists.txt @@ -26,6 +26,7 @@ set(HEADERS story.h text.h variable.h + window.h zscii.h # czech.z3.h # zork1.h @@ -52,6 +53,7 @@ set(SOURCE story.c text.c variable.c + window.c zscii.c ) list(TRANSFORM SOURCE PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/../../src/") diff --git a/ports/f256/f256zip.c b/ports/f256/f256zip.c index 74459e3..8d5bc6a 100644 --- a/ports/f256/f256zip.c +++ b/ports/f256/f256zip.c @@ -42,6 +42,7 @@ #include "memory.h" #include "state.h" #include "interpreter.h" +#include "lib.h" #define BASE_ADDRESS 0x10000 @@ -153,6 +154,13 @@ bool portFileSave(void) { char portCharGet(void) { +#if 1 + 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(); } diff --git a/ports/zip.c b/ports/zip.c index 62df6fe..4a80511 100644 --- a/ports/zip.c +++ b/ports/zip.c @@ -41,4 +41,5 @@ #include "../src/story.c" #include "../src/text.c" #include "../src/variable.c" +#include "../src/window.c" #include "../src/zscii.c" diff --git a/src/interpreter.c b/src/interpreter.c index 2ba54e5..dfaa728 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -363,8 +363,3 @@ void interpreterTokenizeInput(void) { ZPOKE(__state.operands[1] + 1, numToks); } - - -void interpreterUpdateStatusBar(void) { - //***TODO*** -} diff --git a/src/oc_input.c b/src/oc_input.c index 06b1973..62a2035 100644 --- a/src/oc_input.c +++ b/src/oc_input.c @@ -28,6 +28,7 @@ #include "messages.h" #include "interpreter.h" #include "text.h" +#include "window.h" void opcodes_read(void) { @@ -43,7 +44,7 @@ void opcodes_read(void) { if (inputLen == 0) portDie(MSG_OP_INPUT_BUFFER_TOO_SMALL); - interpreterUpdateStatusBar(); + windowUpdateStatusBar(); // Read input from user. textInput(input, inputLen); diff --git a/src/oc_save.c b/src/oc_save.c index e95ccb8..f798a28 100644 --- a/src/oc_save.c +++ b/src/oc_save.c @@ -28,6 +28,7 @@ #include "story.h" #include "opcodes.h" #include "memory.h" +#include "window.h" void opcodes_save(void) { @@ -47,5 +48,12 @@ void opcodes_restart(void) { void opcodes_restore(void) { - interpreterDoBranch(portFileRestore() ? 1 : 0); + bool ok = portFileRestore(); + uint16_t oldLines = __state.upperWindowLineCount; + + // Collapse upper window. + __state.upperWindowLineCount = 0; + windowSplit(oldLines, 0); + + interpreterDoBranch(ok ? 1 : 0); } diff --git a/src/oc_window.c b/src/oc_window.c index 0762a46..334ce1c 100644 --- a/src/oc_window.c +++ b/src/oc_window.c @@ -22,3 +22,34 @@ #include "oc_window.h" +#include "story.h" +#include "state.h" +#include "messages.h" +#include "window.h" + + +void opcodes_set_window(void) { + uint16_t oldWindow = __state.upperWindowLineCount; + + // If the interpreter has not set the flag for splitting, this is illegal. + if ((storyFlags() & STORY_FLAG_V3_SCREEN_SPLITTING) == 0) portDie(MSG_OP_WIN_NO_SPLITTING); + + __state.currentWindow = __state.operands[0]; + windowSet(oldWindow, __state.currentWindow); +} + + +void opcodes_show_status(void) { + windowUpdateStatusBar(); +} + + +void opcodes_split_window(void) { + uint16_t oldSize = __state.upperWindowLineCount; + + // If the interpreter has not set the flag for splitting, this is illegal. + if ((storyFlags() & STORY_FLAG_V3_SCREEN_SPLITTING) == 0) portDie(MSG_OP_WIN_NO_SPLITTING); + + __state.upperWindowLineCount = __state.operands[0]; + windowSplit(oldSize, __state.upperWindowLineCount); +} diff --git a/src/opcodes.c b/src/opcodes.c index a2718ba..a8eed2c 100644 --- a/src/opcodes.c +++ b/src/opcodes.c @@ -122,11 +122,11 @@ void opcodesBuiltInitialTable(void) { if (storyVersion() < 3) return; - mOP(188, show_status); + OP(188, show_status); OP(189, verify); - mOP(234, split_window); - mOP(235, set_window); + OP(234, split_window); + OP(235, set_window); mOP(243, output_stream); mOP(244, input_stream); diff --git a/src/portme.c b/src/portme.c index 7300ab9..138d1f5 100644 --- a/src/portme.c +++ b/src/portme.c @@ -25,15 +25,17 @@ #include #include #include -#include -#include #include "portme.h" #include "story.h" -#include "memory.h" #include "state.h" #include "interpreter.h" +#pragma push_macro("bool") +#undef bool +#include +#pragma pop_macro("bool") + #include "zork1.h" //#include "czech.z3.h" @@ -52,15 +54,47 @@ void portByteSet(uint32_t address, uint8_t value) { } +char portCharGet(void) { +#if 1 + static char playback[] = "open mailbox\ntake leaflet\nread leaflet\n"; + static uint32_t pointer = 0; + + if (pointer < strlen(playback)) return playback[pointer++]; + + return getch(); +#else + return getch(); +#endif +} + + +void portCharGetPos(byte *x, byte *y) { + byte xt; + byte yt; + + getyx(stdscr, yt, xt); + + *x = xt + 1; + *y = yt + 1; +} + + void portCharPrint(char c) { - printf("%c", c); - fflush(stdout); + addch(c); + refresh(); +} + + +void portCharSetPos(byte x, byte y) { + move(x - 1, y - 1); } void portDie(char *fmt, ...) { va_list ap; + endwin(); + printf("\n"); va_start(ap, fmt); vprintf(fmt, ap); @@ -124,46 +158,6 @@ bool portFileSave(void) { } -/* reads from keypress, doesn't echo */ -int getch(void) { - struct termios oldattr, newattr; - int ch; - tcgetattr( STDIN_FILENO, &oldattr ); - newattr = oldattr; - newattr.c_lflag &= ~( ICANON | ECHO ); - tcsetattr( STDIN_FILENO, TCSANOW, &newattr ); - ch = getchar(); - tcsetattr( STDIN_FILENO, TCSANOW, &oldattr ); - return ch; -} - - -void portInput(uint32_t ramAddr, uint8_t length) { - uint8_t i; - char c; - - for (i=0; i 0) { - printf("\b \b"); - i -=2; - } - } else { - ZPOKE(ramAddr + i, c); - // Enter/Return. - if ((c == 13) || (c == 10)) { - printf("\n"); - break; - } - printf("%c", c); - } - fflush(stdout); - } -} - - 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. @@ -173,21 +167,9 @@ uint16_t portRandomGet(int16_t range) { void portStoryLoad(void) { - // For now, we just copy an embedded Zork 1 into RAM. memcpy(_RAM, zork1, zork1_len); //memcpy(_RAM, czech_z3, czech_z3_len); - - // 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. - - // Currently no status bar. - ZPOKE(STORY_FLAG_V3, ZPEEK(STORY_FLAG_V3) | STORY_FLAG_V3_STATUS_LINE_NOT_AVAILABLE); - - // Uncomment for status bar and window splitting. - //POKE(STORY_FLAG_V3, PEEK(STORY_FLAG_V3) & ~STORY_FLAG_V3_STATUS_LINE_NOT_AVAILABLE); - //POKE(STORY_FLAG_V3, PEEK(STORY_FLAG_V3) | STORY_FLAG_V3_SCREEN_SPLITTING); } @@ -204,11 +186,17 @@ void portWordSet(uint32_t address, uint16_t value) { int main(void) { + initscr(); + cbreak(); + noecho(); + stateReset(); portStoryLoad(); opcodesSetup(); - storySetup(); + storySetup(COLS - 1, LINES - 1); interpreterRun(); + endwin(); + return 0; } diff --git a/src/text.c b/src/text.c index 252f4c0..663bbac 100644 --- a/src/text.c +++ b/src/text.c @@ -31,14 +31,13 @@ void textCharPrint(char c) { static char lineBuffer[256]; static uint16_t lineWidth = 0; byte width = ZPEEK(STORY_SCREEN_WIDTH_CHARACTERS); - byte height = ZPEEK(STORY_SCREEN_HEIGHT_LINES); byte space; byte i; byte x; byte y; // Do we need to word wrap? - if (lineWidth == width - 1) { + if (lineWidth == width - 2) { // Are we printing a space or CR? If so, just go to next line. if ((c == ' ') || (c == '\n')) { c = '\n'; @@ -50,8 +49,7 @@ void textCharPrint(char c) { // Did we find a space? if (space == 0) { // No. Just wrap. - portCharPrint('\n'); - lineWidth = 0; + c = '\n'; } else { // Move to after space. space++; @@ -71,8 +69,7 @@ void textCharPrint(char c) { lineBuffer[x++] = lineBuffer[i]; portCharPrint(lineBuffer[i]); } - // Wrap buffer. - lineWidth = 0; + lineWidth = x; } } } diff --git a/src/window.c b/src/window.c new file mode 100644 index 0000000..21a7554 --- /dev/null +++ b/src/window.c @@ -0,0 +1,39 @@ +/* + * 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 "window.h" + + +void windowSet(uint16_t oldWindow, uint16_t newWindow) { + +} + + +void windowSplit(uint16_t oldLines, uint16_t newLines) { + +} + + +void windowUpdateStatusBar(void) { + //***TODO*** +}