/* * arith.c -- functions to emulate Manchester Prototype's * arithmetic operations * * LW 06/17/89 */ #include "madm.h" #include "display.h" #include "proto.h" #include "graphics.h" #include "keyboard.h" #include #include /* * subtract -- subtract the number in storage location "s" * from the accumulator */ void subtract( ADDR s ){ Accumulator[A_LINE] -= Store[s]; display_line(A_TUBE, A_LINE); } /* * control.c -- functions to emulate Manchester Prototype's * control operations * * LW 06/17/89 */ /* * stop -- Stop the machine. */ void stop( ADDR s ){ MADM_status = STOPPED; } /* * unused -- unknown operation (null instruction assumed) */ void unused( ADDR s ){ } /* * test -- skip the next instruction if the accumulator is * less than zero. */ void test( ADDR s ){ if (Accumulator[A_LINE] < 0) ++Control[CI_LINE]; } /* * jump -- replace the C.I. with the number in line "s" */ void jump( ADDR s ){ Control[CI_LINE] = Store[s]; display_line(C_TUBE, CI_LINE); } /* * rjump -- add the number in line "s" to the C.I. */ void rjump( ADDR s ) { Control[CI_LINE] += Store[s]; display_line(C_TUBE, CI_LINE); } /* * cursor.c -- functions to manipulate the cursor used in * editing programs for the Prototype Manchester Machine * * LW 06/18/89 */ typedef struct { ADDR c_line; /* current line in store */ unsigned c_bit; /* current bit in line */ unsigned c_x, /* x position of cursor on screen */ c_y; /* y position of cursor on screen */ } CURSOR; static CURSOR Cursor = { 0, 0, S_X - 1, S_Y - 1 }; /* * show_cursor -- display the cursor at its current screen position */ void show_cursor() { draw_box(1, Cursor.c_x, Cursor.c_y, Cursor.c_x+BLOB_WIDTH+1, Cursor.c_y+BLOB_HEIGHT+1); } /* * erase_cursor -- remove the cursor from its current screen position */ void erase_cursor() { draw_box(0, Cursor.c_x, Cursor.c_y, Cursor.c_x+BLOB_WIDTH+1, Cursor.c_y+BLOB_HEIGHT+1); } /* * place_cursor -- place the cursor at a given bit ("bit") * on a given line ("line") and display it * at its new position */ void place_cursor(line, bit) ADDR line; unsigned bit; { erase_cursor(); Cursor.c_line = line % STORE_SIZE; Cursor.c_bit = bit % LINE_BITS; Cursor.c_x = S_X - 1 + Cursor.c_bit * (BLOB_WIDTH + H_SPACE) + (Cursor.c_bit/4) * XH_SPACE + (Cursor.c_bit/16) * (2 * XH_SPACE); Cursor.c_y = S_Y - 1 - Cursor.c_line * (BLOB_HEIGHT + V_SPACE) - (Cursor.c_line/4) * XV_SPACE; show_cursor(); } /* * move_cursor -- move the cursor a given distance from the current line * ("d_line") and a given distance from the current bit * ("d_bit") */ void move_cursor(d_line, d_bit) int d_line, d_bit; { place_cursor(Cursor.c_line + d_line, Cursor.c_bit + d_bit); } /* * toggle_current_bit -- toggle the bit at the cursor's current location */ void toggle_current_bit() { toggle_bit(Cursor.c_line, Cursor.c_bit); } /* * display_current_bit -- display the bit at the cursor's current location */ void display_current_bit() { display_bit(S_TUBE, Cursor.c_line, Cursor.c_bit); } /* * display.c -- routines to display binary values on the simulated * Williams Tube "memory" for the Prototype Mark I. * * LW 06/17/89 */ /* initialize the monitor tubes */ MONITOR_TUBE monitors[NUM_MONITORS] = { {A_X, A_Y, Accumulator}, {C_X, C_Y, Control}, {S_X, S_Y, Store} }; /* * display_bit -- display the single bit value at position "bit" * on line "line" on the given monitor tube ("tube"). * Note that numbers are displayed with the least * significant bit on the left (backwards binary). */ void display_bit(tube, line, bit) int tube; ADDR line; unsigned bit; { blob((int)(monitors[tube].mt_val[line] >> bit) & 0x1, monitors[tube].mt_x + bit * (BLOB_WIDTH + H_SPACE) + (bit/4) * XH_SPACE + (bit/16) * (2 * XH_SPACE), monitors[tube].mt_y - line * (BLOB_HEIGHT + V_SPACE) - (line/4) * XV_SPACE); } /* * display_line -- display the value on a given "line" of the * given monitor "tube". */ void display_line(tube, line) int tube; ADDR line; { set_up_line(monitors[tube].mt_val[line]); show_line(monitors[tube].mt_x, monitors[tube].mt_y - line * (BLOB_HEIGHT + V_SPACE) - (line/4) * XV_SPACE); } /* * edit.c -- program editing functions for the Manchester Mark I * Prototype * * LW 06/17/89 */ /* * edit -- get commands from the keyboard and obey them * until either the QUIT or START command is entered. * Return the value 0 for QUIT (exit the simulator) or * 1 for START (start the program). */ int edit(){ ADDR line; place_cursor((ADDR)Control[CI_LINE], 0); for (;;) { switch (next_cmd()) { case QUIT_CMD: erase_cursor(); return(0); case START_CMD: erase_cursor(); MADM_status = RUNNING; return(1); case SSTEP_CMD: erase_cursor(); MADM_status = MANUAL; return(1); case CLEAR_CMD: /* KSC switch clears the store */ for (line = 0; line < STORE_SIZE; line++) { Store[line] = 0; display_line(S_TUBE, line); } show_cursor(); break; case CLR_AC_CMD: /* KCC switch clears A & C */ Accumulator[A_LINE] = Control[CI_LINE] = Control[PI_LINE] = 0; display_line(A_TUBE, A_LINE); display_line(C_TUBE, CI_LINE); display_line(C_TUBE, PI_LINE); place_cursor(0, 0); break; case TOGGLE_CMD: toggle_current_bit(); display_current_bit(); break; case UP_CMD: move_cursor(-1, 0); break; case DOWN_CMD: move_cursor(1, 0); break; case LEFT_CMD: move_cursor(0, -1); break; case RIGHT_CMD: move_cursor(0, 1); break; } } } /* * toggle_bit -- toggle the value of a given bit ("bit") in a given * line ("line") in the store. */ void toggle_bit(ADDR line, unsigned bit){ Store[line] ^= 1L << bit; } /* * execute.c -- fetch and execute instructions for the Manchester Prototype * * LW 06/16/89 */ void execute(){ do { fetch_instruction(); exec_instruction(); } while (MADM_status == RUNNING && !cmd_ready()); } /* * exec_ins.c -- execute the Present Instruction * * LW 06/17/89 */ /* * jump table -- one C function for each function code */ typedef void (*EXEC_FUNC)(ADDR); static EXEC_FUNC Optab[] = { /* function */ /* Operation */ jump, /* s, C */ rjump, /* c + s, C */ load_negative, /* -s, A */ store_accumulator, /* a, S */ subtract, /* a - s, A */ subtract, /* undocumented a - s, A */ test, /* Test */ stop /* Stop */ }; void exec_instruction(){ /* call the appropriate function to perform the operation */ Optab[Staticisor.i_func](Staticisor.i_addr); } /* * fetch.c -- fetch the next instruction from the store into * the P.I. and separate it into its components * * LW 06/17/89 */ void fetch_instruction() { Control[PI_LINE] = Store[++Control[CI_LINE] & MAX_ADDR]; display_line(C_TUBE, CI_LINE); display_line(C_TUBE, PI_LINE); Staticisor.i_addr = Control[PI_LINE] & MAX_ADDR; Staticisor.i_func = (Control[PI_LINE] >> ADDR_BITS) & MAX_FUNC; } /* * graphics.c -- system dependent graphics routines for Prototype * Manchester Mark I simulator. * * [IBM/PC Aztec C 4.10 Version] * * NOTE: Display routines in the simulator assume that * (0, 0) and (X_MAX, Y_MAX) are the lower-left and upper- * right corners of the graphics screen. If system-supplied * functions assume otherwise, the adjustments should be * made here. * * LW 06/18/89 */ #define odd(n) ((n) & 1) /* * set_up_graphics -- put the display into graphics mode (if necessary) * and set up the display screen. */ void set_up_graphics() { static const char main_title[] = "Manchester Mark I Prototype"; static const char sub_title[] = "(1948)"; mode('m'); /* IBM/PC 320x200 color graphics mode */ show_label((H_DOTS - sizeof(main_title)*CHAR_WIDTH)/2, V_DOTS - 1, main_title); show_label((H_DOTS - sizeof(sub_title)*CHAR_WIDTH)/2, V_DOTS - 1 - CHAR_WIDTH, sub_title); /* note that C & A labels are aligned horizontally */ show_label(A_X - CHAR_WIDTH, C_Y + CHAR_HEIGHT, "A:"); show_label(C_X - CHAR_WIDTH, C_Y + CHAR_HEIGHT, "C:"); show_label(S_X - CHAR_WIDTH, S_Y + CHAR_HEIGHT, "S:"); } /* * clear_graphics -- clear the graphics display */ void clear_graphics() { mode('l'); /* IBM/PC 80x25 color text mode */ } /* * blob -- display a blob of light at coordinates (x, y) on the screen. * A "dash" blob will be displayed for a non-zero "value", a * "dot" for zero (this simulates the "look" of the Williams * Tube memory for the Prototype Mark I). */ void blob(value, x, y) int value; unsigned x, y; { if (value != 0) color('w'); /* white dashes */ else color(0); /* black background */ point(x+1, y); if (value == 0) color('r'); /* dots are red (to look dimmer) */ point(x, y); } static unsigned char Scan_lines[(LINE_WIDTH+DOTS_PER_BYTE-1)/DOTS_PER_BYTE]; /* * set_up_line -- set up the scan line(s) needed to display a given * "value" on the screen. * * NOTE: The current algorithm for this routine works * only if S_X, BLOB_WIDTH+H_SPACE, and XHSPACE * are divisible by DOTS_PER_BYTE; */ void set_up_line(value) LINE value; { register unsigned i, byte; byte = 0; for (i = 0; i < LINE_BITS; i++) { if (i % 16 == 0) byte += 3 * XH_SPACE / DOTS_PER_BYTE; else if (i % 4 == 0) byte += XH_SPACE / DOTS_PER_BYTE; Scan_lines[byte] = (value & 0x1) ? 0xf0 : 0x80; value >>= 1; byte += (BLOB_WIDTH + H_SPACE) / DOTS_PER_BYTE; } } /* * show_line -- display the value last set up by "set_up_line" at * location (x, y) on the screen. */ void show_line(x, y) unsigned x, y; { extern unsigned _dsval; /* segment for static data */ register unsigned offset; /* offset into video buffer */ offset = ((V_DOTS - 1 - y) & ~0x1) * ((H_DOTS / DOTS_PER_BYTE) / 2) + (x / DOTS_PER_BYTE); if (!odd(y)) offset += 0x2000; /* assume small data model & CGA card */ movblock(Scan_lines, _dsval, offset, 0xb800, sizeof(Scan_lines)); } /* * draw_box -- draw a box whose lower left hand corner is (lo_x, lo_y) * and whose upper right hand corner is (hi_x, hi_y). * The remaining parameter ("visible") determines if the box * is to be visible or not. */ void draw_box(visible, lo_x, lo_y, hi_x, hi_y) int visible; unsigned lo_x, lo_y, hi_x, hi_y; { if (visible) color('w'); /* foreground color */ else color(0); /* background color */ line(lo_x, lo_y, hi_x, lo_y); lineto(hi_x, hi_y); lineto(lo_x, hi_y); lineto(lo_x, lo_y); } /* * init.c -- routines to initialize the prototype Manchester machine * * LW 06/17/89 */ static int Demo = 0; /* flag will be non-zero if demo is desired */ /* * Code for Kilburn's Highest Factor Routine * (the world's first working program: * 21 June 1948) * * 18 July 1948 version * * This code is loaded into the store if the * program is run as a demo. */ static LINE Orig_pgm[] = { 0, 0x4018, /* -24, A */ 0x601a, /* a, 26 */ 0x401a, /* -26, A */ 0x601b, /* a, 27 */ 0x4017, /* -23, A */ 0x801b, /* a - 27, A */ 0xc000, /* Test */ 0x2014, /* c + 20, C */ 0x801a, /* a - 26, A */ 0x6019, /* a, 25 */ 0x4019, /* -25, A */ 0xc000, /* Test */ 0xe000, /* Stop */ 0x401a, /* -26, A */ 0x8015, /* a - 21, A */ 0x601b, /* a, 27 */ 0x401b, /* -27, A */ 0x601a, /* a, 26 */ 0x0016, /* 22, C */ -3, /* [20] */ 1, /* [21] */ 4, /* [22] */ -0x40000, /* [23] = -(2**18) */ 0x3ffff /* [24] = (2**18) - 1 */ }; #define NUM_INSTRUCTIONS (sizeof(Orig_pgm) / sizeof(*Orig_pgm)) /* * initialize -- load the demo program into the store (if necessary) * and display the contents of the store on the * simulated Williams tube. */ void initialize() { ADDR line; if (Demo) { for (line = 0; line < NUM_INSTRUCTIONS; line++) Store[line] = Orig_pgm[line]; } set_up_graphics(); display_line(A_TUBE, A_LINE); for (line = 0; line < CONTROL_SIZE; line++) display_line(C_TUBE, line); for (line = 0; line < STORE_SIZE; line++) display_line(S_TUBE, line); } /* * clean_up -- restore the PC or terminal to its original condition */ void clean_up() { clear_graphics(); } /* * process_options -- process any options that may have been entered * on the command line. Currently only "-d" (load * the demo program) is supported. */ void process_options(argc, argv) int argc; char *argv[]; { int i; for (i = 1; i < argc; i++) { if (argv[i][0] == '-' && tolower(argv[i][1]) == 'd') Demo = 1; else fprintf(stderr, "%s: Unknown option\n", argv[i]); } } /* * madm.c -- Global variables for Mark I Prototype simulator * * LW 06/17/89 */ /* * Stuff in Williams Tubes */ LINE Accumulator[ACCUM_SIZE]; /* arithmetic register */ LINE Control[CONTROL_SIZE]; /* program counter */ LINE Store[STORE_SIZE]; /* main memory */ INSTRUCTION Staticisor; /* current instruction */ /* * control information */ STATUS MADM_status = STOPPED; /* is machine running or not? */ /* * main.c -- Manchester Prototype main program * * LW 05/30/89 */ main(argc, argv) int argc; char *argv[]; { process_options(argc, argv); initialize(); while (edit()) execute(); clean_up(); } /* * memory.c -- functions to emulate Manchester Prototype's memory operations * * LW 06/17/89 */ /* * load_negative -- load the complement of the number in line "s" * into the accumulator */ void load_negative( ADDR s){ Accumulator[A_LINE] = -Store[s]; display_line(A_TUBE, A_LINE); } /* * store_accumulator -- store the number in the accumulator into line "s" */ void store_accumulator( ADDR s) { Store[s] = Accumulator[A_LINE]; display_line(S_TUBE, s); }