1
0
mirror of https://github.com/dschmenk/PLASMA.git synced 2025-04-07 16:41:59 +00:00

Add new files for C VM

This commit is contained in:
David Schmenk 2024-08-10 08:20:46 -07:00
parent bde74778b0
commit dfb6afc5f0
2 changed files with 888 additions and 0 deletions

798
src/vmsrc/cmdsys.c Normal file
View File

@ -0,0 +1,798 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <termios.h>
#include "plvm.h"
/*
* TERM I/O structure for CLI interface
*/
struct termios org_tio;
/*
* Symbol table
*/
#define SYMTBLSZ 1024
#define SYMSZ 16
#define MOD_ADDR 0x1000
#define DEF_CALL 0x0300
#define DEF_CALLSZ 0x0B00
#define DEF_ENTRYSZ 6
byte symtbl[SYMTBLSZ];
byte *lastsym = symtbl;
uword heap = 0x0200;
uword deftbl = DEF_CALL;
uword lastdef = DEF_CALL;
/*
* CMDSYS exports
*/
char *syslib_exp[] = {
"CMDSYS",
"MACHID",
"PUTC",
"PUTLN",
"PUTS",
"PUTI",
"GETC",
"GETS",
"PUTB",
"PUTH",
"TOUPPER",
"CALL",
"SYSCALL",
"HEAPMARK",
"HEAPALLOCALLIGN",
"HEAPALLOC",
"HEAPRELEASE",
"HEAPAVAIL",
"MEMSET",
"MEMCPY",
"STRCPY",
"STRCAT",
"SEXT",
"DIVMOD",
"ISUGT",
"ISUGE",
"ISULT",
"ISULE",
0
};
#if 0
void call(M6502 *mpu, uword pc)
{
unsigned int i, s;
int a, b;
char c, sz[64];
if (show_state)
printf("\nCall: %s\n", mem_6502[pc] ? syslib_exp[mem_6502[pc] - 1] : "BYTECODE");
switch (mem_6502[pc++])
{
case 0: // BYTECODE in mem_6502
vm_interp(mpu, mem_6502 + (mem_6502[pc] + (mem_6502[pc + 1] << 8)));
break;
case 1: // CMDSYS call
printf("CMD call code!\n");
break;
case 2: // MACHID
printf("MACHID call code!\n");
break;
case 3: // LIBRARY STDLIB::PUTC
c = POP;
if (c == 0x0D)
c = '\n';
putchar(c);
break;
case 4: // LIBRARY STDLIB::PUTNL
putchar('\n');
fflush(stdout);
break;
case 5: // LIBRARY STDLIB::PUTS
s = POP;
i = mem_6502[s++];
while (i--)
{
c = mem_6502[s++];
if (c == 0x0D)
c = '\n';
putchar(c);
}
break;
case 6: // LIBRARY STDLIB::PUTI
i = POP;
printf("%d", i);
break;
case 7: // LIBRARY STDLIB::GETC
PUSH(getchar());
break;
case 8: // LIBRARY STDLIB::GETS
c = POP;
putchar(c);
fgets(sz, 63, stdin);
for (i = 0; sz[i]; i++)
mem_6502[0x200 + i] = sz[i];
mem_6502[0x200 + i] = 0;
mem_6502[0x1FF] = i;
PUSH(0x1FF);
break;
case 9: // LIBRARY STDLIB::PUTB
i = UPOP;
printf("%02X", i);
break;
case 10: // LIBRARY STDLIB::PUTH
i = UPOP;
printf("%04X", i);
break;
case 24: // LIBRARY CMDSYS::DIVMOD
a = POP;
b = POP;
PUSH(b / a);
PUSH(b % a);
break;
default:
printf("\nUnimplemented call code:$%02X\n", mem_6502[pc - 1]);
exit(1);
}
}
#endif
/*
* Simple I/O routines
*/
/*
* CFFA1 emulation
*/
#define CFFADest 0x00
#define CFFAFileName 0x02
#define CFFAOldName 0x04
#define CFFAFileType 0x06
#define CFFAAuxType 0x07
#define CFFAFileSize 0x09
#define CFFAEntryPtr 0x0B
void pfail(const char *msg)
{
fflush(stdout);
perror(msg);
exit(1);
}
int save(uword address, unsigned length, const char *path)
{
FILE *file;
int count;
if (!(file = fopen(path, "wb")))
return 0;
while ((count = fwrite(mem_6502 + address, 1, length, file)))
{
address += count;
length -= count;
}
fclose(file);
return 1;
}
int load(uword address, const char *path)
{
FILE *file;
int count;
size_t max = 0x10000 - address;
if (!(file = fopen(path, "rb")))
return 0;
while ((count = fread(mem_6502 + address, 1, max, file)) > 0)
{
address += count;
max -= count;
}
fclose(file);
return 1;
}
char *strlower(char *strptr)
{
int i;
for (i = 0; strptr[i]; i++)
strptr[i] = tolower(strptr[i]);
return strptr;
}
int cffa1(M6502 *mpu, uword address, byte data)
{
char *fileptr, filename[64];
int addr;
struct stat sbuf;
switch (mpu->registers->x)
{
case 0x02: // quit
exit(0);
break;
case 0x14: // find dir entry
addr = mem_6502[CFFAFileName] | (mem_6502[CFFAFileName + 1] << 8);
memset(filename, 0, 64);
strncpy(filename, (char *)(mem_6502 + addr + 1), mem_6502[addr]);
strlower(filename);
strcat(filename, ".mod");
if (!(stat(filename, &sbuf)))
{
// DirEntry @ $9100
mem_6502[CFFAEntryPtr] = 0x00;
mem_6502[CFFAEntryPtr + 1] = 0x91;
mem_6502[0x9115] = sbuf.st_size;
mem_6502[0x9116] = sbuf.st_size >> 8;
mpu->registers->a = 0;
}
else
mpu->registers->a = -1;
break;
case 0x22: // load file
addr = mem_6502[CFFAFileName] | (mem_6502[CFFAFileName + 1] << 8);
memset(filename, 0, 64);
strncpy(filename, (char *)(mem_6502 + addr + 1), mem_6502[addr]);
strlower(filename);
strcat(filename, ".mod");
addr = mem_6502[CFFADest] | (mem_6502[CFFADest + 1] << 8);
mpu->registers->a = load(addr, filename) - 1;
break;
default:
{
char state[64];
fprintf(stderr, "Unimplemented CFFA function: %02X\n", mpu->registers->x);
M6502_dump(mpu, state);
fflush(stdout);
fprintf(stderr, "\nCFFA1 %s\n", state);
pfail("ABORT");
}
break;
}
RTS;
}
/*
* Character I/O emulation
*/
int paused = 0;
unsigned char keypending = 0;
int bye(M6502 *mpu, uword addr, byte data) { exit(0); return 0; }
int cout(M6502 *mpu, uword addr, byte data) { if (mpu->registers->a == 0x8D) putchar('\n'); putchar(mpu->registers->a & 0x7F); fflush(stdout); RTS; }
unsigned char keypressed(M6502 *mpu)
{
unsigned char cin, cext[2];
if (read(STDIN_FILENO, &cin, 1) > 0)
{
if (cin == 0x03) // CTRL-C
{
mpu->flags |= M6502_SingleStep;
paused = 1;
}
if (cin == 0x1B) // Look for left arrow
{
if (read(STDIN_FILENO, cext, 2) == 2 && cext[0] == '[' && cext[1] == 'D')
cin = 0x08;
}
keypending = cin | 0x80;
}
return keypending & 0x80;
}
unsigned char keyin(M6502 *mpu)
{
unsigned char cin;
if (!keypending)
keypressed(mpu);
cin = keypending;
keypending = 0;
return cin;
}
int rd6820kbdctl(M6502 *mpu, uword addr, byte data) { return keypressed(mpu); }
int rd6820vidctl(M6502 *mpu, uword addr, byte data) { return 0x00; }
int rd6820kbd(M6502 *mpu, uword addr, byte data) { return keyin(mpu); }
int rd6820vid(M6502 *mpu, uword addr, byte data) { return 0x80; }
int wr6820vid(M6502 *mpu, uword addr, byte data) { if (data == 0x8D) putchar('\n'); putchar(data & 0x7F); fflush(stdout); return 0; }
int setTraps(M6502 *mpu)
{
//
// Apple 1 memory-mapped IO
//
M6502_setCallback(mpu, read, 0xD010, rd6820kbd);
M6502_setCallback(mpu, read, 0xD011, rd6820kbdctl);
M6502_setCallback(mpu, read, 0xD012, rd6820vid);
M6502_setCallback(mpu, write, 0xD012, wr6820vid);
M6502_setCallback(mpu, read, 0xD013, rd6820vidctl);
//
// CFFA1 and ROM calls
//
M6502_setCallback(mpu, call, 0x9000, bye);
M6502_setCallback(mpu, call, 0x900C, cffa1);
M6502_setCallback(mpu, call, 0xFFEF, cout);
//
// Hook BRK to enter VM or native code
//
M6502_setCallback(mpu, call, VM_INLINE_ENTRY, vm_indef);
M6502_setCallback(mpu, call, VM_EXT_ENTRY, vm_exdef);
M6502_setCallback(mpu, call, VM_NATV_ENTRY, vm_natvdef);
M6502_setCallback(mpu, call, 0x0000, vm_irq);
//
// Hook Apple 1 IINTERP entrypoint
//
M6502_setCallback(mpu, call, 0x0283, vm_indef);
M6502_setCallback(mpu, call, 0x0293, vm_iidef);
return 0;
}
/*
* M6502_run() with trace/single step ability
*/
void M6502_exec(M6502 *mpu)
{
char state[64];
char insn[64];
do
{
if (mpu->flags & M6502_SingleStep)
{
M6502_dump(mpu, state);
M6502_disassemble(mpu, mpu->registers->pc, insn);
printf("%s : %s\r\n", state, insn);
if (paused || (keypressed(mpu) && keypending == 0x83))
{
keypending = 0;
while (!keypressed(mpu));
if (keypending == (0x80|'C'))
paused = 0;
else if (keypending == (0x80|'Q'))
break;
else if (keypending == 0x9B) // Escape (QUIT)
exit(-1);
keypending = 0;
}
}
} while (M6502_run(mpu) > 0);
}
void resetInput(void)
{
tcsetattr(STDIN_FILENO, TCSANOW, &org_tio);
}
void setRawInput(void)
{
struct termios termio;
tcgetattr(STDIN_FILENO, &termio); // save current port settings
memcpy(&org_tio, &termio, sizeof(struct termios));
termio.c_cflag = /*BAUDRATE | CRTSCTS |*/ CS8 | CLOCAL | CREAD;
termio.c_iflag = IGNPAR;
termio.c_oflag = 0;
termio.c_lflag = 0; // set input mode (non-canonical, no echo,...)
termio.c_cc[VTIME] = 0; // inter-character timer unused
termio.c_cc[VMIN] = 0; // non-blocking read
tcsetattr(STDIN_FILENO, TCSANOW, &termio);
atexit(resetInput);
}
/*
* Utility routines.
*
* A DCI string is one that has the high bit set for every character except the last.
* More space efficient than C or Pascal strings.
*/
int dcitos(byte *dci, char *str)
{
int len = 0;
do
str[len] = tolower(*dci & 0x7F);
while ((len++ < 16) && (*dci++ & 0x80));
str[len] = 0;
return len;
}
int stodci(char *str, byte *dci)
{
int len = 0;
do
dci[len] = toupper(*str) | 0x80;
while (*str++ && (len++ < 16));
dci[len - 1] &= 0x7F;
return len;
}
/*
* Heap routines.
*/
uword avail_heap(void)
{
uword vm_fp = UWORD_PTR(&mem_6502[FP]);
return vm_fp - heap;
}
uword alloc_heap(int size)
{
uword vm_fp = UWORD_PTR(&mem_6502[FP]);
uword addr = heap;
heap += size;
if (heap >= vm_fp)
{
printf("Error: heap/frame collision.\n");
exit (1);
}
return addr;
}
uword free_heap(int size)
{
uword vm_fp = UWORD_PTR(&mem_6502[FP]);
heap -= size;
return vm_fp - heap;
}
uword mark_heap(void)
{
return heap;
}
uword release_heap(uword newheap)
{
uword vm_fp = UWORD_PTR(&mem_6502[FP]);
heap = newheap;
return vm_fp - heap;
}
/*
* DCI table routines,
*/
void dump_tbl(byte *tbl)
{
int len;
byte *entbl;
while (*tbl)
{
len = 0;
while (*tbl & 0x80)
{
putchar(*tbl++ & 0x7F);
len++;
}
putchar(*tbl++);
putchar(':');
while (len++ < 15)
putchar(' ');
printf("$%04X\n", tbl[0] | (tbl[1] << 8));
tbl += 2;
}
}
uword lookup_tbl(byte *dci, byte *tbl)
{
char str[20];
byte *match, *entry = tbl;
while (*entry)
{
match = dci;
while (*entry == *match)
{
if (!(*entry & 0x80))
return entry[1] | (entry[2] << 8);
entry++;
match++;
}
while (*entry++ & 0x80);
entry += 2;
}
dcitos(dci, str);
printf("\nError: symbols %s not found in symbol table.\n", str);
return 0;
}
uword add_tbl(byte *dci, int val, byte **last)
{
while (*dci & 0x80)
*(*last)++ = *dci++;
*(*last)++ = *dci;
*(*last)++ = val;
*(*last)++ = val >> 8;
return 0;
}
/*
* Symbol table routines.
*/
void dump_sym(void)
{
printf("\nSystem Symbol Table:\n");
dump_tbl(symtbl);
}
uword lookup_sym(byte *sym)
{
return lookup_tbl(sym, symtbl);
}
uword add_sym(byte *sym, int addr)
{
return add_tbl(sym, addr, &lastsym);
}
/*
* Module routines.
*/
uword defcall_add(int bank, int addr)
{
mem_6502[lastdef] = bank ? 2 : 1;
mem_6502[lastdef + 1] = addr;
mem_6502[lastdef + 2] = addr >> 8;
return lastdef++;
}
uword def_lookup(byte *cdd, int defaddr)
{
int i, calldef = 0;
for (i = 0; cdd[i * 4] == 0x00; i++)
{
if ((cdd[i * 4 + 1] | (cdd[i * 4 + 2] << 8)) == defaddr)
{
calldef = cdd + i * 4 - mem_6502;
break;
}
}
return calldef;
}
uword extern_lookup(byte *esd, int index)
{
byte *sym;
char string[32];
while (*esd)
{
sym = esd;
esd += dcitos(esd, string);
if ((esd[0] & 0x10) && (esd[1] == index))
return lookup_sym(sym);
esd += 3;
}
printf("\nError: extern index %d not found in ESD.\n", index);
return 0;
}
int load_mod(M6502 *mpu, byte *mod)
{
uword modsize, hdrlen, len, end, magic, bytecode, fixup, addr, sysflags, defcnt = 0, init = 0, modaddr = mark_heap();
word modfix;
byte *moddep, *rld, *esd, *cdd, *sym;
byte header[128];
int fd;
char filename[48], string[17];
dcitos(mod, filename);
printf("Load module %s\n", filename);
if (strlen(filename) < 8 || (filename[strlen(filename) - 7] != '#'))
strcat(filename, ".mod");
fd = open(filename, O_RDONLY, 0);
if ((fd > 0) && (len = read(fd, header, 128)) > 0)
{
moddep = header + 1;
modsize = header[0] | (header[1] << 8);
magic = header[2] | (header[3] << 8);
if (magic == 0x6502)
{
//
// This is a relocatable bytecode module.
//
sysflags = header[4] | (header[5] << 8);
bytecode = header[6] | (header[7] << 8);
defcnt = header[8] | (header[9] << 8);
init = header[10] | (header[11] << 8);
moddep = header + 12;
//
// Load module dependencies.
//
while (*moddep)
{
if (lookup_sym(moddep) == 0)
{
if (fd)
{
close(fd);
fd = 0;
}
load_mod(mpu, moddep);
}
moddep += dcitos(moddep, string);
}
if (fd == 0)
{
fd = open(filename, O_RDONLY, 0);
len = read(fd, header, 128);
}
}
//
// Alloc heap space for relocated module (data + bytecode).
//
moddep += 1;
hdrlen = moddep - header;
len -= hdrlen;
modaddr = mark_heap();
end = modaddr + len;
//
// Read in remainder of module into memory for fixups.
//
memcpy(mem_6502 + modaddr, moddep, len);
while ((len = read(fd, mem_6502 + end, 4096)) > 0)
end += len;
close(fd);
//
// Apply all fixups and symbol import/export.
//
modfix = modaddr - hdrlen + 2; // - MOD_ADDR;
bytecode += modfix - MOD_ADDR;
end = modaddr - hdrlen + modsize + 2;
rld = mem_6502 + end; // Re-Locatable Directory
esd = rld; // Extern+Entry Symbol Directory
while (*esd != 0x00) // Scan to end of RLD
esd += 4;
esd++;
cdd = rld;
if (show_state)
{
//
// Dump different parts of module.
//
printf("Module load addr: $%04X\n", modaddr);
printf("Module size: %d\n", end - modaddr + hdrlen);
printf("Module code+data size: %d\n", modsize);
printf("Module magic: $%04X\n", magic);
printf("Module sysflags: $%04X\n", sysflags);
printf("Module bytecode: $%04X\n", bytecode);
printf("Module def count: $%04X\n", defcnt);
printf("Module init: $%04X\n", init ? init + modfix - MOD_ADDR : 0);
}
//
// Add module to symbol table.
//
add_sym(mod, modaddr);
//
// Print out the Re-Location Dictionary.
//
if (show_state)
printf("\nRe-Location Dictionary:\n");
while (*rld)
{
if (rld[0] == 0x02)
{
if (show_state) printf("\tDEF CODE");
addr = rld[1] | (rld[2] << 8);
addr += modfix - MOD_ADDR;
rld[0] = 0; // Set call code to 0
rld[1] = addr;
rld[2] = addr >> 8;
end = rld - mem_6502 + 4;
}
else
{
addr = rld[1] | (rld[2] << 8);
if (addr > 12)
{
addr += modfix;
if (rld[0] & 0x80)
fixup = (mem_6502[addr] | (mem_6502[addr + 1] << 8));
else
fixup = mem_6502[addr];
if (rld[0] & 0x10)
{
if (show_state) printf("\tEXTERN[$%02X] ", rld[3]);
fixup += extern_lookup(esd, rld[3]);
}
else
{
fixup += modfix - MOD_ADDR;
if (fixup >= bytecode)
{
//
// Replace with call def dictionary.
//
if (show_state) printf("\tDEF[$%04X->", fixup);
fixup = def_lookup(cdd, fixup);
if (show_state) printf("$%04X] ", fixup);
}
else
if (show_state) printf("\tINTERN ");
}
if (rld[0] & 0x80)
{
if (show_state) printf("WORD");
mem_6502[addr] = fixup;
mem_6502[addr + 1] = fixup >> 8;
}
else
{
if (show_state) printf("BYTE");
mem_6502[addr] = fixup;
}
}
else
{
if (show_state) printf("\tIGNORE (HDR) ");
}
}
if (show_state) printf("@$%04X\n", addr);
rld += 4;
}
if (show_state) printf("\nExternal/Entry Symbol Directory:\n");
while (*esd)
{
sym = esd;
esd += dcitos(esd, string);
if (esd[0] & 0x10)
{
if (show_state) printf("\tIMPORT %s[$%02X]\n", string, esd[1]);
}
else if (esd[0] & 0x08)
{
addr = esd[1] | (esd[2] << 8);
addr += modfix - MOD_ADDR;
if (show_state) printf("\tEXPORT %s@$%04X\n", string, addr);
if (addr >= bytecode)
addr = def_lookup(cdd, addr);
add_sym(sym, addr);
}
esd += 3;
}
}
else
{
printf("Error: Unable to load module %s\n", filename);
exit (1);
}
//
// Reserve heap space for relocated module.
//
alloc_heap(end - modaddr);
//
//Call init routine.
//
if (init)
{
vm_interp(mpu, mem_6502 + init + modfix - MOD_ADDR);
// release_heap(init + modfix - MOD_ADDR); // Free up init code
init = mem_6502[++mpu->registers->s + 0x100];
init |= mem_6502[++mpu->registers->s + 0x100] << 8;
return init;
}
return 0;
}
int main(int argc, char **argv)
{
byte dci[32];
int i;
char *interpfile = "a1plasma.bin";
M6502 *mpu = M6502_new(0, mem_6502, 0);
if (--argc)
{
argv++;
while (argc && (*argv)[0] == '-')
{
if ((*argv)[1] == 's')
show_state = 1;
else if ((*argv)[1] == 't')
mpu->flags |= M6502_SingleStep;
argc--;
argv++;
}
if (argc)
interpfile = *argv;
}
//
// Add default library.
//
for (i = 0; syslib_exp[i]; i++)
{
mem_6502[i] = i; // ???
stodci(syslib_exp[i], dci);
add_sym(dci, i+1);
}
//
// Load module from command line
//
// PLVMC version
//stodci(interpfile, dci);
//if (show_state) dump_sym();
//load_mod(dci);
//if (show_state) dump_sym();
// lib6502 version
if (!load(0x280, interpfile))
pfail(interpfile);
setRawInput();
setTraps(mpu);
M6502_reset(mpu);
mpu->registers->pc = 0x0280;
mem_6502[ESP] = ESTK_SIZE;
mem_6502[FPL] = 0x00;
mem_6502[FPH] = 0x80;
mem_6502[PPL] = 0x00;
mem_6502[PPH] = 0x80;
M6502_exec(mpu);
M6502_delete(mpu);
return 0;
}

