Minor updates to new page on SIMH PDP-11 simulation.
[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
e5f02f11 86 <Simulation runs until user presses Ctrl-e.>
477e344c
AT
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'.
e5f02f11 115 <Simulation runs until user presses Ctrl-e.>
477e344c
AT
116 Simulation stopped, PC: 001000 (BR 1000)
117 sim> quit
118 Goodbye
119
e5f02f11 120Because this program is an infinite loop, we pressed `Ctrl-e` to pause the
477e344c
AT
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
e5f02f11
AT
127need to bother with disk images; we will load a binary directly into the
128PDP-11's memory using SIMH's `load` command.
477e344c
AT
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
e5f02f11
AT
164installation instructions. For example:
165
166 $ git clone git://git.subgeniuskitty.com/pdp11-bin2load
167 $ cd pdp11-bin2load
168 $ make install
169 $ export PATH=$HOME/bin:$PATH
170 $ bin2load -h
171 bin2load v2 (www.subgeniuskitty.com)
172 Usage: bin2load -i <file> -o <file> [-a <address>]
173 -i <file> Raw binary file to be written to tape.
174 For example, output from 'pdp11-aout-objdump' (see README.md).
175 -o <file> Output file created by bin2load containing tape image for use with SIMH.
176 -a <address> (optional) Address on PDP-11 at which to load tape contents.
477e344c
AT
177
178
179## Load and Execute ##
180
181We can extract a binary from the a.out file generated by our cross compiler
182using the `pdp11-aout-objcopy` tool built at the same time as the cross
183compiler. This binary can then be converted by `bin2load` and loaded into SIMH.
184
185 % pdp11-aout-objcopy --only-section=.text --output-target binary program.out program.bin
186 % bin2load -i program.bin -o program.pdp11 -a 01000
187 % pdp11
188
189 PDP-11 simulator V3.9-0
190 sim> load program.pdp11
191 sim> go
192
193If we pass a starting address to `bin2load` with the `-a` flag (as shown
e5f02f11
AT
194above), then SIMH configures the simulation to begin execution at the passed
195address.
477e344c
AT
196
197The `load <file>` and `go` command may also be included in the SIMH
198configuration file, automatically loading and executing whenever the simulator
199is started.
200
201
202# Execution #
203
204Let's bring all these steps together. Assume we've built our cross compiler and
205created a `Hello, World!` program like the one shown in the four files below.
206
207**`program.c`:**
208
209 #include <stdint.h>
210
211 #define XCSR (*((volatile uint16_t *)0177564))
212 #define XBUF (*((volatile uint16_t *)0177566))
213
214 void
215 putch(uint16_t c)
216 {
217 while((XCSR && 0200) == 0) continue;
218 XBUF = c;
219 }
220
221 void
222 print_string(const char * string)
223 {
224 while (*string != '\0') {
225 putch(*string);
226 string++;
227 }
228 }
229
230 void
231 cstart(void)
232 {
233 volatile uint16_t * test_word = (volatile uint16_t *) 0140000;
234 *test_word = 0123456;
235
236 print_string("Hello, World!\r\n");
237 }
238
239**`bootstrap.s`:**
240
241 .globl _start
242
243 _start:
244 mov $01000,sp
245 jsr pc,_cstart
246 halt
247
248**`pdp11.ld`:**
249
250 OUTPUT_FORMAT("a.out-pdp11")
251 ENTRY(start)
252 phys = 00001000;
253 SECTIONS
254 {
255 .text phys : AT(phys) {
256 code = .;
257 *(.text)
258 *(.rodata)
259 . = ALIGN(0100);
260 }
261 .data : AT(phys + (data - code))
262 {
263 data = .;
264 *(.data)
265 . = ALIGN(0100);
266 }
267 .bss : AT(phys + (bss - code))
268 {
269 bss = .;
270 *(.bss)
271 . = ALIGN(0100);
272 }
273 end = .;
274 }
275
276**`simh.conf`:**
277
278 load program.pdp11
279 go 1000
280
281Note that the program writes the value `0123456` to address `0140000` and then
282prints the string `Hello, World!` to the console SLU before halting.
283
284We can compile and execute this program with the following sequence of
e5f02f11
AT
285commands. After the program halts, we are able to examine address `0140000` and
286see the value `0123456` written by the program.
477e344c
AT
287
288 % pdp11-aout-as -o bootstrap.o bootstrap.s
289 % pdp11-aout-gcc -c -Wall -Wno-unused-function -O0 -ffreestanding \
290 -fomit-frame-pointer -fno-builtin-alloca -std=c99 -o program.o program.c
291 % pdp11-aout-ld -T pdp11.ld --entry _start bootstrap.o program.o -o program.out
292 % pdp11-aout-objcopy --only-section=.text --output-target binary program.out program.bin
293 % bin2load -i program.bin -o program.pdp11 -a 01000
294 % pdp11 simh.conf
295 Paper tape will load at address 01000.
296
297 PDP-11 simulator V3.9-0
298 Hello, World!
299 HALT instruction, PC: 001012 (BR 1016)
300 sim> examine 0140000
301 140000: 123456
302 sim> quit
303 Goodbye
304
305TADA! Now you can test your programs on the simulator as you write them.
306
307
308# Beyond Basics #
309
310SIMH includes many features beyond the basics shown in this document. The
311curious user may enter `help` at the `sim>` prompt for more information.
312
313However, before leaving the topic of testing code on simulated PDP-11s, I want
314to mention two simulated pieces of hardware provided by SIMH that can be of use
315in this task.
316
317
318## Line Printer ##
319
e5f02f11 320The DEC LP11 line printer is simulated by SIMH and its output saved to a text
477e344c
AT
321file during the simulation. To save output as `line_printer.txt`, execute the
322following command in SIMH.
323
324 sim> attach lpt line_printer.txt
325
326The DEC LP11 is controlled by two registers, the Line Printer Status register
327(`LPS`) located at `0177514` and the Line Printer Data Buffer register (`LPDB`)
328at `0177516`. To use the LP11, simply test bit 7 of `LPS` for printer readiness
329and then write a 7-bit character into the low bits of `LPDB`.
330
331For example, you could use something like the following C code to print from
332the simulation to the printer text file.
333
334 #define LPS (*((volatile uint16_t *)0177514))
335 #define LPDB (*((volatile uint16_t *)0177516))
336
337 void
338 putch(uint16_t c)
339 {
340 /* Test bit 7 (aka: 0200) of LPS for readiness. */
341 while((LPS && 0200) == 0) continue;
342 /* Transfer a byte when ready. */
343 LPDB = c;
344 }
345
346This provides an easy method for your PDP-11 program to output data to the host
347which is automatically saved, all while requiring minimal code be added to the
348PDP-11's program.
349
350
351## Serial via Telnet ##
352
353The DEC DC11 asynchronous line interface is used between the PDP-11 and a
354serial asynch line. Via SIMH, these lines can be redirected to TCP ports which
355are accessible via `telnet`. For example, to enable eight separate lines in
356SIMH and listen on port `1170`, type the following at the `sim>` prompt or add
357to your SIMH configuration file.
358
359 sim> set dci en
360 sim> set dci lines=8
361 sim> set dci 1170
362
363Now you can `telnet` to port `1170` and SIMH will redirect your connection to
364the first available line of the simulated DC11.
365
366Inside the simulation, interacting with a DC11 is fairly simple.
367
368Four registers are associated with each serial line. The first is at addresses
369`0177400`-`0177406`, the next at `0177410`-`0177416`, etc. Within a block of
370four words, the registers are assigned as follows.
371
372 - `1774xx0`: Receiver Status (`RCSR`)
373 - `1774xx2`: Receiver Buffer (`RBUF`)
374 - `1774xx4`: Transmitter Status (`XCSR`)
375 - `1774xx6`: Transmitter Buffer (`XBUF`)
376
377Your program should test bit 7 of the `RCSR` and `XCSR` registers to determine
378when the serial line has received a byte and is ready for it to be read, or the
379serial line is ready to transmit a byte. When ready, transfer a byte to the
380lower half of `XBUF` or from the lower half of `RBUF`.
381
382The code for these operations should look just like the code given above for the
383LP11 line printer.
384
385For a full description of the programming interface of the DC11, see the
386[PDP-11 Peripherals Handbook](http://www.bitsavers.org/pdf/dec/pdp11/handbooks/PDP11_PeripheralsHbk_1972.pdf)
387starting on page 109.
388
389Using these simulated serial lines, you can easily interact with your PDP-11
390program via TCP ports, either manually using a `telnet` program, or
391programmatically. This can be a powerful debugging tool.
392