Jump to content
  • entries
    11
  • comments
    59
  • views
    14,436

CDP 1802 Assembler in Rich Extended Basic on the TI 99/4A Computer


The ELF computer, which first made its appearance in August 1976 in the Popular Electronics magazine, was a very basic experimenter's board based on the RCA CDP1802 CPU, a rather obscure processor primarily used in embedded systems by the likes of the DOD and NASA. It's main advantage was its simplicity of interfacing and ease of programming. Needless to say that the ELF developed a large following as many hobbyists built their own boards from scratch using the published details in the magazine, and several companies sprung up offering upgraded versions of it.

 

1013535391_PopularElectronicsELF.thumb.png.4a8017342882044ef9517a31ff09584f.png

 

Here's the original article: PopularElecELF.pdf

 

A modern iteration of it is the so-called Membership Card designed by Lee Hart, which reduces the ELF to the size of an Altoids tin, adds 32K of memory (as compared to the original 256 bytes) and replaces the hexadecimal display with a row of LEDs. It also conveniently incorporates a DB25 connector with the computer signals routed to it for easy interfacing. In fact, it is still being developed and sold here.

 

153053728_20200201_073647-Copy.thumb.jpg.ab67a8baf3fbf0da42a0339e0c656050.jpg

 

The ELF is programmed by entering CDP1802 machine language in binary using the 8 data switches, and as you might imagine this could become extremely tedious for long programs and very error prone, not to mention the horrendous debugging process using just this set up. Incidentally, I did write a basic monitor for the ELF which could help alleviate the difficulty of program development called ELFMON, and it can be found on the attached disk, but it was still clunky at best.

 

Happily, there was a far better way to go about programming the ELF, using a full-fledged computer to write the program in 1802 assembler, assemble it, and transfer it directly to the ELF using the DB25 connector. And while this could be done using any computer, I decided to do it using the TI 99/4A computer. Essentially it became a retrocomputing project inside another retrocomputer! ?

 

The way to go about this is to place a byte on the data lines of the ELF's connector (pins 2-9), toggle the ELF's input switch using pin 1 to store the byte in the ELF's memory, then repeat the process for the rest of the instructions. Clearly, the TI's parallel port would be ideal for this, so I created an adapter cable to connect it to the ELF.

 

1834028804_20200201_073708-Copy.thumb.jpg.43295fbc88d704d7135e3d6e8219b183.jpg

 

The pinout is as follows:

 

     TI PIO                   ELF DB25

        1  -------------------->1

        2  -------------------->2

        3  -------------------->3

        4  -------------------->4

        5 --------------------->5

        6  -------------------->6

        7  -------------------->7

        8  -------------------->8

        9  -------------------->9

        12 ------------------->14

 

From there, it was just a matter of software. The ELF program can written directly on the TI using any one of the available text editors, with the following format:

 

<label><opcode><operand><comment>

 

Each part needs to be separated by a single space. The label is optional and can be up to 6 alpha-numeric characters, the opcode cannot exceed 4 characters, the operand cannot exceed 7 characters, and the comment is optional and of arbitrary length.

When referring to a label in the operand field, the label needs to be preceded by a *.

Register numbers should be entered as a single hex digit from 0 to F. 

Numbers should be entered as either 2 or 4 digit hex digits from 0 to F preceded by a >. For example F is entered as >0F. B41 is entered as >0B41.

Finally, the last opcode of the program should be the reserved word END .

 

If the formatting is wrong, then the assembler output will be wrong as well!

 

Once the program is typed in, it should be saved in the standard TI DV/80 format.

 

I wrote a primitive CDP 1802 assembler in Rich Extended Basic (RXB) which is my favorite interfacing language because it has facilities to access hardware at the low-level. The assembler is sloooooooooow, but hey it beats flipping switches! The operation of the assembler is self-explanatory: just follow the prompts. Below is a listing of the program:

 

 