90
src/vmsrc/plvm.h Normal file
View File

@ -0,0 +1,90 @@
#include "lib6502/config.h"
#include "lib6502/lib6502.h"
typedef uint8_t code;
typedef uint8_t byte;
typedef int16_t word;
typedef uint16_t uword;
typedef uint16_t address;
/*
* Bytecode memory
*/
#ifdef __LITTLE_ENDIAN__
#define BYTE_PTR(bp) (*(byte *)(bp))
#define WORD_PTR(wp) (*(word *)(wp))
#define UWORD_PTR(wp) (*(uword *)(wp))
#else
#define BYTE_PTR(bp) ((byte)((bp)[0]))
#define WORD_PTR(bp) ((word)((bp)[0] | ((bp)[1] << 8)))
#define UWORD_PTR(bp) ((uword)((bp)[0] | ((bp)[1] << 8)))
#endif
/*
* 6502 H/W stack
*/
#define PHA (mem_6502[mpu->registers->s-- + 0x100]=mpu->registers->a)
#define PLA (mpu->registers->a=mem_6502[++mpu->registers->s + 0x100])
#define PHP (mem_6502[mpu->registers->s-- + 0x100]=mpu->registers->p)
#define PLP (mpu->registers->p=mem_6502[++mpu->registers->s + 0x100])
#define RTI \
{ \
word pc; \
PLP; \
pc = mem_6502[++mpu->registers->s + 0x100]; \
pc |= mem_6502[++mpu->registers->s + 0x100] << 8; \
return pc; \
}
#define RTS \
{ \
word pc; \
pc = mem_6502[++mpu->registers->s + 0x100]; \
pc |= mem_6502[++mpu->registers->s + 0x100] << 8; \
return pc + 1; \
}
/*
* VM eval stack
*/
#define PUSH(v) (*(--esp))=(v)
#define POP ((word)(*(esp++)))
#define UPOP ((uword)(*(esp++)))
#define TOS (esp[0])
/*
* 6502 memory map
*/
#define MEM_SIZE 0x00010000
#define ESTK_SIZE 16
/*
* Zero page VM locations matching Apple ZP
*/
#define ESTKH 0xC0 // Hi bytes of VM stack
#define ESTKL 0xD0 // Lo bytes of VM stack
#define FP 0xE0 // Frame pointer
#define FPL 0xE0
#define FPH 0xE1
#define PP 0xE2 // String pool pointer
#define PPL 0xE2
#define PPH 0xE3
#define ESP 0xE5 // Temp storage for VM stack pointer
/*
* VM entrypoints
*/
#define VM_INLINE_ENTRY 0xFFF0
#define VM_EXT_ENTRY 0xFFF4
#define VM_NATV_ENTRY 0xFFF8
/*
* VM callouts
*/
void M6502_exec(M6502 *mpu);
typedef void (*VM_Callout)(M6502 *mpu);
extern int show_state;
extern byte mem_6502[];
extern char *syslib_exp[];
extern int vm_adddef(code * defaddr);
extern int vm_addnatv(VM_Callout);
extern int vm_irq(M6502 *mpu, uword address, byte data);
extern int vm_indef(M6502 *mpu, uword address, byte data);
extern int vm_iidef(M6502 *mpu, uword address, byte data);
extern int vm_exdef(M6502 *mpu, uword address, byte data);
extern int vm_natvdef(M6502 *mpu, uword address, byte data);
extern void vm_interp(M6502 *mpu, code *vm_ip);