Simulate Thunderclock in Slot 4

See the new sample for details/example.

Note that reading 12/24-hour time formats that have colons will not
currently work, as the colons will terminate input and result in
invisible ?EXTRA IGNORED errors.

Fixes #44
This commit is contained in:
Joshua Bell 2024-02-10 18:22:08 -08:00
parent f18ad47d1c
commit 555f8de2ae
6 changed files with 154 additions and 15 deletions

View File

@ -44,7 +44,7 @@ CodeMirror.defineMode('basic', function(config, parserConfig) {
"HPLOT": STATEMENT,
"HTAB": STATEMENT,
"IF": STATEMENT,
"IN#": UNSUPPORTED,
"IN#": STATEMENT,
"INPUT": STATEMENT,
"INT": FUNCTION,
"INVERSE": STATEMENT,

134
dos.js
View File

@ -39,6 +39,11 @@ function DOS(tty) {
tty_readChar,
tty_writeChar,
// Hooked I/O routines
hooked_readLine,
hooked_readChar,
hooked_writeChar,
// character output state
commandBuffer = "",
commandMode = false,
@ -358,8 +363,25 @@ function DOS(tty) {
args = parseArgs(m[2]);
if (slot === 0) {
if (tty.setFirmwareActive) { tty.setFirmwareActive(false); }
hooked_writeChar = tty_writeChar;
} else if (slot === 3) {
if (tty.setFirmwareActive) { tty.setFirmwareActive(true); }
hooked_writeChar = tty_writeChar;
} else if (slot === 4) {
hooked_writeChar = clock_writeChar;
} else {
doserror(DOSErrors.RANGE_ERROR);
}
} else if ((m = command.match(/^IN#\s*([\x20-\x2B\x2D-\x7E]+)(,[\x20-\x7E]*)?/))) {
// IN# slot Direct input to slot
slot = Number(m[1]);
args = parseArgs(m[2]);
if (slot === 0 || slot === 3) {
hooked_readLine = tty_readLine;
hooked_readChar = tty_readChar;
} else if (slot === 4) {
hooked_readLine = clock_readLine;
hooked_readChar = clock_readChar;
} else {
doserror(DOSErrors.RANGE_ERROR);
}
@ -381,7 +403,15 @@ function DOS(tty) {
tty_readChar = tty.readChar;
tty_writeChar = tty.writeChar;
tty.readLine = function dos_readLine(callback, prompt) {
hooked_readLine = tty_readLine;
hooked_readChar = tty_readChar;
hooked_writeChar = tty_writeChar;
tty.readLine = dos_readLine;
tty.readChar = dos_readChar;
tty.writeChar = dos_writeChar;
function dos_readLine(callback, prompt) {
var string = "", c, data, len, fp, buffer;
if (mode === "r") {
@ -415,12 +445,11 @@ function DOS(tty) {
// Non-blocking return
setTimeout(function() { callback(string); }, 0);
} else {
tty_readLine(callback, prompt);
hooked_readLine(callback, prompt);
}
}
};
tty.readChar = function dos_readChar(callback) {
function dos_readChar(callback) {
var character = "";
if (mode === "r") {
@ -432,17 +461,17 @@ function DOS(tty) {
activebuffer.filepointer += 1;
if (monico & MON_I && tty) {
tty_writeChar(character);
hooked_writeChar(character);
}
// Non-blocking return
setTimeout(function() { callback(character); }, 0);
} else {
tty_readChar(callback);
hooked_readChar(callback);
}
};
}
tty.writeChar = function dos_writeChar(c) {
function dos_writeChar(c) {
if (commandMode) {
if (c === "\r") {
@ -463,7 +492,7 @@ function DOS(tty) {
var buf, d;
if (monico & MON_O) {
tty_writeChar(c);
hooked_writeChar(c);
}
buf = activebuffer;
@ -484,8 +513,89 @@ function DOS(tty) {
buf.filepointer += 1;
} else {
tty_writeChar(c);
hooked_writeChar(c);
}
}
//----------------------------------------------------------------------
// Clock routine - vaguely ThunderClock compatible
//----------------------------------------------------------------------
var clockbuf = '';
function clock_writeChar(c) {
var DAYS = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'];
var MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN',
'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'];
function spad2(s) {
return ('00' + String(s)).slice(-2);
}
function zpad2(s) {
return ('00' + String(s)).slice(-2);
}
function zpad3(s) {
return ('000' + String(s)).slice(-3);
}
}; // writeChar
var now = new Date();
switch (c) {
default:
case '%': // AM/PM ASCII string mode - e.g. "TUE MAY 12 4:32:55 PM"
case '>': // AM/PM ASCII string mode - e.g. "TUE MAY 12 4:32:55 PM"
clockbuf =
DAYS[now.getDay()] + ' ' +
MONTHS[now.getMonth()] + ' ' +
now.getDate() + ' ' +
spad2((now.getHours() === 0 ? 12 : now.getHours() > 12 ? now.getHours() - 12 : now.getHours())) + ':' +
zpad2(now.getMinutes()) + ':' +
zpad2(now.getSeconds()) + ' ' +
(now.getHours() < 12 ? 'AM' : 'PM');
break;
case '&': // 24 hour ASCII string - e.g. "TUE MAY 12 16:32:55"
case '<': // 24 hour ASCII string - e.g. "TUE MAY 12 16:32:55"
clockbuf =
DAYS[now.getDay()] + ' ' +
MONTHS[now.getMonth()] + ' ' +
now.getDate() + ' ' +
spad2(now.getHours()) + ':' +
zpad2(now.getMinutes()) + ':' +
zpad2(now.getSeconds()) + ' ' +
(now.getHours() < 12 ? 'AM' : 'PM');
break;
case ' ': // Mountain Computer Apple Clock Format - e.g. "05/12 16;32;55.000"
clockbuf =
zpad2(now.getMonth()+1) + '/' +
zpad2(now.getDate()) + ' ' +
zpad2(now.getHours()) + ';' +
zpad2(now.getMinutes()) + ';' +
zpad2(now.getSeconds()) + '.' +
zpad3(now.getMilliseconds());
break;
case '#': // Numeric format, e.g. MO,DW,DT,HR,MN,SEC
clockbuf = [
now.getMonth()+1,
now.getDay(),
now.getDate(),
now.getHours(),
now.getMinutes(),
now.getSeconds()
].join(',');
break;
}
clockbuf += '\r';
}
function clock_readLine(callback, prompt) {
tty.writeString(prompt); // TODO: Correct? Newline?
var tmp = clockbuf;
clockbuf = '';
callback(tmp);
}
function clock_readChar(callback) {
if (!clockbuf.length) {
callback('\r');
} else {
var c = clockbuf.substring(0, 1);
clockbuf = clockbuf.slice(1);
callback(c);
}
}
}

View File

@ -169,7 +169,6 @@ can be literals (unquoted strings), strings, or numbers
<h3>Native Platform Interaction <var>- NOT IMPLEMENTED</var></h3>
<dl>
<dt>HIMEM: <var>aexpr</var><dd>Set upper address of variable memory
<dt>IN# <var>aexpr</var><dd>Direct input from slot
<dt>LOMEM: <var>aexpr</var><dd>Set lower address of variable memory
<dt>WAIT <var>aexpr</var>, <var>aexpr</var> [, <var>aexpr</var>]<dd>Wait until memory location masked by second argument equals third argument (or zero)
</dl>
@ -234,6 +233,13 @@ can be literals (unquoted strings), strings, or numbers
<ul>
<li><code>PR#0</code> &mdash; set 40 column mode
<li><code>PR#3</code> &mdash; set 80 column mode
<li><code>PR#4</code> &mdash; write to Thunderclock
</ul>
<dt>IN# <var>aexpr</var><dd>Direct output to slot
<ul>
<li><code>IN#0</code> &mdash; read input from keyboard or DOS
<li><code>IN#4</code> &mdash; read from Thunderclock
</ul>
</dl>

View File

@ -93,6 +93,7 @@ sample.onelinetrain One Liner Train (Chris ten Den)
sample.piglatin Pig Latin Translator (Gregg Buntin)
sample.nuclear Nuclear Power Plant (Stephen R. Berggren c/o Kevin Riggle)
sample.factors Prime Factors (Cristiano Trabuio)
sample.thunderclock Thunderclock (incomplete)
# ____________________________________________
# Traveller RPG Utilities

View File

@ -0,0 +1,21 @@
10 PRINT "Mountain Clock Format"
20 PRINT CHR$(4);"PR#4"
30 PRINT CHR$(4);"IN#4"
40 INPUT " ";T$
50 PRINT CHR$(4);"PR#0"
60 PRINT CHR$(4);"IN#0"
70 PRINT T$
80 PRINT
100 PRINT "Numeric Format"
110 PRINT CHR$(4);"PR#4"
120 PRINT CHR$(4);"IN#4"
130 INPUT "#";MO,DW,DT,HR,MN,SEC
140 PRINT CHR$(4);"PR#0"
150 PRINT CHR$(4);"IN#0"
160 PRINT "Month: ";MO
161 PRINT "Day: ";DW
162 PRINT "Date: ";DT
163 PRINT "Hours: ";HR
164 PRINT "Minutes: ";MN
165 PRINT "Seconds: ";SEC

3
tty.js
View File

@ -236,7 +236,8 @@ function TTY(screenElement, keyboardElement) {
};
this.setFirmwareActive = function setFirmwareActive(active) {
init(active, 24, active ? 80 : 40);
if (active !== firmwareActive)
init(active, 24, active ? 80 : 40);
};
this.isFirmwareActive = function isFirmwareActive() {