• Please review our updated Terms and Rules here

Z80 Intel hex loader

MykeLawson

Experienced Member
Joined
Mar 21, 2014
Messages
377
I came across this many moons ago, and the website I got it from seems to be shut down. Anyway, I figured I'd post it in case it could be of some value...... Maybe something to build on. I try to grab interesting things and chunks of interesting things that I think might come in handy some day. If nothing else, they give me ideas I can build on.
 

Attachments

  • 8251 sample code 01.pdf
    28.3 KB · Views: 21
Well, in the last episode of me trying to gen up a dual serial card using 8251A chips and figure out the right way to load the software I want to use the card to load (https://forum.vcfed.org/index.php?threads/program-load-not-0100h-best-practive.1241263/), I mentioned I screwed up the chip reset line. Well, in fixing that, I discovered that one of the two 8251 chips had power & ground reversed. I figure I blew that chip. Turns out I may not have. At least based of the fact that when I read the status of both chips, after initializing them exactly the same, I get a status code of 85h from both of them.

That tells me that DSR is at zero, the Tx buffer is empty (which it should be), and TxRDY is set. So, I'm thinking both chips are okay and doing what they are supposed to do. Now I have to conjure up some more test software to see if I can get further along.
 
Any particular reason that you're using the 8251? There are much better chips out there for the purpose. I hope you managed to get the 8251A--the original 8251 was horribly quirky.

Back in the day, I liked the Signetics 2651 or 2661. Has a BRG built in and is easy to interface. Even a Z80 SIO or DART or intel 8274 is easier.
 
Not forgetting the 6402 which is to a large extent hardware configurable for things like the number of stop bits by tying some of its pins into one state or another. It's especially useful for micro trainer systems with very little code memory where you don't want to have to waste a couple of dozen bytes on a one-off UART initialisation routine which will only be run once.
 
Yep- it's an 8251A. Why am I using it? The Z-80 CPU board I have, has one on it. I've had them on my S-100 stuff back in the early 80's, and I guess because I'm comfortable with it. So, off I go.....
 
Yep- it's an 8251A. Why am I using it? The Z-80 CPU board I have, has one on it. I've had them on my S-100 stuff back in the early 80's, and I guess because I'm comfortable with it. So, off I go.....
I wrote a little hex loader that runs nicely on Z-80's. It accepts Intel HEX files sent over the console port. It relocates itself to high memory, and decodes the HEX and loads the program at 0100H. All you have to do is run a "save" to save the image to disk(ette).

The trick is to get your terminal emulator to send the HEX file slowly enough so that the Z-80 can handle it without dropping characters. I believe that many of the more popular terminal emulators have options to insert delays? I don't know the details. I believe the 8251 is primitive enough and has limited buffering so that this could be an issue? I use the program with Linux minicom and a little bit of C doggerel that slows down the transfer. I just do "trickle <whatever.hex> > /dev/ttyUSBx" to send the file. It's kind of painful to send lots of files this way, but it works well (with patience). Oh, and of course the size of the file that you can send is limited by whatever Z-80 memory you have available. I think on my CP/M 2.2 that would be about 53k?

Happy to share the code with you if you think it would be useful.

Roger
 
Oh, I'd love to get a copy of that for sure. My serial interface is running at 9600 baud, so I suspect it should work. if not, I'm sure a few strategically placed delays should work fine.
 
For what it's worth, when we did our SDLC/HDLC board, we used an 8251A and it was strictly DMA. There's not much buffering in that chip and sync waits for no man... :)
 
So, it appears I have fixed the first couple of hardware & design issues; the reset line not inverted and the swapped power for the second 8251A. I figured I'd kludge together a simple BASIC program as a way to do a test. BASIC is on the machine and it's easiest to 'code & go' and change on the fly. I'm thinking both on the interfaces are at least working, since the status code being returned is identical for both. So, that implies I can at least read and write to the two 8251A's. What is a head scratcher is the fact that the data ports for both are always 00. I'm hoping it is timing, but I also had hoped the use of BASIC as a test tool would be slow enough to allow the 8251A's to get setup with a byte to read.