//1802 ASSEMBLER FOR ELF MEMBERSHIP CARD
//BY WALID MAALOULI
//JANUARY 2020
//VERSION 0.1

CALL CLEAR
OPTION BASE 0
DIM REFTABLE$(100),HEX$(16),REFADR(100)
RESTORE HexData
FOR I=0 TO 15
	READ HEXVALUE$::
	HEX$(I)=HEXVALUE$
NEXT I
CRU=2432 !RS232 CRU OF >1300 DIVIDED BY 2

PRINT "    CDP 1802 ASSEMBLER"
PRINT " WALID MAALOULI - JAN 2020"
PRINT::PRINT::PRINT::PRINT

//GET SOURCE FILE
ON ERROR InputSource
InputSource:
INPUT "ENTER SOURCE FILE PATH:     ":SOURCE$
OPEN #1:SOURCE$
ON ERROR STOP
PRINT::PRINT "1- SEND HEX FILE TO ELF"
PRINT "2- ASSEMBLE FILE"
PRINT::INPUT FCTN
IF FCTN=1 THEN
	SendELF
	
PRINT::PRINT "ENTER DECIMAL START ADDRESS:"
INPUT OFFSET

PRINT::PRINT "SELECT OUTPUT OPTION:"
PRINT "1- LIST TO SCREEN"
PRINT "2- SEND TO PRINTER"
PRINT "3- SAVE TO FILE"
PRINT "4- SEND TO MEMBERSHIP CARD"
PRINT
GetOutputSelect:
INPUT OUTSEL
IF OUTSEL<>1 AND OUTSEL<>2 AND OUTSEL<>3 AND OUTSEL<>4 THEN
	GetOutputSelect
IF OUTSEL=2 THEN
	OPEN #2:"PIO",OUTPUT
IF OUTSEL=4 THEN
	SendELF
IF OUTSEL<>3 THEN
	StartAsm
ON ERROR InputSource1
InputSource1:
PRINT
INPUT "ENTER SAVE FILE PATH:       ":DEST$
OPEN #2:DEST$,OUTPUT
ON ERROR STOP
GOTO StartAsm

SendELF:
PRINT
PRINT "PREPARE ELF TO RECEIVE DATA:"
PRINT 
PRINT "1-CONNECT CABLE"
PRINT "2-LOAD AND CLR SWITCHES DOWN"
PRINT "3-READ SWITCH UP"
PRINT "4-ALL DATA SWITCHES UP"
PRINT
PRINT "PRESS ANY KEY WHEN READY"
CALL KEY("",0,K,S)
IF FCTN=2 THEN
	StartAsm

//TRANSFER HEX FILE TO ELF
TransferHex:
IF EOF(1) THEN
	CLOSE #1::
	PRINT::
	PRINT "TRANSFER COMPLETE!"::
	STOP
LINPUT #1:LINE$
PRINT LINE$
HVAL$=SEG$(LINE$,6,2)
CALL HEXDEC(HVAL$,DECVAL)
GOSUB SendByte
IF SEG$(LINE$,9,1)="" OR SEG$(LINE$,9,1)=" " THEN
	TransferHex
HVAL$=SEG$(LINE$,9,2)
CALL HEXDEC(HVAL$,DECVAL)
GOSUB SendByte
IF SEG$(LINE$,12,1)="" OR SEG$(LINE$,12,1)=" " THEN
	TransferHex
HVAL$=SEG$(LINE$,12,2)
CALL HEXDEC(HVAL$,DECVAL)
GOSUB SendByte
GOTO TransferHex

//START OF ASSEMBLY
StartAsm:
LINE=OFFSET
RPOINT=0
PASS=1
PRINT::PRINT "FIRST PASS"::PRINT

//READ LINE FROM FILE
ReadLine:
TEMP$=""
TEMP1$=""
LINPUT #1:LINE$
IF PASS=1 THEN
	PRINT SEG$(LINE$,1,19)
IF PASS=2 THEN
	SkipLabel
