Skip to content

I guess Meh About Sums it Up – Branch Optimization IV

The linear version of the branch optimization works pretty well now. I ran it on the Dhrystone code with a python profiler where it took .1 sec to shorten 100 of 144 branches in 8000 lines of code. On the same code the copt peephole optimizer did 119 optimizations on the 3000 lines of macro code it read. Probably safe to say that the copt changes saved about five or ten times as much as the branch optimizer since the peephole is usually taking code out at the macro level so several machine instructions a go and the optimized branches really only take out the equivalent of 1/2 instruction. If I could do it in a single pass or as part of copt fine but on its own maybe not worth it.

I ran a quick comparison of the linear version with the recursion version and there’s just no contest. The recursion version ran much much longer. I also accidentally overwrote the recursive version which i had not versioned so that’s that!

#18-02-10 linear two pass scan - no recursion
#18-02-13 trying to include aligns with jumps.
import sys
sys.path.append('/lcc42/examples/branchanal/')
from opdefsNW import *
from opfuncsNW import *
asminfile = open(sys.argv[1],'r')
asmdata = asminfile.read().expandtabs()
asmlines = asmdata.split('\n')
print len(asmlines), 'lines read'
jumpcount=0;aligncount=0
repcount=0
progsize=0
i=0
tln=0;pln=0;mln=0 #temp label numbers for $$,+ -
labeldefs={}
labelrefs=[]
def ireplace(old, new, text): #    Replace case insensitive
    index_l = text.lower().index(old.lower())
    return text[:index_l] + new + text[index_l + len(old):] 
def gotlabel(token):
	global tln,pln,mln
	label=token.split(':')[0]
	if label.startswith("$$"):
		label+=str(tln)
	elif label.startswith("+"): #check for temporary label
		label+=str(pln)
		pln+=1
	elif label.startswith("-"): #check for temporary label
		mln+=1
		label+=str(mln)
	else:
		tln+=1
	labeldefs[label]=progsize

def fmtlabel(label):
	global tln,pln,mln
	if label.startswith("$$"):
		label+=str(tln)
	elif label.startswith("+"): #check for temporary label
		label+=str(pln)
	elif label.startswith("-"): #check for temporary label
		label+=str(mln)
	return label

for line in asmlines:
	aline=line.split(";")[0] #get rid of any comment portion
	tokens=aline.split();
	if tokens:
		#print "%d %X %s" % (i+1,progsize,aline)
		if (not aline.startswith(' ')) or tokens[0].endswith(':'): #if it has a label
			gotlabel(tokens[0])
			tokens=tokens[1:]			#get rid of it
		if tokens:
			if tokens[0].lower() in jumpdefs:
				jumpcount+=1
				labelrefs.append([i,progsize,tokens[0].lower(),fmtlabel(tokens[1].split(",")[0])])
			elif tokens[0].lower()=="align":
				aligncount+=1
				labelrefs.append([i,progsize,tokens[0].lower(),tokens[1].split(",")[0],process(tokens,progsize)])
				#print "align ",i,progsize,tokens[0].lower(),tokens[1].split(",")[0],process(tokens,progsize)
			progsize+=process(tokens,progsize)

	i+=1;
print "pass 1 completed. ",len(labeldefs)," labels found. ",len(labelrefs)," jumps found."
#print labelrefs,labeldefs
adjb=0;adjl=0
repacount=0
i=0
for ref in labelrefs: # line index, location, branch op(or align), label referenced(or alignment)
	line=ref[0]; loc=ref[1]; brop=ref[2]; label=ref[3]
	adjamt=0
	if brop=="align":
		repacount+=1
		newsize=process([brop,label],loc)
		adjamt=newsize-ref[4]
		labelrefs[i][4]=newsize
		#print "*A*%4d %4x %s" %(line+1,loc,brop), label, ref[4],newsize, adjamt
	elif (loc+1)//256==labeldefs[label]//256:
		asmlines[line]=ireplace(brop,jumpreps[brop],asmlines[line])
		#print "%4d %4x %s %4x" %(line+1,loc,asmlines[line],labeldefs[label])
		repcount+=1
		adjamt=-1
	if not adjamt==0:
		for adjref in labelrefs: # line index, location, branch op, label referenced
			if adjref[0]>line: # for jumps further down the way
				if adjref[2]=="align":
					pass #print "A%d %x "%(adjref[0],adjref[1]),adjref[2],adjref[3],adjref[4]
				adjref[1]+=adjamt #get adjusted
				adjb+=1
		for k, labloc in labeldefs.iteritems():
			if labloc>loc:
				labeldefs[k] +=adjamt
				adjl+=1
	i+=1
#print labeldefs
#for k,labloc in labeldefs.iteritems():
#	print "%4x %s" % (labloc,k)
print repcount,"+",repacount," fixup cycles ",adjb," branch fixups ",adjl," label fixups"
asmoutfile=open(sys.argv[1].split('.')[0]+".basm",'w'); asmoutfile.truncate()
i=0
for line in asmlines:
	asmoutfile.write(line+'\n')
	i+=1
print i, 'lines written'
print "%d long jumps found, %d shortened" % (jumpcount,repcount)
##################opdefsNW follows
opsizes={
	'or':1,'ori':2,'xor':1,'xri':2,
	'and':1,'ani':2,
	'out':1,'inp':1,
	'sep':1,'ldi':2,'plo':1,'phi':1,'glo':1,'ghi':1,
	'sm':1,'smb':1,'smbi':2,
	'sd':1,'sdb':1,'sdi':2,'sdbi':2,
	'skp':1,'lskp':1,'lsnf':1,'lsdf':1,'lsz':1,'lsnz':1,
	'sex':1,'lda':1,'ret':1,'nop':1,'dec':1,'stxd':1,
	'shr':1,'shrc':1,'shl':1,'shlc':1,
	'str':1,'ldn':1,
	'add':1,'adi':2,'adc':1,'adci':2,'str':1,'smi':2,'inc':1,
	'dis':1,'sav':1,
	'bz':2,'bnz':2,'br':2,'b3':2,'bn3':2,'bnf':2,'bdf':2,
        'cpy2': 4, 'cpy1': 2, 'cpy4': 8,
        'zext': 3,'sext': 9,'zext4':4,'sext4':11,
        'negi2':9,'negi4':32,
        'alu2':12,'alu2i':8,
        'alu4':22,'alu4i':16,
        'ldad': 6,
        'lda2': -8,
        'shl2i': -6,'shri2i': -8,'shru2i': -6,
        'shl4':12,'shri4':14,
        'shl4i': -12,'shri4i': -14,
        'st1':-10,'st2':-13,'str1':2,
        'ld2':-12,'ld1':-10,
        'ldn1':2,'ldn2':4,
        'jzu2':8,
        'jnzu2':8,'jnzu1':4,'sjnzu2':6,'sjnzu1':3, #the sj codes are short branch variants
        'jeqi2':18,'jcu2':13,'jneu2': 18,
        'jci2': 20,'jci4': 28,'jcu4': 28,
        'jcu2i':9,
        'jni2i':17,'jnu2i':9,
        'jneu2i': 12,
        'jneu4':39,
        'jequ2i':12,'jci2i':17,
        'jcf4':69,
        'ld2z':4,
        'ldi4':12,'st4':-19,'ld4':-16, 
        'incm': -1,'decm': -1,
        'popr': 5, 'pushr': 4,
        'popf': 5, 'popl': 4,
        'lbr': 3,'lbnz':3,'lbz':3,'lbnf':3,'lbdf':3,
        'equ': 0, 'db': -1,  'dw':2,'dd':4,
        'align':-1, #force alignment
        'cretn': 1,'ccall': 3,
         'seq': 1,   'req': 1,
        'listing': 0,   'include': -3,
        'release': -1,'reserve': -1,
        'jumpv':10,
        'relaxed':0, 'macexp': 0,
        'ldx':1,'ldxa':1,'irx':1,
        'org':-1}
        
jumpdefs={
	'lbr': 3,'lbnz':3,'lbz':3,'lbdf':3,'lbnf':3
	}
jumpreps={
	'lbr': 'br','lbnz':'bnz','lbz':'bz','lbdf':'bdf','lbnf':'bnf'	
	}

