• Please review our updated Terms and Rules here

Fun with REL format

horo

Experienced Member
Joined
Jan 22, 2025
Messages
86
While working on the WozMon for the Z80-MBC2, I wondered how many methods there are for generating an Intel HEX file from CP/M source code – apart from the obvious options (Linux: zasm -uw --target=ram -x wozmon.z80 -o wozmon.hex – it also handles undocumented opcodes such as LD A,IXL) and (CP/M: z80asm wozmon/fh – which only handles the documented ones and needs macros to add the undocs), I ended up with the CP/M variant zsm4 wozmon,wozmon=wozmon.z80, which understands undocumented opcodes but generates a wozmon.rel file.
OK, so how do I create a HEX file from that?
Microsoft L80 is supposed to be able to generate HEX (l80 wozmon/n/x/e), but always reports
Code:
Link-80  3.44  09-Dec-81  Copyright (c) 1981 Microsoft
?Nothing Loaded
*
DR’s link80 and SLR’s slrnk do not generate HEX.

Right, what does the (MS or DR) REL format look like? Let’s ask the AI. My current go-to, DeepSeek, is having an incredibly hard time finding the specification and is spitting out URLs (→404) and non-existent documents – so what does the market leader, ChatGPT, have to say? It finds the specs straight away, but can’t put together anything that can even remotely analyse a REL file; it can’t even manage to work out the name of the module when I pointed it to the relevant part of the spec.
Deepseek, on the other hand, when fed the spec and womon.rel attached as a hexdump, delivered a rel2hex.py that created an almost perfect HEX file (only with 0x100 offset to the addresses; the LLM apparently had it ‘in the back of its mind’ that CP/M always starts at 100h.) It also ‘optimised’ away three data bytes preset with 00 at the end of the program; apparently, the ‘idea’ of padded sectors came into play there.
After my two hints, rel2hex ran smoothly and produced a HEX file identical to the zasm and z80asm results. Also the handling of ASEG, CSEG, DSEG was ok.
Not bad at all.
Why would anyone do something like this? In the course of my computer archaeology, I am constantly amazed at how much functionality can be crammed into so few bytes, which are then processed at a snail’s pace by a processor that is practically mathematically illiterate.
Sometimes I think the upcoming generation should look at these oldie Z80s and learn to walk before they start running.
 
The Microsoft L80 manual describes the REL format, see "FORMAT OF LINK-80 COMPATIBLE OBJECT FILES".

I think you've got the wrong commandline syntax for L80. In your example, I believe you must use "l80 wozmon,wozmon/n/x/e" possibly with a "/p:XXXX" to establish the link/load address (e.g. "/p:100" for standard CP/M 0100H).
 
Also, DRI's ASM.COM and MAC.COM produce HEX output exclusively. 'zmac' will produce REL or HEX or whatever you specify. If you're not actually linking multiple modules, using REL is kind of overkill.
 
ASM.COM and MAC.COM
8080 :(

"l80 wozmon,wozmon/n/x/e"
Code:
l80 WOZMON_A,WOZMON_A/N/X/E

Link-80  3.44  09-Dec-81  Copyright (c) 1981 Microsoft
%Overlaying Data area

Data    0000    0FBA    < 4026>

38831 Bytes Free
[0000   0FBA       15]
Origin below loader memory, move anyway(Y or N)?n

Y and N result in a big wozmon.hex with most 00 in - this is not a padding b/c there is no useful code in the complete file at all. Also /P does not help.

REL is kind of overkill

Yes, as I wrote, I have two perfectly working Z80 -> HEX solutions: zasm (Linux) and my all-time (from the 80s on) favorite SLR's z80asm that I use especially for creating embedded ROM code. MAC and RMAC have issues (besides the fact that they talk 8080) when the code lives e.g. at 0xF000 and not 0x100.
Curiosity killed the cat - but I like to deep-dive into old code structures. This is just my version of a crossword puzzle or Sudoku.

Funny fact that they decided to use a bitstream to encode the segments in a most space-efficient way that is quite time-consuming to extract - this clearly shows the heritage from the 70s where every kilobyte of core or semiconductor memory costed a fortune while it did not matter if the code building took five minutes instead of two. Programmers that started with time sharing machines (myself included, as a 15-year-old schoolboy writing FORTRAN at the Berlin university) tried hardly to avoid bugs - it's frustrating to deliver your punch cards and receive two pages fanfold paper saying "syntax error in 5" or "division by zero" hours later. :(
 
Processing REL files does not consume an excessive amount of time, it's just that writing the code to that is not something for a novice to take on. And for simple cases it's just more work than is needed when going directly from ASM code to HEX is all that's needed.

DRI's tools understand only Intel Mnemonics, but are not limited to 8080 instructions. It is possible to generate Z80 instructions without much difficulty.

As far as L80 is concerned, it has it's quirks. One of them is that it insists on actually loading the code into memory at the designated ORG address (and even allows you to run it there), which means it could clobber parts of CP/M or page 0. I don't think that "feature" has been useful to very many people, and that narrow view limits what you can use it for. DRI's LINK.COM is much smarter about that, and that can be used to generate ROM and OS code. I think there must be some way to get around that ORG problem in L80, but I just don't use that if at all possible. These are the kinds of issues that we all had to deal with, and they influenced our decisions about which tools to use and then which mnemonics to use. DRI's LINK.COM has some features that L80 does not, which are needed to build system components for CP/NET, MP/M, and CP/M-3. The M80/L80 chain was incompatible with that and using a single family of tools consistently was often better than switching around.
 
For some reason, I don't get the obsession with HEX files. Generating binaries is both faster and more straight-forward, and converting those into HEX format (if needed) can be done afterwards.

But I guess it makes sense for tooling designed around 7-bit ASCII transmissions and 5-bit baudot-coded teletype paper tape.

There is a (semi-)modern reimplementation of L80 called ld80, but since I found it on a Github page with your name, you probably know all about it. :)
 
Yeah, that ld80 is one I modified to add support for some of the DRI formats needed to generate system files. Originally, that came from the zmac download site. In theory, zmac/ld80 could now replace DRI's RMAC/LINK (and supports Zilog Mnemonics) but I have not converted my builds over to that.
 
Doug – I see, you did a lot of improvements to George Phillips' version 0.7. Obviously I missed your repo when I detected the big missing point from George's 0.7 – a CP/M linker that cannot produce COM files – this is what I changed in my repo, plus adding also ar80.
 
But I guess it makes sense for tooling designed around 7-bit ASCII transmissions
I use the hex format (as well as the woz format) often when I work on the bare metal of my Z80-MBC2. It has a mode to load and execute IHEX directly, and also WozMon, a minimal monitor program (dump, modify, execute) that is the slightly enhanced Z80 version of the original Apple II monitor written by Steve Wozniak (the original code size was 256 bytes). It uses an even simpler hex mode to load or display memory content in lines with a 16-bit address word with trailing colon followed by up to 16 8-bit data bytes separated with spaces: AAAA: DD DD DD DD ...
To handle the transfer I've written a small python tool iload that uses the serial port parallel to my VT100 terminal emulation and can send ASCII text (e.g. BASIC code), Intel HEX or WOZ files directly or converted from binary with EOL delay and CR, LF, or CRLF termination.
 
Back
Top