Blob Blame History Raw
From 18afc7c7038280e2443c64e31cf2f0092a484004 Mon Sep 17 00:00:00 2001
From: Greg King <gregdk@users.sf.net>
Date: Fri, 27 Sep 2019 03:38:51 -0400
Subject: [PATCH 039/170] Created a target and a library for the Commander X16
 prototype computer.

---
 asminc/cbm_kernal.inc         |  36 ++--
 asminc/cx16.inc               | 316 ++++++++++++++++++++++++++++++++++
 cfg/cx16-asm.cfg              |  37 ++++
 cfg/cx16-bank.cfg             | 112 ++++++++++++
 cfg/cx16.cfg                  |  45 +++++
 doc/ca65.sgml                 |   3 +-
 doc/cc65.sgml                 |   4 +
 doc/cx16.sgml                 | 311 +++++++++++++++++++++++++++++++++
 doc/index.sgml                |   3 +
 doc/intro.sgml                |  27 ++-
 include/cbm.h                 |   4 +-
 include/cx16.h                | 189 ++++++++++++++++++++
 libsrc/Makefile               |   1 +
 libsrc/cx16/_scrsize.s        |  14 ++
 libsrc/cx16/bankramaddr.s     |  50 ++++++
 libsrc/cx16/bordercolor.s     |  27 +++
 libsrc/cx16/break.s           | 109 ++++++++++++
 libsrc/cx16/cgetc.s           |  72 ++++++++
 libsrc/cx16/clrscr.s          |  26 +++
 libsrc/cx16/color.s           |  43 +++++
 libsrc/cx16/conio.s           |   9 +
 libsrc/cx16/cpeekc.s          |  48 ++++++
 libsrc/cx16/cpeekcolor.s      |  27 +++
 libsrc/cx16/cpeekrevers.s     |  32 ++++
 libsrc/cx16/cpeeks.s          |   0
 libsrc/cx16/cputc.s           |  98 +++++++++++
 libsrc/cx16/crt0.s            | 115 +++++++++++++
 libsrc/cx16/devnum.s          |   8 +
 libsrc/cx16/get_ostype.s      |  20 +++
 libsrc/cx16/get_tv.s          |  31 ++++
 libsrc/cx16/irq.s             |  48 ++++++
 libsrc/cx16/joy/cx16-stdjoy.s | 119 +++++++++++++
 libsrc/cx16/joy_stat_stddrv.s |  10 ++
 libsrc/cx16/joy_stddrv.s      |  13 ++
 libsrc/cx16/kbhit.s           |  17 ++
 libsrc/cx16/kernal.s          |  59 +++++++
 libsrc/cx16/libref.s          |  18 ++
 libsrc/cx16/mainargs.s        | 137 +++++++++++++++
 libsrc/cx16/revers.s          |  23 +++
 libsrc/cx16/set_tv.s          |  32 ++++
 libsrc/cx16/status.s          |   6 +
 libsrc/cx16/sysuname.s        |  37 ++++
 libsrc/cx16/videomode.s       |  30 ++++
 libsrc/cx16/waitvsync.s       |  17 ++
 src/ca65/main.c               |   6 +-
 src/cc65/main.c               |   4 +
 src/common/target.c           |  16 +-
 src/common/target.h           |   4 +-
 testcode/lib/joy-test.c       |  86 +++++----
 49 files changed, 2440 insertions(+), 59 deletions(-)
 create mode 100644 asminc/cx16.inc
 create mode 100644 cfg/cx16-asm.cfg
 create mode 100644 cfg/cx16-bank.cfg
 create mode 100644 cfg/cx16.cfg
 create mode 100644 doc/cx16.sgml
 create mode 100644 include/cx16.h
 create mode 100644 libsrc/cx16/_scrsize.s
 create mode 100644 libsrc/cx16/bankramaddr.s
 create mode 100644 libsrc/cx16/bordercolor.s
 create mode 100644 libsrc/cx16/break.s
 create mode 100644 libsrc/cx16/cgetc.s
 create mode 100644 libsrc/cx16/clrscr.s
 create mode 100644 libsrc/cx16/color.s
 create mode 100644 libsrc/cx16/conio.s
 create mode 100644 libsrc/cx16/cpeekc.s
 create mode 100644 libsrc/cx16/cpeekcolor.s
 create mode 100644 libsrc/cx16/cpeekrevers.s
 create mode 100644 libsrc/cx16/cpeeks.s
 create mode 100644 libsrc/cx16/cputc.s
 create mode 100644 libsrc/cx16/crt0.s
 create mode 100644 libsrc/cx16/devnum.s
 create mode 100644 libsrc/cx16/get_ostype.s
 create mode 100644 libsrc/cx16/get_tv.s
 create mode 100644 libsrc/cx16/irq.s
 create mode 100644 libsrc/cx16/joy/cx16-stdjoy.s
 create mode 100644 libsrc/cx16/joy_stat_stddrv.s
 create mode 100644 libsrc/cx16/joy_stddrv.s
 create mode 100644 libsrc/cx16/kbhit.s
 create mode 100644 libsrc/cx16/kernal.s
 create mode 100644 libsrc/cx16/libref.s
 create mode 100644 libsrc/cx16/mainargs.s
 create mode 100644 libsrc/cx16/revers.s
 create mode 100644 libsrc/cx16/set_tv.s
 create mode 100644 libsrc/cx16/status.s
 create mode 100644 libsrc/cx16/sysuname.s
 create mode 100644 libsrc/cx16/videomode.s
 create mode 100644 libsrc/cx16/waitvsync.s

diff --git a/asminc/cbm_kernal.inc b/asminc/cbm_kernal.inc
index 29a6e5dd..79edce06 100644
--- a/asminc/cbm_kernal.inc
+++ b/asminc/cbm_kernal.inc
@@ -1,30 +1,45 @@
 ;
 ; Olli Savia <ops@iki.fi>
 ;
-; Commodore kernal functions
+; Commodore Kernal functions
 ;
 
+.if .def(__CX16__)
+  ; CX16 extended jump table
+  GETJOY       := $FF06
+.endif
+
 .if .def(__C128__)
-  ; C128 Extended jump table
+  ; C128 extended jump table
   C64MODE      := $FF4D
-  SWAPPER      := $FF5F
   SETBNK       := $FF68
 .endif
 
-.if .def(__C64__) || .def(__C128__) || .def(__C16__)
+.if .def(__C128__) || .def(__CX16__)
+  ; Extended jump table
+  CLSALL       := $FF4A
+  SWAPPER      := $FF5F
+  JSRFAR       := $FF6E
+  INDFET       := $FF74
+  INDSTA       := $FF77
+  INDCMP       := $FF7A
+  PRIMM        := $FF7D
+.endif
+
+.if .def(__C64__) || .def(__C128__) || .def(__C16__) || .def(__CX16__)
   CINT         := $FF81
   IOINIT       := $FF84
   RAMTAS       := $FF87
 .elseif .def(__VIC20__)
-  CINT         := $E518         ; No entries are in the kernal jump table of the Vic20 for these three (3) functions.
+  CINT         := $E518         ; No entries are in the Kernal jump table of the VIC-20 for these three (3) functions.
   IOINIT       := $FDF9         ; The entries for these functions have been set to point directly to the functions
-  RAMTAS       := $FD8D         ; in the kernal to maintain compatibility with the other Commodore platforms.
+  RAMTAS       := $FD8D         ; in the Kernal, to maintain compatibility with the other Commodore platforms.
 .elseif .def(__CBM510__) || .def(__CBM610__)
   IOINIT       := $FF7B
   CINT         := $FF7E
 .endif
 
-.if .def(__VIC20__) || .def(__C64__) || .def(__C128__) || .def(__C16__)
+.if .def(__VIC20__) || .def(__C64__) || .def(__C128__) || .def(__C16__) || .def(__CX16__)
   RESTOR       := $FF8A
   VECTOR       := $FF8D
 .elseif .def(__CBM510__) || .def(__CBM610__)
@@ -32,7 +47,7 @@
   RESTOR       := $FF87
 .endif
 
-.if .def(__CBM510__) || .def(__CBM610__) || .def(__VIC20__) || .def(__C64__) || .def(__C128__) || .def(__C16__)
+.if .def(__CBM510__) || .def(__CBM610__) || .def(__VIC20__) || .def(__C64__) || .def(__C128__) || .def(__C16__) || .def(__CX16__)
   SETMSG       := $FF90
   SECOND       := $FF93
   TKSA         := $FF96
@@ -64,7 +79,7 @@ CHRIN          := $FFCF
 BSOUT          := $FFD2
 CHROUT         := $FFD2
 
-.if .def(__CBM510__) || .def(__CBM610__) || .def(__VIC20__) || .def(__C64__) || .def(__C128__) || .def(__C16__)
+.if .def(__CBM510__) || .def(__CBM610__) || .def(__VIC20__) || .def(__C64__) || .def(__C128__) || .def(__C16__) || .def(__CX16__)
   LOAD         := $FFD5
   SAVE         := $FFD8
   SETTIM       := $FFDB
@@ -77,7 +92,7 @@ GETIN          := $FFE4
 CLALL          := $FFE7
 UDTIM          := $FFEA
 
-.if .def(__CBM510__) || .def(__CBM610__) || .def(__VIC20__) || .def(__C64__) || .def(__C128__) || .def(__C16__)
+.if .def(__CBM510__) || .def(__CBM610__) || .def(__VIC20__) || .def(__C64__) || .def(__C128__) || .def(__C16__) || .def(__CX16__)
   SCREEN       := $FFED
   PLOT         := $FFF0
   IOBASE       := $FFF3
@@ -102,7 +117,6 @@ UDTIM          := $FFEA
   CURS_SET     := $CD57
   CURS_ON      := $CD6F
   CURS_OFF     := $CD9F
-  INDFET       := $FF74
 .elseif .def(__C16__)
   CLRSCR       := $D88B
   KBDREAD      := $D8C1
