Added page describing use of SIMH to simulate a PDP-11 for running code compiled...
[website_subgeniuskitty.com] / data / development / pdp-11 / modern_c_software_development / pdp11-simulator-simh.md
CommitLineData
477e344c
AT
1# Overview #
2
3TODO: Write introduction. Goal is to run baremetal PDP-11 programs in SIMH, a
4PDP-11 simulator.
5
6TODO: What kind of joint header do I want across all the articles in a set,
7linking them together?
8
9# SIMH Installation #
10
11We first need to install [SIMH](https://github.com/simh/simh), a program that
12simulates many computer architectures including the DEC PDP-11.
13
14 - **FreeBSD:** Either install package via `pkg install simh` or build from
15 ports under `emulators/simh`.
16
17 - **Debian:** Install as package via `sudo apt install simh`.
18
19 - **Windows:** Download [pre-built snapshot binaries](https://github.com/simh/Win32-Development-Binaries).
20
21 - **Other:** Read the [SIMH README](https://github.com/simh/simh) section
22 "Building Simulators Yourself".
23
24After installation, launch the PDP-11 simulator with the command `pdp11`. It
25should display a prompt `sim>` where you can type `quit` followed by `ENTER` to
26exit the simulator.
27
28 % pdp11
29
30 PDP-11 simulator V3.9-0
31 sim> quit
32 Goodbye
33 %
34
35
36# SIMH Basics #
37
38Now that SIMH is installed, go ahead and launch it again with the command
39`pdp11`. This should result in the prompt `sim>`. All commands entered at this
40`sim>` prompt are executed by the SIMH simulator itself, not by the simulated
41PDP-11. For example, we could display the configuration of the simulated
42PDP-11 defined in SIMH.
43
44 sim> show configuration
45 PDP-11 simulator configuration
46
47 CPU, 11/73, NOCIS, idle disabled, autoconfiguration enabled, 256KB SYSTEM
48 [...]
49
50We can change the simulated PDP-11, for example by changing to a different
51model.
52
53 sim> set cpu 11/40
54 Disabling XQ
55 sim> show configuration
56 PDP-11 simulator configuration
57
58 CPU, 11/40, NOFIS, idle disabled, autoconfiguration enabled, 256KB SYSTEM
59 [...]
60
61SIMH also allows us to deposit values directly into memory and read them back.
62Note that addresses in SIMH refer to the physical address rather than the
63virtual address, and thus may be up to 22-bits long on certain models, as shown
64below.
65
66 sim> examine 0140000
67 140000: 000000
68 sim> deposit 0140000 042
69 sim> examine 0140000
70 140000: 000042
71 sim>
72
73Once a program is loaded into memory, the simulated PDP-11 may be commanded to
74execute it via the command `go <address>`. For example, if the program was
75loaded starting at address `01000`, then begin execution with `go 01000`.
76
77
78## Interrupting Simulation ##
79
80At any time, the simulation may be paused by pressing `Ctrl-e`. This returns to
81the `sim>` prompt where memory may be examined or other extra-sim capabilities
82executed. For example, if we have the PDP-11 CPU execute a tight infinite loop,
83the simulation never naturally ends, but we can pause it, allowing us to exit.
84
85 sim> go
86 <Press Ctrl-e>
87 Simulation stopped, PC: 004166 (BR 4166)
88 sim> quit
89 Goodbye
90 %
91
92Note that, instead of `quit`, typing `go` would have resumed the simulation
93exactly where it paused, shown in the status message where the `PC` register is
94set to address `04166`.
95
96
97## Saving Configuration ##
98
99Any command that may be entered at the `sim>` prompt, may also be entered in a
100configuration file. For example, consider the following file named `simh.conf`.
101
102 deposit 01000 0777
103 echo Just set address 01000 to the instruction 'BRANCH 01000'.
104 go 01000
105
106We can tell SIMH to load this file and execute each line as though it were
107typed at the `sim>` prompt by including the config file name. Even though the
108commands are not displayed, we can see from the `echo` command that they were
109executed.
110
111 % pdp11 simh.conf
112
113 PDP-11 simulator V3.9-0
114 Just set address 01000 to the instruction 'BRANCH 01000'.
115
116 Simulation stopped, PC: 001000 (BR 1000)
117 sim> quit
118 Goodbye
119
120Since this program is an infinite loop, we pressed `Ctrl-e` to pause the
121simulation and allow us to quit.
122
123
124# SIMH Loader #
125
126Since we're trying to run bare-metal code on this simulated PDP-11, we don't
127need to bother with disk images; instead we will load a binary directly into
128the PDP-11's memory using SIMH's `load` command.
129
130
131## Loader Format ##
132
133The loader included with SIMH doesn't accept a raw binary file or the a.out
134executable generated by our cross compiler. Instead, it expects a paper tape
135image file in the following format.
136
137
138 Loader format consists of blocks, optionally preceded, separated, and
139 followed by zeroes. Each block consists of the following entries. Note
140 that all entries are one byte.
141
142 0001
143 0000
144 Low byte of block length (data byte count + 6 for header, excludes checksum)
145 High byte of block length
146 Low byte of load address
147 High byte of load address
148 Data byte 0
149 ...
150 Data byte N
151 Checksum
152
153 The 8-bit checksum for a block is the twos-complement of the lower eight
154 sum bits for all six header bytes and all data bytes.
155
156 If the block length is exactly six bytes (i.e. only header, no data),
157 then the block marks the end-of-tape. The checksum should be zero. If
158 the load address of this final block is not 000001, then it is used as
159 the starting PC.
160
161If you don't want to generate this format yourself, use the utility
162[bin2load](https://git.subgeniuskitty.com/pdp11-bin2load/.git) which converts a
163binary image into a SIMH compatible loader format. See the `README.md` file for
164installation instructions.
165
166
167## Load and Execute ##
168
169We can extract a binary from the a.out file generated by our cross compiler
170using the `pdp11-aout-objcopy` tool built at the same time as the cross
171compiler. This binary can then be converted by `bin2load` and loaded into SIMH.
172
173 % pdp11-aout-objcopy --only-section=.text --output-target binary program.out program.bin
174 % bin2load -i program.bin -o program.pdp11 -a 01000
175 % pdp11
176
177 PDP-11 simulator V3.9-0
178 sim> load program.pdp11
179 sim> go
180
181If we pass a starting address to `bin2load` with the `-a` flag (as shown
182above), then SIMH configures the simulation so that execution starts at address
183`01000` when the `go` command is entered.
184
185The `load <file>` and `go` command may also be included in the SIMH
186configuration file, automatically loading and executing whenever the simulator
187is started.
188
189
190# Execution #
191
192Let's bring all these steps together. Assume we've built our cross compiler and
193created a `Hello, World!` program like the one shown in the four files below.
194
195**`program.c`:**
196
197 #include <stdint.h>
198
199 #define XCSR (*((volatile uint16_t *)0177564))
200 #define XBUF (*((volatile uint16_t *)0177566))
201
202 void
203 putch(uint16_t c)
204 {
205 while((XCSR && 0200) == 0) continue;
206 XBUF = c;
207 }
208
209 void
210 print_string(const char * string)
211 {
212 while (*string != '\0') {
213 putch(*string);
214 string++;
215 }
216 }
217
218 void
219 cstart(void)
220 {
221 volatile uint16_t * test_word = (volatile uint16_t *) 0140000;
222 *test_word = 0123456;
223
224 print_string("Hello, World!\r\n");
225 }
226
227**`bootstrap.s`:**
228
229 .globl _start
230
231 _start:
232 mov $01000,sp
233 jsr pc,_cstart
234 halt
235
236**`pdp11.ld`:**
237
238 OUTPUT_FORMAT("a.out-pdp11")
239 ENTRY(start)
240 phys = 00001000;
241 SECTIONS
242 {
243 .text phys : AT(phys) {
244 code = .;
245 *(.text)
246 *(.rodata)
247 . = ALIGN(0100);
248 }
249 .data : AT(phys + (data - code))
250 {
251 data = .;
252 *(.data)
253 . = ALIGN(0100);
254 }
255 .bss : AT(phys + (bss - code))
256 {
257 bss = .;
258 *(.bss)
259 . = ALIGN(0100);
260 }
261 end = .;
262 }
263
264**`simh.conf`:**
265
266 load program.pdp11
267 go 1000
268
269Note that the program writes the value `0123456` to address `0140000` and then
270prints the string `Hello, World!` to the console SLU before halting.
271
272We can compile and execute this program with the following sequence of
273commands. Note that after the program halts, we are able to examine address
274`0140000` and see the value `0123456` written by the program.
275
276 % pdp11-aout-as -o bootstrap.o bootstrap.s
277 % pdp11-aout-gcc -c -Wall -Wno-unused-function -O0 -ffreestanding \
278 -fomit-frame-pointer -fno-builtin-alloca -std=c99 -o program.o program.c
279 % pdp11-aout-ld -T pdp11.ld --entry _start bootstrap.o program.o -o program.out
280 % pdp11-aout-objcopy --only-section=.text --output-target binary program.out program.bin
281 % bin2load -i program.bin -o program.pdp11 -a 01000
282 % pdp11 simh.conf
283 Paper tape will load at address 01000.
284
285 PDP-11 simulator V3.9-0
286 Hello, World!
287 HALT instruction, PC: 001012 (BR 1016)
288 sim> examine 0140000
289 140000: 123456
290 sim> quit
291 Goodbye
292
293TADA! Now you can test your programs on the simulator as you write them.
294
295
296# Beyond Basics #
297
298SIMH includes many features beyond the basics shown in this document. The
299curious user may enter `help` at the `sim>` prompt for more information.
300
301However, before leaving the topic of testing code on simulated PDP-11s, I want
302to mention two simulated pieces of hardware provided by SIMH that can be of use
303in this task.
304
305
306## Line Printer ##
307
308The DEC LP11 line printer is emulated by SIMH and its output is saved to a text
309file during the simulation. To save output as `line_printer.txt`, execute the
310following command in SIMH.
311
312 sim> attach lpt line_printer.txt
313
314The DEC LP11 is controlled by two registers, the Line Printer Status register
315(`LPS`) located at `0177514` and the Line Printer Data Buffer register (`LPDB`)
316at `0177516`. To use the LP11, simply test bit 7 of `LPS` for printer readiness
317and then write a 7-bit character into the low bits of `LPDB`.
318
319For example, you could use something like the following C code to print from
320the simulation to the printer text file.
321
322 #define LPS (*((volatile uint16_t *)0177514))
323 #define LPDB (*((volatile uint16_t *)0177516))
324
325 void
326 putch(uint16_t c)
327 {
328 /* Test bit 7 (aka: 0200) of LPS for readiness. */
329 while((LPS && 0200) == 0) continue;
330 /* Transfer a byte when ready. */
331 LPDB = c;
332 }
333
334This provides an easy method for your PDP-11 program to output data to the host
335which is automatically saved, all while requiring minimal code be added to the
336PDP-11's program.
337
338
339## Serial via Telnet ##
340
341The DEC DC11 asynchronous line interface is used between the PDP-11 and a
342serial asynch line. Via SIMH, these lines can be redirected to TCP ports which
343are accessible via `telnet`. For example, to enable eight separate lines in
344SIMH and listen on port `1170`, type the following at the `sim>` prompt or add
345to your SIMH configuration file.
346
347 sim> set dci en
348 sim> set dci lines=8
349 sim> set dci 1170
350
351Now you can `telnet` to port `1170` and SIMH will redirect your connection to
352the first available line of the simulated DC11.
353
354Inside the simulation, interacting with a DC11 is fairly simple.
355
356Four registers are associated with each serial line. The first is at addresses
357`0177400`-`0177406`, the next at `0177410`-`0177416`, etc. Within a block of
358four words, the registers are assigned as follows.
359
360 - `1774xx0`: Receiver Status (`RCSR`)
361 - `1774xx2`: Receiver Buffer (`RBUF`)
362 - `1774xx4`: Transmitter Status (`XCSR`)
363 - `1774xx6`: Transmitter Buffer (`XBUF`)
364
365Your program should test bit 7 of the `RCSR` and `XCSR` registers to determine
366when the serial line has received a byte and is ready for it to be read, or the
367serial line is ready to transmit a byte. When ready, transfer a byte to the
368lower half of `XBUF` or from the lower half of `RBUF`.
369
370The code for these operations should look just like the code given above for the
371LP11 line printer.
372
373For a full description of the programming interface of the DC11, see the
374[PDP-11 Peripherals Handbook](http://www.bitsavers.org/pdf/dec/pdp11/handbooks/PDP11_PeripheralsHbk_1972.pdf)
375starting on page 109.
376
377Using these simulated serial lines, you can easily interact with your PDP-11
378program via TCP ports, either manually using a `telnet` program, or
379programmatically. This can be a powerful debugging tool.
380