Skip to content

Another Stab at Xmodem

May 5, 2017

17-05-05 xmodem test rig
Xmodem is a very simple and easy to understand protocol but i have spent a LOT of time diddling with it. I’m trying to combine it with some bit bang serial routines for the 1802 as a return to a serial bootloader.  I’m really just using xmodem for pacing and i’m not checking any error conditions. Testing it involved my usual “fantastic collection of stamps” approach with a python sender on windows, a couple of serial ports, and the logic analyzer.

After hours of flaky results it finally started working today when I overwrote low memory and had to switch back from the 1806 to the 1802.  Now it seems bulletproof! I’ll retry with the 1806 tomorrow with some confidence that i can make it work.

The reason for the custom python sender is so i could build up from working with very small blocks and visible control characters. I’m munging all the code together here so i can track where i was when it started working.

#include <olduino.h>
#include <nstdlib.h>
#include <cpu1802spd4port7.h>
int XR(unsigned char *);
void dump(unsigned char* data, unsigned int len){
	unsigned int i=0;
	printf("dumping %d bytes at %x",len,data);
	for(i=0;i<len;i++){
		if (0==(i%8)) printf("\n%x ",data);
		printf("%cx ",*data++);
	}
	printf("\n");
}
void main(){
	int ret;
	asm(" seq\n"); //make sure Q is high to start
	ret=XR((unsigned char *)0x7000);
	printf("XR returns %x\n",ret);
	dump((unsigned char *)0x7000,ret-0x7000-1);
	while(1);
}
void includeser(){
	asm(" include xrwjr2.asm");
}
#include <olduino.c>
#include <nstdlib.c>
**************xrwjr2,asm include file follows ***************
NAK:	EQU 0x15;'N'
SOH:	EQU 0x01;'S'
EOT:	EQU 0x04;'T'
ACK:	EQU 0x06;'K'
Rrcv:	EQU 8
Rsnd:	EQU 9
blksize:	EQU 128
trc:	MACRO
	dec 2
	str 2
	out 7
	ENDM
; XMODEM receiver based on xr.asm by Michael H Riley and serial routines by Josh Bensadon
; See bottom of file for full acknowledgements and links.
; On entry R12 points to memory where received data will go
; On exit R15 has the last byte written
	align 64
_XR:	   ldaD	   Rsnd,serout
	   ldaD    Rrcv,serin
	ldi '<' 	trc  	   ldi     NAK                 ; need to send NAK to start            sep     Rsnd filelp:    ;receive address is in R12, length goes in R11 ;begining of block read. returns to filelp or exits to filedn   				            sep     Rrcv               ; wait for next incoming character            smi     EOT              ; check for EOT            lbz     filedn           ; jump if so 	   sep     Rrcv               ; read block number            sep     Rrcv               ; read inverted block number            ldi     blksize             ; 128 bytes to receive            plo     r11 readlp:    sep     Rrcv               ; read data byte            str     r12                  ; store into output buffer            inc     r12                  ; point to next position            dec     r11                  ; decrement block count            glo     r11                  ; see if done            bnz     readlp              ; loop back if not ;end of block read            sep     Rrcv               ; read checksum byte            ldi     ACK                  ; send an ACK            sep     Rsnd            lbr     filelp              ; loop back for more filedn:                ldi     ACK                  ; acknowledge end of transmission            sep     Rsnd 	   glo	   R12			;copy last address to return register 	   plo     R15 	   ghi     R12 	   phi     R15 	ldi '>'
	trc
           cretn	           	; and return to caller

; *******************************************************************
; *** This software is copyright 2005 by Michael H Riley          ***
; *** You have permission to use, modify, copy, and distribute    ***
; *** this software so long as this copyright notice is retained. ***
; *** This software may not be used in commercial applications    ***
; *** without express written permission from the author.         ***
; *******************************************************************
;**********************************************************************
;bit-bang Serial routines adapted from Josh Bensadon's VELFbios-v3.1.asm
;Transmit Byte via Q connected to RS232 driver
;call via SCRT
;Byte to send in D
;Destroys r14
;----------------------------------------------------------------------
 	IFNDEF 	LCC1802CPUSPEED
LCC1802CPUSPEED EQU 4000000
 	ENDIF
bitdelay: MACRO baudrate,cpuspeed,baseline,xreg
	rept ((cpuspeed/(baudrate*8)-baseline))/3
	NOP
	endm
	rept (((cpuspeed/(baudrate*8)-baseline)#3))>=1
	sex xreg
	endm
	ENDM
__BAUDRATE EQU 	9600
	align 32
serout:			;entry from assembly with char in D
	phi R14		;save char in R14.1
	ldi 9		;9 bits to transmit (1 start + 8 data)
	plo r14
	ghi R14
	shl		;set start bit
	rshr		;DF=0

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

_putcser:
	ldad Rsnd,serout
	glo R12
	sep Rsnd
	cretn

_getcser:
	ldad Rrcv,serin
	sep Rrcv
	plo R15
	ldi 0
	phi R15
	cretn

************************SSX3.PY custom sender follows *****************
from __future__ import print_function
import sys
import logging
logging.basicConfig()
import serial
try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO
from time import sleep
import os
if len(sys.argv)>1:
    filename=sys.argv[1]
else:
    filename="xserial.c"
fileSize=os.path.getsize(filename)
print ("File Size is",fileSize)

def xmodem_send(serial, file):
	NAK='\x15'
	blocksize=128
	t=0
	#	t, anim ='|/-\\'
	while 1:
	    if serial.read(1) != NAK:
		t = t + 1
		print ('.')
		if t == 3 : return False
	    else:
		break

	p = 1
	s = file.read(blocksize)
	while s:
	    s = s + '\xFF'*(blocksize - len(s))
	    chk = 0
	    for c in s:
		chk+=ord(c)
		#print (c,ord(c),chk,chk%256)
	    while 1:
		serial.write('\x01')#('S') #SOH)
		print('S')
		serial.write(chr(p))
		print('n')
		serial.write(chr(255 - p))
		print('-')
		serial.write(s)
		print(s)
		serial.write(chr(chk%256))
		#serial.write('ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ')
		print('c')
		serial.flush()
		print ('checksum is ',format(chk%256, '02X'),end=' ')
		answer = serial.read(1)
		if len(answer)!=0:
			print('answer is ',format(ord(answer[0]), '02X'))
		else:
			print ("Timeout I guess")
		if  answer == '\x15': continue
		if  answer == '\x06': break
		return False
	    s = file.read(blocksize)
	    p = (p + 1)%256
	    print ('.')
	serial.write('\x04')#('T')
	serial.flush()
	sleep(.1)
	serial.write('TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT')
	serial.flush()

	return True

#Main program starts here - define the serial port, set RTS off, then open it
#open the file to be loaded
stream = open(filename,'rb')

port = serial.Serial(parity=serial.PARITY_NONE,
                     bytesize=serial.EIGHTBITS,
                     stopbits=serial.STOPBITS_ONE,timeout=10,xonxoff=0,
                     rtscts=0,dsrdtr=0,baudrate=9600)
port.rts=False
port.port='COM4'
port.open()
port.rts=True
sleep(0.001)
port.rts=False
port.flushInput()
#transfer the file
result=xmodem_send(port, stream)

stream.close()
port.close()

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

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

Advertisements

From → Uncategorized

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: