Skip to content

Commit b223519

Browse files
committed
feat(Z80): track registers, generate xrefs
1 parent 5b3cba6 commit b223519

6 files changed

Lines changed: 252 additions & 31 deletions

File tree

z80/decoder/decoder.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,16 @@ bool z80_decode_op(const RDContext* ctx, RDInstruction* instr, usize idx,
6262

6363
op->kind = RD_OP_IMM;
6464
op->imm = (u64)v;
65+
op->size = sizeof(u8);
6566
return true;
6667
}
6768

6869
case Z80_OP_NN: {
6970
u16 v;
7071
if(!rd_read_le16(ctx, cur_address, &v)) return false;
7172

72-
bool is_branch = (res->instr.flow == RD_IF_JUMP ||
73-
res->instr.flow == RD_IF_JUMP_COND ||
74-
res->instr.flow == RD_IF_CALL ||
75-
res->instr.flow == RD_IF_CALL_COND);
76-
77-
if(is_branch) {
73+
if(z80_instr_is_branch(&res->instr) ||
74+
z80_instr_is_load_store(&res->instr)) {
7875
op->kind = RD_OP_ADDR;
7976
op->addr = (RDAddress)v;
8077
}
@@ -83,6 +80,7 @@ bool z80_decode_op(const RDContext* ctx, RDInstruction* instr, usize idx,
8380
op->imm = (u64)v;
8481
}
8582

83+
op->size = sizeof(u16);
8684
return true;
8785
}
8886

@@ -92,6 +90,7 @@ bool z80_decode_op(const RDContext* ctx, RDInstruction* instr, usize idx,
9290

9391
op->kind = Z80_USEROP_IND_N;
9492
op->imm = (u64)v;
93+
op->size = sizeof(u8);
9594
return true;
9695
}
9796

@@ -101,6 +100,7 @@ bool z80_decode_op(const RDContext* ctx, RDInstruction* instr, usize idx,
101100

102101
op->kind = Z80_USEROP_IND_NN;
103102
op->imm = (u64)v;
103+
op->size = sizeof(u16);
104104
return true;
105105
}
106106

z80/decoder/instructions.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,21 @@ static const char* const Z80_MNEMONICS[Z80_INSTR_COUNT] = {
6363
[Z80_INSTR_INDR] = "indr", [Z80_INSTR_OTDR] = "otdr",
6464
};
6565

66-
const char* z80_get_mnemonic(Z80InstructionId id) {
67-
return id < Z80_INSTR_COUNT ? Z80_MNEMONICS[id] : NULL;
66+
const char* z80_get_mnemonic(const RDInstruction* instr, RDProcessor* p) {
67+
RD_UNUSED(p);
68+
return instr->id < Z80_INSTR_COUNT ? Z80_MNEMONICS[instr->id] : NULL;
69+
}
70+
71+
bool z80_instr_is_load_store(const Z80Instruction* instr) {
72+
if(instr->id != Z80_INSTR_LD) return false;
73+
74+
// only SP destination counts, per policy
75+
return instr->operands[0] == Z80_OP_SP;
76+
}
77+
78+
bool z80_instr_is_branch(const Z80Instruction* instr) {
79+
return (instr->flow == RD_IF_JUMP || instr->flow == RD_IF_JUMP_COND ||
80+
instr->flow == RD_IF_CALL || instr->flow == RD_IF_CALL_COND);
6881
}
6982

7083
bool z80_find_instruction(RDContext* ctx, RDAddress address,

z80/decoder/instructions.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ static inline Z80Opcode z80_split(u8 b) {
2525
return ob;
2626
}
2727

28-
const char* z80_get_mnemonic(Z80InstructionId id);
28+
const char* z80_get_mnemonic(const RDInstruction* instr, RDProcessor* p);
29+
bool z80_instr_is_load_store(const Z80Instruction* instr);
30+
bool z80_instr_is_branch(const Z80Instruction* instr);
2931
bool z80_find_instruction(RDContext* ctx, RDAddress address,
3032
Z80InstructionResult* res);

z80/decoder/registers.c

Lines changed: 196 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,84 @@
11
#include "registers.h"
2+
#include "decoder.h"
3+
#include <string.h>
4+
5+
static Z80RegId _z80_get_register_id(const char* name) {
6+
if(!name) return Z80_REG_INVALID;
7+
8+
if(!strcmp(name, "a")) return Z80_REG_A;
9+
if(!strcmp(name, "b")) return Z80_REG_B;
10+
if(!strcmp(name, "c")) return Z80_REG_C;
11+
if(!strcmp(name, "d")) return Z80_REG_D;
12+
if(!strcmp(name, "e")) return Z80_REG_E;
13+
if(!strcmp(name, "h")) return Z80_REG_H;
14+
if(!strcmp(name, "l")) return Z80_REG_L;
15+
16+
if(!strcmp(name, "bc")) return Z80_REG_BC;
17+
if(!strcmp(name, "de")) return Z80_REG_DE;
18+
if(!strcmp(name, "hl")) return Z80_REG_HL;
19+
if(!strcmp(name, "sp")) return Z80_REG_SP;
20+
if(!strcmp(name, "af")) return Z80_REG_AF;
21+
22+
if(!strcmp(name, "i")) return Z80_REG_I;
23+
if(!strcmp(name, "r")) return Z80_REG_R;
24+
25+
if(!strcmp(name, "ix")) return Z80_REG_IX;
26+
if(!strcmp(name, "ixh")) return Z80_REG_IXH;
27+
if(!strcmp(name, "ixl")) return Z80_REG_IXL;
28+
if(!strcmp(name, "iy")) return Z80_REG_IY;
29+
if(!strcmp(name, "iyh")) return Z80_REG_IYH;
30+
if(!strcmp(name, "iyl")) return Z80_REG_IYL;
31+
32+
if(!strcmp(name, "bc'")) return Z80_REG_SHD_BC;
33+
if(!strcmp(name, "de'")) return Z80_REG_SHD_DE;
34+
if(!strcmp(name, "hl'")) return Z80_REG_SHD_HL;
35+
if(!strcmp(name, "af'")) return Z80_REG_SHD_AF;
36+
37+
return Z80_REG_INVALID;
38+
}
39+
40+
static u64 _z80_reg_width_mask(Z80RegId id) {
41+
switch(id) {
42+
case Z80_REG_A:
43+
case Z80_REG_B:
44+
case Z80_REG_C:
45+
case Z80_REG_D:
46+
case Z80_REG_E:
47+
case Z80_REG_H:
48+
case Z80_REG_L:
49+
case Z80_REG_IXH:
50+
case Z80_REG_IXL:
51+
case Z80_REG_IYH:
52+
case Z80_REG_IYL:
53+
case Z80_REG_I:
54+
case Z80_REG_R: return 0xFF; // 8-bit
55+
56+
default: return 0xFFFF; // BC, DE, HL, SP, AF, IX, IY, shadows: 16-bit
57+
}
58+
}
59+
60+
static Z80RegId _z80_canonical_reg(Z80RegId id) {
61+
switch(id) {
62+
case Z80_REG_B:
63+
case Z80_REG_C: return Z80_REG_BC;
64+
65+
case Z80_REG_D:
66+
case Z80_REG_E: return Z80_REG_DE;
67+
68+
case Z80_REG_H:
69+
case Z80_REG_L: return Z80_REG_HL;
70+
71+
case Z80_REG_IXH:
72+
case Z80_REG_IXL: return Z80_REG_IX;
73+
74+
case Z80_REG_IYH:
75+
case Z80_REG_IYL:
76+
return Z80_REG_IY;
77+
78+
// A, HL, BC, DE, SP, IX, IY, I, R, shadows: already canonical
79+
default: return id;
80+
}
81+
}
282

383
const char* z80_cc_name(Z80Condition cc) {
484
switch(cc) {
@@ -16,7 +96,9 @@ const char* z80_cc_name(Z80Condition cc) {
1696
return "???";
1797
}
1898

19-
const char* z80_reg_name(Z80RegId reg) {
99+
const char* z80_get_reg_name(RDReg reg, RDProcessor* p) {
100+
RD_UNUSED(p);
101+
20102
switch(reg) {
21103
case Z80_REG_I: return "i";
22104
case Z80_REG_R: return "r";
@@ -52,3 +134,116 @@ const char* z80_reg_name(Z80RegId reg) {
52134

53135
return NULL;
54136
}
137+
138+
bool z80_get_reg_mask(const char* name, RDRegMask* m, RDProcessor* p) {
139+
RD_UNUSED(p);
140+
141+
Z80RegId id = _z80_get_register_id(name);
142+
if(id == Z80_REG_INVALID) return false;
143+
144+
switch(id) {
145+
case Z80_REG_H:
146+
case Z80_REG_B:
147+
case Z80_REG_D:
148+
case Z80_REG_IXH:
149+
case Z80_REG_IYH:
150+
m->mask = 0xFF00;
151+
m->shift = 8;
152+
break;
153+
154+
case Z80_REG_L:
155+
case Z80_REG_C:
156+
case Z80_REG_E:
157+
case Z80_REG_IXL:
158+
case Z80_REG_IYL: m->mask = 0x00FF; break;
159+
160+
default: m->mask = RD_REGMASK_FULL; break;
161+
}
162+
163+
m->reg = _z80_canonical_reg(id);
164+
return true;
165+
}
166+
167+
void z80_track_regs(RDContext* ctx, const RDInstruction* instr) {
168+
const RDOperand* dst = &instr->operands[0];
169+
const RDOperand* src = &instr->operands[1];
170+
171+
switch(instr->id) {
172+
case Z80_INSTR_LD: {
173+
if(dst->kind != RD_OP_REG) break;
174+
175+
switch(src->kind) {
176+
case RD_OP_IMM:
177+
rd_set_regval_id(ctx, dst->reg, src->imm);
178+
break;
179+
180+
case RD_OP_REG: {
181+
RDRegValue v;
182+
if(rd_get_regval_id(ctx, src->reg, &v))
183+
rd_set_regval_id(ctx, dst->reg, v);
184+
else
185+
rd_del_regval_id(ctx, dst->reg);
186+
break;
187+
}
188+
189+
// memory-sourced (LD r,(HL) / LD rp,(nn) / etc.)
190+
// unknown statically
191+
default: rd_del_regval_id(ctx, dst->reg); break;
192+
}
193+
break;
194+
}
195+
196+
case Z80_INSTR_INC:
197+
case Z80_INSTR_DEC: {
198+
if(dst->kind != RD_OP_REG) break;
199+
RDRegValue v;
200+
if(rd_get_regval_id(ctx, dst->reg, &v)) {
201+
v += (instr->id == Z80_INSTR_INC) ? 1 : -1;
202+
rd_set_regval_id(ctx, dst->reg,
203+
v & _z80_reg_width_mask((Z80RegId)dst->reg));
204+
}
205+
break;
206+
}
207+
208+
// ADD HL,rp only, ADC/SBC need carry-in, not tracked
209+
case Z80_INSTR_ADD: {
210+
if(dst->kind != RD_OP_REG || src->kind != RD_OP_REG) break;
211+
212+
RDRegValue a, b;
213+
if(rd_get_regval_id(ctx, dst->reg, &a) &&
214+
rd_get_regval_id(ctx, src->reg, &b))
215+
rd_set_regval_id(ctx, dst->reg, (a + b) & 0xFFFF);
216+
else
217+
rd_del_regval_id(ctx, dst->reg);
218+
break;
219+
}
220+
221+
// ADC/SBC HL,rp: carry flag isn't tracked
222+
case Z80_INSTR_ADC:
223+
case Z80_INSTR_SBC:
224+
// POP value comes from stack, (same conservative call as x86_track_pop)
225+
case Z80_INSTR_POP:
226+
// EX DE,HL / EX (SP),HL swap/stack-load,
227+
// invalidate rather than attempt to swap tracked state
228+
case Z80_INSTR_EX: {
229+
if(dst->kind == RD_OP_REG) rd_del_regval_id(ctx, dst->reg);
230+
if(src->kind == RD_OP_REG) rd_del_regval_id(ctx, src->reg);
231+
break;
232+
}
233+
234+
default: break;
235+
}
236+
}
237+
238+
void z80_track_derefs(RDContext* ctx, const RDInstruction* instr) {
239+
for(int i = 0; i < 2; i++) {
240+
const RDOperand* op = &instr->operands[i];
241+
if(op->kind != Z80_USEROP_IND_REG) continue;
242+
243+
RDRegValue v;
244+
if(rd_get_regval_id(ctx, op->reg, &v)) {
245+
rd_add_xref(ctx, instr->address, v,
246+
(i == 0) ? RD_DR_WRITE : RD_DR_READ);
247+
}
248+
}
249+
}

z80/decoder/registers.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,7 @@ typedef enum {
2929
} Z80Condition;
3030

3131
const char* z80_cc_name(Z80Condition cc);
32-
const char* z80_reg_name(Z80RegId reg);
32+
const char* z80_get_reg_name(RDReg reg, RDProcessor* p);
33+
bool z80_get_reg_mask(const char* name, RDRegMask* m, RDProcessor* p);
34+
void z80_track_regs(RDContext* ctx, const RDInstruction* instr);
35+
void z80_track_derefs(RDContext* ctx, const RDInstruction* instr);

z80/plugin.c

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,6 @@
22
#include "decoder/registers.h"
33
#include <redasm/redasm.h>
44

5-
static const char* _z80_get_mnemonic(const RDInstruction* instr,
6-
RDProcessor* p) {
7-
RD_UNUSED(p);
8-
return z80_get_mnemonic(instr->id);
9-
}
10-
11-
static const char* _z80_get_reg_name(RDReg r, RDProcessor* p) {
12-
RD_UNUSED(p);
13-
return z80_reg_name((Z80RegId)r);
14-
}
15-
165
static void _z80_decode(RDContext* ctx, RDInstruction* instr, RDProcessor* p) {
176
RD_UNUSED(p);
187

@@ -32,12 +21,21 @@ static void _z80_emulate(RDContext* ctx, const RDInstruction* instr,
3221
RDProcessor* p) {
3322
RD_UNUSED(p);
3423

35-
if(rd_instr_is_branch(instr)) { // jump OR call, gates everything below
36-
RDXRefType kind = rd_instr_is_call(instr) ? RD_CR_CALL : RD_CR_JUMP;
37-
38-
for(int i = 0; i < Z80_MAX_OPERANDS; i++) {
39-
if(instr->operands[i].kind == RD_OP_ADDR)
40-
rd_add_xref(ctx, instr->address, instr->operands[i].addr, kind);
24+
z80_track_regs(ctx, instr);
25+
z80_track_derefs(ctx, instr);
26+
27+
rd_foreach_operand(i, op, instr) {
28+
if(op->kind == RD_OP_ADDR) {
29+
if(rd_instr_is_call(instr))
30+
rd_add_xref(ctx, instr->address, op->addr, RD_CR_CALL);
31+
else if(rd_instr_is_jump(instr))
32+
rd_add_xref(ctx, instr->address, op->addr, RD_CR_JUMP);
33+
else
34+
rd_add_xref(ctx, instr->address, op->addr, RD_DR_ADDRESS);
35+
}
36+
else if(op->kind == Z80_USEROP_IND_NN) {
37+
rd_add_xref(ctx, instr->address, op->imm,
38+
(i == 0) ? RD_DR_WRITE : RD_DR_READ);
4139
}
4240
}
4341

@@ -51,6 +49,15 @@ static bool _z80_render_operand(RDRenderer* r, const RDInstruction* instr,
5149
const RDOperand* op = &instr->operands[idx];
5250

5351
switch(op->kind) {
52+
case RD_OP_IMM: {
53+
if(op->size == sizeof(u16)) {
54+
rd_renderer_loc(r, (RDAddress)op->imm, 2, RD_NUM_DEFAULT);
55+
return true;
56+
}
57+
58+
return false;
59+
}
60+
5461
case RD_OP_DISPL: {
5562
rd_renderer_norm(r, "(");
5663
rd_renderer_reg(r, op->displ.base);
@@ -74,7 +81,7 @@ static bool _z80_render_operand(RDRenderer* r, const RDInstruction* instr,
7481

7582
case Z80_USEROP_IND_NN: {
7683
rd_renderer_norm(r, "(");
77-
rd_renderer_num(r, (i64)op->imm, 16, 2, RD_NUM_DEFAULT);
84+
rd_renderer_loc(r, (i64)op->imm, 0, RD_NUM_DEFAULT);
7885
rd_renderer_norm(r, ")");
7986
return true;
8087
}
@@ -111,8 +118,9 @@ static const RDProcessorPlugin Z80 = {
111118
.id = "z80",
112119
.name = "Zilog 80",
113120
.ptr_size = sizeof(u16),
114-
.get_mnemonic = _z80_get_mnemonic,
115-
.get_reg_name = _z80_get_reg_name,
121+
.get_mnemonic = z80_get_mnemonic,
122+
.get_reg_name = z80_get_reg_name,
123+
.get_reg_mask = z80_get_reg_mask,
116124
.decode = _z80_decode,
117125
.emulate = _z80_emulate,
118126
.render_operand = _z80_render_operand,

0 commit comments

Comments
 (0)