#######################opfuncsNW follows
from opdefsNW import *
def opsizer(opsize,tokens,currentpc):
    op=tokens[0].lower()
    operands=tokens[1].split(",")
    #print "op=",op," operands=",operands
    if op=='reserve':
        n=int(operands[0])
        if (n<9):
              return n
        else:
              return 10
    elif op=='release':
        n=int(operands[0])
        if (n",operands
        if len(tokens)>=3 and tokens[2]=='dup':
              return int(tokens[1]) #tokens[1] because dup is separated from the count by a space
        else:
	      #print "db length is ",len(operands)
              return len(operands)
    elif op=='include':
        if operands[0].lower().startswith('lcc1802prolo'):
            return 3
        else:
            if operands[0].startswith('lcc1802epilo'):
                epiloglocation=currentpc
            return 0
    elif op in ['ld2','ld1','lda2','st2','st1','ld4','st4']: #ops that can be offset or direct storage refs
    	 #print operands
         if operands[1].lower()=="'o'": #offset/index
            return opsize*-1 #returns the whole value
         else:
            return (opsize*-1)-2 #direct op is 2 smaller
    elif op in ['shl2i','shri2i','shlu2i','shri2i','shru2i','shl4i','shri4i']:
         return int(operands[1])*-1*opsize
    elif op in ['incm','decm']:
         return int(operands[1])*-1*opsize
    elif op=='align':
        boundary=int(operands[0])
        if (currentpc%boundary)==0:
            return 0
        else:
            return boundary-(currentpc%boundary)
    elif op=='org':
    	print "I Hope this ORG is not harmful! ",operands[0]
    	return 0
    else:
        print '**************opsizer oops',tokens,opsize
        x = raw_input("Press Enter to continue")
       
            
        
    return 999
    
def process(tokens,currentpc):
    global opdefs
    try:
        #print 'processing ',tokens,"**",tokens[0],"**",opsizes[tokens[0]],currentpc
        opsize=opsizes[tokens[0].lower()]
    except:
        print 'process oops', tokens
        opsize=0
        print tokens[0],opsizes
        x = raw_input("Press Enter to continue")

    if opsize>=0:
        thisopsize=opsize
    else:
        thisopsize=opsizer(opsize,tokens,currentpc)
    #print tokens[0],opsize,thisopsize
    return thisopsize

Advertisements

Huh – The Awkwardness of Aligns – Branch Optimization III

I redid the branch optimization code to eliminate recursion. The new version does a single pass over the program to locate all the labels and long branches, then starts back down the list of branches looking at their location vs their target. When it finds an unnecessary long branch it shortens it in the program text and runs over all the branches and labels further down the program subtracting one from their location. This gets rid of the recursion but does a LOT of looping through the lists – in one modest sized C program it does 170 fixup scans making 18000 branch fixups and 24000 label fixups. The code is easier to understand though. One major problem is that it doesn’t work! Because I don’t look at the actual code when i’m doing the fixups – just the list ob branches and labels – I’m not processing align statements which will have a different effect on the program counter when they get moved. The only thing I can think of to do is to treat the aligns like jumps – include them in the jump list and, when I come to one, give it the “length” required to do the alignment. I’m frankly not clear as to whether that will work!


UPDATE:
It does work although I don’t completely trust it. If it fails though, the worst that will happen is that i will get assembly errors – all i’m doing is changing long to short branches and if i’m wrong it will be obvious. The 50 line comparulator test program ran successfully with 175 of 210 long branches shortened. I looked at some of the others and they were genuinely not convertible – the code is fairly long and a switch statement generates a lot of branches. If It was important I could manipulate the C source to reduce branch spans – maybe more function calls.

The other interesting thing about this is that if this were routine I could get rid of at least some of the “align”s and use long branches knowing they would probably get optimized out.

The current version of the code is called branchalignear.py.

#18-02-10 linear two pass scan - no recursion
#18-02-13 trying to include aligns with jumps.
import sys
sys.path.append('/lcc42/examples/branchanal/')
from opdefsNW import *
from opfuncsNW import *
asminfile = open(sys.argv[1],'r')
asmdata = asminfile.read().expandtabs()
asmlines = asmdata.split('\n')
print len(asmlines), 'lines read'
jumpcount=0;aligncount=0
repcount=0
progsize=0
i=0
tln=0;pln=0;mln=0 #temp label numbers for $$,+ -
labeldefs={}
labelrefs=[]
def ireplace(old, new, text): #    Replace case insensitive
    index_l = text.lower().index(old.lower())
    return text[:index_l] + new + text[index_l + len(old):] 
def gotlabel(token):
	global tln,pln,mln
	label=token.split(':')[0]
	if label.startswith("$$"):
		label+=str(tln)
	elif label.startswith("+"): #check for temporary label
		label+=str(pln)
		pln+=1
	elif label.startswith("-"): #check for temporary label
		mln+=1
		label+=str(mln)
	else:
		tln+=1
	labeldefs[label]=progsize

def fmtlabel(label):
	global tln,pln,mln
	if label.startswith("$$"):
		label+=str(tln)
	elif label.startswith("+"): #check for temporary label
		label+=str(pln)
	elif label.startswith("-"): #check for temporary label
		label+=str(mln)
	return label

for line in asmlines:
	aline=line.split(";")[0] #get rid of any comment portion
	tokens=aline.split();
	if tokens:
		#print "%d %X %s" % (i+1,progsize,aline)
		if (not aline.startswith(' ')) or tokens[0].endswith(':'): #if it has a label
			gotlabel(tokens[0])
			tokens=tokens[1:]			#get rid of it
		if tokens:
			if tokens[0].lower() in jumpdefs:
				jumpcount+=1
				labelrefs.append([i,progsize,tokens[0].lower(),fmtlabel(tokens[1].split(",")[0])])
			elif tokens[0].lower()=="align":
				aligncount+=1
				labelrefs.append([i,progsize,tokens[0].lower(),tokens[1].split(",")[0],process(tokens,progsize)])
				#print "align ",i,progsize,tokens[0].lower(),tokens[1].split(",")[0],process(tokens,progsize)
			progsize+=process(tokens,progsize)

	i+=1;
print "pass 1 completed. ",len(labeldefs)," labels found. ",len(labelrefs)," jumps found."
#print labelrefs,labeldefs
adjb=0;adjl=0
repacount=0
i=0
for ref in labelrefs: # line index, location, branch op(or align), label referenced(or alignment)
	line=ref[0]; loc=ref[1]; brop=ref[2]; label=ref[3]
	adjamt=0
	if brop=="align":
		repacount+=1
		newsize=process([brop,label],loc)
		adjamt=newsize-ref[4]
		labelrefs[i][4]=newsize
		#print "*A*%4d %4x %s" %(line+1,loc,brop), label, ref[4],newsize, adjamt
	elif (loc+1)//256==labeldefs[label]//256:
		asmlines[line]=ireplace(brop,jumpreps[brop],asmlines[line])
		#print "%4d %4x %s %4x" %(line+1,loc,asmlines[line],labeldefs[label])
		repcount+=1
		adjamt=-1
	if not adjamt==0:
		for adjref in labelrefs: # line index, location, branch op, label referenced
			if adjref[0]>line: # for jumps further down the way
				if adjref[2]=="align":
					pass #print "A%d %x "%(adjref[0],adjref[1]),adjref[2],adjref[3],adjref[4]
				adjref[1]+=adjamt #get adjusted
				adjb+=1
		for k, labloc in labeldefs.iteritems():
			if labloc>loc:
				labeldefs[k] +=adjamt
				adjl+=1
	i+=1
#print labeldefs
#for k,labloc in labeldefs.iteritems():
#	print "%4x %s" % (labloc,k)
print repcount,"+",repacount," fixup cycles ",adjb," branch fixups ",adjl," label fixups"
asmoutfile=open(sys.argv[1].split('.')[0]+".basm",'w'); asmoutfile.truncate()
i=0
for line in asmlines:
	asmoutfile.write(line+'\n')
	i+=1
print i, 'lines written'
print "%d long jumps found, %d shortened" % (jumpcount,repcount)

Also for the record, when i run this of one of the selftest programs 00comparulator.c the 50 lines of C generate 6400 lines of assembly(it’s compilicated code and it uses the floating point library) including 210 long jumps of which 170 get optimized away. As noted above the 170 branch optimizations cause 20100 branch/align fixups and 25222 label fixups.

Branch Optimization II

18-02-08 branch analysis blink
I figured out my overall theory and tried it out successfully. The code runs through the expanded assembly a line at a time keeping track of the size of code generated so it always knows the program counter. When it encounters a label it stores it with its address. When it encounters a long branch it checks the target to see if it’s on the same page and if so it converts it to a shot branch. Sometimes the label hasn’t been defined yet so it calls itself recursively starting at the next line looking for that label. The insight, if there is one, is that because I’m always moving forward, shortening a branch will never screw up a decision i’ve already made – it can only move the target closer.

This works, at least for Blink. There were six long branches encountered including the branch to lcc1802init generated by the prolog. I don’t bother with the branch to lcc1802init but the other five were successfully converted to short branches.

Now I need to try a bigger program sample and clean up the code.

Sure – But Now What? Branch Optimization


The 1802 has two types of branch operations: short branches can only branch within a page(the top byte of the program counter stays the same); and long branches which can go anywhere in memory.

The long branches are slower and they disturb some video timing so programmers traditionally avoid them but the C compiler output is littered with them because I just have no control over where things will be in memory.

I had this idea a while ago that i could run over the code after the fact substituting short branches where they would work. It’s not quite as simple as figuring out where each branch is going because the act of shortening branches will move things. Anyway, I made a stab at processing the compiler output with python a while ago but it just got to seem too hard because the compiler generates the branches inside macros like “JNZU2” which generate multiple instructions including sometimes multiple branches. I went back at it yesterday using the .i output from the assembler which has all the macros expanded to ordinary instructions. As of this morning I can process the output of compiling “blink.c” which generates 1100 lines of assembler instructions (because it has the olduino library included) and my python program is assigning the correct locations to the labels.

Now I just have to remember the rest of my idea of how to convert the branches. Also, there are a number of tricky bits left such as the reuse of temporary labels like $$xx and + and -.

The code is way beyond awful but it’s not super-long so if i can figure out what i’m doing I’ll rewrite it completely.

It’s a bit interesting to note that in the 1100 lines resulting from blink there are only 40-ish branches but over 200 labels. Probably 20 or 30 of the labels are register equates or similar and a lot of the rest must be function or data labels that would never be ref’d in branches.

{'bnz': 14, 'lbr': 5, 'br': 8, 'b3': 1, 'bn3': 0, 'lbnz': 2, 
'lbz': 0, 'bnf': 12, 'bz': 6}

{'_setqOff': 213, '$$bitloop': 1208, '_olduinoincluder': 106, 
'_digitalWrite': 128, '$$cknext': 1287, '_call': 241, 
'$$ney0y': 628, 'SUBOVHD': 272, '_inp': 1349, '_delay': 45, 
'rwork': 177, '$$norestore': 861, '_modI2': 223, '_modI4': 1310, 
'R4': 0, 'R5': 0, 'R6': 0, 'R7': 0, 'R0': 0, 'R1': 0, 'R2': 0, 
'R3': 0, '_dubdabx': 1143, 'R8': 0, 'R9': 0, '$$setbitoff': 151, 
'$$_00000': 203, '_mulU2': 512, '_setqOn': 211, '$$mulrshft': 540, 
'$$upnxt': 1264, '_main': 6, '_mulu4': 870, 'retVal': 0, 
'memAddr': 0, 'L12': 176, '$$loop': 783, '$$mulrlp': 516, 
'_out': 1375, '_divu2': 549, '_divu4': 768, '$$done': 1297, 
'R14': 0, 'R15': 0, 'R12': 0, 'R13': 0, 'R10': 0, 'R11': 0, 
'+': 1392, 'Rt1': 0, 'Rt2': 0, '$$clrlp': 1174, '$$dnoadd': 1219, 
'$$_die': 206, '$$again': 581, 'L4': 13, '$$mulrdone': 548, '
_PIN4': 3, '_digitalRead': 168, 'RL8': 0, 'RL6': 0, 'RL0': 0, 
'_out4': 231, '_out5': 1344, 'RL10': 0, 'RL12': 0, '$$mulrnoadd': 534, '$$testexit': 618, '_oneMs': 272, '$$check_sub': 918, '$$dcklp': 1211, 
'L11': 74, '$$mloop': 887, '$$pos1': 1062, '$$pos2': 1101, 
'$$bvtable': 159, '_modU4': 1298, '_return': 257, 'SP': 0, 
'_modU2': 215, '$$cktop': 1189, '$$endlp': 865, '_putc': 1344, 
'RCALL': 0, '$$shift': 942, '$$dshlp': 1239, 'RPC': 0, 
'lcc1802init': 177, '_divI4': 1024, '_divI2': 651, '$$out': 642, '$$computequot': 603, '$$mslp': 274, 'L7': 92, '_mysetqOn': 4, 
'L5': 37, 'L3': 40, 'L1': 5, '$$outit': 156, 'L8': 70, 'L9': 73, 
'retAddr': 0, 'regArg1': 0, 'RRET': 0, 'Rp1p2': 0, 'regArg2': 0}

Saturday Morning With Logic Friday

The Olduino/X with hardware SPI and no AVR has a bunch of glue logic for I/O mapping and controlling the SPI clock. There are five 7400-series chips with a bunch of unused sections and logic sections used as inverters. I know I could simplify it myself but I had been looking for an opportunity to try Logic Friday and this seemed perfect. Logic Friday is a free tool for minimizing logic functions and mapping them to standard gates. It has a GUI interface for drawing gate diagrams and a menu of tools for minimizing and redrawing them.

I took a first crack at the logic that enables the SPI input shift register(MISR) onto the 1802 bus. This had three two-input NAND gates and the truth tables looked right. Logic Friday minimized the truth tables but didn’t do much to the gate diagram – still three 7400 gates.

I realized doing this that I no longer care what that gate does when the 1802 is in reset(/CLR low) so i dummied that input high and reran things. Logic Friday’s result had only one gate to my three. I’m sure I’d have figured that out myself but I have spent hours mooning over these logic changes so i appreciated the win.

I put in the whole of the logic and ran it through. The gate count actually went up from 12 to 13 but where mine was spread over five chips with four unused sections Logic Friday used only three chips and these are all standard where one of mine is a three-input NOR. Again, I would have puzzled this out eventually but this was clearly easier.

It’s also interesting to me that if i choose joint rather than independent optimization and let it use the 3 input NOR it still comes in at 3 packages but only 10 gates which has to mean less work somewhere. The other thing that occurs to me is that i might be able to smoke test this by leaving out of the mix the gates that are already on the board and would be tough to change.
18-02-03 LogicFriday7

Boyd Programmer’s Calculator II


I’m running out of time before we leave for the winter but I wanted to button the Boyd back up with some sort of code in the ROM. This program just accepts key presses as hex input, converts to decimal and puts it on the display. With another hour I would have shown the hex as i entered it but it wasn’t to be.

The code is simple but it’s using the standard library functions for math and converting integer to ascii for display. I was a bit worried I would run over the 1805’s 64 bytes of RAM with the stack and variables but not so far – I think the lowest the stack got was 10D5 meaning there’s a whole 17 bytes left when i’m down in the bowels of a conversion routince calling a division routine. I imagine i can improve that when i need to.

//boydmonseg - simple keyboard/display program using the boyd display in full segmented mode.
#include "olduino.h"
#include 
#define initleds(mode) 	asm(" req\n seq\n dec 2\n ldi " #mode "\n str 2\n out 7\n req\n")
#include "boyd.h" //definitions for the boyd calculator
#define nofloats
unsigned char boydscan();
void disp1(unsigned char d){//display a byte as two hex digits
	asm(" glo 12\n ani 0x0f\n" //prep bottom digit
		" dec 2\n str 2\n out 7\n"
		" glo 12\n shr\n shr\n shr\n shr\n" //prep top digit
		" dec 2\n str 2\n out 7\n"
		);

}

void dispval(unsigned char v){
	register unsigned int i;
	initleds(0b11010000); //LEDs in hex decode mode
	disp1(v);
	for (i=6;i!=0;i--) out(7,0);

}

unsigned int strlen(char *str)
{
	unsigned int slen = 0 ;
	while (*str != 0) {
      slen++ ;
      str++ ;
   }
   return slen;
}
char * itoa(int s, char *buffer){ //convert an integer to printable ascii in a buffer supplied by the caller
	unsigned int r,k,n;
	unsigned int flag=0;
	char * bptr; bptr=buffer;
	if (s0){
		for(r=0;k0||k==1){
			*bptr=('0'+r);bptr++;
			flag='y';
		}
		//n=n-r*k;
		k=k/10;
	}

	*bptr='\0';
	return buffer;
}

void dispstr(char * str){//display 8 or fewer characters on the boyd LEDs
	register unsigned int i,L;
	L=min(strlen((char *)str),8);//length to display
	initleds(0b11110000); //LEDs in no-decode mode
	if (L0;i--){ //blank trailing positions
			out(7,0);
		}
	}
	for (i=L;i&gt;0;i--){
		out(7,boydsegments[str[i-1]]);
	}
}