I have the transmit tied to receiver at the actual DB9 connector. I think I may try wrapping the XMT & RCV pins of the 8251A to see if that works better. That would tell me the issue is in the 1488 & 1489 driver circuitry.

I attached the BASIC 'test' code and it's interesting that the first pass through to send an ASCII 'A' out yields a status code of 85, where was all subsequent passes yields a status code of 84. The journey continues.
 

Attachments

  • comprt4.txt
    1.2 KB · Views: 6
Well, if it's any help, here's an (I think) 8251 comms interface that I wrote about 40 years ago. The board had "special function latches" that enabled external clocks for synchronous comm and loopback mode; you can ignore those.

Code:
;*	asynchronous communications driver.
;
;	set by default to 300 baud, even parity, 1 stop
;

pasdat	equ	20h	; ascom data port
passts	equ	22h	; ascom status
pasmod	equ	24h	; ascom mode
pascmd	equ	26h	; ascom command
passf1	equ	28h	; special function latch 1
passf2	equ	2ah	; special function latch 2


ascin:	call	ascsts	; ascom input
	ora	a
	jz	ascin	; wait for character
	in	pasdat	; get data
	ani	07fh
	ret		; exit...

ascsts: call	ascit	; initialize
	in	passts	; status ascom for input
	ani	00111000b
	jz	ascst2	; if no error
	mvi	a,00110111b
	out	pascmd	; clear error
ascst2: in	passts	; get status
	ani	10b
	rz		; if not ready
	mvi	a,255
	ret		; exit...data ready

ascout: call	ascit	; initialize
	in	passts	; ascom output
	ani	00111000b
	jz	ascot2	; if no error
	mvi	a,00110111b
	out	pascmd	; reset error
ascot2: in	passts
	ani	01b
	jz	ascout	; if transmit busy
	mvi	a,00100111b
	out	pascmd	; set rts
	mov	a,c
	out	pasdat
	ret		; exit...


ascit:	mvi	a,0
ascia	equ	$-1	; initialize ascom routine
	ora	a
	rnz		; exit if done
	inr	a
	sta	ascia	; set initialized
	xra	a
	out	passf1	; clear special function latches
	out	passf2
	mvi	a,01111010b
	out	pasmod
	mvi	a,00110101b	; 300 baud, even, 1 stop 7 bits
	out	pasmod
	mvi	a,00100111b
	out	pascmd		; send rcv dtr
	ret

8080 mnemonics; I trust you can understand them.
 
This may be a more general question, but it stems from the serial hex download program I am writing. As I've mentioned, the program starts at 0100h like all CP/M programs, but relocates the functional part up to address DB00h to execute the serial hex download part. My source code has two org statements; one for the relocation code at 0100h and the other for the serial hex download part at DB00h. When I assemble it, the resultant .COM file is 55K in size. I was a bit perplexed at this and discovered it is full of zeros from 0110h to DAFFh. I'm thinking that the relocation code would be moving zeros from 0110h to DB00h, which obviously would not work. So, any ideas how you would go about getting rid of all the space full of zeros? I sure did not expect that, and I'm sure there is some directive I have overlooked.

Code:
; STD Bus Serial Board Addresses
SACR            EQU    025H        ;2SIO port A control register
SADR            EQU    024H        ;2SIO port A data register
SBCR            EQU    027H        ;2SIO port B control register
SBDR            EQU    026H        ;2SIO port B data register
BDOS            EQU    0005H            ;DOS ENTRY POINT
PRINTF          EQU    9               ;PRINT STRING
CR            EQU    0DH            ;CARRIAGE RETURN
LF            EQU    0AH            ;LINE FEED
CONS            EQU    1            ;READ CONSOLE
TYPEF            EQU    2               ;WRITE CONSOLE
;------------------------------------------------------
;Code to start program and move to higher memory
;------------------------------------------------------
        org    0100h
        ld    hl,code_origin    ;start of code to transfer
        ld    bc,code_end-code_start+1    ;length of code to transfer
        ld    de,0DB00h    ;target of transfer
        ldir            ;Z80 transfer instruction
        jp    0DB00h
