From dc7591ac7faf4ae02919193f7aa86e632f444ed2 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Wed, 11 Nov 2020 04:43:30 -0800 Subject: [PATCH] Initial commit. `pdp11-serial-loader` program with corresponding recipient program to run on PDP-11. Use for bootstrapping bare PDP-11s with code. --- LICENSE.txt | 21 +++++++++ Makefile | 23 ++++++++++ README.md | 84 +++++++++++++++++++++++++++++++++++ client.md | 45 +++++++++++++++++++ server.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 296 insertions(+) create mode 100644 LICENSE.txt create mode 100644 Makefile create mode 100644 README.md create mode 100644 client.md create mode 100644 server.c diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..7c29737 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT/X Consortium License + +© 2020 Aaron Taylor + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b62549d --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +# © 2017 Aaron Taylor +# See LICENSE file for copyright and license details. + +PREFIX = $(HOME) +BINPREFIX = $(PREFIX)/bin + +CC = cc +CC_FLAGS += -Wall -std=c99 + +all: pdp11-serial-loader + +pdp11-serial-loader: + @$(CC) $(CC_FLAGS) -o $@ server.c + +install: all + @mkdir -p $(BINPREFIX) + @cp pdp11-serial-loader $(BINPREFIX)/pdp11-serial-loader + +uninstall: + @rm -f $(BINPREFIX)/pdp11-serial-loader + +clean: + @rm -f pdp11-serial-loader pdp11-serial-loader.core diff --git a/README.md b/README.md new file mode 100644 index 0000000..d44c23a --- /dev/null +++ b/README.md @@ -0,0 +1,84 @@ +# Overview # + +The programs contained in this directoy allow bootstrapping of a completely +blank PDP-11 from an external host. It is not required that the PDP-11 have any +pre-existing code in the form of drives or ROMs. + +# Status # + +The client has been tested with a PDP-11/23+ (KDF11-B on SLU2) and a PDP-11/73 +(KDJ11-B with DLV11-J). It should work with most PDP-11 CPUs and SLUs which are +compatible with the DLV11-J register layout. + +The server has been tested with a PC running FreeBSD. + +# Instructions # + +These instructions assume your PDP-11 has a serial connection to a UNIX host, +the `bootstrap_server.c` program has been compiled on the UNIX host, and the +UNIX host has a binary image ready for transfer to the PDP-11. + +Begin by loading the program contained in `client.txt` into the PDP-11 via the +frontpanel switches or ODT. Set the PDP-11's CPU registers as follows. + + - **R0:** The starting memory address at which the binary image should be stored. + + - **R1:** The number of bytes in the binary image. This information will be displayed + by `server.c` immediately prior to transfer. + + - **R2:** The base (`RCSR`) register address of the SLU connected to the UNIX host. + + - **R3:** The address to which control should be transferred after the binary image + is transferred to the PDP-11. + +Begin execution on the PDP-11. If the bootstrap program was loaded at the +memory addresses listed in `client.txt`, begin execution at address `70000`. + +Initiate the transfer from the UNIX host with the command + + pdp11-serial-loader -i -o + +where `` is the path to the device file for the serial port connected to +the PDP-11 and `` is the path to the binary image file. For example, if +using the first serial port under FreeBSD and a binary image named `pdp11.bin`, +execute this command. + + pdp11-serial-loader -i pdp11.bin -o /dev/cuau0 + +In FreeBSD, add your user to the `dialer` group for permission to access the +serial port. + + pw groupmod dialer -m ataylor + exec su -l ataylor + +Once the binary image has transferred to the PDP-11, it will begin execution at +the address contained in `R3` which should be your code. + +# Binary Image Format # + +Since the bootstrap program must be manually toggled into the PDP-11, brevity +is paramount. Thus, the binary image matches the little-endian byte ordering of +the PDP-11 word. + +Consider the following example program. + + CLR R0 + MOV R0 R1 + +This would correspond to a binary image with consecutive bytes of: + +| Byte | Value | +| ---- | -------- | +| 0 | 00000000 | +| 1 | 00001010 | +| 2 | 00000001 | +| 3 | 00010000 | + +If loaded into memory starting at location `2000`, the PDP-11's memory would +look like this: + +| Address | Value | +| ------- | ---------------- | +| 2000 | 0000101000000000 | +| 2002 | 0001000000000001 | + diff --git a/client.md b/client.md new file mode 100644 index 0000000..60673ec --- /dev/null +++ b/client.md @@ -0,0 +1,45 @@ +© 2017 Aaron Taylor +See LICENSE.txt file for copyright and license details. + +This is a loader for the PDP-11. It is designed to work in conjunction with the +`server.c` program, also found in this directory. + +**Notes:** + + - Configure the loader by setting the registers to different values. + + - Program is position independent. Addresses are only examples. + + - All addresses and values are octal. + + +| Address | Value | Instruction | Description | +| ------- | ------ | ---------------- | ---------------------------------------------------------- | +| R0 | 1000 | | Starting memory address to store transferred data | +| R1 | xxxx | | Number of bytes to transfer (displayed by server.c) | +| R2 | 176540 | | Address of RCSR register for SLU | +| R3 | 1000 | | Address for program execution after transfer is complete | +| 70000 | 105712 | TSTB (R2) | Set N=1 in PSW when SLU contains received byte | +| 70002 | 100376 | BPL -> 70000 | Loop until SLU has another byte ready to read | +| 70004 | 116220 | MOVB X(R2) (R0)+ | Copy byte from SLU to memory and increment location | +| 70006 | 2 | X=2 | RCSR + 2 = RBUF | +| 70010 | 005301 | DEC R1 | Decrement number of bytes remaining to receive | +| 70012 | 005701 | TST R1 | Set Z=1 in PSW after receiving last byte | +| 70014 | 001371 | BNE -> 70000 | Loop until all bytes are transferred | +| 70016 | 010307 | MOV R3 R7 | Set new PC and begin execution of new instructions | + +If you suspect data transfer problems, the following program will generate a checksum which +should match that displayed by server.c during the transfer. Remember to halt after loading +the image so it remains unmodified. + +| Address | Value | Instruction | Description | +| ------- | ------ | ---------------- | ---------------------------------------------------------- | +| R0 | 1000 | | Starting memory address | +| R4 | xxxx | | Number of words (not bytes!) to check | +| R5 | 0 | | Checksum will be here after program terminates | +| 71000 | 062005 | ADD (R0)+ R5 | Add word to checksum | +| 71002 | 005304 | DEC R4 | Decrement number of words to check | +| 71004 | 005704 | TST R4 | Set Z=1 in PSW after checking last word | +| 71006 | 001374 | BNE -> 71000 | Loop until all words are checked | +| 71010 | 000000 | HALT | Halt so user can examine checksum in R5 | + diff --git a/server.c b/server.c new file mode 100644 index 0000000..8543292 --- /dev/null +++ b/server.c @@ -0,0 +1,123 @@ +/* + * © 2017 Aaron Taylor + * See LICENSE.txt file for copyright and license details. + */ + +#include +#include +#include +#include +#include +#include + +#define VER_MAJOR 1 +#define VER_MINOR 0 + +void print_usage( char ** argv ) { + printf( "PDP-11 Serial Loader %d.%d (www.subgeniuskitty.com)\n" + "Usage: %s [option] ...\n" + "Options:\n" + " -h Help (prints this message)\n" + " -i Path to input file (raw binary image)\n" + " -o Path to serial port (/dev/cuau0 for first port on FreeBSD)\n" + , VER_MAJOR, VER_MINOR, argv[0] + ); +} + +int main( int argc, char ** argv ) { + /* + * Variable declarations + */ + int serial_fd; + FILE * binary_fd; + uint16_t bytes = 0; + struct termios options; + char * device, * binary_file; + + /* + * Process command line arguments + */ + int c; + while((c = getopt(argc,argv,"hi:o:")) != -1) { + switch(c) { + case 'h': + print_usage(argv); + exit(EXIT_SUCCESS); + break; + case 'i': + binary_file = optarg; + break; + case 'o': + device = optarg; + break; + default: + break; + } + } + + /* + * Process the binary image file + */ + if((binary_fd = fopen(binary_file,"r")) < 0) { + fprintf(stderr, "Failed to open %s\n", binary_file); + exit(EXIT_FAILURE); + } + + fseek(binary_fd, 0L, SEEK_END); + bytes = (uint16_t) ftell(binary_fd); + rewind(binary_fd); + + uint16_t * buffer = malloc(bytes); + fread(buffer, 1, bytes, binary_fd); + fclose(binary_fd); + + /* + * Open and configure the serial port + */ + if((serial_fd = open(device, O_RDWR | O_NOCTTY)) < 0) { + fprintf(stderr, "Failed to open %s\n", device); + exit(EXIT_FAILURE); + } + + tcgetattr(serial_fd, &options); + + options.c_cflag &= ~CSIZE; /* Mask the character size bits */ + options.c_cflag |= CS8; /* Select 8 data bits */ + options.c_cflag &= ~PARENB; /* Select no parity */ + options.c_cflag &= ~CSTOPB; /* Select 1 stop bit */ + options.c_oflag &= ~OPOST; /* Select raw output */ + options.c_cflag |= CLOCAL; /* CLOCAL -> Succeed on open() even w/o connection */ + options.c_cflag |= CREAD; /* CREAD -> Enable receiver */ + + tcsetattr(serial_fd, TCSANOW, &options); /* TCSANOW -> The change occurs immediately */ + + cfsetspeed(&options, B9600); + + /* + * Sanity check with user + */ + uint16_t checksum = 0; + uint16_t words = bytes / 2; + for(int i = 0; i < words; i++) checksum += buffer[i]; + printf("Checksum: %o\n", checksum); + printf("Bytes (for R1): %o\n", bytes); + printf("Press ENTER to begin transmission."); + int k; + if((k = getchar()) != 012) { /* Only continue on ENTER (LF -> 012) */ + close(serial_fd); + exit(EXIT_FAILURE); + } + + /* + * Begin transfer + */ + write(serial_fd, buffer, bytes); + + /* + * Clean up and GTFO + */ + free(buffer); + close(serial_fd); + + exit(EXIT_SUCCESS); +} -- 2.20.1