Skip to content

Plan B – an Xmodem-based Bootloader

November 18, 2015

I now have a decent programming workflow for the Olduino/Z. I can compile them in C on windows and use the bootloader to get them onto the olduino.

In the video, the right hand window is textpad editing a small program written in C. I compile it using SDCC and convert it to a binary file.
15-11-18 textpad

The left window is Putty, connected to the olduino/z over a usb-serial cable. I use the putty file transfer menu to start an xmodem file transfer then reset the olduino.

On reset, the bootloader tries to initiate an xmodem transfer. If there’s a transfer ready to go, it loads the binary at 0x8000 – the beginning of RAM and passes control to it. If there isn’t a transfer ready, it just jumps to the program already in RAM. If, while it’s waiting for the transfer, I hit the ennter key, it jumps into the monitor.

To get this working, I modified the monitor to jump into the bootloader first thing. I made a copy of the xmodem code and modified it to serve as the bootloader. I kept it in a separate file and it’s org’d at 0x1800 so it still easily fits in an 8K eeprom.

It all works well enough except for a couple of things:
-I have to explicitly start the xmodem transfer in Putty rather than having a texpad macro or menu item to do it.
-When I hit enter to go into the monitor it would be nice to be able to examine the regs as they were wgen the olduino was reset but at the moment they look screwy.

bootpatch:	;patch point for the bootloader
	call	SEL_RS232	;instruction replaced by patch 
	call 	bootload
	ret			;and return

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>;
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<;
;	Plan B Bootloader via XMODEM FILE TRANSFER
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>;
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<;
;----------------------------------------------------------------------------------------------------; XMODEM ROUTINES


;---------------------------------------------------------------------------------
;Bootloader Entry
;ENTRY:	TOP OF staCK HOLDS RETURN ADDRESS to get back to before main menu
;---------------------------------------------------------------------------------
bootload:
		call	bootwink	;blip the LED on bit 6
;---------------------------------------------------------------------------------
;XMUP - XMODEM UPLOAD (receive file from Terminal to IMSAI 8080)
;INPUT STARTING ADDRESS
;SEND 'C' OR NAK TO HOST TO START CRC/CS TRANSFER
;---------------------------------------------------------------------------------
		LD	HL,0x8000	;Load to beginning of RAM

	;HL = Address of data to send from the IMSAI 8080

		CALL	boot_INIT	;Starts the transfer & Receives first PACKET
					;Cancelled Transfers will cause a RET

boot_LP1	CALL	boot_RECV	;Receives the next packet @HL, Resends if NAK
					;Cancelled Transfers will cause a RET
		JR C,	boot_LP1	;Jump until EOT Received
		JP	bootPURGE


;---------------------------------------------------------------------------------
;START XMODEM RECEIVING and RECEIVE FIRST PACKET
;---------------------------------------------------------------------------------
boot_INIT	LD	E,3		;3 ATTEMPTS TO INITIATE XMODEM CRC TRANSFER
		LD	A,1		;EXPECTED SEQ NUMBER starts at 1
		LD	(XMSEQ),A
boot_CRC	CALL	bootPURGE
		LD	A,'C'		;Send C
		LD	(XMTYPE),A	;Save as XM Type (CRC or CS)
		CALL	Put_Char
		CALL	bootGET_1st	;Await a packet
		JR NC,	boot_TSEQ	;Jump if first packet received
		JP NZ,	boot_fail	;Cancel if there was a response that was not a header
		DEC	E		;Otherwise, if no response, retry a few times
		JR NZ,	boot_CRC

		JP	boot_nil	;Abort


;--------------------- XMODEM RECEIVE
;Entry:	XMR_TSEQ in the middle of the routine
;Pre:	C=1 (expected first block as received when negogiating CRC or Checksum)
;	HL=Memory to dump the file to
;Uses:	B to count the 128 bytes per block
;	C to track Block Number expected
;	DE as CRC (Within Loop) (D is destroyed when Getting Header)
;------------------------------------
boot_RECV	LD	A,ACK		;Send Ack to start Receiving next packet
		CALL	Put_Char
bootR_LP	CALL	bootGET_HDR
		JR NC,	boot_TSEQ
		PUSH	HL
		JR Z,	boot_NAK		;NACK IF TIMED OUT
		POP	HL
		CP	EOT
		JP NZ,	boot_fail	;CANCEL IF CAN RECEIVED (OR JUST NOT EOT)
		LD	A,ACK
		CALL	Put_Char
		JP	boot_DONE