diff --git a/asminc/cx16.inc b/asminc/cx16.inc
new file mode 100644
index 00000000..6f3f1c73
--- /dev/null
+++ b/asminc/cx16.inc
@@ -0,0 +1,316 @@
+;
+; CX16 definitions
+;
+
+; ---------------------------------------------------------------------------
+; Constants
+
+.enum COLOR
+  BLACK         = $00
+  WHITE
+  RED
+  CYAN
+  VIOLET
+  PURPLE        = VIOLET
+  GREEN
+  BLUE
+  YELLOW
+  ORANGE
+  BROWN
+  LIGHTRED
+  GRAY1
+  GRAY2
+  LIGHTGREEN
+  LIGHTBLUE
+  GRAY3
+.endenum
+
+; Special keys
+.enum KEY
+  F1    = $85
+  F3
+  F5
+  F7
+  F2
+  F4
+  F6
+  F8
+  F9    = $10
+  F10   = $15
+  F11
+  F12
+.endenum
+
+; ---------------------------------------------------------------------------
+; Zero page
+
+; BASIC
+VARTAB          := $2D          ; Pointer to start of BASIC variables
+MEMSIZE         := $37          ; Pointer to highest BASIC RAM location (+1)
+TXTPTR          := $7A          ; Pointer into BASIC source code
+
+; Kernal
+IN_DEV          := $99          ; Current input device number
+OUT_DEV         := $9A          ; Current output device number
+IMPARM          := $9B          ; Pointer for PRIMM function
+TIME            := $A0          ; 60 Hz. clock
+FNAM_LEN        := $B7          ; Length of filename
+SECADR          := $B9          ; Secondary address
+DEVNUM          := $BA          ; Device number
+FNAM            := $BB          ; Pointer to filename
+KEY_COUNT       := $C6          ; Number of keys in input buffer
+RVS             := $C7          ; Reverse flag
+CURS_FLAG       := $CC          ; 1 = cursor off
+CURS_BLINK      := $CD          ; Blink counter
+CURS_CHAR       := $CE          ; Character under the cursor
+CURS_STATE      := $CF          ; Cursor blink state
+SCREEN_PTR      := $D1          ; Pointer to current row on text screen (16 bits)
+CURS_X          := $D3          ; Cursor column
+CURS_Y          := $D6          ; Cursor row
+LLEN            := $D9          ; Line length
+NLINES          := $DA          ; Number of screen lines
+JOY1            := $EF          ; 3 bytes of NES/SNES gamepad data
+JOY2            := $F2
+FREKZP          := $FB          ; Five unused bytes
+
+; Page two
+
+BASIC_BUF       := $200         ; Location of command-line
+BASIC_BUF_LEN   = 89            ; Maximum length of command-line
+
+CHARCOLOR       := $286
+CURS_COLOR      := $287         ; Color under the cursor
+
+; ---------------------------------------------------------------------------
+; Vector and other locations
+
+IRQVec          := $0314
+BRKVec          := $0316
+NMIVec          := $0318
+
+; ---------------------------------------------------------------------------
+; I/O locations
+
+; Video Enhanced Retro Adapter
+; Has audio, SPI, and UART.
+.scope VERA
+  ; External registers
+  .struct
+                .org    $9F20
+  ADDR          .faraddr        ; Address for data port access
+  DATA0         .byte           ; First data port
+  DATA1         .byte           ; Second data port
+  CTRL          .byte           ; Control register
+  IRQ_EN        .byte           ; Interrupt enable bits
+  IRQ_FLAGS     .byte           ; Interrupt flags
+  .endstruct
+  .enum                         ; Address automatic increment amounts
+  INC0          =       0 << 4
+  INC1          =       1 << 4
+  INC2          =       2 << 4
+  INC4          =       3 << 4
+  INC8          =       4 << 4
+  INC16         =       5 << 4
+  INC32         =       6 << 4
+  INC64         =       7 << 4
+  INC128        =       8 << 4
+  INC256        =       9 << 4
+  INC512        =       10 << 4
+  INC1024       =       11 << 4
+  INC2048       =       12 << 4
+  INC4096       =       13 << 4
+  INC8192       =       14 << 4
+  INC16384      =       15 << 4
+  .endenum
+  ; Internal RAM and registers
+  VRAM          :=      $000000
+  .scope        COMPOSER        ; Display composer
+    .struct
+                .org    $0F0000
+    VIDEO       .byte
+    HSCALE      .byte
+    VSCALE      .byte
+    FRAME       .byte
+    HSTART_LO   .byte
+    HSTOP_LO    .byte
+    VSTART_LO   .byte
+    VSTOP_LO    .byte
+    STRTSTOP_HI .byte
+    IRQ_LINE    .word
+    .endstruct
+    .enum       MODE            ; Output mode
+      DISABLE   =       0
+      VGA
+      NTSC
+      RGB                       ; Interlaced, composite sync
+    .endenum
+    .enum
+    ENABLE_COLOR        =       0 << 2
+    DISABLE_COLOR       =       1 << 2  ; NTSC monochrome
+    .endenum
+  .endscope
+  PALETTE       :=      $0F1000
+  .struct       L0              ; Layer 0 registers
+                .org    $0F2000
+    CTRL0       .byte           ; Display mode control
+    CTRL1       .byte           ; Geometry control
+    MAP_BASE    .addr
+    TILE_BASE   .addr
+    HSCROLL     .word           ; Horizontal scroll
+    VSCROLL     .word           ; Vertical scroll
+  .endstruct
+  .struct       L1              ; Layer 1 registers (same as layer 0)
+                .org    $0F3000
+    CTRL0       .byte
+    CTRL1       .byte
+    MAP_BASE    .addr
+    TILE_BASE   .addr
+    HSCROLL     .word
+    VSCROLL     .word
+  .endstruct
+  .enum         MAP             ; Map geometry
+    WIDTH32     =       0
+    WIDTH64
+    WIDTH128
+    WIDTH256
+    HEIGHT32    =       0 << 2
+    HEIGHT64    =       1 << 2
+    HEIGHT128   =       2 << 2
+    HEIGHT256   =       3 << 2
+  .endenum
+  .scope        TILE            ; Tile geometry
+    .enum
+    WIDTH8      =       0 << 4
+    WIDTH16     =       1 << 4
+    WIDTH320    =       WIDTH8
+    WIDTH640    =       WIDTH16
+    HEIGHT8     =       0 << 5
+    HEIGHT16    =       1 << 5
+    .endenum
+    .enum       FLIP
+      NONE      =       0 << 2
+      HORIZ     =       1 << 2
+      VERT      =       2 << 2
+      BOTH      =       3 << 2
+    .endenum
+  .endscope
+  .enum         DMODE           ; Display modes
+    TEXT16      =       0 << 5
+    TEXT256     =       1 << 5
+    TILE4       =       2 << 5
+    TILE16      =       3 << 5
+    TILE256     =       4 << 5
+    BITMAP4     =       5 << 5
+    BITMAP16    =       6 << 5
+    BITMAP256   =       7 << 5
+  .endenum
+  .scope        SPRITE
+    .struct
+                .org    $0F4000
+    CTRL        .byte           ; Enables sprites
+    COLLISION   .byte
+    .endstruct
+    .enum       FLIP
+      NONE      =       0
+      HORIZ
+      VERT
+      BOTH
+    .endenum
+    .enum                       ; Sprite geometry
+    WIDTH8      =       0 << 4
+    WIDTH16     =       1 << 4
+    WIDTH32     =       2 << 4
+    WIDTH64     =       3 << 4
+    HEIGHT8     =       0 << 6
+    HEIGHT16    =       1 << 6
+    HEIGHT32    =       2 << 6
+    HEIGHT64    =       3 << 6
+    COLORS16    =       0 << 7
+    COLORS256   =       1 << 7
+    .endenum
+    .enum       DEPTH
+      DISABLE   =       0 << 2
+      CANVAS    =       1 << 2
+      LAYER0    =       2 << 2
+      LAYER1    =       3 << 2
+    .endenum
+    ATTRIB      :=      $0F5000 ; Sprite attributes
+  .endscope
+  AUDIO         :=      $0F6000
+  .scope        SPI
+    .struct
+                .org    $0F7000
+    DATA        .byte
+    CONTROL     .byte
+    .endstruct
+    .enum
+    DESELECT    =       0
+    SELECT
+    BUSY_MASK   =       1 << 1
+    .endenum
+  .endscope
+  .scope        UART            ; Universal Asyncronous Receiver Transmitter
+    .struct
+                .org    $0F8000
+    DATA        .byte
+    STATUS      .byte
+    BPS_DIV     .word
+    .endstruct
+    .enum       MASK
+      RECEIVE   =       1 << 0
+      TRANSMIT  =       1 << 1
+    .endenum
+  .endscope
+.endscope
+
+; 65c22
+.struct VIA1                    ; Versatile Interface Adapter
+        .org    $9F60
+  PRB   .byte                   ; ROM bank, IEC  (Port Register B)
+  PRA   .byte                   ; RAM bank  (Port Register A)
+  DDRB  .byte                   ; (Data Direction Register B)
+  DDRA  .byte                   ; (Data Direction Register A)
+  T1    .word                   ; (Timer 1)
+  T1L   .word                   ; (Timer 1 Latch)
+  T2    .word                   ; (Timer 2)
+  SR    .byte                   ; (Shift Register)
+  ACR   .byte                   ; (Auxiliary Control Register)
+  PCR   .byte                   ; (Peripheral Control Register)
+  IFR   .byte                   ; (Interrupt Flags Register)
+  IER   .byte                   ; (Interrupt Enable Register)
+  PRA2  .byte                   ; RAM bank  (Port Register A without handshaking)
+.endstruct
+
+; 65c22
+.struct VIA2
+        .org    $9F70
+  PRB   .byte
+  PRA   .byte			; NES controller communication
+  DDRB  .byte
+  DDRA  .byte
+  T1    .word
+  T1L   .word
+  T2    .word
+  SR    .byte
+  ACR   .byte
+  PCR   .byte
+  IFR   .byte
+  IER   .byte
+  PRA2  .byte
+.endstruct
+
+; Real-Time Clock
+
+; X16 Emulator device
+; This device doesn't exist on the real machine.
+.struct EMULATOR
+                .org    $9FB0
+  DEBUG         .byte           ; Boolean: debugging enabled
+  VIDACCESSLOG  .byte           ; Boolean: log VERA activity
+  KEYBOARDLOG   .byte           ; Boolean: log keyboard data
+  ECHO          .byte           ; Boolean: echo enabled
+  SAVEXIT       .byte           ; Boolean: save on exit
+                .res    $D - $5
+  KEYMAP        .byte           ; Current keyboard layout number
+  DETECT        .byte   2       ; If is "16" string, then running on emulator
+.endstruct
diff --git a/cfg/cx16-asm.cfg b/cfg/cx16-asm.cfg
new file mode 100644
index 00000000..53f6da17
--- /dev/null
+++ b/cfg/cx16-asm.cfg
@@ -0,0 +1,37 @@
+FEATURES {
+    STARTADDRESS: default = $0801;
+}
+SYMBOLS {
+    __LOADADDR__: type = import;
+    __HIMEM__:    type = weak, value = $9F00;
+}
+MEMORY {
+    ZP:       file = "", start = $0004,  size = $0090 - $0004, define = yes;
+    LOADADDR: file = %O, start = %S - 2, size = $0002;
+    MAIN:     file = %O, start = %S,     size = __HIMEM__ - %S;
+}
+SEGMENTS {
+    ZEROPAGE: load = ZP,       type = zp;
+    LOADADDR: load = LOADADDR, type = ro;
+    EXEHDR:   load = MAIN,     type = ro, optional = yes;
+    LOWCODE:  load = MAIN,     type = ro, optional = yes;
+    CODE:     load = MAIN,     type = ro;
+    RODATA:   load = MAIN,     type = ro;
+    DATA:     load = MAIN,     type = rw;
+    BSS:      load = MAIN,     type = bss,                define = yes;
+}
+FEATURES {
+    CONDES: type    = constructor,
+            label   = __CONSTRUCTOR_TABLE__,
+            count   = __CONSTRUCTOR_COUNT__,
+            segment = ONCE;
+    CONDES: type    = destructor,
+            label   = __DESTRUCTOR_TABLE__,
+            count   = __DESTRUCTOR_COUNT__,
+            segment = RODATA;
+    CONDES: type    = interruptor,
+            label   = __INTERRUPTOR_TABLE__,
+            count   = __INTERRUPTOR_COUNT__,
+            segment = RODATA,
+            import  = __CALLIRQ__;
+}
diff --git a/cfg/cx16-bank.cfg b/cfg/cx16-bank.cfg
new file mode 100644
index 00000000..52438fba
--- /dev/null
+++ b/cfg/cx16-bank.cfg
@@ -0,0 +1,112 @@
+FEATURES {
+    STARTADDRESS: default = $0801;
+}
+SYMBOLS {
+    __LOADADDR__:     type = import;
+    __EXEHDR__:       type = import;
+    __BANKRAMADDR__:  type = import;
+    __STACKSIZE__:    type = weak,   value = $0800; # 2K stack
+    __HIMEM__:        type = weak,   value = $9F00;
+    __BANKRAMSTART__: type = export, value = $A000;
+    __BANKRAMSIZE__:  type = weak,   value = $2000; # 8K banked RAM
+}
+MEMORY {
+    ZP:         file = "", define = yes,  start = $0004,                size = $0090 - $0004;
+    LOADADDR:   file = %O,                start = %S - 2,               size = $0002;
+    HEADER:     file = %O, define = yes,  start = %S,                   size = $000D;
+    MAIN:       file = %O, define = yes,  start = __HEADER_LAST__,      size = __HIMEM__ - __HEADER_LAST__;
+    BSS:        file = "",                start = __ONCE_RUN__,         size = __HIMEM__ - __ONCE_RUN__ - __STACKSIZE__;
+    BRAM00ADDR: file = "%O.00",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM00:     file = "%O.00",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM01ADDR: file = "%O.01",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM01:     file = "%O.01",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM02ADDR: file = "%O.02",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM02:     file = "%O.02",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM03ADDR: file = "%O.03",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM03:     file = "%O.03",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM04ADDR: file = "%O.04",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM04:     file = "%O.04",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM05ADDR: file = "%O.05",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM05:     file = "%O.05",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM06ADDR: file = "%O.06",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM06:     file = "%O.06",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM07ADDR: file = "%O.07",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM07:     file = "%O.07",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM08ADDR: file = "%O.08",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM08:     file = "%O.08",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM09ADDR: file = "%O.09",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM09:     file = "%O.09",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM0AADDR: file = "%O.0a",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM0A:     file = "%O.0a",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM0BADDR: file = "%O.0b",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM0B:     file = "%O.0b",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM0CADDR: file = "%O.0c",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM0C:     file = "%O.0c",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM0DADDR: file = "%O.0d",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM0D:     file = "%O.0d",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM0EADDR: file = "%O.0e",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM0E:     file = "%O.0e",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+    BRAM0FADDR: file = "%O.0f",           start = __BANKRAMSTART__ - 2, size = $0002;
+    BRAM0F:     file = "%O.0f",           start = __BANKRAMSTART__,     size = __BANKRAMSIZE__;
+}
+SEGMENTS {
+    ZEROPAGE:   load = ZP,         type = zp;
+    EXTZP:      load = ZP,         type = zp,                optional = yes;
+    LOADADDR:   load = LOADADDR,   type = ro;
+    EXEHDR:     load = HEADER,     type = ro;
+    STARTUP:    load = MAIN,       type = ro;
+    LOWCODE:    load = MAIN,       type = ro,                optional = yes;
+    CODE:       load = MAIN,       type = ro;
+    RODATA:     load = MAIN,       type = ro;
+    DATA:       load = MAIN,       type = rw;
+    INIT:       load = MAIN,       type = rw;
+    ONCE:       load = MAIN,       type = ro,  define = yes;
+    BSS:        load = BSS,        type = bss, define = yes;
+    BRAM00ADDR: load = BRAM00ADDR, type = ro,                optional = yes;
+    BANKRAM00:  load = BRAM00,     type = rw,  define = yes, optional = yes;
+    BRAM01ADDR: load = BRAM01ADDR, type = ro,                optional = yes;
+    BANKRAM01:  load = BRAM01,     type = rw,  define = yes, optional = yes;
+    BRAM02ADDR: load = BRAM02ADDR, type = ro,                optional = yes;
+    BANKRAM02:  load = BRAM02,     type = rw,  define = yes, optional = yes;
+    BRAM03ADDR: load = BRAM03ADDR, type = ro,                optional = yes;
+    BANKRAM03:  load = BRAM03,     type = rw,  define = yes, optional = yes;
+    BRAM04ADDR: load = BRAM04ADDR, type = ro,                optional = yes;
+    BANKRAM04:  load = BRAM04,     type = rw,  define = yes, optional = yes;
+    BRAM05ADDR: load = BRAM05ADDR, type = ro,                optional = yes;
+    BANKRAM05:  load = BRAM05,     type = rw,  define = yes, optional = yes;
+    BRAM06ADDR: load = BRAM06ADDR, type = ro,                optional = yes;
+    BANKRAM06:  load = BRAM06,     type = rw,  define = yes, optional = yes;
+    BRAM07ADDR: load = BRAM07ADDR, type = ro,                optional = yes;
+    BANKRAM07:  load = BRAM07,     type = rw,  define = yes, optional = yes;
+    BRAM08ADDR: load = BRAM08ADDR, type = ro,                optional = yes;
+    BANKRAM08:  load = BRAM08,     type = rw,  define = yes, optional = yes;
+    BRAM09ADDR: load = BRAM09ADDR, type = ro,                optional = yes;
+    BANKRAM09:  load = BRAM09,     type = rw,  define = yes, optional = yes;
+    BRAM0AADDR: load = BRAM0AADDR, type = ro,                optional = yes;
+    BANKRAM0A:  load = BRAM0A,     type = rw,  define = yes, optional = yes;
+    BRAM0BADDR: load = BRAM0BADDR, type = ro,                optional = yes;
+    BANKRAM0B:  load = BRAM0B,     type = rw,  define = yes, optional = yes;
+    BRAM0CADDR: load = BRAM0CADDR, type = ro,                optional = yes;
+    BANKRAM0C:  load = BRAM0C,     type = rw,  define = yes, optional = yes;
+    BRAM0DADDR: load = BRAM0DADDR, type = ro,                optional = yes;
+    BANKRAM0D:  load = BRAM0D,     type = rw,  define = yes, optional = yes;
+    BRAM0EADDR: load = BRAM0EADDR, type = ro,                optional = yes;
+    BANKRAM0E:  load = BRAM0E,     type = rw,  define = yes, optional = yes;
+    BRAM0FADDR: load = BRAM0FADDR, type = ro,                optional = yes;
+    BANKRAM0F:  load = BRAM0F,     type = rw,  define = yes, optional = yes;
+}
+FEATURES {
+    CONDES: type    = constructor,
+            label   = __CONSTRUCTOR_TABLE__,
+            count   = __CONSTRUCTOR_COUNT__,
+            segment = ONCE;
+    CONDES: type    = destructor,
+            label   = __DESTRUCTOR_TABLE__,
+            count   = __DESTRUCTOR_COUNT__,
+            segment = RODATA;
+    CONDES: type    = interruptor,
+            label   = __INTERRUPTOR_TABLE__,
+            count   = __INTERRUPTOR_COUNT__,
+            segment = RODATA,
+            import  = __CALLIRQ__;
+}
diff --git a/cfg/cx16.cfg b/cfg/cx16.cfg
new file mode 100644
index 00000000..f912e0f8
--- /dev/null
+++ b/cfg/cx16.cfg
@@ -0,0 +1,45 @@
+FEATURES {
+    STARTADDRESS: default = $0801;
+}
+SYMBOLS {
+    __LOADADDR__:     type = import;
+    __EXEHDR__:       type = import;
+    __STACKSIZE__:    type = weak,   value = $0800; # 2k stack
+    __HIMEM__:        type = weak,   value = $9F00;
+}
+MEMORY {
+    ZP:       file = "", define = yes, start = $0004,                size = $0090 - $0004;
+    LOADADDR: file = %O,               start = %S - 2,               size = $0002;
+    HEADER:   file = %O, define = yes, start = %S,                   size = $000D;
+    MAIN:     file = %O, define = yes, start = __HEADER_LAST__,      size = __HIMEM__ - __HEADER_LAST__;
+    BSS:      file = "",               start = __ONCE_RUN__,         size = __HIMEM__ - __ONCE_RUN__ - __STACKSIZE__;
+}
+SEGMENTS {
+    ZEROPAGE: load = ZP,       type = zp;
+    EXTZP:    load = ZP,       type = zp,                optional = yes;
+    LOADADDR: load = LOADADDR, type = ro;
+    EXEHDR:   load = HEADER,   type = ro;
+    STARTUP:  load = MAIN,     type = ro;
+    LOWCODE:  load = MAIN,     type = ro,                optional = yes;
+    CODE:     load = MAIN,     type = ro;
+    RODATA:   load = MAIN,     type = ro;
+    DATA:     load = MAIN,     type = rw;
+    INIT:     load = MAIN,     type = rw;
+    ONCE:     load = MAIN,     type = ro,  define = yes;
+    BSS:      load = BSS,      type = bss, define = yes;
+}
+FEATURES {
+    CONDES: type    = constructor,
+            label   = __CONSTRUCTOR_TABLE__,
+            count   = __CONSTRUCTOR_COUNT__,
+            segment = ONCE;
+    CONDES: type    = destructor,
+            label   = __DESTRUCTOR_TABLE__,
+            count   = __DESTRUCTOR_COUNT__,
+            segment = RODATA;
+    CONDES: type    = interruptor,
+            label   = __INTERRUPTOR_TABLE__,
+            count   = __INTERRUPTOR_COUNT__,
+            segment = RODATA,
+            import  = __CALLIRQ__;
+}
diff --git a/doc/ca65.sgml b/doc/ca65.sgml
index 8a5e307d..ad72189c 100644
--- a/doc/ca65.sgml
+++ b/doc/ca65.sgml
@@ -4653,9 +4653,10 @@ compiler, depending on the target system selected:
 <item><tt/__C128__/ - Target system is <tt/c128/
 <item><tt/__C16__/ - Target system is <tt/c16/ or <tt/plus4/
 <item><tt/__C64__/ - Target system is <tt/c64/
