Skip to content

maarten-pennings/P2000

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

P2000T

I retrieved a Philips P2000T from my attic, and started to investigate it. This document describes my findings.

Firstly, getting to understand BASIC, next a quick look at improving my video, next, detailing the BASIC cartridge. I also tried to archive a P2000 program I wrote 40 years ago.

P2000T

Scroll down for an extensive photo gallery.

Contents

BASIC

I started with the cartridge "Basic Interpreter 16K" or "P2305" (for slot "1"). Trying a "10-print" program works as expected. Still I had some issues.

Editing

To edit a basic program, say line 30, you can not just type LIST, cursor to line 30 and start correcting (as I do on the C64). You have to give the command EDIT 30, and then you are in a sort of VI editor (that only helps if you did a bit of Linux). This editor has three states: command, insert, overwrite.

By default, you are in the command state where pressing a key gives a command:

  • I ("insert") switches to insert state; each character typed next is inserted before the old text. CODE switches back to the command state.
  • C ("change") switches to overwrite state; each character typed next overwrites the old text. CODE switches back to the command state.
  • X ("append") moves to end of line and switches to the insert state.
  • H ("del eoln") deletes to end of line and switches to the insert state.
  • S ("search") moves the cursor to the next character typed. Use Shift Cursor right for find next.
  • K ("kill search") deletes to the next character typed.
  • ENTER commits changes.
  • N ("next") commits changes and edits next line.
  • STOP (Shift , on numeric keypad) aborts all changes.
  • Backspace backspaces one character left of the cursor, and Shift Backspace deletes one character under the cursor.
  • Tip: also the line number can be edited (then, the old line stays as is).

In all modes, the cursor keys are operational.

Tip: LIST . and EDIT . list respectively edit the last listed or edited line. Numeric Shift 0 (Def) is a shorthand for EDIT ..

Tip: Numeric Shift 9 (M) toggles paged listing; when LIST has filled one screen, a meer (more) is shown.

This overview is for BASIC 1.1; BASIC 1.0 UK is severely limited: e.g. the cursor keys don't work and and C is temporary. The old EDIT seems to be more VI like than I thought, see P2000GG page 5.

Tape

The tape recorder works as a disk drive. It is formatted in blocks of 1k byte. One side of the tape has 42 blocks.

You can get a directory with Zoek (Shift 1 on numeric keyboard), and load and save is with commands CLOAD and CSAVE to tape blocks.

dir

The directory listing only shows the first character of every file in BASIC 1.0 UK, the screenshot is for BASIC 1.1 NL.

The BAS indicates the file type (BASIC program). Another example is SNG which indicates an array of singles was saved to the tape. The B behind the file type indicates this file requires th BASIC cartridge. Command for savinf singles array A is something like CSAVE* A @ "filename".

The key Shift 7 on the numeric keypad [oo] formats a tape.

Serial port

The P2000T has a printer port at the back.

printerport

The great thing is that this is actually a serial port. To connect it to a PC, you need an old style D25 connector/converter, and a cable to convert from serial to USB. I used a US232R-500-BULK.

Just using RX/TX is not enough, probably RTS needs to be high as well. Check details in natlab nieuwsbrief nr 76

In the PC start a terminal, e.g. ninjaterm, connect to the correct COM port with settings 1200,8,N,1, and try LPRINT to print a line, LLIST to list the program on the printer, or use print-screen (Shift 00 on numeric keyboard) to copy the current screen to the printer.

Here an example on an empty screen. I first did a list, then a run (which prints due to the LPRINT statements), then a llist (which prints the listing), and finally I pressed print screen.

Terminal

The print screen button does not seem to work in BASIC 1.0 UK.

The LLIST and print screen are low threshold features to get data from P2000T to PC.

Character modes

The cartridge has two character modes, lets call them mix and all-caps. This mode is not to be confused with the capslock state.

In the mix mode, this is the behavior of shift and capslock.

key no shift, no capslock shift, no capslock no shift, capslock shift, capslock
[A] a A A A
[1!] 1 ! ! !

In the all-caps mode, this is the behavior of shift and capslock.

key no shift, no capslock shift, no capslock no shift, capslock shift, capslock
[A] A A A A
[1!] 1 ! ! !

To engage capslock press the Capslock key, to disengage capslock press the Shift key. To toggle the character mode press Shift and Tab.

Keys

BASIC itself is not case sensitive, so print and PRINT are the same, and so are a$ and A$. What is more, BASIC converts these to uppercase when you type the lowercase (in program text).

However, as I discovered, some programs ask Are you sure [y/n]? and they do not accept (uppercase) Y.

BASIC 1.1 NL starts in mix mode, but the BASIC 1.0 UK starts in all-caps mode.

Later I found out the POKE &H60B6,0 selects all-caps and POKE &H60B6,1 selects mix mode.

Special characters

P2000 BASIC has several special characters. But there is no comprehensive list. This is copied from various places in the Dutch Gebruiksaanwijzing.

CHRS() description lower set
0 einde toonstring
1 cursor aan
2 cursor uit
3 zet linker helft van beeldgeheugen op scherm (OUT 48,0) en CHR$(29)
4 positioneer cursor. Te volgen door CHR$(regel)CHR$(kolom)
5 scherm naar printer
6 definieer plaats cursor als nieuw cursor punt
7 korte piep
8 cursor naar links met overloop op vorige regel
9 horizontale TAB
10 cursor omlaag of scherm opvoer
11 wis karakter links van cursor
12 wis venster, zet cursor en cursorpunt links boven in venster
13 cursor naar linker kantlijn venster
14 zet deel-PRINT aan
15 wis van plaats cursor tot cursorpunt
16 cursor 1 stap naar links (tot randen van het venster)
17 cursor 1 stap omhoog (tot randen van het venster)
18 cursor 1 stap omlaag (tot randen van het venster)
19 cursor 1 stap naar rechts (tot randen van het venster)
20 zet cursor op kolom k; te volgen door CHR$(k)
21 wis tot einde schermregel
22 wis tot einde venster
23 toonstring, CHR$(23)CHR$(T)CHR$(N1)CHR$(N2)...CHR$(0)
T lengte toon (in 4T ms), frequentie 34.7/N kHz
24 cursor 1 stap naar rechts met overloop
28 zet venster op 24 regels, 40 kolommen en wis venster
29 als cursor niet links in het venster staat dan CHR$(13) + CHR$(10)
30 zet deel-PRINT uit
31 zet cursor op cursorpunt