;------------------------------------------------------
;Start of actual program
;------------------------------------------------------
code_origin:
        org    0DB00h
code_start:
;
;READ AND PRINT SUCCESSIVE BUFFERS
        LD    DE,SIGNON               ;WELCOME MESSAGE
;D,E ADDRESSES MESSAGE ENDING WITH "$"
    LD    C,PRINTF            ;PRINT BUFFER FUNCTION 9
    CALL    BDOS
;------------------------------------------------------
;Rest of the program continues from here
;------------------------------------------------------
 
If you're using the simple CP/M-80 assembler, you'll have to code the absolute addresses as expressions with a stated relocation factor.

Some assemblers have distinct origin and location counters--the origin counter for where the code is assembled and the location counter for where it will be executed, so the adjustment is done for you. In particular, if you're using Microsoft M80, see page 3-15 in the manual. Plain old ASM doesn't have this feature. I'll check RMAC later if you wish.
 
I'm using zDevStudio. I love it. I guess what I could do Is assemble them as separate programs and then join them as one. More steps, but it would work.
1671765571882.png
 
This may be a more general question, but it stems from the serial hex download program I am writing. As I've mentioned, the program starts at 0100h like all CP/M programs, but relocates the functional part up to address DB00h to execute the serial hex download part. My source code has two org statements; one for the relocation code at 0100h and the other for the serial hex download part at DB00h. When I assemble it, the resultant .COM file is 55K in size. I was a bit perplexed at this and discovered it is full of zeros from 0110h to DAFFh. I'm thinking that the relocation code would be moving zeros from 0110h to DB00h, which obviously would not work. So, any ideas how you would go about getting rid of all the space full of zeros? I sure did not expect that, and I'm sure there is some directive I have overlooked.

Code:
; STD Bus Serial Board Addresses
SACR            EQU    025H        ;2SIO port A control register
SADR            EQU    024H        ;2SIO port A data register
SBCR            EQU    027H        ;2SIO port B control register
SBDR            EQU    026H        ;2SIO port B data register
BDOS            EQU    0005H            ;DOS ENTRY POINT
PRINTF          EQU    9               ;PRINT STRING
CR            EQU    0DH            ;CARRIAGE RETURN
LF            EQU    0AH            ;LINE FEED
CONS            EQU    1            ;READ CONSOLE
TYPEF            EQU    2               ;WRITE CONSOLE
;------------------------------------------------------
;Code to start program and move to higher memory
;------------------------------------------------------
        org    0100h
        ld    hl,code_origin    ;start of code to transfer
        ld    bc,code_end-code_start+1    ;length of code to transfer
        ld    de,0DB00h    ;target of transfer
        ldir            ;Z80 transfer instruction
        jp    0DB00h
;------------------------------------------------------
;Start of actual program
;------------------------------------------------------
code_origin:
        org    0DB00h
code_start:
;
;READ AND PRINT SUCCESSIVE BUFFERS
        LD    DE,SIGNON               ;WELCOME MESSAGE
;D,E ADDRESSES MESSAGE ENDING WITH "$"
    LD    C,PRINTF            ;PRINT BUFFER FUNCTION 9
    CALL    BDOS
;------------------------------------------------------
;Rest of the program continues from here
;------------------------------------------------------
Well, I figured out a workable way to do this.... Assemble the relocation portion of the code and the actual program, separately from each other, using their respective ORG addresses. Then a simple concatenation using a simple DOS copy command to join the two and it seems to not pad anything. So, I'm hoping that should do the trick.
 
Back
Top