Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

The Intel HEX File Format

435 views
Skip to first unread message

Mr. Emmanuel Roche, France

unread,
Apr 1, 2020, 3:22:24 AM4/1/20
to
INTELHEX.WS4
------------

-- "The Intel HEX File Format"
by Mr. Emmanuel ROCHE, France

Ok. So, while working on something, I found a subroutine saving an area of
memory using an "Intel HEX file". Well, it so happens that they are well known
under CP/M, since ASM and MAC output this kind of file. And LOAD creates a
CP/M COMmand file from them. But let us review what some classic CP/M texts
say about this file format.


- "A simple technique for static relocation of absolute machine code"
Gary Kildall
DDJ ("Dr. Dobb's Journal"), #22, Vol.3, No.2, February 1978, pp.10-13

(Retyped by Emmanuel ROCHE.)

(...) The translator output for an Intel 8080 microcomputer is an Intel HEX
format file, containing a sequence of absolute records which give a load
address and byte values to be stored starting at the load address. The exact
format of each record, shown in Figure 3, begins with a colon (":") followed
immediately by a 2-digit record length (RL) and 4-digit load address (LA). The
2-digit record type (RT) is always zero for absolute records, and is followed
by RL pairs of hexadecimal digits to be placed at LA through LA+RL-1 in
memory.

+--------------------------------------+
| : | RL | LA | RT | d1 d2 ... dN | CS |
+--------------------------------------+
where:
RL = record length (01-FF)
LA = load address (0000-FFFF)
RT = record type (00)
d1 = data byte #1
d2 = data byte #2
...
dN = data byte #N
CS = checksum byte

Figure 3. Intel Hex file format

The record terminates with a pair of checksum digits: if the byte values
(hexadecimal digit pairs) are summed, starting immediately after the colon,
and continuing through the end of the record (including the checksum byte),
then the sum should be zero when computed with an 8-bit counter. The checksum
byte is included as an error detection mechanism. The last record of an Intel
HEX file is denoted by a record length of 00. (...)


- "ASM User's Guide"
Digital Research, 1976

(Retyped by Emmanuel ROCHE.)


4.2 The END directive

The END statement is optional in an assembly language program but, if it is
present, it must be the last statement (all subsequent statements are ignored
in the assembly). The two forms of the END directive are:

label END
label END expression

where the label is again optional. If the first form is used, the assembly
process stops, and the default starting address of the program is taken as
0000. Otherwise, the expression is evaluated, and becomes the program starting
address (this starting address is included in the last record of the Intel HEX
file which results from the assembly). Thus, most CP/M assembly language
programs end with the statement:

END 100H

resulting in the default starting address of 100H (beginning of the Transient
Program Area).


- "SID User's Guide"
Fourth Printing: January 1982

1.1 Starting SID

(c) SID x.HEX

Command form (c) is similar to form (b) except that the test program is
assumed to be in Intel HEX file format, as directly produced by ASM or MAC. In
this case, the initial value of the Program Counter is obtained from the
terminating record of the HEX file unless this value is zero, in which case
the Program Counter is set to the beginning of the TPA. As the ASM and MAC
manuals discuss, the Program Counter value can be given on the "END" statement
in the source program.


Ok. So, since I found this routine strangely written, I was wondering if it
was working correctly? So, I ran my own version, named SAVEHEX. Now, this
routine happens to have been published in the comp.os.cpm Newsgroup a long
time ago, the 21th of September, 2001! Waow! In computer time, this is
prehistoric! I had a look to what HEX files were stashed on my Internet
computer. There were a dozen of files, one of them being "8080 BASIC VER 1",
better known as "Altair BASIC". All of them seemed to be good. There was
remaining one lone "TOTO.HEX" file. Don't ask me why, but I tried it... and
horror! The checksum produced errors! I was in a state of shock, since I had
used this routine for probably 30 years! (And nobody ever complained since its
publication.)

As Edsger Dijkstra famously said: "Program testing can be used to show the
presence of bugs, but never to show their absence!"

