muddle/src/zscii.c

172 lines
3.9 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.
*/
#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;
}