The column "dft" indicates how each line starts.

CHRS() dft description higher set
128 ? zwart (voorgrond kleur) in teletext
129 rood (voorgrond kleur)
130 groen (voorgrond kleur)
131 geel (voorgrond kleur)
132 blauw (voorgrond kleur)
133 magenta/paars (voorgrond kleur)
134 cyaan/lichtblauw (voorgrond kleur)
135 × wit (voorgrond kleur)
136 laat de rest van de regel knipperen
137 × schakelt knipperen weer uit
138 ? start box - not used in teletext
139 ? end box - not used in teletext
140 × schakelt op lettertekens van normale hoogte
141 schakelt op lettertekens van dubbele hoogte
142 ? double width - not used in teletext
143 ? double height and width - not used in teletext
144 ? grafische mode (mosaic) zwart
145 grafische mode (mosaic) rood
146 grafische mode (mosaic) groen
147 grafische mode (mosaic) geel
148 grafische mode (mosaic) blauw
149 grafische mode (mosaic) magenta/paars
150 grafische mode (mosaic) cyaan/lichtblauw
151 grafische mode (mosaic) wit
152 ? conceal in teletext
153 × mosaic continuous set
154 mosaic separated set
155 ? control sequence intro - not used in teletext
156 achtergrond kleur (terug) op zwart
157 achtergrond kleur wordt huidige voorgrond kleur
158 rest van regel: herhaal grafische char bij kleurwissel (ipv spatie)
159 × zet chr$(158) weer uit

Two small examples:

Demo of some special characters

Demo of some special characters

A puzzling example can also be found in Nieuwsbrief natlab at 127.

100 REM Color demo
110 N=50 : GOSUB 200
120 N=50+128 : GOSUB 200
130 END
200 FOR B=129 TO 135
210 FOR F=129 TO 135
220 PRINT CHR$(B);CHR$(157);CHR$(F);CHR$(N+30);CHR$(N);
230 NEXT F:PRINT:NEXT B:PRINT:RETURN

Colors

Finally, a demo you need to hear:

90 CLEAR 500:REM Frere Jacques
100 DEF FNC$(F)=CHR$(INT(34700/F))
110 DEF FNPLAY$(MS,TUNE$)=CHR$(23)+CHR$(MS/4)+TUNE$+CHR$(0)
120 S1$=FNC$(262)+FNC$(294)+FNC$(323)+FNC$(262)
130 S2$=FNC$(330)+FNC$(349)+FNC$(392)
140 S3$=FNC$(392)+FNC$(440)+FNC$(392)+FNC$(349)+FNC$(330)+FNC$(262)
150 S4$=FNC$(262)+FNC$(196)+FNC$(262)
160 T$=S1$+S1$+S2$+S2$+S3$+S3$+S4$+S4$
170 PRINT FNPLAY$(300,T$)

Funny things happen. For example with ASCII code 35. Recall 12 is clear screen, ensuring all next prints happen at &H5000.

PRINT CHR$(12);CHR$(34);CHR$(35);CHR$(36);: FOR I=&H500 TO I+2: PRINT PEEK(I); :NEXT 
"#$ 34 95 36

Also funny, ASCI code 126.

PRINT CHR$(12);CHR$(125);CHR$(126);CHR$(127);: FOR I=&H500 TO I+2: PRINT PEEK(I); :NEXT 
¾¼■ 125 92 127

The Dutch Gebruiksaanwijzing page 146 claims this has to do with ASCII compatibility. OK ... I guess.

What about this sequence?

PRINT CHR$(12);CHR$(131);CHR$(65);CHR$(66);CHR$(67);: FOR I=&H500 TO I+3: PRINT PEEK(I); :NEXT 
ABC 3  65  66  67

And all text is in yellow, but with code 3, not 131. I found this teletext table.

poke name long name description
0 ABK ALPHA BLACK alphanumeric set (dus niet 1 van de 2 mosaic sets), kleur zwart
1 ANR ALPHA RED alphanumeric set, kleur rood
2 ANG ALPHA GREEN alphanumeric set, kleur groen
3 ANY ALPHA YELLOW alphanumeric set, kleur geel
4 ANB ALPHA BLUE alphanumeric set, kleur blauw
5 ANM ALPHA MAGENTA alphanumeric set, kleur magenta
6 ANC ALPHA CYAN alphanumeric set, kleur cyaan
7 ANW ALPHA WHITE alphanumeric set, kleur wit
8 FSH FLASH knipperend
9 STD STEADY niet knipperend
10 SBX START BOX wordt niet gebruikt bij teletekst
11 EBX END BOX wordt niet gebruikt bij teletekst
12 NSZ NORMAL SIZE hoogte van de letter = 1 regel
13 DBH DOUBLE HEIGHT hoogte van de letter = 2 regels (zie opmerkingen)
14 DBW DOUBLE WIDTH 2 x normale breedte, wordt niet gebruikt bij teletekst
15 DBS DOUBLE SIZE 2 x normale breedte en hoogte, wordt niet gebruikt bij teletekst
16 MBK MOSAIC BLACK mosaic set (1 van de twee, zie 25 en 26), kleur zwart
17 MSR MOSAIC RED mosaic set, kleur rood
18 MSG MOSAIC GREEN mosaic set, kleur groen
19 MSY MOSAIC YELLOW mosaic set, kleur geel
20 MSB MOSAIC BLUE mosaic set, kleur blauw
21 MSM MOSAIC MAGENTA mosaic set, kleur magenta
22 MSC MOSAIC CYAN mosaic set, kleur cyaan
23 MSW MOSAIC WHITE mosaic set, kleur wit
24 CDY CONCEAL DISPLAY verborgen text, opgeheven wanneer andere kleur wordt gekozen
25 SPL STOP LINING bij mosaic continuous set gebruiken
26 STL START LINING bij mosaic separated set gebruiken
27 CSI CONTROL SEQUENCE INTRO wordt niet gebruikt bij teletekst
28 BBD BLACK BACKGROUND achtergrondkleur van de characters zwart
29 NBD NEW BACKGROUND achtergrondkleur wordt de kleur van de characters
30 HMS HOLD MOSAIC bij mosaic set de laatst gebruikte character weergeven als er een control character staat
31 RMS RELEASE MOSAIC bij mosaic set een spatie weergeven als er een control character staat (default)

