Home
Admin | Edit

COSMAC VIP low-level coding quick-start

Contents

Introduction


This is a small quick-start / reminder to low-level RCA 1802 (1974) assembly programming for the COSMAC VIP and related computers.

The RCA 1802 is a pretty cool (still in production) simple pre-RISC 8-bit CPU with 16 16-bit registers, it was notably used in the aerospace and in a series of diy home computers in the 70s (Comx-35, Pecom 32, Telmac 1800 etc.), it is slower than a 6502 (not optimized for throughput) but still pack a lot of interesting weirdness.

The COSMAC VIP is a late 70s 2 to 4 KB RAM microcomputer which sported a RCA CDP1861 video chip with a 1-bit framebuffer of up to 64x128 (64x32 by default), this computer had several variants and also many extensions to adds colors support, better sounds or 32 KB RAM etc.

These computers are the direct inspiration for fantasy video game console such as PICO-8 or TIC-80 as CHIP-8 virtual machine originate from there.

Resources

This game project is particularly interesting to go to lower level on the COSMAC VIP. (not using ROM routines)

There is some code golf intros (and games) available for this platform such as I Love Cats or I'm Thinking About Cats Again. (many from Orby !)

Quick-start tooling

Emulator

Emma 02 is a pretty good emulator of computers based on the RCA 1802 microprocessor, to emulate the Cosmac VIP i go on the "RCA" tab and select "Cosmac VIP" in the list then load my program by clicking on RAM SW button then start the emulation. (may uncheck "VP590 Colour Board" for the original b&w experience, "Super-chip" option allow 64x128 resolution) This works for mostly all other systems as well.

The emulator have a bunch of tools such as a debugger or memory view in the "Tools" tab.

Assembler

I use A18 (~1985 tool !) as an assembler, it has a nice set of features, a guide is available here, i then use this bash script to assemble :

#!/bin/bash
set -eu
./a18 $1.asm -l $1.lst -o $1.hex -b $1.bin
echo "bytes: "$(wc -c $1.bin)

On any assembly errors the .lst file can be checked to know the faulty lines and reasons, also show the opcode.

The .hex file is Intel hexadecimal object file format which can be loaded in the emulator. (the .bin also works)

Complete documentation for the assembler can be found in the package as a18.doc.

COSMAC Graphics quick-start


This all assume 4KB of onboard RAM. (VRAM / STAK may have to be adjusted for lower amount)

64x32 (default resolution)

Here is a 40 bytes program that display a single pixel at the center of the screen on the COSMAC VIP :

 incl "1802.inc"

VRAM equ 0F00h
STAK equ 0E00h

 org 000h

 ; prepare ROM refresh interrupt (source: RCA COSMAC VIP manual)
 load r1, 8146h
 load r2, STAK
 load r3, start
 sep r3 ; PC is now r3, necessary as the interrupt routine use r0
start
 sex r2
 load rb, VRAM ; set display page
 inp 1 ; screen on

 ; put pixel
 ldi high(VRAM)
 phi r4
 ldi (32+16*64) / 8 ; compute pixel position x=32 y=16 (a byte = 8 pixels)
 plo r4
 ldn r4 ; get pixels
 ori 1 shl (7 - (32 mod 8)) ; compute the pixel to be ON from the X pos, 'xri' can be used to set a pixel OFF instead
 str r4

loop
 req ; optional, make sure no beeping occurs (a beeping is triggered by toggling the Q flag)
 br loop

 end

Some pretty cool glitch effects can be done already by modifying the display page !

Many opcodes are just a single byte, except certain load and branch instructions.

Note : load is an assembler pseudo instruction which expand to 4 instructions :

ldi high(VRAM)
phi r3
ldi low(VRAM)
plo r3

The 1802.inc has constants to alias register names :

 cpu 1802

r0 EQU 0
r1 EQU 1
r2 EQU 2
r3 EQU 3
r4 EQU 4
r5 EQU 5
r6 EQU 6
r7 EQU 7
r8 EQU 8
r9 EQU 9
ra EQU 10
rb EQU 11
rc EQU 12
rd EQU 13
re EQU 14
rf EQU 15

128x64

