diff --git a/CMakeLists.txt b/CMakeLists.txt index df63640..e29070d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ set(HEADERS state.h stddclmr.h story.h + text.h variable.h zscii.h # czech.z3.h @@ -50,6 +51,7 @@ set(SOURCE portme.c state.c story.c + text.c variable.c zscii.c ) @@ -58,6 +60,8 @@ 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) diff --git a/include/common.h b/include/common.h index 0fe1a8a..46782cf 100644 --- a/include/common.h +++ b/include/common.h @@ -28,7 +28,9 @@ #include -#define DEBUGGING +//#define DEBUGGING + +#define STACK_SIZE 2048 //***TODO*** How big does this really need to be? Old games are 1024, new...? #ifdef DEBUGGING diff --git a/include/portme.h b/include/portme.h index e797acb..c1893b5 100644 --- a/include/portme.h +++ b/include/portme.h @@ -28,17 +28,19 @@ #include "common.h" -uint8_t portByteGet(uint16_t address); -void portByteSet(uint16_t address, uint8_t value); +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 portCharSetPos(byte x, byte y); void portDie(char *fmt, ...); bool portFileRestore(void); bool portFileSave(void); -void portInput(uint32_t ramAddr, uint8_t length); uint16_t portRandomGet(int16_t range); void portStoryLoad(void); -uint16_t portWordGet(uint16_t address); -void portWordSet(uint16_t address, uint16_t value); +uint16_t portWordGet(uint32_t address); +void portWordSet(uint32_t address, uint16_t value); #endif // PORTME_H diff --git a/include/state.h b/include/state.h index 449ef0d..3098acf 100644 --- a/include/state.h +++ b/include/state.h @@ -30,7 +30,7 @@ typedef struct stateS { - uint16_t stack[2048]; //***TODO*** How big does this really need to be? Old games are 1024, new...? + uint16_t stack[STACK_SIZE]; bool quit; uint32_t pc; // Program Counter uint16_t sp; // Stack Pointer diff --git a/include/story.h b/include/story.h index f1d4e12..4caa2c3 100644 --- a/include/story.h +++ b/include/story.h @@ -117,8 +117,8 @@ #define storyHXTrueDefaultBackgroundColor() portWordGet(storyHeaderExtensionTableAddress() + 12) -uint32_t storyLength(); -void storySetup(void); +uint32_t storyLength(void); +void storySetup(byte width, byte height); #endif // STORY_H diff --git a/include/text.h b/include/text.h new file mode 100644 index 0000000..a432478 --- /dev/null +++ b/include/text.h @@ -0,0 +1,35 @@ +/* + * 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 TEXT_H +#define TEXT_H + + +#include "common.h" + + +void textCharPrint(char c); +void textInput(uint32_t ramAddr, uint8_t length); + + +#endif // TEXT_H diff --git a/ports/f256/CMakeLists.txt b/ports/f256/CMakeLists.txt index 16d9759..20aed0a 100644 --- a/ports/f256/CMakeLists.txt +++ b/ports/f256/CMakeLists.txt @@ -24,6 +24,7 @@ set(HEADERS state.h stddclmr.h story.h + text.h variable.h zscii.h # czech.z3.h @@ -49,6 +50,7 @@ set(SOURCE opcodes.c state.c story.c + text.c variable.c zscii.c ) diff --git a/ports/f256/f256zip.c b/ports/f256/f256zip.c index c534cf2..74459e3 100644 --- a/ports/f256/f256zip.c +++ b/ports/f256/f256zip.c @@ -47,16 +47,27 @@ #define BASE_ADDRESS 0x10000 -uint8_t portByteGet(uint16_t address) { +uint8_t portByteGet(uint32_t address) { return FAR_PEEK(BASE_ADDRESS + address); } -void portByteSet(uint16_t address, uint8_t value) { +void portByteSet(uint32_t address, uint8_t value) { FAR_POKE(BASE_ADDRESS + address, value); } +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; @@ -64,6 +75,12 @@ void portCharPrint(char c) { } +void portCharSetPos(byte x, byte y) { + // ZIP uses 1-based coordinates. + textGotoXY(x - 1, y - 1); +} + + void portDie(char *fmt, ...) { #ifdef DEBUGGING printf("%s\n", fmt); // Yeah, this isn't right. @@ -135,39 +152,8 @@ bool portFileSave(void) { } -void portInput(uint32_t ramAddr, uint8_t length) { - uint8_t i; - char c[2] = { 0, 0 }; - byte x; - byte y; - - for (i=0; i 0) { - textGetXY(&x, &y); - if (x > 0) { - x--; - } else { - x = 0; - y--; - } - textGotoXY(x, y); - textPrint(" "); - textGotoXY(x, y); - i--; - } - } else { - ZPOKE(ramAddr + i, c[0]); - // Enter/Return. - if ((c[0] == 13) || (c[0] == 10)) { - textPrint("\n"); - break; - } - textPrint(c); - } - } +char portCharGet(void) { + return getchar(); } @@ -188,28 +174,20 @@ uint16_t portRandomGet(int16_t range) { void portStoryLoad(void) { + // On the F256, the story is loaded into RAM along with the ZIP. + // 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. - - ZPOKE(STORY_SCREEN_WIDTH_CHARACTERS, 80); - ZPOKE(STORY_SCREEN_HEIGHT_LINES, 30); - - // 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); } -uint16_t portWordGet(uint16_t address) { +uint16_t portWordGet(uint32_t address) { return SWAP_UINT16(FAR_PEEKW(BASE_ADDRESS + address)); } -void portWordSet(uint16_t address, uint16_t value) { +void portWordSet(uint32_t address, uint16_t value) { FAR_POKEW(BASE_ADDRESS + address, SWAP_UINT16(value)); } @@ -223,7 +201,7 @@ int main(void) { stateReset(); portStoryLoad(); opcodesSetup(); - storySetup(); + storySetup(80, 30); interpreterRun(); return 0; diff --git a/ports/zip.c b/ports/zip.c index 4ce399b..62df6fe 100644 --- a/ports/zip.c +++ b/ports/zip.c @@ -39,5 +39,6 @@ #include "../src/opcodes.c" #include "../src/state.c" #include "../src/story.c" +#include "../src/text.c" #include "../src/variable.c" #include "../src/zscii.c" diff --git a/src/oc_input.c b/src/oc_input.c index 345df83..06b1973 100644 --- a/src/oc_input.c +++ b/src/oc_input.c @@ -27,6 +27,7 @@ #include "memory.h" #include "messages.h" #include "interpreter.h" +#include "text.h" void opcodes_read(void) { @@ -45,7 +46,7 @@ void opcodes_read(void) { interpreterUpdateStatusBar(); // Read input from user. - portInput(input, inputLen); + textInput(input, inputLen); #ifdef DEBUGGING printf("Input: ["); diff --git a/src/oc_output.c b/src/oc_output.c index 541e94a..08cc335 100644 --- a/src/oc_output.c +++ b/src/oc_output.c @@ -30,10 +30,11 @@ #include "object.h" #include "messages.h" #include "interpreter.h" +#include "text.h" void opcodes_new_line(void) { - portCharPrint('\n'); + textCharPrint('\n'); } @@ -49,7 +50,7 @@ void opcodes_print_addr(void) { void opcodes_print_char(void) { char c = zsciiDecodeChar(__state.operands[0]); - if (c) portCharPrint(c); + if (c) textCharPrint(c); } @@ -59,7 +60,7 @@ void opcodes_print_num_r(int32_t val) { opcodes_print_num_r(val / 10); } - portCharPrint('0' + (val % 10)); + textCharPrint('0' + (val % 10)); } @@ -67,7 +68,7 @@ void opcodes_print_num(void) { int32_t n = (int16_t)__state.operands[0]; if (n < 0) { - portCharPrint('-'); + textCharPrint('-'); n = -n; } opcodes_print_num_r(n); @@ -97,6 +98,6 @@ void opcodes_print_paddr(void) { void opcodes_print_ret(void) { __state.pc += zsciiPrint(__state.pc, 0); - portCharPrint('\n'); + textCharPrint('\n'); interpreterDoReturn(1); } diff --git a/src/oc_save.c b/src/oc_save.c index f82e068..e95ccb8 100644 --- a/src/oc_save.c +++ b/src/oc_save.c @@ -27,6 +27,7 @@ #include "portme.h" #include "story.h" #include "opcodes.h" +#include "memory.h" void opcodes_save(void) { @@ -35,10 +36,13 @@ void opcodes_save(void) { void opcodes_restart(void) { + byte width = ZPEEK(STORY_SCREEN_WIDTH_CHARACTERS); + byte height = ZPEEK(STORY_SCREEN_HEIGHT_LINES); + stateReset(); portStoryLoad(); opcodesSetup(); - storySetup(); + storySetup(width, height); } diff --git a/src/portme.c b/src/portme.c index 543e4c8..7300ab9 100644 --- a/src/portme.c +++ b/src/portme.c @@ -42,12 +42,12 @@ static uint8_t _RAM[1024 * (512 - 64)]; -uint8_t portByteGet(uint16_t address) { +uint8_t portByteGet(uint32_t address) { return _RAM[address]; } -void portByteSet(uint16_t address, uint8_t value) { +void portByteSet(uint32_t address, uint8_t value) { _RAM[address] = value; } @@ -148,7 +148,7 @@ void portInput(uint32_t ramAddr, uint8_t length) { if ((c == 8) || (c == 127)) { if (i > 0) { printf("\b \b"); - i--; + i -=2; } } else { ZPOKE(ramAddr + i, c); @@ -191,12 +191,12 @@ void portStoryLoad(void) { } -uint16_t portWordGet(uint16_t address) { +uint16_t portWordGet(uint32_t address) { return ((uint16_t)_RAM[address] << 8) | ((uint16_t)_RAM[address + 1]); } -void portWordSet(uint16_t address, uint16_t value) { +void portWordSet(uint32_t address, uint16_t value) { _RAM[address] = (value >> 8) & 0xff; // MSB first. _RAM[address + 1] = value & 0xff; } diff --git a/src/story.c b/src/story.c index aa4e70d..4da79ba 100644 --- a/src/story.c +++ b/src/story.c @@ -23,6 +23,7 @@ #include "story.h" #include "state.h" +#include "memory.h" uint32_t storyLength() { @@ -40,10 +41,20 @@ uint32_t storyLength() { } -void storySetup(void) { +void storySetup(byte width, byte height) { char *p; uint16_t i; + ZPOKE(STORY_SCREEN_WIDTH_CHARACTERS, width); + ZPOKE(STORY_SCREEN_HEIGHT_LINES, height); + + // 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); + // V6+ this is the address of main(), not just a raw starting address. __state.pc = storyInitialPC(); __state.sp = 0; diff --git a/src/text.c b/src/text.c new file mode 100644 index 0000000..252f4c0 --- /dev/null +++ b/src/text.c @@ -0,0 +1,126 @@ +/* + * 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 "text.h" +#include "portme.h" +#include "memory.h" +#include "story.h" + + +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) { + // Are we printing a space or CR? If so, just go to next line. + if ((c == ' ') || (c == '\n')) { + c = '\n'; + } else { + // Scan the line backwards for the first space. + for (space=lineWidth-1; space>0; space--) { + if (lineBuffer[space] == ' ') break; + } + // Did we find a space? + if (space == 0) { + // No. Just wrap. + portCharPrint('\n'); + lineWidth = 0; + } else { + // Move to after space. + space++; + // Erase text to move to next line from this line. + portCharGetPos(&x, &y); + portCharSetPos(space, y); + for (i=space; i<=lineWidth; i++) { + portCharPrint(' '); + } + // Wrap display. + portCharPrint('\n'); + // Move erased characters to front of buffer & redisplay. + portCharGetPos(&x, &y); + portCharSetPos(1, y); + x = 0; + for (i=space; i<=lineWidth; i++) { + lineBuffer[x++] = lineBuffer[i]; + portCharPrint(lineBuffer[i]); + } + // Wrap buffer. + lineWidth = 0; + } + } + } + + // Display it. + portCharPrint(c); + + // Update buffer. + if (c == '\n') { + // New line. + lineWidth = 0; + } else { + lineBuffer[lineWidth++] = c; + } +} + + +void textInput(uint32_t ramAddr, uint8_t length) { + uint8_t i; + char c; + byte x; + byte y; + + for (i=0; i 0) { + portCharGetPos(&x, &y); + if (x > 1) { + x--; + } else { + x = 1; + y--; + } + portCharSetPos(x, y); + portCharPrint(' '); + portCharSetPos(x, y); + i -= 2; + } + } else { + ZPOKE(ramAddr + i, c); + // Enter/Return. + if ((c == 13) || (c == 10)) { + portCharPrint('\n'); + break; + } + portCharPrint(c); + } + } +} diff --git a/src/zscii.c b/src/zscii.c index e3cc9d2..97f0de1 100644 --- a/src/zscii.c +++ b/src/zscii.c @@ -27,6 +27,7 @@ #include "story.h" #include "state.h" #include "memory.h" +#include "text.h" char zsciiDecodeChar(uint16_t z) { @@ -87,7 +88,7 @@ uint32_t zsciiPrint(uint32_t zstr, bool abbr) { printVal = zsciiDecodeChar(zsciiCode); if (printVal) { decodedChars++; - portCharPrint(printVal); + textCharPrint(printVal); } alphabet = 0; useAbbrTable = 0; @@ -157,7 +158,7 @@ uint32_t zsciiPrint(uint32_t zstr, bool abbr) { if (printVal) { decodedChars++; - portCharPrint(printVal); + textCharPrint(printVal); } if (alphabet && !newShift) alphabet = 0;