diff --git a/SSEM/ssem_cpu.c b/SSEM/ssem_cpu.c new file mode 100644 index 00000000..28057afd --- /dev/null +++ b/SSEM/ssem_cpu.c @@ -0,0 +1,259 @@ +/* ssem_cpu.c: SSEM (Small Scale Experimental Machine) CPU simulator + + Based on the SIMH package written by Robert M Supnik + + Copyright (c) 2006-2008, Gerardo Ospina + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + This is not a supported product, but the author welcomes bug reports and fixes. + Mail to ngospina@panix.com + + cpu SSEM CPU + + The system state for the SSEM is: + + A[0]<0:31> accumulator + C[0]<0:31> current instruction + C[1]<0:31> present instruction + + The SSEM has just one instruction format: + + 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | |inst | |address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + SSEM instructions: + + <13:15> operation + + 000 0 C[0] <- S[n] + 001 1 C[0] <- C[0] + S[n] + 010 2 A[0] <- -S[n] + 011 3 S[n] <- A[0] + 100 4 A[0] <- A[0] - S[n] + 110 6 C[0] <- C[0] + 1 if (A[0] < 0) + 111 7 Stop the machine + + The SSEM has 32 32b words of memory. + + This routine is the instruction decode routine for the SSEM. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated + CI. It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + Stop instruction + breakpoint encountered + + 2. Interrupts. There are no interrupts. + + 3. Non-existent memory. All of memory always exists. + + 4. Adding I/O devices. The SSEM could not support additional + I/O devices. +*/ + +#include "ssem_defs.h" + +uint32 S[MEMSIZE] = { 0 }; /* storage (memory) */ + +int32 A[MEMSIZE] = { 0 }; /* A[0] accumulator */ +uint32 C[MEMSIZE] = { 0, 0 }; /* C[0] current instruction */ + /* C[1] present instruction */ +uint32 Staticisor = 0; + +extern int32 sim_interval; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern int32 sim_step; + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_one_inst (uint32 opc, uint32 ir); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX, MEMSIZE) }; + +REG cpu_reg[] = { + { DRDATA (CI, C[0], 5), REG_VMAD }, + { HRDATA (A, A[0], 32), REG_VMIO }, + { HRDATA (PI, C[1], 32), REG_VMIO + REG_HRO }, + { HRDATA (LF, Staticisor, 32), REG_VMIO + REG_HRO }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_SSEM, 0, "Manchester SSEM (Small Scale Experimental Machine)", "SSEM" }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 10, 5, 1, 16, 32, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL + }; + +t_stat sim_instr (void) +{ +t_stat reason = 0; + +sim_cancel_step (); /* defang SCP step */ + +/* Main instruction fetch/decode loop */ +do { + + if (sim_interval <= 0) { /* check clock queue */ +#if !UNIX_PLATFORM + if ((reason = sim_poll_kbd()) == SCPE_STOP) { /* poll on platforms without reliable signalling */ + break; + } +#endif + if (reason = sim_process_event ()) break; + } + + if (sim_brk_summ && /* breakpoint? */ + sim_brk_test (*C, SWMASK ('E'))) { + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + /* Increment current instruction */ + *C = (*C + 1) & AMASK; + + /* Get present instruction */ + C[1] = Read (*C); + + Staticisor = C[1] & IMASK; /* get instruction */ + sim_interval = sim_interval - 1; + + if (reason = cpu_one_inst (*C, Staticisor)) { /* one instr; error? */ + break; + } + + if (sim_step && (--sim_step <= 0)) /* do step count */ + reason = SCPE_STOP; + + } while (reason == 0); /* loop until halted */ + +return reason; +} + +t_stat cpu_reset (DEVICE *dptr) +{ +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = Read (addr); +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +Write (addr, val); +return SCPE_OK; +} + +/* Execute one instruction */ + +t_stat cpu_one_inst (uint32 opc, uint32 ir) +{ +uint32 ea, op; +t_stat reason = 0; + +op = I_GETOP (ir); /* opcode */ +switch (op) { /* case on opcode */ + + case OP_JUMP_INDIRECT: /* C[0] <- S[ea] */ + ea = I_GETEA (ir); /* address */ + *C = Read(ea); + break; + + case OP_JUMP_INDIRECT_RELATIVE: /* C[0] <- C[0] + S[ea] */ + ea = I_GETEA (ir); /* address */ + *C += Read(ea); + break; + + case OP_LOAD_NEGATED: /* A[0] <- -S[ea] */ + ea = I_GETEA (ir); /* address */ + *A = -((int32)Read(ea)); + break; + + case OP_STORE: /* S[ea] <- A[0] */ + ea = I_GETEA (ir); /* address */ + Write(ea, (uint32) *A); + break; + + case OP_SUBSTRACT: /* A[0] <- A[0] - S[ea] */ + case OP_UNDOCUMENTED: + ea = I_GETEA (ir); /* address */ + *A -= ((int32) Read(ea)); + break; + + case OP_TEST: /* C[0] <- C[0] + 1 if (A[0] < 0) */ + if (*A < 0){ + *C += 1; + } + break; + + case OP_STOP: /* Stop the machine */ + reason = STOP_STOP; /* stop simulation */ + break; + } /* end switch */ + +return reason; +} + +/* Support routines */ + +uint32 Read (uint32 ea) +{ +return S[ea] & MMASK; +} + +void Write (uint32 ea, uint32 dat) +{ +S[ea] = dat & MMASK; +return; +} diff --git a/SSEM/ssem_defs.h b/SSEM/ssem_defs.h new file mode 100644 index 00000000..43eeb3af --- /dev/null +++ b/SSEM/ssem_defs.h @@ -0,0 +1,82 @@ +/* ssem_defs.h: SSEM (Small Scale Experimental Machine) simulator definitions + + Based on the SIMH package written by Robert M Supnik + + Copyright (c) 2006-2008 Gerardo Ospina + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + This is not a supported product, but the author welcomes bug reports and fixes. + Mail to ngospina@panix.com +*/ + +#ifndef _SSEM_DEFS_H_ +#define _SSEM_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_STOP 1 /* STOP */ +#define STOP_IBKPT 2 /* breakpoint */ + +/* Memory */ + +#define MEMSIZE 32 /* memory size */ +#define AMASK 0x1F /* addr mask */ + +/* Architectural constants */ + +#define MMASK 0xFFFFFFFF /* memory mask */ +#define IMASK 0x0000E01F /* instruction mask */ +#define UMASK 0xFFFF1FE0 /* unused bits mask */ +#define SMASK 0x80000000 /* sign mask */ + +/* Instruction format */ + +#define I_M_OP 0x07 /* opcode */ +#define I_V_OP 13 +#define I_OP (I_M_OP << I_V_OP) +#define I_GETOP(x) (((x) >> I_V_OP) & I_M_OP) +#define I_M_EA AMASK /* address */ +#define I_V_EA 0 +#define I_EA (I_M_EA) +#define I_GETEA(x) ((x) & I_M_EA) + +/* Unit flags */ + +#define UNIT_V_SSEM (UNIT_V_UF + 0) +#define UNIT_SSEM (1u << UNIT_V_SSEM) + +/* Instructions */ + +enum opcodes { + OP_JUMP_INDIRECT, OP_JUMP_INDIRECT_RELATIVE, OP_LOAD_NEGATED, OP_STORE, + OP_SUBSTRACT, OP_UNDOCUMENTED, OP_TEST, OP_STOP + }; + +/* Prototypes */ + +uint32 Read (uint32 ea); +void Write (uint32 ea, uint32 dat); + +#endif diff --git a/SSEM/ssem_sys.c b/SSEM/ssem_sys.c new file mode 100644 index 00000000..5972f343 --- /dev/null +++ b/SSEM/ssem_sys.c @@ -0,0 +1,426 @@ +/* ssem_sys.c: SSEM (Small Scale Experimental Machine) simulator interface + + Based on the SIMH package written by Robert M Supnik + + Copyright (c) 2006-2008 Gerardo Ospina + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + This is not a supported product, but the author welcomes bug reports and fixes. + Mail to ngospina@panix.com +*/ + +#include +#include "ssem_defs.h" + +extern uint32 S[]; +extern uint32 C[]; +extern int32 A[]; + +extern DEVICE cpu_dev; +extern REG cpu_reg[]; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax maximum number of words for examine/deposit + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader + + fprint_sym memory examine + parser_sym memory deposit +*/ + +char sim_name[] = "SSEM"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Stop", + "Breakpoint", + }; + +/* SSEM binary dump */ + +t_stat ssem_dump (FILE *fi) +{ +if (sim_fwrite(A, sizeof(int32), 1, fi) != 1 || + sim_fwrite(C, sizeof(uint32), 1, fi) != 1 || + sim_fwrite(S, sizeof(uint32), MEMSIZE, fi) != MEMSIZE) { + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* SSEM binary loader */ + +t_stat ssem_load_dmp (FILE *fi) +{ +C[1] = 0; +if (sim_fread(A, sizeof(int32), 1, fi) != 1 || + sim_fread(C, sizeof(uint32), 1, fi) != 1 || + sim_fread(S, sizeof(uint32), MEMSIZE, fi) != MEMSIZE) { + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Loader + + Inputs: + *fi = input stream + *cptr = VM-specific arguments + *fnam = file name + flag = 1 = dump, 0 = load + Outputs: + return = status code +*/ + +t_stat sim_load (FILE *fi, char *cptr, char *fnam, int flag) +{ +size_t len; + +len = strlen(fnam); +if (len <= 4 || strcmp(fnam + (len - 4), ".dmp") != 0) return SCPE_ARG; + +if (flag == 1) return ssem_dump(fi); +return ssem_load_dmp(fi); +} + +/* Utility routine - prints number in decimal */ + +t_stat ssem_fprint_decimal (FILE *of, uint32 inst) +{ +if (inst & SMASK) + fprintf (of, "%d [%u]", inst, inst); +else + fprintf (of, "%d", inst); +return SCPE_OK; +} + +/* Utility routine - prints number in backward binary */ + +t_stat ssem_fprint_binary_number (FILE *of, uint32 inst, uint8 nbits) +{ +int i; +uint32 n; + +n = inst; +for (i = 0; i < nbits; i++) { + fprintf(of, "%d", n & 1 ? 1 : 0); + n >>= 1; + } +return SCPE_OK; +} + +/* Utility routine - prints instruction in backward binary */ + +t_stat ssem_fprint_binary (FILE *of, uint32 inst, int flag) +{ +uint32 op, ea; + +if (!flag) return ssem_fprint_binary_number(of, inst, 32); + +op = I_GETOP (inst); +if (op != OP_TEST && op != OP_STOP) { + ea = I_GETEA (inst); + ssem_fprint_binary_number(of, ea, 5); + fprintf (of, " "); + } +ssem_fprint_binary_number(of, op, 3); + +return SCPE_OK; +} + +/* Utility routine + + prints instruction in the mnemomic style used in the 1998 + competition reference manual: + "The Manchester University Small Scale Experimental Machine + Programmer's Reference manual" + http://www.computer50.org/mark1/prog98/ssemref.html +*/ + +t_stat ssem_fprint_competition_mnemonic (FILE *of, uint32 inst) +{ +uint32 op, ea; + +op = I_GETOP (inst); +switch (op) { + case OP_JUMP_INDIRECT: /* JMP */ + ea = I_GETEA (inst); + fprintf (of, "JMP %d", ea); + break; + + case OP_JUMP_INDIRECT_RELATIVE: /* JRP */ + ea = I_GETEA (inst); + fprintf (of, "JRP %d", ea); + break; + + case OP_LOAD_NEGATED: /* LDN */ + ea = I_GETEA (inst); + fprintf (of, "LDN %d", ea); + break; + + case OP_STORE: /* STO */ + ea = I_GETEA (inst); + fprintf (of, "STO %d", ea); + break; + + case OP_SUBSTRACT: /* SUB */ + ea = I_GETEA (inst); + fprintf (of, "SUB %d", ea); + break; + + case OP_UNDOCUMENTED: /* invalid instruction */ + return SCPE_ARG; + + case OP_TEST: /* CMP */ + fprintf (of, "CMP"); + break; + + case OP_STOP: /* STOP */ + fprintf (of, "STOP"); + break; /* end switch */ + } + +return SCPE_OK; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to data + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +uint32 inst; + +if (sw & SWMASK ('H')) return SCPE_ARG; /* hexadecimal? */ + +inst = val[0]; + +if (sw & SWMASK ('D')) /* decimal? */ + return ssem_fprint_decimal(of, inst); + +if (sw & SWMASK ('M')) { /* mnemomic? */ + return ssem_fprint_competition_mnemonic(of, inst); + } + +return ssem_fprint_binary(of, inst, sw & SWMASK ('I') || sw & SWMASK ('M')); +} + +static const char *opcode[] = { + "JMP", "JRP", "LDN", + "STO", "SUB", "", + "CMP", "STOP", + NULL +}; + +/* Utility function - parses decimal number. */ + +t_stat parse_sym_d (char *cptr, t_value *val) +{ +char *start; +int n; + +start = cptr; +if (*cptr == '-') cptr++; /* skip sign */ +n = 0; +while (*cptr >= '0' && *cptr <= '9') { + n = (n * 10) + (*cptr - '0'); + cptr++; + } +if (*start == '-') n = -n; +if (*start) *start = 'x'; + +if (*cptr) return SCPE_ARG; /* junk at end? */ + +*val = n; +return SCPE_OK; +} + +/* Utility function + + Parses mnemonic instruction. + + It accepts the mnemonics used in the 1998 competition reference + manual: + "The Manchester University Small Scale Experimental Machine + Programmer's Reference manual" + http://www.computer50.org/mark1/prog98/ssemref.html +*/ + +t_stat parse_sym_m (char *cptr, t_value *val) +{ +char *start; +uint32 n,a; +char gbuf[CBUFSIZE]; + +start = cptr; +cptr = get_glyph(cptr, gbuf, 0); +if (*start) *start = 'x'; + +for (n = 0; opcode[n] != NULL && strcmp(opcode[n], gbuf) != 0; n++) ; +if (opcode[n] == NULL) return SCPE_ARG; /* invalid mnemonic? */ + +if (!(*cptr) && n > OP_UNDOCUMENTED && n <= OP_STOP) { + *val = n << I_V_OP; return SCPE_OK; + } + +while (isspace (*cptr)) cptr++; /* absorb spaces */ + +if (*cptr < '0' || *cptr > '9') return SCPE_ARG; /* address expected */ + +a = 0; +while (*cptr >= '0' && *cptr <= '9') { + a = (a * 10) + (*cptr - '0'); + cptr++; + } + +if (a >= MEMSIZE) return SCPE_ARG; /* invalid address? */ + +if (*cptr) return SCPE_ARG; /* junk at end? */ + +*val = (n << I_V_OP) + a; +return SCPE_OK; +} + +/* Utility function - parses binary backward number. */ + +t_stat parse_sym_b (char *cptr, t_value *val) +{ +char *start; +int count; +t_value n; + +start = cptr; +count = 0; +n = 0; +while (*cptr == '0' || *cptr == '1') { + n = n + ((*cptr - '0') << count); + count++; + cptr++; + } +if (*start) *start = 'x'; + +if (*cptr) return SCPE_ARG; /* junk at end? */ + +*val = n; +return SCPE_OK; +} + +/* Utility function - parses binary backward instruccion. */ + +t_stat parse_sym_i (char *cptr, t_value *val) +{ +char *start; +int count; +t_value a,n; + +start = cptr; +count = 0; +n = 0; +while (*cptr == '0' || *cptr == '1') { + n = n + ((*cptr - '0') << count); + count++; + cptr++; + } +if (*start) *start = 'x'; + +if (!(*cptr) && n > OP_UNDOCUMENTED && n <= OP_STOP) { + *val = n << I_V_OP; return SCPE_OK; + } + +a = n; +if (a >= MEMSIZE) return SCPE_ARG; /* invalid addresss */ + +while (isspace (*cptr)) cptr++; /* absorb spaces */ + +if (*cptr != '0' && *cptr != '1') return SCPE_ARG; /* instruction expected */ + +count = 0; +n = 0; +while (*cptr == '0' || *cptr == '1') { + n = n + ((*cptr - '0') << count); + count++; + cptr++; + } +if (n >= OP_UNDOCUMENTED) return SCPE_ARG; /* invalid instruction? */ + +if (*cptr) return SCPE_ARG; /* junk at end? */ + +*val = (n << I_V_OP) + a; +return SCPE_OK; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ + +if (sw & SWMASK ('H')) return SCPE_ARG; /* hexadecimal? */ + +while (isspace (*cptr)) cptr++; /* absorb spaces */ + +if (sw & SWMASK ('D')) { /* decimal? */ + return parse_sym_d (cptr, val); + } + +if (sw & SWMASK ('I')) { /* backward binary instruction? */ + return parse_sym_i (cptr, val); + } + +if (sw & SWMASK ('M')) { /* mnemonic? */ + return parse_sym_m (cptr, val); + } + +return parse_sym_b(cptr, val); /* backward binary number */ +}