boot_TSEQ	LD	C,A
		LD	A,(XMSEQ)
		CP	C		;CHECK IF THIS SEQ IS EXPECTED
		JR Z,	boot_SEQ_OK	;Jump if CORRECT SEQ
		DEC	A		;Else test if Previous SEQ
		LD	(XMSEQ),A
		CP	C
		JP NZ,	boot_fail	;CANCEL IF SEQUENCE ISN'T PREVIOUS BLOCK
		CALL	bootPURGE	;ELSE, PURGE AND SEND ACK (ASSUMING PREVIOUS ACK WAS NOT RECEIVED)
		JR	boot_ACK

boot_SEQ_OK	LD	B,128		;128 BYTES PER BLOCK
		LD	C,0		;Clear Checksum
		LD	DE,0x0000	;CLEAR CRC
		PUSH	HL		;Save HL where block is to go
boot_BLK_LP	CALL	TIMED1_GETCHAR
		JR C,	boot_NAK
		LD	(HL),A		;SAVE DATA BYTE
		CALL	CRC_UPDATE
		LD	A,(HL)		;Update checksum
		ADD	A,C
		LD	C,A
		INC	HL		;ADVANCE
		DEC	B
		JR NZ,	boot_BLK_LP
					;After 128 byte packet, verify error checking byte(s)
		LD	A,(XMTYPE)	;Determine if we are using CRC or Checksum
		CP	NAK		;If NAK, then use Checksum
		JR Z,	boot_CCS
		CALL	TIMED1_GETCHAR
		JR C,	boot_NAK
		CP	D
		JR NZ,	boot_NAK
		CALL	TIMED1_GETCHAR
		JR C,	boot_NAK
		CP	E
		JR NZ,	boot_NAK
		JR	boot_ACK

boot_CCS	CALL	TIMED1_GETCHAR
		JP C,	boot_NAK
		CP	C
		JR NZ,	boot_NAK

		;If we were transfering to a FILE, this is where we would write the
		;sector and reset HL to the same 128 byte sector buffer.
		;CALL	WRITE_SECTOR

boot_ACK	;LD	A,ACK		;The sending of the Ack is done by
		;CALL	Put_Char	;the calling routine, to allow writes to disk
		LD	A,(XMSEQ)
		INC	A		;Advance to next SEQ BLOCK
		LD	(XMSEQ),A
		POP	BC
		SCF			;Carry set when NOT last packet
		RET

boot_NAK	POP	HL		;Return HL to start of block
		CALL	bootPURGE
		LD	A,NAK
		CALL	Put_Char
		JR	bootR_LP


;--------------------- XMODEM - GET HEADER
;
;pre:	Nothing
;post:	Carry Set: A=0, (Zero set) if Timeout
;	Carry Set: A=CAN (Not Zero) if Cancel received
;	Carry Set: A=EOT (Not Zero) if End of Tranmission received
;	Carry Clear and A = B = Seq if Header found and is good
;------------------------------------------
bootGET_HDR	LD	A,1		;GET CHAR, 1 SECONDS TIMEOUT (EXPECT SOH)
		CALL	TIMED_GETCHAR
		RET C			;Return if Timed out
		CP	SOH		;TEST IF START OF HEADER
		JR Z,	boot_SEQ	;IF SOH RECEIVED, GET SEQ NEXT
		CP	EOT		;TEST IF END OF TRANSMISSION
		JR Z,	boot_ESC	;IF EOT RECEIVED, TERMINATE XMODEM
		CP	CAN		;TEST IF CANCEL
		JR NZ,	bootGET_HDR
boot_ESC	OR	A		;Clear Z flag (because A<>0)
		SCF
		RET
boot_SEQ	CALL	TIMED1_GETCHAR	;GET SEQ CHAR
		RET C			;Return if Timed out
		LD	B,A		;SAVE SEQ
		CALL	TIMED1_GETCHAR	;GET SEQ COMPLEMENT
		RET C			;Return if Timed out
		CPL
		CP	B		;TEST IF SEQ VALID
		JR NZ,	bootGET_HDR	;LOOP BACK AND TRY AGAIN IF HEADER INCORRECT (SYNC FRAME)
		RET