void main()
{
	unsigned int acc=0;
	unsigned char k,k2;
	char buf[8]="01234567";
	dispval(0x42);

	delay(100);
	dispstr("BOYDPROG");
	delay(2000);
	while(1){
		dispstr("_"); acc=0;
		k=boydscan();
		while(k&lt;16){
			acc=(acc&lt;<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;">&#65279;</span>&lt;4)+k;
			//stringit(buf,k);
			dispstr(itoa(acc,buf));
			k=boydscan();
		}
	}
}

#include &quot;olduino.c&quot; //for the delay routine
void boydinc(){
	asm(&quot; align 256\n&quot;);
	asm(&quot; include \&quot;boydscan.inc\&quot;\n&quot;);
}

I should mention that I'm using one of Jon Kenneke's adapters to fit a 2732 EPROM into the Calculator. Jon supplied it already made up and added a 2732 EPROM in the package.  It had the original calculator firmware so it fired up on the first try – thanks Jon!

It's much thicker than the original ROM but you can close everything up with a little work.  This is probably just obvious but I had to route the ribbon cable back over the battery box to make a single bend in it rather than having an S bend right on top of the EPROM.

Olduino/X – Kicking the AVR Habit

18-01-30 OlduinoX Fullsize Schematic.
Right from the beginning the 1802 Olduino has had an AVR co-processor that communicated with the host for loading and serial comm and generated the clock pulses for the hardware SPI shift registers.

With the narrow ROM and non-volatile RAM I can put the bootloader and bitbang serial into the 1802 and, with a few gates I can generate the SPI clock as well so the AVR is gone gone gone!
18-01-30 SPI CLOCK
I got the SPI clock circuit from Josh Bensadon on the 1802 list who also did the fast serial routines. The narrow ROM came from Herb Johnson also on the Cosmac list. Thanks guys!
IMG_0744
The first implementation is ugly with the 7390 decade counter and three input nor gate linked on a breadboard but i’m imagining they’ll fit pretty easily in the area where the AVR footprint and the icsp header are.

Holy H*ll – This is Awful

;L=max(strlen((char *)str),8);//length to display
	ld2 R12,'O',sp,(12+1) ;reg:INDIRP2(addr)
	Ccall _strlen;CALLU2(ar)*
	cpy2 R11,R15 ;LOADU2*(reg)
	jnU2I R11,8,lbdf,L38 ;LEU2 8 11 L38; LE is flipped test & subtraction
	ld2 R12,'O',sp,(12+1) ;reg:INDIRP2(addr)
	Ccall _strlen;CALLU2(ar)*
	cpy2 R11,R15 ;LOADU2*(reg)
	cpy2 R1,R11 ;LOADU2*(reg)
	lbr L39
L38:
	ldaD R1,8; reg:acon
L39:
	cpy2 R6,R1 ;LOADU2*(reg)

At some point I’ll have to deal with this – callling strlen() twice is an atrocity. It looks like I’m testing to see whether strlen is <8 then either call strlen again or load an 8.

The Old 0x111111 Trick

18-01-27 0x111111

To load code oto the 1802 Olduino I use a program running in the onboard avr that mimics an avr bootloader or programmer and talks to avrdude on the host – he same program used by the arduino ide. This has its origin in my fantasy that i would actually use the arduino toolset to program the 1802. This proved to be a fantasy but the avrdude decision has had benefits – just one more thing i don’t(usually) have to think about.

I am once again running into a problem where avrdude reports an invalid signature being returned by the loader program. Something like this happened years ago when avrdude started requesting the signature differently. I had corrected that in the arduino source but i must have dropped a version somewhere. Good opportunity to synchronize the arduino code base across my laptops and make sure i can reprogram if i need to in florida.


HaHa – i just noticed that the olduino board i grabbed was one of the first working versions – pre-spi, so of course its software is version 2.2 from way before that avrdude change. I’ll just leave that the heck alone! It’s a bit of a tribute to something or other though that the board still works in the much-updated framework.

Ugly But it Works – Segment Addressing on the Boyd Calculator

This is truly ugly but perseverance is important, or something..


I brought up 3 data lines from the CPU board on some flying leads and hacked them on to the display chip. I had to desolder one pin of the chip to isolate it from +V. The display can now be switched from hex to what they call CODEB(0-9 plus some extras) or undecoded. The mode depends on the control character you send with Q active. If you turn off the decoding each bit in the characters you send controls a segment. I can probably find a C header where someone has done the work but for now i just cobbled up enough characters to see my name in lights.

The bit mapping from the data to the LED segments is in the datasheet:
IMG_0721

;testing output to the boyd LED segments
	relaxed on
	req
	seq
	sex 0
	out 7
	db  0xff ; segment data, no decoding
	req

	out 7
	db  d
	out 7
	db y
	out 7
	db  o
	out 7
	db  b
	out 7
	db  L
	out 7
	db  L
	out 7
	db  one
	out 7
	db  b

	br  $
A	equ 0b01111110 ;A
b	equ 0b00011111 ;b
C	equ 0b01001011 ;C
d	equ 0b00111101 ;d
i	equ 0b00010000 ;i
L	equ 0b00001011 ;L
o	equ 0b00011101 ;o
y	equ 0b00110111 ;y
zero	equ 0b01111011 ;0
one	equ 0b00110000 ;1
two	equ 0b01101101 ;2
eight	equ 0b01111111 ;8
sp	equ 0

I guess that mapping of bits to segments is not universal. This page has a nice table of characters to segments that implies the segments are addressed from bit 7 to bit 0 as dec. point,a,b,c,d,e,f,g where the 7218 is doing dec point,a,b,c,e,g,f,d. At least the top 4 bits are the same so i could map the table by hand like his L is 0x0E 0b00001110 would be 0x0B which matches.

Also, here’s a nice page on the 7218. And here’s another page on the 7218 which links to code with an alphabet table that i can use directly.

I’ll embed the code directly in case that link rots:

/* Segment numbering

  _0x40_
|        |
0x02     0x20
|        |
  _0x04_
|        |
0x08     0x10
|        |
  _0x01_

*/
  
unsigned char segments[] = {
  /* Control chars */
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

  /* Symbols and numbers */
  0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x20, /*  !"#$%&' */
  0x00, 0x00, 0x00, 0x00, 0x11, 0x04, 0x00, 0x2c, /* ()*+,-./ */
  0x7b, 0x30, 0x6d, 0x75, 0x36, 0x57, 0x5f, 0x70, /* 01234567 */
  0x7f, 0x77, 0x41, 0x51, 0x00, 0x00, 0x00, 0x00, /* 89:;<=>? */

  /* Uppercase */
  0x6f, 0x7e, 0x1f, 0x4b, 0x3d, 0x4f, 0x4e, 0x5b, /* @ABCDEFG */
  0x3e, 0x10, 0x39, 0x5e, 0x0b, 0x58, 0x1c, 0x1d, /* HIJKLMNO */
  0x6e, 0x67, 0x0c, 0x53, 0x0f, 0x3b, 0x19, 0x23, /* PQRSTUVW */
  0x5c, 0x37, 0x69, 0x00, 0x16, 0x00, 0x00, 0x01, /* XYZ[\]^_ */

  /* Lowercase */
  0x06, 0x7e, 0x1f, 0x4b, 0x3d, 0x4f, 0x4e, 0x5b, /* `abcdefg */
  0x3e, 0x10, 0x39, 0x5e, 0x0b, 0x58, 0x1c, 0x1d, /* hijklmno */
  0x6e, 0x67, 0x0c, 0x53, 0x0f, 0x3b, 0x19, 0x23, /* pqrstuvw */
  0x5c, 0x37, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, /* xyz{|}~  */
};

I’m Not Proud of This


I’ve kind of borked the boyd calculator. I wanted to convert the display to segment addressed rather than decoded. This means supplying 8(or at least 7) data bits to the ICM7218BIJI chip. The additional four bits were originally tied to +V or ground(D4-7 on pins 10=+V,6=0V,5=0V, and 7=+V) to put the chip into CODEB mode where it can show 0:9, -,H,E,L,P and blank. I had switched pin 5 to +V to get hex decoding previously and, at the same time I had to reroute pin 6 although it stayed at 0V. With only two pins to re-butcher I figured i could dive right in. Unfortunately, although the display board uses nice big components they are soldered in and I couldn’t be completely sure of the routing. I left pins 7 and 10 tied together but tried to isolate them from +V and connected them to the 1802’s D4, pin 6 to the 1802’s D5, and pin 5 to the 1802’s D6. I connected with long wire wrap leads at the 2532 socket pins 14,15, and 16 respectively. I’m not too worried about the long data lines because the 1805 is tough and the clock is very slow.

Unfortunately I didn’t check to see that i had successfully isolated pin 10/7 from +V and it looks like there might be a trace under the 7218 connecting them. When I tried a test I got a flash or garbage on the LEDs and then nothing. I then checked for shorts and found that pin 10 was still live to +V. I tried another cut to isolate 10 from 7 and now the whole thing is unresponsive.

The only way forward I can see is to try to desolder the 7218 chip and see what’s underneath it. I don’t think this is irreparable but I need to sit in the corner and think about what i’ve done.

UPDATE: Ah, at least I understand why it’s dead. When I isolated pin 10 I was also cutting off +V to the 7218. Also, I desoldered pin 10 pretty successfully so I can do that if I have to.

The first group below shows the calculator software and the connections for hex mode. In the second I’ve jiggered it to direct segment drive and the display makes sense. The third group shows the further bodging to connect the data lines and the video shows the last gasps before the current silence. Not pretty.

 

 

SPI Clock Without the AVR

18-01-21 SPI Clock

This is my attempt to generate the 8 pulses I need for the SPI clock without using the AVR.  CLOCKis the CPU clock, OUT2 is a positive going pulse generated when N1 goes high and /MRD is low.  I got this from Josh Bensadon but i’m not sure I’ve got it right.

UPDATE: After talking to Josh I’m still not completely sure about this but i do know i need a shoter positive going pulse for OUT2.  Fortunately the pulse that loads the shift register is at the right time although negative going.  I only want to do this when i’m doing output but i have an unused nor gate in the 7427 so the schematic below should be closer to correct.  I have the 7427 and 74390 on their way from digikey so i should be able to mock this up tomorrow. The second image shows the CPU pulses during an OUT 6 operation.  The OUT2TPB signal is labelled S1 in the hand drawn picture to correspond to josh’s schematic at bottom.

18-01-23 SPI Clock

IMG_0683

18-01-23 josh's clock

Assuming all of this worked I would have the basis for a new version of the olduino that would leave out the AVR completely and possibly have SPI at two or three times the speed. Call it 1802 OlduinoX.

 

olduinoX

Geez – A poison Cookie!

18-01-15 poison cookies!

Interestingly I had an error in my code that was trashing the 1st byte of the cookie value before retrieving it and, in combination with other sloppy code, it was propagating that trashed value even after i fixed the code! If there was a cookie in the incoming data it would transfer it to the outgoing header and the only way to clear it would be to win the game which is impossible because the cookie is trashed!

I’ll have to compile a one-off version that will just clear the cookie.  Or I guess I could check that the value is sensible before i use it – that would be more useful.

 

HTTP 1.1 and Connection: Close

My servers have had what i call protocol problems with 0 length transmissions and connections not closing. I was reading HTTP Made Really Easy – A Practical Guide to Writing Clients and Servers and noting that my the http requests I receive these days do seem to contain the header “Connection: keep-alive”. I wonder if this is what keeps connections from closing gracefully. Apparently if i don’t want to support persistent connections i need to include “Connection: close” in my response – I’ll try that!

UPDATE: And Nope

I see exactly the same phenomenon

18-01-15 SFW NFG

The Boyd Programmer’s Calculator

boydprog
This is a mockup of a programmer’s calculator based on the Boyd printing calculator.

I’m pretty sure he had one of these made for his use while writing the original code.

The basic function is entry as either decimal or hex with immediate conversion. The Inst button shows the mnemonic for the hex code(in place of the decimal value), the up and down arrows scroll a list of mnemonics.

I’m going to actually program this and see if it’s useful. The LCD obviously would give a lot more capability.

It occurs to me that it would be good to be able to see the effect of an instruction as well as look up the code.

Olduino Serves Cookies!

The various web and telnet servers that i’ve written for the olduino all use the IP address to distinguish between clients. This falls apart when the clients are coming from behind a firewall because they all have the same IP. I figured this was what cookies were for but they always seemed a bit mysterious. I googled around though and found this. It’s really pretty simple. If you include in the outbound header stream a sequence like:
Set-Cookie: yummy_cookie=choco
Then each succeeding transaction from that client contains the value as
Cookie: yummy_cookie=choco

My initial thought was just to assign each new client an arbitrary short id number and use that instead of the IP address . As a simpler starting point though, I decided to put the secret number in the cookie directly and get rid of the whole table. That’s all you really need to play the game although i’ve given up the ability to track the number of turns.

To incorporate sending the cookie, I changed the hard coded HTML header in doughnuts.h.

	static unsigned char hdr1a[]="HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" //beginning of header
						"<html><body OnLoad=\"document.mf.G.focus();\">"
						"<span style=\"color:#0000A0\">\r\n"
						"<center><b>OLDUINO 1802 DOUGHNUTS SERVER 1.2</b>"
						"<br>(";

became

	static unsigned char hdr1aa[]="HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n"
						"Set-Cookie: OS=";
	static unsigned char secret[]="541";
	static unsigned char hdr1ab[]="\r\n\r\n" //finshes off the http header and begins the body.
						"<html><body OnLoad=\"document.mf.G.focus();\">"
						"<span style=\"color:#0000A0\">\r\n"
						"<center><b>OLDUINO 1802 COOKIES SERVER 0.0</b>"
						"<br>(";

and changed the code in bagelsinit() to include

	setsecret(); //have to set the secret here so it can be sent in the header
	sendconst(hdr1aa); sendconst(secret); sendconst(hdr1ab);

The block below shows a header returned with three cookies in it. OS=218 is the secret,BC=00; OC=01; are left over from my playing around the other day. I thought cookies were supposed to go away when you closed the browser but these seem to be hanging in there.

GET / HTTP/1.1
Host: 169.254.180.2
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: BC=00; OC=01; OS=218

In the previous versions of bagels/doughnuts I only had to read the first 16 characters of any response to parse it. To retrieve the secret value from this gobbledegook I had to read the whole block (500-700 characters depending on the browser) and then fumble though it looking for the cookie. The following getsecret(size) routine gets called with every input and passed the size of the block (which is in the global variable buf). All it’s doing is scanning the last 100 bytes looking for the text “OS=” and taking the next 3 characters to be the secret. I’m sure there’s a more sophisticated approach but …

void getsecret(unsigned int rsize){
	unsigned char* secretcookie;
	secretcookie=strstr(buf+rsize-64,(unsigned char*)"OS=");
	if (secretcookie){
		memcpy(secret,secretcookie+3,3);
	}else{
		printf("secret cookie not found - substituting 999\n");
		strcpy((char *)secret,"999");
	}
}

I haven’t decided yet whether to bung more stuff into cookies or to go back to the id/table idea. Part of my goal is to make a simple messaging system and the id might be more suitable for that. Obviously this is highly susceptible to spoofing so there’s that.


/*****************************************************************************
//  File Name    : cookies.c
//  Version      : 0
//  Description  : Bagels Number Guessing Game for Wiznet w5500
//  Author       : Bill Rowe
//  Target       : Olduino
//  Compiler     : LCC1802
//  Created		 : Mar 12, 2015
*****************************************************************************/
//15-03-13 created based on bagels3 with change to doughnuts
//15-03-16 fixed logic error in response analysis
//17-01-12 adapting to use cookies instead of a table of secrets
#define nofloats
#include <nstdlib.h> //for printf etc.
#include <cpu1802spd4port7.h> //defines processor type, speed, and host port
#include <olduino.h> //for digitalRead, digitalWrite, delay
#include <hspi2.h> //hardware spi header
//end of olduino/1802 specific declares
#include "w5500data.h"  //wiznet definitions
#include "w5500code.h"  //wiznet code definitions
#include "cookies.h"
#define MAX_BUF 750					//buffer size to allow cookies to be retrieved
unsigned char buf[MAX_BUF];			//memory buffer for incoming & outgoing data
unsigned char guess [4]={0};
unsigned int pages=0; games=0; sessions=0;
char pnbuf[8];
union IPaddr thisip={182}; //the ip that a form/request came from
/*
 * strstr.c --
 *
 *	Source code for the "strstr" library routine.
 *
 * Copyright (c) 1988-1993 The Regents of the University of California.
 * Copyright (c) 1994 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: strstr.c,v 1.1.1.3 2003/03/06 00:09:04 landonf Exp $
 */

/*
 *----------------------------------------------------------------------
 *
 * strstr --
 *
 *	Locate the first instance of a substring in a string.
 *
 * Results:
 *	If string contains substring, the return value is the
 *	location of the first matching instance of substring
 *	in string.  If string doesn't contain substring, the
 *	return value is 0.  Matching is done on an exact
 *	character-for-character basis with no wildcards or special
 *	characters.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

unsigned char *
strstr(unsigned char *string,unsigned char * substring)
{
    unsigned char *a, *b;

    /* First scan quickly through the two strings looking for a
     * single-character match.  When it's found, then compare the
     * rest of the substring.
     */

    b = substring;
    if (*b == 0) {
	return string;
    }
    for ( ; *string != 0; string += 1) {
	if (*string != *b) {
	    continue;
	}
	a = string;
	while (1) {
	    if (*b == 0) {
		return string;
	    }
	    if (*a++ != *b++) {
		break;
	    }
	}
	b = substring;
    }
    return 0;
}