I had downloaded this "TOTO.HEX" file 10 years ago. Apparently, it was
containing an Intel 8080 assembler, except that the mnemonics were in reverse
alphabetic order?!? Anyway, all the checksums less than 10hex (16) were
generating bad checksums (one digit only). I had not noticed it, since my
others HEX files were using, apparently, only 2-digits checksums...

So, back to the drawing board -- my keyboard, in my case. So, SAVEHEX was not
producing a correct checksum. So, I modified it to display it after the dump
of the HEX file. Since some old HEX files are quite large (apparently, circa
1976, the standard "Record Length" became 10hex (16), which is a nice value,
since it is compatible with an hexadecimal dump (before, it was common to dump
only 8 values, since programmers were thinking in Octal). My oldest HEX file
contains 24 "data bytes", probably to fill the line of an ASR-33 Teletype.), I
removed the display of the data bytes. I could now check the checksums of all
my HEX files. There was a problem with the way the checksum was computed.
Since Gary Kildall did not explain how the "checksum byte" was computed, I had
a look to my "Intel MCS-80 User's Manual", dated October 1977 (the 1975
version only talked of "punched cards", not paper tape).

It was saying: "data records are type 0; end-of-file records are type 1." It
is obvious that Gary Kildall was used to an older version of the Intel HEX
file format (that he probably created, since he was one of the 3 programmers
of the Intel "Software Group" before 1976) since he only used "type 0"
"absolute records". END statements without "expression" simply generate a
string of ten ASCII zeros. The classic "END 100H" statement generates a
":00010000FF" record, meaning: 00-0100-00-FF. (If you see a "last record"
ending with "01FF", then the HEX file was generated by an Intel program.)

Regarding the "checksum field", the 1977 manual was saying: "(it) contains the
ASCII hexadecimal representation of the 2's complement of the sum of the bytes
(etc.)". Argh! I suddenly realized, upon viewing my old program, that I had
not understood this, when I created SAVEHEX about 30 years ago. It dawned on
me that I had devised my routine interactively, until it worked on the HEX
files that I had available, way back then. (I had not the MCS-80 manual, back
then. I only got it later.) So, the checksum is computed using a "2's
complement". In "computerese", that means: "inverting the bits of the sum and
adding one, or "1 + NOT cs". Since we only want the low-byte, that means (in
BASIC): cs$ = MID$ (HEX$ (1 + NOT cs, 4), 3). I modified my test program and,
of course, it produced correct checksums for all my HEX files! Haaaaa!... (Let
us hope that, this time, it will work for another 30 years!)

Just to be sure, I made an Internet search. I had a look at Wikipedia, but
they do not seem to have heard of CP/M, and the influence that it has had on
MS-DOS and "Windows". Apparently, they do not seem to understand that the
Intel HEX file format can contain much more than the "data bytes". As long as
the lines do not start with a colon (":"), they can contain anything that you
want. I am quite sure that I once saw a big HEX file. I was surprised by its
size, so I TYPEd it. It contained, at the beginning, the source code of a PL/M
program, followed, at the end, by the resulting HEX file produced by the PL/M
compiler. Either this was an option of this compiler (the IBM System 360
version?), or both files were concatenated, for example with PIP. When I did a
search on my Internet computer, I found another HEX file containing several
lines of comments, not at the beginning or at the end, but separating several
lines of "absolute records". Unfortunately, I could find no date, just that it
was from an "(Intel) 8008 Simulator". So, at the beginning of its use, it was
well known that HEX files could contain explanations.

To make things clearer, let me show you what my programs display.

run"savehex

SAVEHEX> Enter Intel HEX File Name: ? ascii

Start address (HEX without H): F000
End address (HEX without H): F0FF

ASCII.HEX
---------

Documentation ? First try of SAVEHEX.BAS
Documentation ? 2nd line
Documentation ? 3rd line
Documentation ? 4th line
Documentation ? 5th line
Documentation ? The END
Documentation ?