LABEL$=SEG$(LINE$,1,6)
FOR I=1 TO LEN(LABEL$)
	IF SEG$(LABEL$,I,1)<>" " THEN
		TEMP1$=TEMP1$&SEG$(LABEL$,I,1)
NEXT I
LABEL$=TEMP1$
SkipLabel:
OPCODE$=SEG$(LINE$,8,4)
IF SEG$(OPCODE$,1,1)=">" AND PASS=1 THEN
	OPRNUM=0::
	GOTO FirstPass
IF SEG$(OPCODE$,1,1)=">" THEN
	HEXVAL$=SEG$(OPCODE$,2,2)::
	OPRNUM=0::
	OPERAND$=""::
	GOTO FoundLabel
FOR I=1 TO LEN(OPCODE$)
	IF SEG$(OPCODE$,I,1)<>" " THEN
		TEMP$=TEMP$&SEG$(OPCODE$,I,1)
NEXT I
OPCODE$=TEMP$
OPERAND$=SEG$(LINE$,13,7)
IF SEG$(OPERAND$,1,1)<>">" THEN
	NotNumber
OPERAND$=SEG$(OPERAND$,2,LEN(OPERAND$)-1)
TEMP$=""
FOR I=1 TO LEN(OPERAND$)
	IF SEG$(OPERAND$,I,1)<>" " THEN
		TEMP$=TEMP$&SEG$(OPERAND$,I,1)
NEXT I
OPERAND$=TEMP$

NotNumber:
IF OPCODE$="END" AND PASS=2 THEN
	PRINT::
	PRINT "ASSEMBLY COMPLETE!"::
	CLOSE#1::
	IF OUTSEL=3 THEN
		CLOSE #2::
		STOP
	ELSE
		STOP
IF OPCODE$="END" THEN
	RESTORE #1::
	PASS=2::
	LINE=OFFSET::
	PRINT::
	PRINT "SECOND PASS"::
	PRINT::
	GOTO ReadLine

//ASSEMBLE LINE
IF PASS=2 THEN
	SearchData
IF RPOINT=49 THEN
	PRINT "REFERENCE TABLE FULL!"::
	STOP
IF LABEL$<>"" THEN 
	REFTABLE$(RPOINT)=LABEL$::
	REFADR(RPOINT)=LINE::
	RPOINT=RPOINT+1
		
SearchData:
IF SEG$(OPCODE$,1,1)="A" THEN
	RESTORE AData
IF SEG$(OPCODE$,1,1)="B" THEN
	RESTORE BData
IF SEG$(OPCODE$,1,1)="D" THEN
	RESTORE DData
IF SEG$(OPCODE$,1,1)="G" THEN
	RESTORE GData
IF SEG$(OPCODE$,1,1)="I" THEN
	RESTORE IData
IF SEG$(OPCODE$,1,1)="L" THEN
	RESTORE LData
IF SEG$(OPCODE$,1,1)="M" THEN
	RESTORE MData
IF SEG$(OPCODE$,1,1)="N" THEN
	RESTORE NData
IF SEG$(OPCODE$,1,1)="O" THEN
	RESTORE OData
IF SEG$(OPCODE$,1,1)="P" THEN
	RESTORE PData
IF SEG$(OPCODE$,1,1)="R" THEN
	RESTORE RData
IF SEG$(OPCODE$,1,1)="S" THEN
	RESTORE SData
IF SEG$(OPCODE$,1,1)="X" THEN
	RESTORE XData
ReadData:
READ OPC$,HEXVAL$,OPRNUM
IF OPC$="XXX" THEN
	PRINT::
	PRINT "INCORRECT OPCODE IN LINE ";LINE::
	STOP
IF OPC$<>OPCODE$ THEN
	ReadData
IF (OPRNUM>0 OR OPRNUM=-1) AND OPERAND$="       " THEN
	PRINT::
	PRINT "MISSING OPERAND IN LINE ";LINE::
	STOP
IF OPCODE$="INP" THEN
	OPERAND$=HEX$(VAL(OPERAND$)+8)