unsigned int tbrnd(){//random numbers ala tiny basic R:=R*2345+6789
	static unsigned int R=1;
	R=R*58653+13849;//R=R*2345+6789;
	//printf("%d\n",R);
	return R;
}
void setsecret(){//set the global secret to a 3 digit random number
	int i=0,d1=0,d2,d3;
	while(d1==0){
		d1=tbrnd()%10;
	}
	d2=tbrnd()%10;
	while(d1==d2){
		d2=tbrnd()%10;
	}
	d3=tbrnd()%10;
	while(d3==d1||d3==d2){
		d3=tbrnd()%10;
	}
	secret[0]=d1+0x30;secret[1]=d2+0x30;secret[2]=d3+0x30;secret[3]=0;
	printf("secret=%s\n",secret);
}
void bagelsinit(){
	int sendrc;
	games++;
	pages++;
	setsecret(); //have to set the secret here so it can be sent in the header
	sendconst(hdr1aa); sendconst(secret); sendconst(hdr1ab);
	send0s(itoa(pages,pnbuf)); sendconst(hdr1b); 	// Now Send the HTTP Response first part
	sendconst(Inst1); 	// Now Send the instructions
	sendconst(Inst2); 	// Now Finish the instructions
	sendconst(gform); 	// Now Send the rest of the page
	sendconst(olduinolink);
	sendconst(trlr); 	// Now Send the trailer
	printf("IP: %d.%d.%d.%d,secret %s\n",
		thisip.c[0],thisip.c[1],thisip.c[2],thisip.c[3],secret);
}
void sendip(unsigned char * ip){
	char strbuf[4];
	send0s(itoa(ip[0],strbuf));
	sendlit(".");
	send0s(itoa(ip[1],strbuf));
	sendlit(".");
	send0s(itoa(ip[2],strbuf));
	sendlit(".");
	send0s(itoa(ip[3],strbuf));
}
void congratulate(){
	sendconst("Congrats!<p> ");
}
void sendrespform(int pico, int fermi){
	int sendrc,i;
	pages++;
	if (fermi==3){//if we're going to need a new secret
		setsecret();//set it now
	}
	sendconst(hdr1aa); sendconst(secret); sendconst(hdr1ab);
	send0s(itoa(pages,pnbuf)); sendconst(hdr1b); 	// Now Send the HTTP Response first part
	sendconst("Your Guess Was: "); 	// Now Send the response
	send0s((char *)guess);
	sendlit("  I say ");
	if (fermi==3){
		sendlit("YOU GOT IT!<P>");
		congratulate();
		sendconst(pform);
		sendconst(olduinolink);
		sendconst(trlr); 	// Now Send the rest of the page
	} else {//once we're here we're going to send the guess form
		if (pico<0){//flag for duplicate digits
			sendlit("<P>Oh, maybe I didn't tell you.  No two digits are the same.<P>");
		} else if ((pico+fermi)==0){
			sendlit("Cookies!");
		} else{
			for(i=0;i<pico;i++) sendlit("Pico ");
			for(i=0;i<fermi;i++) sendlit("Fermi ");
		}
		sendlit("<P>");
		sendconst(gform); 	// Now Send the guess form
		sendconst(Reminder);//send the reminder
		sendconst(Inst2);
		sendconst(olduinolink);
		sendconst(trlr); 	// Now Send the rest of the page
	}
}
unsigned int cpyguess(unsigned char *to,unsigned char *from){//copy the guess until space or length limit
	unsigned int len=0;
	while((len<3) && *from!=' ' &&*from!='+' &&*from!='%'){
		*to++=*from++;
		len++;
	}
	*to=0;
	return len;
}
void getsecret(unsigned int rsize){
	unsigned char* secretcookie;
	printf("buffer at %x, rsize %d, buf[rsize]=%cx\n",buf,rsize,buf[rsize]);
	secretcookie=strstr(buf+rsize-64,(unsigned char*)"OS=");
	if (secretcookie){
		printf("\nHow about this? **%s**\n",secretcookie);
		memcpy(secret,secretcookie+3,3);
	}else{
		printf("secret cookie not found - substituting 999\n");
		strcpy((char *)secret,"999");
	}
	printf("done retrieving secret\n");
}
void bagelsturn(){
	int i, /*ipslot,*/ pico=0, fermi=0;
	if(strcmp(secret,"999")){//as long as we have an actual secret
		//ipturns[thisipslot]++; //count the turn
		printf("Guess: %d.%d.%d.%d, secret %s\n",
			thisip.c[0],thisip.c[1],thisip.c[2],thisip.c[3],secret);
		cpyguess(guess,buf+8);
		for (i=0;i<3;i++){
			if (guess[i]==secret[i]){
				fermi++;
			}
		}
		if (guess[0]==secret[1]||guess[0]==secret[2]){pico++;}
		if (guess[1]==secret[0]||guess[1]==secret[2]){pico++;}
		if (guess[2]==secret[0]||guess[2]==secret[1]){pico++;}
		if (guess[0]==guess[1]||guess[0]==guess[2]||guess[1]==guess[2]||strlen((char *)guess)!=3){pico=-1;}
		sendrespform(pico, fermi);
	}else{
		printf("Uninitialized secret for IP %d.%d.%d.%d\n",
			thisip.c[0],thisip.c[1],thisip.c[2],thisip.c[3]);
		bagelsinit();
	}
}

void handlesession(){	//handle a session once it's established
	unsigned int rsize,strncmpval; unsigned int rxrd;
	unsigned int tries=10;
	rsize=wizGetCtl16(SnRX_RSR); //get the size of the received data
	rxrd = wizGetCtl16(SnRX_RD);
	while(rsize==0 && tries-->0){
		delay(20);
		printf("re-size ");
		rsize=wizGetCtl16(SnRX_RSR); //retry size of the received data
	}
	rxrd = wizGetCtl16(SnRX_RD);
	printf("**rxsz=%d\n",rsize);
	printf("**rxrd=%d\n",rxrd);
	if (rsize>0){
		thisip.l=getip();
		if (recv0(buf,min(748,rsize))>0){ //get enough characters to distinguish the request
			buf[min(748,rsize)]=0;//ensure it's terminated
			printf("%s\n",buf);
  			if (strncmp((char *)buf,"POST /",6)==0){
  				bagelsinit(); //initialize game, send the form
			}
			else if (strncmp((char *)buf,"GET /favicon",12)==0){
  				sendnak(); //no favicon here
			}
  			else if (strncmp((char *)buf,"GET /?G=",8)==0){
				getsecret(rsize);
				bagelsturn();	//give player his turn
			}
  			else if (strncmp((char *)buf,"GET /T",6)==0){
 				//bagelspeek(); //show the IP table
			}
  			else if (strncmp((char *)buf,"GET /",5)==0){
 				bagelsinit(0); //initialize game, send the form
			}
  			else{
				printf("\nmystery meat\n");
 				bagelsinit(0); //initialize game, send the form
			}
		}
	}
	printf("flushing %d\n",rsize);
  	if (rsize>0) flush(rsize);	//get rid of the received data
	rxrd = wizGetCtl16(SnRX_RD);
	printf("after recv, rxrd=%d\n",rxrd);
	wizCmd(CR_DISCON);// Disconnect the connection- we're done here
	printf("done\n>\n");
	sessions++;
}

void main(void){
	unsigned char socket0status;
    unsigned char ip_addr[] = {169,254,180,2};//{192,168,1,182};//{10,0,0,180};//
    unsigned int SFWs=0; //count of times we've seen SOCK_FIN_WAIT
    printf("Olduino 1802 Cookies Server 0.0\n");
	delay(500);
    wiz_Init(ip_addr); //initialize the wiznet chip
	while(1){  // Loop forever
		socket0status=wizGetCtl8(SnSR); //socket 0 status
		switch (socket0status){
			case SOCK_CLOSED: //initial condition
				SFWs=0;
				socket0_init();	//initialize socket 0
				break;
			case SOCK_ESTABLISHED: //someone wants to talk to the server
				SFWs=0;
				handlesession();
				printf("%d sessions, %d games, %d pages\n",sessions,games,pages);
				break;
			//following are cases where we have to reset and reopen the socket
			case SOCK_FIN_WAIT:
				printf("SOCK_FIN_WAIT:");
				if (++SFWs>2){
					printf(" lost patience, closing\n");
					wizCmd(CR_CLOSE);
				}else{
					printf(" ignoring\n");
				}
				break;
			case SOCK_CLOSING: case SOCK_TIME_WAIT:
			case SOCK_CLOSE_WAIT: case SOCK_LAST_ACK:
				SFWs=0;
				wizCmd(CR_CLOSE);
				break;
		}
		tbrnd(); //cycle the generator
		delay(100);
	}
}
#include <olduino.c>
#include <nstdlib.c>
#include <hspi2.c>
#include "w5500code.c"

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

Processor Abuse

My C compiler can generate a LOT of code. I don’t really mind as long as it works but sometimes I look under the covers for fun.

The 8 bit version of the transpose from pixie format to LCD format cycles through 8 C statements like the following.
B[1]=B[0] = (A[0] & 128) | (A[1] & 128)>>1 | (A[2] & 128)>>2 | (A[3] & 128)>>3 |
(A[4] & 128)>>4 | (A[5] & 128)>>5 | (A[6] & 128)>>6 | (A[7] )>>7;

That generates over 600 lines of listing – maybe 500 instructions. That and its 7 companions gets iterated 16X8 times for each screen conversion so about 1000X500 instructions. There’s a bunch of other code around it but that alone is enough to account for the time for each pass. The thing is, even though the 1802 is handicapped for multi-bit shifting and memory addressing, the statement has only four one byte inputs(A[0]=A[1] etc.) and four one byte outputs. Even I should be able to code it up in a manageable stretch of assembly code.

Assume for the sake of argument that A[0]=A[1] is in the high byte of R8, A[2]=A[3] is in the low byte, A[4] and A[6] are in R9, then the operation on A[0] and A[2] would be something like:

  GHI R8    ;A[0]
  ANI 128
  STR R2    ;work area on the stack
  SHR
  OR
  STR R2    ;work area now has (A[0] & 128) | (A[0] & 128)>>1

  GLO R8     ;A[2]
  ANI 128
  SHR
  SHR
  OR
  STR R2
  SHR 
  OR
  STR R2    ;work area now has (A[0] & 128) | (A[1] & 128)>>1 | (A[2] & 128)>>2 | (A[3] & 128)>>3

