Z-80 Instruction Lengths
In order to handle breakpoints functionality of step, next, and continue, it is necessary to know 1) how long the current instruction is and 2) whether and how it branches.
So I coded up a quick function that takes a pointer to a location and returns 1) 1-4 in the lower 3 bits with instruction length, 2 bits with the branch type (absolute, relative, return, and jp (hl)), one bit to indicate the branch is conditional, and one bit for instructions that always branch away and will never get to the next instruction.
Note that the ‘rst’ and ‘halt’ commands don’t have branch information. Anyone wanting to try to use this code will want to add that if they need it.
Here it is:
1 // The following are found in pBranchInfo
2 #define BRANCH_CALL (0x08)
3 // relative to pc + (int8_t)(opcode+1) ALWAYS or COND
4 #define BRANCH_REL (0x20)
5 // absolute to (void*)(opcode+1) ALWAYS or COND
6 #define BRANCH_ABS (0x10)
7 // return to (void*)(sp-2) ALWAYS or COND
8 #define BRANCH_RET (0x30)
9 // to (hl) // ALWAYS but never COND
10 #define BRANCH_HL (0x00)
11 // conditional--bp necessary after instruction
12 #define BRANCH_COND (0x40)
13 // always--will never got to pc+length--certain to branch
14 #define BRANCH_ALWAYS (0x80)
15
16
17 struct InstLen
18 {
19 uint8_t mask;
20 uint8_t value;
21 uint8_t data;
22 };
23
24 static const struct InstLen scan00[] =
25 {
26 // ld rr, **
27 {
28 0xcf, 0x01, 3
29 },
30 // ld (hl)/a <-> **
31 {
32 0xe7, 0x22, 3
33 },
34 // ld r, *
35 {
36 0xc7, 0x06, 2
37 },
38 // jr f, *
39 {
40 0xe7, 0x20, 2 | BRANCH_REL | BRANCH_COND
41 },
42 // djnz *
43 {
44 0xff, 0x10, 2 | BRANCH_REL | BRANCH_COND
45 },
46 // jr *
47 {
48 0xff, 0x18, 2 | BRANCH_REL | BRANCH_ALWAYS
49 },
50 };
51
52 static const struct InstLen scan11[] =
53 {
54 // ret f (not detault b/c branch)
55 {
56 0xc7, 0xc0, 1 | BRANCH_RET | BRANCH_COND
57 },
58 // ret (not detault b/c branch)
59 {
60 0xff, 0xc9, 1 | BRANCH_RET | BRANCH_ALWAYS
61 },
62 // jp (hl)
63 {
64 0xff, 0xe9, 1 | BRANCH_HL | BRANCH_ALWAYS
65 },
66 // jp f, **
67 {
68 0xc7, 0xc2, 3 | BRANCH_ABS | BRANCH_COND
69 },
70 // call f, **
71 {
72 0xc7, 0xc4, 3 | BRANCH_ABS | BRANCH_COND | BRANCH_CALL
73 },
74 // jp **
75 {
76 0xff, 0xc3, 3 | BRANCH_ABS | BRANCH_ALWAYS
77 },
78 // call **
79 {
80 0xff, 0xcd, 3 | BRANCH_ABS | BRANCH_CALL
81 },
82 // arith *, *
83 {
84 0xc7, 0xc6, 2
85 },
86 // out/in(*), a
87 {
88 0xf7, 0xd3, 2
89 },
90 // bit op
91 {
92 0xff, 0xcb, 2
93 }
94 };
95
96 static const struct InstLen scanED[] =
97 {
98 // ld (rr) <-> **
99 {
100 0xc7, 0x43, 4
101 }
102 };
103
104 static const struct InstLen scanDDFD[] =
105 {
106 // ld ix, **
107 {
108 0xff, 0x21, 4
109 },
110 // ld ix <-> (**)
111 {
112 0xf7, 0x22, 4
113 },
114 // ld (ix+*), * order dep
115 {
116 0xff, 0x36, 4
117 },
118 // various
119 {
120 0x07, 0x06, 3
121 },
122 // ld (ix+*), r
123 {
124 0xb8, 0x30, 3
125 },
126 // bit ops
127 {
128 0xff, 0xcb, 3
129 },
130 };
131
132 // Returns the length in the first 3 bits and the BRANCH_* bits in the upper 5 bits
133 uint8_t z80InstructionLength(uint8_t* start)
134 {
135 uint8_t opcode = *start++;
136 const struct InstLen* scanner;
137 int i;
138 uint8_t length;
139 uint8_t instructionLength = 1;
140
141 if ((opcode & 0x80) == 0)
142 {
143 if ((opcode & 0x40) == 0)
144 {
145 scanner = scan00;
146 length = sizeof(scan00) / sizeof(scan00[0]);
147 }
148 else
149 {
150 return 1;
151 }
152 }
153 else
154 {
155 if ((opcode & 0x40) == 0)
156 {
157 return 1;
158 }
159 else
160 {
161 if ((opcode & 0xdf) == 0xdd)
162 {
163 scanner = scanDDFD;
164 length = sizeof(scanDDFD) / sizeof(scanDDFD[0]);
165 instructionLength = 2;
166 opcode = *start++;
167 }
168 else if (opcode == 0xed)
169 {
170 scanner = scanED;
171 length = sizeof(scanED) / sizeof(scanED[0]);
172 instructionLength = 2;
173 opcode = *start++;
174 }
175 else
176 {
177 scanner = scan11;
178 length = sizeof(scan11) / sizeof(scan11[0]);
179 }
180 }
181 }
182 for (i = 0; i < length; i++)
183 {
184 const struct InstLen* scan = &scanner[i];
185 if ((scan->mask & opcode) == scan->value)
186 {
187 instructionLength = scan->data;
188 break;
189 }
190 }
191 return instructionLength;
192 }