Imported slu/mmu/interrupt/register code from WIP memtester program into C template.
authorAaron Taylor <ataylor@subgeniuskitty.com>
Mon, 11 Jan 2021 06:22:01 +0000 (22:22 -0800)
committerAaron Taylor <ataylor@subgeniuskitty.com>
Mon, 11 Jan 2021 06:22:01 +0000 (22:22 -0800)
Makefile
pdp11/interrupt_handler.s [new file with mode: 0644]
pdp11/pdp11.c
pdp11/pdp11.h
pdp11/pdp11_interrupt.c [new file with mode: 0644]
pdp11/pdp11_mmu.c [new file with mode: 0644]
pdp11/pdp11_mmu.h [new file with mode: 0644]
pdp11/pdp11_register.h [new file with mode: 0644]
pdp11/pdp11_slu.c [new file with mode: 0644]
pdp11/pdp11_slu.h [new file with mode: 0644]

index 51c8869..f4a8632 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -33,9 +33,6 @@ PDP_LOAD        = bin2pdp
 SIMH_CMD        = pdp11
 SIMH_CFG        = simh.conf
 
 SIMH_CMD        = pdp11
 SIMH_CFG        = simh.conf
 
-# List of all source files in PDP-11 library.
-PDPLIB_SRC      != ls pdp11/*.c
-
 
 ####################################################################################################
 # Targets
 
 ####################################################################################################
 # Targets
@@ -44,14 +41,19 @@ all: aout
 
 aout: 
        $(AS) $(AS_FLAGS) -o init.o init.s
 
 aout: 
        $(AS) $(AS_FLAGS) -o init.o init.s
-       $(CC) $(CC_FLAGS) -o pdp11.o $(PDPLIB_SRC)
+       $(AS) $(AS_FLAGS) -o pdp11_inthandler.o pdp11/interrupt_handler.s
+       $(CC) $(CC_FLAGS) -o pdp11_interrupt.o pdp11/pdp11_interrupt.c
+       $(CC) $(CC_FLAGS) -o pdp11_slu.o pdp11/pdp11_slu.c
+       $(CC) $(CC_FLAGS) -o pdp11_mmu.o pdp11/pdp11_mmu.c
+       $(CC) $(CC_FLAGS) -o pdp11.o pdp11/pdp11.c
 
        ##############################################
        # Insert your program's build commands here. #
        $(CC) $(CC_FLAGS) -o hello.o hello.c
        ##############################################
 
 
        ##############################################
        # Insert your program's build commands here. #
        $(CC) $(CC_FLAGS) -o hello.o hello.c
        ##############################################
 
-       $(LD) $(LD_FLAGS) init.o pdp11.o hello.o -o program.out
+       $(LD) $(LD_FLAGS) init.o pdp11_inthandler.o pdp11_interrupt.o pdp11_slu.o pdp11_mmu.o pdp11.o \
+               hello.o -o program.out
 
 clean:
        @rm -rf *.o *.out *.pdp11 *.bin *.core
 
 clean:
        @rm -rf *.o *.out *.pdp11 *.bin *.core
diff --git a/pdp11/interrupt_handler.s b/pdp11/interrupt_handler.s
new file mode 100644 (file)
index 0000000..970363b
--- /dev/null
@@ -0,0 +1,24 @@
+# (c) 2020 Aaron Taylor <ataylor at subgeniuskitty dot com>
+# See License.txt file for copyright and license details.
+
+.globl _int4_asm_handler
+.globl _int4_c_handler
+
+_int4_asm_handler:
+    # GCC appears to be using the same calling convention documented in the
+    # original UNIX manuals. Most of these register saves are probably
+    # unnecessary.
+    mov r0,-(sp)
+    mov r1,-(sp)
+    mov r2,-(sp)
+    mov r3,-(sp)
+    mov r4,-(sp)
+    mov r5,-(sp)
+    jsr pc, _int4_c_handler
+    mov (sp)+,r5
+    mov (sp)+,r4
+    mov (sp)+,r3
+    mov (sp)+,r2
+    mov (sp)+,r1
+    mov (sp)+,r0
+    rti
index f92bad5..edfd4fa 100644 (file)
@@ -3,33 +3,6 @@
 
 #include <stdint.h>
 
 
 #include <stdint.h>
 
-/*
- * Polled console I/O
- */
-
-#define RCSR (*((volatile uint16_t *)0177560))
-#define RBUF (*((volatile uint16_t *)0177562))
-#define XCSR (*((volatile uint16_t *)0177564))
-#define XBUF (*((volatile uint16_t *)0177566))
-
-void
-putch(uint16_t c)
-{
-    while((XCSR && 0200) == 0) continue;
-    XBUF = c;
-}
-
-uint16_t
-getch(void)
-{
-    while((RCSR && 0200) == 0) continue;
-    return RBUF;
-}
-
-/*
- * Busy-loop
- */
-
 void
 wait(uint16_t count)
 {
 void
 wait(uint16_t count)
 {
index 2aff814..f8bb40c 100644 (file)
@@ -5,9 +5,10 @@
 #define SGK_PDP11_H
 
 #include <stdint.h>
 #define SGK_PDP11_H
 
 #include <stdint.h>
+#include "./pdp11_register.h"
+#include "./pdp11_mmu.h"
+#include "./pdp11_slu.h"
 
 
-void putch(uint16_t);
-uint16_t getch(void);
 void wait(uint16_t);
 
 #endif /* SGK_PDP11_H */
 void wait(uint16_t);
 
 #endif /* SGK_PDP11_H */
diff --git a/pdp11/pdp11_interrupt.c b/pdp11/pdp11_interrupt.c
new file mode 100644 (file)
index 0000000..5d563aa
--- /dev/null
@@ -0,0 +1,10 @@
+// (c) 2020 Aaron Taylor <ataylor at subgeniuskitty dot com>
+// See License.txt file for copyright and license details.
+
+#include "pdp11.h"
+
+void
+int4_c_handler(void)
+{
+    printf("\nHandling interrupt 4\n");
+}
diff --git a/pdp11/pdp11_mmu.c b/pdp11/pdp11_mmu.c
new file mode 100644 (file)
index 0000000..32d5d27
--- /dev/null
@@ -0,0 +1,53 @@
+// (c) 2020 Aaron Taylor <ataylor at subgeniuskitty dot com>
+// See License.txt file for copyright and license details.
+
+#include <stdint.h>
+#include "pdp11_register.h"
+#include "pdp11_slu.h"
+
+void
+init_mmu(void)
+{
+
+    /*
+     * First populate the relocation registers, all zero since we want an
+     * identity mapping, except the highest page that we remap to the physical MMIO
+     * range.
+     */
+    SET(KISAR0,PAR_PAF,0000000);
+    SET(KISAR1,PAR_PAF,0000000);
+    SET(KISAR2,PAR_PAF,0000000);
+    SET(KISAR3,PAR_PAF,0000000);
+    SET(KISAR4,PAR_PAF,0000000);
+    SET(KISAR5,PAR_PAF,0000000);
+    SET(KISAR6,PAR_PAF,0000000);
+    SET(KISAR7,PAR_PAF,0177600);
+
+    /*
+     * Now populate the page descriptor registers. See EK-KDJ1B-UG page 1-19
+     * for details of each field.
+     */
+    uint16_t data = 0;
+    SET(data,PDR_BYPASCACHE,0);
+    SET(data,PDR_PAGELEN,0177);
+    SET(data,PDR_PAGEWRITEN,0);
+    SET(data,PDR_EXPANDDIR,0);
+    SET(data,PDR_ACCESSCTRL,03);
+
+    KISDR0 = data;
+    KISDR1 = data;
+    KISDR2 = data;
+    KISDR3 = data;
+    KISDR4 = data;
+    KISDR5 = data;
+    KISDR6 = data;
+    KISDR7 = data;
+
+    /*
+     * Enable the MMU with a 22-bit mapping.
+     */
+    SET(MMR3,MMR3_KRNSPLTID,0);
+    SET(MMR3,MMR3_EN_22BIT,1);
+    SET(MMR0,MMR0_EN_MMU,1);
+}
+
diff --git a/pdp11/pdp11_mmu.h b/pdp11/pdp11_mmu.h
new file mode 100644 (file)
index 0000000..9280850
--- /dev/null
@@ -0,0 +1,15 @@
+// (c) 2020 Aaron Taylor <ataylor at subgeniuskitty dot com>
+// See License.txt file for copyright and license details.
+
+#ifndef SGK_PDP11_MMU_H
+#define SGK_PDP11_MMU_H
+
+/*
+ * Initializes the MMU with a 22-bit kernel-mode I-space map that relocates the
+ * MMIO page to the physical MMIO region at the top of the physical address
+ * space while leaving the lower 56kB identity mapped. Assumes the CPU is
+ * already running in kernel mode with MMU hardware disabled.
+ */
+void init_mmu(void);
+
+#endif // SGK_PDP11_MMU_H
diff --git a/pdp11/pdp11_register.h b/pdp11/pdp11_register.h
new file mode 100644 (file)
index 0000000..c3f0ce6
--- /dev/null
@@ -0,0 +1,253 @@
+// (c) 2020 Aaron Taylor <ataylor at subgeniuskitty dot com>
+// See LICENSE.txt file for copyright and license details.
+
+#ifndef SGK_PDP11_REGISTER_H
+#define SGK_PDP11_REGISTER_H
+
+
+/*
+ * =============================================================================
+ * Register Access Macros
+ * =============================================================================
+ */
+
+/*
+ * Use these macros along with the register address/field definitions in this
+ * file to get/set memory mapped registers.
+ *
+ * For example:
+ *     SET(KISDR2,PDR_PAGELEN,0100);
+ *     SET(MMR3,MMR3_EN_22BIT,1);
+ *     uint16_t value = GET(PSW,PSW_REGSET);
+ */
+
+#define GET(reg,regspec) GET_EXPANDED(reg,regspec)
+#define GET_EXPANDED(reg,mask,offset) ((reg & (mask << offset)) >> offset)
+
+#define SET(reg,regspec,value) SET_EXPANDED(reg,regspec,value)
+#define SET_EXPANDED(reg,mask,offset,value) (reg = ((reg & (~(mask << offset))) | (value << offset)))
+
+
+/*
+ * =============================================================================
+ * Register Address Definitions
+ * =============================================================================
+ */
+
+// Register names correspond to KDJ11-B User Guide (EK-KDJ1B-UG).
+// http://bitsavers.org/pdf/dec/pdp11/1173/EK-KDJ1B-UG_KDJ11-B_Nov86.pdf
+
+/* CPU */
+
+#define PSW      (*((volatile uint16_t *)0177776))
+#define PIRQ     (*((volatile uint16_t *)0177772))
+#define CPUERR   (*((volatile uint16_t *)0177766))
+
+/* MMU */
+
+#define MMR0     (*((volatile uint16_t *)0177572))
+#define MMR1     (*((volatile uint16_t *)0177574))
+#define MMR2     (*((volatile uint16_t *)0177576))
+#define MMR3     (*((volatile uint16_t *)0172516))
+
+#define UISDR0   (*((volatile uint16_t *)0177600))
+#define UISDR1   (*((volatile uint16_t *)0177602))
+#define UISDR2   (*((volatile uint16_t *)0177604))
+#define UISDR3   (*((volatile uint16_t *)0177606))
+#define UISDR4   (*((volatile uint16_t *)0177610))
+#define UISDR5   (*((volatile uint16_t *)0177612))
+#define UISDR6   (*((volatile uint16_t *)0177614))
+#define UISDR7   (*((volatile uint16_t *)0177616))
+
+#define UDSDR0   (*((volatile uint16_t *)0177620))
+#define UDSDR1   (*((volatile uint16_t *)0177622))
+#define UDSDR2   (*((volatile uint16_t *)0177624))
+#define UDSDR3   (*((volatile uint16_t *)0177626))
+#define UDSDR4   (*((volatile uint16_t *)0177630))
+#define UDSDR5   (*((volatile uint16_t *)0177632))
+#define UDSDR6   (*((volatile uint16_t *)0177634))
+#define UDSDR7   (*((volatile uint16_t *)0177636))
+
+#define UISAR0   (*((volatile uint16_t *)0177640))
+#define UISAR1   (*((volatile uint16_t *)0177642))
+#define UISAR2   (*((volatile uint16_t *)0177644))
+#define UISAR3   (*((volatile uint16_t *)0177646))
+#define UISAR4   (*((volatile uint16_t *)0177650))
+#define UISAR5   (*((volatile uint16_t *)0177652))
+#define UISAR6   (*((volatile uint16_t *)0177654))
+#define UISAR7   (*((volatile uint16_t *)0177656))
+
+#define UDSAR0   (*((volatile uint16_t *)0177660))
+#define UDSAR1   (*((volatile uint16_t *)0177662))
+#define UDSAR2   (*((volatile uint16_t *)0177664))
+#define UDSAR3   (*((volatile uint16_t *)0177666))
+#define UDSAR4   (*((volatile uint16_t *)0177670))
+#define UDSAR5   (*((volatile uint16_t *)0177672))
+#define UDSAR6   (*((volatile uint16_t *)0177674))
+#define UDSAR7   (*((volatile uint16_t *)0177676))
+
+#define SISDR0   (*((volatile uint16_t *)0172200))
+#define SISDR1   (*((volatile uint16_t *)0172202))
+#define SISDR2   (*((volatile uint16_t *)0172204))
+#define SISDR3   (*((volatile uint16_t *)0172206))
+#define SISDR4   (*((volatile uint16_t *)0172210))
+#define SISDR5   (*((volatile uint16_t *)0172212))
+#define SISDR6   (*((volatile uint16_t *)0172214))
+#define SISDR7   (*((volatile uint16_t *)0172216))
+
+#define SDSDR0   (*((volatile uint16_t *)0172220))
+#define SDSDR1   (*((volatile uint16_t *)0172222))
+#define SDSDR2   (*((volatile uint16_t *)0172224))
+#define SDSDR3   (*((volatile uint16_t *)0172226))
+#define SDSDR4   (*((volatile uint16_t *)0172230))
+#define SDSDR5   (*((volatile uint16_t *)0172232))
+#define SDSDR6   (*((volatile uint16_t *)0172234))
+#define SDSDR7   (*((volatile uint16_t *)0172236))
+
+#define SISAR0   (*((volatile uint16_t *)0172240))
+#define SISAR1   (*((volatile uint16_t *)0172242))
+#define SISAR2   (*((volatile uint16_t *)0172244))
+#define SISAR3   (*((volatile uint16_t *)0172246))
+#define SISAR4   (*((volatile uint16_t *)0172250))
+#define SISAR5   (*((volatile uint16_t *)0172252))
+#define SISAR6   (*((volatile uint16_t *)0172254))
+#define SISAR7   (*((volatile uint16_t *)0172256))
+
+#define SDSAR0   (*((volatile uint16_t *)0172260))
+#define SDSAR1   (*((volatile uint16_t *)0172262))
+#define SDSAR2   (*((volatile uint16_t *)0172264))
+#define SDSAR3   (*((volatile uint16_t *)0172266))
+#define SDSAR4   (*((volatile uint16_t *)0172270))
+#define SDSAR5   (*((volatile uint16_t *)0172272))
+#define SDSAR6   (*((volatile uint16_t *)0172274))
+#define SDSAR7   (*((volatile uint16_t *)0172276))
+
+#define KISDR0   (*((volatile uint16_t *)0172300))
+#define KISDR1   (*((volatile uint16_t *)0172302))
+#define KISDR2   (*((volatile uint16_t *)0172304))
+#define KISDR3   (*((volatile uint16_t *)0172306))
+#define KISDR4   (*((volatile uint16_t *)0172310))
+#define KISDR5   (*((volatile uint16_t *)0172312))
+#define KISDR6   (*((volatile uint16_t *)0172314))
+#define KISDR7   (*((volatile uint16_t *)0172316))
+
+#define KDSDR0   (*((volatile uint16_t *)0172320))
+#define KDSDR1   (*((volatile uint16_t *)0172322))
+#define KDSDR2   (*((volatile uint16_t *)0172324))
+#define KDSDR3   (*((volatile uint16_t *)0172326))
+#define KDSDR4   (*((volatile uint16_t *)0172330))
+#define KDSDR5   (*((volatile uint16_t *)0172332))
+#define KDSDR6   (*((volatile uint16_t *)0172334))
+#define KDSDR7   (*((volatile uint16_t *)0172336))
+
+#define KISAR0   (*((volatile uint16_t *)0172340))
+#define KISAR1   (*((volatile uint16_t *)0172342))
+#define KISAR2   (*((volatile uint16_t *)0172344))
+#define KISAR3   (*((volatile uint16_t *)0172346))
+#define KISAR4   (*((volatile uint16_t *)0172350))
+#define KISAR5   (*((volatile uint16_t *)0172352))
+#define KISAR6   (*((volatile uint16_t *)0172354))
+#define KISAR7   (*((volatile uint16_t *)0172356))
+
+#define KDSAR0   (*((volatile uint16_t *)0172360))
+#define KDSAR1   (*((volatile uint16_t *)0172362))
+#define KDSAR2   (*((volatile uint16_t *)0172364))
+#define KDSAR3   (*((volatile uint16_t *)0172366))
+#define KDSAR4   (*((volatile uint16_t *)0172370))
+#define KDSAR5   (*((volatile uint16_t *)0172372))
+#define KDSAR6   (*((volatile uint16_t *)0172374))
+#define KDSAR7   (*((volatile uint16_t *)0172376))
+
+/* SLU */
+
+#define CONSRCSR (*((volatile uint16_t *)0177560))
+#define CONSRBUF (*((volatile uint16_t *)0177562))
+#define CONSXCSR (*((volatile uint16_t *)0177564))
+#define CONSXBUF (*((volatile uint16_t *)0177566))
+
+#define SLU0RCSR (*((volatile uint16_t *)0176500))
+#define SLU0RBUF (*((volatile uint16_t *)0176502))
+#define SLU0XCSR (*((volatile uint16_t *)0176504))
+#define SLU0XBUF (*((volatile uint16_t *)0176506))
+
+#define SLU1RCSR (*((volatile uint16_t *)0176510))
+#define SLU1RBUF (*((volatile uint16_t *)0176512))
+#define SLU1XCSR (*((volatile uint16_t *)0176514))
+#define SLU1XBUF (*((volatile uint16_t *)0176516))
+
+#define SLU2RCSR (*((volatile uint16_t *)0176520))
+#define SLU2RBUF (*((volatile uint16_t *)0176522))
+#define SLU2XCSR (*((volatile uint16_t *)0176524))
+#define SLU2XBUF (*((volatile uint16_t *)0176524))
+
+#define SLU3RCSR (*((volatile uint16_t *)0176530))
+#define SLU3RBUF (*((volatile uint16_t *)0176532))
+#define SLU3XCSR (*((volatile uint16_t *)0176534))
+#define SLU3XBUF (*((volatile uint16_t *)0176536))
+
+
+/*
+ * =============================================================================
+ * Register Field Definitions
+ * =============================================================================
+ */
+
+/*
+ * These take the form of "(width),(offset)".  For example, if a field is
+ * 4-bits wide and occupies bits 2-5 (starting from 0), then it would be
+ * defined as "(017),(2)".
+ */
+
+/* CPU */
+
+
+/* MMU */
+#define MMR0_AB_NONRES (01),(15)
+#define MMR0_AB_PAGLEN (01),(14)
+#define MMR0_AB_RO     (01),(13)
+#define MMR0_PG_MODE   (03),(5)
+#define MMR0_PG_SPLTID (01),(4)
+#define MMR0_PG_NUMBER (07),(1)
+#define MMR0_EN_MMU    (01),(0)
+
+#define MMR1_A_CHANGE  (037),(11)
+#define MMR1_A_REGSTR  (07),(8)
+#define MMR1_B_CHANGE  (037),(3)
+#define MMR1_B_REGSTR  (07),(0)
+
+#define MMR2_PC        (0177777),(0)
+
+#define MMR3_EN_22BIT  (01),(4)
+#define MMR3_EN_CSMINS (01),(3)
+#define MMR3_KRNSPLTID (01),(2)
+#define MMR3_SUPSPLTID (01),(1)
+#define MMR3_USRSPLTID (01),(0)
+
+#define PAR_PAF        (0177777),(0)
+
+#define PDR_BYPASCACHE (01),(15)
+#define PDR_PAGELEN    (0177),(8)
+#define PDR_PAGEWRITEN (01),(6)
+#define PDR_EXPANDDIR  (01),(3)
+#define PDR_ACCESSCTRL (03),(1)
+
+/* SLU */
+
+#define SLURCSR_RCVRDY (01),(7)
+#define SLURCSR_RCVINT (01),(6)
+#define SLURCSR_RCVEN  (01),(0)
+
+#define SLURBUF_ERROR  (01),(15)
+#define SLURBUF_EOVRUN (01),(14)
+#define SLURBUF_EFRAME (01),(13)
+#define SLURBUF_EPARIT (01),(12)
+#define SLURBUF_BUFFER (0377),(0)
+
+#define SLUXCSR_XMTBRK (01),(0)
+#define SLUXCSR_XMTINT (01),(6)
+#define SLUXCSR_XMTRDY (01),(7)
+
+#define SLUXBUF_BUFFER (0377),(0)
+
+
+#endif // SGK_PDP11_REGISTER_H
diff --git a/pdp11/pdp11_slu.c b/pdp11/pdp11_slu.c
new file mode 100644 (file)
index 0000000..1d4667c
--- /dev/null
@@ -0,0 +1,132 @@
+// (c) 2020 Aaron Taylor <ataylor at subgeniuskitty dot com>
+// See License.txt file for copyright and license details.
+
+#include <stdarg.h>
+#include <stdint.h>
+#include "pdp11.h"
+
+/*
+ * Polled console I/O
+ */
+
+void
+putch(uint16_t c)
+{
+    while(!(GET(CONSXCSR,SLUXCSR_XMTRDY))) continue;
+    CONSXBUF = c;
+}
+
+uint16_t
+getch(void)
+{
+    while(!(GET(CONSRCSR,SLURCSR_RCVRDY))) continue;
+    return CONSRBUF;
+}
+
+
+/*
+ * Helper functions for use with console IO
+ */
+
+void
+print_string(const char * string)
+{
+    while (*string != '\0') {
+        putch(*string);
+        string++;
+    }
+}
+
+char
+digit_to_ascii(uint16_t digit)
+{
+    if (digit > 15) {
+        print_string("ERROR: Out of range in digit_to_ascii()\r\n");
+        // TODO: How do I want to handle runtime errors?
+        return 0;
+    } else if (digit > 9) {
+        digit -= 10;
+        digit += 'a';
+        return digit;
+    } else {
+        digit += '0';
+        return digit;
+    }
+}
+
+void
+print_uint16_in_octal(uint16_t number)
+{
+    char output[7]; // +6 for digits; +1 for null-term
+    for (int i = 5; i >= 0; i--) {
+        output[i] = digit_to_ascii(number % 8);
+        number = number / 8;
+    }
+    output[6] = '\0';
+    putch('0');
+    print_string(output);
+}
+
+void
+printf(const char * format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    while (*format) {
+        switch (*format) {
+            case '%':
+                format++;
+                switch (*format) {
+                    case 's':
+                        {
+                        char * s = va_arg(ap, char *);
+                        print_string(s);
+                        break;
+                        }
+                    case 'o':
+                        {
+                        uint16_t u = va_arg(ap, unsigned int);
+                        print_uint16_in_octal(u);
+                        break;
+                        }
+                    default:
+                        /* Put it back and look innocent. */
+                        putch('%');
+                        putch(*format);
+                        break;
+                }
+                break;
+            case '\\':
+                format++;
+                switch (*format) {
+                    case 'n':
+                        print_string("\r\n");
+                        break;
+                    case 't':
+                        putch('\t');
+                        break;
+                    case '%':
+                        putch('%');
+                        break;
+                    case '\\':
+                        putch('\\');
+                        break;
+                    default:
+                        /* Maybe this escape wasn't meant for us. */
+                        putch('\\');
+                        putch(*format);
+                        break;
+                }
+                break;
+            case '\n':
+                print_string("\r\n");
+                break;
+            default:
+                putch(*format);
+                break;
+        }
+        format++;
+    }
+    va_end(ap);
+}
+
diff --git a/pdp11/pdp11_slu.h b/pdp11/pdp11_slu.h
new file mode 100644 (file)
index 0000000..9949360
--- /dev/null
@@ -0,0 +1,27 @@
+// (c) 2020 Aaron Taylor <ataylor at subgeniuskitty dot com>
+// See License.txt file for copyright and license details.
+
+#ifndef SGK_PDP11_SLU_H
+#define SGK_PDP11_SLU_H
+
+#include <stdint.h>
+#include "pdp11.h"
+
+/*
+ * Polled IO functions for default console port.
+ */
+void putch(uint16_t c);
+uint16_t getch(void);
+
+/*
+ * Simple printf function. Replaces:
+ *     %s - Print a string
+ *     %o - Print a uint16 in octal
+ *     \n - Print a newline suitable for this system ('\r\n')
+ *     \t - Print a tab
+ *     \% - Print a '%'
+ * Also converts existing newlines in the 'format' string to '\r\n'.
+ */
+void printf(const char * format, ...);
+
+#endif // SGK_PDP11_SLU_H