From 82363e1a5c92ba443d1d79ba159146d7d7b727de Mon Sep 17 00:00:00 2001 From: Ross Wilson Date: Tue, 3 Nov 2015 19:54:12 +0700 Subject: [PATCH] Implementing new DSL instructions --- vimlac/Makefile | 2 +- vimlac/memory.c | 5 + vimlac/ptrptp.c | 62 ++++++++----- vimlac/test_cpu.c | 230 ++++++++++++++++++++++++++++------------------ vimlac/vimlac.c | 33 +++++++ 5 files changed, 217 insertions(+), 115 deletions(-) diff --git a/vimlac/Makefile b/vimlac/Makefile index 45b79c0..a9dfb2e 100755 --- a/vimlac/Makefile +++ b/vimlac/Makefile @@ -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 diff --git a/vimlac/memory.c b/vimlac/memory.c index 3bfa084..30c0efd 100755 --- a/vimlac/memory.c +++ b/vimlac/memory.c @@ -143,3 +143,8 @@ mem_save_core(char *filename) fclose(fd); } + +void +mem_set_PTR_rom(void) +{ +} diff --git a/vimlac/ptrptp.c b/vimlac/ptrptp.c index 4209472..2ce7b3d 100755 --- a/vimlac/ptrptp.c +++ b/vimlac/ptrptp.c @@ -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) diff --git a/vimlac/test_cpu.c b/vimlac/test_cpu.c index aac660b..6f715fe 100644 --- a/vimlac/test_cpu.c +++ b/vimlac/test_cpu.c @@ -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 - * where is one of AC, L, PC or DS, value is any value - * (all registers are set to 0 initially) - * - * setmem - * where is an address and value is any value OR - * [] where the value is the assembled opcode - * - * run [] - * execute one instruction, if optional is used PC := addr before - * - * checkcycles - * check number of executed cycles is - * - * checkreg - * check register (AC, L, PC or DS) has value - * - * checkmem - * check that memory at has - * - * allreg - * sets all registers to - * a "allreg 0" is assumed before each test - * - * allmem - * sets all of memory to - * 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 " 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 +// setmem
+// allreg +// allmem +// bootrom +// romwrite +// run [] +// rununtil
+// checkcycles +// checkreg +// checkmem +// checkcpu +// checkdcpu +// mount +// dismount +// checkfile //////////////////////////////////////////////////////////////////////////////// /****************************************************************************** @@ -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); } diff --git a/vimlac/vimlac.c b/vimlac/vimlac.c index d8ccfa2..4c5c691 100755 --- a/vimlac/vimlac.c +++ b/vimlac/vimlac.c @@ -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) {