with two more blocks for A[4] and A[6] – maybe a total of about 40 instructions. Huh, that’s not much more than a 10 to 1 improvement and it would be a pain to write.

     445/     36F : (MACRO)             	ldaD R11,128; reg:acon
     445/     36F : F8 80                       ldi     (128)&255
     445/     371 : AB                          plo     R11
     445/     372 : F8 00                       ldi     (128)>>8; was/256
     445/     374 : BB                          phi     R11
     446/     375 : (MACRO)             	ld1 R10,'O',sp,(66)
     446/     375 : =>TRUE                      if      ('O'='O')
     446/     375 :  (MACRO-2)                          ldAX    memAddr,SP,(66)
     446/     375 : 82                          glo     SP
     446/     376 : FC 42                       adi     ((66))#256
     446/     378 : AE                          plo     MEMADDR
     446/     379 : 92                          ghi     SP
     446/     37A : 7C 00                       adci    ((66))>>8; was/256
     446/     37C : BE                          phi     MEMADDR
     446/     37D : =>FALSE                     else
     446/     37D :                                     ldAD    memAddr,SP
     446/     37D : [446]                       endif
     446/     37D : 0E                          ldn     memAddr
     446/     37E : AA                          plo     R10
     447/     37F : (MACRO)             	zExt R10 ;CVUI2: widen unsigned char to signed int (zero extend)
     447/     37F : F8 00                       ldi     0
     447/     381 : BA                          phi     R10
     448/     382 : (MACRO)             	alu2 R10,R10,R11,and,and
     448/     382 : 22                          dec     sp ;make a work ares
     448/     383 : 8B                          glo     R11
     448/     384 : 52                          str     sp
     448/     385 : 8A                          glo     R10
     448/     386 : F2                          AND             ;calculate the low order byte
     448/     387 : AA                          plo     R10
     448/     388 : 9B                          ghi     R11
     448/     389 : 52                          str     sp
     448/     38A : 9A                          ghi     R10
 AS V1.42 Beta [Bld 123] - source file pixie8main.asm - page 9 - 1/1/2018 14:47:05

     448/     38B : F2                          AND             ;calculate the high byte
     448/     38C : BA                          phi     R10
     448/     38D : 12                          inc     sp      ;release the work area
     449/     38E : (MACRO)             	ld1 R9,'O',sp,(67)
     449/     38E : =>TRUE                      if      ('O'='O')
     449/     38E :  (MACRO-2)                          ldAX    memAddr,SP,(67)
     449/     38E : 82                          glo     SP
     449/     38F : FC 43                       adi     ((67))#256
     449/     391 : AE                          plo     MEMADDR
     449/     392 : 92                          ghi     SP
     449/     393 : 7C 00                       adci    ((67))>>8; was/256
     449/     395 : BE                          phi     MEMADDR
     449/     396 : =>FALSE                     else
     449/     396 :                                     ldAD    memAddr,SP
     449/     396 : [449]                       endif
     449/     396 : 0E                          ldn     memAddr
     449/     397 : A9                          plo     R9
     450/     398 : (MACRO)             	zExt R9 ;CVUI2: widen unsigned char to signed int (zero extend)
     450/     398 : F8 00                       ldi     0
     450/     39A : B9                          phi     R9
     451/     39B : (MACRO)             	alu2 R9,R9,R11,and,and
     451/     39B : 22                          dec     sp ;make a work ares
     451/     39C : 8B                          glo     R11
     451/     39D : 52                          str     sp
     451/     39E : 89                          glo     R9
     451/     39F : F2                          AND             ;calculate the low order byte
     451/     3A0 : A9                          plo     R9
     451/     3A1 : 9B                          ghi     R11
     451/     3A2 : 52                          str     sp
     451/     3A3 : 99                          ghi     R9
     451/     3A4 : F2                          AND             ;calculate the high byte
     451/     3A5 : B9                          phi     R9
     451/     3A6 : 12                          inc     sp      ;release the work area
     452/     3A7 : (MACRO)             	shrI2I R9,1
     452/     3A7 :                             rept    1
     452/     3A7 :                             shrI2   R9
     452/     3A7 :                             endm
     452/     3A7 :  (MACRO-2)                  shrI2   R9
     452/     3A7 : 99                          ghi     R9
     452/     3A8 : FE                          shl             ;set DF to the sign
     452/     3A9 : 99                          ghi     R9     ;get the top byte back
     452/     3AA : 76                          shrc            ;shift one bit extending the sign
     452/     3AB : B9                          phi     R9
     452/     3AC : 89                          glo     R9
     452/     3AD : 76                          shrc
     452/     3AE : A9                          plo     R9
     453/     3AF : (MACRO)             	alu2 R10,R10,R9,or,or
     453/     3AF : 22                          dec     sp ;make a work ares
     453/     3B0 : 89                          glo     R9
     453/     3B1 : 52                          str     sp
     453/     3B2 : 8A                          glo     R10
     453/     3B3 : F1                          OR             ;calculate the low order byte
     453/     3B4 : AA                          plo     R10
     453/     3B5 : 99                          ghi     R9
     453/     3B6 : 52                          str     sp
     453/     3B7 : 9A                          ghi     R10
     453/     3B8 : F1                          OR             ;calculate the high byte
     453/     3B9 : BA                          phi     R10
     453/     3BA : 12                          inc     sp      ;release the work area
     454/     3BB : (MACRO)             	ld1 R9,'O',sp,(68)
 AS V1.42 Beta [Bld 123] - source file pixie8main.asm - page 10 - 1/1/2018 14:47:05

     454/     3BB : =>TRUE                      if      ('O'='O')
     454/     3BB :  (MACRO-2)                          ldAX    memAddr,SP,(68)
     454/     3BB : 82                          glo     SP
     454/     3BC : FC 44                       adi     ((68))#256
     454/     3BE : AE                          plo     MEMADDR
     454/     3BF : 92                          ghi     SP
     454/     3C0 : 7C 00                       adci    ((68))>>8; was/256
     454/     3C2 : BE                          phi     MEMADDR
     454/     3C3 : =>FALSE                     else
     454/     3C3 :                                     ldAD    memAddr,SP
     454/     3C3 : [454]                       endif
     454/     3C3 : 0E                          ldn     memAddr
     454/     3C4 : A9                          plo     R9
     455/     3C5 : (MACRO)             	zExt R9 ;CVUI2: widen unsigned char to signed int (zero extend)
     455/     3C5 : F8 00                       ldi     0
     455/     3C7 : B9                          phi     R9
     456/     3C8 : (MACRO)             	alu2 R9,R9,R11,and,and
     456/     3C8 : 22                          dec     sp ;make a work ares
     456/     3C9 : 8B                          glo     R11
     456/     3CA : 52                          str     sp
     456/     3CB : 89                          glo     R9
     456/     3CC : F2                          AND             ;calculate the low order byte
     456/     3CD : A9                          plo     R9
     456/     3CE : 9B                          ghi     R11
     456/     3CF : 52                          str     sp
     456/     3D0 : 99                          ghi     R9
     456/     3D1 : F2                          AND             ;calculate the high byte
     456/     3D2 : B9                          phi     R9
     456/     3D3 : 12                          inc     sp      ;release the work area
     457/     3D4 : (MACRO)             	shrI2I R9,2
     457/     3D4 :                             rept    2
     457/     3D4 :                             shrI2   R9
     457/     3D4 :                             endm
     457/     3D4 :  (MACRO-2)                  shrI2   R9
     457/     3D4 : 99                          ghi     R9
     457/     3D5 : FE                          shl             ;set DF to the sign
     457/     3D6 : 99                          ghi     R9     ;get the top byte back
     457/     3D7 : 76                          shrc            ;shift one bit extending the sign
     457/     3D8 : B9                          phi     R9
     457/     3D9 : 89                          glo     R9
     457/     3DA : 76                          shrc
     457/     3DB : A9                          plo     R9
     457/     3DC :  (MACRO-2)                  shrI2   R9
     457/     3DC : 99                          ghi     R9
     457/     3DD : FE                          shl             ;set DF to the sign
     457/     3DE : 99                          ghi     R9     ;get the top byte back
     457/     3DF : 76                          shrc            ;shift one bit extending the sign
     457/     3E0 : B9                          phi     R9
     457/     3E1 : 89                          glo     R9
     457/     3E2 : 76                          shrc
     457/     3E3 : A9                          plo     R9
     458/     3E4 : (MACRO)             	alu2 R10,R10,R9,or,or
     458/     3E4 : 22                          dec     sp ;make a work ares
     458/     3E5 : 89                          glo     R9
     458/     3E6 : 52                          str     sp
     458/     3E7 : 8A                          glo     R10
     458/     3E8 : F1                          OR             ;calculate the low order byte
     458/     3E9 : AA                          plo     R10
     458/     3EA : 99                          ghi     R9
     458/     3EB : 52                          str     sp
 AS V1.42 Beta [Bld 123] - source file pixie8main.asm - page 11 - 1/1/2018 14:47:05

     458/     3EC : 9A                          ghi     R10
     458/     3ED : F1                          OR             ;calculate the high byte
     458/     3EE : BA                          phi     R10
     458/     3EF : 12                          inc     sp      ;release the work area
     459/     3F0 : (MACRO)             	ld1 R9,'O',sp,(69)
     459/     3F0 : =>TRUE                      if      ('O'='O')
     459/     3F0 :  (MACRO-2)                          ldAX    memAddr,SP,(69)
     459/     3F0 : 82                          glo     SP
     459/     3F1 : FC 45                       adi     ((69))#256
     459/     3F3 : AE                          plo     MEMADDR
     459/     3F4 : 92                          ghi     SP
     459/     3F5 : 7C 00                       adci    ((69))>>8; was/256
     459/     3F7 : BE                          phi     MEMADDR
     459/     3F8 : =>FALSE                     else
     459/     3F8 :                                     ldAD    memAddr,SP
     459/     3F8 : [459]                       endif
     459/     3F8 : 0E                          ldn     memAddr
     459/     3F9 : A9                          plo     R9
     460/     3FA : (MACRO)             	zExt R9 ;CVUI2: widen unsigned char to signed int (zero extend)
     460/     3FA : F8 00                       ldi     0
     460/     3FC : B9                          phi     R9
     461/     3FD : (MACRO)             	alu2 R9,R9,R11,and,and
     461/     3FD : 22                          dec     sp ;make a work ares
     461/     3FE : 8B                          glo     R11
     461/     3FF : 52                          str     sp
     461/     400 : 89                          glo     R9
     461/     401 : F2                          AND             ;calculate the low order byte
     461/     402 : A9                          plo     R9
     461/     403 : 9B                          ghi     R11
     461/     404 : 52                          str     sp
     461/     405 : 99                          ghi     R9
     461/     406 : F2                          AND             ;calculate the high byte
     461/     407 : B9                          phi     R9
     461/     408 : 12                          inc     sp      ;release the work area
     462/     409 : (MACRO)             	shrI2I R9,3
     462/     409 :                             rept    3
     462/     409 :                             shrI2   R9
     462/     409 :                             endm
     462/     409 :  (MACRO-2)                  shrI2   R9
     462/     409 : 99                          ghi     R9
     462/     40A : FE                          shl             ;set DF to the sign
     462/     40B : 99                          ghi     R9     ;get the top byte back
     462/     40C : 76                          shrc            ;shift one bit extending the sign
     462/     40D : B9                          phi     R9
     462/     40E : 89                          glo     R9
     462/     40F : 76                          shrc
     462/     410 : A9                          plo     R9
     462/     411 :  (MACRO-2)                  shrI2   R9
     462/     411 : 99                          ghi     R9
     462/     412 : FE                          shl             ;set DF to the sign
     462/     413 : 99                          ghi     R9     ;get the top byte back
     462/     414 : 76                          shrc            ;shift one bit extending the sign
     462/     415 : B9                          phi     R9
     462/     416 : 89                          glo     R9
     462/     417 : 76                          shrc
     462/     418 : A9                          plo     R9
     462/     419 :  (MACRO-2)                  shrI2   R9
     462/     419 : 99                          ghi     R9
     462/     41A : FE                          shl             ;set DF to the sign
     462/     41B : 99                          ghi     R9     ;get the top byte back
 AS V1.42 Beta [Bld 123] - source file pixie8main.asm - page 12 - 1/1/2018 14:47:05

     462/     41C : 76                          shrc            ;shift one bit extending the sign
     462/     41D : B9                          phi     R9
     462/     41E : 89                          glo     R9
     462/     41F : 76                          shrc
     462/     420 : A9                          plo     R9
     463/     421 : (MACRO)             	alu2 R10,R10,R9,or,or
     463/     421 : 22                          dec     sp ;make a work ares
     463/     422 : 89                          glo     R9
     463/     423 : 52                          str     sp
     463/     424 : 8A                          glo     R10
     463/     425 : F1                          OR             ;calculate the low order byte
     463/     426 : AA                          plo     R10
     463/     427 : 99                          ghi     R9
     463/     428 : 52                          str     sp
     463/     429 : 9A                          ghi     R10
     463/     42A : F1                          OR             ;calculate the high byte
     463/     42B : BA                          phi     R10
     463/     42C : 12                          inc     sp      ;release the work area
     464/     42D : (MACRO)             	ld1 R9,'O',sp,(70)
     464/     42D : =>TRUE                      if      ('O'='O')
     464/     42D :  (MACRO-2)                          ldAX    memAddr,SP,(70)
     464/     42D : 82                          glo     SP
     464/     42E : FC 46                       adi     ((70))#256
     464/     430 : AE                          plo     MEMADDR
     464/     431 : 92                          ghi     SP
     464/     432 : 7C 00                       adci    ((70))>>8; was/256
     464/     434 : BE                          phi     MEMADDR
     464/     435 : =>FALSE                     else
     464/     435 :                                     ldAD    memAddr,SP
     464/     435 : [464]                       endif
     464/     435 : 0E                          ldn     memAddr
     464/     436 : A9                          plo     R9
     465/     437 : (MACRO)             	zExt R9 ;CVUI2: widen unsigned char to signed int (zero extend)
     465/     437 : F8 00                       ldi     0
     465/     439 : B9                          phi     R9
     466/     43A : (MACRO)             	alu2 R9,R9,R11,and,and
     466/     43A : 22                          dec     sp ;make a work ares
     466/     43B : 8B                          glo     R11
     466/     43C : 52                          str     sp
     466/     43D : 89                          glo     R9
     466/     43E : F2                          AND             ;calculate the low order byte
     466/     43F : A9                          plo     R9
     466/     440 : 9B                          ghi     R11
     466/     441 : 52                          str     sp
     466/     442 : 99                          ghi     R9
     466/     443 : F2                          AND             ;calculate the high byte
     466/     444 : B9                          phi     R9
     466/     445 : 12                          inc     sp      ;release the work area
     467/     446 : (MACRO)             	shrI2I R9,4
     467/     446 :                             rept    4
     467/     446 :                             shrI2   R9
     467/     446 :                             endm
     467/     446 :  (MACRO-2)                  shrI2   R9
     467/     446 : 99                          ghi     R9
     467/     447 : FE                          shl             ;set DF to the sign
     467/     448 : 99                          ghi     R9     ;get the top byte back
     467/     449 : 76                          shrc            ;shift one bit extending the sign
     467/     44A : B9                          phi     R9
     467/     44B : 89                          glo     R9
     467/     44C : 76                          shrc
 AS V1.42 Beta [Bld 123] - source file pixie8main.asm - page 13 - 1/1/2018 14:47:05

     467/     44D : A9                          plo     R9
     467/     44E :  (MACRO-2)                  shrI2   R9
     467/     44E : 99                          ghi     R9
     467/     44F : FE                          shl             ;set DF to the sign
     467/     450 : 99                          ghi     R9     ;get the top byte back
     467/     451 : 76                          shrc            ;shift one bit extending the sign
     467/     452 : B9                          phi     R9
     467/     453 : 89                          glo     R9
     467/     454 : 76                          shrc
     467/     455 : A9                          plo     R9
     467/     456 :  (MACRO-2)                  shrI2   R9
     467/     456 : 99                          ghi     R9
     467/     457 : FE                          shl             ;set DF to the sign
     467/     458 : 99                          ghi     R9     ;get the top byte back
     467/     459 : 76                          shrc            ;shift one bit extending the sign
     467/     45A : B9                          phi     R9
     467/     45B : 89                          glo     R9
     467/     45C : 76                          shrc
     467/     45D : A9                          plo     R9
     467/     45E :  (MACRO-2)                  shrI2   R9
     467/     45E : 99                          ghi     R9
     467/     45F : FE                          shl             ;set DF to the sign
     467/     460 : 99                          ghi     R9     ;get the top byte back
     467/     461 : 76                          shrc            ;shift one bit extending the sign
     467/     462 : B9                          phi     R9
     467/     463 : 89                          glo     R9
     467/     464 : 76                          shrc
     467/     465 : A9                          plo     R9
     468/     466 : (MACRO)             	alu2 R10,R10,R9,or,or
     468/     466 : 22                          dec     sp ;make a work ares
     468/     467 : 89                          glo     R9
     468/     468 : 52                          str     sp
     468/     469 : 8A                          glo     R10
     468/     46A : F1                          OR             ;calculate the low order byte
     468/     46B : AA                          plo     R10
     468/     46C : 99                          ghi     R9
     468/     46D : 52                          str     sp
     468/     46E : 9A                          ghi     R10
     468/     46F : F1                          OR             ;calculate the high byte
     468/     470 : BA                          phi     R10
     468/     471 : 12                          inc     sp      ;release the work area
     469/     472 : (MACRO)             	ld1 R9,'O',sp,(71)
     469/     472 : =>TRUE                      if      ('O'='O')
     469/     472 :  (MACRO-2)                          ldAX    memAddr,SP,(71)
     469/     472 : 82                          glo     SP
     469/     473 : FC 47                       adi     ((71))#256
     469/     475 : AE                          plo     MEMADDR
     469/     476 : 92                          ghi     SP
     469/     477 : 7C 00                       adci    ((71))>>8; was/256
     469/     479 : BE                          phi     MEMADDR
     469/     47A : =>FALSE                     else
     469/     47A :                                     ldAD    memAddr,SP
     469/     47A : [469]                       endif
     469/     47A : 0E                          ldn     memAddr
     469/     47B : A9                          plo     R9
     470/     47C : (MACRO)             	zExt R9 ;CVUI2: widen unsigned char to signed int (zero extend)
     470/     47C : F8 00                       ldi     0
     470/     47E : B9                          phi     R9
     471/     47F : (MACRO)             	alu2 R9,R9,R11,and,and
     471/     47F : 22                          dec     sp ;make a work ares
 AS V1.42 Beta [Bld 123] - source file pixie8main.asm - page 14 - 1/1/2018 14:47:05

     471/     480 : 8B                          glo     R11
     471/     481 : 52                          str     sp
     471/     482 : 89                          glo     R9
     471/     483 : F2                          AND             ;calculate the low order byte
     471/     484 : A9                          plo     R9
     471/     485 : 9B                          ghi     R11
     471/     486 : 52                          str     sp
     471/     487 : 99                          ghi     R9
     471/     488 : F2                          AND             ;calculate the high byte
     471/     489 : B9                          phi     R9
     471/     48A : 12                          inc     sp      ;release the work area
     472/     48B : (MACRO)             	shrI2I R9,5
     472/     48B :                             rept    5
     472/     48B :                             shrI2   R9
     472/     48B :                             endm
     472/     48B :  (MACRO-2)                  shrI2   R9
     472/     48B : 99                          ghi     R9
     472/     48C : FE                          shl             ;set DF to the sign
     472/     48D : 99                          ghi     R9     ;get the top byte back
     472/     48E : 76                          shrc            ;shift one bit extending the sign
     472/     48F : B9                          phi     R9
     472/     490 : 89                          glo     R9
     472/     491 : 76                          shrc
     472/     492 : A9                          plo     R9
     472/     493 :  (MACRO-2)                  shrI2   R9
     472/     493 : 99                          ghi     R9
     472/     494 : FE                          shl             ;set DF to the sign
     472/     495 : 99                          ghi     R9     ;get the top byte back
     472/     496 : 76                          shrc            ;shift one bit extending the sign
     472/     497 : B9                          phi     R9
     472/     498 : 89                          glo     R9
     472/     499 : 76                          shrc
     472/     49A : A9                          plo     R9
     472/     49B :  (MACRO-2)                  shrI2   R9
     472/     49B : 99                          ghi     R9
     472/     49C : FE                          shl             ;set DF to the sign
     472/     49D : 99                          ghi     R9     ;get the top byte back
     472/     49E : 76                          shrc            ;shift one bit extending the sign
     472/     49F : B9                          phi     R9
     472/     4A0 : 89                          glo     R9
     472/     4A1 : 76                          shrc
     472/     4A2 : A9                          plo     R9
     472/     4A3 :  (MACRO-2)                  shrI2   R9
     472/     4A3 : 99                          ghi     R9
     472/     4A4 : FE                          shl             ;set DF to the sign
     472/     4A5 : 99                          ghi     R9     ;get the top byte back
     472/     4A6 : 76                          shrc            ;shift one bit extending the sign
     472/     4A7 : B9                          phi     R9
     472/     4A8 : 89                          glo     R9
     472/     4A9 : 76                          shrc
     472/     4AA : A9                          plo     R9
     472/     4AB :  (MACRO-2)                  shrI2   R9
     472/     4AB : 99                          ghi     R9
     472/     4AC : FE                          shl             ;set DF to the sign
     472/     4AD : 99                          ghi     R9     ;get the top byte back
     472/     4AE : 76                          shrc            ;shift one bit extending the sign
     472/     4AF : B9                          phi     R9
     472/     4B0 : 89                          glo     R9
     472/     4B1 : 76                          shrc
     472/     4B2 : A9                          plo     R9
 AS V1.42 Beta [Bld 123] - source file pixie8main.asm - page 15 - 1/1/2018 14:47:05

     473/     4B3 : (MACRO)             	alu2 R10,R10,R9,or,or
     473/     4B3 : 22                          dec     sp ;make a work ares
     473/     4B4 : 89                          glo     R9
     473/     4B5 : 52                          str     sp
     473/     4B6 : 8A                          glo     R10
     473/     4B7 : F1                          OR             ;calculate the low order byte
     473/     4B8 : AA                          plo     R10
     473/     4B9 : 99                          ghi     R9
     473/     4BA : 52                          str     sp
     473/     4BB : 9A                          ghi     R10
     473/     4BC : F1                          OR             ;calculate the high byte
     473/     4BD : BA                          phi     R10
     473/     4BE : 12                          inc     sp      ;release the work area
     474/     4BF : (MACRO)             	ld1 R9,'O',sp,(72)
     474/     4BF : =>TRUE                      if      ('O'='O')
     474/     4BF :  (MACRO-2)                          ldAX    memAddr,SP,(72)
     474/     4BF : 82                          glo     SP
     474/     4C0 : FC 48                       adi     ((72))#256
     474/     4C2 : AE                          plo     MEMADDR
     474/     4C3 : 92                          ghi     SP
     474/     4C4 : 7C 00                       adci    ((72))>>8; was/256
     474/     4C6 : BE                          phi     MEMADDR
     474/     4C7 : =>FALSE                     else
     474/     4C7 :                                     ldAD    memAddr,SP
     474/     4C7 : [474]                       endif
     474/     4C7 : 0E                          ldn     memAddr
     474/     4C8 : A9                          plo     R9
     475/     4C9 : (MACRO)             	zExt R9 ;CVUI2: widen unsigned char to signed int (zero extend)
     475/     4C9 : F8 00                       ldi     0
     475/     4CB : B9                          phi     R9
     476/     4CC : (MACRO)             	alu2 R11,R9,R11,and,and
     476/     4CC : 22                          dec     sp ;make a work ares
     476/     4CD : 8B                          glo     R11
     476/     4CE : 52                          str     sp
     476/     4CF : 89                          glo     R9
     476/     4D0 : F2                          AND             ;calculate the low order byte
     476/     4D1 : AB                          plo     R11
     476/     4D2 : 9B                          ghi     R11
     476/     4D3 : 52                          str     sp
     476/     4D4 : 99                          ghi     R9
     476/     4D5 : F2                          AND             ;calculate the high byte
     476/     4D6 : BB                          phi     R11
     476/     4D7 : 12                          inc     sp      ;release the work area
     477/     4D8 : (MACRO)             	shrI2I R11,6
     477/     4D8 :                             rept    6
     477/     4D8 :                             shrI2   R11
     477/     4D8 :                             endm
     477/     4D8 :  (MACRO-2)                  shrI2   R11
     477/     4D8 : 9B                          ghi     R11
     477/     4D9 : FE                          shl             ;set DF to the sign
     477/     4DA : 9B                          ghi     R11     ;get the top byte back
     477/     4DB : 76                          shrc            ;shift one bit extending the sign
     477/     4DC : BB                          phi     R11
     477/     4DD : 8B                          glo     R11
     477/     4DE : 76                          shrc
     477/     4DF : AB                          plo     R11
     477/     4E0 :  (MACRO-2)                  shrI2   R11
     477/     4E0 : 9B                          ghi     R11
     477/     4E1 : FE                          shl             ;set DF to the sign
     477/     4E2 : 9B                          ghi     R11     ;get the top byte back
 AS V1.42 Beta [Bld 123] - source file pixie8main.asm - page 16 - 1/1/2018 14:47:05

     477/     4E3 : 76                          shrc            ;shift one bit extending the sign
     477/     4E4 : BB                          phi     R11
     477/     4E5 : 8B                          glo     R11
     477/     4E6 : 76                          shrc
     477/     4E7 : AB                          plo     R11
     477/     4E8 :  (MACRO-2)                  shrI2   R11
     477/     4E8 : 9B                          ghi     R11
     477/     4E9 : FE                          shl             ;set DF to the sign
     477/     4EA : 9B                          ghi     R11     ;get the top byte back
     477/     4EB : 76                          shrc            ;shift one bit extending the sign
     477/     4EC : BB                          phi     R11
     477/     4ED : 8B                          glo     R11
     477/     4EE : 76                          shrc
     477/     4EF : AB                          plo     R11
     477/     4F0 :  (MACRO-2)                  shrI2   R11
     477/     4F0 : 9B                          ghi     R11
     477/     4F1 : FE                          shl             ;set DF to the sign
     477/     4F2 : 9B                          ghi     R11     ;get the top byte back
     477/     4F3 : 76                          shrc            ;shift one bit extending the sign
     477/     4F4 : BB                          phi     R11
     477/     4F5 : 8B                          glo     R11
     477/     4F6 : 76                          shrc
     477/     4F7 : AB                          plo     R11
     477/     4F8 :  (MACRO-2)                  shrI2   R11
     477/     4F8 : 9B                          ghi     R11
     477/     4F9 : FE                          shl             ;set DF to the sign
     477/     4FA : 9B                          ghi     R11     ;get the top byte back
     477/     4FB : 76                          shrc            ;shift one bit extending the sign
     477/     4FC : BB                          phi     R11
     477/     4FD : 8B                          glo     R11
     477/     4FE : 76                          shrc
     477/     4FF : AB                          plo     R11
     477/     500 :  (MACRO-2)                  shrI2   R11
     477/     500 : 9B                          ghi     R11
     477/     501 : FE                          shl             ;set DF to the sign
     477/     502 : 9B                          ghi     R11     ;get the top byte back
     477/     503 : 76                          shrc            ;shift one bit extending the sign
     477/     504 : BB                          phi     R11
     477/     505 : 8B                          glo     R11
     477/     506 : 76                          shrc
     477/     507 : AB                          plo     R11
     478/     508 : (MACRO)             	alu2 R11,R10,R11,or,or
     478/     508 : 22                          dec     sp ;make a work ares
     478/     509 : 8B                          glo     R11
     478/     50A : 52                          str     sp
     478/     50B : 8A                          glo     R10
     478/     50C : F1                          OR             ;calculate the low order byte
     478/     50D : AB                          plo     R11
     478/     50E : 9B                          ghi     R11
     478/     50F : 52                          str     sp
     478/     510 : 9A                          ghi     R10
     478/     511 : F1                          OR             ;calculate the high byte
     478/     512 : BB                          phi     R11
     478/     513 : 12                          inc     sp      ;release the work area
     479/     514 : (MACRO)             	ld1 R10,'O',sp,(73)
     479/     514 : =>TRUE                      if      ('O'='O')
     479/     514 :  (MACRO-2)                          ldAX    memAddr,SP,(73)
     479/     514 : 82                          glo     SP
     479/     515 : FC 49                       adi     ((73))#256
     479/     517 : AE                          plo     MEMADDR
 AS V1.42 Beta [Bld 123] - source file pixie8main.asm - page 17 - 1/1/2018 14:47:05

     479/     518 : 92                          ghi     SP
     479/     519 : 7C 00                       adci    ((73))>>8; was/256
     479/     51B : BE                          phi     MEMADDR
     479/     51C : =>FALSE                     else
     479/     51C :                                     ldAD    memAddr,SP
     479/     51C : [479]                       endif
     479/     51C : 0E                          ldn     memAddr
     479/     51D : AA                          plo     R10
     480/     51E : (MACRO)             	zExt R10 ;CVUI2: widen unsigned char to signed int (zero extend)
     480/     51E : F8 00                       ldi     0
     480/     520 : BA                          phi     R10
     481/     521 : (MACRO)             	shrI2I R10,7
     481/     521 :                             rept    7
     481/     521 :                             shrI2   R10
     481/     521 :                             endm
     481/     521 :  (MACRO-2)                  shrI2   R10
     481/     521 : 9A                          ghi     R10
     481/     522 : FE                          shl             ;set DF to the sign
     481/     523 : 9A                          ghi     R10     ;get the top byte back
     481/     524 : 76                          shrc            ;shift one bit extending the sign
     481/     525 : BA                          phi     R10
     481/     526 : 8A                          glo     R10
     481/     527 : 76                          shrc
     481/     528 : AA                          plo     R10
     481/     529 :  (MACRO-2)                  shrI2   R10
     481/     529 : 9A                          ghi     R10
     481/     52A : FE                          shl             ;set DF to the sign
     481/     52B : 9A                          ghi     R10     ;get the top byte back
     481/     52C : 76                          shrc            ;shift one bit extending the sign
     481/     52D : BA                          phi     R10
     481/     52E : 8A                          glo     R10
     481/     52F : 76                          shrc
     481/     530 : AA                          plo     R10
     481/     531 :  (MACRO-2)                  shrI2   R10
     481/     531 : 9A                          ghi     R10
     481/     532 : FE                          shl             ;set DF to the sign
     481/     533 : 9A                          ghi     R10     ;get the top byte back
     481/     534 : 76                          shrc            ;shift one bit extending the sign
     481/     535 : BA                          phi     R10
     481/     536 : 8A                          glo     R10
     481/     537 : 76                          shrc
     481/     538 : AA                          plo     R10
     481/     539 :  (MACRO-2)                  shrI2   R10
     481/     539 : 9A                          ghi     R10
     481/     53A : FE                          shl             ;set DF to the sign
     481/     53B : 9A                          ghi     R10     ;get the top byte back
     481/     53C : 76                          shrc            ;shift one bit extending the sign
     481/     53D : BA                          phi     R10
     481/     53E : 8A                          glo     R10
     481/     53F : 76                          shrc
     481/     540 : AA                          plo     R10
     481/     541 :  (MACRO-2)                  shrI2   R10
     481/     541 : 9A                          ghi     R10
     481/     542 : FE                          shl             ;set DF to the sign
     481/     543 : 9A                          ghi     R10     ;get the top byte back
     481/     544 : 76                          shrc            ;shift one bit extending the sign
     481/     545 : BA                          phi     R10
     481/     546 : 8A                          glo     R10
     481/     547 : 76                          shrc
     481/     548 : AA                          plo     R10
 AS V1.42 Beta [Bld 123] - source file pixie8main.asm - page 18 - 1/1/2018 14:47:05

     481/     549 :  (MACRO-2)                  shrI2   R10
     481/     549 : 9A                          ghi     R10
     481/     54A : FE                          shl             ;set DF to the sign
     481/     54B : 9A                          ghi     R10     ;get the top byte back
     481/     54C : 76                          shrc            ;shift one bit extending the sign
     481/     54D : BA                          phi     R10
     481/     54E : 8A                          glo     R10
     481/     54F : 76                          shrc
     481/     550 : AA                          plo     R10
     481/     551 :  (MACRO-2)                  shrI2   R10
     481/     551 : 9A                          ghi     R10
     481/     552 : FE                          shl             ;set DF to the sign
     481/     553 : 9A                          ghi     R10     ;get the top byte back
     481/     554 : 76                          shrc            ;shift one bit extending the sign
     481/     555 : BA                          phi     R10
     481/     556 : 8A                          glo     R10
     481/     557 : 76                          shrc
     481/     558 : AA                          plo     R10
     482/     559 : (MACRO)             	alu2 R11,R11,R10,or,or
     482/     559 : 22                          dec     sp ;make a work ares
     482/     55A : 8A                          glo     R10
     482/     55B : 52                          str     sp
     482/     55C : 8B                          glo     R11
     482/     55D : F1                          OR             ;calculate the low order byte
     482/     55E : AB                          plo     R11
     482/     55F : 9A                          ghi     R10
     482/     560 : 52                          str     sp
     482/     561 : 9B                          ghi     R11
     482/     562 : F1                          OR             ;calculate the high byte
     482/     563 : BB                          phi     R11
     482/     564 : 12                          inc     sp      ;release the work area
     483/     565 : (MACRO)             	ldA2 R10,'O',sp,(50); reg:addr
     483/     565 :                     ;Oct 13 (50) added to ldAD call
     483/     565 : =>TRUE                      if      ('O'='O')
     483/     565 :  (MACRO-2)                          ldAX    R10,SP,(50)
     483/     565 : 82                          glo     SP
     483/     566 : FC 32                       adi     ((50))#256
     483/     568 : AA                          plo     R10
     483/     569 : 92                          ghi     SP
     483/     56A : 7C 00                       adci    ((50))>>8; was/256
     483/     56C : BA                          phi     R10
     483/     56D : =>FALSE                     else
     483/     56D :                                     ldAD    R10,SP+(50)
     483/     56D : [483]                       endif
     484/     56D : (MACRO)             	str1 R11,R10; ASGNU1(indaddr,reg)		DH
     484/     56D : 8B                          glo     R11
     484/     56E : 5A                          str     R10
     485/     56F : (MACRO)             	ldA2 R10,'O',sp,(51); reg:addr
     485/     56F :                     ;Oct 13 (51) added to ldAD call
     485/     56F : =>TRUE                      if      ('O'='O')
     485/     56F :  (MACRO-2)                          ldAX    R10,SP,(51)
     485/     56F : 82                          glo     SP
     485/     570 : FC 33                       adi     ((51))#256
     485/     572 : AA                          plo     R10
     485/     573 : 92                          ghi     SP
     485/     574 : 7C 00                       adci    ((51))>>8; was/256
     485/     576 : BA                          phi     R10
     485/     577 : =>FALSE                     else
     485/     577 :                                     ldAD    R10,SP+(51)
     485/     577 : [485]                       endif
 AS V1.42 Beta [Bld 123] - source file pixie8main.asm - page 19 - 1/1/2018 14:47:05

     486/     577 : (MACRO)             	str1 R11,R10; ASGNU1(indaddr,reg)		DH
     486/     577 : 8B                          glo     R11
     486/     578 : 5A                          str     R10
     487/     579 :                     ;asm(" macexp off\n");

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