IF OPRNUM=-1 THEN
	HEXVAL$=SEG$(HEXVAL$,1,1)&SEG$(OPERAND$,1,1)::
	OPERAND$=""
IF SEG$(OPERAND$,1,1)<>"*" OR PASS=1 THEN
	FoundLabel
OPERAND$=SEG$(OPERAND$,2,LEN(OPERAND$)-1)
TEMP$=""
FOR I=1 TO LEN(OPERAND$)
	IF SEG$(OPERAND$,I,1)<>" " THEN
		TEMP$=TEMP$&SEG$(OPERAND$,I,1)
NEXT I
OPERAND$=TEMP$	
FOR I=0 TO 49
	IF REFTABLE$(I)<>OPERAND$ THEN
		NextEntry
	CALL HEX(REFADR(I),OPERAND$)
	IF OPRNUM=1 THEN
		OPERAND$=SEG$(OPERAND$,3,2)
	GOTO FoundLabel
NextEntry:
NEXT I
PRINT "LABEL NOT FOUND IN LINE ";LINE::
STOP
FoundLabel:
IF PASS=1 THEN
	FirstPass
CALL HEX(LINE,HEXLINE$)
ASMLINE$=HEXLINE$&" "&HEXVAL$&" "&OPERAND$
PRINT ASMLINE$
IF OUTSEL=4 THEN
	ElfSend
IF OUTSEL=2 OR OUTSEL=3 THEN
	PRINT #2:ASMLINE$
GOTO FirstPass
ElfSend:
CALL HEX(HEXVAL$,DECVAL)
GOSUB SendByte
IF OPRNUM<=0 THEN
	FirstPass
IF LEN(OPERAND$)>2 THEN
	OPR1$=SEG$(OPERAND$,1,2)::
	CALL HEX(OPR1$,DECVAL)::
	GOSUB SendByte::
	OPERAND$=SEG$(OPERAND$,3,2)

CALL HEXDEC(OPERAND$,DECVAL)
GOSUB SendByte
	
FirstPass:
IF OPRNUM=-1 THEN 
	OPRNUM=0
LINE=LINE+OPRNUM+1
GOTO ReadLine
	
//OPCODE DATABASE
AData:
DATA ADC,74,0		
DATA ADD,F4,0		
DATA ADI,FC,1		
DATA AND,F2,0		
DATA ANI,FA,1	
DATA XXX,XX,0
BData:	
DATA B1,34,1		
DATA B2,35,1
DATA B3,36,1		
DATA B4,37,1		
DATA BDF,33,1	
DATA BN1,3C,1
DATA BN2,3D,1
DATA BN3,3E,1
DATA BN4,3F,1
DATA BNF,3B,1
DATA BNQ,39,1
DATA BNZ,3A,1
DATA BQ,31,1
DATA BR,30,1
DATA BZ,32,1
DATA XXX,XX,0
DData:
DATA DEC,20,-1	
DATA DIS,71,0
DATA XXX,XX,0
GData:
DATA GHI,90,-1
DATA GLO,80,-1
DATA XXX,XX,0
IData:
DATA IDL,00,0
DATA INC,10,-1
DATA INP,60,-1
DATA IRX,60,0
DATA XXX,XX,0
LData:
DATA LBDF,C3,2
DATA LBNF,CB,2
DATA LBNQ,C9,2
DATA LBNZ,CA,2
DATA LBQ,C1,2
DATA LBR,C0,2
DATA LBZ,C2,2
DATA LDA,40,-1
DATA LDI,F8,1
DATA LDN,00,-1
DATA LDX,F0,0
DATA LDXA,72,0
DATA LSDF,CF,0
DATA LSIE,CC,0
DATA LSKP,C8,0
DATA LSNF,C7,0
DATA LSNQ,C5,0
DATA LSNZ,C6,0
DATA LSQ,CD,0
DATA LSZ,CE,0
DATA XXX,XX,0
MData:
DATA MARK,79,0
DATA XXX,XX,0
NData:
DATA NOP,C4,0
DATA XXX,XX,0
OData:
DATA OR,F1,0
DATA ORI,F9,1
DATA OUT,60,-1
DATA XXX,XX,0
PData:
DATA PHI,B0,-1
DATA PLO,A0,-1
DATA XXX,XX,0
RData:
DATA REQ,7A,0
DATA RET,70,0
DATA XXX,XX,0
SData:
DATA SAV,78,0
DATA SD,F5,0
DATA SDB,75,0
DATA SDBI,7D,1
DATA SDI,FD,1
DATA SEP,D0,-1
DATA SEQ,7B,0
DATA SEX,E0,-1
DATA SHL,FE,0
DATA SHLC,7E,0
DATA SHR,F6,0
DATA SHRC,76,0
DATA SKP,38,0
DATA SM,F7,0
DATA SMB,77,0
DATA SMBI,7F,1
DATA SMI,FF,1
DATA STR,50,-1
DATA STXD,73,0
DATA XXX,XX,0
XData:
DATA XOR,F3,0
DATA XRI,FB,1
DATA XXX,XX,0	