BASIC versions

What really confused me in the beginning was that my P2000T did not do what the manual Gebruiksaanwijzing specified (copy of other repo). Later I found out the manual is for version NL 1.1, and my cartridge is UK 1.0.

Differences I found

  • Version 1.1 adds many control chars. For example see that control character 18 is not working in 1.0.

    control chars

  • Several keyboard keys are not working: cursor (!), print screen (numeric Shift 5), clear screen (numeric Shift upper right), Def for edit last (numeric Shift 0).

    I did have a "Tekstbewerking" or "P2301" (for slot "1") cartridge, and there the cursor keys did work, so I did know it was not a hardware fault.

  • Tape directory (Zoek numeric Shift 1) only shows first char of file name, instead of full name, file type and file size.

  • Statement inp("") to get a single keyboard key does not work in 1.0.

  • Default character mode is all-caps in 1.0. I did not know I could switch to mixed (see above), but my program required that.

P2000 BASIC features

How do I grade the P2000 BASIC? My reference is Commodore 64 (C64) BASIC.

  • Having to use EDIT instead of screen editing is a bummer.

  • Good to have support for hex with (&H9000) and HEX$(). Who needs OCT$(), but where is BIN$()?

  • I'm missing DEEK() and DOKE() for two-byte addresses.

  • Nice there is a way to get the address of a variable with VARPTR(var).

  • DEF FN is much more useful than in e.g. C64, since the function can have multiple arguments and the return type can be string. See for example FNPLAY$() in a previous section.

  • Unlike C64 BASIC where all computations are done in single precision, P2000 BASIC has several data types: single, double, int, string.

  • CLEAR 50,&H9000 allows reserving some RAM from BASIC to e.g. a machine language routine.

  • Support for up to 10 machine language routines, and the ability so set and address for them: DEF USR5=&H9001. Calling is via USR5(23).

  • Misses file OPEN and file READ# and WRITE#. CLOAD* and CSAVE* load and save entire arrays. Nicely high level, but maybe too inflexible.

  • Triggering an error with ERROR <num>, even using fresh numbers is nice. Also good to have ON ERROR GOTO, ERL (line) and ERR (error code).

  • Some string functions are nice, INSTR(pos,str1,str2), some are ok, STRING(num,char). For me SPACE$(num) is overkill.

  • Printer support (LPRINT) is nice, but since this is actually a serial port, why is there no LINPUT.

  • I'm not sure what to think about PRINT USING. Very verbose. Not flexible enough. can only be used with PRINT - why not in a string expression?

Tekstbewerking

I have a second cartridge called "Tekstbewerking" P 2301. That's Dutch for word processing.

Screenshot tekstbewerking

I was a bit puzzled how that operates, and I couldn't find any user manual. Here is what I found out.

  • The top 3 lines are the various status bars.

    • Line one normally shows WP 1 and I have no idea what that means; maybe "WordProcessing" slot "1" (see below for slots). Once a tape is inserted, it starts "syncing" to tape (reading directory), and it shows the filename instead of WP. If a T is flickering, the tape is running (save, load, ...).
    • Next line is some printer settings: REG/PAG is probably lines per page, REGELAFST is probably line spacing, and TEKENAFST is probably font size. There is a fourth setting, out of view now: spaces before the left margin.
    • Third line is the "ruler". First three characters show the line the cursor is on, every 10 spaces the column number is printed, a * marks a tab stop, / mark being near to the right margin (sounding a bell), and a small block marks the left margin. The big block marks the column of the cursor.
  • When the "Tekstbewerking" cartridge is inserted and the P2000 turned on, you are immediately in a blank document. You can start typing. Some editing keys are working:

    • for cursor left, with shift moves to begin of line.
    • for cursor up, with shift moves to begin of document.
    • for cursor down, with shift moves to end of document.
    • for cursor right, does not seem to have a shift function.
    • -×- deletes a line, with shift deletes document, without confirmations (!).
    • The underscore key inverts text - do not know what that is (underlining "on"?).
    • Pressing BS backspaces; pressing CODE then BS deletes. Note that CODE is not a shift key, press CODE, release it, then press BS. If you keep CODE pressed, after a second or so, the keyboard buffer is filled up (P2000 has auto repeat for keys), and the P2000 starts beeping.
  • There are no function keys, and no control key, so it took me quite some time to figure out how to change the settings.

    • You have to press CODE to edit the ruler. Once in the ruler, move left and right with the cursor keys, and enter "ruler" characters like - (nothing ie erase tabs), * (tabstop), + (left tabstop), / (bell zone), RETURN (set right margin). With CODE you return to the document.
    • While editing the ruler (after pressing CODE ), another CODE moves to settings. Here press TAB to cycle through the four settings REG/PAG, REGELAFST, TEKENAFST and left margin. To change a settings cycle through the values with and . With CODE you return to ruler editing, and another CODE brings you back to the document.
  • Shift numeric-00 seems to print, but I do not get serial output, only DRUKKER CONTROLEREN (check printer).

  • Tape handling still puzzles me.

    • I believe that a tape has 7 slots, each storing a document.

    • If you insert an empty cassette it starts to spin and a T is shown in status line 1. Then it asks "FORMATEREN ? (J/N)". If you answer J, it asks GEEF NAAAM IN, and I type e.g. Maarten.

    • If, in BASIC, you do a Shift numeric 1 (ZOEK) on this cassette you get 7 empty (well, 5 bytes) document slots

      7 empty document slots

    • If you insert such a formatted cassette in "Tekstbewerking", status line 1 changes from WP 1 to T:Maarten.1 (with some spaces). I believe the 1 means that this is the slot where the cassette tape's head is.

    • You can now type some text in the document on the screen.

    • To save the screen content requires some magic steps.

      • Press shift numeric 7 (tape symbol) to manage the tape. This highlights the slot number in status line 1.
      • (You need to press shift numeric , (STOP) to leave the tape manager)
      • Press a slot number 1..7 to select a slot.
      • Press shift numeric 6 (OPN) to save the document to the selected slot.
      • A save wipes the document on the screen.
    • To load a document from a slot requires similar magic steps.

      • Press shift numeric 7 (tape symbol) to manage the tape.
      • Press a slot number 1..7 to select a slot.
      • Press shift numeric 4 (INL) to load from the selected slot and add it to the document on the screen at the cursor position.
      • I tried to load from 1 and got GEEN TEKST OP PAGIN (no text on page).
    • My experiment

      • I saved text in slot 2.

      • I wiped the screen document (shift -×-) typed short text, placed the cursor in the middle, then did the load sequence and indeed the text previously saved in slot 2 is now inserted on my screen.

      • I saved this to slot 4, and did a ZOEK in BASIC.

        slots 2 and 4 in use

  • Pressing shift numeric 8 (disk symbol) is a sort of reset - screen document wiped (!) - tape spins. Do not understand.

  • Pressing shift numeric 9 (M) allows entering a digit 1..9 (also 8 and 9, so not a tape slot), but all other keys give an error beep. Do not understand.

    I found is one no-beep exception. After shift numeric 9 (M), then 1, then shift numeric 6 (OPN), I did not get a beep, I did get a beep when cursor was at end-of-file. Is this a save to clipboard-1?