Converting a bitmap for the OLED

IMG_0552
I don’t know why this seemed so hard but it did. Converting a .bmp file to display on the OLED was as simple as loading it into the LCD Assistant program and saving it as a header file with vertical orientation. Presumably the horizontal orientation would give you something suitable for the pixie-type display.
Below is the original image(in jpg form) and the display of LCD Assistant before saving it. The code is the same adaptation of the nokia lcd code. I tried the animation sequence but, not surprisingly, the control instructions would have to change.

/*  oledmain.c
    Display an image on an OLED display using hardware SPI shift register
    Dec 14 2017 - adapting for OLED display/SSD1306 controller
    Dec 16 2017 - recovered from olduino post(!)
    Dec 31 Displaying 128X64 Santa with Merry Christmas text(whew)
*/
#include <olduino.h>
#include <nstdlib.h>
#include <cpu1802spd4port7.h>
#include <hspi2.h>
#include <hspi2Lcd.h>
//from lcd assistant conversion of santa3-128X64.bmp in vertical mode
unsigned char spibytes[]={
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0xF0, 0x90, 0x88, 0x80, 0x50, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xE0, 0xC0, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xB8, 0xF8, 0xF8, 0xFF, 0x0C, 0x04, 0x82, 0x83, 0x81, 0xC0,
0xC0, 0xC0, 0xC0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0xE3, 0xFB, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0x08, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCC, 0xEC, 0xFE, 0xFC, 0xFC,
0x87, 0x84, 0x80, 0x82, 0x83, 0x83, 0x81, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xE0, 0xE0, 0xE0, 0xC0, 0xC0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x80, 0x40, 0x60, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x7F, 0x3F, 0x3F, 0x7F,
0x7F, 0x3F, 0xFF, 0xAC, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0C, 0xE4, 0x36, 0x17,
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x7F, 0x6B, 0x0F, 0x08,
0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x88, 0xC8, 0x2C, 0x3E, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0xFF, 0xDF, 0xFF, 0xB1, 0x00, 0x08, 0x18, 0x10, 0x10, 0x10, 0x18,
0x08, 0x08, 0x04, 0x04, 0x02, 0x33, 0x19, 0xF9, 0xF9, 0xF1, 0xE3, 0xE7, 0xFE, 0xFC, 0xFC, 0xFC,
0xEC, 0xE6, 0xE6, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xF7, 0xFF, 0xF9, 0xF9,
0xF9, 0xFB, 0xFB, 0xFF, 0x07, 0x03, 0x03, 0x07, 0x0F, 0x0F, 0x0C, 0x0C, 0x1C, 0x1C, 0x1C, 0x00,
0x07, 0x03, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1C, 0x38, 0x60, 0xE0, 0xCF, 0xCF, 0xDF, 0xFF, 0xFF, 0x3F, 0x7F, 0x7F, 0x7F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x0F, 0x07, 0x03,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x07, 0x06, 0x06, 0x0C,
0x0C, 0x0D, 0x19, 0x1F, 0x3F, 0x33, 0x33, 0x63, 0x63, 0xC1, 0xC1, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xF0, 0xF0, 0xF0, 0xC0, 0x00, 0xC0, 0xF0, 0xF0, 0xF0, 0x00, 0x80, 0xC0, 0x40, 0x40, 0xC0,
0x80, 0x00, 0xC0, 0xC0, 0x40, 0x40, 0x00, 0xC0, 0xC0, 0x40, 0x40, 0xC0, 0xC0, 0x00, 0x00, 0x00,
0xC0, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0x30, 0x10, 0x10, 0x30, 0x20, 0x00, 0xF0, 0xF0, 0x80,
0x40, 0xC0, 0x80, 0x00, 0xC0, 0xC0, 0x40, 0x40, 0x00, 0xD0, 0xD0, 0x00, 0x80, 0xC0, 0x40, 0x40,
0xC0, 0x80, 0x40, 0xE0, 0xF0, 0x40, 0x00, 0xC0, 0xC0, 0x80, 0x40, 0xC0, 0xC0, 0x80, 0x40, 0xC0,
0x80, 0x00, 0x80, 0x40, 0x40, 0x40, 0xC0, 0x80, 0x00, 0x80, 0xC0, 0x40, 0x40, 0xC0, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1F, 0x1F, 0x00, 0x0F, 0x1C, 0x0F, 0x00, 0x1F, 0x1F, 0x00, 0x0F, 0x1F, 0x12, 0x12, 0x1B,
0x0B, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x40, 0x47, 0x7F, 0x38, 0x1F,
0x07, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x18, 0x10, 0x10, 0x18, 0x08, 0x00, 0x1F, 0x1F, 0x00,
0x00, 0x1F, 0x1F, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x00, 0x09, 0x1B, 0x13, 0x16,
0x1E, 0x0C, 0x00, 0x0F, 0x1F, 0x10, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x1F,
0x1F, 0x00, 0x0C, 0x1E, 0x13, 0x11, 0x1F, 0x1F, 0x00, 0x09, 0x1B, 0x13, 0x16, 0x1E, 0x0C, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
void animate(){
	unsigned int offset;
	for (offset=0;offset<128;offset++){
		gotoXY(0,1); //position to 1st line with ship in it
		digitalWrite(lcdcmd,LCD_D); //get out of command mode
		LcdWriteN(spibytes+128+offset,128-offset);
		if (offset>0){
			LcdWriteN(spibytes+128,offset);
		}
		gotoXY(0,2); //position to 2nd line with ship in it
		digitalWrite(lcdcmd,LCD_D); //get out of command mode
		LcdWriteN(spibytes+256+offset,128-offset);
		if (offset>0){
			LcdWriteN(spibytes+256,offset);
		}
		gotoXY(0,3); //position to 2nd line with ship in it
		digitalWrite(lcdcmd,LCD_D); //get out of command mode
		LcdWriteN(spibytes+384+offset,128-offset);
		if (offset>0){
			LcdWriteN(spibytes+384,offset);
		}
		delay(100);
	}
}

#define LcdSS 4
void oledInitialise(void)
{
    unsigned char initbytes[32]={ //cribbed from adafruit library
        0xAE,0xD5,0x80,0xA8,0x3F,0xD3,0x00,0x40,
        0x8D,0x14,0x20,0x00,0xA1,0xC8,0xDA,0x12,
        0x81,0xCF,0xD9,0xF1,0xDB,0x40,0xA4,0xA6,
        0x2E,0xAF,0x21,0x00,0x7F,0x22,0x00,0x07};
    unsigned int i;

    digitalWrite(lcdreset,LOW); //reset the LCD
    digitalWrite(lcdreset,HIGH); //release the reset
    digitalWrite(LcdSS,LOW);    //select the LCD
    for (i=0;i<32;i++){
        LcdWrite(LCD_C, initbytes[i]); //send initialization
    }

}
void oledSet(unsigned char what) //set the whole LCD to a bit pattern
{
  unsigned int index;
  for (index = 0; index <1024; index++)
  {
    LcdWrite(LCD_D, what);
  }
}
void main()
{
    printf("oledmain.c here\n");
    PIN4=0;
    digitalWrite(LcdSS,HIGH);
    setqon();
    digitalWrite(LcdSS,LOW);
    oledInitialise();
    setqoff();
    //oledSet(0xf0);
    digitalWrite(lcdcmd,LCD_D); //set data
    spiSendN(spibytes,128*8);
}
#include <hspi2.c>
#include <hspi2Lcd.c>
#include <olduino.c>
#include <nstdlib.c>

As an aside, as i was fumbling around trying to get something to work i tried another conversion program at https://sourceforge.net/projects/bmp2glcd/files/bmp2glcd/bmp2glcd_ver_1.02/bmp2glcd_v1-02-src.tar.gz/download

Mapping A Pixie Bitmap Onto the OLED

img_0355

For my first try I used a starship bitmap that i had already transformed for the oled but I want to try directly using code that’s meant to display on the 1861 pixie display. The issue is that the pixie display expects to receive a scanline as 8 bytes which it serializes to 64 bits for a scan line. The OLED expects each byte to represent 8 vertical bits so that for bytes A,B,C,D,E,F,G,H
The Pixie displays bits as
A0A1A2…A7B0B1…B7…..H7 on a single scanline
while the OLED lines them up as
A0B0C0D0...H0
A1.........H1
.
.
A7.........H7

to be the first 8 pixels of 8 separate lines.
The 1802 UNO runs code on the AVR to read the emulated 1802’s memory and transposes it for the OLED. The code is in C-ish C++ so i just cribbed it to run directly on the 1802 as follows:

void oledpaint(uint8_t l1, const uint8_t l2)
{
  uint8_t line, block, i, Bx;
  uint32_t B[8], A[8];
  int32_t m=1;
  int32_t n=1;
  uint32_t x,y,t;

  // explanation of the following code: the Pixie display takes a memory byte as a horizontal line of 8 pixels.
  // Alas, the OLED wants a vertical column of 8 pixels.
  // So you need to transpose a block of 8 RAM bytes into 8 vertical bars of 8 pixels, then send these to the OLED.
  // This is calculation intensive, especially when using readable code. So there's some tough code taken from Hacker's Delight.

  // NOTE: we copy ram into an 8 byte A buffer. This copy is because we use line doubling (a pixel is a 2*2 pixel block on the OLED).
  // NOTE 2: it would be simple to set up different memory modes of 128*64, 32*32 etc next to the hard-wired 64*64 mode.

  for (line=l1;line<(l1+l2);line++)        // 8 lines because we're using line doubling
  {
    for (block=0;block<8;block++)
    {
      for (i=0;i<4;i++)           // take just 4 bytes below current top byte, not 8 bytes: line doubling
      {
        A[i*2+1] = A[i*2] = starship[line*32 + block + (3-i)*(8)];  // line*32 - line doubling, would be 64 otherwise. (3-i): line doubling, would be (7-i) otherwise
      }

      // the code below is from Hacker's Delight, see
      // http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt
      // note this trickery depends on 32 bit variables, otherwise
      // you get garbled results.
       x = (A[0]<<24)   | (A[m]<<16)   | (A[2*m]<<8) | A[3*m];
       y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m];

       t = (x ^ (x >> 7)) & 0x00AA00AA;  x = x ^ t ^ (t << 7);
       t = (y ^ (y >> 7)) & 0x00AA00AA;  y = y ^ t ^ (t << 7);

       t = (x ^ (x >>14)) & 0x0000CCCC;  x = x ^ t ^ (t <<14);
       t = (y ^ (y >>14)) & 0x0000CCCC;  y = y ^ t ^ (t <<14);

       t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
       y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
       x = t;

       B[0]=x>>24;    B[n]=x>>16;    B[2*n]=x>>8;  B[3*n]=x;
       B[4*n]=y>>24;  B[5*n]=y>>16;  B[6*n]=y>>8;  B[7*n]=y;

      // ----- end of hacker's delight code ------------------
      // B now contains 8 bytes for the OLED

      for (i=0;i<8;i++)
      {
		  spiSend(B[i]);spiSend(B[i]);
      }
    }
  }
}