//Hexadecimal numbers
HexData:
DATA 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F	

//SEND DATA TO ELF ROUTINE
SendByte:
CALL IO(3,1,CRU,1) !TURN ON RS232 CARD
CALL IO(3,1,CRU+7,1) !TURN ON RS232 LED
CALL IO(3,1,CRU+2,1) !SET HANDSHAKE OUT LINE TO HIGH
CALL IO(3,1,CRU+1,0) !SET PIO PORT TO OUTPUT
CALL LOAD(20480,DECVAL) !PLACE BYTE ON PIO PORT
CALL IO(3,1,CRU+2,0) !CYCLE THE HANDSHAKE OUT LINE
CALL IO(3,1,CRU+7,0)
CALL IO(3,1,CRU+2,1) !TURN RS232 LED OFF
CALL IO(3,1,CRU,0) !TURN OFF RS232
RETURN

The attached disk contains the assembler called ELFASM as well as 3 programs for the ELF. I use the extension _S to indicate that this is the text source file which contains the assembly language code as well as the program instructions, and the _HEX extension to indicate that this is the assembled hexadecimal version of the program suitable for downloading to the ELF. Feel free to use your own extensions as you see fit.

 

  1. ELFMON is the ELF monitor program I mentioned earlier
  2. CYLON is a small demo of the Cylon Eyes effect on the ELF's LED's (a.k.a Battlestar Galactica)
  3. HILO is a small game where you have to guess a random computer picked number with as few guesses as possible

 

ELFASM can assemble a source file and then output it to the screen, to a file in HEX format, to the parallel printer, or transfer it to the ELF directly using the adapter cable. You can also load a previously assembled HEX file and transfer it to the ELF without the need of assembling it.

 

And here's a video of the entire project. As is usual with my hobby projects, it is highly unlikely anyone else will find this useful outside of myself, but hey, it was a great learning experience :)

 

 

ELF.dsk

 

  • Like 4

8 Comments


Recommended Comments

15 hours ago, pedalpowered said:

Very cool! I was looking at the membership card fairly recently. Was hoping to have a video chip, though. I'm very interested in the COMX-35.

Thanks :) I love the MC. Quite ingenious and very capable. As for the COMX, it's as rare as hen's teeth. I wish someone would make a replica of it... Here's a site with lots of info on it: http://www.comx35.com/comx35.html

On 7/9/2020 at 10:04 PM, Vorticon said:

Thanks :) I love the MC. Quite ingenious and very capable. As for the COMX, it's as rare as hen's teeth. I wish someone would make a replica of it... Here's a site with lots of info on it: http://www.comx35.com/comx35.html

i'll just leave this here in case you hadn't stumbled across the project yet (apologies for necro-posting)

 

https://github.com/schlae/comix-35

 

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...