-<item><tt/__CBM__/ - Target is a Commodore system
+<item><tt/__CBM__/ - Target is a Commodore or Commodore-alike system
 <item><tt/__CBM510__/ - Target system is <tt/cbm510/
 <item><tt/__CBM610__/ - Target system is <tt/cbm610/
+<item><tt/__CX16__/ - Target system is <tt/cx16/
 <item><tt/__GEOS__/ - Target is a GEOS system
 <item><tt/__GEOS_APPLE__/ - Target system is <tt/geos-apple/
 <item><tt/__GEOS_CBM__/ - Target system is <tt/geos-cbm/
diff --git a/doc/cc65.sgml b/doc/cc65.sgml
index 601e364e..8691f947 100644
--- a/doc/cc65.sgml
+++ b/doc/cc65.sgml
@@ -901,6 +901,10 @@ The compiler defines several macros at startup:
   <item><tt/__CC65_STD_CC65__/
   </itemize>
 
+  <tag><tt>__CX16__</tt></tag>
+
+  This macro is defined if the target is the Commander X16 (-t cx16).
+
   <tag><tt>__DATE__</tt></tag>
 
   This macro expands to the date of translation of the preprocessing
diff --git a/doc/cx16.sgml b/doc/cx16.sgml
new file mode 100644
index 00000000..077458bf
--- /dev/null
+++ b/doc/cx16.sgml
@@ -0,0 +1,311 @@
+<!doctype linuxdoc system>
+
+<article>
+<title>Commander X16-specific information for cc65
+<author><url url="mailto:greg.king5@verizon.net" name="Greg King">
+
+<abstract>
+An overview over the CX16 run-time system as it's implemented for the cc65 C
+compiler.
+</abstract>
+
+<!-- Table of contents -->
+<toc>
+
+<!-- Begin the document -->
+
+<sect>Overview<p>
+
+The Commander X16 is a modern small computer with firmware that is based on
+the ROMs in Commodore's VIC-20 and 64C.  It has a couple of the I/O chips that
+are in the VIC-20.
+
+This file contains an overview of the CX16 run-time system as it comes with the
+cc65 C compiler. It describes the memory layout, CX16-specific header files,
+available drivers, and any pitfalls specific to that platform.
+
+Please note that CX16-specific functions just are mentioned here; they are
+described in detail in the separate <url url="funcref.html" name="function
+reference">. Even functions marked as "platform dependent" may be available on
+more than one platform. Please see the function reference for more
+information.
+
+
+
+<sect>Binary format<p>
+
+The standard binary output format generated by the linker for the CX16 target
+is a machine language program with a one-line BASIC stub which calls the
+machine language part via SYS. That means that a program can be loaded as a
+BASIC program, and started with RUN. It is, of course, possible to change that
+behaviour by using a modified start-up file and linker config.
+
+
+
+<sect>Memory layout<p>
+
+cc65-generated programs with the default setup run with the I/O area and the
+Kernal ROM visible.  That means that Kernal entry points can be called directly.
+The usable memory ranges are &dollar;0800 - &dollar;9EFF and &dollar;A000 -
+&dollar;BFFF.
+
+Special locations:
+
+<descrip>
+  <tag/Stack/
+  The C run-time stack is located at &dollar;9EFF, and grows downward.
+
+  <tag/Heap/
+  The C heap is located at the end of the program, and grows toward the C
+  run-time stack.
+
+  <tag/Bank RAM/
+  Bank RAM is located at &dollar;A000 - &dollar;BFFF.  It's an eight-Kibibyte
+  window into a half Mibibyte or two Mibibytes of banked RAM.
+
+  <tag/Bank ROM/
+  Bank ROM is located at &dollar;C000 - &dollar;FFFF.  It's a sixteen-Kibibyte
+  window into 128 Kibibytes of banked ROM.
+</descrip><p>
+
+
+
+<sect>Linker configurations<p>
+
+The ld65 linker comes with a default config. file for the Commander X16, which
+is used via <tt/-t cx16/. The cx16 package comes with additional secondary
+linker config. files which are used via <tt/-t cx16 -C &lt;configfile&gt;/.
+
+
+<sect1>Default config. file (<tt/cx16.cfg/)<p>
+
+The default configuration is tailored to C programs. It supplies the load
+address and a small BASIC stub that starts the compiled program using a SYS
+command.
+
+
+<sect1><tt/cx16-asm.cfg/<p>
+
+This configuration is made for Assembly programmers who don't need a special
+setup. The default start address is &dollar;0801. It can be changed with the
+linker command-line option <tt/--start-addr/ or <tt/-S/. All standard segments,
+with the exception of <tt/ZEROPAGE/, are written to the output file;
+and, a two-byte load address is prepended.
+
+To use that config. file, assemble with <tt/-t cx16/, and link with <tt/-C
+cx16-asm.cfg/. The former will make sure that the correct character
+translations are in effect, while the latter supplies the actual config.
+When using <tt/cl65/, use both command-line options.
+
+Sample command line for <tt/cl65/:
+<tscreen><verb>
+cl65 -o file.prg -t cx16 -C cx16-asm.cfg source.s
+</verb></tscreen>
+
+To generate code that loads to &dollar;A000:
+<tscreen><verb>
+cl65 -o file.prg -Wl -S,$A000 -t cX16 -C cX16-asm.cfg source.s
+</verb></tscreen>
+
+It also is possible to add a small BASIC header to the program, that uses SYS
+to jump to the program entry point (which is the start of the code segment).
+The advantage is that the program can be started using RUN.
+
+To generate a program with a BASIC SYS header, use
+<tscreen><verb>
+cl65 -o file.prg -u __EXEHDR__ -t cx16 -C cx16-asm.cfg source.s
+</verb></tscreen>
+
+Please note that, in this case, a changed start address doesn't make sense,
+because the program must be loaded to BASIC's start address.
+
+
+
+<sect>Platform-specific header files<p>
+
+Programs containing CX16-specific code may use the <tt/cx16.h/ or <tt/cbm.h/
+header files. Using the later may be an option when writing code for more than
+one CBM-like platform, because it includes <tt/cx16.h/, and declares several
+functions common to all CBM-like platforms.
+
+
+<sect1>CX16-specific functions<p>
+
+The functions listed below are special for the CX16. See the <url
+url="funcref.html" name="function reference"> for declarations and usage.
+
+<itemize>
+<item>get_ostype()
+<item>set_tv()
+<item>videomode()
+<item>waitvsync()
+</itemize>
+
+
+<sect1>CBM-specific functions<p>
+
+Some functions are available for all (or, at least most) of the Commodore-like
+machines. See the <url url="funcref.html" name="function reference"> for
+declarations and usage.
+
+<itemize>
+<item>cbm_close()
+<item>cbm_closedir()
+<item>cbm_k_basin()
+<item>cbm_k_bsout()
+<item>cbm_k_chkin()
+<item>cbm_k_ckout()
+<item>cbm_k_close()
+<item>cbm_k_clrch()
+<item>cbm_k_load()
+<item>cbm_k_open()
+<item>cbm_k_readst()
+<item>cbm_k_save()
+<item>cbm_k_second()
+<item>cbm_k_setlfs()
+<item>cbm_k_setnam()
+<item>cbm_k_tksa()
+<item>cbm_load()
+<item>cbm_open()
+<item>cbm_opendir()
+<item>cbm_read()
+<item>cbm_readdir()
+<item>cbm_save()
+<item>cbm_write()
+<item>get_tv()
+</itemize>
+
+
+<sect1>Hardware access<p>
+
+The following pseudo variables declared in the <tt/cx16.h/ header file do allow
+access to hardware located in the address space. Some variables are
+structures, accessing the struct fields will access the chip registers.
+
+<descrip>
+  <tag><tt/VERA/</tag>
+  The <tt/VERA/ structure allows access
+  to the Video Enhanced Retro Adapter chip.
+
+  <tag><tt/VIA1, VIA2/</tag>
+  Access to the two VIA (Versatile Interface Adapter) chips is available via
+  the <tt/VIA1/ and <tt/VIA2/ variables. The structure behind those variables
+  is explained in <tt/_6522.h/.
+
+  <tag><tt/BANK_RAM/</tag>
+  A character array that mirrors the eight-Kibibyte window, at &dollar;A000,
+  into banked RAM.
+</descrip><p>
+
+
+
+<sect>Loadable drivers<p>
+
+The names in the parentheses denote the symbols to be used for static linking of the drivers.
+
+
+<sect1>Graphics drivers<p>
+
+No graphics drivers are available currently for the CX16.
+
+
+<sect1>Extended memory drivers<p>
+
+No extended memory drivers are available currently for the CX16.
+
+
+<sect1>Joystick drivers<p>
+
+The default drivers, <tt/joy_stddrv (joy_static_stddrv)/,
+point to <tt/cX16-stdjoy.joy (cx16_stdjoy_joy)/.
+
+<descrip>
+  <tag><tt/cX16-stdjoy.joy (cX16_stdjoy_joy)/</tag>
+  Supports up to two NES and SNES controllers connected to the joystick ports
+  of the CX16.  It reads the four directions, and the A, B, Select, and Start
+  buttons.  Button A is the primary fire button.
+</descrip><p>
+
+
+<sect1>Mouse drivers<p>
+
+No mouse drivers are available currently for the CX16.
+
+
+<sect1>RS232 device drivers<p>
+
+No serial drivers are available currently for the CX16.
+
+
+
+<sect>Limitations<p>
+
+The Commander X16 still is being designed.  It's configuration can change at
+any time.  Some changes could make old programs fail to work.
+
+
+
+<sect>Other hints<p>
+
+
+<sect1>Escape code<p>
+
+For an Esc, press <tt/Ctrl/ and the <tt/[/ key.
+
+
+<sect1>Passing arguments to the program<p>
+
+Command-line arguments can be passed to <tt/main()/.  Because that is not
+supported directly by BASIC, the following syntax was chosen:
+<tscreen><verb>
+    RUN:REM ARG1 " ARG2 IS QUOTED" ARG3 "" ARG5
+</verb></tscreen>
+
+<enum>
+<item>Arguments are separated by spaces.
+<item>Arguments may be quoted.
+<item>Leading and trailing spaces around an argument are ignored. Spaces within
+      a quoted argument are allowed.
+<item>The first argument passed to <tt/main()/ is the program name.
+<item>A maximum number of 10 arguments (including the program name) are
+      supported.
+</enum>
+
+
+<sect1>Program return code<p>
+
+The program return code (low byte) is passed back to BASIC by use of the
+<tt/ST/ variable.
+
+
+<sect1>Interrupts<p>
+
+The run-time for the CX16 uses routines marked as <tt/.INTERRUPTOR/ for
+interrupt handlers. Such routines must be written as simple machine language
+subroutines, and will be called automatically by the interrupt handler code
+if they are linked into a program. See the discussion of the <tt/.CONDES/
+feature in the <url url="ca65.html" name="assembler manual">.
+
+
+
+<sect>License<p>
+
+This software is provided "as-is", without any expressed or implied
+warranty.  In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+<enum>
+<item>  The origin of this software must not be misrepresented; you must not
+        claim that you wrote the original software. If you use this software
+        in a product, an acknowledgment in the product documentation would be
+        appreciated, but is not required.
+<item>  Altered source versions must be plainly marked as such, and must not
+        be misrepresented as being the original software.
+<item>  This notice may not be removed or altered from any source
+        distribution.
+</enum>
+
+</article>
diff --git a/doc/index.sgml b/doc/index.sgml
index aecfb7de..01325529 100644
--- a/doc/index.sgml
+++ b/doc/index.sgml
@@ -145,6 +145,9 @@
   <tag><htmlurl url="creativision.html" name="creativision.html"></tag>
   Topics specific to the Creativision Console.
 
+  <tag><htmlurl url="cx16.html" name="cx16.html"></tag>
+  Topics specific to the Commander X16.
+
   <tag><htmlurl url="gamate.html" name="gamate.html"></tag>
   Topics specific to the Bit Corporation Gamate Console.
 
diff --git a/doc/intro.sgml b/doc/intro.sgml
index 994d30bc..b2b141d1 100644
--- a/doc/intro.sgml
+++ b/doc/intro.sgml
@@ -402,11 +402,36 @@ RUN
 The emulation, also, supports that method.
 
 
+<sect1>Commander X16
+
+<sect2>x16-emulator<p>
+Available at <url
+url="https://github.com/commanderx16/x16-emulator/releases">:
+
+Emulates the Commander X16 Single Board Computer, with sound, SD card images,
+VGA and NTSC video, and a NES game controller emulation.  Includes a monitor. 
+It runs on all SDL2 platforms.
+
+Compile the tutorial with
+<tscreen><verb>
+cl65 -O -t cx16 hello.c text.s
+</verb></tscreen>
+
+Start the emulator.  Then, type
+<tscreen><verb>
+LOAD"HELLO",1
+RUN
+</verb></tscreen>
+(Type those lines in lower-case; but, they will appear as upper-case.)
+
+On a real computer, you would type an <tt/8/ instead of a <tt/1/.
+
+
 <sect1>Commodore
 
 <sect2>VICE<p>
 Available at <url
-url="http://vice-emu.sourceforge.net/">:
+url="https://vice-emu.sourceforge.net/">:
 
 Emulates Commodore 64/128/VIC-20/PET/CBM II/Plus 4 computers. Supports
 printers, serial port and adapters, stereo sound, disk drives and images, RAM expansions,
diff --git a/include/cbm.h b/include/cbm.h
index 0a2d6469..d9b31543 100644
--- a/include/cbm.h
+++ b/include/cbm.h
@@ -65,6 +65,8 @@
 #  include <cbm610.h>
 #elif defined(__PET__)    && !defined(_PET_H)
 #  include <pet.h>
+#elif defined(__CX16__)   && !defined(_CX16_H)
+#  include <cx16.h>
 #endif
 
 /* Include definitions for CBM file types */
@@ -300,5 +302,3 @@ void __fastcall__ cbm_closedir (unsigned char lfn);
 
 /* End of cbm.h */
 #endif
-
-
diff --git a/include/cx16.h b/include/cx16.h
new file mode 100644
index 00000000..db32d846
--- /dev/null
+++ b/include/cx16.h
@@ -0,0 +1,189 @@
+/*****************************************************************************/
+/*                                                                           */
+/*                                  cx16.h                                   */
+/*                                                                           */
+/*                     CX16 system-specific definitions                      */
+/*                                                                           */
+/*                                                                           */
+/* This software is provided "as-is", without any expressed or implied       */
+/* warranty.  In no event will the authors be held liable for any damages    */
+/* arising from the use of this software.                                    */
+/*                                                                           */
+/* Permission is granted to anyone to use this software for any purpose,     */
+/* including commercial applications, and to alter it and redistribute it    */
+/* freely, subject to the following restrictions:                            */
+/*                                                                           */
+/* 1. The origin of this software must not be misrepresented; you must not   */
+/*    claim that you wrote the original software. If you use this software   */
+/*    in a product, an acknowledgment in the product documentation would be  */
+/*    appreciated, but is not required.                                      */
+/* 2. Altered source versions must be plainly marked as such, and must not   */
+/*    be misrepresented as being the original software.                      */
+/* 3. This notice may not be removed or altered from any source              */
+/*    distribution.                                                          */
+/*                                                                           */
+/*****************************************************************************/
+
+
+
+#ifndef _CX16_H
+#define _CX16_H
+
+
+
+/* Check for errors */
+#ifndef __CX16__
+#  error This module may be used only when compiling for the CX16!
+#endif
+
+
+
+/*****************************************************************************/
+/*                                   Data                                    */
+/*****************************************************************************/
+
+
+
+/* Additional key defines */
+#define CH_F1                   0x85
+#define CH_F2                   0x89
+#define CH_F3                   0x86
+#define CH_F4                   0x8A
+#define CH_F5                   0x87
+#define CH_F6                   0x8B
+#define CH_F7                   0x88
+#define CH_F8                   0x8C
+#define CH_F9                   0x10
+#define CH_F10                  0x15
+#define CH_F11                  0x16
+#define CH_F12                  0x17
+
+/* Color defines */
+#define COLOR_BLACK             0x00
+#define COLOR_WHITE             0x01
+#define COLOR_RED               0x02
+#define COLOR_CYAN              0x03
+#define COLOR_VIOLET            0x04
+#define COLOR_PURPLE            COLOR_VIOLET
+#define COLOR_GREEN             0x05
+#define COLOR_BLUE              0x06
+#define COLOR_YELLOW            0x07
+#define COLOR_ORANGE            0x08
+#define COLOR_BROWN             0x09
+#define COLOR_LIGHTRED          0x0A
+#define COLOR_GRAY1             0x0B
+#define COLOR_GRAY2             0x0C
+#define COLOR_LIGHTGREEN        0x0D
+#define COLOR_LIGHTBLUE         0x0E
+#define COLOR_GRAY3             0x0F
+
+/* Masks for joy_read() */
+#define JOY_BTN_1_MASK  0x80
+#define JOY_BTN_2_MASK  0x40
+#define JOY_BTN_3_MASK  0x20
+#define JOY_BTN_4_MASK  0x10
+#define JOY_UP_MASK     0x08
+#define JOY_DOWN_MASK   0x04
+#define JOY_LEFT_MASK   0x02
+#define JOY_RIGHT_MASK  0x01
+
+#define JOY_BTN_A_MASK  JOY_BTN_1_MASK
+#define JOY_BTN_B_MASK  JOY_BTN_2_MASK
+#define JOY_SELECT_MASK JOY_BTN_3_MASK
+#define JOY_START_MASK  JOY_BTN_4_MASK
+
+#define JOY_BTN_A(v)    ((v) & JOY_BTN_A_MASK)
+#define JOY_BTN_B(v)    ((v) & JOY_BTN_B_MASK)
+#define JOY_SELECT(v)   ((v) & JOY_SELECT_MASK)
+#define JOY_START(v)    ((v) & JOY_START_MASK)
+
+#define JOY_FIRE2_MASK  JOY_BTN_2_MASK
+#define JOY_FIRE2(v)    ((v) & JOY_FIRE2_MASK)
+
+/* get_tv() return codes
+** set_tv() argument codes
+*/
+#define TV_NONE         0
+#define TV_VGA          1
+#define TV_NTSC_COLOR   2
+#define TV_RGB          3
+#define TV_NONE2        4
+#define TV_VGA2         5
+#define TV_NTSC_MONO    6
+#define TV_RGB2         7
+
+/* Video mode defines */
+#define VIDEOMODE_40x30         40u
+#define VIDEOMODE_80x60         80u
+#define VIDEOMODE_40COL         VIDEOMODE_40x30
+#define VIDEOMODE_80COL         VIDEOMODE_80x60
+
+
+/* Define hardware */
+
+/* Define a structure with the Video Enhanced Retro Adapter's
+** external registers.
+*/
+struct __vera {
+    unsigned short      address;        /* Address for data ports */
+    unsigned char       address_hi;
+    unsigned char       data0;          /* Data port 0 */
+    unsigned char       data1;          /* Data port 1 */
+    unsigned char       control;        /* Control register */
+    unsigned char       irq_enable;     /* Interrupt enable bits */
+    unsigned char       irq_flags;      /* Interrupt flags */
+};
+#define VERA    (*(volatile struct __vera *)0x9F20)
+
+#include <_6522.h>
+#define VIA1    (*(volatile struct __6522 *)0x9F60)
+#define VIA2    (*(volatile struct __6522 *)0x9F70)
+
+/* Define a structure with the x16emu's settings registers. */
+struct __emul {
+    unsigned char       debug;          /* Boolean: debugging enabled */
+    unsigned char       vera_action;    /* Boolean: displaying VERA activity */
+    unsigned char       keyboard;       /* Boolean: displaying typed keys */
+    unsigned char       echo;           /* Boolean: Kernal output echoed to host */
+    unsigned char       save_on_exit;   /* Boolean: save SD card when quitting */
+    unsigned char       unused[0xD - 0x5];
+    unsigned char       keymap;         /* Keyboard layout number */
+       const char       detect[2];      /* "16" if running on x16emu */
+};
+#define EMULATOR (*(volatile struct __emul)0x9FB0)
+
+
+
+/* The addresses of the static drivers */
+
+extern void cx16_stdjoy_joy[];          /* Referred to by joy_static_stddrv[] */
+
+
+
+/*****************************************************************************/
+/*                                   Code                                    */
+/*****************************************************************************/
+
+
+
+signed char get_ostype (void);
+/* Get the ROM build version.
+** -1 -- custom build
+** Negative -- prerelease build
+** Positive -- release build
+*/
+
+void __fastcall__ set_tv (unsigned char);
+/* Set the video mode the machine will use.
+** Call with a TV_xx constant.
+*/
+
+unsigned char __fastcall__ videomode (unsigned char Mode);
+/* Set the video mode, return the old mode. Call with one of the VIDEOMODE_xx
+** constants.
+*/
+
+
+
+/* End of cX16.h */
+#endif
diff --git a/libsrc/Makefile b/libsrc/Makefile
index 0ebec46b..d9ddb3cc 100644
--- a/libsrc/Makefile
+++ b/libsrc/Makefile
@@ -7,6 +7,7 @@ CBMS = c128   \
        c64    \
        cbm510 \
        cbm610 \
+       cx16   \
        pet    \
        plus4  \
        vic20
diff --git a/libsrc/cx16/_scrsize.s b/libsrc/cx16/_scrsize.s
new file mode 100644
index 00000000..8059f04d
--- /dev/null
+++ b/libsrc/cx16/_scrsize.s
@@ -0,0 +1,14 @@
+;
+; 2019-09-16, Greg King
+;
+; Screen size info
+;
+
+        .export         screensize
+
+        .include        "cx16.inc"
+
+screensize:
+        ldx     LLEN
+        ldy     NLINES
+        rts
diff --git a/libsrc/cx16/bankramaddr.s b/libsrc/cx16/bankramaddr.s
new file mode 100644
index 00000000..53d96e91
--- /dev/null
+++ b/libsrc/cx16/bankramaddr.s
@@ -0,0 +1,50 @@
+;
+; 2019-09-16, Greg King
+;
+; This module supplies the load addresses that are expected
+; by a Commander X16 in the first two bytes of banked RAM load files.
+;
+
+        ; The following symbol is used by a linker config. to force
+        ; this module to get included into the output files.
+        .export         __BANKRAMADDR__: abs = 1
+
+.segment        "BRAM00ADDR"
+
+        .addr   *+2
+
+.segment        "BRAM01ADDR"
+
+        .addr   *+2
+
+.segment        "BRAM02ADDR"
+
+        .addr   *+2
+
+.segment        "BRAM03ADDR"
+
+        .addr   *+2
+
+.segment        "BRAM04ADDR"
+
+        .addr   *+2
+
+.segment        "BRAM05ADDR"
+
+        .addr   *+2
+
+.segment        "BRAM06ADDR"
+
+        .addr   *+2
+
+.segment        "BRAM07ADDR"
+
+        .addr   *+2
+
+.segment        "BRAM08ADDR"
+
+        .addr   *+2
+
+.segment        "BRAM09ADDR"
+
+        .addr   *+2
diff --git a/libsrc/cx16/bordercolor.s b/libsrc/cx16/bordercolor.s
new file mode 100644
index 00000000..6691e2ec
--- /dev/null
+++ b/libsrc/cx16/bordercolor.s
@@ -0,0 +1,27 @@
+;
+; 2019-09-23, Greg King
+;
+; unsigned char __fastcall__ bordercolor (unsigned char color);
+; /* Set the color for the border. The old color setting is returned. */
+;
+
+        .export         _bordercolor
+
+        .include        "cx16.inc"
+
+_bordercolor:
+        tax
+
+        ; Point to the border color register.
+
+        stz     VERA::CTRL              ; Use port 0
+        lda     #<VERA::COMPOSER::FRAME
+        sta     VERA::ADDR
+        lda     #>VERA::COMPOSER::FRAME
+        sta     VERA::ADDR+1
+        ldy     #^VERA::COMPOSER::FRAME | VERA::INC0
+        sty     VERA::ADDR+2
+
+        lda     VERA::DATA0             ; get old value
+        stx     VERA::DATA0             ; set new value
+        rts
diff --git a/libsrc/cx16/break.s b/libsrc/cx16/break.s
new file mode 100644
index 00000000..ec569b9a
--- /dev/null
+++ b/libsrc/cx16/break.s
@@ -0,0 +1,109 @@
+;
+; 1998-09-27, Ullrich von Bassewitz
+; 2019-09-08, Greg King
+;
+; void __fastcall__ set_brk (unsigned Addr);
+; void reset_brk (void);
+;
+
+        .export         _set_brk, _reset_brk
+        .destructor     _reset_brk
+        .export         _brk_a, _brk_x, _brk_y, _brk_sr, _brk_pc
+
+        .include        "cx16.inc"
+
+
+.bss
+_brk_a:         .res    1
+_brk_x:         .res    1
+_brk_y:         .res    1
+_brk_sr:        .res    1
+_brk_pc:        .res    2
+
+oldvec:         .res    2               ; Old vector
+
+
+.data
+uservec:        jmp     $FFFF           ; Patched at runtime
+
+
+.code
+
+; Set the break vector
+.proc   _set_brk
+
+        sta     uservec+1
+        stx     uservec+2       ; Set the user vector
+
+        lda     oldvec
+        ora     oldvec+1        ; Did we save the vector already?
+        bne     L1              ; Jump if we installed the handler already
+
+        lda     BRKVec
+        sta     oldvec
+        lda     BRKVec+1
+        sta     oldvec+1        ; Save the old vector
+
+L1:     lda     #<brk_handler   ; Set the break vector to our routine
+        ldx     #>brk_handler
+        sta     BRKVec
+        stx     BRKVec+1
+        rts
+
+.endproc
+
+
+; Reset the break vector
+.proc   _reset_brk
+
+        lda     oldvec
+        ldx     oldvec+1
+        beq     @L9             ; Jump if vector not installed
+        sta     BRKVec
+        stx     BRKVec+1
+        lda     #$00
+        sta     oldvec          ; Clear the old vector
+        stx     oldvec+1
+@L9:    rts
+
+.endproc
+
+
+
+; Break handler, called if a break occurs
+
+.proc   brk_handler
+
+        pla
+        sta     _brk_y
+        pla
+        sta     _brk_x
+        pla
+        sta     _brk_a
+        pla
+        and     #$EF            ; Clear break bit
+        sta     _brk_sr
+        pla                     ; PC low
+        sec
+        sbc     #2              ; Point to start of brk
+        sta     _brk_pc
+        pla                     ; PC high
+        sbc     #0
+        sta     _brk_pc+1
+
+        jsr     uservec         ; Call the user's routine
+
+        lda     _brk_pc+1
+        pha
+        lda     _brk_pc
+        pha
+        lda     _brk_sr
+        pha
+        ldx     _brk_x
+        ldy     _brk_y
+        lda     _brk_a
+        rti                     ; Jump back...
+
+.endproc
+
+
diff --git a/libsrc/cx16/cgetc.s b/libsrc/cx16/cgetc.s
new file mode 100644
index 00000000..14cad5f5
--- /dev/null
+++ b/libsrc/cx16/cgetc.s
@@ -0,0 +1,72 @@
+;
+; 2019-09-23, Greg King
+;
+; char cgetc (void);
+; /* Return a character from the keyboard. */
+;
+
+        .export         _cgetc
+
+        .import         cursor, GETIN
+
+        .include        "cx16.inc"
+        .macpack        generic
+
+
+_cgetc: ldx     KEY_COUNT       ; Get number of characters
+        bnz     L3              ; Jump if there are already chars waiting
+
+; Switch the cursor on if wanted.
+
+        lda     CURS_FLAG       ; Save cursor's current enable flag
+        tay
+        lda     cursor
+        jsr     setcursor
+L1:     lda     KEY_COUNT
+        bze     L1              ; Wait for key
+        tya
+        eor     #%00000001      ; (Cursor flag uses negative logic)
+        jsr     setcursor       ; Restore previous cursor condition
+
+; An internal Kernal function can't be used because it might be moved in future
+; revisions.  Use an official function; but, make sure that it reads
+; the keyboard.
+
+L3:     ldy     IN_DEV          ; Save current input device
+        stz     IN_DEV          ; Keyboard
+        jsr     GETIN           ; Read char, and return in .A
+        sty     IN_DEV          ; Restore input device
+        ldx     #>$0000
+        rts
+
+; Switch the cursor on or off.
+
+setcursor:
+        tax                     ; On or off?
+        bnz     seton           ; Go set it on
+        lda     CURS_FLAG       ; Is the cursor currently off?
+        bnz     crs9            ; Jump if yes
+        inc     CURS_FLAG       ; Mark it as off
+        ldx     CURS_STATE      ; Cursor currently displayed?
+        bze     crs9            ; Jump if not
+
+; Restore the current character in video RAM.
+; Restore that character's colors.
+
+        stz     VERA::CTRL      ; Use port 0
+        lda     CURS_Y
+        sta     VERA::ADDR+1    ; Set row number
+        lda     #VERA::INC1     ; Increment address by one
+        sta     VERA::ADDR+2
+        lda     CURS_X          ; Get character column
+        asl     a
+        sta     VERA::ADDR
+        ldx     CURS_CHAR
+        stx     VERA::DATA0
+        ldx     CURS_COLOR
+        stx     VERA::DATA0
+        stz     CURS_STATE      ; Cursor not displayed
+crs9:   rts
+
+seton:  stz     CURS_FLAG
+        rts
diff --git a/libsrc/cx16/clrscr.s b/libsrc/cx16/clrscr.s
new file mode 100644
index 00000000..51fae6bf
--- /dev/null
+++ b/libsrc/cx16/clrscr.s
@@ -0,0 +1,26 @@
+;
+; 2019-09-22, Greg King
+;
+; void clrscr (void);
+; /* Clear the screen. */
+;
+
+        .export         _clrscr
+
+        .import         CHROUT
+
+        .include        "cx16.inc"
+
+
+; An internal Kernal function can't be used because it might be moved in future
+; revisions.  Use an official function; but, make sure that it prints
+; to the screen.
+
+_clrscr:
+        ldy     OUT_DEV         ; Save current output device
+        ldx     #$03            ; Screen device
+        stx     OUT_DEV
+        lda     #$93
+        jsr     CHROUT          ; Print clear-screen character
+        sty     OUT_DEV         ; Restore output device
+        rts
diff --git a/libsrc/cx16/color.s b/libsrc/cx16/color.s
new file mode 100644
index 00000000..1be01c47
--- /dev/null
+++ b/libsrc/cx16/color.s
@@ -0,0 +1,43 @@
+;
+; 2019-09-16, Greg King
+;
+; unsigned char __fastcall__ textcolor (unsigned char color);
+; unsigned char __fastcall__ bgcolor (unsigned char color);
+;
+
+
+        .export         _textcolor, _bgcolor
+
+        .importzp       tmp1
+        .include        "cx16.inc"
+
+_textcolor:
+        and     #$0F
+        sta     tmp1
+        ldx     CHARCOLOR       ; get old values
+        txa
+        and     #<~$0F          ; keep screen color, remove text color
+        ora     tmp1
+        sta     CHARCOLOR       ; set new values
+        txa
+        and     #$0F
+        rts
+
+
+_bgcolor:
+        asl     a               ; move number to screen-color nybble
+        asl     a
+        asl     a
+        asl     a
+        sta     tmp1
+        ldx     CHARCOLOR       ; get old values
+        txa
+        and     #<~$F0          ; remove screen color, keep text color
+        ora     tmp1
+        sta     CHARCOLOR       ; set new values
+        txa
+        lsr     a               ; get screen color
+        lsr     a
+        lsr     a
+        lsr     a
+        rts
diff --git a/libsrc/cx16/conio.s b/libsrc/cx16/conio.s
new file mode 100644
index 00000000..e760af21
--- /dev/null
+++ b/libsrc/cx16/conio.s
@@ -0,0 +1,9 @@
+;
+; 2019-09-23, Greg King
+;
+; Low-level stuff for screen output/console input
+;
+
+        .exportzp       CURS_X, CURS_Y
+
+        .include        "cx16.inc"
diff --git a/libsrc/cx16/cpeekc.s b/libsrc/cx16/cpeekc.s
new file mode 100644
index 00000000..2f02623b
--- /dev/null
+++ b/libsrc/cx16/cpeekc.s
@@ -0,0 +1,48 @@
+;
+; 2016-02-28, Groepaz
+; 2019-09-25, Greg King
+;
+; char cpeekc (void);
+; /* Return the character from the current cursor position. */
+;
+
+        .export         _cpeekc
+
+        .include        "cx16.inc"
+
+
+_cpeekc:
+        php
+        sei                     ; don't let cursor blinking interfere
+        stz     VERA::CTRL      ; use port 0
+        lda     CURS_Y
+        sta     VERA::ADDR+1    ; set row number
+        stz     VERA::ADDR+2
+        lda     CURS_X          ; get character column
+        asl     a
+        sta     VERA::ADDR
+        lda     VERA::DATA0     ; get screen code
+        plp
+        ldx     #>$0000
+        and     #<~%10000000    ; remove reverse bit
+
+; Convert the screen code into a PetSCII code.
+; $00 - $1F: +$40
+; $20 - $3F
+; $40 - $5f: +$20
+; $60 - $7F: +$40
+
+        cmp     #$20
+        bcs     @sk1            ;(bge)
+        ora     #$40
+        rts
+
+@sk1:   cmp     #$40
+        bcc     @end            ;(blt)
+        cmp     #$60
+        bcc     @sk2            ;(blt)
+        ;sec
+        adc     #$20 - $01
+@sk2:   ;clc                    ; both above cmp and adc clear carry flag
+        adc     #$20
+@end:   rts
diff --git a/libsrc/cx16/cpeekcolor.s b/libsrc/cx16/cpeekcolor.s
new file mode 100644
index 00000000..4e3a39a2
--- /dev/null
+++ b/libsrc/cx16/cpeekcolor.s
@@ -0,0 +1,27 @@
+;
+; 2019-09-25, Greg King
+;
+; unsigned char cpeekcolor (void);
+; /* Return the colors from the current cursor position. */
+;
+
+        .export         _cpeekcolor
+
+        .include        "cx16.inc"
+
+
+_cpeekcolor:
+        php
+        sei                     ; don't let cursor blinking interfere
+        stz     VERA::CTRL      ; use port 0
+        lda     CURS_Y
+        sta     VERA::ADDR+1    ; set row number
+        stz     VERA::ADDR+2
+        lda     CURS_X          ; get character column
+        sec                     ; color attribute is second byte
+        rol     a
+        sta     VERA::ADDR
+        lda     VERA::DATA0     ; get color
+        plp
+        ldx     #>$0000
+        rts
diff --git a/libsrc/cx16/cpeekrevers.s b/libsrc/cx16/cpeekrevers.s
new file mode 100644
index 00000000..d67dd295
--- /dev/null
+++ b/libsrc/cx16/cpeekrevers.s
@@ -0,0 +1,32 @@
+;
+; 2016-02-28, Groepaz
+; 2019-09-25, Greg King
+;
+; unsigned char cpeekrevers (void);
+; /* Return the reverse attribute from the current cursor position.
+; ** If the character is reversed, then return 1; return 0 otherwise.
+; */
+;
+
+        .export         _cpeekrevers
+
+        .include        "cx16.inc"
+
+
+_cpeekrevers:
+        php
+        sei                     ; don't let cursor blinking interfere
+        stz     VERA::CTRL      ; use port 0
+        lda     CURS_Y
+        sta     VERA::ADDR+1    ; set row number
+        stz     VERA::ADDR+2
+        lda     CURS_X          ; get character column
+        asl     a
+        sta     VERA::ADDR
+        lda     VERA::DATA0     ; get screen code
+        plp
+        and     #%10000000      ; get reverse bit
+        asl     a
+        tax                     ; ldx #>$0000
+        rol     a               ; return boolean value
+        rts
diff --git a/libsrc/cx16/cpeeks.s b/libsrc/cx16/cpeeks.s
new file mode 100644
index 00000000..e69de29b
diff --git a/libsrc/cx16/cputc.s b/libsrc/cx16/cputc.s
new file mode 100644
index 00000000..cf0a5fa2
--- /dev/null
+++ b/libsrc/cx16/cputc.s
@@ -0,0 +1,98 @@
+;
+; 2019-09-23, Greg King
+;
+; void __fastcall__ cputcxy (unsigned char x, unsigned char y, char c);
+; void __fastcall__ cputc (char c);
+;
+
+        .export         _cputcxy, _cputc, cputdirect, putchar
+        .export         newline, plot
+
+        .import         gotoxy, PLOT
+
+        .include        "cx16.inc"
+        .macpack        generic
+
+
+; First, move to a new position.
+
+_cputcxy:
+        pha                     ; Save C
+        jsr     gotoxy          ; Set cursor, drop x and y
+        pla                     ; Restore C
+
+; Print a character.
+
+_cputc: cmp     #$0D            ; LF?
+        beq     newline
+        cmp     #$0A            ; CR?
+        beq     plotx0
+
+; Printable char of some sort
+
+        cmp     #' '
+        blt     cputdirect      ; Other control char
+        tay
+        bmi     L10
+        cmp     #$60
+        blt     L2
+        and     #<~%00100000
+        bra     cputdirect
+
+; Handle character if high bit set
+
+L10:    and     #<~%10000000    ; Remove high bit
+        ora     #%01000000
+        bra     cputdirect
+
+L2:     and     #<~%01000000
+
+cputdirect:
+        jsr     putchar         ; Write character to screen, return .Y
+
+; Advance cursor position.
+
+        iny
+        cpy     LLEN            ; Reached end of line?
+        bne     L3
+        jsr     newline         ; Next line
+        ldy     #$00            ; + CR
+L3:     sty     CURS_X
+        rts
+
+; Move down.
+
+newline:
+        inc     SCREEN_PTR+1
+        inc     CURS_Y
+        rts
+
+
+; Set the cursor's position, calculate RAM pointer.
+
+plotx0: stz     CURS_X
+plot:   ldy     CURS_X
+        ldx     CURS_Y
+        clc
+        jmp     PLOT            ; Set the new cursor
+
+
+; Write one screen-code and color to the video RAM without doing anything else.
+; Return the x position in Y.
+
+putchar:
+        ora     RVS             ; Set revers bit
+        tax
+        stz     VERA::CTRL      ; Use port 0
+        lda     CURS_Y
+        sta     VERA::ADDR+1    ; Set row number
+        lda     #VERA::INC1     ; Increment address by one
+        sta     VERA::ADDR+2
+        ldy     CURS_X          ; Get character column
+        tya
+        asl     a
+        sta     VERA::ADDR
+        stx     VERA::DATA0
+        lda     CHARCOLOR
+        sta     VERA::DATA0
+        rts
diff --git a/libsrc/cx16/crt0.s b/libsrc/cx16/crt0.s
new file mode 100644
index 00000000..181e6308
--- /dev/null
+++ b/libsrc/cx16/crt0.s
@@ -0,0 +1,115 @@
+;
+; Start-up code for cc65 (CX16 version)
+;
+
+        .export         _exit
+        .export         __STARTUP__ : absolute = 1      ; Mark as start-up
+
+        .import         initlib, donelib
+        .import         zerobss, callmain
+        .import         BSOUT
+        .import         __MAIN_START__, __MAIN_SIZE__   ; Linker-generated
+        .importzp       ST
+
+        .include        "zeropage.inc"
+        .include        "cx16.inc"
+
+
+; ------------------------------------------------------------------------
+; Start-up code
+
+.segment        "STARTUP"
+
+Start:  tsx
+        stx     spsave          ; Save the system stack ptr
+
+; Save space by putting some of the start-up code in the ONCE segment,
+; which will be re-used by the BSS segment, the heap, and the C stack.
+
+        jsr     init
+
+; Clear the BSS data.
+
+        jsr     zerobss
+
+; Push the command-line arguments; and, call main().
+
+        jsr     callmain
+
+; Back from main() [this is also the exit() entry]. Run the module destructors.
+
+_exit:  pha                     ; Save the return code on stack
+        jsr     donelib
+
+; Copy back the zero-page stuff.
+
+        ldx     #zpspace-1
+L2:     lda     zpsave,x
+        sta     sp,x
+        dex
+        bpl     L2
+
+; Place the program return code into BASIC's status variable.
+
+        pla
+        sta     ST
+
+; Restore the system stuff.
+
+        ldx     spsave
+        txs                     ; Restore stack pointer
+        ldx     banksave
+        stx     VIA1::PRA2      ; Restore former RAM bank
+
+; Back to BASIC.
+
+        rts
+
+
+; ------------------------------------------------------------------------
+
+.segment        "ONCE"
+
+init:
+
+; Change to the first RAM bank.
+
+        lda     VIA1::PRA2
+        sta     banksave        ; Save the current bank number
+        lda     #$00            ; Choose RAM bank zero
+        sta     VIA1::PRA2
+
+; Save the zero-page locations that we need.
+
+        ldx     #zpspace-1
+L1:     lda     sp,x
+        sta     zpsave,x
+        dex
+        bpl     L1
+
+; Set up the stack.
+
+        lda     #<(__MAIN_START__ + __MAIN_SIZE__)
+        ldx     #>(__MAIN_START__ + __MAIN_SIZE__)
+        sta     sp
+        stx     sp+1            ; Set argument stack ptr
+
+; Switch to the second charset.
+
+        lda     #$0E
+        jsr     BSOUT
+
+; Call the module constructors.
+
+        jmp     initlib
+
+
+; ------------------------------------------------------------------------
+; Data
+
+.segment        "INIT"
+
+banksave:
+        .res    1
+spsave: .res    1
+zpsave: .res    zpspace
diff --git a/libsrc/cx16/devnum.s b/libsrc/cx16/devnum.s
new file mode 100644
index 00000000..6a59d6ec
--- /dev/null
+++ b/libsrc/cx16/devnum.s
@@ -0,0 +1,8 @@
+;
+; 2010-02-14, Oliver Schmidt
+; 2019-09-08, Greg King
+;
+
+        .include        "cx16.inc"
+
+        .exportzp       devnum := DEVNUM
diff --git a/libsrc/cx16/get_ostype.s b/libsrc/cx16/get_ostype.s
new file mode 100644
index 00000000..a778b6ea
--- /dev/null
+++ b/libsrc/cx16/get_ostype.s
@@ -0,0 +1,20 @@
+;
+; 2019-09-09, Greg King
+;
+; signed char get_ostype(void)
+; /* Return a "build version". */
+;
+; Positive number -- release build
+; Negative number -- prerelease build
+; -1 -- custom build
+;
+
+        .export         _get_ostype
+
+.proc   _get_ostype
+        ldx     #>$0000
+        lda     $ff80
+        bpl     :+
+        dex                     ; negative
+:       rts
+.endproc
diff --git a/libsrc/cx16/get_tv.s b/libsrc/cx16/get_tv.s
new file mode 100644
index 00000000..79a577df
--- /dev/null
+++ b/libsrc/cx16/get_tv.s
@@ -0,0 +1,31 @@
+;
+; 2019-09-20, Greg King
+;
+; unsigned char get_tv (void);
+; /* Return the video mode the machine is using. */
+;
+
+        .export         _get_tv
+
+        .include        "cx16.inc"
+
+
+.proc   _get_tv
+        php
+        sei                             ; Don't let interrupts interfere
+
+        ; Point to the video output register.
+
+        stz     VERA::CTRL              ; Use port 0
+        lda     #<VERA::COMPOSER::VIDEO
+        ldx     #>VERA::COMPOSER::VIDEO
+        ldy     #^VERA::COMPOSER::VIDEO
+        sta     VERA::ADDR
+        stx     VERA::ADDR+1
+        sty     VERA::ADDR+2
+
+        lda     VERA::DATA0
+        plp                             ; Re-enable interrupts
+        and     #$07                    ; Get the type of output signal
+        rts
+.endproc
diff --git a/libsrc/cx16/irq.s b/libsrc/cx16/irq.s
new file mode 100644
index 00000000..0d9d54c6
--- /dev/null
+++ b/libsrc/cx16/irq.s
@@ -0,0 +1,48 @@
+;
+; IRQ handling (CX16 version)
+;
+
+        .export         initirq, doneirq
+        .import         callirq
+
+        .include        "cx16.inc"
+
+; ------------------------------------------------------------------------
+
+.segment        "ONCE"
+
+initirq:
+        lda     IRQVec
+        ldx     IRQVec+1
+        sta     IRQInd+1
+        stx     IRQInd+2
+        lda     #<IRQStub
+        ldx     #>IRQStub
+        jmp     setvec
+
+; ------------------------------------------------------------------------
+
+.code
+
+doneirq:
+        lda     IRQInd+1
+        ldx     IRQInd+2
+setvec: sei
+        sta     IRQVec
+        stx     IRQVec+1
+        cli
+        rts
+
+; ------------------------------------------------------------------------
+
+.segment        "LOWCODE"
+
+IRQStub:
+        jsr     callirq                 ; Call the functions
+        jmp     IRQInd                  ; Jump to the saved IRQ vector
+
+; ------------------------------------------------------------------------
+
+.data
+
+IRQInd: jmp     $0000
diff --git a/libsrc/cx16/joy/cx16-stdjoy.s b/libsrc/cx16/joy/cx16-stdjoy.s
new file mode 100644
index 00000000..8c7ddd2f
--- /dev/null
+++ b/libsrc/cx16/joy/cx16-stdjoy.s
@@ -0,0 +1,119 @@
+;
+; Standard joystick driver for the CX16.
+; May be used multiple times when statically linked to the application.
+;
+; 2019-09-23, Greg King
+;
+
+        .include        "joy-kernel.inc"
+        .include        "joy-error.inc"
+
+        .include        "cbm_kernal.inc"
+        .include        "cx16.inc"
+
+        .macpack        generic
+        .macpack        module
+
+
+; ------------------------------------------------------------------------
+; Header. Includes jump table
+
+        module_header   _cx16_stdjoy_joy
+
+; Driver signature
+
+        .byte   $6A, $6F, $79           ; ASCII "joy"
+        .byte   JOY_API_VERSION         ; Driver API version number
+
+; Library reference
+
+        .addr   $0000
+
+; Jump table.
+
+        .addr   INSTALL
+        .addr   UNINSTALL
+        .addr   COUNT
+        .addr   READ
+
+; ------------------------------------------------------------------------
+; Constant
+
+JOY_COUNT       = 2             ; Number of joysticks we support
+
+; ------------------------------------------------------------------------
+; Data.
+
+
+.code
+
+; ------------------------------------------------------------------------
+; INSTALL routine. Is called after the driver is loaded into memory.
+; If possible, check if the hardware is present, and determine the amount
+; of memory available.
+; Must return a JOY_ERR_xx code in a/x.
+;
+
+INSTALL:
+        lda     #<JOY_ERR_OK
+        ldx     #>JOY_ERR_OK
+;       rts                     ; Run into UNINSTALL instead
+
+; ------------------------------------------------------------------------
+; UNINSTALL routine. Is called before the driver is removed from memory.
+; Can do clean-up or whatever. Must not return anything.
+;
+
+UNINSTALL:
+        rts
+
+; ------------------------------------------------------------------------
+; COUNT: Return the total number of possible joysticks in a/x.
+;
+
+COUNT:  lda     #<JOY_COUNT
+        ldx     #>JOY_COUNT
+        rts
+
+; ------------------------------------------------------------------------
+; READ: Read a particular joystick passed in A.
+;
+; TODO: Find a way to report the SNES controller's extra four lines.
+;
+
+READ:   pha
+        jsr     GETJOY
+        pla
+        bne     pad2
+
+; Read game pad 1
+
+pad1:   lda     JOY1 + 1
+        bit     #%00001110
+        beq     nes1
+        asl     JOY1            ; Get SNES's B button
+        ror     a               ; Put it next to the A button
+        asl     JOY1            ; Drop SNES's Y button
+        asl     a               ; Get the B button
+        ror     JOY1
+        asl     a               ; Get SNES's A button
+        ror     JOY1            ; Make byte look like NES pad
+nes1:   lda     JOY1
+        eor     #%11111111      ; We don't want the pad's negative logic
+        rts
+
+; Read game pad 2
+
+pad2:   lda     JOY2 + 1
+        bit     #%00001110
+        beq     nes2
+        asl     JOY2
+        ror     a
+        asl     JOY2
+        asl     a
+        ror     JOY2
+        asl     a
+        ror     JOY2
+nes2:   lda     JOY2
+        eor     #%11111111
+        rts
diff --git a/libsrc/cx16/joy_stat_stddrv.s b/libsrc/cx16/joy_stat_stddrv.s
new file mode 100644
index 00000000..0e1a3e94
--- /dev/null
+++ b/libsrc/cx16/joy_stat_stddrv.s
@@ -0,0 +1,10 @@
+;
+; Address of the static standard joystick driver
+;
+; 2019-09-19, Greg King
+;
+; const void joy_static_stddrv[];
+;
+
+        .import _cx16_stdjoy_joy
+        .export _joy_static_stddrv := _cx16_stdjoy_joy
diff --git a/libsrc/cx16/joy_stddrv.s b/libsrc/cx16/joy_stddrv.s
new file mode 100644
index 00000000..4edf9afc
--- /dev/null
+++ b/libsrc/cx16/joy_stddrv.s
@@ -0,0 +1,13 @@
+;
+; Name of the standard joystick driver
+;
+; 2019-09-19, Greg King
+;
+; const char joy_stddrv[];
+;
+
+        .export _joy_stddrv
+
+.rodata
+
+_joy_stddrv:    .asciiz "cx16-stdjoy.joy"
diff --git a/libsrc/cx16/kbhit.s b/libsrc/cx16/kbhit.s
new file mode 100644
index 00000000..8ceba64b
--- /dev/null
+++ b/libsrc/cx16/kbhit.s
@@ -0,0 +1,17 @@
+;
+; 2019-09-20, Greg King
+;
+; unsigned char kbhit (void);
+; /* Returns non-zero (true) if a typed character is waiting. */
+;
+
+        .export         _kbhit
+
+        .include        "cx16.inc"
+
+
+.proc   _kbhit
+        ldx     #>$0000         ; High byte of return
+        lda     KEY_COUNT       ; Get number of characters
+        rts
+.endproc
diff --git a/libsrc/cx16/kernal.s b/libsrc/cx16/kernal.s
new file mode 100644
index 00000000..faf91385
--- /dev/null
+++ b/libsrc/cx16/kernal.s
@@ -0,0 +1,59 @@
+;
+; 2019-09-22, Greg King
+;
+; CX16 Kernal functions
+;
+
+        .include        "cbm_kernal.inc"
+
+        .export         GETJOY
+
+        .export         CLSALL
+        .export         SWAPPER
+        .export         JSRFAR
+        .export         INDFET
+        .export         INDSTA
+        .export         INDCMP
+        .export         PRIMM
+
+        .export         CINT
+        .export         IOINIT
+        .export         RAMTAS
+        .export         RESTOR
+        .export         VECTOR
+        .export         SETMSG
+        .export         SECOND
+        .export         TKSA
+        .export         MEMTOP
+        .export         MEMBOT
+        .export         SCNKEY
+        .export         SETTMO
+        .export         ACPTR
+        .export         CIOUT
+        .export         UNTLK
+        .export         UNLSN
+        .export         LISTEN
+        .export         TALK
+        .export         READST
+        .export         SETLFS
+        .export         SETNAM
+        .export         OPEN
+        .export         CLOSE
+        .export         CHKIN
+        .export         CKOUT
+        .export         CLRCH
+        .export         BASIN
+        .export         CHRIN
+        .export         BSOUT
+        .export         CHROUT
+        .export         LOAD
+        .export         SAVE
+        .export         SETTIM
+        .export         RDTIM
+        .export         STOP
+        .export         GETIN
+        .export         CLALL
+        .export         UDTIM
+        .export         SCREEN
+        .export         PLOT
+        .export         IOBASE
diff --git a/libsrc/cx16/libref.s b/libsrc/cx16/libref.s
new file mode 100644
index 00000000..54ebb91d
--- /dev/null
+++ b/libsrc/cx16/libref.s
@@ -0,0 +1,18 @@
+;
+; 2013-05-31, Oliver Schmidt
+; 2019-09-22, Greg King
+;
+
+        .export         em_libref
+        .export         joy_libref
+        .export         mouse_libref
+        .export         ser_libref
+        .export         tgi_libref
+
+        .import         _exit
+
+em_libref       := _exit
+joy_libref      := _exit
+mouse_libref    := _exit
+ser_libref      := _exit
+tgi_libref      := _exit
diff --git a/libsrc/cx16/mainargs.s b/libsrc/cx16/mainargs.s
new file mode 100644
index 00000000..fe4a071c
--- /dev/null
+++ b/libsrc/cx16/mainargs.s
@@ -0,0 +1,137 @@
+; mainargs.s
+;
+; Ullrich von Bassewitz, 2003-03-07
+; Based on code from Stefan A. Haubenthal, <polluks@web.de>
+; 2005-02-26, Ullrich von Bassewitz
+; 2019-09-08, Greg King
+;
+; Scan a group of arguments that are in BASIC's input-buffer.
+; Build an array that points to the beginning of each argument.
+; Send, to main(), that array and the count of the arguments.
+;
+; Command-lines look like these lines:
+;
+; run
+; run : rem
+; run:rem arg1 " arg 2 is quoted "  arg3 "" arg5
+;
+; "run" and "rem" are entokenned; the args. are not.  Leading and trailing
+; spaces outside of quotes are ignored.
+;
+; TO-DO:
+; - The "file-name" might be a path-name; don't copy the directory-components.
+; - Add a control-character quoting mechanism.
+
+        .constructor    initmainargs, 24
+        .import         __argc, __argv
+
+        .include        "cx16.inc"
+
+
+MAXARGS  = 10                   ; Maximum number of arguments allowed
+REM      = $8f                  ; BASIC token-code
+NAME_LEN = 16                   ; Maximum length of command-name
+
+; Get possible command-line arguments. Goes into the special ONCE segment,
+; which may be reused after the startup code is run
+
+.segment        "ONCE"
+
+initmainargs:
+
+; Assume that the program was loaded, a moment ago, by the traditional LOAD
+; statement.  Save the "most-recent filename" as argument #0.
+
+        lda     #0              ; The terminating NUL character
+        ldy     FNAM_LEN
+        cpy     #NAME_LEN + 1
+        bcc     L1
+        ldy     #NAME_LEN       ; Limit the length
+        bne     L1              ; Branch always
+L0:     lda     (FNAM),y
+L1:     sta     name,y
+        dey
+        bpl     L0
+        inc     __argc          ; argc always is equal to, at least, 1
+
+; Find the "rem" token.
+
+        ldx     #0
+L2:     lda     BASIC_BUF,x
+        beq     done            ; No "rem," no args.
+        inx
+        cmp     #REM
+        bne     L2
+        ldy     #1 * 2
+
+; Find the next argument
+
+next:   lda     BASIC_BUF,x
+        beq     done            ; End of line reached
+        inx
+        cmp     #' '            ; Skip leading spaces
+        beq     next
+
+; Found start of next argument. We've incremented the pointer in X already, so
+; it points to the second character of the argument. This is useful since we
+; will check now for a quoted argument, in which case we will have to skip this
+; first character.
+
+found:  cmp     #'"'            ; Is the argument quoted?
+        beq     setterm         ; Jump if so
+        dex                     ; Reset pointer to first argument character
+        lda     #' '            ; A space ends the argument
+setterm:sta     term            ; Set end of argument marker
+
+; Now store a pointer to the argument into the next slot. Since the BASIC
+; input buffer is located at the start of a RAM page, no calculations are
+; necessary.
+
+        txa                     ; Get low byte
+        sta     argv,y          ; argv[y]= &arg
+        iny
+        lda     #>BASIC_BUF
+        sta     argv,y
+        iny
+        inc     __argc          ; Found another arg
+
+; Search for the end of the argument
+
+argloop:lda     BASIC_BUF,x
+        beq     done
+        inx
+        cmp     term
+        bne     argloop
+
+; We've found the end of the argument. X points one character behind it, and
+; A contains the terminating character. To make the argument a valid C string,
+; replace the terminating character by a zero.
+
+        lda     #0
+        sta     BASIC_BUF-1,x
+
+; Check if the maximum number of command line arguments is reached. If not,
+; parse the next one.
+
+        lda     __argc          ; Get low byte of argument count
+        cmp     #MAXARGS        ; Maximum number of arguments reached?
+        bcc     next            ; Parse next one if not
+
+; (The last vector in argv[] already is NULL.)
+
+done:   lda     #<argv
+        ldx     #>argv
+        sta     __argv
+        stx     __argv + 1
+        rts
+
+.segment        "INIT"
+
+term:   .res    1
+name:   .res    NAME_LEN + 1
+
+.data
+
+; char* argv[MAXARGS+1]={name};
+argv:   .addr   name
+        .res    MAXARGS * 2
diff --git a/libsrc/cx16/revers.s b/libsrc/cx16/revers.s
new file mode 100644
index 00000000..300237ee
--- /dev/null
+++ b/libsrc/cx16/revers.s
@@ -0,0 +1,23 @@
+;
+; 2019-09-16, Greg King
+;
+; unsigned char __fastcall__ revers (unsigned char onoff);
+;
+
+        .export         _revers
+
+        .include        "cx16.inc"
+
+.proc   _revers
+        ldy     #$00            ; Assume revers off
+        tax                     ; Test on/off
+        beq     :+              ; Jump if off
+        ldy     #$80            ; Load "on" value
+        ldx     #>$0000         ; Zero high byte of result
+:       lda     RVS             ; Load old value
+        sty     RVS             ; Set new value
+        clc
+        rol     a               ; Convert bit-mask into boolean
+        rol     a
+        rts
+.endproc
diff --git a/libsrc/cx16/set_tv.s b/libsrc/cx16/set_tv.s
new file mode 100644
index 00000000..0cf49aff
--- /dev/null
+++ b/libsrc/cx16/set_tv.s
@@ -0,0 +1,32 @@
+;
+; 2019-09-20, Greg King
+;
+; void __fastcall__ set_tv (unsigned char);
+; /* Set the video mode the machine will use. */
+;
+
+        .export         _set_tv
+
+        .include        "cx16.inc"
+
+
+.proc   _set_tv
+        php
+        pha
+        sei                             ; Don't let interrupts interfere
+
+        ; Point to the video output register.
+
+        stz     VERA::CTRL              ; Use port 0
+        lda     #<VERA::COMPOSER::VIDEO
+        ldx     #>VERA::COMPOSER::VIDEO
+        ldy     #^VERA::COMPOSER::VIDEO
+        sta     VERA::ADDR
+        stx     VERA::ADDR+1
+        sty     VERA::ADDR+2
+
+        pla
+        sta     VERA::DATA0
+        plp                             ; Re-enable interrupts
+        rts
+.endproc
diff --git a/libsrc/cx16/status.s b/libsrc/cx16/status.s
new file mode 100644
index 00000000..6292dc27
--- /dev/null
+++ b/libsrc/cx16/status.s
@@ -0,0 +1,6 @@
+;
+; 2012-09-30, Oliver Schmidt
+; 2019-09-08, Greg King
+;
+
+        .exportzp       ST := $90       ; IEC status byte
diff --git a/libsrc/cx16/sysuname.s b/libsrc/cx16/sysuname.s
new file mode 100644
index 00000000..f8500936
--- /dev/null
+++ b/libsrc/cx16/sysuname.s
@@ -0,0 +1,37 @@
+;
+; 2003-08-12, Ullrich von Bassewitz
+; 2019-09-08, Greg King
+;
+; unsigned char __fastcall__ _sysuname (struct utsname* buf);
+;
+
+        .export         __sysuname, utsdata
+
+        .import         utscopy
+
+__sysuname 	:= 	utscopy
+
+;--------------------------------------------------------------------------
+; Data. We define a fixed utsname struct here, and just copy it.
+
+.rodata
+
+utsdata:
+        ; sysname
+        .asciiz         "cc65"
+
+        ; nodename
+        .asciiz         ""
+
+        ; release
+        .byte           ((.VERSION >> 8) & $0F) + '0'
+        .byte           '.'
+        .byte           ((.VERSION >> 4) & $0F) + '0'
+        .byte           $00
+
+        ; version
+        .byte           (.VERSION & $0F) + '0'
+        .byte           $00
+
+        ; machine
+        .asciiz         "Commander X16"
diff --git a/libsrc/cx16/videomode.s b/libsrc/cx16/videomode.s
new file mode 100644
index 00000000..4582ec1b
--- /dev/null
+++ b/libsrc/cx16/videomode.s
@@ -0,0 +1,30 @@
+;
+; 2009-09-07, Ullrich von Bassewitz
+; 2019-09-23, Greg King
+;
+; unsigned __fastcall__ videomode (unsigned Mode);
+; /* Set the video mode, return the old mode. */
+;
+
+        .export         _videomode
+        .import         SWAPPER
+
+        .include        "cx16.inc"
+
+
+.proc   _videomode
+        cmp     LLEN                    ; Do we have this mode already?
+        beq     @L9
+
+        lda     LLEN                    ; Get current mode ...
+        pha                             ; ... and save it
+
+        jsr     SWAPPER                 ; Toggle the mode
+
+        pla                             ; Get old mode into A
+
+; Done, old mode is in .A
+
+@L9:    ldx     #>$0000                 ; Clear high byte
+        rts
+.endproc
diff --git a/libsrc/cx16/waitvsync.s b/libsrc/cx16/waitvsync.s
new file mode 100644
index 00000000..3fb6354f
--- /dev/null
+++ b/libsrc/cx16/waitvsync.s
@@ -0,0 +1,17 @@
+;
+; 2019-09-26, Greg King
+;
+; void waitvsync (void);
+;
+; VERA's vertical sync. causes IRQs which increment the jiffy clock.
+;
+
+        .export         _waitvsync
+
+        .include        "cx16.inc"
+
+_waitvsync:
+        lda     TIME + 2
+:       cmp     TIME + 2
+        beq     :-              ; Wait for next jiffy
+        rts
diff --git a/src/ca65/main.c b/src/ca65/main.c
index 33e0a74b..ab19d0b4 100644
--- a/src/ca65/main.c
+++ b/src/ca65/main.c
@@ -296,7 +296,7 @@ static void SetSys (const char* Sys)
 
         case TGT_ATMOS:
             NewSymbol ("__ATMOS__", 1);
-            break; 
+            break;
 
         case TGT_TELESTRAT:
              NewSymbol ("__TELESTRAT__", 1);
@@ -330,6 +330,10 @@ static void SetSys (const char* Sys)
             NewSymbol ("__PCE__", 1);
             break;
 
+        case TGT_CX16:
+            CBMSystem ("__CX16__");
+            break;
+
         default:
             AbEnd ("Invalid target name: '%s'", Sys);
 
diff --git a/src/cc65/main.c b/src/cc65/main.c
index 871e21eb..a4f794fb 100644
--- a/src/cc65/main.c
+++ b/src/cc65/main.c
@@ -285,6 +285,10 @@ static void SetSys (const char* Sys)
             DefineNumericMacro ("__PCE__", 1);
             break;
 
+        case TGT_CX16:
+            cbmsys ("__CX16__");
+            break;
+
         default:
             AbEnd ("Unknown target system type %d", Target);
     }
