--- /dev/null
+MIT/X Consortium License
+
+© 2020 Aaron Taylor <ataylor at subgeniuskitty dot com>
+
+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.
--- /dev/null
+# © 2017 Aaron Taylor <ataylor at subgeniuskitty dot com>
+# 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
--- /dev/null
+# 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 <binary> -o <port>
+
+where `<port>` is the path to the device file for the serial port connected to
+the PDP-11 and `<binary>` 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 |
+
--- /dev/null
+© 2017 Aaron Taylor <ataylor at subgeniuskitty dot com>
+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 |
+
--- /dev/null
+/*
+ * © 2017 Aaron Taylor <ataylor at subgeniuskitty dot com>
+ * See LICENSE.txt file for copyright and license details.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#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 <string> Path to input file (raw binary image)\n"
+ " -o <string> 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);
+}