1
0
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:
Ross Wilson
2015-11-03 19:54:12 +07:00
parent d9adb7e0a5
commit 82363e1a5c
5 changed files with 217 additions and 115 deletions

View File

@@ -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

View File

@@ -143,3 +143,8 @@ mem_save_core(char *filename)
fclose(fd);
}
void
mem_set_PTR_rom(void)
{
}

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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)
{