267 lines
6.9 KiB
C++
267 lines
6.9 KiB
C++
// =============================================================
|
|
// crc's _ _
|
|
// (_) | ___
|
|
// | | |/ _ \ a tiny virtual computer
|
|
// | | | (_) | 64kw RAM, 32-bit, Dual Stack, MISC
|
|
// |_|_|\___/ ilo-teensy41.ino (c) charles childers
|
|
// ============================================================
|
|
// This is an ilo for the Teensy 4.1. It derives from the
|
|
// ilo-banked.c, as with the DOS & Mac System 5/6/7 ports.
|
|
//
|
|
// Requirements:
|
|
//
|
|
// - Teensy 4.1
|
|
// - FAT formatted SD card containing these files:
|
|
// - ilo.rom (the system image)
|
|
// - ilo.blo (the data blocks)
|
|
// - A serial terminal interface
|
|
//
|
|
// ============================================================
|
|
|
|
#include <SD.h>
|
|
#include <TimeLib.h>
|
|
|
|
#define VERBOSE
|
|
|
|
#define SLICES 64
|
|
#define SLICESIZE (65536/SLICES)
|
|
|
|
int ip, sp, rp, data[33], address[257];
|
|
char *blocks, *rom;
|
|
extern int image[];
|
|
|
|
int *memory[SLICES];
|
|
|
|
#define TOS data[sp]
|
|
#define NOS data[sp-1]
|
|
#define TORS address[rp]
|
|
|
|
#define I int
|
|
#define V void
|
|
|
|
I pop() { sp--; return data[sp + 1]; }
|
|
V push(int value) { sp++; data[sp] = value; }
|
|
|
|
V store(int a, int v) {
|
|
int slice = a / SLICESIZE;
|
|
int actual = a - (slice * SLICESIZE);
|
|
memory[slice][actual] = v;
|
|
}
|
|
|
|
int fetch(int a) {
|
|
int slice = a / SLICESIZE;
|
|
int actual = a - (slice * SLICESIZE);
|
|
return memory[slice][actual];
|
|
}
|
|
|
|
V xlog(const char *s, int eol) {
|
|
#ifdef VERBOSE
|
|
if (eol)
|
|
Serial.println(s);
|
|
else
|
|
Serial.print(s);
|
|
#endif
|
|
}
|
|
|
|
V load_image() {
|
|
File rom = SD.open("ilo.rom", FILE_READ);
|
|
int data;
|
|
xlog("ilo: load rom", 1);
|
|
for (int x = 0; x < 65536; x++) {
|
|
data = 0;
|
|
rom.read(&data, 4);
|
|
store(x, data);
|
|
if ((x % 1024) == 0) xlog(".", 0);
|
|
}
|
|
rom.close();
|
|
delay(10);
|
|
xlog("\nilo: rom loaded", 1);
|
|
}
|
|
|
|
V prepare_vm() {
|
|
for (ip = 0; ip < 32; ip++) data[ip] = 0;
|
|
for (ip = 0; ip < 128; ip++) address[ip] = 0;
|
|
ip = sp = rp = 0;
|
|
}
|
|
|
|
V read_block() {
|
|
File blocks = SD.open("ilo.blo", FILE_READ);
|
|
int data;
|
|
int buf = pop();
|
|
int blk = pop();
|
|
blocks.seek(4096 * blk);
|
|
for (int x = 0; x < 1024; x++) {
|
|
data = 0;
|
|
blocks.read(&data, 4);
|
|
store(buf + x, data);
|
|
}
|
|
blocks.close();
|
|
delay(10);
|
|
}
|
|
|
|
V write_block() {
|
|
File blocks = SD.open("ilo.blo", FILE_WRITE);
|
|
int data;
|
|
int buf = pop();
|
|
int blk = pop();
|
|
blocks.seek(4096 * blk);
|
|
for (int x = 0; x < 1024; x++) {
|
|
data = fetch(buf + x);
|
|
blocks.write(&data, 4);
|
|
}
|
|
blocks.close();
|
|
delay(10);
|
|
}
|
|
|
|
int led = 13;
|
|
|
|
V teensy_io() {
|
|
int a, b, c, d, e, f;
|
|
switch (pop()) {
|
|
case 1: digitalWrite(pop(), HIGH);
|
|
break;
|
|
case 2: digitalWrite(pop(), LOW);
|
|
break;
|
|
case 3: a = pop();
|
|
b = pop();
|
|
pinMode(b, a);
|
|
break;
|
|
case 4: digitalRead(pop()) ? push(-1) : push(0);
|
|
break;
|
|
case 10: delay(pop());
|
|
break;
|
|
case 11: delayMicroseconds(pop());
|
|
break;
|
|
case 12: delayNanoseconds(pop());
|
|
break;
|
|
case 20: push(hour());
|
|
break;
|
|
case 21: push(minute());
|
|
break;
|
|
case 22: push(second());
|
|
break;
|
|
case 23: push(day());
|
|
break;
|
|
case 24: push(month());
|
|
break;
|
|
case 25: push(year());
|
|
break;
|
|
}
|
|
}
|
|
|
|
V process(int o) {
|
|
int a, b, target, flag;
|
|
int src, dest, len, kkk;
|
|
char outstr[2] = {'a', 0};
|
|
switch (o) {
|
|
case 0: break;
|
|
case 1: ip++; push(fetch(ip)); break;
|
|
case 2: push(TOS); break;
|
|
case 3: data[sp] = 0; sp--; break;
|
|
case 4: a = TOS; TOS = NOS; NOS = a; break;
|
|
case 5: rp++; TORS = pop(); break;
|
|
case 6: push(TORS); rp--; break;
|
|
case 7: ip = pop() - 1; break;
|
|
case 8: rp++; TORS = ip; ip = pop() - 1; break;
|
|
case 9: target = pop(); flag = pop();
|
|
if (flag != 0) { rp++; TORS = ip; ip = target - 1; }
|
|
break;
|
|
case 10: target = pop(); flag = pop();
|
|
if (flag != 0) ip = target - 1; break;
|
|
case 11: ip = TORS; rp--; break;
|
|
case 12: NOS = (NOS == TOS) ? -1 : 0; sp--; break;
|
|
case 13: NOS = (NOS != TOS) ? -1 : 0; sp--; break;
|
|
case 14: NOS = (NOS < TOS) ? -1 : 0; sp--; break;
|
|
case 15: NOS = (NOS > TOS) ? -1 : 0; sp--; break;
|
|
case 16: TOS = fetch(TOS); break;
|
|
case 17: store(TOS, NOS); sp--; sp--; break;
|
|
case 18: NOS += TOS; sp--; break;
|
|
case 19: NOS -= TOS; sp--; break;
|
|
case 20: NOS *= TOS; sp--; break;
|
|
case 21: a = TOS; b = NOS; TOS = b / a; NOS = b % a; break;
|
|
case 22: NOS = TOS & NOS; sp--; break;
|
|
case 23: NOS = TOS | NOS; sp--; break;
|
|
case 24: NOS = TOS ^ NOS; sp--; break;
|
|
case 25: NOS = NOS << TOS; sp--; break;
|
|
case 26: NOS = NOS >> TOS; sp--; break;
|
|
case 27: len = pop(); dest = pop();
|
|
src = pop(); flag = -1;
|
|
while (len) {
|
|
if (fetch(dest) != fetch(src)) flag = 0;
|
|
len -= 1; src += 1; dest += 1;
|
|
}; push(flag); break;
|
|
case 28: len = pop(); dest = pop();
|
|
src = pop();
|
|
while (len) {
|
|
store(dest, fetch(src));
|
|
len -= 1; src += 1; dest += 1;
|
|
}; break;
|
|
case 29: switch (pop()) {
|
|
case 0: outstr[0] = pop(); Serial.print(outstr);
|
|
if (outstr[0] == 10) { outstr[0] = 13; Serial.print(outstr); } break;
|
|
case 1: kkk = 0; while (kkk <= 0) { kkk = Serial.read(); }
|
|
outstr[0] = kkk; Serial.print(outstr);
|
|
if (kkk == 13) { kkk = 10; }
|
|
push(kkk); break;
|
|
case 2: read_block(); break;
|
|
case 3: write_block(); break;
|
|
case 4: break;
|
|
case 5: ip = -1; break;
|
|
case 6: ip = 65536; break;
|
|
case 7: push(sp); push(rp); break;
|
|
case 100: teensy_io(); break;
|
|
default: break;
|
|
} break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
V execute() {
|
|
int opcode, o;
|
|
ip = 0;
|
|
while (ip < 65536) {
|
|
opcode = fetch(ip);
|
|
o = opcode & 0xFF; if (o) process(o);
|
|
o = (opcode >> 8) & 0xFF; if (o) process(o);
|
|
o = (opcode >> 16) & 0xFF; if (o) process(o);
|
|
o = (opcode >> 24) & 0xFF; if (o) process(o);
|
|
ip++;
|
|
}
|
|
}
|
|
|
|
// the setup routine runs once when you press reset:
|
|
V setup() {
|
|
Serial.begin(9600);
|
|
xlog("ilo: prepare memory", 1);
|
|
int i;
|
|
for (i = 0; i < SLICES; i++)
|
|
memory[i] = (int *)malloc(SLICESIZE * sizeof(int));
|
|
|
|
xlog("ilo: prepare i/o pins", 1);
|
|
pinMode(led, OUTPUT);
|
|
|
|
xlog("ilo: check for SD card", 1);
|
|
if (!SD.begin(BUILTIN_SDCARD)) {
|
|
xlog("!!!! card failed, or not present; check SD card and restart", 1);
|
|
while (1);
|
|
}
|
|
|
|
xlog("ilo: check for ilo.rom", 1);
|
|
if (!SD.exists("ilo.rom")) {
|
|
xlog("!!!! missing ilo.rom; check SD card and restart", 1);
|
|
while (1);
|
|
}
|
|
|
|
xlog("ilo: check for ilo.blo", 1);
|
|
if (!SD.exists("ilo.blo")) {
|
|
xlog("!!!! missing ilo.blo; check SD card and restart", 1);
|
|
while (1);
|
|
}
|
|
}
|
|
|
|
// the loop routine runs over and over again forever:
|
|
V loop() {
|
|
prepare_vm();
|
|
load_image();
|
|
execute();
|
|
}
|