bootGET_1st	LD	A,1		;1st try to get a header -  (EXPECT SOH or cr
		CALL	TIMED_GETCHAR
		RET C			;Return if Timed out
		cp	0x0d		;see if we get a CR
		jp z,	bootmonitor	;this will start the monitor
		CP	SOH		;TEST IF START OF HEADER
		JR Z,	boot_SEQ1	;IF SOH RECEIVED, GET SEQ NEXT
		CP	EOT		;TEST IF END OF TRANSMISSION
		JR Z,	boot_ESC1	;IF EOT RECEIVED, TERMINATE XMODEM
		CP	CAN		;TEST IF CANCEL
		JR NZ,	bootGET_1st
boot_ESC1	OR	A		;Clear Z flag (because A<>0)
		SCF
		RET
boot_SEQ1	CALL	TIMED1_GETCHAR	;GET SEQ CHAR
		RET C			;Return if Timed out
		LD	B,A		;SAVE SEQ
		CALL	TIMED1_GETCHAR	;GET SEQ COMPLEMENT
		RET C			;Return if Timed out
		CPL
		CP	B		;TEST IF SEQ VALID
		JR NZ,	bootGET_1st	;LOOP BACK AND TRY AGAIN IF HEADER INCORRECT (SYNC FRAME)
		RET

boot_DONE:	CALL	PURGE
		CALL	PRINTI
		DB	CR,LF,"bootload complete\r\n",EOS
		LD	SP, StackTop	; Reset Stack = 0xFF80
		jp	0x8000		;start the loaded program
		

;FINISHING CODE PRIOR TO LEAVING XMODEM
bootmonitor:
		CALL	PURGE
		CALL	PRINTI
		DB	"Starting Monitor\r\n",EOS
		POP	BC		;SCRAP CALLING ROUTINE AND HEAD TO PARENT
		pop	bc		;another level
		RET

boot_fail	LD	A,CAN
		CALL	Put_Char
		CALL	Put_Char
		CALL	PURGE
		CALL	PRINTI
		DB	"bootload failed\r\n",EOS
		POP	BC		;SCRAP CALLING ROUTINE AND HEAD TO PARENT
		RET
;no sign of a loader
boot_nil:
		call	bootwink	;blip the led
		LD	SP, StackTop	; Reset Stack = 0xFF80
		jp	0x8000		;start the loaded program

;===============================================
;PURGE - Clears all in coming bytes until the line is clear for 1/4 second
;-----------------------------------------------
bootPURGE	LD	A,1	;OZ 1/4 second purge
		CALL	bootTM_GETCHAR
		JR NC,	bootPURGE
		RET
;===============================================
;TIMED_GETCHAR - Gets a character within a time limit
;in:	A contains # of seconds to wait before returning
;out: 	C=1, No Char (Time Out)
;	C=0, A = Char
;-----------------------------------------------
bootTM_GETCHAR	PUSH	DE
		PUSH	BC
		LD	D,A
bTG_LP1		LD	C,35		;B,C=Loop Count down until timeout
bTG_LP2		CALL	In_Char	;87	;TEST FOR RX DATA
		JP NC,	bTG_RET	;10
		DJNZ	bTG_LP2	;13/8	;110 Cycles inner Loop time. 70*256*.25 ~= 7 mSec
		DEC	C	;5
		JP NZ,	bTG_LP2	;10
		DEC	D
		JP NZ,	bTG_LP1
;		SCF		;SET CARRY TO INDICATE TIME OUT
bTG_RET		POP	BC
		POP	DE
		RET

bootwink:		;blip a led on port 40 bit 6
		ld	a,0xc0	;LED on
		out	(0x40),a
		ld	hl,100
		call	Dly
		ld	a,0x80	;led off
		out	(0x40),a
		ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>;
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<;
;	Chapter 3	Main Loop, RS-232 MONITOR, MENU selection
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>;
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<;

		CALL	bootpatch	;call to bootloader - was CALL SEL_RS232

		LD	HL,RS232_SPLASH
		LD	A,(RC_TYPE)
		AND	7
		RLCA
		CALL	ADD_HL_A
		CALL	LD_HL_HL
		CALL	PRINT
		LD	A,(RC_TYPE)
		OR	A
		JP   Z,	SPLASH_VERSION
		LD	H,HIGH RC_TYPE
		LD	L,A
		LD	A,(HL)
		CALL	SPACE_PUT_BYTE
		CALL	REG_DISP_ALL
		JP	SKIP_TABLE2

Advertisements

From → Olduino/Z

One Comment

Trackbacks & Pingbacks

  1. Bit Bang Serial | olduino

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: