From 477e344cc223251d4d3c66618b270358402e01b4 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Tue, 5 Jan 2021 03:39:55 -0800 Subject: [PATCH] Added page describing use of SIMH to simulate a PDP-11 for running code compiled as part of "Modern C on PDP-11" series of guides. --- .../pdp11-simulator-simh.md | 380 ++++++++++++++++++ .../pdp11-simulator-simh.metadata | 6 + 2 files changed, 386 insertions(+) create mode 100644 data/development/pdp-11/modern_c_software_development/pdp11-simulator-simh.md create mode 100644 data/development/pdp-11/modern_c_software_development/pdp11-simulator-simh.metadata 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 index 0000000..f2d71b3 --- /dev/null +++ b/data/development/pdp-11/modern_c_software_development/pdp11-simulator-simh.md @@ -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
`. 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 + + 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 ` 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 + + #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 index 0000000..9eba239 --- /dev/null +++ b/data/development/pdp-11/modern_c_software_development/pdp11-simulator-simh.metadata @@ -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 -- 2.20.1