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
- COSMAC VIP Instruction Manual (1978)
- CDP1802 User Manual
- CDP1861 manual (show various refresh interrupt code for 64x64 etc.)
- Emma 02 documentation is always helpful (also for variants)
- blog posts about the 1802
- blog posts about Chip-8 and some hardware bits
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 top


