db1571d2f3
FossilOrigin-Name: 6a34f9ce18e124e010668a9c2eaafe8d7aff3e5b35cb5fa67809f56abf9bb26a
331 lines
5.9 KiB
C
331 lines
5.9 KiB
C
/* Nga ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
Copyright (c) 2008 - 2021, 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;
|
|
}
|
|
}
|