/* * 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 "zscii.h" #include "portme.h" #include "messages.h" #include "story.h" #include "state.h" #include "memory.h" #include "text.h" char zsciiDecodeChar(uint16_t z) { char c = 0; //***TODO*** V6+ has more codes. if ((z >= 32) && (z <= 126)) { c = z; // ASCII } else if (z == 13) { c = '\n'; } else if ((z >= 155) && (z <= 251)) { c = '?'; //***TODO*** Extended ZSCII } return c; } uint32_t zsciiPrint(uint32_t zstr, bool abbr) { uint16_t code = 0; uint8_t alphabet = 0; uint8_t useAbbrTable = 0; uint8_t zsciiCollector = 0; uint16_t zsciiCode = 0; uint32_t decodedChars = 0; uint32_t pc = zstr; char printVal = 0; uint32_t index; uint32_t ptr; uint16_t abbrAddr; uint32_t abbrDecodedChars; int8_t i; uint8_t ch; int16_t newShift; do { code = ZPEEKW(pc); pc += 2; // Characters are 5 bits each, packed three to a 16-bit word. for (i=10; i>=0; i-=5) { newShift = 0; printVal = 0; ch = ((code >> i) & 0x1f); if (zsciiCollector) { if (zsciiCollector == 2) { zsciiCode |= ((uint16_t)ch) << 5; } else { zsciiCode |= ((uint16_t)ch); } zsciiCollector--; if (!zsciiCollector) { printVal = zsciiDecodeChar(zsciiCode); if (printVal) { decodedChars++; textCharPrint(printVal); } alphabet = 0; useAbbrTable = 0; zsciiCode = 0; } // !zsciiCollector continue; } else if (useAbbrTable) { // zsciiCollector if (abbr) portDie(MSG_INT_NO_ABBR); index = ((32 * (((uint32_t)useAbbrTable) - 1)) + (uint32_t)ch); ptr = storyAbbreviationTableAddress() + (index * 2); abbrAddr = ZPEEKW(ptr); ptr += 2; decodedChars += zsciiPrint(abbrAddr * 2, true); useAbbrTable = 0; alphabet = 0; //***FIXME*** Version 3+ has no shift-lock but V1 needs it. continue; } // useAbbrTable switch (ch) { case 0: printVal = ' '; break; case 1: if (storyVersion() == 1) { printVal = '\n'; } else { useAbbrTable = 1; } break; case 2: case 3: if (storyVersion() <= 2) { portDie(MSG_INT_V12_SHIFT); } else { useAbbrTable = ch; } break; case 4: case 5: if (storyVersion() <= 2) { portDie(MSG_INT_V12_SHIFT_LOCK); } else { newShift = 1; alphabet = ch - 3; } break; default: if ((ch == 6) && (alphabet == 2)) { zsciiCollector = 2; } else { printVal = __state.alphabetTable[(alphabet * 26) + (ch - 6)]; } break; } if (printVal) { decodedChars++; textCharPrint(printVal); } if (alphabet && !newShift) alphabet = 0; } // for // There is no NULL terminator, you look for a word with the top bit set. } while ((code & (1 << 15)) == 0); return pc - zstr; }