Added page describing use of SIMH to simulate a PDP-11 for running code compiled...
authorAaron Taylor <ataylor@subgeniuskitty.com>
Tue, 5 Jan 2021 11:39:55 +0000 (03:39 -0800)
committerAaron Taylor <ataylor@subgeniuskitty.com>
Tue, 5 Jan 2021 11:39:55 +0000 (03:39 -0800)
data/development/pdp-11/modern_c_software_development/pdp11-simulator-simh.md [new file with mode: 0644]
data/development/pdp-11/modern_c_software_development/pdp11-simulator-simh.metadata [new file with mode: 0644]

diff --git a/data/development/pdp-11/modern_c_software_development/pdp11-simulator-simh.md b/data/development/pdp-11/modern_c_software_development/pdp11-simulator-simh.md
new file mode 100644 (file)
index 0000000..f2d71b3
--- /dev/null
@@ -0,0 +1,380 @@
+# Overview #
+
+TODO: Write introduction. Goal is to run baremetal PDP-11 programs in SIMH, a
+PDP-11 simulator.
+
+TODO: What kind of joint header do I want across all the articles in a set,
+linking them together?
+
+# SIMH Installation #
+
+We first need to install [SIMH](https://github.com/simh/simh), a program that
+simulates many computer architectures including the DEC PDP-11.
+
+  - **FreeBSD:** Either install package via `pkg install simh` or build from
+    ports under `emulators/simh`.
+
+  - **Debian:** Install as package via `sudo apt install simh`.
+
+  - **Windows:** Download [pre-built snapshot binaries](https://github.com/simh/Win32-Development-Binaries).
+
+  - **Other:** Read the [SIMH README](https://github.com/simh/simh) section
+    "Building Simulators Yourself".
+
+After installation, launch the PDP-11 simulator with the command `pdp11`. It
+should display a prompt `sim>` where you can type `quit` followed by `ENTER` to
+exit the simulator.
+
+    % pdp11
+    
+    PDP-11 simulator V3.9-0
+    sim> quit
+    Goodbye
+    %
+
+
+# SIMH Basics #
+
+Now that SIMH is installed, go ahead and launch it again with the command
+`pdp11`. This should result in the prompt `sim>`. All commands entered at this
+`sim>` prompt are executed by the SIMH simulator itself, not by the simulated
+PDP-11. For example, we could display the configuration of the simulated
+PDP-11 defined in SIMH.
+
+    sim> show configuration
+    PDP-11 simulator configuration
+
+    CPU, 11/73, NOCIS, idle disabled, autoconfiguration enabled, 256KB SYSTEM
+    [...]
+
+We can change the simulated PDP-11, for example by changing to a different
+model.
+
+    sim> set cpu 11/40
+    Disabling XQ
+    sim> show configuration
+    PDP-11 simulator configuration
+    
+    CPU, 11/40, NOFIS, idle disabled, autoconfiguration enabled, 256KB SYSTEM
+    [...]
+
+SIMH also allows us to deposit values directly into memory and read them back.
+Note that addresses in SIMH refer to the physical address rather than the
+virtual address, and thus may be up to 22-bits long on certain models, as shown
+below.
+
+    sim> examine 0140000
+    140000: 000000
+    sim> deposit 0140000 042
+    sim> examine 0140000
+    140000: 000042
+    sim>
+
+Once a program is loaded into memory, the simulated PDP-11 may be commanded to
+execute it via the command `go <address>`. For example, if the program was
+loaded starting at address `01000`, then begin execution with `go 01000`.
+
+
+## Interrupting Simulation ##
+
+At any time, the simulation may be paused by pressing `Ctrl-e`. This returns to
+the `sim>` prompt where memory may be examined or other extra-sim capabilities
+executed. For example, if we have the PDP-11 CPU execute a tight infinite loop,
+the simulation never naturally ends, but we can pause it, allowing us to exit.
+
+    sim> go
+    <Press Ctrl-e>
+    Simulation stopped, PC: 004166 (BR 4166)
+    sim> quit
+    Goodbye
+    %
+
+Note that, instead of `quit`, typing `go` would have resumed the simulation
+exactly where it paused, shown in the status message where the `PC` register is
+set to address `04166`.
+
+
+## Saving Configuration ##
+
+Any command that may be entered at the `sim>` prompt, may also be entered in a
+configuration file. For example, consider the following file named `simh.conf`.
+
+    deposit 01000 0777
+    echo Just set address 01000 to the instruction 'BRANCH 01000'.
+    go 01000
+
+We can tell SIMH to load this file and execute each line as though it were
+typed at the `sim>` prompt by including the config file name. Even though the
+commands are not displayed, we can see from the `echo` command that they were
+executed.
+
+    % pdp11 simh.conf
+    
+    PDP-11 simulator V3.9-0
+    Just set address 01000 to the instruction 'BRANCH 01000'.
+    
+    Simulation stopped, PC: 001000 (BR 1000)
+    sim> quit
+    Goodbye
+
+Since this program is an infinite loop, we pressed `Ctrl-e` to pause the
+simulation and allow us to quit.
+
+
+# SIMH Loader #
+
+Since we're trying to run bare-metal code on this simulated PDP-11, we don't
+need to bother with disk images; instead we will load a binary directly into
+the PDP-11's memory using SIMH's `load` command.
+
+
+## Loader Format ##
+
+The loader included with SIMH doesn't accept a raw binary file or the a.out
+executable generated by our cross compiler. Instead, it expects a paper tape
+image file in the following format.
+
+
+    Loader format consists of blocks, optionally preceded, separated, and
+    followed by zeroes. Each block consists of the following entries. Note
+    that all entries are one byte.
+    
+    0001
+    0000
+    Low byte of block length (data byte count + 6 for header, excludes checksum)
+    High byte of block length
+    Low byte of load address
+    High byte of load address
+    Data byte 0
+    ...
+    Data byte N
+    Checksum
+    
+    The 8-bit checksum for a block is the twos-complement of the lower eight
+    sum bits for all six header bytes and all data bytes.
+    
+    If the block length is exactly six bytes (i.e. only header, no data),
+    then the block marks the end-of-tape. The checksum should be zero.  If
+    the load address of this final block is not 000001, then it is used as
+    the starting PC.
+
+If you don't want to generate this format yourself, use the utility
+[bin2load](https://git.subgeniuskitty.com/pdp11-bin2load/.git) which converts a
+binary image into a SIMH compatible loader format. See the `README.md` file for
+installation instructions.
+
+
+## Load and Execute ##
+
+We can extract a binary from the a.out file generated by our cross compiler
+using the `pdp11-aout-objcopy` tool built at the same time as the cross
+compiler. This binary can then be converted by `bin2load` and loaded into SIMH.
+
+    % pdp11-aout-objcopy --only-section=.text --output-target binary program.out program.bin
+    % bin2load -i program.bin -o program.pdp11 -a 01000
+    % pdp11
+
+    PDP-11 simulator V3.9-0
+    sim> load program.pdp11
+    sim> go
+
+If we pass a starting address to `bin2load` with the `-a` flag (as shown
+above), then SIMH configures the simulation so that execution starts at address
+`01000` when the `go` command is entered.
+
+The `load <file>` and `go` command may also be included in the SIMH
+configuration file, automatically loading and executing whenever the simulator
+is started.
+
+
+# Execution #
+
+Let's bring all these steps together. Assume we've built our cross compiler and
+created a `Hello, World!` program like the one shown in the four files below.
+
+**`program.c`:**
+
+    #include <stdint.h>
+    
+    #define XCSR (*((volatile uint16_t *)0177564))
+    #define XBUF (*((volatile uint16_t *)0177566))
+    
+    void
+    putch(uint16_t c)
+    {
+        while((XCSR && 0200) == 0) continue;
+        XBUF = c;
+    }
+    
+    void
+    print_string(const char * string)
+    {
+        while (*string != '\0') {
+            putch(*string);
+            string++;
+        }
+    }
+    
+    void
+    cstart(void)
+    {
+            volatile uint16_t * test_word = (volatile uint16_t *) 0140000;
+            *test_word = 0123456;
+    
+            print_string("Hello, World!\r\n");
+    }
+
+**`bootstrap.s`:**
+
+    .globl _start
+    
+    _start:
+        mov $01000,sp
+        jsr pc,_cstart
+        halt
+
+**`pdp11.ld`:**
+
+    OUTPUT_FORMAT("a.out-pdp11")
+    ENTRY(start)
+    phys = 00001000;
+    SECTIONS
+    {
+      .text phys : AT(phys) {
+        code = .;
+        *(.text)
+        *(.rodata)
+        . = ALIGN(0100);
+      }
+      .data : AT(phys + (data - code))
+      {
+        data = .;
+        *(.data)
+        . = ALIGN(0100);
+      }
+      .bss : AT(phys + (bss - code))
+      {
+        bss = .;
+        *(.bss)
+        . = ALIGN(0100);
+      }
+      end = .;
+    }
+
+**`simh.conf`:**
+
+    load program.pdp11
+    go 1000
+
+Note that the program writes the value `0123456` to address `0140000` and then
+prints the string `Hello, World!` to the console SLU before halting.
+
+We can compile and execute this program with the following sequence of
+commands. Note that after the program halts, we are able to examine address
+`0140000` and see the value `0123456` written by the program.
+
+    % pdp11-aout-as -o bootstrap.o bootstrap.s
+    % pdp11-aout-gcc -c -Wall -Wno-unused-function -O0 -ffreestanding \
+        -fomit-frame-pointer -fno-builtin-alloca -std=c99 -o program.o program.c
+    % pdp11-aout-ld -T pdp11.ld --entry _start bootstrap.o program.o -o program.out
+    % pdp11-aout-objcopy --only-section=.text --output-target binary program.out program.bin
+    % bin2load -i program.bin -o program.pdp11 -a 01000
+    % pdp11 simh.conf
+    Paper tape will load at address 01000.
+    
+    PDP-11 simulator V3.9-0
+    Hello, World!
+    HALT instruction, PC: 001012 (BR 1016)
+    sim> examine 0140000
+    140000: 123456
+    sim> quit
+    Goodbye
+
+TADA! Now you can test your programs on the simulator as you write them.
+
+
+# Beyond Basics #
+
+SIMH includes many features beyond the basics shown in this document. The
+curious user may enter `help` at the `sim>` prompt for more information.
+
+However, before leaving the topic of testing code on simulated PDP-11s, I want
+to mention two simulated pieces of hardware provided by SIMH that can be of use
+in this task.
+
+
+## Line Printer ##
+
+The DEC LP11 line printer is emulated by SIMH and its output is saved to a text
+file during the simulation. To save output as `line_printer.txt`, execute the
+following command in SIMH.
+
+    sim> attach lpt line_printer.txt
+
+The DEC LP11 is controlled by two registers, the Line Printer Status register
+(`LPS`) located at `0177514` and the Line Printer Data Buffer register (`LPDB`)
+at `0177516`. To use the LP11, simply test bit 7 of `LPS` for printer readiness
+and then write a 7-bit character into the low bits of `LPDB`.
+
+For example, you could use something like the following C code to print from
+the simulation to the printer text file.
+
+    #define LPS  (*((volatile uint16_t *)0177514))
+    #define LPDB (*((volatile uint16_t *)0177516))
+    
+    void
+    putch(uint16_t c)
+    {
+        /* Test bit 7 (aka: 0200) of LPS for readiness. */
+        while((LPS && 0200) == 0) continue;
+        /* Transfer a byte when ready. */
+        LPDB = c;
+    }
+
+This provides an easy method for your PDP-11 program to output data to the host
+which is automatically saved, all while requiring minimal code be added to the
+PDP-11's program.
+
+
+## Serial via Telnet ##
+
+The DEC DC11 asynchronous line interface is used between the PDP-11 and a
+serial asynch line. Via SIMH, these lines can be redirected to TCP ports which
+are accessible via `telnet`. For example, to enable eight separate lines in
+SIMH and listen on port `1170`, type the following at the `sim>` prompt or add
+to your SIMH configuration file.
+
+    sim> set dci en
+    sim> set dci lines=8
+    sim> set dci 1170
+
+Now you can `telnet` to port `1170` and SIMH will redirect your connection to
+the first available line of the simulated DC11.
+
+Inside the simulation, interacting with a DC11 is fairly simple.
+
+Four registers are associated with each serial line. The first is at addresses
+`0177400`-`0177406`, the next at `0177410`-`0177416`, etc. Within a block of
+four words, the registers are assigned as follows.
+
+  - `1774xx0`: Receiver Status (`RCSR`)
+  - `1774xx2`: Receiver Buffer (`RBUF`)
+  - `1774xx4`: Transmitter Status (`XCSR`)
+  - `1774xx6`: Transmitter Buffer (`XBUF`)
+
+Your program should test bit 7 of the `RCSR` and `XCSR` registers to determine
+when the serial line has received a byte and is ready for it to be read, or the
+serial line is ready to transmit a byte. When ready, transfer a byte to the
+lower half of `XBUF` or from the lower half of `RBUF`.
+
+The code for these operations should look just like the code given above for the
+LP11 line printer.
+
+For a full description of the programming interface of the DC11, see the 
+[PDP-11 Peripherals Handbook](http://www.bitsavers.org/pdf/dec/pdp11/handbooks/PDP11_PeripheralsHbk_1972.pdf)
+starting on page 109.
+
+Using these simulated serial lines, you can easily interact with your PDP-11
+program via TCP ports, either manually using a `telnet` program, or
+programmatically. This can be a powerful debugging tool.
+
diff --git a/data/development/pdp-11/modern_c_software_development/pdp11-simulator-simh.metadata b/data/development/pdp-11/modern_c_software_development/pdp11-simulator-simh.metadata
new file mode 100644 (file)
index 0000000..9eba239
--- /dev/null
@@ -0,0 +1,6 @@
+[DEFAULT]
+page_title = PDP-11 Simulation - Executing baremetal C code on simulated PDP-11 hardware
+meta_keywords = 
+meta_description = 
+menu_text = Simulation
+menu_priority = 6000