00000230: 00 00 00 00 c3 7e 40 5b 5a 38 30 20 43 6f 6e 74 .....~@[Z80 Cont
00000240: 72 6f 6c 20 53 79 73 74 65 6d 20 20 20 56 65 72 rol System Ver
00000250: 73 69 6f 6e 20 20 33 28 31 33 31 29 20 20 31 30 sion 3(131) 10
00000260: 2d 4d 61 72 2d 38 38 20 2d 20 2f 4d 5d 20 20 20 -Mar-88 - /M]
00000270: 20 20 00 43 6f 70 79 72 69 67 68 74 20 31 39 38 .Copyright 198
00000280: 34 2c 38 35 2c 38 36 2c 38 37 20 54 61 6e 64 79 4,85,86,87 Tandy
00000290: 20 43 6f 72 70 6f 72 61 74 69 6f 6e 0a 41 6c 6c Corporation.All
/* END BOOT */
BOOT
DIAG
Ah, right; I recently had a worst-case experience with this disassembling the Supersoft CPU Diagnostics, which extensively use a print routine that takes its argument in memory immediately after the call. That involved creating well over a hundred z80dasm block entries manually, and was a miserable experience.Because of this, strings and such belonging to modules will be with those modules and thus scattered through the binary.
If you're looking for a pro-level disassembler, you may want to consider IDA Pro. It's not cheap but is very good.
Ah, right; I recently had a worst-case experience with this disassembling the Supersoft CPU Diagnostics, which extensively use a print routine that takes its argument in memory immediately after the call. That involved creating well over a hundred z80dasm block entries manually, and was a miserable experience.
It was common even in the x80 days, particularly when ROM code was involved.This technique was fairly common back in the day.
push psw
lda item
mov c,a
pop psw
...
item db ....
mvi c,0
item equ $-1
I more commonly hear this called "self-modifying code." And for the simple things it's often used for, I've never found it to be any big deal at all; it's usually quite obvious where and why it's being used.Another gotcha from the old days, is "plugging" code in RAM.
That's why my disassembler is a manual tracing disassembler. I can just go search for "CALL Lxxxx" and manually touch up each of those. I'm making no attempt at a stuffy calls tree, I just keep track of what each byte of the source binary needs to be.Ah, right; I recently had a worst-case experience with this disassembling the Supersoft CPU Diagnostics, which extensively use a print routine that takes its argument in memory immediately after the call. That involved creating well over a hundred z80dasm block entries manually, and was a miserable experience.
This binary is the z80ctl program for Tandy's Xenix System III for the Tandy 6000. This is the code that turns the Model II/12 Z80 computer into an I/O coprocessor for the 68000 CPU. This particular binary supports the so-called 'mux' boards, multiplier serial cards. There were two different multiport serial cards for the Model II series computers. One version of the z80ctl supports the three port cards, and the other version supports the four port. They cannot coexist due to not enough memory for both drivers in z89ctl..... But it's obviously Z80 code, plenty of C3 xx xx in there. Hmm, interesting that one of the strings is "68k crashed", could this be from a Model 16 or 6000?
I'm going to use my own disassembler on it (see sig) once I figure out what the heck this even is.
That's why my disassembler is a manual tracing disassembler. I can just go search for "CALL Lxxxx" and manually touch up each of those.
I would go with extending that just slightly: allow marking any given RST or CALL to a particular address as, "data after RST/CALL, terminated with $nn." (Or fixed length of n.) That would cover pretty much everything I've seen.One of my dream disassembler features would be a list of "subroutine 1234 always has weird crap after it", but usually each one is so bespoke that all you can do is just have it stop disasembly. But RST instructions done that way usually have a fixed number of bytes after, and are quite common, so I actually have handling for those.
You've not seen the more complicated stuff that I've seen, heh. But at the very least, "terminated with bit 7 set" is also rather common. The main problem is the UI necessary to specify all of those call addresses. It's easier for me to simply deal with them manually than to write code to do it. Everything is at the level of "what this byte does", not trying to make an enormous tree of all the calls.That would cover pretty much everything I've seen.
Ah, well that's easily enough to deal with: just add a mask for the termination byte as well.You've not seen the more complicated stuff that I've seen, heh. But at the very least, "terminated with bit 7 set" is also rather common.
Seems pretty simple to me! :-)The main problem is the UI necessary to specify all of those call addresses.
data call-suffix $1234 term $80 mask $80 dbtype char
It was the only way to modify addresses on many first- and second-generation CPUs.The two most common cases I can think of are for I/O to arbitrary ports on 8080/8085 and for a massive increase in speed and ease of use for certain sorts of copy routines on the 6502.
Funny, I would think a tracing disassembler wouldn't have much luck with arguments after the call, since it would be the routine that, I guess, tweaks the PC on the stack for when it returns from the subroutine, not necessarily something the disassembler could see.This caused me to ask about for a tracing disassembler for Z80,
Particularly in ROM code, it's very convenient, not just for message display, but tables can also follow the call (e.g. a computed GOTO). The advantage is that no registers are destroyed (any saving needed is left to the called routine. In 8080 code, the called routine can use the XTHL instruction to swap the HL pair with what's pointed to by SP, incrementing HL through the literal, then XTHL back and return. A similar scheme applies in x86 code. In the case of the 8080, it's also self-relocating, although the location of the servicing routine is not, obviously.Funny, I would think a tracing disassembler wouldn't have much luck with arguments after the call,