That routine does a LOT of 32 bit math. It generates almost 8K of 1802 code most of which is calls to 32 bit math routines. It takes almost 5 seconds to fill the screen which, at 4MHz is 1.25 MILLION instructions! On the UNO 1802 the AVR is a lot more powerful but Oscar still splits the updates into 4 to reduce jumpiness. I have visions of breaking up the update and running at 12MHz but it still needs to get sped up considerably.

There are a couple of easy wins – the variables m and n are constant 1’s which a good compiler would factor out but mine won’t. Likewise making B an 8 bit char made a big difference. A couple of other changes got it down to around 1.2 seconds at 4MHz but there it sits.

OLED Display on SPI

IMG_0345
This is definitely a work in progress but I always love to see the Cosmac Elf starship.

The display is SPI and addressed similarly to the Nokia cellphone displays which I have played with before. The dimensions are different (128X64 vs 84*96) bu the bytes are laid out the same way so i was able to use the guts of the code for the LCD to drive the oled.

I was inspired to do it by Al Williams and Oscar Vermeulen’s UNO1802 which emulates an 1802 on an arduino uno and mimics the pixie display on the OLED. Their code is actually quite a bit more spohisticated than mine because it translates on the fly from the pixie display which wants 64 consecutive bits for each scan line to the oled which wants 128 BYTES each of which covers 8 scan lines. I may try to duplicate this because it would let me use more pixie code but it would probably be slow and i had already done the transposition once for the Nokia LCD.