Same for 128x64 resolution (51 bytes) :

 incl "1802.inc"

VRAM equ 0C00h ; make room for the additional pixels -> 128*64/8
STAK equ 0B00h

 org 000h

 ; prepare refresh interrupt (source: RCA COSMAC VIP manual)
 load r1, Interrupt
 load r2, STAK
 load r3, start
 sep r3 ; PC is now r3, necessary as the interrupt routine use r0
start
 sex r2
 inp 1 ; screen on

 ; put pixel
 ldi high(VRAM + (64+32*128) / 8) ; compute pixel position x=64 y=32 (high byte)
 phi r4
 ldi low((64+32*128) / 8) ; compute pixel position x=64 y=32 (low byte)
 plo r4
 ldn r4 ; get pixels
 ori 1 shl (7 - (64 mod 8)) ; compute the pixel to be ON from the X pos, 'xri' can be used to set a pixel OFF instead
 str r4

loop
 req ; optional, make sure no beeping occurs (a beeping is triggered by toggling the Q flag)
 br loop

; source: CDP1861 manual / Emma 02 documentation
InterruptRet
 ldxa
 ret
Interrupt
 nop
 dec r2
 sav
 dec r2
 str r2
 sex r2
 sex r2
 load r0, VRAM ; display page
 br InterruptRet

 end

Main difference is the custom refresh interrupt routine to support the resolution, side effect is that the high byte must be accounted for when the position is computed.

Note : must check "Super-chip / Chip 10 - High resolution" in Emma 02 before running.

Check CDP1861 manual for the 64x64 interrupt code, the code is slightly bigger though.

Note : i am not sure a 128 pixels wide mode exists on the original as most content on the internet mention 64x128 usually but this works in Emma 02 so... must be true with some hardware bits

128x64 HAKMEM 149 patterns


Here is a full example (134 bytes) of plotting HAKMEM 149 patterns (with a set pixel routine) :

  incl "1802.inc"

; variables
X equ 0A00h
Y equ 0A01h
I equ 0A02h

STAK equ 0B00h
VRAM equ 0C00h

 org 000h

 ; prepare refresh interrupt (source: RCA COSMAC VIP manual)
 load r1, Interrupt
 load r2, STAK
 load r3, start
 sep r3 ; PC is now r3, necessary as the interrupt routine use r0
start
 sex r2
 inp 1 ; screen on

 load r4, X
 load r5, Y
 load r6, I

 ; prepare HAKMEM 149 initial values
 ldi 1
 str r4
 ldi 0
 str r5

loop
 ; HAKMEM 149 iteration
 sex r4
 ldn r5
 shr
 shr
 add
 adi 43
 str r4 ; x += (y >> 2) + 43
 sex r5
 sd
 str r5 ; y -= x
 shr
 shr
 sex r4
 add
 adi 43
 str r4 ; x += (y >> 2) + 43

 ; scale down + compute VRAM address (set pixel first part)
 sex r6
 ldn r5
 shr
 shr
 plo r7 ; y >> 2
 shr
 shr
 shr
 shr
 adi 12
 phi r8 ; VRAM address high byte
 glo r7
 ani 15
 shl
 shl
 shl
 shl
 str r6 ; y position in VRAM
 ldn r4
 shr
 plo r7 ; x >> 1
 shr
 shr
 shr
 add
 plo r8 ; VRAM address low byte (x + y)

 ; compute pixel value at VRAM address (set pixel second part)
 glo r7
 ani 7
 sdi 7
 plo ra ; p = 7 - (x % 8)
 ldi 0
 phi ra
 ldi 1
 plo r9
shloop ; compute 1 << p
 glo r9
 shl
 plo r9
 dec ra
 glo ra
 bnz shloop
 glo r9
 str r6
 ldn r8 ; get pixels
 or ; compute pixel to be ON from the X position
 str r8
 br loop

; source: CDP1861 manual / Emma 02 documentation
InterruptRet
 ldxa
 ret
Interrupt
 nop
 dec r2
 sav
 dec r2
 str r2
 sex r2
 sex r2
 load r0, VRAM
 br InterruptRet
 end

Not optimized at all but run fairly quickly in emulator.

back to topLicence Creative Commons