:10F00000000102030405060708090A0B0C0D0E0F88|................
:10F01000101112131415161718191A1B1C1D1E1F78|................
:10F02000202122232425262728292A2B2C2D2E2F68| !"#$%&'()*+,-./
:10F03000303132333435363738393A3B3C3D3E3F58|0123456789:;<=>?
:10F04000404142434445464748494A4B4C4D4E4F48|@ABCDEFGHIJKLMNO
:10F05000505152535455565758595A5B5C5D5E5F38|PQRSTUVWXYZ[\]^_
:10F06000606162636465666768696A6B6C6D6E6F28|`abcdefghijklmno
:10F07000707172737475767778797A7B7C7D7E7F18|pqrstuvwxyz{|}~.
:10F08000808182838485868788898A8B8C8D8E8F08|................
:10F09000909192939495969798999A9B9C9D9E9FF8|................
:10F0A000A0A1A2A3A4A5A6A7A8A9AAABACADAEAFE8|................
:10F0B000B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFD8|................
:10F0C000C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFC8|................
:10F0D000D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFB8|................
:10F0E000E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFA8|................
:10F0F000F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF98|................
:0000000000

First address: F000
Last address: F0FF
Bytes read: 0100
Records read: 02

Ok

Explanations: The first 3 lines are "prompts" from the program, asking (as you
can see) the name of the output HEX file, and (since it is a "memory image")
its starting and ending addresses. All the remaining lines are output by the
program, and show the contents of the resulting HEX file. This way, you are
not surprised by what it contains. So, the HEX file starts by its name,
underlined. Since the HEX file can contain explanations, the program asks the
operator to provide some comments. So, I put some lines. The program ends
adding those comments lines when you type only on the ENTER key. So, if you
want some empty lines, don't forget to type a space before pressing the ENTER
key. Once you have finished typing your comments, the program shows you the
"absolute records", with an ASCII dump to show you its contents. Since the
"memory image" can be more than a screenfull, the program stops at each 24
lines of output, waiting for you to press the spacebar. Contrary to a standard
ASCII file, the HEX file does not end with a CP/M End-Of-File character (1AH),
but with the standard "last record": :0000000000. (Since my program has a
different purpose than LOAD/HEXCOM, it does not care about the Load Address.
Anyway, F000 is different from 0100H.) In case you are interested in how it is
done, its listing follows.

10 REM SAVEHEX.BAS by Emmanuel ROCHE
20 :
30 PRINT
40 INPUT "SAVEHEX> Enter Intel HEX File Name: " ; file$
50 PRINT
60 INPUT " Start address (HEX without H): " ; sa$
70 INPUT " End address (HEX without H): " ; ea$
80 PRINT
90 :
100 sa = VAL ("&H" + sa$)
110 sb = sa
120 ea = VAL ("&H" + ea$)
130 ea = ea + 1
140 IF sa + 1 > ea THEN PRINT CHR$ (7) ; "Error: Start > End." : GOTO 850
150 :
160 file$ = file$ + ".HEX"
170 OPEN "O", #1, file$
180 lc = 0
190 :
200 PRINT #1,UPPER$(file$)
210 PRINT UPPER$(file$)
220 PRINT #1,STRING$(LEN (file$), "-")
230 PRINT STRING$(LEN (file$), "-")
240 PRINT #1
250 PRINT
260 LINE INPUT "Documentation ? " ; doc$
270 IF doc$ = "" THEN GOTO 310
280 PRINT #1,doc$
290 lc = lc + 1
300 GOTO 260
310 IF lc <> 0 THEN PRINT #1
320 PRINT
330 lc = lc + 5
340 :
350 PRINT #1,":" ;
360 PRINT ":" ;
370 cs = 0
380 :
390 rl$ = HEX$ (ea - sa, 2)
400 rl = VAL ("&H" + rl$)
410 IF rl = 0 THEN PRINT #1,"0000000000"
420 IF rl = 0 THEN PRINT "0000000000"
430 IF rl = 0 THEN GOTO 800
440 IF rl > 16 THEN rl = 16
450 PRINT #1,HEX$ (rl, 2) ;
460 PRINT HEX$ (rl, 2) ;
470 cs = cs + rl
480 :
490 PRINT #1,HEX$ (sa, 4) ;
500 PRINT HEX$ (sa, 4) ;
510 hb = INT (sa / 256)
520 lb = sa - hb * 256
530 cs = cs + hb + lb
540 :
550 PRINT #1,"00" ;
560 PRINT "00" ;
570 :
580 alpha$ = ""
590 FOR i = 1 TO rl
600 db = PEEK (sa)
610 db$ = HEX$ (db, 2)
620 PRINT #1,db$ ;
630 PRINT db$ ;
640 al$ = CHR$ (db)
650 IF (db < &H20) OR (db > &H7E) THEN al$ = "."
660 alpha$ = alpha$ + al$
670 cs = cs + db
680 sa = sa + 1
690 NEXT i
700 :
710 cs$ = MID$ (HEX$ (1 + NOT cs, 4), 3)
720 PRINT #1,cs$
730 PRINT cs$ TAB (44) "|" alpha$
740 :
750 lc = lc + 1
760 IF lc = 24 THEN lc = 0 : WHILE INKEY$ = "" : WEND
770 :
780 GOTO 350
790 :
800 PRINT
810 PRINT "First address: " ; HEX$ (sb, 4)
820 PRINT "Last address: " ; HEX$ (ea - 1, 4)
830 PRINT "Bytes read: " ; HEX$ (ea - sb, 4)
840 PRINT "Records read: " ; HEX$ ( (ea - sb) / 128, 2)
850 PRINT
860 CLOSE
870 END
Ok

Now that we have an HEX file, let us try to load and put it into memory.

run"loadhex

LOADHEX> Enter Intel HEX File Name: ? ascii

ASCII.HEX
---------

First try of SAVEHEX.BAS
2nd line
3rd line
4th line
5th line
The END

F000| 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F |................
F010| 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F |................
F020| 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F | !"#$%&'()*+,-./
F030| 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F |0123456789:;<=>?
F040| 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F |@ABCDEFGHIJKLMNO
F050| 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F |PQRSTUVWXYZ[\]^_
F060| 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F |`abcdefghijklmno
F070| 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F |pqrstuvwxyz{|}~.
F080| 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F |................
F090| 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F |................
F0A0| A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF |................
F0B0| B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF |................
F0C0| C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF |................
F0D0| D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF |................
F0E0| E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF |................
F0F0| F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF |................

