Downloaded a random EPROM from a device into the XGPro software and realized immediately it was Z80 code. How? Well, having worked with the Z80 for so many years you start to recognize that the first several bytes of almost every Z80 program start with very specific bytes. This is mostly to show @JKnightandKARR (https://savagechats.com/index.php?action=profile;u=4) how I decode Z80 assembly code.
Z80-ROM-Dump.jpg
Here is a screenshot of the buffer window. As you can see, the first byte is $31, which is the Z80 opcode for:
LD SP,<address>, where <address> is the start address of the stack pointer (SP). So, in this case, that would be:
LD SP, $A000
The next byte is $F3, which is the Z80 opcode for:
DI, which disables interrupts.
The next byte is $C3, which is the Z80 opcode for:
JP <address>, where <address> is the the value to be loaded into the program counter (jumped to). The next two bytes are the address, so:
JP $0100
You can see that the program jumps to $0100. That's because $0038 through $0066+ are interrupt vector addresses. At $0100 we see a byte value of CD, which is the Z80 opcode for CALL <address>, with the following two bytes being $19 and $01, the subroutine would be located at $0119.
Next we have $CD $8E $01, which is CALL $018E.
Continuing, this is what we have:
$0100 CALL $0119
$0102 CALL $018E
$0106 CALL $0326
$0109 CALL $OEF6
$010C LD A,($8E40)
$010F OR A
$0110 JP NZ,$0106
I will eventually disassemble the entire thing, but thus far we can see that the code does what almost every Z80 program does, which is set the stack pointer, disable interrupts, then start setting up hardware, which is most likely what the first few CALLs are doing. $0110 could be considered a "LOOP" for all intents and purposes, because it unconditionally jumps back to $0106, which would be the "DO" of the loop. In between it's making a couple of calls, the loading the A register with the contents of memory address $8E40, then activating the flags, until it gets a $00 in the A register, at which point the loop exits and the code continues.
It will be interesting to see exactly what is happening here. More to come.
Okay, I have attached the original binary file, and a disassembled version using DZ80 Version 2.0. For those who are curious. I have no idea what the code is for as it is from an obsolete industrial control unit. But you might find something interesting.
NOTE: Disassembled code often contains data blocks, which the disassembler will try to turn into op-codes. So you need to be able to tell what is happening as you go to make use of the code. I may come back later and point out any useful blocks I find. Much of what I followed seems to be initializing external hardware for which the disassembled source has no references for.
Since this code is from a piece of hardware for which we know NOTHING of the I/O map, we can assume IN / OUT instructions are talking to various external hardware, however, other blocks of code could be doing any number of things. Have a look at the following block of code.
0122 d30c out (0ch),a
0124 d34c out (4ch),a
0126 d35c out (5ch),a
0128 d36c out (6ch),a
012a 2f cpl
012b d31c out (1ch),a
012d d3f1 out (0f1h),a
012f 3ec0 ld a,0c0h
0131 d3f2 out (0f2h),a
0133 210080 ld hl,8000h
0136 01501e ld bc,1e50h
0139 af xor a
013a 77 ld (hl),a
013b 23 inc hl
013c 0b dec bc
013d 79 ld a,c
013e b0 or b
013f c23901 jp nz,0139h
0142 3e38 ld a,38h
0144 32048d ld (8d04h),a
0147 3e37 ld a,37h
0149 32058d ld (8d05h),a
014c cd373e call 3e37h
014f 21378d ld hl,8d37h
0152 060e ld b,0eh
0154 3eff ld a,0ffh
0156 77 ld (hl),a
0157 23 inc hl
0158 10fc djnz 0156h
015a 21248d ld hl,8d24h
015d 0605 ld b,05h
015f 3ef0 ld a,0f0h
0161 77 ld (hl),a
0162 23 inc hl
0163 10fc djnz 0161h
0165 212c8d ld hl,8d2ch
0168 0605 ld b,05h
016a 3ef0 ld a,0f0h
016c 77 ld (hl),a
016d 23 inc hl
016e 10fc djnz 016ch
0170 213f8e ld hl,8e3fh
0173 3601 ld (hl),01h
0175 213e8e ld hl,8e3eh
0178 3603 ld (hl),03h
017a 2100c0 ld hl,0c000h
017d 010020 ld bc,2000h
0180 af xor a
0181 77 ld (hl),a
0182 23 inc hl
0183 0b dec bc
0184 79 ld a,c
0185 b0 or b
0186 c28001 jp nz,0180h
0189 3eff ld a,0ffh
018b ed47 ld i,a
018d c9 ret
018e db3c in a,(3ch)
0190 e61f and 1fh
0192 47 ld b,a
0193 cdce01 call 01ceh
0196 ed56 im 1
0198 fb ei
This section is interesting to me because it seems that after sending the value of the A register to several outputs, the code is then, ostensibly, filling 7,760 bytes of RAM, starting at $8000 with $00, clearing it. But the block of code that does this has me baffled. Perhaps @granz or @MicroNut could offer some thoughts here. My OCD is causing me to get hung up here.
0133 210080 ld hl,8000h
0136 01501e ld bc,1e50h
0139 af xor a
013a 77 ld (hl),a
013b 23 inc hl
013c 0b dec bc
013d 79 ld a,c
013e b0 or b
013f c23901 jp nz,0139h
In this section, the HL pair is being loaded with $8000, which is most of my designs is the bottom of RAM. The BC pair is being loaded with $1E50 (7760 in decimal). Next there is an XOR A, which would have the effect of setting the A register to $00. Next we're loading the memory address at HL with $00, incrementing the HL registers, decrementing the BC registers, and then, here's where it gets weird in my mind...
XOR A is a faster way of doing a "LD A, $00", so that makes sense. Normally, the next instruction would be testing to see if the BC register has reach zero and to loop if not. I know that the "LD A, C" and "OR B" are used to detect when the BC registers reach zero, but I can't remember why it does it this way. Obviously, it keeps going until BC is zero, clearing those bytes.