retroforth/vm/nga-c/nga.c
crc 153a375fe9 nga-c: on retro-unix, default to a switch() based opcode processor (compile with -DFAST to enable on others)
FossilOrigin-Name: c797dd703b545b24c20ae29b26c46dc38a253181aeebb81ef22f1617622f12df
2021-04-08 13:14:05 +00:00

331 lines
5.9 KiB
C

/* 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);
if (fileLen > IMAGE_SIZE) {
fclose(fp);
printf("\nERROR (nga/ngaLoadImage): Image is larger than alloted space!\n");
exit(1);
}
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() {
cpu.ip = cpu.sp = cpu.rp = 0;
for (cpu.ip = 0; cpu.ip < IMAGE_SIZE; cpu.ip++)
memory[cpu.ip] = 0; /* NO - nop instruction */
for (cpu.ip = 0; cpu.ip < STACK_DEPTH; cpu.ip++)
cpu.data[cpu.ip] = 0;
for (cpu.ip = 0; cpu.ip < ADDRESSES; cpu.ip++)
cpu.address[cpu.ip] = 0;
}
void inst_no() {
}
void inst_li() {
cpu.sp++;
cpu.ip++;
TOS = memory[cpu.ip];
}
void inst_du() {
cpu.sp++;
cpu.data[cpu.sp] = NOS;
}
void inst_dr() {
cpu.data[cpu.sp] = 0;
if (--cpu.sp < 0)
cpu.ip = IMAGE_SIZE;
}
void inst_sw() {
CELL a;
a = TOS;
TOS = NOS;
NOS = a;
}
void inst_pu() {
cpu.rp++;
TORS = TOS;
inst_dr();
}
void inst_po() {
cpu.sp++;
TOS = TORS;
cpu.rp--;
}
void inst_ju() {
cpu.ip = TOS - 1;
inst_dr();
}
void inst_ca() {
cpu.rp++;
TORS = cpu.ip;
cpu.ip = TOS - 1;
inst_dr();
}
void inst_cc() {
CELL a, b;
a = TOS; inst_dr(); /* Target */
b = TOS; inst_dr(); /* Flag */
if (b != 0) {
cpu.rp++;
TORS = cpu.ip;
cpu.ip = a - 1;
}
}
void inst_re() {
cpu.ip = TORS;
cpu.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() {
#ifndef NOCHECKS
if (TOS >= IMAGE_SIZE || TOS < -5) {
cpu.ip = IMAGE_SIZE;
printf("\nERROR (nga/inst_fe): Fetch beyond valid memory range\n");
exit(1);
} else {
#endif
switch (TOS) {
case -1: TOS = cpu.sp - 1; break;
case -2: TOS = cpu.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;
}
#ifndef NOCHECKS
}
#endif
}
void inst_st() {
#ifndef NOCHECKS
if (TOS <= IMAGE_SIZE && TOS >= 0) {
#endif
memory[TOS] = NOS;
inst_dr();
inst_dr();
#ifndef NOCHECKS
} else {
cpu.ip = IMAGE_SIZE;
printf("\nERROR (nga/inst_st): Store beyond valid memory range\n");
exit(1);
}
#endif
}
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;
#ifndef NOCHECKS
if (a == 0) {
printf("\nERROR (nga/inst_di): Division by zero\n");
exit(1);
}
#endif
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();
cpu.ip = TORS;
cpu.rp--;
}
}
void inst_ha() {
cpu.ip = IMAGE_SIZE;
}
void inst_ie() {
cpu.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) {
#ifdef FAST
switch (opcode) {
case 0: break;
case 1: inst_li(); break;
case 2: inst_du(); break;
case 3: inst_dr(); break;
case 4: inst_sw(); break;
case 5: inst_pu(); break;
case 6: inst_po(); break;
case 7: inst_ju(); break;
case 8: inst_ca(); break;
case 9: inst_cc(); break;
case 10: inst_re(); break;
case 11: inst_eq(); break;
case 12: inst_ne(); break;
case 13: inst_lt(); break;
case 14: inst_gt(); break;
case 15: inst_fe(); break;
case 16: inst_st(); break;
case 17: inst_ad(); break;
case 18: inst_su(); break;
case 19: inst_mu(); break;
case 20: inst_di(); break;
case 21: inst_an(); break;
case 22: inst_or(); break;
case 23: inst_xo(); break;
case 24: inst_sh(); break;
case 25: inst_zr(); break;
case 26: inst_ha(); break;
case 27: inst_ie(); break;
case 28: inst_iq(); break;
case 29: inst_ii(); break;
default: break;
}
#else
if (opcode != 0)
instructions[opcode]();
#endif
}
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;
}
}