First address: F000
Last address: F0FF
Bytes read: 0100
Records read: 02

Ok

Explanations: The program asks for the name of the HEX file, then first
displays the comment lines, then a full dump of the contents of the HEX file.
It ends by displaying the statistics. In case you are interested in how it is
done, its listing follows.

10 REM LOADHEX.BAS by Emmanuel ROCHE
20 :
30 PRINT
40 INPUT "LOADHEX> Enter Intel HEX File Name: " ; file$
50 PRINT
60 :
70 file$ = file$ + ".HEX"
80 OPEN "I", #1, file$
90 lc = 0 : lc2 = 0
100 LINE INPUT #1, rec$
110 IF LEFT$ (rec$, 1) = ":" THEN GOTO 150
120 PRINT rec$
130 GOTO 520
140 :
150 IF LEFT$ (rec$, 1) <> ":" THEN GOTO 120
160 csum = 0 : lc2 = lc2 + 1
170 ' Record Length
180 rl$ = MID$ (rec$, 2, 2)
190 rl = VAL ("&H" + rl$)
200 IF rl = 0 THEN GOTO 610
210 csum = csum + rl
220 ' Load Address
230 la$ = MID$ (rec$, 4, 4)
240 PRINT la$ "| " ;
250 la = VAL ("&H" + la$)
260 IF lc2 = 1 THEN fa = la
270 hila = INT (la / 256)
280 csum = csum + hila
290 lola = la - hila * 256
300 csum = csum + lola
310 ' Only Record Type: 01
320 ' Data Byte(s)
330 alpha$ = ""
340 FOR i = 1 TO rl
350 db$ = MID$ (rec$, 8+i*2, 2)
360 PRINT db$ " " ;
370 db = VAL ("&H" + db$)
380 csum = csum + db
390 al$ = CHR$ (db)
400 IF (db < &H20) OR (db > &H7E) THEN al$ = "."
410 alpha$ = alpha$ + al$
420 POKE la, db
430 la = la + 1
440 NEXT i
450 PRINT TAB (55) "|" alpha$
460 ' Checksum
470 cs$ = RIGHT$ (rec$, 2)
480 cs = VAL ("&H" + cs$)
490 csum = csum + cs
500 locsum = csum MOD 256
510 IF locsum <> 0 THEN GOTO 560
520 lc = lc + 1
530 IF lc = 24 THEN lc = 0 : WHILE INKEY$ = "" : WEND
540 GOTO 100
550 :
560 PRINT
570 PRINT CHR$ (7) ; "Checksum Error in line: " ; la$
580 PRINT
590 END
600 :
610 PRINT
620 PRINT "First address: " ; HEX$ (fa, 4)
630 PRINT "Last address: " ; HEX$ (la - 1, 4)
640 PRINT "Bytes read: " ; HEX$ (la - fa, 4)
650 PRINT "Records read: " ; HEX$ ( (la - fa) \ 128, 2)
660 CLOSE
670 PRINT
680 END
Ok

