mirror of
https://github.com/rzzzwilson/pymlac.git
synced 2025-06-10 09:32:41 +00:00
Implementing new DSL instructions
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
DEVFILES = cpu.o dcpu.o ptrptp.o memory.o kb.o ttyin.o ttyout.o trace.o error.o log.o plist.o
|
||||
DEVFILES = cpu.o dcpu.o ptrptp.o memory.o kb.o ttyin.o ttyout.o trace.o error.o log.o plist.o bootstrap.o
|
||||
OFILES = vimlac.o $(DEVFILES)
|
||||
|
||||
#CFLAGS=-fPIC -O2 -Wall -ansi -pedantic -std=c99 -g
|
||||
|
||||
@@ -143,3 +143,8 @@ mem_save_core(char *filename)
|
||||
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
void
|
||||
mem_set_PTR_rom(void)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -56,17 +56,18 @@ static bool ptr_at_eof = true;
|
||||
static BYTE ptr_value = PTR_EOF;
|
||||
|
||||
|
||||
int ptr_mount(char *fname)
|
||||
int
|
||||
ptr_mount(char *fname)
|
||||
{
|
||||
if (device_use && STREQ(device_use, InUsePTP))
|
||||
verror("ptr_mount: Can't mount PTR file, being used as PTP");
|
||||
;
|
||||
error("ptr_mount: Can't mount PTR file, being used as PTP");
|
||||
|
||||
device_filename = fname;
|
||||
device_open_file = fopen(fname, "rb");
|
||||
if (device_open_file == NULL)
|
||||
{
|
||||
ptr_dismount();
|
||||
return errno;
|
||||
error("ptr_mount: can't mount file %s", fname);
|
||||
}
|
||||
device_motor_on = false;
|
||||
device_ready = false;
|
||||
@@ -78,10 +79,11 @@ int ptr_mount(char *fname)
|
||||
}
|
||||
|
||||
|
||||
void ptr_dismount(void)
|
||||
void
|
||||
ptr_dismount(void)
|
||||
{
|
||||
if (device_use && STREQ(device_use, InUsePTP))
|
||||
verror("ptr_mount: Can't dismount PTR file, being used as PTP");
|
||||
error("ptr_mount: Can't dismount PTR file, being used as PTP");
|
||||
|
||||
if (device_open_file)
|
||||
fclose(device_open_file);
|
||||
@@ -94,10 +96,11 @@ void ptr_dismount(void)
|
||||
}
|
||||
|
||||
|
||||
void ptr_start(void)
|
||||
void
|
||||
ptr_start(void)
|
||||
{
|
||||
if (device_use && STREQ(device_use, InUsePTP))
|
||||
verror("ptrptp_start: Can't start PTR motor, being used as PTP");
|
||||
error("ptrptp_start: Can't start PTR motor, being used as PTP");
|
||||
|
||||
device_motor_on = true;
|
||||
device_ready = false;
|
||||
@@ -105,32 +108,36 @@ void ptr_start(void)
|
||||
}
|
||||
|
||||
|
||||
void ptr_stop(void)
|
||||
void
|
||||
ptr_stop(void)
|
||||
{
|
||||
if (device_use && STREQ(device_use, InUsePTP))
|
||||
verror("ptr_stop: Can't stop PTR motor, being used as PTP");
|
||||
error("ptr_stop: Can't stop PTR motor, being used as PTP");
|
||||
|
||||
device_motor_on = false;
|
||||
device_cycle_count = PTR_NOT_PTR_READY_CYCLES;
|
||||
}
|
||||
|
||||
|
||||
int ptr_read(void)
|
||||
int
|
||||
ptr_read(void)
|
||||
{
|
||||
if (device_use && STREQ(device_use, InUsePTP))
|
||||
verror("ptr_read: Can't read PTR, device being used as PTP");
|
||||
error("ptr_read: Can't read PTR, device being used as PTP");
|
||||
|
||||
return ptr_value;
|
||||
}
|
||||
|
||||
|
||||
bool ptr_ready(void)
|
||||
bool
|
||||
ptr_ready(void)
|
||||
{
|
||||
return device_ready;
|
||||
}
|
||||
|
||||
|
||||
void ptr_tick(long cycles)
|
||||
void
|
||||
ptr_tick(long cycles)
|
||||
{
|
||||
// if not being used as PTR, do nothing
|
||||
if (device_use && !STREQ(device_use, InUsePTR))
|
||||
@@ -167,10 +174,11 @@ void ptr_tick(long cycles)
|
||||
|
||||
// MID
|
||||
|
||||
int ptp_mount(char *fname)
|
||||
int
|
||||
ptp_mount(char *fname)
|
||||
{
|
||||
if (device_use && STREQ(device_use, InUsePTR))
|
||||
verror("ptp_mount: Can't mount PTP, device being used as PTR");
|
||||
error("ptp_mount: Can't mount PTP, device being used as PTR");
|
||||
|
||||
device_use = InUsePTP;
|
||||
device_filename = fname;
|
||||
@@ -178,7 +186,7 @@ int ptp_mount(char *fname)
|
||||
if (device_open_file == NULL)
|
||||
{
|
||||
ptp_dismount();
|
||||
return errno;
|
||||
error("ptp_mount: Can't mount file %s", fname);
|
||||
}
|
||||
device_motor_on = false;
|
||||
device_ready = false;
|
||||
@@ -188,10 +196,11 @@ int ptp_mount(char *fname)
|
||||
}
|
||||
|
||||
|
||||
void ptp_dismount(void)
|
||||
void
|
||||
ptp_dismount(void)
|
||||
{
|
||||
if (device_use && STREQ(device_use, InUsePTR))
|
||||
verror("ptp_dismount: Can't dismount PTP, device being used as PTR");
|
||||
error("ptp_dismount: Can't dismount PTP, device being used as PTR");
|
||||
|
||||
if (device_open_file)
|
||||
if (fclose(device_open_file) != 0)
|
||||
@@ -202,7 +211,8 @@ void ptp_dismount(void)
|
||||
}
|
||||
|
||||
|
||||
void ptp_start(void)
|
||||
void
|
||||
ptp_start(void)
|
||||
{
|
||||
device_motor_on = true;
|
||||
device_ready = false;
|
||||
@@ -210,14 +220,16 @@ void ptp_start(void)
|
||||
}
|
||||
|
||||
|
||||
void ptp_stop(void)
|
||||
void
|
||||
ptp_stop(void)
|
||||
{
|
||||
device_motor_on = false;
|
||||
device_cycle_count = PTP_NOT_READY_CYCLES;
|
||||
}
|
||||
|
||||
|
||||
void ptp_punch(BYTE value)
|
||||
void
|
||||
ptp_punch(BYTE value)
|
||||
{
|
||||
if (device_motor_on && device_open_file != NULL)
|
||||
{
|
||||
@@ -227,13 +239,15 @@ void ptp_punch(BYTE value)
|
||||
}
|
||||
|
||||
|
||||
bool ptp_ready(void)
|
||||
bool
|
||||
ptp_ready(void)
|
||||
{
|
||||
return device_ready;
|
||||
}
|
||||
|
||||
|
||||
void ptp_tick(long cycles)
|
||||
void
|
||||
ptp_tick(long cycles)
|
||||
{
|
||||
/* if no state change */
|
||||
if (!device_motor_on || device_open_file == NULL)
|
||||
|
||||
@@ -1,53 +1,10 @@
|
||||
/*
|
||||
* Test the CPU implementation.
|
||||
*
|
||||
* We implement a small interpreter to test the CPU. The test code is read in
|
||||
* from a file:
|
||||
* We implement a small interpreter to test the CPU. The DSL implemented is
|
||||
* documented at: https://github.com/rzzzwilson/pymlac/wiki/pymlac%20DSL
|
||||
*
|
||||
* # LAW
|
||||
* setreg ac 012345; setreg l 1; setreg pc 0100; setmem 0100 [LAW 0]; RUN; checkcycles 1; checkreg pc 0101; checkreg ac 0
|
||||
* setreg ac 012345; setreg l 0; setmem 0100 [LAW 0]; RUN 0100
|
||||
* checkcycles 1; checkreg pc 0101; checkreg ac 0
|
||||
*
|
||||
* The instructions are delimited by ';' characters. A line beginning with a
|
||||
* white space is a continuation of the previous line.
|
||||
* Lines with '#' in column 1 are comments.
|
||||
*
|
||||
* The test instructions are:
|
||||
* setreg <name> <value>
|
||||
* where <name> is one of AC, L, PC or DS, value is any value
|
||||
* (all registers are set to 0 initially)
|
||||
*
|
||||
* setmem <addr> <value>
|
||||
* where <addr> is an address and value is any value OR
|
||||
* [<instruction>] where the value is the assembled opcode
|
||||
*
|
||||
* run [<addr>]
|
||||
* execute one instruction, if optional <addr> is used PC := addr before
|
||||
*
|
||||
* checkcycles <number>
|
||||
* check number of executed cycles is <number>
|
||||
*
|
||||
* checkreg <name> <value>
|
||||
* check register (AC, L, PC or DS) has value <value>
|
||||
*
|
||||
* checkmem <addr> <value>
|
||||
* check that memory at <addr> has <value>
|
||||
*
|
||||
* allreg <value>
|
||||
* sets all registers to <value>
|
||||
* a "allreg 0" is assumed before each test
|
||||
*
|
||||
* allmem <value>
|
||||
* sets all of memory to <value>
|
||||
* a "allmem 0" is assumed before each test
|
||||
*
|
||||
* In addition, all of memory is checked for changed values after execution
|
||||
* except where an explicit "checkmem <addr> <value>" has been performed.
|
||||
* Additionally, registers that aren't explicitly checked are tested to make
|
||||
* sure they didn't change.
|
||||
*
|
||||
* If a test line finds no error, just print the fully assembled test line.
|
||||
* If a test line finds no error, print nothing.
|
||||
* If any errors are found, print line followed by all errors.
|
||||
*/
|
||||
|
||||
@@ -64,6 +21,7 @@
|
||||
#include "error.h"
|
||||
#include "log.h"
|
||||
#include "plist.h"
|
||||
#include "bootstrap.h"
|
||||
|
||||
|
||||
// string comparison macro
|
||||
@@ -154,6 +112,23 @@ strupper(char *str)
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Description : Convert a string to lower case.
|
||||
Parameters : str - address of string to convert
|
||||
Returns :
|
||||
Comments : The string is converted 'in situ'.
|
||||
******************************************************************************/
|
||||
void
|
||||
strlower(char *str)
|
||||
{
|
||||
while (*str)
|
||||
{
|
||||
*str = tolower(*str);
|
||||
++str;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Description : Convert a string value into a WORD integer.
|
||||
Parameters : str - address of string holding [0-9] characters
|
||||
@@ -334,7 +309,26 @@ get_mem_plist(WORD addr, WORD *value)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// The following functions are the DSL functions.
|
||||
// Here we implement the DSL primitives. They all take two parameters which are
|
||||
// the DSL field1 and field2 values (lowercase strings). If one or both are
|
||||
// missing the parameter is None.
|
||||
//
|
||||
// setreg <name> <value>
|
||||
// setmem <address> <value>
|
||||
// allreg <value>
|
||||
// allmem <value>
|
||||
// bootrom <type>
|
||||
// romwrite <bool>
|
||||
// run [<number>]
|
||||
// rununtil <address>
|
||||
// checkcycles <number>
|
||||
// checkreg <name> <value>
|
||||
// checkmem <addr> <value>
|
||||
// checkcpu <state>
|
||||
// checkdcpu <state>
|
||||
// mount <device> <filename>
|
||||
// dismount <device>
|
||||
// checkfile <file1> <file2>
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/******************************************************************************
|
||||
@@ -369,29 +363,6 @@ setreg(char *name, char *fld2)
|
||||
return result;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Description : Set the display ON or OFF.
|
||||
Parameters : state - string containing 'on' or 'off'
|
||||
: var2 - unused
|
||||
Returns : The number of errors encountered (0 or 1).
|
||||
Comments :
|
||||
******************************************************************************/
|
||||
int
|
||||
setd(char *state, char *var2)
|
||||
{
|
||||
if (STREQ(state, "ON"))
|
||||
dcpu_start();
|
||||
else if (STREQ(state, "OFF"))
|
||||
dcpu_stop();
|
||||
else
|
||||
{
|
||||
vlog("setd: bad state: %s", state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Description : Set a memory cell to a value;
|
||||
@@ -470,28 +441,77 @@ allmem(char *value, char *var2)
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Description : Run one or more instructions on the CPU.
|
||||
Parameters : num - address of memory cell (string, may be NULL)
|
||||
: fld2 - unused
|
||||
Description : Set the boot ROM to a papertape or TTY bootstrap.
|
||||
Parameters : type - type of ROM, eother 'PTR', 'TTY' or NULL
|
||||
: ignore - unused
|
||||
Returns : The number of errors encountered (0 or 1).
|
||||
Comments :
|
||||
******************************************************************************/
|
||||
int
|
||||
run_one(char *num, char *fld2)
|
||||
bootrom(char *type, char *ignore)
|
||||
{
|
||||
if (STREQ(type, "PTR"))
|
||||
mem_set_rom(PtrROMImage);
|
||||
else if (STREQ(type, "TTY"))
|
||||
mem_set_rom(TtyROMImage);
|
||||
else
|
||||
{
|
||||
vlog("boot_rom: bad bootstrap type: %s", type);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Description : Set the ROM write capability.
|
||||
Parameters : write - sets if ROM writable, "ON" or "OFF"
|
||||
: ignore - unused
|
||||
Returns : The number of errors encountered (0 or 1).
|
||||
Comments :
|
||||
******************************************************************************/
|
||||
int
|
||||
romwrite(char *write, char *fld2)
|
||||
{
|
||||
if (STREQ(write, "ON"))
|
||||
mem_set_rom_readonly(false);
|
||||
else if (STREQ(write, "OFF"))
|
||||
mem_set_rom_readonly(true);
|
||||
else
|
||||
{
|
||||
vlog("romwrite: bad ROM writable code: %s", write);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Description : Run one or more instructions on the CPU.
|
||||
Parameters : num - the number of instructions to run (assume 1 if NULL)
|
||||
: ignore - unused
|
||||
Returns : The number of errors encountered (0 or 1).
|
||||
Comments :
|
||||
******************************************************************************/
|
||||
int
|
||||
run(char *num, char *ignore)
|
||||
{
|
||||
int run_num = 0;
|
||||
|
||||
// if given address run from that address, else use PC contents
|
||||
// if not given a number of instructions, assume 1
|
||||
if (!num)
|
||||
run_num = 1;
|
||||
else
|
||||
run_num = str2word(num);
|
||||
num = "1";
|
||||
|
||||
run_num = str2word(num);
|
||||
|
||||
vlog("run: executing %d instructions", run_num);
|
||||
|
||||
cpu_start();
|
||||
while (run_num--)
|
||||
{
|
||||
int cycles = cpu_execute_one();
|
||||
|
||||
vlog("run: in loop, cycles=%d", cycles);
|
||||
ptr_tick(cycles);
|
||||
ptp_tick(cycles);
|
||||
|
||||
@@ -504,13 +524,13 @@ run_one(char *num, char *fld2)
|
||||
|
||||
/******************************************************************************
|
||||
Description : Run the CPU until PC is the same as 'addr'.
|
||||
Parameters : addr - PC contents to stop at
|
||||
: fld2 - unused
|
||||
Parameters : addr - PC contents to stop at
|
||||
: ignore - unused
|
||||
Returns : The number of errors encountered (0 or 1).
|
||||
Comments :
|
||||
Comments : We execute one instruction before checking PC.
|
||||
******************************************************************************/
|
||||
int
|
||||
rununtil(char *addr, char *fld2)
|
||||
rununtil(char *addr, char *ignore)
|
||||
{
|
||||
WORD stop_pc = str2word(addr);
|
||||
|
||||
@@ -531,13 +551,13 @@ rununtil(char *addr, char *fld2)
|
||||
|
||||
/******************************************************************************
|
||||
Description : Check that the number of cycles used is as expected.
|
||||
Parameters : cycles - address of memory cell (string)
|
||||
: var2 - unused
|
||||
Parameters : cycles - number of cycles expected
|
||||
: ignore - unused
|
||||
Returns : The number of errors encountered (0 or 1).
|
||||
Comments :
|
||||
******************************************************************************/
|
||||
int
|
||||
checkcycles(char *cycles, char *fld2)
|
||||
checkcycles(char *cycles, char *ignore)
|
||||
{
|
||||
WORD c = str2word(cycles);
|
||||
|
||||
@@ -650,6 +670,32 @@ checkdcpu(char *state, char *unused)
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Description : Mount a file on a device.
|
||||
Parameters : device - a device name ("PTR", "PTP", "TTYIN" or "TTYOUT")
|
||||
: filename - path to the file to mount
|
||||
Returns : The number of errors encountered (0 or 1).
|
||||
Comments : If the device is an input device the file *must* exist.
|
||||
******************************************************************************/
|
||||
int
|
||||
mount(char *device, char *filename)
|
||||
{
|
||||
strlower(filename);
|
||||
|
||||
if (STREQ(device, "PTR"))
|
||||
ptr_mount(filename);
|
||||
else if (STREQ(device, "PTP"))
|
||||
ptp_mount(filename);
|
||||
else
|
||||
{
|
||||
vlog("mount: mount device name not recognized: %s", device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Description : Check that a memory address contents is as expected.
|
||||
Parameters : address - memory address to check (string)
|
||||
@@ -1258,6 +1304,8 @@ run_one_test(Test *test)
|
||||
cpu_set_PC(InitRegValue);
|
||||
cpu_set_DS(InitRegValue);
|
||||
|
||||
UsedCycles = 0;
|
||||
|
||||
for (Command *cmd = test->commands; cmd; cmd = cmd->next)
|
||||
{
|
||||
char *opcode = cmd->opcode;
|
||||
@@ -1280,7 +1328,7 @@ run_one_test(Test *test)
|
||||
else if (STREQ(opcode, "SETMEM"))
|
||||
error += setmem(fld1, fld2);
|
||||
else if (STREQ(opcode, "RUN"))
|
||||
error += run_one(fld1, fld2);
|
||||
error += run(fld1, fld2);
|
||||
else if (STREQ(opcode, "RUNUNTIL"))
|
||||
error += rununtil(fld1, fld2);
|
||||
else if (STREQ(opcode, "CHECKCYCLES"))
|
||||
@@ -1293,14 +1341,16 @@ run_one_test(Test *test)
|
||||
error += allreg(fld1, fld2);
|
||||
else if (STREQ(opcode, "ALLMEM"))
|
||||
error += allmem(fld1, fld2);
|
||||
else if (STREQ(opcode, "bootrom"))
|
||||
error += allmem(fld1, fld2);
|
||||
else if (STREQ(opcode, "CHECKRUN"))
|
||||
error += checkrun(fld1, fld2);
|
||||
else if (STREQ(opcode, "SETD"))
|
||||
error += setd(fld1, fld2);
|
||||
else if (STREQ(opcode, "CHECKDCPU"))
|
||||
error += checkdcpu(fld1, fld2);
|
||||
else if (STREQ(opcode, "CHECKCPU"))
|
||||
error += checkcpu(fld1, fld2);
|
||||
else if (STREQ(opcode, "MOUNT"))
|
||||
error += mount(fld1, fld2);
|
||||
else
|
||||
{
|
||||
printf("Unrecognized operation '%s' at line %d\n",
|
||||
@@ -1328,7 +1378,7 @@ Description : Run tests and collect number of errors.
|
||||
Comments :
|
||||
******************************************************************************/
|
||||
int
|
||||
run(Test *test)
|
||||
run_tests(Test *test)
|
||||
{
|
||||
int errors = 0;
|
||||
int one_test_errors = 0;
|
||||
@@ -1411,7 +1461,7 @@ execute(char *script)
|
||||
//#endif
|
||||
|
||||
// execute tests
|
||||
return run(test);
|
||||
return run_tests(test);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -47,6 +47,39 @@ WORD PtrROMImage[] = {0060077, /* start lac base ;40 get load address */
|
||||
0003700, /* go word 03700H;76 */
|
||||
0003677 /* base word 03677H;77 */};
|
||||
|
||||
WORD TtyROMImage[] = {0060077, /* start lac base ;40 get load address */
|
||||
0020010, /* dac 10 ;41 put into auto-inc reg */
|
||||
0104100, /* lwc 0100 ;42 -0100 into AC */
|
||||
0020020, /* dac 20 ;43 put into memory */
|
||||
0001061, /* hon ;44 start PTR */
|
||||
0100011, /* wait cal ;45 clear AC+LINK */
|
||||
0002400, /* hsf ;46 skip if PTR has data */
|
||||
0010046, /* jmp .-1 ;47 wait until is data */
|
||||
0001051, /* hrb ;50 read PTR -> AC */
|
||||
0074075, /* sam what ;51 skip if AC == 2 */
|
||||
0010045, /* jmp wait ;52 wait until PTR return 0 */
|
||||
0002400, /* loop hsf ;53 skip if PTR has data */
|
||||
0010053, /* jmp .-1 ;54 wait until is data */
|
||||
0001051, /* hrb ;55 read PTR -> AC */
|
||||
0003003, /* ral 3 ;56 move byte into high AC */
|
||||
0003003, /* ral 3 ;57 */
|
||||
0003002, /* ral 2 ;60 */
|
||||
0102400, /* hsn ;61 wait until PTR moves */
|
||||
0010061, /* jmp .-1 ;62 */
|
||||
0002400, /* hsf ;63 skip if PTR has data */
|
||||
0010063, /* jmp .-1 ;64 wait until is data */
|
||||
0001051, /* hrb ;65 read PTR -> AC */
|
||||
0120010, /* dac *10 ;66 store word, inc pointer */
|
||||
0102400, /* hsn ;67 wait until PTR moves */
|
||||
0010067, /* jmp .-1 ;70 */
|
||||
0100011, /* cal ;71 clear AC & LINK */
|
||||
0030020, /* isz 20 ;72 inc mem and skip zero */
|
||||
0010053, /* jmp loop ;73 if not finished, jump */
|
||||
0110076, /* jmp *go ;74 execute loader */
|
||||
0000002, /* what data 2 ;75 */
|
||||
0003700, /* go word 03700H;76 */
|
||||
0003677 /* base word 03677H;77 */};
|
||||
|
||||
void
|
||||
run(WORD pc)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user