Video

I started with using the RF link. I then analyzed my board and found three mods were applied. Finally, I made a DIN-SCART cable.

RF link

The P2000T has two video ports. One is the traditional RF port, to connect to a TV (that's the "T" in P2000T). The other is some RGB port.

TV channel

I somehow did have an DIN6 to SCART cable, but it didn't work; the screen was bright white. So I started with the RF link. That did require to scan for the correct channel: C3 or 57 MHz. I have it on preset P3 which I labeled P2000.

TV channel

The quality was pretty lousy, sorry most screenshots in this document are with using the RF link. Only very late I decided to make a DIN-SCART link.

By the way, SCART comes from France, and is there known as Péritel. Interestingly, SCART was mandated in Europe from 1981 onward, whereas the P2000 hit the market in 1980. It should come as no surprise that this DIN RGB link needs tweaks to get running.

Mods

In Natlab nieuwsbrief we find (page 5 and 6) a remarks about inverting the SYNC signal. It seems my P2000T has that mod, and it seems needed for the DIN to SCART link.

Video mod1

In Natlab nieuwsbrief we find (page 156) a remarks about adding a transistor to boost the SYNC signal. It seems my P2000T has that mod, and it seems helpful for the DIN to SCART link.

Video mod2

I also believe the daughter board is a video mod. It seems to replace SAA5020 chip with a pcb that contains the SAA5020 plus some extra stuff. I have no idea what this is doing.

Video mod3

DIN-SCART cable

I found an nice one pager with instructions how to make a DIN-SCART cable. I hand soldered resistors, but you could also make a PCB.

I happen to have DIN-SCART cable, but it didn't work, so I opened it. On the DIN side, all six pins were wired, but with wrong colors. I decided to not change the DIN side, it is too small. After closing the plug, I won't see the wrong colors anymore.

In the picture below, the colored lines have the color of the actual wires in my cable. The signals they carry has been written as labels to the wires.

DIN-SCART wiring

There was another issue: my SCART plug did not have all pins, not even half. Fortunately, it had all the pins I needed, except AUDIO-L. But that is fine. Of course, the pins were connected wrongly.

I cut all wires on the SCART side, inserted all resistors and shrink tube, and closed the cable again. Fortunately, there is plenty of room in the SCART plug (that was the main reason to only solder on this side). Note, I did followed the tip: if you don't have a 560Ω resistor (not in basic range), use two in parallel: 3k3Ω en 680Ω.

SCART

Result

I'm very happy with the result. Quality is much better; especially deeper colors and less ghosting.

Video improvement

What is more important: with the old setup (RF cable), the TV lost sync when there were large non-black areas (text with non-black background). That problem is gone!

Cartridge

Since I have BASIC UK 1.0, I borrowed the better NL 1.1 and started to investigate. The dream is to make my own cartridge.

Memory map

The cartridge is mapped to 0x1000-0x5000, as shown in the P2000T memory map below (see also the FieldSupportManual at the bottom of page 3-3, or the Reference manual at page 4/4).

memory map

As the Reference manual explains, the monitor is the basic software for primitive functions such as start-up, interrupt handling, and some input and output.

Above the monitor we find a 16k gap where a cartridge could slot in. Next is two pages (a 40×25) of video memory, the P2000M even has double the amount.

The standard P2000T comes with 16k byte RAM, a gap for another 16k and a gap for several 8k banks.

Cartridge internals

Here is a photo of the P 2305 Basic Interpreter 16K (UK 1.0).

Cartridge inside

Notice that the cartridge has

  • One HN462732, a 4096-word x 8-bit UV erasable and programmable read only memory by Hitachi Semiconductor
  • Three SBB2632, a 32768 bit MOS N-channel static ROM, organized as 4096 8-bit words by Mullard (acquired by Philips).
  • Two 74LS367, a hex tri-state buffers.
  • One 74LS138, a 3-Line To 8-Line Decoders/Demultiplexers.

Cartridge header

The Monitor program in the first 4k checks the start of cartridge memory if there is a valid cartridge to jump to. It inspects its header, see Reference manual page 4/14.

Cartridge header

I wrote a quick checker on BASIC UK 1.0.

10 FOR I= &H1000 to I+15
20 PRINT HEX$(PEEK(I));" ";
30 NEXT 
RUN 
5E 0 0 0 0 20 20 20 20 20 20 20 20 1 0 0

That is a boring result: valid signature (5E) count and initial checksum zero (0 0 0 0), cartridge name 8 spaces (20..20), and revision 1.

Running the same in BASIC NL 1.1, gives more interesting results:

5E FB F 0 80 52 6F 62 52 6F 62 38 33 2 0 0 

I wrote the following program to print the cartridge header of BASIC NL 1.1 and to validate the checksum.

110 DEFFNH2$(X)=RIGHT$("0"+HEX$(X),2)
120 DEFFNH4$(X)=RIGHT$("000"+HEX$(X),4)
125 A=&H1000:S=PEEK(A)
130 PRINT FNH4$(A);" sig   ";FNH2$(S)
140 A=A+1:N=PEEK(A)+256*PEEK(A+1)
145 PRINT FNH4$(A);" count ";FNH4$(N)
150 A=A+2:C=PEEK(A)+256*PEEK(A+1)
155 PRINT FNH4$(A);" insum ";FNH4$(C)
170 A=A+2:FOR I=0TO7:I$=I$+CHR$(PEEK(A+I)):NEXT I:PRINT FNH4$(A);" cname ";I$
180 R=PEEK(A+8)
185 PRINT FNH4$(A+8);" rev   ";FNH2$(R)
190 X=PEEK(A+9)+256*PEEK(A+10)
195 PRINT FNH4$(A+9);" resvd ";FNH4$(X)
197 PRINT FNH4$(A)"-"FNH4$(A+N-1);"  ";
200 REM Compute checksum over N bytes
210 IFN>0THENC=C+PEEK(A):A=A+1:N=N-1:GOTO210
220 IF C>65535 THEN C=C-65536:GOTO 220
300 PRINT "CSUM ";FNH4$(C);" ";
310 IF C=0 THENPRINT"ok" ELSEPRINT"err"

This was the output

1000 sig   5E
1001 count 0FFB
1003 insum 8000
1005 cname RobRob83
100D rev   02
100E resvd 0000 
1005-1FFF  CSUM 0000 ok
  • The 5E signature on address 1000 looks ok.
  • The count 0FFB on address 1001/1002 is strange. The checksum is computed from 1005 onward, 1005+0FFB-1 is 1FFF, so this covers only the first 4k of the BASIC rom. Maybe the checksum only checks the EPROM part of the cartridge.
  • The checksum is a bit tricky, what is listed here is the initial value of the checksum. All bytes from 1005 to 1FFF are added to that, and then the remaining value should be 0000. I'm surprised that the initial value is such a neat number (8000).
  • At 1005 we find the cartridge name RobRob83. It is never (?) shown to the user. This seems to be the authors' signature.
  • Apparently revision 2 (UK 1.0 has revision 1).
  • Finally zeros for the reserved last two bytes.

My BASIC program computes the checksum as Ivo explains it , and indeed, the resulting value is 0.

; validate cartridge ROM 
; inputs:
; 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

validate_cartridge:
    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

rom_test_loop:  
    ld a,b                      ; is byte count zero? 
    or c                        ; 
    jr nz,do_ROM_test           ; no, so keep checking 
    ld a,d                      ; all bytes were added to DE, checksum shall now be zero
    ret z                       ; Z is ok, NZ = checksum error
    jp bootstrap                ; try to load a program from tape

do_ROM_test:    
    inc hl                      ;get next byte
    ld a,(hl)   
    add a,e                     ;add to 16 bit checksum 
    jr nc,no_add_carry  
    inc d                       ;handle carry 
no_add_carry:   
    ld e,a                      ;sum back in e
    dec bc                      ;dec bytes done 
    jr rom_test_loop

Since I was surprised about the "neat" number for the initial checksum field, I downloaded another cartridge image and computed the checksum. This time in Python, see checksum. This CPM Nater cartridge is also 16k, also does not check its full content (here 8k of 16k instead of 4k of 16k), but the checksum matches.

Cartridge dump

Since my BASIC NL 1.1 is a borrowed cartridge, I decide to make a dump of it, to the printer (using LPRINT).

110 DEFFNH2$(X)=RIGHT$("0"+HEX$(X),2)
120 DEFFNH4$(X)=RIGHT$("000"+HEX$(X),4)
130 FOR A=&H1000 TO A+16*1024-1 STEP 8
140 LPRINT FNH4$(A);" ";:H$=""
150 FOR I=A TO A+7:D=PEEK(I)
160 LPRINT " ";FNH2$(D);
165 IF D<32 OR D>127 THEN D=46
168 H$=H$+CHR$(D)
170 NEXT I
180 LPRINT"  ";H$
190 NEXT A

I connected my P2000T with a serial-to-USB cable to my PC, and recorded the log. Here is the first couple of lines of the log.

1000  5E FB 0F 00 80 52 6F 62  ^....Rob
1008  52 6F 62 38 33 02 00 00  Rob83...
1010  C3 66 1F C3 E6 60 C3 C4  .f...`..
1018  1F 1C 86 8D 50 48 49 4C  ....PHIL
1020  49 50 53 20 43 41 53 53  IPS CASS
1028  45 54 54 45 20 42 41 53  ETTE BAS
1030  49 43 04 03 02 83 56 65  IC....Ve
1038  72 73 69 65 20 31 2E 31  rsie 1.1
1040  86 4E 4C 04 05 02 86 00  .NL.....
1048  C9 00 C3 C0 60 C3 3A 19  ....`.:.
1050  C3 D0 60 C3 48 15 C3 5A  ..`.H..Z
1058  1F C3 4F 17 C3 E2 16 C3  ..O.....
1060  C3 60 C3 18 19 C3 53 1D  .`....S.
1068  C9 00 00 C9 00 00 C3 63  .......c

You find the complete log in the repo basic1.1.log. You will find some empty lines, my assumption is that the P2000 adds 6 empty lines every 66 lines, to skip the perforation of the continuous paper of the 1980s' printers. Later I found on page 49 Gebruiksaanwijzing that this assumption is indeed true, but that the constants (66 lines per page, 6 lines skip are actually settable in Monitor RAM via a poke.

Finally, I wrote a python script to convert that log to pure binary.

inname= "basic1.1.log"
outname= "basic1.1.bin"
bin =b""
print( f"Reading {inname}")
with open(inname, 'r') as file:
  for line in file:
    if len(line)!=40 : continue
    hexs = line[6:29].split(" ")
    bin+= bytes([int(x, 16) for x in hexs])

print( f"converted {len(bin)} bytes")

print( f"Writing {outname}")
with open(outname, 'wb') as file:
  file.write(bin)

The resulting binary is also in the repo basic1.1.bin. It size is 16384 bytes, which is exactly 16k. Good. I downloaded the binary BASICNL1.1 from the P2000 preservation project , and they are identical. My ripper works.

Addressing

Observe that the 16k byte (E)(E)(P)ROM in the cartridge is mapped to 4 memory pages of each 4k byte: from 0x1000 to 0x4FFF. This poses an addressing problem. If the Z80 puts 0x1000 on the bus, and there is no special logic, the cartridge ROM will see that address on its A0-A13 pins. This means that the first 4k of the ROM is wasted: when an address in the range 0x000-0x0FFF is generated by the Z80, the chip select of the ROM is in state not-selected. When the address is in the range 0x1000 to 0x4FFF, the chip select is in state selected.

Wasting 4k is not nice. We could fold the last 4k block to the bottom of the ROM, having this content: 4000-4FFF, 1000-1FFF, 2000-2FFF, 3000-3FFF. No waste but messy to flash the PROM. We could add adder circuitry, which subtracts 4K of the address bus. Lots of chips.

I was very confused by this, but the Simple Cartridge from Ivo Filot explains how this is done in practice.

The problem at hand is that we need to compute the address lines A12C and A13C for the cartridge from the bus lines A12 and A12 (and signal lines CARS1 and CARS2). Answer: A12C is not(A12) and A13C is the cartridge select signal CARS2. See the orange column below.

Cartridge address mapping

We still need a cartridge select signal CARS; this is simply the OR of CARS1 and CARS2. And there is this thing that the CARS1 and CARS2 (and CARS) are actually low active, so we need some NOTs and an AND instead of an OR, just as Ivo shows in his "Address line decoding" section of his schematics.

Archive GRAFIEK

This is a section with a rather personal goal. Feel free to skip.

I found a tape with a BASIC program GRAFIEK that won me and my friend Marcel a P2000 back in 1983. We were supposed to study our finals, but we were programming in the contest halls during a one-week event. Back then, neither of us owned a computer.

Tape with GRAFIEK

I set myself as goal to archive that program.

Screenshots

The program is written in BASIC and is just 5k bytes in size. It is called GRAFIEK on the tape and FUNCTIE ONDERZOEK on screen. In English that would be something like GRAPH and FUNCTION ANALYSIS.

Tape directory

Is the tape getting old? I miss the T program which is on paper but not in the dir. I decided to made a copy on another tape. Funnily that "dir" looks different - but still better than the UK 1.0.

Tape directory

GRAFIEK allows the user to enter a formula; here I just entered SIN(X), and the domain (Df) and range (Bf due to the Dutch word "bereik").

Main menu

Then comes a tricky step: the program modifies itself; it pokes the formula on line 10. It uses :REM to separate the formula from all the spaces on line 10.

Self modifying code

Of course, as a user we don't see that poking. Once the formula is entered, the program will plot the graph of f(x) using the teletext block graphics.

Graph plot

GRAFIEK can also approximate the roots of the graph

Roots

and even plot axis and tangents (here for x=5).

Tangent

The feature that I liked best is that the program allowed one to zoom-in on the graph (or out) by drawing a "box of interest" (the yellow zone in the screenshot left).

Zoom

LLIST

The easiest way to transfer GRAFIEK to PC is by using the LLIST command. Once again, I connected my P2000T with a serial-to-USB cable to my PC, and recorded the log. I did some light hand editing:

  • I removed the 6 empty lines that occur every 66 lines.
  • I removed line breaks for BASIC lines exceeding 80 characters.

This results in llist log.

Dump trial

I thought it might also be a good idea to have a hex-dump of the program instead of just the BASIC program text produced by LLIST.

I wrote a BASIC program that dumps (prints) a BASIC program in memory has in hex. It assumes the following layout of a BASIC line: <ptr-to-next-line> <line-num> <token> <token> ... <nul>. I had to hunt 0x6000 and further to find the start of BASIC. Later I found on page 52 Gebruiksaanwijzing:

Het BASIC programma begint normaal op &H6547. Vanaf dit adres staat een ketting van programma regels in het geheugen.

Or, "The BASIC program by default starts at &H6547. From that address there is a linked list of program lines."

Here is my "BASIC dumper":

10 PRINT "ABCDE";
20 GOTO 10
9000 CLEAR99:A=&H6547:Z$="00000":S$=" "
9003 DEF FND(A)=PEEK(A)+256*PEEK(A+1)
9005 DEF FNH2$(X)=RIGHT$(Z$+HEX$(X),2)
9007 DEF FNH4$(X)=RIGHT$(Z$+HEX$(X),4)
9020 N=FND(A):LPRINTFNH4$(A)S$FNH4$(N);
9027 IFN=0THEN LPRINT:END
9028 LPRINT STR$(FND(A+2));
9030 H$="":FOR I=A TO N-1:D=PEEK(I)
9032 LPRINT S$FNH2$(D);
9034 IF D<32 OR D>127 THEN D=46
9036 H$=H$+CHR$(D)
9040 NEXT:LPRINTS$"("H$")":A=N:GOTO9020

A run 9000 dumps itself over serial port. I captured that in a file, and I cheated a bit: I removed several line endings manually. Note, all numbers are hex except the third in each line, that is the BASIC line number in decimal.

6547 6556 10 56 65 0A 00 A5 20 22 41 42 43 44 45 22 3B 00 (Ve... "ABCDE";.)
6556 655F 20 5F 65 14 00 88 20 31 30 00 (_e... 10.)
655F 6582 9000 82 65 28 23 AB 39 39 3A 41 CA 26 48 36 35 34 37 3A 5A 24 CA 22 30 30 30 30 30 22 3A 53 24 CA 22 20 22 00 (.e(#.99:A.&H6547:Z$."00000":S$." ".)
6582 659E 9003 9E 65 2B 23 A3 20 B1 44 28 41 29 CA DB 28 41 29 BD 32 35 36 BF DB 28 41 BD 31 29 00 (.e+#. .D(A)..(A).256..(A.1).)
659E 65B9 9005 B9 65 2D 23 A3 20 B1 48 32 24 28 58 29 CA E9 28 5A 24 BD E2 28 58 29 2C 32 29 00 (.e-#. .H2$(X)..(Z$..(X),2).)
65B9 65D4 9007 D4 65 2F 23 A3 20 B1 48 34 24 28 58 29 CA E9 28 5A 24 BD E2 28 58 29 2C 34 29 00 (.e/#. .H4$(X)..(Z$..(X),4).)
65D4 65F3 9020 F3 65 3C 23 4E CA B1 44 28 41 29 3A A2 B1 48 34 24 28 41 29 53 24 B1 48 34 24 28 4E 29 3B 00 (.e<#N..D(A):..H4$(A)S$.H4$(N);.)
65F3 6601 9027 01 66 43 23 8A 4E CA 30 BA 20 A2 3A 80 00 (.fC#.N.0. .:..)
6601 6613 9028 13 66 44 23 A2 20 E3 28 B1 44 28 41 BD 32 29 29 3B 00 (.fD#. .(.D(A.2));.)
6613 6630 9030 30 66 46 23 48 24 CA 22 22 3A 81 20 49 CA 41 20 B0 20 4E BE 31 3A 44 CA DB 28 49 29 00 (0fF#H$."":. I.A . N.1:D..(I).)
6630 6641 9032 41 66 48 23 A2 20 53 24 B1 48 32 24 28 44 29 3B 00 (AfH#. S$.H2$(D);.)
6641 665B 9034 5B 66 4A 23 8A 20 44 CB 33 32 20 C3 20 44 C9 31 32 37 20 BA 20 44 CA 34 36 00 ([fJ#. D.32 . D.127 . D.46.)
665B 666A 9036 6A 66 4C 23 48 24 CA 48 24 BD E6 28 44 29 00 (jfL#H$.H$..(D).)
666A 6686 9040 86 66 50 23 82 3A A2 53 24 22 28 22 48 24 22 29 22 3A 41 CA 4E 3A 88 39 30 32 30 00 (.fP#.:.S$"("H$")":A.N:.9020.)
6686 0000

Dump GRAFIEK

I added the dump lines (9000-9040) to GRAFIEK so that it can dump itself. It prints some extra markers to facilitate the merging of long lines: each dumped line starts with >>, the BASIC line number is followed by :, and the character string at the end of the line is enclosed in double parenthesis (( and )).

This time I canceled the 6 lines perforation skipping by issuing a POKE &H60A9,0. Then I commanded RUN 9000 and captured the log. Once again I removed all line breaks for BASIC lines exceeding 80 characters, resulting in dump log.

Since this dump log is made by adding lines 9000-9040, you will find them in the dump log and not in the llist log.

40 year later analysis

Do I understand my 40 year old program?

These comments refer to the llist source.

  • Line 290-406 plots the graph

    • Line 290 itself erases both screens, and ensures the first char of the left screen is "MOSAIC YELLOW"
    • Lines 300 and 310 compute the x and y scale from domain and range ("domein" and "bereik" in Dutch, hence the D and B. "onder" and "boven" for low and high, hence "O" and "B"). SX and SY are scale in pixels, AX and AY is scale in screen cells. Note that one screen cell is 2×3 pixels.
    • Error trapping is switched on (function might take the square root of a negative number) in line 315.
    • Line 320 starts the plotting by looping X of the range. D is pixel density, with default 1.1 set in line 2. The OUT 48,39 ensures we see the right part of the screen (memory frame buffer is 80×25, we see columns 39 and further, I would expect OUT 48,40).
    • Lines 330 and 340 compute the Y by substituting X in function, and then computing the pixel coordinates XP and YP.
    • There is another error: when plotting, the top line of the screen shows -, ., or + for each column, indicating if the function value is below the screen (-), on the screen (.) or above the screen (+). Variable C has the ASCII value of that tag (43=+, 45=-, 46=.). The assignment to C on line 330 should be 46 (.) and the C=46 on line 340 should be C=43 and after the THEN just as on line 350.
    • If out or range (BO and BB) lines 340 and 350 skip plotting and jump to 395. Looks like another error, should have probably jumped to 398, because P and Q on 395 are not initialized.
    • Lines 370-392 compute which cell to update (P), with what mask (N), and what the new code is (Q).
    • Line 395 pokes the new code and checks for a key press (&H6000 is keyboard buffer).
    • Line 398 prints the -, ., + tag in C at the top of the screen. This also functions as progress bar.
    • Line 400 loops for next pixel.
    • Line 405 beeps completion of plot.
    • Line 406 waits for a key press.
  • Line 999-1020 perform error handling while plotting.

    • Extra tags are introduced: 94 ^, 63 ?, 33 ! for errors in lines 999, 1000, and 1010.
    • There is some unclear error handling, but 1020 resumes plotting at 398, printing the tag C.
  • Line 410-998 is main menu with key handling.

    • Starts on line 410 by switching back to left part of the screen.
    • Line 440 prints the title (using T$).
    • As a reminder the domain of the function Df and its range Bf is printed on line 445.
    • Line 448 prints the function formula from F$ in chunks G$ of max 38 chars.
    • Lines 453 and 455 determine if axis can be drawn given selected domain and range, by setting bits 0 and 1 in AS.
    • Lines 458-520 print the main menu.
    • Line 799 clears the keyboard buffer
    • At line 800 the keyboard is scanned This uses the keyboard buffer which holds key codes, not ASCII codes. keybaord mapping
    • There is an error: line 940 jumps to a non-existing line 7000, but key 53 is intercepted at line 855.
    • Lines 810-998 handle or dispatch the key.
  • Line 2000-2100 draws axis (depending on the flags in AS).

  • Line 3000-3110 allow the user to draw a zoom rectangle.

    • Line 3000 manipulates the screen, and sets the coordinates of the zoom box to the entire screen.
    • Line 3010 gives the user instructions to hit the cursor keys to control the zoom box, or SPC when ready.
    • Line 3020: the zoom rectangle uses INP("") which does not get a key code, but an ASCII code. Out of range key code loops on line 3020
    • Line 3025 handles space by jumping to the zoomn activation in 3100 and 3110
    • Line 3030 handles the cases 16 (cursor left), 17 (cursor up), 18 (cursor down), 19 (cursor right), 32 (space = ready) by an ON-GOTO.
    • Lines 3050-385 are the routines the ON-GOTO jumps to. They all jump back to key reading at 3020.
    • Lines 3100 and 3110 activate the new zoom box by addapting DO/DB and BO/BB. It does this by looking at L which is either "zoom in" or "zoom out" (Beperken L=29 or Vergroten L=31).
  • Lines 5000-5070 draw a tangent line.

  • Lines 6000-6100 compute roots.

  • Line 60-160 is the self modifying code part.

    • When you run this program after CLOAD, line 10 has an error-free function definition DEF FNF(X)=SIN(X). Once the user has selected F Functie wijzigen the user has actually dynamically changed the program, and I believe V1= signals that case - not sure why.
    • Line 60 instructs the user to enter a function.
    • Line 70 lets the user input the function (in F$), computes its length (in LF) and the address in this program where the function should be poked (N).
      This means that there can not be any change to the lines before line 10.
    • Line 80 is a look-up table, mapping functions to basic tokens (e.g. ATN() has token value 218).
    • Line 90 loops over all characters in the entered function.
    • Line 100 computes the ASCII value of the loop character, and if it is a digit (48 0 .. 57 9), X (88), or parenthesis (40 (, 41 )) the token to poke B is the same as the ASCII value A.
    • Lines 102-107 translate operators to tokens (42 *, 43 +, 45 -, 47 /, 94 ^).
    • Line 115 skips lookup if a token B is found.
    • Lines 120 and 130 do the lookup of a 3-letter string in the DATA table.
    • Line 160 pokes the token B at N. When the complete string is done, a colon : and REM (token 142) is poked to isolate the function definition. String F$ is prefixed with f(x), and the definition is updated by jumping to line 10.
  • Line 210-260 lets the user enter domain and range limits.

  • The start of the program, lines 3-50, do some magic with flags (V1, V2, V3) and error handling that puzzles me.

    • Line 3 set T$ to the title of the program.
    • Line 6 POKE24758,0 or POKE &H60B6,0 selects all-caps, and sets some constants.
    • Line 10 defines the function to be plotted. Note there is REM with as many spaces as BASIC allows. This is important for the self modifying code. Using xs would have been better than spaces.
    • Now I get puzzled. Line 20 sets error trapping. I guess line 40 is a crude check if the function has errors. If so, it goes to 42 (trap), if not it jumps to ??? 215 or 315, or 50 or 410, ... chaos.
    • Line 50 tells the user there is an error in the function definition.
    • Note that error 64 means that the STOP key is pressed.

I am surprised about the level of detail I knew this machine. All the poke addresses, the character codes, INPand OUT features. I never owned a P2000. There was no internet to look up those features. We had a about a week for the contest - it was part of an "Alles voor Eva" fair. Would the contest hall have had User Manuals for us?

Photo gallery

Photos of my P200T for future reference.

Main unit

Main unit

Main unit


Main unit top side

Main unit top side


Main unit right side

Main unit right side


Main unit left side

Main unit left side


Main unit back side

Main unit back side


Main unit bottom side

Main unit bottom side


Main unit bottom sticker 1

Main unit bottom sticker 1


Main unit bottom sticker 2

Main unit bottom sticker 2

Details

Detail main keyboard

Detail main keyboard


Detail numeric keyboard

Detail numeric keyboard


Detail tape unit

Detail tape unit


Detail tapes

Detail tapes


Detail power button

Detail power button


Detail slot 1

Detail slot 1


Detail mains connector

Detail mains connector


Detail video and printer port

Detail video and printer port

Cartridges

Cartridges top view

Cartridge top view


Cartridges side (label) view

Cartridges side (label) view


Cartridge BASIC UK 1.0 inside

Cartridge BASIC UK 1.0 inside


Cartridge IC socket in BASIC UK 1.0

Cartridge IC socket in BASIC UK 1.0


Splash screen no cartridge

Splash screen no cartridge


Splash screen BASIC UK 1.0 cartridge

Cartridge Splash screen BASIC UK 1.0


Splash screen BASIC NL 1.1 cartridge

Cartridge Splash screen BASIC NL 1.1


Splash screen tekstbewerking cartridge

Cartridge Splash screen tekstbewerking

Inside

Inside top cover

Inside cover


Inside top view

Inside top view


Inside PCB north side (and daughter card)

Inside PCB north side


Inside PCB south side

Inside PCB south side


Inside PCB east side

Inside PCB east side


Inside PCB west side

Inside PCB west side


Inside PCB south east zoom

Inside PCB south east zoom


Inside PCB north east zoom

Inside PCB north east zoom


Inside PCB north zoom, daughter card removed

Inside PCB north daughter card


Inside PCB north zoom, back side daughter card

Inside PCB back side daughter card


Inside PCB north west zoom (daughter card removed)

Inside PCB north west zoom


Inside cable to keyboard

Inside cable to keyboard


Inside cable to tape and power

Inside cable to tape and power


Inside power supply

Inside power supply


Inside power button

Inside power button


Inside tape top

Inside tape top


Inside tape PCB

Inside tape PCB

Possible TODOs

  • Try GRAFIEK in a P2000 emulator.
  • Make a cartridge with BASIC NL 1.1.
  • Make GRAFIEK 2.0.

Links

(end)

About

Philips P2000

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors