retroforth/vm/nga-c/barebones.c
crc 0287339ff9 retro-barebones: update w/latest image, some cleanups
FossilOrigin-Name: 02f302af576d502ba19eb058deb9236a218bb8d85c0e321c6058686b1a8ce5e8
2020-11-02 13:25:54 +00:00

385 lines
7 KiB
C

/* RETRO ------------------------------------------------------
A personal, minimalistic forth
Copyright (c) 2016 - 2020 Charles Childers
This is a minimalist implementation of an interactive
RETRO system with an embedded image and the listerner loop
being written in RETRO.
Building:
cp ngaImage barebones.image
retro-extend barebones.image interface/barebones.retro
retro-embedimage barebones.image >vm/nga-c/barebones_image.c
cd vm/nga-c
make barebones
---------------------------------------------------------- */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
#define CELL int32_t
#define CELL_MIN INT_MIN + 1
#define CELL_MAX INT_MAX - 1
#define IMAGE_SIZE 242000 /* Amount of RAM. 968kB by default. */
#define ADDRESSES 256 /* Depth of address stack */
#define STACK_DEPTH 128 /* Depth of data stack */
CELL sp, rp, ip; /* Data, address, instruction pointers */
CELL data[STACK_DEPTH]; /* The data stack */
CELL address[ADDRESSES]; /* The address stack */
CELL memory[IMAGE_SIZE + 1]; /* The memory for the image */
#define TOS data[sp] /* Shortcut for top item on stack */
#define NOS data[sp-1] /* Shortcut for second item on stack */
#define TORS address[rp] /* Shortcut for top item on address stack */
#define NUM_DEVICES 2
typedef void (*Handler)(void);
Handler IO_deviceHandlers[NUM_DEVICES + 1];
Handler IO_queryHandlers[NUM_DEVICES + 1];
#include "barebones_image.c"
CELL stack_pop();
void stack_push(CELL value);
void execute(CELL cell);
CELL load_image();
void prepare_vm();
void process_opcode(CELL opcode);
void process_opcode_bundle(CELL opcode);
int validate_opcode_bundle(CELL opcode);
CELL stack_pop() {
sp--;
return data[sp + 1];
}
void stack_push(CELL value) {
sp++;
data[sp] = value;
}
void generic_output() {
putc(stack_pop(), stdout);
fflush(stdout);
}
void generic_output_query() {
stack_push(0);
stack_push(0);
}
void generic_input() {
stack_push(getc(stdin));
if (TOS == 127) TOS = 8;
}
void generic_input_query() {
stack_push(0);
stack_push(1);
}
void execute(CELL cell) {
CELL opcode;
rp = 1;
ip = cell;
while (ip < IMAGE_SIZE) {
opcode = memory[ip];
if (validate_opcode_bundle(opcode) != 0) {
process_opcode_bundle(opcode);
} else {
printf("Invalid instruction!\n");
exit(1);
}
ip++;
if (rp == 0)
ip = IMAGE_SIZE;
}
}
int main(int argc, char **argv) {
IO_deviceHandlers[0] = generic_output;
IO_deviceHandlers[1] = generic_input;
IO_queryHandlers[0] = generic_output_query;
IO_queryHandlers[1] = generic_input_query;
prepare_vm();
load_image();
execute(0);
exit(0);
}
/* Nga ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (c) 2008 - 2020, Charles Childers
Copyright (c) 2009 - 2010, Luke Parrish
Copyright (c) 2010, Marc Simpson
Copyright (c) 2010, Jay Skeer
Copyright (c) 2011, Kenneth Keating
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
#ifndef NUM_DEVICES
#define NUM_DEVICES 0
#endif
CELL load_image(char *imageFile) {
FILE *fp;
CELL imageSize;
long fileLen;
CELL i;
if ((fp = fopen(imageFile, "rb")) != NULL) {
/* Determine length (in cells) */
fseek(fp, 0, SEEK_END);
fileLen = ftell(fp) / sizeof(CELL);
rewind(fp);
/* Read the file into memory */
imageSize = fread(&memory, sizeof(CELL), fileLen, fp);
fclose(fp);
}
else {
for (i = 0; i < ngaImageCells; i++)
memory[i] = ngaImage[i];
imageSize = i;
}
return imageSize;
}
void prepare_vm() {
ip = sp = rp = 0;
for (ip = 0; ip < IMAGE_SIZE; ip++)
memory[ip] = 0; /* NO - nop instruction */
for (ip = 0; ip < STACK_DEPTH; ip++)
data[ip] = 0;
for (ip = 0; ip < ADDRESSES; ip++)
address[ip] = 0;
}
void inst_no() {
}
void inst_li() {
sp++;
ip++;
TOS = memory[ip];
}
void inst_du() {
sp++;
data[sp] = NOS;
}
void inst_dr() {
data[sp] = 0;
if (--sp < 0)
ip = IMAGE_SIZE;
}
void inst_sw() {
CELL a;
a = TOS;
TOS = NOS;
NOS = a;
}
void inst_pu() {
rp++;
TORS = TOS;
inst_dr();
}
void inst_po() {
sp++;
TOS = TORS;
rp--;
}
void inst_ju() {
ip = TOS - 1;
inst_dr();
}
void inst_ca() {
rp++;
TORS = ip;
ip = TOS - 1;
inst_dr();
}
void inst_cc() {
CELL a, b;
a = TOS; inst_dr(); /* Target */
b = TOS; inst_dr(); /* Flag */
if (b != 0) {
rp++;
TORS = ip;
ip = a - 1;
}
}
void inst_re() {
ip = TORS;
rp--;
}
void inst_eq() {
NOS = (NOS == TOS) ? -1 : 0;
inst_dr();
}
void inst_ne() {
NOS = (NOS != TOS) ? -1 : 0;
inst_dr();
}
void inst_lt() {
NOS = (NOS < TOS) ? -1 : 0;
inst_dr();
}
void inst_gt() {
NOS = (NOS > TOS) ? -1 : 0;
inst_dr();
}
void inst_fe() {
switch (TOS) {
case -1: TOS = sp - 1; break;
case -2: TOS = rp; break;
case -3: TOS = IMAGE_SIZE; break;
case -4: TOS = CELL_MIN; break;
case -5: TOS = CELL_MAX; break;
default: TOS = memory[TOS]; break;
}
}
void inst_st() {
if (TOS <= IMAGE_SIZE && TOS >= 0) {
memory[TOS] = NOS;
inst_dr();
inst_dr();
} else {
ip = IMAGE_SIZE;
}
}
void inst_ad() {
NOS += TOS;
inst_dr();
}
void inst_su() {
NOS -= TOS;
inst_dr();
}
void inst_mu() {
NOS *= TOS;
inst_dr();
}
void inst_di() {
CELL a, b;
a = TOS;
b = NOS;
TOS = b / a;
NOS = b % a;
}
void inst_an() {
NOS = TOS & NOS;
inst_dr();
}
void inst_or() {
NOS = TOS | NOS;
inst_dr();
}
void inst_xo() {
NOS = TOS ^ NOS;
inst_dr();
}
void inst_sh() {
CELL y = TOS;
CELL x = NOS;
if (TOS < 0)
NOS = NOS << (TOS * -1);
else {
if (x < 0 && y > 0)
NOS = x >> y | ~(~0U >> y);
else
NOS = x >> y;
}
inst_dr();
}
void inst_zr() {
if (TOS == 0) {
inst_dr();
ip = TORS;
rp--;
}
}
void inst_ha() {
ip = IMAGE_SIZE;
}
void inst_ie() {
sp++;
TOS = NUM_DEVICES;
}
void inst_iq() {
CELL Device = TOS;
inst_dr();
IO_queryHandlers[Device]();
}
void inst_ii() {
CELL Device = TOS;
inst_dr();
IO_deviceHandlers[Device]();
}
Handler instructions[] = {
inst_no, inst_li, inst_du, inst_dr, inst_sw, inst_pu, inst_po,
inst_ju, inst_ca, inst_cc, inst_re, inst_eq, inst_ne, inst_lt,
inst_gt, inst_fe, inst_st, inst_ad, inst_su, inst_mu, inst_di,
inst_an, inst_or, inst_xo, inst_sh, inst_zr, inst_ha, inst_ie,
inst_iq, inst_ii
};
void process_opcode(CELL opcode) {
if (opcode != 0)
instructions[opcode]();
}
int validate_opcode_bundle(CELL opcode) {
CELL raw = opcode;
CELL current;
int valid = -1;
int i;
for (i = 0; i < 4; i++) {
current = raw & 0xFF;
if (!(current >= 0 && current <= 29))
valid = 0;
raw = raw >> 8;
}
return valid;
}
void process_opcode_bundle(CELL opcode) {
CELL raw = opcode;
int i;
for (i = 0; i < 4; i++) {
process_opcode(raw & 0xFF);
raw = raw >> 8;
}
}