diff --git a/src/common/target.c b/src/common/target.c
index 56ea0a2f..a21ef212 100644
--- a/src/common/target.c
+++ b/src/common/target.c
@@ -135,11 +135,11 @@ static const unsigned char CTPET[256] = {
 typedef struct TargetEntry TargetEntry;
 struct TargetEntry {
     char        Name[13];               /* Target name */
-    target_t    Id;                     /* Target id */
+    target_t    Id;                     /* Target ID */
 };
 
-/* Table that maps target names to ids. Sorted alphabetically for bsearch.
-** Allows multiple entries for one target id (target name aliases).
+/* Table that maps target names to IDs. Sorted alphabetically for bsearch().
+** Allows multiple entries for one target ID (target name aliases).
 */
 static const TargetEntry TargetMap[] = {
     {   "apple2",       TGT_APPLE2       },
@@ -157,6 +157,7 @@ static const TargetEntry TargetMap[] = {
     {   "cbm510",       TGT_CBM510       },
     {   "cbm610",       TGT_CBM610       },
     {   "creativision", TGT_CREATIVISION },
+    {   "cx16",         TGT_CX16         },
     {   "gamate",       TGT_GAMATE       },
     {   "geos",         TGT_GEOS_CBM     },
     {   "geos-apple",   TGT_GEOS_APPLE   },
@@ -179,7 +180,7 @@ static const TargetEntry TargetMap[] = {
 #define MAP_ENTRY_COUNT         (sizeof (TargetMap) / sizeof (TargetMap[0]))
 
 
-/* Table with target properties by target id */
+/* Table with target properties by target ID */
 static const TargetProperties PropertyTable[TGT_COUNT] = {
     { "none",           CPU_6502,       BINFMT_BINARY,      CTNone  },
     { "module",         CPU_6502,       BINFMT_O65,         CTNone  },
@@ -213,6 +214,7 @@ static const TargetProperties PropertyTable[TGT_COUNT] = {
     { "pce",            CPU_HUC6280,    BINFMT_BINARY,      CTNone  },
     { "gamate",         CPU_6502,       BINFMT_BINARY,      CTNone  },
     { "c65",            CPU_4510,       BINFMT_BINARY,      CTPET   },
+    { "cx16",           CPU_65C02,      BINFMT_BINARY,      CTPET   },
 };
 
 /* Target system */
@@ -235,7 +237,7 @@ static int Compare (const void* Key, const void* Entry)
 
 
 target_t FindTarget (const char* Name)
-/* Find a target by name and return the target id. TGT_UNKNOWN is returned if
+/* Find a target by name and return the target ID. TGT_UNKNOWN is returned if
 ** the given name is no valid target.
 */
 {
@@ -243,7 +245,7 @@ target_t FindTarget (const char* Name)
     const TargetEntry* T;
     T = bsearch (Name, TargetMap, MAP_ENTRY_COUNT, sizeof (TargetMap[0]), Compare);
 
-    /* Return the target id */
+    /* Return the target ID */
     return (T == 0)? TGT_UNKNOWN : T->Id;
 }
 
@@ -252,7 +254,7 @@ target_t FindTarget (const char* Name)
 const TargetProperties* GetTargetProperties (target_t Target)
 /* Return the properties for a target */
 {
-    /* Must have a valid target id */
+    /* Must have a valid target ID */
     PRECONDITION (Target >= 0 && Target < TGT_COUNT);
 
     /* Return the array entry */
diff --git a/src/common/target.h b/src/common/target.h
index 5b086e40..50c400e2 100644
--- a/src/common/target.h
+++ b/src/common/target.h
@@ -84,6 +84,7 @@ typedef enum {
     TGT_PCENGINE,
     TGT_GAMATE,
     TGT_C65,
+    TGT_CX16,
     TGT_COUNT                   /* Number of target systems */
 } target_t;
 
@@ -102,7 +103,7 @@ extern target_t         Target;
 /* Types of available output formats */
 #define BINFMT_DEFAULT          0       /* Default (binary) */
 #define BINFMT_BINARY           1       /* Straight binary format */
-#define BINFMT_O65              2       /* Andre Fachats o65 format */
+#define BINFMT_O65              2       /* Andre Fachat's o65 format */
 #define BINFMT_ATARIEXE         3       /* Standard Atari binary load */
 
 
@@ -127,5 +128,4 @@ const char* GetTargetName (target_t Target);
 
 
 /* End of target.h */
-
 #endif
diff --git a/testcode/lib/joy-test.c b/testcode/lib/joy-test.c
index 3d584bf9..53d63c5c 100644
--- a/testcode/lib/joy-test.c
+++ b/testcode/lib/joy-test.c
@@ -1,4 +1,3 @@
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
@@ -14,16 +13,15 @@
 #  define DYN_DRV       0
 
 /*
-** link existing drivers like this:
+** Link existing drivers this way:
 **
 ** cl65 -DJOYSTICK_DRIVER=c64_hitjoy_joy -o joy-test.prg joy-test.c
 **
-** for testing a new driver you will have to uncomment the define below, and
-** link your driver like this:
+** For testing a new driver, you need to uncomment the declaration below,
+** and link your driver this way:
 **
 ** co65 ../../target/c64/drv/joy/c64-hitjoy.joy -o hitjoy.s --code-label _hitjoy
 ** cl65 -DJOYSTICK_DRIVER=hitjoy -o joy-test.prg joy-test.c hitjoy.s
-**
 */
 
 /* extern char JOYSTICK_DRIVER; */
@@ -40,10 +38,8 @@
 int main (void)
 {
     unsigned char j;
-    unsigned char count;
-    unsigned char i;
+    unsigned char i, count;
     unsigned char Res;
-    unsigned char ch, kb;
 
     clrscr ();
 
@@ -58,47 +54,69 @@ int main (void)
     if (Res != JOY_ERR_OK) {
         cprintf ("Error in joy_load_driver: %u\r\n", Res);
 #if DYN_DRV
-        cprintf ("os: %u, %s\r\n", _oserror, _stroserror (_oserror));
+        cprintf ("OS: %u, %s\r\n", _oserror, _stroserror (_oserror));
 #endif
-        exit (EXIT_FAILURE);
+        return EXIT_FAILURE;
     }
 
     count = joy_count ();
 #if defined(__ATARI5200__) || defined(__CREATIVISION__)
-    cprintf ("JOYSTICKS: %d", count);
+    cprintf ("JOYSTICKS: %u.", count);
 #else
-    cprintf ("Driver supports %d joystick(s)", count);
+    cprintf ("Driver supports %u joystick%s", count, count == 1 ? "." : "s.");
 #endif
     while (1) {
         for (i = 0; i < count; ++i) {
-            gotoxy (0, i+1);
             j = joy_read (i);
-#if defined(__ATARI5200__) || defined(__CREATIVISION__)
-            cprintf ("%1d:%-3s%-3s%-3s%-3s%-3s %02x",
+#if defined(__NES__) || defined(__CX16__)
+            /* two lines for each device */
+            gotoxy (0, i * 2 +1);
+            cprintf ("%2u:%-6s%-6s%-6s%-6s\r\n"
+                     "   %-6s%-6s%-6s%-6s $%02X",
                      i,
-                     JOY_UP(j)?    " U " : " - ",
-                     JOY_DOWN(j)?  " D " : " - ",
-                     JOY_LEFT(j)?  " L " : " - ",
-                     JOY_RIGHT(j)? " R " : " - ",
-                     JOY_BTN_1(j)? " 1 " : " - ", j);
+                     JOY_UP(j)    ? "  up  " : " ---- ",
+                     JOY_DOWN(j)  ? " down " : " ---- ",
+                     JOY_LEFT(j)  ? " left " : " ---- ",
+                     JOY_RIGHT(j) ? " right" : " ---- ",
+                     JOY_BTN_1(j) ? "btn A " : " ---- ",
+                     JOY_BTN_2(j) ? "btn B " : " ---- ",
+                     JOY_BTN_3(j) ? "select" : " ---- ",
+                     JOY_BTN_4(j) ? " start" : " ---- ",
+                     j);
 #else
-            cprintf ("%2d: %-6s%-6s%-6s%-6s%-6s %02x",
+            /* one line for each device */
+            gotoxy (0, i + 1);
+#  if defined(__ATARI5200__) || defined(__CREATIVISION__)
+            cprintf ("%1u:%-3s%-3s%-3s%-3s%-3s %02X",
                      i,
-                     JOY_UP(j)?    "  up  " : " ---- ",
-                     JOY_DOWN(j)?  " down " : " ---- ",
-                     JOY_LEFT(j)?  " left " : " ---- ",
-                     JOY_RIGHT(j)? "right " : " ---- ",
-                     JOY_BTN_1(j)? "button" : " ---- ", j);
+                     JOY_UP(j)    ? " U " : " - ",
+                     JOY_DOWN(j)  ? " D " : " - ",
+                     JOY_LEFT(j)  ? " L " : " - ",
+                     JOY_RIGHT(j) ? " R " : " - ",
+                     JOY_BTN_1(j) ? " 1 " : " - ",
+                     j);
+#  else
+            cprintf ("%2u: %-6s%-6s%-6s%-6s%-6s $%02X",
+                     i,
+                     JOY_UP(j)    ? "  up  " : " ---- ",
+                     JOY_DOWN(j)  ? " down " : " ---- ",
+                     JOY_LEFT(j)  ? " left " : " ---- ",
+                     JOY_RIGHT(j) ? "right " : " ---- ",
+                     JOY_BTN_1(j) ? "button" : " ---- ",
+                     j);
+#  endif
 #endif
         }
 
-        /* show pressed key, so we can verify keyboard is working */
-        kb = kbhit ();
-        ch = kb ? cgetc () : ' ';
-        gotoxy (1, i+2);
-        revers (kb);
-        cprintf ("kbd: %c", ch);
-        revers (0);
+        /* Show any pressed keys; so that we can verify that the keyboard is working. */
+        if (kbhit ()) {
+#if defined(__NES__) || defined(__CX16__)
+            gotoxy (1, i * 2 + 2);
+#else
+            gotoxy (1, i + 2);
+#endif
+            cprintf ("keyboard: $%02X", cgetc ());
+        }
     }
-    return 0;
+    return EXIT_SUCCESS;
 }
-- 
2.26.0