Alright. So far, so good. So, LOAD/HEXCOM take an HEX file and create a CP/M
COMmand file containing a "memory image" loading at 100H, the start of the
CP/M TPA (Transient Program Area). But what if we want to load some machine
language routine in upper memory, like below the BDOS? it is then that LOADHEX
can be useful. For some unknown reason, microcomputer magazines were full of
listings of BASIC programs containing DATA lines full of hexadecimal stuff,
sometimes written "&HFF", sometimes "FF", and finishing (with luck) with a
checksum for the whole program... Compare this with the Intel HEX file format,
which has a checksum every 16 bytes! In my experience, the HEX file is the
best way to transfer binary stuff. Yet, for some unknown reason, microcomputer
magazines were full of corrections, and of "checker" programs, when each line
of an HEX file could have contained its own checking...

But there remains one use of HEX files. What if the "memory image" is starting
at 0000? The standard CP/M LOAD/HEXCOM utility was designed to create a CP/M
COMmand file loading at 100H: it aborts if you give it an HEX file containing
a Load Address less than 100H. Some historical Intel 8080 programs, like the
Altair BASIC, were created to run directly from the hardware, without any
Operating System like CP/M. Also, since they usually were written to use as
little memory as possible, they were often using the "Restart" (1-byte)
instructions of the Intel 8080 CPU (the Zilog Z-80 CPU did not yet exist, at
the time), rather than the 3-byte CALL instruction. So, if we want to study
those historical programs, a way to create them under CP/M is needed. The
input is clear: an HEX file. But how to name the output? Since it is starting
at 0000H, it could be ZERo, but I have used the BIN filetype for years. So, on
my computer, it is simple: COM files load at 0100H, BIN files load at 0000H.
So, let us modify LOADHEX so it can create BIN files.

run"hexbin

HEXBIN> Enter Intel HEX File Name: ? ascii

ASCII.HEX
---------

First try of SAVEHEX.BAS
2nd line
3rd line
4th line
5th line
The END

