The source code for the PCB, the cartridge enclosure as well as the simple hello world assembly program can all be found in this Github repository.
The most basic cartridge that can be built for the P2000T is one wherein SLOT1
of the P2000T directly interfaces with a parallel ROM chip. The SLOT1 cartridges
essentially exposes 16kb of ROM memory accessible by the
P2000T at addresses
0x1000 - 0x4FFF. Here, it is explained in detail how such
a cartridge interfaces with the P2000T.
Throughout this text, we use the notation
0x00 to refer to hexadecimal notation
The schematic for a basic SLOT1 cartridge is given below. The source files to design your own PCB can be found in the Github repository. We here explain how this cartridge works.
As can be seen from Figure 1, address pins
A0-A11 directly interface
with the parallel ROM, for which in this example the relatively abundant and
cheap SST39SF010 has been chosen. The signals on pins
A13 first have
to go through some logic circuitry (explained in detail below) before they
can interface with the ROM. Note that
A14-A16 of the ROM are tied to
ground. Given 14 address pins (
A0-A13), a total of 16kb of data is available
to the cartridge. Note that this implies that basically 112 kb of the 128 kb
total capacity of the ROM is “wasted”, though by making use of some kind of
switching circuitry (see multicartridge) we can potentially make use of the
remainder of the ROMs capacity.
Observe that the pinout for pins 13A and 12B is swapped in the field manual. The correct layout is given in the schematic as shown in Figure 1.
Section 3.1 i this field manual describes the address decoding logics as
seen in Figure 3. There are two cartridge selection signals, i.e.
CARS2 that are active low when either the lower 8kb bank or
the upper 8kb bank of the cartridge is selected. Notably,
CARS1 goes low
A12 goes high and in a similar fashion,
CARS2 goes low when
high. In terms of interfacing with the parallel ROM, we need to introduce some
additional logic to set the right address at the ROM pins.
A12of the P2000T is inverted to give address line
A12on the parallel rom chip.
CARS2of the P2000T is inverted to give address line
A13on the parallel ROM chip.
CARS2goes low, i.e. pins 13A and 12B on the PCB respectively, the
CSpin on the parallel ROM chip needs to go low as well. This is done via two NAND gates.
Given the above logics, a single 74HC00 quad NAND-gate will suffice in mapping the P2000T signals to the parallel ROM.
Given the dimensions of the slot and the cartridge, i.e. the back side of a
cartridge being somewhat larger than its front side, it is recommended
when using DIP components to place them on the back side of the cartridge. A
little bit counter-intuitive is that the back side hosts the
A-set of pins
whereas the front side hosts the
B-set of pins. A visualization of the front
and back side of the PCB is given in Figures Figure 4 and Figure 5, respectively.
For the SST39SF010 chip, a PLCC32 type of socket has been used. This is mainly to keep the footprint relatively small. There also exists a DIP package for this very chip, though this chip is a little bit more expensive.
Having discussed the hardware side of the SLOT1 cartridge, we now proceed
to elaborate on the software side. The P2000T internal ROM located at
$0000-$1000 will only interface with a cartridge in SLOT1 if it contains
a certain set of valied entries in its first 16 bytes. These first 16
bytes are referred to here as the cartridge header.
The cartridge header of every ROM contain a validation byte, four byte of instructions for cartridge validation procedure and finally 11 bytes for the cartridge title, as shown in the table below.
Number of bytes to check for checksum
16 bit checksum
How this cartridge is used when starting from the cartridge will be explained on the basis of a simple “hello world” program.
Simple Hello World Program#
The source code to display the display the text “Hello World” on screen,
is given in the code snippet below. The line
org 0x1000 indicates that this cartridge
starts at memory location
0x1000. Next, the 16 bytes corresponding to the cartridge
header are provided as two sets of bytes. The first set contains the signature byte,
the number of bytes to be evaluated to produce the checksum and finally the 16 bit
The program starts with a
jp printmsg instruction which copies the data at the
message: to memory address
0x5000, which corresponds to the start of
video memory. After the data has been copied, the program is placed in an
; This is an example Hello World assembly file
; signature, byte count, checksum
; name of the cartridge (11 bytes)
; display Hello World
DB "Hello World!",255
; set infinite loop
; check cartridge
ld a,(Cartridge_ROM) ; first byte of cartridge ROM
and 0f5h ; mask with 0b11110101
cp 054h ; egual to 0b11110100 ?
jr nz,bootstrap ; no cartridge signature present, try loading from tape
ld hl,Cartridge_ROM ; pointer to 1st byte of ROM
ld a,(hl) ; get 1st byte
bit 0,a ; bit 0 set?
jr nz,bootstrap ; then try to bootstrap
push hl ; save start of cartridge
call validate_cartridge ; check 1st 8k bank of cartridge ROM
; jumps into cassette load on checksum error
If the first byte of the ROM after
and 0xF5 corresponds to
the first bit of the first byte is not set, then the
routine is launched. In other words, if the first byte is equal to
b0001x1x1 which is
routine is executed.
validate_cartridge routine can be found on line 755 and onwards. Here, we observe that this routine is basically a 16 bit checksum based
on the arguments of the second to fifth bytes of the cartridge. The first
pair of bytes correspond to the number of bytes that need to be checked for
the checksum. The second pair of bytes corresponds to the value of the checksum.
; validate cartridge ROM
; HL points to 1st byte of cartridge ROM to check
; 1st 5 bytes of cartridge ROM:
; defb signature
; defw len
; defw checksum
; returns: Z flag if success
; jumps into cassette bootstrap routine on error
inc hl ; skip signature byte
ld c,(hl) ; lo byte of byte count
inc hl ;
ld b,(hl) ; hi byte of byte count
inc hl ;
ld e,(hl) ; lo byte of checksum
inc hl ;
ld d,(hl) ; hi byte of checksum
ld a,b ; is byte count zero?
or c ;
jr nz,do_ROM_test ; no, so keep checking
ld a,d ; checksum also zero?
; can be zero from the start: OK
or e ; otherwise all bytes were added to DE.
; result should then also be zero, if not it is a checksum error
ret z ; Z is ok, NZ = checksum error
jp bootstrap ; try to load a program from tape
inc hl ;get next byte
add a,e ;add to 16 bit checksum
inc d ;handle carry
ld e,a ;sum back in e
dec bc ;dec bytes done
When the sum over all the bytes (as set by byte count) corresponds to the
checksum, the cartridge is validated and the cartridge launches from address
Rather than inserting a “naked” PCB into your P2000T, it is more elegant to place the PCB inside a nice cartridge. Many electronics hobbyists now have access to 3D printers and a nice enclosure for the PCB has been designed.
.stl files for 3d-printing an enclosure for the basic cartridge can be
found in the Github repository.
Although the “official” P2000T Field Manual contains an error in the pin lay-out for SLOT1, it is worth noting that the P2000 User Group (P2000gg; gg=Gebruikers Groep) published in one of their newsletters a nice overview page (see Figure 7) showing the pin lay-out for several of the connectors on the P2000t.