Initial commit. `pdp11-serial-loader` program with corresponding
authorAaron Taylor <ataylor@subgeniuskitty.com>
Wed, 11 Nov 2020 12:43:30 +0000 (04:43 -0800)
committerAaron Taylor <ataylor@subgeniuskitty.com>
Wed, 11 Nov 2020 12:43:30 +0000 (04:43 -0800)
recipient program to run on PDP-11. Use for bootstrapping bare
PDP-11s with code.

LICENSE.txt [new file with mode: 0644]
Makefile [new file with mode: 0644]
README.md [new file with mode: 0644]
client.md [new file with mode: 0644]
server.c [new file with mode: 0644]

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..7c29737
--- /dev/null
@@ -0,0 +1,21 @@
+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.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..b62549d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,23 @@
+# © 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
diff --git a/README.md b/README.md
new file mode 100644 (file)
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 <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 |
+
diff --git a/client.md b/client.md
new file mode 100644 (file)
index 0000000..60673ec
--- /dev/null
+++ b/client.md
@@ -0,0 +1,45 @@
+© 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                    |
+
diff --git a/server.c b/server.c
new file mode 100644 (file)
index 0000000..8543292
--- /dev/null
+++ b/server.c
@@ -0,0 +1,123 @@
+/*
+ * © 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);
+}