F000| 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F |................
F010| 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F |................
F020| 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F | !"#$%&'()*+,-./
F030| 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F |0123456789:;<=>?
F040| 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F |@ABCDEFGHIJKLMNO
F050| 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F |PQRSTUVWXYZ[\]^_
F060| 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F |`abcdefghijklmno
F070| 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F |pqrstuvwxyz{|}~.
F080| 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F |................
F090| 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F |................
F0A0| A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF |................
F0B0| B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF |................
F0C0| C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF |................
F0D0| D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF |................
F0E0| E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF |................
F0F0| F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF |................

First address: F000
Last address: F0FF
Bytes read: 0100
Records read: 02

Ok

Explanations: As can be seen, HEXBIN is very similar to LOADHEX. You enter the
name of the HEX file, and the program displays a full dump as it is creating
the BIN file, ending with the statistics. In case you are interested in how it
is done, its listing follows.

10 REM HEXBIN.BAS by Emmanuel ROCHE
20 :
30 PRINT
40 INPUT "HEXBIN> Enter Intel HEX File Name: " ; file$
50 PRINT
60 :
70 file1$ = file$ + ".HEX"
80 file2$ = file$ + ".BIN"
90 OPEN "I", #1, file1$
100 OPEN "R", #2, file2$, 1
110 FIELD #2, 1 AS db2$
120 lc = 0 : lc2 = 0
130 LINE INPUT #1, rec$
140 IF LEFT$ (rec$, 1) = ":" THEN GOTO 180
150 PRINT rec$
160 GOTO 560
170 :
180 IF LEFT$ (rec$, 1) <> ":" THEN GOTO 150
190 csum = 0 : lc2 = lc2 + 1
200 ' Record Length
210 rl$ = MID$ (rec$, 2, 2)
220 rl = VAL ("&H" + rl$)
230 IF rl = 0 THEN GOTO 650
240 csum = csum + rl
250 ' Load Address
260 la$ = MID$ (rec$, 4, 4)
270 PRINT la$ "| " ;
280 la = VAL ("&H" + la$)
290 IF lc2 = 1 THEN fa = la
300 hila = INT (la / 256)
310 csum = csum + hila
320 lola = la - hila * 256
330 csum = csum + lola
340 ' Only Record Type: 01
350 ' Data Byte(s)
360 alpha$ = ""
370 FOR i = 1 TO rl
380 db$ = MID$ (rec$, 8+i*2, 2)
390 PRINT db$ " " ;
400 db = VAL ("&H" + db$)
410 csum = csum + db
420 al$ = CHR$ (db)
430 LSET db2$ = al$
440 PUT #2
450 IF (db < &H20) OR (db > &H7E) THEN al$ = "."
460 alpha$ = alpha$ + al$
470 la = la + 1
480 NEXT i
490 PRINT TAB (55) "|" alpha$
500 ' Checksum
510 cs$ = RIGHT$ (rec$, 2)
520 cs = VAL ("&H" + cs$)
530 csum = csum + cs
540 locsum = csum MOD 256
550 IF locsum <> 0 THEN GOTO 600
560 lc = lc + 1
570 IF lc = 24 THEN lc = 0 : WHILE INKEY$ = "" : WEND
580 GOTO 130
590 :
600 PRINT
610 PRINT CHR$ (7) ; "Checksum Error in line: " ; la$
620 PRINT
630 END
640 :
650 PRINT
660 PRINT "First address: " ; HEX$ (fa, 4)
670 PRINT "Last address: " ; HEX$ (la - 1, 4)
680 PRINT "Bytes read: " ; HEX$ (la - fa, 4)
690 PRINT "Records read: " ; HEX$ ( (la - fa) / 128, 2)
700 PRINT
710 CLOSE
720 END
Ok

In case you wonder what the BIN file contains, let us show its contents.

run"dumpfile

DUMPFILE> Enter filename.typ: ? ascii.bin

0000| 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F |................
0010| 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F |................
0020| 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F | !"#$%&'()*+,-./
0030| 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F |0123456789:;<=>?
0040| 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F |@ABCDEFGHIJKLMNO
0050| 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F |PQRSTUVWXYZ[\]^_
0060| 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F |`abcdefghijklmno
0070| 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F |pqrstuvwxyz{|}~.
0080| 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F |................
0090| 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F |................
00A0| A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF |................
00B0| B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF |................
00C0| C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF |................
00D0| D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF |................
00E0| E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF |................
00F0| F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF |................