The gallery below shows the OLED hooked up to an arduino running an adafruit demo and progressively rewired onto the olduino with, eventually, a correct display.

The code block below contains the display program and the header file with the starship graphic. All of the SPI code and most of the LCD-specific stuff is reused except for the initialization which has different command bytes.

/*
	Display the COSMAC starship on an OLED display using hardware SPI shift register
	Dec 14 2017 - adapting for OLED display/SSD1306 controller
*/
#include <olduino.h>
#include <nstdlib.h>
#include <cpu1802spd4port7.h>
#include <hspi2.h>
#include <hspi2Lcd.h>
#include "oledship.h"
//#include "santaw2.h"
#define LcdSS 4
void oledInitialise(void)
{
  	unsigned char initbytes[32]={ //cribbed from adafruit library
		0xAE,0xD5,0x80,0xA8,0x3F,0xD3,0x00,0x40,
		0x8D,0x14,0x20,0x00,0xA1,0xC8,0xDA,0x12,
		0x81,0xCF,0xD9,0xF1,0xDB,0x40,0xA4,0xA6,
		0x2E,0xAF,0x21,0x00,0x7F,0x22,0x00,0x07};
	unsigned int i;

  	digitalWrite(lcdreset,LOW); //reset the LCD
  	digitalWrite(lcdreset,HIGH); //release the reset
  	digitalWrite(LcdSS,LOW);	//select the LCD
  	for (i=0;i<32;i++){
		LcdWrite(LCD_C, initbytes[i]); //send initialization
	}

}
void oledSet(unsigned char what) //set the whole LCD to a bit pattern
{
  unsigned int index;
  for (index = 0; index <1024; index++)
  {
    LcdWrite(LCD_D, what);
  }
}
void main()
{
 	printf("oledmain.c here\n");
 	PIN4=0;
 	digitalWrite(LcdSS,HIGH);
 	setqon();
 	digitalWrite(LcdSS,LOW);
	oledInitialise();
	setqoff();
	//oledSet(0xf0);
	digitalWrite(lcdcmd,LCD_D); //set data
	spiSendN(spibytes,128*8);
}
#include <hspi2.c>
#include <hspi2Lcd.c>
#include <olduino.c>
#include <nstdlib.c>

//oledship.h header file follows
//each byte fills 1 horizontal position in 8 display lines
static unsigned char spibytes[]={
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,

0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,

0,0,0,0,0,0,0,0,0,0,
0x00,0x1F,0x11,0x11,0x1B,0x00,0x1F,0x11,0x11,0x1F,0x00,0x17,0x15,0x15,0x1D,0x00,
0x1F,0x03,0x04,0x03,0x1F,0x00,0x1F,0x05,0x05,0x1F,0x00,0x1F,0x11,0x11,0x1B,0x00,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x80,0x80,0x80,0x80,0x80,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,

0,0,0,0,0,0,0,0,0,0,
0x00,0x1C,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x16,0x22,0x22,0x6A,0x6B,0x6B,0x6A,
0x22,0x22,0x16,0x34,0x54,0x94,0x94,0x94,0x94,0x34,0x54,0x88,0x00,0x00,0x00,0x03,
0x04,0x04,0x04,0x04,0x04,0xFC,0x00,0x00,0xFC,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0xC4,0x44,0x44,0x44,0x04,0xC4,0x04,0x04,0x04,0x04,0xC4,0x44,0x44,0x46,0x01,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,

0,0,0,0,0,0,0,0,0,0,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x04,0x0e,0x1F,0x11,0x20,0x24,0x24,0x24,0x25,0x25,0x25,0x15,0x11,0x11,0x11,
0x11,0x11,0x09,0x09,0x09,0x09,0x08,0x08,0x09,0x09,0x09,0x0f,0x00,0x00,0x00,0x00,
0x00,0x07,0x05,0x05,0x04,0x00,0x07,0x04,0x04,0x04,0x00,0x07,0x01,0x01,0x00,0x00,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,

0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,


0x0E,0x11,0x11,0x0E,0x00,0x1F,0x10,0x10,0x10,0x00,0x1f,0x11,0x11,0x0E,0x00, //O L D
0x0F,0x10,0x10,0x0F,0x00,0x00,0X1F,0x00,0X00,0x1F,0x04,0x08,0x1F,0x00, //U I N
0x1F,0x11,0x11,0x1F,0x00,0X00,0X00,0X00,0X00,0X00,0x1F,0x15,0x15,0x11,0x00,	//O sp E
0x1f,0x11,0x11,0x0E,0x00, 0x00,0x1F,0x00,0X00, 0x01,0x1F,0x01,0x00,	//D I T
0x00,0x1F,0x00,0X00,0x0E,0x11,0x11,0x0E,0x00,0x1F,0x04,0x08,0x1F,0x00, //I O N
0,0X17,0,0, //!
0,0,0,0,0,0,0,0,0,0, //10 empty bits
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,
};