Skip to content

Woohoo – 12MHz and 57600 Baud


So this is a bit of a rough collection of stuff but it does hang together and work. The video shows the xmodem bootloader operating at 57600 baud which seems pretty remarkable – god knows it’s quick.

The serial monitor is not much improved but I stuck the bootloader on the front of it. The bootloader and monitor are in a narrow ROM that fits below the NVRAM memory on the membership card. The ROM is addressed at 0 so it gets control first. It calls the bootloader which attempts to download a new program to the RAM at 0x8000 and jump to it. If there’s no xmodem sender present the bootloader jumps to the RAM which hopefully has a program already loaded. I can get to my monitor program by using a terminal program to send a character while the bootloader is looking for an xmodem response.

The code block below contains my bootloader/monitor, the xmodem receiver (derived from Mike Riley’s XR, the bitbang serial routines, and the python xmodem sender. The python routine is only special in that it toggles the RTS line connected to the membership card RUN pin to restart on demand.

The gallery below shows how the narrow ROM fits under the RAM slot on the membership card. The ROM and a lot of the info and encouragement to get it going came from Herb Johnson.

I would point out that the address lines going to the narrow slot are scrambled relative to the wide slot. I would say that if you have some fixed thing to put in a ROM using that slot is ok but if you are as crappy a programmer as I am and need to reprogram it a dozen times you should rent some tiny fingers before you start.

UPDATE: Lee Hart apparently has redone the CPU board to make the faster oscillator and descramble the address lines – great that he keeps the design fresh – I’ll be testing it soon. I’m not sure it’s orderable yet but soon I’m sure.

//VserialNW.c - serial demo program
//17-11-21 adapted to use makeit.bat for setting options
#include <olduino.h>
#include <nstdlib.h>
#define putc(x) putcser(x)
unsigned char getcser();
#define getc() getcser()
void putcser(unsigned char);
#include <dumper.c>
#define EOT 4
#define REGADDR (unsigned int *)0xFF00 //where the registers are stored on reset
#define CODEADDR (unsigned char *)0x8000 //address where code is loaded
void regdump(unsigned int* data){
	unsigned int i;
	for(i=0;i<16;i++){
		if (0==(i%4)) printf("\nR%cx ",i);
		printf("%x ",*data++);
	}
	printf("\n");
}
void dispatch(unsigned char * addr){
	asm(" cpy2 r0,r12\n"	//copy target address to R0
		" sex 0\n"			//x register=0
		" sep 0\n"			//and pass control
	);
}
int gethexit(unsigned char cin){ //returns the corresponding hex value for 0-f, -1 otherwise
	if ((cin>='0') && (cin<='9')){
		return cin-'0';
	}else if ((cin>='a') & (cin<='f')){
		return cin-'a'+10;
	}else{
		return -1;
	}
}

int XR(unsigned char *); //xmodem receiver
int bootload(){
	int ret=XR(CODEADDR);
	if (ret==EOT){
		dispatch(CODEADDR);
	} else if(ret=='T'){//timeout flag
		dispatch(CODEADDR);
	} else{
		printf("Interrupt - starting monitor\n");
	}
	return 0;
}

void main(){
	unsigned char cin='?';
	unsigned char seqop=0x7b, reqop=0x7a, brop=0x30;
	unsigned char * addr=0;
	int ret;
	unsigned int caddr=0;
	int hexval;
	asm(" seq\n"); //make sure Q is high to start
	bootload();//try the loader
	printf("\nSerial Monitor Here - CODEADDR is %x. Baudrate 57600\n",CODEADDR);
	while(1){
		printf("> ");
		cin=getc();
		hexval=gethexit(cin);
		while(hexval>=0){//accumulate address entries - non hex digit characters will return -1
			printf("%c",cin);
			caddr*=16;caddr+=hexval;
			cin=getc();
			hexval=gethexit(cin);
		}
		switch (cin){
			case 'x': //xmodem receive
				printf("\nCalling XR in 2 sec\n",ret);
				delay(2000);
				ret=XR(addr);
				printf("XR returns %x\n",ret);
				if (ret==EOT){
					dump(addr,256);
				} else if(ret=='T'){//timeout flag
					printf("Timeout - no transfer\n");
				} else{
					printf("Interrupt - no transfer\n");
				}
				break;
			case '\r':
				printf("->%x\n",caddr);
				addr=(unsigned char *)caddr;caddr=0;
				dump(addr,16);
				break;
			case '?':			//display memory at addr
				printf("?\n");
				dump(addr,16);
				break;
			case '!':			//alter memory at addr
				printf("!\n");
				*addr=seqop;*(addr+1)=reqop;*(addr+2)=brop;*(addr+3)=(unsigned char)((unsigned int)addr&255); //poke in a loop
				dump(addr,16);
				break;
			case 'g':			//go to program at addr (rp=0)
				printf("g\n");
				dispatch(addr);
				break;
			case 'r':
				printf("r\n");
				regdump(REGADDR);
				break;
			case '+':
				printf("+\n");
				addr+=16;
				dump(addr,16);
				break;
			case '-':
				printf("-\n");
				addr-=16;
				dump(addr,16);
				break;
			default:
				printf("%c/%cx unrecognized\n",cin,cin);
				break;
		}
	}
}

void includeser2(){
	asm("BAUDRATE EQU 	57600\n");
	asm(" include xrwjrT3.inc\n");
	asm(" include serwjrT3.inc\n");
}

#include <olduino.c>
#include <nstdlib.c>
void saveregs(){asm("SAVEREGS: EQU 1\n");}
********************XRWJRT3.inc follows
NAK:	EQU 0x15
SOH:	EQU 0x01
EOT:	EQU 0x04
ACK:	EQU 0x06
CAN:	EQU 0x18
Rrcv:	EQU 8
Rsnd:	EQU 9
blksize:	EQU 128
; XMODEM receiver based on xr.asm by Michael H Riley and serial routines by Josh Bensadon   
; See bottom of file for full acknowledgements and links.
; On entry R12 points to memory where received data will go
; On exit R15 =EOT for success, 'T' for timeout, or ffxx where xx is the first char received if other than SOH or EOT

	align 64
_XR:	ldaD	Rsnd,serout
	ldaD    Rrcv,serinT
	ld2z	R15	;clear the return code


initNAK: ldi    NAK	; need to send NAK to start
	sep     Rsnd
	sep     Rrcv  	; wait for next incoming character
	bnf     ckeot	;continue if no timeout

	ldi     NAK	; resend NAK to start
      	sep     Rsnd
      	sep     Rrcv    ; wait for next incoming character
        bnf     ckeot	;continue if no timeout

	ldi     NAK	; resend NAK to start
      	sep     Rsnd
      	sep     Rrcv    ; wait for next incoming character
        bnf     ckeot	;continue if no timeout

	ldi	'T'	;set return value to 'T'
	plo	R15
	cretn

filelp:    ;receive address is in R12

;begining of block read. returns to filelp or exits to filedn   				
           sep     Rrcv               ; wait for next incoming character(will not time out)

ckeot:    plo	   R15		; save char received
	   xri     EOT          ; check for EOT
           lbz     filedn       ; jump if so
           glo	   R15		;
           xri	   SOH		; check for SOH   
           lbnz	   interrupt	; interrupted transmission - return interrupting character

	   sep     Rrcv               ; read block number
           sep     Rrcv               ; read inverted block number

           ldi     blksize             ; 128 bytes to receive
           plo     r11

readlp:    sep     Rrcv               ; read data byte
           str     r12                  ; store into output buffer
           inc     r12                  ; point to next position
           dec     r11                  ; decrement block count
           glo     r11                  ; see if done
           bnz     readlp              ; loop back if not
;end of block read
           sep     Rrcv               ; read checksum byte


           ldi     ACK                  ; send an ACK
           sep     Rsnd
           lbr     filelp              ; loop back for more

filedn:    
           ldi     ACK                  ; acknowledge end of transmission
           sep     Rsnd
           cretn	           	; and return to caller - R15 contains EOT

interrupt:    
           ldi     CAN                  ; invalid char received - try to cancel the session
           sep     Rsnd
           ldi	   0xff			;make sure R15 isn't 0
           phi     R15
           cretn	           	; and return to caller - R15 contains interrupting character


; *******************************************************************
; *** This software is copyright 2005 by Michael H Riley          ***
; *** You have permission to use, modify, copy, and distribute    ***
; *** this software so long as this copyright notice is retained. ***
; *** This software may not be used in commercial applications    ***
; *** without express written permission from the author.         ***
; *******************************************************************
; *********************************************************************
; *** xmodem code based on xr.asm copyright 2005 by Michael H Riley ***
; *** You have permission to use, modify, copy, and distribute      ***
; *** this software so long as this copyright notice is retained.   ***
; *** This software may not be used in commercial applications      ***
; *** without express written permission from the author.           ***
; *********************************************************************
;http://www.elf-emulation.com/software.html
******************serWJRT3.inc follows
;bit-bang Serial routines adapted from Josh Bensadon's VELFbios-v3.1.asm
;https://groups.yahoo.com/neo/groups/cosmacelf/files/Member-JoshBensadon/
;Transmit Byte via Q connected to RS232 driver
;call via sep, returns via sep R3
;Byte to send in D
;Destroys r14
;17-12-08 this version times out on the first call only - approx .6 sec
;----------------------------------------------------------------------
 	IFNDEF 	CPUSPEED
CPUSPEED EQU 4000000
 	ENDIF
 	IFNDEF 	BAUDRATE
BAUDRATE EQU 9600
 	ENDIF
 	MACEXP ON
;
bitdelay: MACRO baudrate,cpuspeed,baseline,xreg
	rept ((cpuspeed/(baudrate*8)-baseline))/3
	NOP
	endm
	rept (((cpuspeed/(baudrate*8)-baseline)#3))>=1
	sex xreg
	endm
	ENDM

	align 128
serout:			;entry from assembly with char in D
	phi R14		;save char in R14.1
	ldi 9		;9 bits to transmit (1 start + 8 data)
	plo r14
	ghi R14
	shl		;set start bit
	rshr		;DF=0

.txcloop:
	bdf $+5		;10.5   jump to seq to send a 1 bit
	req		;11.5   send a 0 bit
	br $+5		;1      jump +5 to next shift
	seq		;11.5   send a 1 bit
	br $+2		;1      jump +2 to next shift (NOP for timing)
	rshr		;2      shift next bit to DF flag
	phi r14		;3      save D in r14.1
	DEC r14		;4      dec bit count
	glo r14		;5      get bit count
	bz .txcret	;6      if 0 then all 9 bits (start and data) sent
	ghi r14		;7      restore D
	bitdelay BAUDRATE,CPUSPEED,20,2,{EXPAND}
	br .txcloop	;9.5    loop back to send next bit
.txcret: ghi r14		;7
	bitdelay BAUDRATE,CPUSPEED,16,2
	seq		;11.5 stop bit
	bitdelay BAUDRATE,CPUSPEED,4,2
	sep R3		;return 
	br serout	;reset for next time
;**********************************************************************
;serinT
;Receive Byte via EF3 connected to RS232 receiver
;Receives 8 bits
;call via sep
;Returns with Byte received in D, DF is set if the start bit is never seen
;Destroys r14
;----------------------------------------------------------------------
	align 64
serinT:	;serial input with timeout of  approx .6 sec - scales with CPU speed as much as possible
toct	set  (CPUSPEED/1000000)*24
	if toct>255
toct set 255
	endif
	ldi toct
	phi 14
	plo 14
.rxcw:	b3 .okgo	;check for start bit after each instruction
	dec 14
	b3 .okgo	;check for start bit after each instruction
	ghi 14
	b3 .okgo	;check for start bit after each instruction
	bnz .rxcw
;here we've had a timeout - set DF and return
	ldi 1
	shr
	sep R3		;return
	br  serinT	;for next time
.serinN: bn3 .serinN	;serial input without timeout	
.okgo:			;here we know the start bit is present
 	ldi 8		;start bit +7 bits from loop, last bit on returning
	plo r14
	ldi 0
	NOP		;delay to center samples

.rxcloop:
	bitdelay BAUDRATE,CPUSPEED,20,2
	b3 $+6		;11.5 sample rx input bit
	ori 80h		;1
	br $+4		;2
	phi r14		;1
	phi r14		;2
	shr		;3
	phi r14		;4
	DEC r14		;5
	glo r14		;6
	bz .rxcret	;7
	ghi r14		;8
	br  .rxcloop	;9
.rxcret: ghi r14	;8
	ghi r14		;9
	bitdelay BAUDRATE,CPUSPEED,20,2
	b3 $+4		;11.5 sample last rx input bit
	ori 80h		; for a 1 bit
	adi 0		;clear the DF flag because no timeout
	sep R3		;return
	br  .serinN	;for next time - only timeout on first call
;
serinN equ .serinN
_getcser:
	ldad Rrcv,serinN
	sep Rrcv
	plo R15
	ldi 0
	phi R15
	cretn
_putcser:
	ldad Rsnd,serout
	glo R12
	sep Rsnd
	cretn
	
***************Python XMODEM Sender xmboot.py follows
#xmbootb.py xmodem sender for the 1802 olduino
from __future__ import print_function
import sys
import logging
logging.basicConfig()
import serial
try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO
from xmodem import XMODEM, CRC
from time import sleep
import os
if len(sys.argv)>1:
    filename=sys.argv[1]
else:
    filename="a.bin"
fileSize=os.path.getsize(filename)
print ("File Size is",fileSize)
xpect=(fileSize/128)

def getc(size, timeout=1):
    return port.read(size)
xcount=0
xprog=0
def putc(data, timeout=1):
    global xcount,xprog
    port.write(data)
    if (len(data)==128): # if it's a block
    	xcount=xcount+1
    	newprog=(xcount*50)/xpect
    	for x in range(newprog-xprog):
            sys.stdout.write("#")
        sys.stdout.flush()
        xprog=newprog
    sleep(0.001) # give device time to send ACK

#Main program starts here - define the serial port, set RTS off, then open it
port = serial.Serial(parity=serial.PARITY_NONE,
                     bytesize=serial.EIGHTBITS,
                     stopbits=serial.STOPBITS_ONE,timeout=5,xonxoff=0,
                     rtscts=0,dsrdtr=0,baudrate=57600)
port.rts=False
port.port='COM3'
port.open()
port.rts=True
sleep(0.1)
port.rts=False
#open the file to be loaded
stream = open(filename,'rb')
sleep(0.2) #give olduino a chance to get Q under control
port.reset_input_buffer() # clear any accumulated garbage
#port.read(1) #ditch the 1st char - always seems to be 0

#transfer the file
result=XMODEM(getc, putc).send(stream, quiet = 1)

stream.close()
port.close()

if result:
    print ("\ntransfer successful")
    sleep(.25)
else:
    print ("\ntransfer unsuccessful")
    x=raw_input("press enter to continue...");	

***************Python xmodem sender follows
#xmbootb.py xmodem sender for the 1802 olduino
from __future__ import print_function
import sys
import logging
logging.basicConfig()
import serial
try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO
from xmodem import XMODEM, CRC
from time import sleep
import os
if len(sys.argv)>1:
    filename=sys.argv[1]
else:
    filename="a.bin"
fileSize=os.path.getsize(filename)
print ("File Size is",fileSize)
xpect=(fileSize/128)

def getc(size, timeout=1):
    return port.read(size)
xcount=0
xprog=0
def putc(data, timeout=1):
    global xcount,xprog
    port.write(data)
    if (len(data)==128): # if it's a block
    	xcount=xcount+1
    	newprog=(xcount*50)/xpect
    	for x in range(newprog-xprog):
            sys.stdout.write("#")
        sys.stdout.flush()
        xprog=newprog
    sleep(0.001) # give device time to send ACK

#Main program starts here - define the serial port, set RTS off, then open it
port = serial.Serial(parity=serial.PARITY_NONE,
                     bytesize=serial.EIGHTBITS,
                     stopbits=serial.STOPBITS_ONE,timeout=5,xonxoff=0,
                     rtscts=0,dsrdtr=0,baudrate=57600)
port.rts=False
port.port='COM3'
port.open()
port.rts=True
sleep(0.1)
port.rts=False
#open the file to be loaded
stream = open(filename,'rb')
sleep(0.2) #give olduino a chance to get Q under control
port.reset_input_buffer() # clear any accumulated garbage
#port.read(1) #ditch the 1st char - always seems to be 0

#transfer the file
result=XMODEM(getc, putc).send(stream, quiet = 1)

stream.close()
port.close()

if result:
    print ("\ntransfer successful")
    sleep(.25)
else:
    print ("\ntransfer unsuccessful")
    x=raw_input("press enter to continue...");

Advertisements

Baby Steps Monitor

I need some kind of simple onboard debugging monitor for the olduino at least until I get the xmodem loader working well. I’ve started one written in C. It counts on getting the registers saved during initialization and it can display the regs and memory. next step will be to add the xmodem receive command and then automate an initial xmodem attempt on startup. I’m doing this all in C of course which makes it large but i don’t care! I have a 32k rom to fill.
In the sequence below I’ve entered ‘r’ to display the registers then 7f00 cr to set the working address to 7f00 where the regs are saved then ? to display memory at that location which verifies the r display.

15:38:54.511> Serial Monitor Here
15:38:57.067> > registers
15:38:57.067> 
15:38:57.145> R00 12DB 0001 7FE3 0506 
15:38:57.145> R04 1321 1331 014E 0072 
15:38:57.223> R08 019D FF0A 000B 0000 
15:38:57.223> R0C 0020 002B 7F1D 0072 
15:44:48.347> > ?
15:44:48.347> 
15:44:48.470> 0000 C0 12 97 00 81 73 91 73 86 73 96 73 87 73 97 73 
15:44:56.472> > 0007
15:44:57.162> > 007F
15:44:58.536> > 07F0
15:44:58.660> > 7F00
15:45:01.910> > ->7F00
15:45:01.910> 
15:45:02.035> 7F00 12 DB 00 01 7F E3 05 06 13 21 13 31 01 4E 00 72 
#include <olduino.h>
#include <nstdlib.h>
#define putc(x) putcser(x)
unsigned char getcser();
#define getc() getcser()
void putcser(unsigned char);
#include <dumper.c>
void regdump(unsigned int* data){
	unsigned int i;
	for(i=0;i<16;i++){
		if (0==(i%4)) printf("\nR%cx ",i);
		printf("%x ",*data++);
	}
	printf("\n");
}
void main(){
	unsigned char cin='?';
	unsigned char * addr=0;
	unsigned int* regaddr=(unsigned int*)0x7f00;
	unsigned int caddr=0;
	asm(" seq\n"); //make sure Q is high to start
	printf("\nSerial Monitor Here\n");
	while(1){
		printf("> ");
		cin=getc();
		if ((cin>='0') &(cin<='9')){
			caddr*=16;caddr+=cin-'0';
			printf("%x\n",caddr);
		}else if ((cin>='a') & (cin<='f')){
			caddr*=16;caddr+=cin-'a'+10;
			printf("%x\n",caddr);
		}else{
			switch (cin){
				case '\r':
					printf("->%x\n",caddr);
					addr=(unsigned char *)caddr;caddr=0;
					dump(addr,16);
					break;
				case '?':
					printf("?\n");
					dump(addr,16);
					break;
				case 'r':
					printf("registers\n");
					regdump(regaddr);
					break;
				case '+':
					printf("+\n");
					addr+=16;
					dump(addr,16);
					break;
				case '-':
					printf("-\n");
					addr-=16;
					dump(addr,16);
					break;
				default:
					printf("%c/%xc unrecognized\n",cin,cin);
					break;
			}
		}
	}
}
void includeser(){

	asm("BAUDRATE EQU 	9600\n");
	asm(" include VELFserial3.inc\n");
}
#include <olduino.c>
#include <nstdlib.c>
void saveregs(){asm("SAVEREGS: EQU 1\n");}

I always want the last word so that wordpress doesn’t eat my code!

Serial Jumble

17-11-22 logic vs windows
Just for my records. I see a fair bit of corruption in the serial datastream shown by my terminal program. There’s enough that i wouldn’t consider it useable. I tracked down one instance and lined up the logic analyzer with the terminal program output. The logic analyzer trace looks fine but somehow windows or the terminal program is goobering it. Note that i’ve been using this program forever with no such issues. typically seeing this i would think the AVR was at fault but it’s not involved in the serial flow. I am using the pololu serial adapter which is unusual for me – maybe that’s what’s up.

UPDATE: yup – the pololu adapter was causing the issue. when i use my normal ftdi cable it’s fine. For the record, the “monitor program” and serial routines are below. AND: I tried the pololu adapter at 4800 baud with similar results. just not practical for what i’m doing. Unfortunate because It means swapping cables to test. I do have another FTDI adapter I can try though.

//VserialNW.c - serial demo program
//17-11-21 adapted to use makeit.bat for setting options
#include <olduino.h>
#include <nstdlib.h>
#define putc(x) putcser(x)
unsigned char getcser();
#define getc() getcser()
void putcser(unsigned char);
#include <dumper.c>
void main(){
	unsigned char cin='?';
	unsigned char * addr=0;
	asm(" seq\n"); //make sure Q is high to start
	printf("\nSerial Monitor Here\n");
	while(1){
		printf("> ");
		cin=getc();
		switch (cin){
			case '?':
				printf("?\n");
				dump(addr,256);
				break;
			case '+':
				printf("+\n");
				addr+=256;
				dump(addr,256);
				break;
			default:
				printf("%c/%x unrecognized\n",cin,cin);
				break;
		}
	}
}
void includeser(){
	asm(" include VELFserial3.inc");
}
#include <olduino.c>
#include <nstdlib.c>
************VELFserial3.inc follows********************
;**********************************************************************
;bit-bang Serial routines adapted from Josh Bensadon's VELFbios-v3.1.asm
;Transmit Byte via Q connected to RS232 driver
;call via SCRT
;Byte to send in r12.0
;Destroys r14
;17-11-22 regoobered to 
;----------------------------------------------------------------------
bitdelay: MACRO baudrate,cpuspeed,baseline,xreg
	rept ((cpuspeed/(baudrate*8)-baseline))/3
	NOP
	endm
	rept (((cpuspeed/(baudrate*8)-baseline)#3))>=1
	sex xreg
	endm
	ENDM
 IFNDEF BAUDRATE
BAUDRATE EQU 	9600
 ENDIF
 IFNDEF CPUSPEED
CPUSPEED EQU 	2000000
 ENDIF
	align 32
_putcser:		;entry from C program with character in reg 12
	ldi 9		;9 bits to transmit (1 start + 8 data)
	plo r14
	glo r12
	shl		;set start bit
	rshr		;DF=0

.txcloop:
	bdf $+5		;10.5   jump to seq to send a 1 bit
	req		;11.5   send a 0 bit
	br $+5		;1      jump +5 to next shift
	seq		;11.5   send a 1 bit
	br $+2		;1      jump +2 to next shift (NOP for timing)
	rshr		;2      shift next bit to DF flag
	phi r14		;3      save D in r14.1
	DEC r14		;4      dec bit count
	glo r14		;5      get bit count
	bz .txcret	;6      if 0 then all 9 bits (start and data) sent
	ghi r14		;7      restore D
	bitdelay BAUDRATE,CPUSPEED,20,2
	br .txcloop	;9.5    loop back to send next bit
.txcret: ghi r14		;7
	bitdelay BAUDRATE,CPUSPEED,16,2
	seq		;11.5 stop bit
	cretn		;return and re-call will cover stop bit time
;**********************************************************************
;rx_char
;Receive Byte via EF3 connected to RS232 receiver
;Receives 8 bits
;call via SCRT
;Returns with Byte received in D and rf.0
;Destroys r14.0
;----------------------------------------------------------------------
	align 64
_getcser:
 	ldi 8		;start bit +7 bits from loop, last bit on returning
	plo r14
	ldi 0
.rxcw:			;wait for start bit
	;bn4 $+4		;loop while high
	;br  RomLoad
	bn3 .rxcw	;each instr takes 9us, we need 104us = 11.5
			;delay 1/2 bit time to center samples
	NOP		;     Don't test for correct start bit
	NOP		;     it will work. if there's too much
	NOP		;     noise on the line, shorten the cable!
.rxcloop:
	bitdelay BAUDRATE,CPUSPEED,20,2
	b3 $+6		;11.5 sample rx input bit
	ori 80h		;1
	br $+4		;2
	phi r14		;1
	phi r14		;2
	shr		;3
	phi r14		;4
	DEC r14		;5
	glo r14		;6
	bz .rxcret	;7
	ghi r14		;8
	br  .rxcloop	;9
.rxcret: ghi r14	;8
	ghi r14		;9
	bitdelay BAUDRATE,CPUSPEED,20,2
	b3 $+4		;11.5 sample last rx input bit
	ori 80h		;1
	plo r15
	cretn

17-11-22 serialOK

UPDATE: The analogy to the AVR made me think that maybe i was just overrunning a buffer in the Pololu chip. I tried it cutting down the size of the dump to 16 bytes and it’s fine – great news.

Well Poop – That Time When a Resonator Broke My Heart.

17-11-16 2!=6
I’ve spent hours trying to figure out why my bootloader wouldn’t work in the ROM with a 2 MHz resonator – the timing seemed mysteriously way off. In desperation I tried a different CPU board with a 4 MHz resonator(adjusting the code) and it worked. I measured the clock on the original board and found the the clearly marked 2 MHz resonator was oscillating at 6 MHz!

Now my code still doesn’t work but at least that’s my fault!

Programming the CY27C256

The address lines going to narrow memory slot on the Membership Card (U8 which is under U2). Are scrambled a bit. To compensate for that I wrote a little python script to read the assembler output and scramble the data so that the 1802 will see the correct code.

So the sequence is:

  • assemble and scramble the code using the a02boot.bat file
  • program the CY27C256 using the TL866CS programmer (skip the blank check)

As it happens, the first try at programming the chip failed verify at location 4000 expecting 00 it found 7B.  This is kind of suspicious since 0000 is 7B but I’m ignoring it for now.

I’m attempting to load the xmodem bootloader which is now under 256 bytes and should run from RAM or ROM.  It will load code to 0x200 and branch to it. I’m compiling it for 9600 baud at 2MHz which will be easy to test and will scale up to a delightful 57600 baud at 12MHz.

Echo this will prepare a boot image for the narrow ROM on a REV J membership card
@c:\lcc42\bin\asw -cpu 1802 -quiet -x -i ..\..\include -L  %1.asm
@c:\lcc42\bin\p2hex -r $-$ %1 %1.hex
C:\Users\bill\Documents\olduinoz\asmx\hex2bin %1.hex
python scramble2.py %1.bin
import sys
fi = open(sys.argv[1], 'rb')
inp = bytearray(fi.read())
fi.close()
n=len(inp)
outp=bytearray(n+16) # allows for address bit 0 going to A4 i.e 1>17
print n
for a in range(0,n):
    a2=a&0xFFE2|((a&0x10)>>1)|((a&0x08)>>1)|((a&0x04)>>2)|((a&0x01)<<4)
    outp[a2]=inp[a]
fo = open(sys.argv[1]+'.scrambled', 'wb')
fo.write(outp)
fo.close()
;17-07-19 Standalone xmodem boot loader xmboot.asm
;	receives to fixed address APPADDR
;17-10-04 includes dummy program at 0x200
;17-11-07 adapting for ROM use. 
	relaxed on
NAK:	EQU 0x15
SOH:	EQU 0x01
EOT:	EQU 0x04
ACK:	EQU 0x06
Rrcv:	EQU 8
Rsnd:	EQU 9
R14:	EQU 14
R12:	EQU 12
R11:	EQU 11
R3:	EQU 3
R0:	EQU 0
APPADDR: EQU 0x200
blksize:	EQU 128
ldAD:	macro	reg1,directaddress	;load an absolute address or a constant into a register
	ldi	(directaddress)&255
	plo	reg1
	ldi	(directaddress)>>8; was/256
	phi	reg1
	endm

	seq			;prep Q for serial output
; XMODEM receiver based on xr.asm by Michael H Riley and serial routines by Josh Bensadon   
	ldaD	R12,APPADDR

here:
	ghi	R0		;get our page address
	phi	Rsnd		;use for receive routine
	phi	Rrcv		; and send routine
	ldi	serout&255
	plo	Rsnd		;bottom of send address
	ldi    serinT&255	
	plo	Rrcv		;bottom of receive address

	ldi     NAK             ; need to send NAK to start
        sep     Rsnd
        sep     Rrcv          	; test for incoming character or timeout
        bnf     ckeot		;continue if no timeout
	ldaD    Rrcv,serinT	;reload address of serial routine with timeout
	ldi     NAK             ; retry NAK to start
        sep     Rsnd
        sep     Rrcv          	; test for incoming character or timeout
        bnf     ckeot		;continue if no timeout
	br 	launchapp		;timeout - launch already loaded application

filelp:    ;receive address is in R12, length goes in R11
;begining of block read. returns to filelp or exits to filedn   				
        sep     Rrcv          	; wait for incoming character
ckeot:	smi     EOT              ; check for EOT
        bz     filedn           ; jump if so

	sep     Rrcv               ; read block number
	sep     Rrcv               ; read inverted block number

	ldi     blksize             ; 128 bytes to receive
	plo     r11

readlp: sep     Rrcv               ; read data byte
        str     r12                  ; store into output buffer
        inc     r12                  ; point to next position
        dec     r11                  ; decrement block count
        glo     r11                  ; see if done
        bnz     readlp              ; loop back if not
;end of block read
        sep     Rrcv               ; read checksum byte


        ldi     ACK                  ; send an ACK
        sep     Rsnd
       	lbr     filelp              ; loop back for more
filedn:    
        ldi     ACK                  ; acknowledge end of transmission
        sep     Rsnd

launchapp:				;dispatch code newly loaded or previously left 
	sex	0		;X=0
	lbr	Appaddr		;go to loaded application


; *******************************************************************
; *** This software is copyright 2005 by Michael H Riley          ***
; *** You have permission to use, modify, copy, and distribute    ***
; *** this software so long as this copyright notice is retained. ***
; *** This software may not be used in commercial applications    ***
; *** without express written permission from the author.         ***
; *******************************************************************
	db	"17-10-04 10:00"

LCC1802CPUSPEED EQU 2000000
__BAUDRATE EQU 	9600
	include "serwjrT1.inc"	
;bit-bang Serial routines adapted from Josh Bensadon's VELFbios-v3.1.asm
;https://groups.yahoo.com/neo/groups/cosmacelf/files/Member-JoshBensadon/
;Transmit Byte via Q connected to RS232 driver
;call via sep, returns via sep R3
;Byte to send in D
;Destroys r14
;17-09-02 this version times out on the first call only - approx 1.5 sec at 4MHz
;----------------------------------------------------------------------
 	IFNDEF 	LCC1802CPUSPEED
LCC1802CPUSPEED EQU 4000000
 	ENDIF
 	MACEXP ON
bitdelay: MACRO baudrate,cpuspeed,baseline,xreg
	rept ((cpuspeed/(baudrate*8)-baseline))/3
	NOP
	endm
	rept (((cpuspeed/(baudrate*8)-baseline)#3))>=1
	sex xreg
	endm
	ENDM
 	IFNDEF 	__BAUDRATE
__BAUDRATE EQU 	9600
	ENDIF
	align 64
serout:			;entry from assembly with char in D
	phi R14		;save char in R14.1
	ldi 9		;9 bits to transmit (1 start + 8 data)
	plo r14
	ghi R14
	shl		;set start bit
	rshr		;DF=0

.txcloop:
	bdf $+5		;10.5   jump to seq to send a 1 bit
	req		;11.5   send a 0 bit
	br $+5		;1      jump +5 to next shift
	seq		;11.5   send a 1 bit
	br $+2		;1      jump +2 to next shift (NOP for timing)
	rshr		;2      shift next bit to DF flag
	phi r14		;3      save D in r14.1
	DEC r14		;4      dec bit count
	glo r14		;5      get bit count
	bz .txcret	;6      if 0 then all 9 bits (start and data) sent
	ghi r14		;7      restore D
	bitdelay __BAUDRATE,LCC1802CPUSPEED,20,2,{EXPAND}
	br .txcloop	;9.5    loop back to send next bit
.txcret: ghi r14		;7
	bitdelay __BAUDRATE,LCC1802CPUSPEED,16,2
	seq		;11.5 stop bit
	bitdelay __BAUDRATE,LCC1802CPUSPEED,4,2
	sep R3		;return 
	br serout	;reset for next time
;**********************************************************************
;serinT
;Receive Byte via EF3 connected to RS232 receiver
;Receives 8 bits
;call via sep
;Returns with Byte received in D, DF is set if the start bit is never seen
;Destroys r14
;----------------------------------------------------------------------
	align 64
serinT:			;serial input with timeout of 65535*6*16 machine cycles - approx 1.5 sec at 4MHZ
	ldaD R14,0x4000	;R14 is timeout loop counter
.rxcw:	b3 .okgo	;check for start bit after each instruction
	dec 14
	b3 .okgo	;check for start bit after each instruction
	ghi 14
	b3 .okgo	;check for start bit after each instruction
	bnz .rxcw
;here we've had a timeout - set DF and return
	ldi 1
	shr
	sep R3		;return
	br  serinT	;for next time
.serinN:	bn3 .serinN	;serial input without timeout	
.okgo:			;here we know the start bit is present
 	ldi 8		;start bit +7 bits from loop, last bit on returning
	plo r14
	ldi 0
	NOP		;delay to center samples

.rxcloop:
	bitdelay __BAUDRATE,LCC1802CPUSPEED,20,2
	b3 $+6		;11.5 sample rx input bit
	ori 80h		;1
	br $+4		;2
	phi r14		;1
	phi r14		;2
	shr		;3
	phi r14		;4
	DEC r14		;5
	glo r14		;6
	bz .rxcret	;7
	ghi r14		;8
	br  .rxcloop	;9
.rxcret: ghi r14	;8
	ghi r14		;9
	bitdelay __BAUDRATE,LCC1802CPUSPEED,20,2
	b3 $+4		;11.5 sample last rx input bit
	ori 80h		; for a 1 bit
	adi 0		;clear the DF flag because no timeout
	sep R3		;return
	br  .serinN	;for next time - only timeout on first call
	

UPDATE:
17-11-10 descrambled
The good news is that the scrambling/descrambling works. The ROM is addessed at 8000 and a little dump program shows the correct code.
Testing is a bit of a bugger though. I need a “trampoline” at location 0 to jump to the ROM. The ROM should load code over top of it. Using the logic analyzer I can see that the ROM is sending the NAK twice as designed then dispatching the code at location 0 which branches back to it and NAKs some more. promising anyway.

Printing on the Boyd Calculator

img_0044
The Boyd Calculator has a thermal printer and part of my plan has always been to use it for ascii art or cellular automata or something.

I’m temporarily stymied programming it because I borked my eeprom chip.  For next time though, I’m recording the following:

     268/     366 : 7B                   seq ;activate the printer
     269/     367 : E3                   sex 3 ;inline output data
     270/     368 : 61                   out 1
     271/     369 : 34                   db '4'
     272/     36A : 61                   out 1
     273/     36B : 32                   db '2'
     274/     36C : 61                   out 1
     275/     36D : 34                   db '4'
     276/     36E : 61                   out 1
     277/     36F : 32                   db '2'
     278/     370 : 61                   out 1
     279/     371 : 34                   db '4'
     280/     372 : 61                   out 1
     281/     373 : 32                   db '2'
     282/     374 : 61                   out 1
     283/     375 : 34                   db '4'
     284/     376 : 61                   out 1
     285/     377 : 32                   db '2'
     286/     378 : 61                   out 1
     287/     379 : 34                   db '4'
     288/     37A : 61                   out 1
     289/     37B : 32                   db '2'
     290/     37C : 61                   out 1
     291/     37D : 34                   db '4'
     292/     37E : 61                   out 1
     293/     37F : 32                   db '2'
     294/     380 : 7A                   req ;deactivate printer?
     295/     381 : E2                   sex 2 ;reset X register!

 

The printer is very easy to talk to.  As you pointed out, the 8049 is doing all the hard work for you.

Here's some pieces of the code that prints.

        SEX    R(F)    ;X=RF
        SEQ        ;Q=1
        LDI    10h                         ;93B: F8 10
        PHI    R(F)                        ;93D: BF
        LDI    1Fh                         ;93E: F8 1F
        PLO    R(F)    ;RF=101F Temp for OUTPUT

             ;this prints a minus sign (only when it needs to be printed of course)
        LDI    '-'                        ;947: F8 2D
        STR    R(F)                       ;949: 5F
        OUT    PIN(1)    ;OUT1 = '-'

               ;this prints one of the numbers, notice the bcd to ascii conversion
        LDN    R(D)                       ;967:  D
        ANI    0Fh                         ;968: FA  F
        ORI    30h                         ;96A: F9 30
        STR    R(F)                       ;96C: 5F
        OUT    PIN(1)                     ;96D: 61     

                the last 2 characters are printed as ASCII characters, M(RD) is loaded with stuff like 'X', '=', '+'
        LDN    R(D)
        STR    R(F)                       ;A35: 5F
        OUT    PIN(1)                     ;A36: 61     

        At the end of the printing, Q is cleared.

 

0933: GLO  R4   D=00
0934: ANI  40   D=00
0936: BNZ  32
0938: NOP
0939: SEX  RF
093A: SEQ			*
093B: LDI  10
093D: PHI  RF   RF=10D2
093E: LDI  1F
0940: PLO  RF   RF=101F
0941: LDI  06
0943: PLO  RD   RD=1006
0944: LDN  RD   D=00
0945: BZ   50
0950: DEC  RD   RD=1005
0951: LDN  RD   D=00
0952: BZ   5B
095B: LDI  20
095D: STR  RF   M(101F)=20	12
095E: OUT  1    [20]		**1
095F: DEC  RF   RF=101F
0960: NOP
0961: DEC  RD   RD=1004
0962: LDN  RD   D=E0
0963: XRI  E0   D=00
0965: BZ   71
0971: LDI  20
0973: STR  RF   M(101F)=20	8
0974: OUT  1    [20]		**2
0975: DEC  RF   RF=101F
0976: NOP
0977: DEC  RD   RD=1003
0978: LDN  RD   D=E0
0979: XRI  E0   D=00
097B: BZ   87
0987: LDI  20
0989: STR  RF   M(101F)=20	8
098A: OUT  1    [20]		**3
098B: DEC  RF   RF=101F
098C: NOP
098D: DEC  RD   RD=1002
098E: LDN  RD   D=E0
098F: XRI  E0   D=00
0991: BZ   9D
099D: LDI  20
099F: STR  RF   M(101F)=20	8
09A0: OUT  1    [20]		**4
09A1: DEC  RF   RF=101F
09A2: NOP
09A3: LDI  20
09A5: STR  RF   M(101F)=20	4
09A6: OUT  1    [20]		**5
09A7: DEC  RF   RF=101F
09A8: NOP
09A9: DEC  RD   RD=1001
09AA: LDN  RD   D=E0
09AB: XRI  E0   D=00
09AD: BZ   DA
09DA: LDI  20
09DC: STR  RF   M(101F)=20	8
09DD: OUT  1    [20]		**6
09DE: DEC  RF   RF=101F
09DF: NOP
09E0: LDI  20
09E2: STR  RF   M(101F)=20	4
09E3: OUT  1    [20]		**7
09E4: DEC  RF   RF=101F
09E5: NOP
09E6: LDI  20
09E8: STR  RF   M(101F)=20	4
09E9: OUT  1    [20]		**8
09EA: DEC  RF   RF=101F
09EB: NOP
09EC: DEC  RD   RD=1000
09ED: LDN  RD   D=F0
09EE: XRI  E0   D=10
09F0: LBZ  0A1F
09F3: LDN  RD   D=F0
09F4: ANI  0F   D=00
09F6: SMI  0A   D=F6
09F8: LBDF 0A0C
09FB: LDI  20
09FD: STR  RF   M(101F)=20	13
09FE: OUT  1    [20]		**9
09FF: DEC  RF   RF=101F
0A00: NOP
0A01: LDN  RD   D=F0
0A02: ANI  0F   D=00
0A04: ORI  30   D=30
0A06: STR  RF   M(101F)=30	6
0A07: OUT  1    [30]		**10
0A08: DEC  RF   RF=101F
0A09: NOP
0A0A: BR   2B
0A2B: LDI  20
0A2D: STR  RF   M(101F)=20	5
0A2E: OUT  1    [20]		**11
0A2F: DEC  RF   RF=101F
0A30: NOP
0A31: LDI  1E
0A33: PLO  RD   RD=101E
0A34: LDN  RD   D=43
0A35: STR  RF   M(101F)=43	6
0A36: OUT  1    [43]		**12
0A37: DEC  RF   RF=101F
0A38: NOP
0A39: DEC  RD   RD=101D
0A3A: LDN  RD   D=20
0A3B: STR  RF   M(101F)=20	5
0A3C: OUT  1    [20]		**13
0A3D: DEC  RF   RF=101F
0A3E: NOP			2
0A3F: REQ			*
0A40: LBR  0932

I note that between the initial seq and the first “OUT 1” there are a dozen instructions. There are 13 OUT 1’s rather than the 12 I thought with at least 4 instructions between them. There are a couple of instructions between the last OUT 1 and the req.

void hello(){
	asm(" seq ;activate the printer\n"
	    " sex 3 ;inline output data\n"
	    " nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '4'\n"
	    " nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '2'\n"
	    " nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '4'\n"
	    " nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '2'\n"
	    " nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '4'\n"
	    " nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '2'\n"
	    " nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '4'\n"
	    " nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '2'\n"
	    " nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '4'\n"
	    " nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '2'\n"
	    " nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '4'\n"
	    " nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '2'\n"
	    " nop\n nop\n nop\n nop\n"
	    " out 1\n"
	    " db '2'\n"
	    " nop\n nop\n"
	    " req ;deactivate printer?\n"
	    " sex 2 ;reset X register!\n"
	);

UPDATE: So i tried my code by shoehorning it into the first 7f bytes of the borked eeprom. I ran it with no result. trying it in emma it seems like it should be close enough so i dunno what’s wrong.

My next step is to try to hoke up a larger eeprom adapter to fit the whole of the original prom contents and go from there.

0004: DEC  R9   R9=FD00
Hit Breakpoint (after long timing loop)
0008: SEQ
0009: SEX  R0
000A: NOP
000B: NOP
000C: NOP
000D: NOP
000E: NOP
000F: NOP
0010: NOP
0011: NOP
0012: OUT  1    [20]
0014: NOP
0015: NOP
0016: NOP
0017: NOP
0018: OUT  1    [20]
001A: NOP
001B: NOP
001C: NOP
001D: NOP
001E: OUT  1    [20]
0020: NOP
0021: NOP
0022: NOP
0023: NOP
0024: OUT  1    [20]
0026: NOP
0027: NOP
0028: NOP
0029: NOP
002A: OUT  1    [20]
002C: NOP
002D: NOP
002E: NOP
002F: NOP
0030: OUT  1    [20]
0032: NOP
0033: NOP
0034: NOP
0035: NOP
0036: OUT  1    [20]
0038: NOP
0039: NOP
003A: NOP
003B: NOP
003C: OUT  1    [20]
003E: NOP
003F: NOP
0040: NOP
0041: NOP
0042: OUT  1    [20]
0044: NOP
0045: NOP
0046: NOP
0047: NOP
0048: OUT  1    [30]
004A: NOP
004B: NOP
004C: NOP
004D: NOP
004E: OUT  1    [20]
0050: NOP
0051: NOP
0052: NOP
0053: NOP
0054: OUT  1    [43]
0056: NOP
0057: NOP
0058: NOP
0059: NOP
005A: OUT  1    [20]
005C: NOP
005D: NOP
005E: REQ

I always want the last word so that wordpress doesn’t eat my code!

The Usual Sunday WTF


Rainy day so i figured i would dig into the Boyd calculator printing process. Of course my tools are getting in the way.

I have a new purpose-built programmer – a TL866CS that I have used several times successfully and when I tried it with the EXEL XL28C16BP that i’m using in the boyd it programmed fine. then i changed the code and everything went to sh*t. The basic phenomenon is that the code at location 0-7F gets overlaid with the code at 80-FF. Same for 1xx, 2xx etc. Like A7 was being forced low.

I reconstructed the ghetto programmer that i had been using before the TL866CS and just got all kinds of crazy inconsistent/unrepeatable results. Finally I found that the output-enable for the 74595 shift register supplying the high address bits was disconnected. ** this is probably why I couldn’t get it to program the UV EPROM’s!**.

Having reconnected the /OE I found that the ghetto programmer gives the same results as the TL866CS – like the A7 pin of the flash eeprom was being held low.

I also tried a couple of other things with the TL866CS programmer – reading an already programmed rom and testing an sram chip. Everything checked out properly.

I am now assuming that I wrecked the eeprom chip – the fact that pin 1(A7) is VPP(high voltage) for some other ROMs tempts me to think that i inadvertently selected the wrong chip at some point and fried it.

 

And This Time With Code


Here’s the 16MHz 1802 actually running code from memory. It’s a simple program, all in one page, but it is running lickety split. I had to load it with an 8MHz resonator but it resets and runs coherently. Remember that this chip is rated for 3.2MHz at 5V.

Loading with the olduino definitely didn’t work at 16MHz. I had to load the program at 8MHz then switch the crystal. Further, I tried a more complex blink that runs on many pages and depends on writing memory and it went nowhere. I believe though, that in the right environment, this cpu could successfully run a practical program at 16MHz.

16 MHz 1802 Bangs Out a Full MIP!

 I made a minimal test circuit to do a first cut speed test of 1802’s, selecting those that might run at 16MHz. At first I made the usual circuit where you tie a combination of data lines high/low so the 1802 always reads a C4(No-OP) instruction. This just continuously executes NOPs and you can watch the address lines march up. Then I had the bright idea to somehow alternate the instructions that toggle Q so an external LED would cycle. to do this i tied the top 7 data lines to binary 0111 101 and looped bit 0 around to A0. This way the 1802 alternately sees a 7A or 7B depending on whether the address is even or odd. Bingo, the first chip I tried ran successfully at 16MHz. No guarantee that it will run in a real circuit but it’s encouraging and I can tell which chips are worth trying.

In the screen shot below the top signal is SC0 which is pulsing at 1MHZ as instructions are fetched. The second signal is Q which is alternating with every other instruction.
17-11-01 aMIP

Membership Card Goes Up Past 11!

 

img_0026-1I tried another CDP1802ACE chip from my ebay purchase – nominally manufactured by Harris in 1996.  These chips are rated for 3.2MHz at 5V but this one runs quite happily at 12MHz.  To run the Dhrystone benchmark I had to change the putc routine to insert long delays to pace the output to the AVR/PC at no more than 57,600 baud(5760 characters/second).

This will not be the record for 1802 speed – Ian May has been running them at over 32MHz with overvoltage and a Peltier cooler(!) – but it’s certainly a record for Drhrystones/second with my compiler in a more-or-less stock membership card.

At just under 5 seconds for 1000 executions I make it 208 Dhrystones/second.  This is not as fast as a Z-80 at 4MHz but it’s wicked fast for an 1802!

img_0027
UPDATE: I’ve tried 5 of the 10 eBay chips – 3 of them were fine at 12 MHz and two are only reliable at 10. I had also bought some 1802’s from an aliexpress vendor – these were older RCA chips and the one I tried would clock at 8MHz but was only reliable at 6.

14:12:53.231> Execution starts, 1000 runs through Dhrystone !@#
14:12:58.047> Execution ends!@#
14:12:58.047>
14:12:58.047> Final values of the variables used in the benchmark:
14:12:58.047>
14:12:58.047> Int_Glob:            5
14:12:58.047>         should be:   5
14:12:58.047> Bool_Glob:           1
14:12:58.047>         should be:   1
14:12:58.047> Ch_1_Glob:           A
14:12:58.047>         should be:   A
14:12:58.667> Ch_2_Glob:           B
14:12:58.667>         should be:   B
14:12:58.667> Arr_1_Glob[8]:       7
14:12:58.667>         should be:   7
14:12:58.667> Arr_2_Glob[8][7]:    1010
14:12:58.667>         should be:   Number_Of_Runs + 10
14:12:58.667> Ptr_Glob->
14:12:58.667>   Ptr_Comp:          24576
14:12:58.667>         should be:   (implementation-dependent)
14:12:58.667>   Discr:             0
14:12:58.667>         should be:   0
14:12:58.667>   Enum_Comp:         2
14:12:58.667>         should be:   2
14:12:58.667>   Int_Comp:          17
14:12:58.667>         should be:   17
14:12:58.667>   Str_Comp:          DHRYSTONE PROGRAM, SOME STRING
14:12:58.667>         should be:   DHRYSTONE PROGRAM, SOME STRING
14:12:58.667> Next_Ptr_Glob->
14:12:58.667>   Ptr_Comp:          24576
14:12:58.667>         should be:   (implementation-dependent), same as above
14:12:58.667>   Discr:             0
14:12:58.667>         should be:   0
14:12:58.667>   Enum_Comp:         1
14:12:58.667>         should be:   1
14:12:58.667>   Int_Comp:          18
14:12:58.667>         should be:   18
14:12:58.667>   Str_Comp:          DHRYSTONE PROGRAM, SOME STRING
14:12:58.667>         should be:   DHRYSTONE PROGRAM, SOME STRING
14:12:58.667> Int_1_Loc:           5
14:12:58.667>         should be:   5
14:12:58.667> Int_2_Loc:           13
14:12:58.667>         should be:   13
14:12:58.667> Int_3_Loc:           7
14:12:58.667>         should be:   7
14:12:58.667> Enum_Loc:            1
14:12:58.667>         should be:   1
14:12:58.667> Str_1_Loc:           DHRYSTONE PROGRAM, 1'ST STRING
14:12:58.667>         should be:   DHRYSTONE PROGRAM, 1'ST STRING
14:12:58.667> Str_2_Loc:           DHRYSTONE PROGRAM, 2'ND STRING
14:12:58.667>         should be:   DHRYSTONE PROGRAM, 2'ND STRING
14:12:58.667> done

 

;IO1802.inc contains input/output runtime routines for LCC1802
;The port is in regArg1, the output byte is in regArg2
;17-10-16 adding _putcpace(port,byte) to limit output speed to the AVR
;         defaulting putc(x) to putcpace(7,x)
	align 64
_putc:		;default putc(x) is paced output on port 7
	cpy2 	13,12	;move data to second param reg
	ldad	12,7	;select port 7
	br	_putcpace ;drop into paced output
_putcpace:	;putcpace(x,y) does host output on port x at no more than about 57600 baud
		;i'm doing the pacing before the output so i can branch to _out when done.
 if MOMCPU=$1805	;first balance off the call path for the 1806
	nop
	nop
	nop
 endif
 ifdef CPUSPEED
   if CPUSPEED>4000000	;now deal with the processor speed
      ;targetting a thruput of 57600 baud - 5760 characters per second
      ;each character needs CPUSPEED/5760/16 instruction times
      ;the baseline out() is 42 instructions including call/return
       NEEDINST: equ (CPUSPEED/5760/16)
       ldi (NEEDINST-42+1)/2
-:     smi 1
       bnz -
    endif
  endif
  br _out

_inp:		;raw port input
		;stores a small tailored program on the stack and executes it
	dec	sp	;work backwards
	ldi	0xD3	;return instruction
	stxd
	glo	regarg1	;get the port number
	ani	0x07	;clean it
	bz	+	; inp(0) isn't valid
	ori	0x68	;make it an input instruction
	stxd		;store it for execution
	cpy2	rt1,sp	;rt1&sp now point to an empty byte to be overwritten by the inp instruction
	inc	rt1	;rt1 points to the 6x instruction
	sep	rt1	;execute it
;we will come back to here with the input byte in D
	inc	sp	;step over the work area
	plo	retVal	;save it to return
	ldi	0
	phi	retval	;clear top byte
+	inc	sp	;need to get rid of the 6x instruction
	inc	sp	;and the D3
	Cretn		;and we're done

_out:		;raw port output
		;stores a small tailored program on the stack and executes it
		;this could be bolder:
		;store the program as 6x cc D5 where x is the port number and cc is the char
		;then SEP sp
		;the D5 would return to the calling program and finish fixing the stack.
		;saves 6 instructions but it's a bit tricky.
	dec	sp	;work backwards
	ldi	0xD3	;return instruction
	stxd
	cpy2	rt1,sp	;rt1 will point to the OUT instruction
	glo	regarg1	;get the port number
	ani	0x07	;clean it
	ori	0x60	;make it an out instruction - 60 is harmless
	stxd		;store it for execution
	glo	regarg2	;get the byte to be written
	str	sp	;store it where sp points
	sep	rt1	;execute it
;we will come back to here with sp stepped up by one
+	inc	sp	;need to get rid of the 6x instruction
	inc	sp	;and the D3
	Cretn		;and we're done

I always want the last word so that wordpress doesn’t eat my code!

An 1802 Origin Story With a Twist


The images are from an article on fastcodesign titled Rediscovering History’s Lost First Female Video Game Developer

Quite reasonably it’s focused on the role of Joyce Weisbecker who wrote some of the earliest code for what became the 1802 but it also does a good job of relating the development of the COSMAC architecture as her father Joseph’s pet project.  The Fred prototypes were built based on TTL chips and only later integrated into the cmos 1801/1802 series.  Herb Johnson has done a great compendium of Fred info here.

Joyce Weisbecker is still around and has been generous with her time for this article and other discussions with fans of her father’s work

Yay – The Grandson Compiler Is Close…

I’ve been working on a new version of the compiler off and on all this year and finally, in the last couple of days, I’ve done some thorough testing.

Version NW supports code generation for the 1804/5/6 with option “Wf-cpu1805” and also allows tailoring for the run-time environment with “-Wf-env=xxxx”. Under the covers I’ve switched the stack discipline so the 1802/4/5/6 maintains a clean stack – R2 always points BELOW the last byte used.

The -cpu1805 option sets the CPU 1805 for the assembler, tells the macros to generate 1804/5/6 specific instructons, and also tells the compiler that registers 4 and 5 are available for variables.

The -env=xxxx makes the compiler include xxxxprolog.inc before the compiler prolog so you can set codeloc, stackloc, cpuspeed, or whatever and include xxxxepilog.inc after the compiler epilog so you can have a custom I/O setup.

Testing for the 1806 code is a bit painful: I have a fakeloader setup that will load code to an other than location 0 but I have to run the test suite of a dozen or so programs for the 1802 at location 0, 1802 at an offset, then 1805. In every one of those sequences I think I found errors – mostly related to the stack discipline.

Half-baked Benchmark

I jury-rigged a dhrystone benchmark for the 1802 at 10MHz. It clocked about 150 dhrystones per second compared to my best previous result of 81/sec for an 1806 at 4MHz. For comparison, a Z80 at 4MHz can do 300/sec but with a much better compiler.

Biggest problem in adapting it was to have the AVR be fast enough to grab the report output from the 1802. I had to put a ton of nops in the 1802 print routines. Not sure if this affected timing.

Overclocking Failure Modes

Using my simple two chip circuit, I wanted to investigate what happened when an 180x ran over its clock limits. I tried my oldest chip, an 1802CD and a newer 1802ACE.

I was running a program equivalent to the following;

0000 LBR FF03
FF03 LBR 0000

This was all actually in the first 6 bytes of an NVRAM with all except the first 3 address lines pulled low.

The reason for this code was to force the 1802 to run in two pages alternately because there was reason to believe that getting the high order address byte correct could be an early failure point.

The first three images show the 1802CD running at 2 and 4 MHz and failing at 6.  In the 2 and 4 MHz cases, keying on the TPAs, you see the init cycle then fetch from 0000, execute 0001, execute 0002, fetch FF03, execute FF04, execute FF05.

17-10-14 CDP1802CD 6MHz zoom

At 6 MHZ it is going through the motions of fetch/execute but i don’t think it’s doing anything sensible. The first fetch is from something like xxxxx000xxxxx111, the subsequent execute reads something like xxxxx000xxxxx001 then it might be trying to fetch from xxxxx010xxxxx111 but the high address is gone before the TPA negative edge.  I think it has just lost it’s sh*t.  Presumably there are a whole bunch of things going on in the chip which are keyed off clock pulses and each with its own calculus of rise and fall setup and hold times. As long as the clock pulses are reasonably far apart it all works but if you crowd them: chaos ensues.

It should be noted that this 40 year old chip has a maximum clock frequency of 2.5Mhz at 5V so it’s a testament to engineering conservatism that it holds together at 4MHz. There still could be a bunch of failures that don’t show up in this simple test but I know from earlier experiments that this chip will execute arbitrary code properly at 4MHz.

Turning to a later version of the chip, the CDP1802ACE is rated for 3.2 MHz at 5V.  In this simple circuit and test it runs fine at 12 MHz and fails at 16 MHz(I don’t have an intermediate frequency resonator). The image below shows it in at 16MHz – it is doing fetch/execute but the address is stuck at xxxxx000xxxxx110. It’s worth noting that even here the high and low addresses are on the bus correctly with respect to TPA.

17-10-14 CDP1202ACE 16mhz
below for the record is the same test at 12MHz which looks fine to me. I have not yet tried this chip with real code in a real circuit.
17-10-14 CDP1202ACE 12mhz

One other learning in passing: The /MRD line is low for >400 ns so there’s just no risk that i would ever oveerun the NVRAM which has a response time of 45 ns.

Also, I can see the extraordinarily tight timing of the high address byte availability at these higher frequencies. The image below shows a fetch where the CPU is going from page 0 to say page FF. The high address lines come up well before TPA but they stay up only for 41 ns after TPA drops. This is enough time for a fast latch to capture it but not a ton more.
17-10-15 TPA1802
The 1806ACEs timing is a bit different than the 1802’s but not in this area. The high address is available for less time overall but the same time after the fall of TPA(which is when you have to latch it)
17-10-15 TPA1806
Also, the 1806 simply will not synch with a 16MHz resonator – it went to around 4-5MHz.

1806s from aliexpress

 

I ordered 5 each 1802ACE, 1802BCE, and 1806ACE from a seller on aliexpress in late august. The BCE devices are slightly higher spec than the A’s and the 1806s are descendants of the 1802 with a few added instructions and some newer features like a built in timer.

The 1802ACEs came last week And the 1806s came today so that’s just about six weeks from order to delivery.  The 1806s look very nice. They’ve obviously been pulled from circuits for resale but they’re in good shape. One lot of 10 1802‘s that I got from eBay is suspiciously nice looking, as if they’ve been sanded down and remarked which seems like a ridiculous amount of work for someone to go to but…

I’m looking forward to testing these for function and performance.

In Which I am Awash in 1802’s

 

I got two lots of 1802s today one lot of ten from an ebay seller and five from an aliexpress store. The ebay lot was very nicely packaged in foam and anti-static plastic, the aliexpress chips were stacked with the bottom chip reversed – very sturdy seeming.

Both sets are marked CDP1802ACE,.

The ebay chips are marked as Harris with a date code(?) of H9610 on top.  The bottoms are all marked MALAY with various lot numbers(?) like YJ66B, WQ24K, YJ52B, YJ80C, YJ37B.

The aliexpress chips are marked as RCA Z with date codes(?) of 610,719, 901, and 923. The bottom markings are less clear but include 6FVT0/06493, 6FVAA/U6714, 6FVM0,K6305, 6BVR0/N2556.

I would say all the chips are pulls rather than new old stock. They are very clean but the bottoms show tool marks and scratches and the pins look a bit battered. Interestingly it looks like someone went to some trouble to restore the original splayed out position of the pins – I had to go through some effort to bend them in like I would with a new chip.

I put one from each batch into a modified Membership Card and tested them both quickly. Both ran a blink sketch at up to 10MHz although the aliexpress Harris chip needed a reload so it might be a bit flaky. I tried a 16MHz resonator for fun but neither chip worked.

Below is one or the other of them blinking away like nobody’s business at 10MHz. It should be noted that this is C code it’s running so it’s accessing memory in the first couple of k of low memory and writing to the stack in high memory- it’s probably a fair test.

Ali express store (about five weeks delivery) https://m.aliexpress.com/store/storeHome.htm?spm=a2g0n.detail-cache.0.0.5cac3117hPRDC8&sellerAdminSeq=228406698#/

eBay seller (about three weeks delivery) https://www.ebay.com/usr/ywj4566

Two Chip 1806 Circuit

I’ve been using Lee Hart’s Membership Card CPU board as the basis for the 1802 Olduino.  It’s great and has a lot of capability and excellent build quality and documentation. As I work toward my high speed 1806 version though, there’s a lot of stuff on the board that I don’t need to drag along – the clock gate chip, the multiple memory types, provision for load mode etc.

As a beginning here’s my two chip 1806 circuit on a breadboard.  I’m using a non-volatile RAM that I programmed using an olduino so I don’t need load mode or a ROM.  I’m not using the upper address bits at the moment so no latch. I’ll next add the address latch and then gating to stop random writes to low memory.

17-09-25 twochip2

UPDATE: I am not getting a solid power on reset with that circuit. I occasionally get a glitch on /CLEAR. It seems there’s major hysteresis in the circuit.  /Clear takes 20 ms to go high with +V but >200ms to go low when +V goes away.  If power comes back within that 200ms it glitches.

Bye-bye mystery circuit

I need a breadboard to start building my three chip wonder so I pulled this off my dead project shelf and I’m cannibalizing it. I honestly have no idea what it was meant to be. I sort of recall a standalone 1802 project that got out of control and got shelved but I don’t see a 40 pin footprint on there and there’s a pin marked miso which doesn’t make a lot of sense. There is a 28 pin ram that supports the 1802 idea but anyway bygones.

Dinosaur Chip

That’s my original 1802 chip in ceramic with gold pins. It’s 40 years old and has been in and out of more circuits than I can count. It’s notably thinner than its later plastic counterparts- I’m surprised I haven’t broken it by now.

And There We Go – UV EPROM Succumbs to Sunlight

17-09-03 erased eprom
So in the end it took about 8 days of mostly sunny weather outdoors on a south-facing window-sill to clear all of the CY27C256. It also got briefly rained on then dried off with rubbing alcohol but i doubt that had any effect.
Actually there are STILL some uncleared locations in the eprom. It’s fine for testing if i’m careful.