Ok

This time, I will not show you the listing of DUMPFILE, since all the versions
of CP/M come with a DUMP utility.

You will note that the "loading address" of this BIN file is 0000. DUMPFILE
recognizes that it is not a COMmand file loading at 0100, so assume that the
file contains binary data, so starts at 0000.

Let us go back to SAVEHEX/LOADHEX. So, they can save/load "memory image"
starting/ending at any memory address. But, under CP/M or any 8-bit 64K
system, there remains one case: "Page addresses". Since CP/M, it is standard
to display memory addresses using the hexadecimal system (before it was in
Octal). So, as we said for BIN/COM files, the memory addresses are 0000/0100.
Notice that those memory addresses can be written 00-00/01-00. That is to say:
Page zero, address zero / Page one, address zero. Now, remember that the
highest memory address in a 8-bit 64K computer is FFFF, that it to say: Page
FF, address FF (where both "FF" are in hex, or 255 decimal). So, the lowest
addresses are in Page zero (or 00) and the highest addresses are in Page FF.
Now, if we remember that CP/M filetypes are 3-letters long, one could use
filetypes of the form P00-PFF (with all the hex values in between) to indicate
at which memory address where to load the HEX file.

I got this idea because I found the HEX filetype limited. I was using the
filename to indicate at which address to load the HEX file. Unfortunately,
since a filename is 8 characters under CP/M, it was consuming half the name!
So, the names were cryptic! One day, I noticed that most of my addresses were
ending with "00", so the loading address could be reduced to the Page address,
which (as explained) could be put inside the filetype... Big advantage: the
full 8-chars name become available to explain the contents of the HEX file!
Also, the program can check that the contents of the HEX file will be loaded
at the address mentioned in the filetype.

This way of "naming" HEX files is useful, of course, if your "data bytes" to
be loaded into memory are more than a Page long, or 256 bytes. Judging by the
length of programs published in microcomputer magazines, it was commonly the
case. It was rare to see a program whose DATAs were less than 16 lines. In
those cases, the standard Intel HEX file format was perfectly adequate.
Indeed, this was the case in the 1977 Intel manual.

run"loadhex

LOADHEX> Enter Intel HEX File Name: ? intellec

Intellec HEX Example
--------------------

Page 6-76 of the "Intel MCS-80 User's Manual", October 1977

3100| 31 1A 32 0E 03 11 7E 31 CD 40 00 3A 92 31 B7 C2 |1.2...~1.@.:.1..
3110| 60 31 0E 00 11 70 31 CD 40 00 3A 92 31 B7 C2 60 |`1...p1.@.:.1..`
3120| 31 2A 7E 31 22 7A 31 0E 03 11 7E 31 CD 40 00 3A |1*~1"z1...~1.@.:
3130| 92 31 B7 C2 60 31 2A 8C 31 7C B5 CA 50 31 0E 04 |.1..`1*.1|..P1..
3140| 11 88 31 CD 40 00 3A 92 31 B7 C2 60 31 C3 27 31 |..1.@.:.1..`1.'1
3150| 0E 01 11 7A 31 CD 40 00 0E 09 11 90 31 CD 40 00 |...z1.@.....1.@.
3160| 0E 0C 11 92 31 CD 40 00 0E 09 11 90 31 CD 40 00 |....1.@.....1.@.
3170| 7E 31 96 31 01 00 00 00 92 31 |~1.1.....1
317C| 92 31 01 00 96 31 80 00 8C 31 92 31 00 00 96 31 |.1...1...1.1...1
318E| 92 31 92 31 |.1.1
3194| 92 31 |.1

First address: 3100
Last address: 3195
Bytes read: 0096
Records read: 01

Ok
system

A>That's All, Folks!


EOF
0 new messages