EEVblog Electronics Community Forum
Products => Computers => Vintage Computing => Topic started by: fabiodl on January 04, 2021, 03:51:04 am
-
In a Z80 based system I am trying to develop a debugger that interrupts the program flow by inserting rst instructions on the bus. I want to do it at the beginning of an instruction, i.e. I do not want to modify later parts of multi-byte opcodes / parameters of the opcodes. Unless I solder additional wires (which I'd like to avoid) I have no access to M1.
My idea is to detect writes, and replace the data on the bus on the fist read after a write.
This holds correct only if there is no instruction that performs a read after a write. If I am not mistaken such instruction does not exist, but I may have overlooked some atypical case.
Is there any (possibly undocumented) instruction that performs a memory read after a memory write, or can I be sure that no such instruction exists?
-
Block transfer?
-
Block transfer?
Could you elaborate? It's been a long time since I programmed a Z80, but I'm unaware of block transfers on those. Perhaps later upward compatible eZ80 implemented such? The original Z80 had some loop construct (DJNZ, iirc), but no DMA or "string" operations.
-
Block transfer?
Could you elaborate? It's been a long time since I programmed a Z80, but I'm unaware of block transfers on those. Perhaps later upward compatible eZ80 implemented such? The original Z80 had some loop construct (DJNZ, iirc), but no DMA or "string" operations.
Yes, it had LDI, LDIR, LDD and LDDR.
-
.
-
Ah, I happily forgot those. Thanks (i guess) for reminding me. :-DD
-
nice point. I believe the practical way they are done is by keeping the PC fixed. I believe they re-read the opcode any loop (at least on emulators, I have to try it on the real hw!) This means that if you steal the bus after, a single cycle is done you do not input "garbage" into the system.
-
LDDR and LDIR, if my memory serves me well, copy blocks of memory from a place to another. So they must perform write after read, after write...
EDIT: it is curious how the (brain) memory works. I haven't touched a Z80 since ~30 years, but I still remember that the machine code for the LDIR instruction is 0xED, 0xB0.
There's also INIR oad OUTIR in which you put a memory address in a 16-bit register, a count in another 16-bit register, and then the IN or OUT instruction would be repeated at successive memory addresses. This could be used to rapidly read or write a series of bytes from/to an I/O device. (I think there were also decrement versions that would go through memory in reverse order, INDR and OUTDR.) I used these in a SASI disk adaptor and in some other special devices that needed a string of bytes transferred. You could get at least half a megabyte/second data transfer, I think.
Jon
-
I tried out every single instruction on the https://github.com/EisernSchild/t80 and there' s no such instruction.
Next step, is checking the real hw.
-
.
-
I tried out every single instruction on the https://github.com/EisernSchild/t80 and there' s no such instruction.
Next step, is checking the real hw.
Not sure about what do you mean with this. All LDI/LDD/LDIR/LDDR and INIR/INDR/OTIR/OTDR do exist (as CPIR/CPDR & DJNZ). I've not used the t80 IP, but on its VHDL sources you can find all of them.
Yeah. I dunno avout T80 and have never used it, but those instructions are definitely part of the Z80 instruction set and were available on all Z80's.
Just check any Z80 reference manual.
-
I tried out every single instruction on the https://github.com/EisernSchild/t80 and there' s no such instruction.
Next step, is checking the real hw.
Yes, I used a hackified assembler, and the mnemonics were 8080-based, not the Zilog-approved ones. I think the op-codes were :
ED B2 for INIR and ED B3 for OUTIR, I got this from pp 62-63 of the Z-80 manual from Zilog.
Jon
-
nice point. I believe the practical way they are done is by keeping the PC fixed. I believe they re-read the opcode any loop (at least on emulators, I have to try it on the real hw!) This means that if you steal the bus after, a single cycle is done you do not input "garbage" into the system.
At least in the case of OTIR the opcodes are repeatedly fetched with M1 cycles while the loop count is being decremented.
Here is an actual example logic analyzer state trace of a real Z80 loading the B register with the value 0x0B and then executing an OTIR instruction 11 times in a loop, where the OTIR 0xED/0xB3 opcodes are fetched again every time.
(Columns are logic analyzer state #, address bus value (hex), data bus value (hex), Inverse Assembler decode).
141 0146 7E LD A,[HL] OPCODE FETCH
142 020F 0B 0B memory read MEM READ
143 0147 47 LD B,A OPCODE FETCH
144 0148 E6 AND FF OPCODE FETCH
145 0149 FF FF memory read MEM READ
146 014A 28 JR Z,0120 OPCODE FETCH
147 014B D4 D4 memory read MEM READ
148 014C 54 LD D,H OPCODE FETCH
149 014D 5D LD E,L OPCODE FETCH
150 014E 23 INC HL OPCODE FETCH
151 014F 0E LD C,05 OPCODE FETCH
152 0150 05 05 memory read MEM READ
153 0151 ED OTIR OPCODE FETCH
154 0152 B3 B3 opcode fetch OPCODE FETCH
155 0210 02 02 memory read MEM READ
156 0A05 02 02 i/o write I/O WRITE
157 0151 ED OTIR OPCODE FETCH
158 0152 B3 B3 opcode fetch OPCODE FETCH
159 0211 10 10 memory read MEM READ
160 0905 10 10 i/o write I/O WRITE
161 0151 ED OTIR OPCODE FETCH
162 0152 B3 B3 opcode fetch OPCODE FETCH
163 0212 14 14 memory read MEM READ
164 0805 14 14 i/o write I/O WRITE
165 0151 ED OTIR OPCODE FETCH
166 0152 B3 B3 opcode fetch OPCODE FETCH
167 0213 4C 4C memory read MEM READ
168 0705 4C 4C i/o write I/O WRITE
169 0151 ED OTIR OPCODE FETCH
170 0152 B3 B3 opcode fetch OPCODE FETCH
171 0214 03 03 memory read MEM READ
172 0605 03 03 i/o write I/O WRITE
173 0151 ED OTIR OPCODE FETCH
174 0152 B3 B3 opcode fetch OPCODE FETCH
175 0215 C1 C1 memory read MEM READ
176 0505 C1 C1 i/o write I/O WRITE
177 0151 ED OTIR OPCODE FETCH
178 0152 B3 B3 opcode fetch OPCODE FETCH
179 0216 05 05 memory read MEM READ
180 0405 05 05 i/o write I/O WRITE
181 0151 ED OTIR OPCODE FETCH
182 0152 B3 B3 opcode fetch OPCODE FETCH
183 0217 80 80 memory read MEM READ
184 0305 80 80 i/o write I/O WRITE
185 0151 ED OTIR OPCODE FETCH
186 0152 B3 B3 opcode fetch OPCODE FETCH
187 0218 11 11 memory read MEM READ
188 0205 11 11 i/o write I/O WRITE
189 0151 ED OTIR OPCODE FETCH
190 0152 B3 B3 opcode fetch OPCODE FETCH
191 0219 15 15 memory read MEM READ
192 0105 15 15 i/o write I/O WRITE
193 0151 ED OTIR OPCODE FETCH
194 0152 B3 B3 opcode fetch OPCODE FETCH
195 021A 20 20 memory read MEM READ
196 0005 20 20 i/o write I/O WRITE
197 0153 62 LD H,D OPCODE FETCH
198 0154 6B LD L,E OPCODE FETCH
199 0155 46 LD B,[HL] OPCODE FETCH
200 020F 0B 0B memory read MEM READ
201 0156 23 INC HL OPCODE FETCH
202 0157 0E LD C,07 OPCODE FETCH
203 0158 07 07 memory read MEM READ
204 0159 ED OTIR OPCODE FETCH
205 015A B3 B3 opcode fetch OPCODE FETCH
206 0210 02 02 memory read MEM READ
207 0A07 02 02 i/o write I/O WRITE
208 0159 ED OTIR OPCODE FETCH
209 015A B3 B3 opcode fetch OPCODE FETCH
210 0211 10 10 memory read MEM READ
211 0907 10 10 i/o write I/O WRITE
212 0159 ED OTIR OPCODE FETCH
213 015A B3 B3 opcode fetch OPCODE FETCH
214 0212 14 14 memory read MEM READ
215 0807 14 14 i/o write I/O WRITE
216 0159 ED OTIR OPCODE FETCH
217 015A B3 B3 opcode fetch OPCODE FETCH
218 0213 4C 4C memory read MEM READ
219 0707 4C 4C i/o write I/O WRITE
220 0159 ED OTIR OPCODE FETCH
221 015A B3 B3 opcode fetch OPCODE FETCH
222 0214 03 03 memory read MEM READ
223 0607 03 03 i/o write I/O WRITE
224 0159 ED OTIR OPCODE FETCH
225 015A B3 B3 opcode fetch OPCODE FETCH
226 0215 C1 C1 memory read MEM READ
227 0507 C1 C1 i/o write I/O WRITE
228 0159 ED OTIR OPCODE FETCH
229 015A B3 B3 opcode fetch OPCODE FETCH
230 0216 05 05 memory read MEM READ
231 0407 05 05 i/o write I/O WRITE
232 0159 ED OTIR OPCODE FETCH
233 015A B3 B3 opcode fetch OPCODE FETCH
234 0217 80 80 memory read MEM READ
235 0307 80 80 i/o write I/O WRITE
236 0159 ED OTIR OPCODE FETCH
237 015A B3 B3 opcode fetch OPCODE FETCH
238 0218 11 11 memory read MEM READ
239 0207 11 11 i/o write I/O WRITE
240 0159 ED OTIR OPCODE FETCH
241 015A B3 B3 opcode fetch OPCODE FETCH
242 0219 15 15 memory read MEM READ
243 0107 15 15 i/o write I/O WRITE
244 0159 ED OTIR OPCODE FETCH
245 015A B3 B3 opcode fetch OPCODE FETCH
246 021A 20 20 memory read MEM READ
247 0007 20 20 i/o write I/O WRITE
-
Thank you very much.
I wrote a program that tests all opcodes (including the undocumented ones) and record the execution on a Z84C0010AEG
All the observed patterns are the following
[('rd',),
('rd', 'rd'),
('rd', 'rd', 'rd'),
('rd', 'rd', 'rd', 'rd'),
('rd', 'rd', 'rd', 'rd', 'rd'),
('rd', 'rd', 'rd', 'rd', 'rd', 'rd'),
('rd', 'rd', 'rd', 'rd', 'rd', 'wr'),
('rd', 'rd', 'rd', 'rd', 'wr'),
('rd', 'rd', 'rd', 'rd', 'wr', 'wr'),
('rd', 'rd', 'rd', 'wr'),
('rd', 'rd', 'rd', 'wr', 'wr'),
('rd', 'rd', 'wr'),
('rd', 'rd', 'wr', 'wr'),
('rd', 'wr'),
('rd', 'wr', 'wr')]
I attach the list of the instructions associated to each pattern
-
nice point. I believe the practical way they are done is by keeping the PC fixed. I believe they re-read the opcode any loop (at least on emulators, I have to try it on the real hw!) This means that if you steal the bus after, a single cycle is done you do not input "garbage" into the system.
This is what I recall, as well, and this is what makes them interruptible - or more specifically, restartable.
Normally, for things like hardware debugging NMI is (was) commonly used. But this of course assumes it's not used for something else, and the firmware can be changed to handle it (and maybe also differentiate between sources of it if it's already used for something other than debugging).