Rizin
unix-like reverse engineering framework and cli tools
reflines.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
2 // SPDX-FileCopyrightText: 2009-2020 nibble <nibble.ds@gmail.com>
3 // SPDX-License-Identifier: LGPL-3.0-only
4 
5 #include <rz_core.h>
6 #include <rz_util.h>
7 #include <rz_cons.h>
8 
9 #define mid_down_refline(a, r) ((r)->from > (r)->to && (a) < (r)->from && (a) > (r)->to)
10 #define mid_up_refline(a, r) ((r)->from < (r)->to && (a) > (r)->from && (a) < (r)->to)
11 #define mid_refline(a, r) (mid_down_refline(a, r) || mid_up_refline(a, r))
12 #define in_refline(a, r) (mid_refline(a, r) || (a) == (r)->from || (a) == (r)->to)
13 
14 typedef struct refline_end {
15  int val;
16  bool is_from;
19 
20 static int cmp_asc(const struct refline_end *a, const struct refline_end *b) {
21  return (a->val > b->val) - (a->val < b->val);
22 }
23 
25  return (a->level < b->level) - (a->level > b->level);
26 }
27 
28 static ReflineEnd *refline_end_new(ut64 val, bool is_from, RzAnalysisRefline *ref) {
29  ReflineEnd *re = RZ_NEW0(struct refline_end);
30  if (!re) {
31  return NULL;
32  }
33  re->val = val;
34  re->is_from = is_from;
35  re->r = ref;
36  return re;
37 }
38 
39 static bool add_refline(RzList *list, RzList *sten, ut64 addr, ut64 to, int *idx) {
40  ReflineEnd *re1, *re2;
42  if (!item) {
43  return false;
44  }
45  item->from = addr;
46  item->to = to;
47  item->index = *idx;
48  item->level = -1;
49  item->direction = (to > addr) ? 1 : -1;
50  *idx += 1;
51  rz_list_append(list, item);
52 
53  re1 = refline_end_new(item->from, true, item);
54  if (!re1) {
55  free(item);
56  return false;
57  }
59 
60  re2 = refline_end_new(item->to, false, item);
61  if (!re2) {
62  free(re1);
63  free(item);
64  return false;
65  }
67  return true;
68 }
69 
71  free(rl);
72 }
73 
74 /* returns a list of RzAnalysisRefline for the code present in the buffer buf, of
75  * length len. A RzAnalysisRefline exists from address A to address B if a jmp,
76  * conditional jmp or call instruction exists at address A and it targets
77  * address B.
78  *
79  * nlines - max number of lines of code to consider
80  * linesout - true if you want to display lines that go outside of the scope [addr;addr+len)
81  * linescall - true if you want to display call lines */
82 RZ_API RzList /*<RzAnalysisRefline *>*/ *rz_analysis_reflines_get(RzAnalysis *analysis, ut64 addr, const ut8 *buf, ut64 len, int nlines, int linesout, int linescall) {
83  RzList *list, *sten;
86  struct refline_end *el;
87  const ut8 *ptr = buf;
88  const ut8 *end = buf + len;
89  ut8 *free_levels;
90  int sz = 0, count = 0;
91  ut64 opc = addr;
92 
93  memset(&op, 0, sizeof(op));
94  /*
95  * 1) find all reflines
96  * 2) sort "from"s and "to"s in a list
97  * 3) traverse the list to find the minimum available level for each refline
98  * * create a sorted list with available levels.
99  * * when we encounter a previously unseen "from" or "to" of a
100  * refline, we occupy the lowest level available for it.
101  * * when we encounter the "from" or "to" of an already seen
102  * refline, we free that level.
103  */
104 
106  if (!list) {
107  return NULL;
108  }
109  sten = rz_list_newf((RzListFree)free);
110  if (!sten) {
111  goto list_err;
112  }
114  /* analyze code block */
115  while (ptr < end && !rz_cons_is_breaked()) {
116  if (nlines != -1) {
117  if (!nlines) {
118  break;
119  }
120  nlines--;
121  }
122  if (analysis->maxreflines && count > analysis->maxreflines) {
123  break;
124  }
125  addr += sz;
126  {
127  RzPVector *metas = rz_meta_get_all_at(analysis, addr);
128  if (metas) {
129  void **it;
130  ut64 skip = 0;
131  rz_pvector_foreach (metas, it) {
132  RzIntervalNode *node = *it;
133  RzAnalysisMetaItem *meta = node->data;
134  switch (meta->type) {
135  case RZ_META_TYPE_DATA:
136  case RZ_META_TYPE_STRING:
137  case RZ_META_TYPE_HIDE:
138  case RZ_META_TYPE_FORMAT:
139  case RZ_META_TYPE_MAGIC:
140  skip = rz_meta_node_size(node);
141  goto do_skip;
142  default:
143  break;
144  }
145  }
146  do_skip:
148  if (skip) {
149  ptr += skip;
150  addr += skip;
151  goto __next;
152  }
153  }
154  }
155  if (!analysis->iob.is_valid_offset(analysis->iob.io, addr, RZ_PERM_X)) {
156  const int size = 4;
157  ptr += size;
158  addr += size;
159  goto __next;
160  }
161 
162  // This can segfault if opcode length and buffer check fails
164  rz_analysis_op(analysis, &op, addr, ptr, (int)(end - ptr), RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_HINT);
165  sz = op.size;
166  if (sz <= 0) {
167  sz = 1;
168  goto __next;
169  }
170 
171  /* store data */
172  switch (op.type) {
174  if (!linescall) {
175  break;
176  }
177  // fallthrough
180  if ((!linesout && (op.jump > opc + len || op.jump < opc)) || !op.jump) {
181  break;
182  }
183  if (!add_refline(list, sten, addr, op.jump, &count)) {
185  goto sten_err;
186  }
187  // add false branch in case its set and its not a call, useful for bf, maybe others
188  if (!op.delay && op.fail != UT64_MAX && op.fail != addr + op.size) {
189  if (!add_refline(list, sten, addr, op.fail, &count)) {
191  goto sten_err;
192  }
193  }
194  break;
196  RzAnalysisCaseOp *caseop;
197  RzListIter *iter;
198 
199  // add caseops
200  if (!op.switch_op) {
201  break;
202  }
203  rz_list_foreach (op.switch_op->cases, iter, caseop) {
204  if (!linesout && (op.jump > opc + len || op.jump < opc)) {
205  goto __next;
206  }
207  if (!add_refline(list, sten, op.switch_op->addr, caseop->jump, &count)) {
209  goto sten_err;
210  }
211  }
212  break;
213  }
214  }
215  __next:
216  ptr += sz;
217  }
220 
221  free_levels = RZ_NEWS0(ut8, rz_list_length(list) + 1);
222  if (!free_levels) {
223  goto sten_err;
224  }
225  int min = 0;
226 
227  rz_list_foreach (sten, iter, el) {
228  if ((el->is_from && el->r->level == -1) || (!el->is_from && el->r->level == -1)) {
229  el->r->level = min + 1;
230  free_levels[min] = 1;
231  if (min < 0) {
232  min = 0;
233  }
234  while (free_levels[++min] == 1) {
235  ;
236  }
237  } else {
238  free_levels[el->r->level - 1] = 0;
239  if (min > el->r->level - 1) {
240  min = el->r->level - 1;
241  }
242  }
243  }
244 
245  /* XXX: the algorithm can be improved. We can calculate the set of
246  * reflines used in each interval of addresses and store them.
247  * Considering rz_analysis_reflines_str is always called with increasing
248  * addresses, we can just traverse linearly the list of intervals to
249  * know which reflines need to be drawn for each address. In this way,
250  * we don't need to traverse again and again the reflines for each call
251  * to rz_analysis_reflines_str, but we can reuse the data already
252  * calculated. Those data will be quickly available because the
253  * intervals will be sorted and the addresses to consider are always
254  * increasing. */
255  free(free_levels);
256  rz_list_free(sten);
257  return list;
258 
259 sten_err:
260 list_err:
261  rz_list_free(sten);
263  return NULL;
264 }
265 
266 RZ_API int rz_analysis_reflines_middle(RzAnalysis *a, RzList /*<RzAnalysisRefline *>*/ *list, ut64 addr, int len) {
267  if (a && list) {
268  RzAnalysisRefline *ref;
269  RzListIter *iter;
270  rz_list_foreach (list, iter, ref) {
271  if ((ref->to > addr) && (ref->to < addr + len)) {
272  return true;
273  }
274  }
275  }
276  return false;
277 }
278 
279 static const char *get_corner_char(RzAnalysisRefline *ref, ut64 addr, bool is_middle_before) {
280  if (ref->from == ref->to) {
281  return "@";
282  }
283  if (addr == ref->to) {
284  if (is_middle_before) {
285  return (ref->from > ref->to) ? " " : "|";
286  }
287  return (ref->from > ref->to) ? "." : "`";
288  }
289  if (addr == ref->from) {
290  if (is_middle_before) {
291  return (ref->from > ref->to) ? "|" : " ";
292  }
293  return (ref->from > ref->to) ? "`" : ",";
294  }
295  return "";
296 }
297 
298 static void add_spaces(RzBuffer *b, int level, int pos, bool wide) {
299  if (pos != -1) {
300  if (wide) {
301  pos *= 2;
302  level *= 2;
303  }
304  if (pos > level + 1) {
305  const char *pd = rz_str_pad(' ', pos - level - 1);
306  rz_buf_append_string(b, pd);
307  }
308  }
309 }
310 
311 static void fill_level(RzBuffer *b, int pos, char ch, RzAnalysisRefline *r, bool wide) {
312  int sz = r->level;
313  if (wide) {
314  sz *= 2;
315  }
316  const char *pd = rz_str_pad(ch, sz - 1);
317  if (pos == -1) {
318  rz_buf_append_string(b, pd);
319  } else {
320  int pdlen = strlen(pd);
321  if (pdlen > 0) {
322  rz_buf_write_at(b, pos, (const ut8 *)pd, pdlen);
323  }
324  }
325 }
326 
327 static inline bool refline_kept(RzAnalysisRefline *ref, bool middle_after, ut64 addr) {
328  if (middle_after) {
329  if (ref->direction < 0) {
330  if (ref->from == addr) {
331  return false;
332  }
333  } else {
334  if (ref->to == addr) {
335  return false;
336  }
337  }
338  }
339  return true;
340 }
341 
342 // TODO: move into another file
343 // TODO: this is TOO SLOW. do not iterate over all reflines
345  RzCore *core = _core;
346  RzCons *cons = core->cons;
347  RzAnalysis *analysis = core->analysis;
348  RzBuffer *b;
349  RzBuffer *c;
350  RzListIter *iter;
351  RzAnalysisRefline *ref;
352  int l;
353  bool wide = opts & RZ_ANALYSIS_REFLINE_TYPE_WIDE;
354  int dir = 0, pos = -1, max_level = -1;
355  bool middle_before = opts & RZ_ANALYSIS_REFLINE_TYPE_MIDDLE_BEFORE;
356  bool middle_after = opts & RZ_ANALYSIS_REFLINE_TYPE_MIDDLE_AFTER;
357  char *str = NULL;
358  char *col_str = NULL;
359 
360  rz_return_val_if_fail(cons && analysis && analysis->reflines, NULL);
361 
362  RzList *lvls = rz_list_new();
363  if (!lvls) {
364  return NULL;
365  }
366  rz_list_foreach (analysis->reflines, iter, ref) {
367  if (core->cons && core->cons->context->breaked) {
368  rz_list_free(lvls);
369  return NULL;
370  }
371  if (in_refline(addr, ref) && refline_kept(ref, middle_after, addr)) {
373  }
374  }
377  rz_buf_append_string(c, " ");
378  rz_buf_append_string(b, " ");
379  rz_list_foreach (lvls, iter, ref) {
380  if (core->cons && core->cons->context->breaked) {
381  rz_list_free(lvls);
382  rz_buf_free(b);
383  rz_buf_free(c);
384  return NULL;
385  }
386  if ((ref->from == addr || ref->to == addr) && !middle_after) {
387  const char *corner = get_corner_char(ref, addr, middle_before);
388  const char ch = ref->from == addr ? '=' : '-';
389  const char ch_col = ref->from >= ref->to ? 't' : 'd';
390  const char *col = (ref->from >= ref->to) ? "t" : "d";
391  if (!pos) {
392  int ch_pos = max_level + 1 - ref->level;
393  if (wide) {
394  ch_pos = ch_pos * 2 - 1;
395  }
396  rz_buf_write_at(b, ch_pos, (ut8 *)corner, 1);
397  rz_buf_write_at(c, ch_pos, (ut8 *)col, 1);
398  fill_level(b, ch_pos + 1, ch, ref, wide);
399  fill_level(c, ch_pos + 1, ch_col, ref, wide);
400  } else {
401  add_spaces(b, ref->level, pos, wide);
402  add_spaces(c, ref->level, pos, wide);
403  rz_buf_append_string(b, corner);
404  rz_buf_append_string(c, col);
405  if (!middle_before) {
406  fill_level(b, -1, ch, ref, wide);
407  fill_level(c, -1, ch_col, ref, wide);
408  }
409  }
410  if (!middle_before) {
411  dir = ref->to == addr ? 1 : 2;
412  }
413  pos = middle_before ? ref->level : 0;
414  } else {
415  if (!pos) {
416  continue;
417  }
418  add_spaces(b, ref->level, pos, wide);
419  add_spaces(c, ref->level, pos, wide);
420  if (ref->from >= ref->to) {
421  rz_buf_append_string(b, ":");
422  rz_buf_append_string(c, "t");
423  } else {
424  rz_buf_append_string(b, "|");
425  rz_buf_append_string(c, "d");
426  }
427  pos = ref->level;
428  }
429  if (max_level == -1) {
430  max_level = ref->level;
431  }
432  }
433  add_spaces(c, 0, pos, wide);
434  add_spaces(b, 0, pos, wide);
436  col_str = rz_buf_to_string(c);
437  rz_buf_free(b);
438  rz_buf_free(c);
439  b = NULL;
440  c = NULL;
441  if (!str || !col_str) {
442  rz_list_free(lvls);
443  // rz_buf_free_to_string already free b and if that is the case
444  // b will be NULL and rz_buf_free will return but if there was
445  // an error we free b here so in other words is safe
446  rz_buf_free(b);
447  rz_buf_free(c);
448  return NULL;
449  }
450  if (core->analysis->lineswidth > 0) {
451  int lw = core->analysis->lineswidth;
452  l = strlen(str);
453  if (l > lw) {
454  rz_str_cpy(str, str + l - lw);
455  rz_str_cpy(col_str, col_str + l - lw);
456  } else {
457  char pfx[128];
458  lw -= l;
459  memset(pfx, ' ', sizeof(pfx));
460  if (lw >= sizeof(pfx)) {
461  lw = sizeof(pfx) - 1;
462  }
463  if (lw > 0) {
464  pfx[lw] = 0;
465  str = rz_str_prepend(str, pfx);
466  col_str = rz_str_prepend(col_str, pfx);
467  }
468  }
469  }
470  const char prev_col = col_str[strlen(col_str) - 1];
471  const char *arr_col = prev_col == 't' ? "tt " : "dd ";
472  str = rz_str_append(str, (dir == 1) ? "-> " : (dir == 2) ? "=< "
473  : " ");
474  col_str = rz_str_append(col_str, arr_col);
475 
476  rz_list_free(lvls);
478  out->str = str;
479  out->cols = col_str;
480  return out;
481 }
482 
484  free(refstr->str);
485  free(refstr->cols);
486  free(refstr);
487 }
size_t len
Definition: 6502dis.c:15
ut8 op
Definition: 6502dis.c:13
ut16 val
Definition: armass64_const.h:6
const lzma_allocator const uint8_t size_t uint8_t * out
Definition: block.h:528
static RzCore * _core
Definition: cmd_debug.c:1622
RZ_API void rz_cons_break_pop(void)
Definition: cons.c:361
RZ_API void rz_cons_break_push(RzConsBreak cb, void *user)
Definition: cons.c:357
RZ_API bool rz_cons_is_breaked(void)
Definition: cons.c:373
#define RZ_API
#define NULL
Definition: cris-opc.c:27
#define r
Definition: crypto_rc6.c:12
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void count
Definition: sflib.h:98
static ut64 opc
Definition: desil.c:33
void skip(file *in, unsigned n)
Definition: gzappend.c:202
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
voidpf void uLong size
Definition: ioapi.h:138
voidpf void * buf
Definition: ioapi.h:138
uint8_t ut8
Definition: lh5801.h:11
return memset(p, 0, total)
static void list(RzEgg *egg)
Definition: rz-gg.c:52
RZ_API RZ_OWN RzList * rz_list_newf(RzListFree f)
Returns a new initialized RzList pointer and sets the free method.
Definition: list.c:248
RZ_API RZ_OWN RzList * rz_list_new(void)
Returns a new initialized RzList pointer (free method is not initialized)
Definition: list.c:235
RZ_API ut32 rz_list_length(RZ_NONNULL const RzList *list)
Returns the length of the list.
Definition: list.c:109
RZ_API RZ_BORROW RzListIter * rz_list_append(RZ_NONNULL RzList *list, void *data)
Appends at the end of the list a new element.
Definition: list.c:288
RZ_API RZ_BORROW RzListIter * rz_list_add_sorted(RZ_NONNULL RzList *list, void *data, RZ_NONNULL RzListComparator cmp)
Adds an element to a sorted list via the RzListComparator.
Definition: list.c:518
RZ_API void rz_list_free(RZ_NONNULL RzList *list)
Empties the list and frees the list pointer.
Definition: list.c:137
RZ_API RzPVector * rz_meta_get_all_at(RzAnalysis *a, ut64 at)
Definition: meta.c:219
int idx
Definition: setup.py:197
RZ_API bool rz_analysis_op_fini(RzAnalysisOp *op)
Definition: op.c:37
RZ_API int rz_analysis_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *data, int len, RzAnalysisOpMask mask)
Definition: op.c:96
#define min(a, b)
Definition: qsort.h:83
static const char * get_corner_char(RzAnalysisRefline *ref, ut64 addr, bool is_middle_before)
Definition: reflines.c:279
static void add_spaces(RzBuffer *b, int level, int pos, bool wide)
Definition: reflines.c:298
static ReflineEnd * refline_end_new(ut64 val, bool is_from, RzAnalysisRefline *ref)
Definition: reflines.c:28
static void fill_level(RzBuffer *b, int pos, char ch, RzAnalysisRefline *r, bool wide)
Definition: reflines.c:311
RZ_API void rz_analysis_reflines_str_free(RzAnalysisRefStr *refstr)
Definition: reflines.c:483
static bool refline_kept(RzAnalysisRefline *ref, bool middle_after, ut64 addr)
Definition: reflines.c:327
RZ_API void rz_analysis_reflines_free(RzAnalysisRefline *rl)
Definition: reflines.c:70
struct refline_end ReflineEnd
RZ_API RzList * rz_analysis_reflines_get(RzAnalysis *analysis, ut64 addr, const ut8 *buf, ut64 len, int nlines, int linesout, int linescall)
Definition: reflines.c:82
static int cmp_asc(const struct refline_end *a, const struct refline_end *b)
Definition: reflines.c:20
#define in_refline(a, r)
Definition: reflines.c:12
RZ_API RzAnalysisRefStr * rz_analysis_reflines_str(void *_core, ut64 addr, int opts)
Definition: reflines.c:344
static int cmp_by_ref_lvl(const RzAnalysisRefline *a, const RzAnalysisRefline *b)
Definition: reflines.c:24
static bool add_refline(RzList *list, RzList *sten, ut64 addr, ut64 to, int *idx)
Definition: reflines.c:39
RZ_API int rz_analysis_reflines_middle(RzAnalysis *a, RzList *list, ut64 addr, int len)
Definition: reflines.c:266
@ RZ_META_TYPE_DATA
Definition: rz_analysis.h:289
@ RZ_META_TYPE_HIDE
Definition: rz_analysis.h:294
@ RZ_META_TYPE_MAGIC
Definition: rz_analysis.h:293
@ RZ_META_TYPE_STRING
Definition: rz_analysis.h:291
@ RZ_META_TYPE_FORMAT
Definition: rz_analysis.h:292
@ RZ_ANALYSIS_OP_MASK_BASIC
Definition: rz_analysis.h:440
@ RZ_ANALYSIS_OP_MASK_HINT
Definition: rz_analysis.h:443
@ RZ_ANALYSIS_REFLINE_TYPE_MIDDLE_BEFORE
Definition: rz_analysis.h:467
@ RZ_ANALYSIS_REFLINE_TYPE_MIDDLE_AFTER
Definition: rz_analysis.h:469
@ RZ_ANALYSIS_REFLINE_TYPE_WIDE
Definition: rz_analysis.h:466
@ RZ_ANALYSIS_OP_TYPE_JMP
Definition: rz_analysis.h:368
@ RZ_ANALYSIS_OP_TYPE_CALL
Definition: rz_analysis.h:378
@ RZ_ANALYSIS_OP_TYPE_SWITCH
Definition: rz_analysis.h:423
@ RZ_ANALYSIS_OP_TYPE_CJMP
Definition: rz_analysis.h:373
#define rz_return_val_if_fail(expr, val)
Definition: rz_assert.h:108
RZ_API st64 rz_buf_write_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL const ut8 *buf, ut64 len)
Write len bytes of the buffer at the specified address.
Definition: buf.c:1197
RZ_API st64 rz_buf_append_string(RZ_NONNULL RzBuffer *b, RZ_NONNULL const char *str)
Append a string to the buffer.
Definition: buf.c:962
RZ_API void rz_buf_free(RzBuffer *b)
Free all internal data hold by the buffer and the buffer.
Definition: buf.c:1253
RZ_API RZ_OWN RzBuffer * rz_buf_new_with_bytes(RZ_NULLABLE RZ_BORROW const ut8 *bytes, ut64 len)
Creates a new buffer with a bytes array.
Definition: buf.c:465
RZ_API RZ_OWN char * rz_buf_to_string(RZ_NONNULL RzBuffer *b)
Stringify the buffer.
Definition: buf.c:642
void(* RzListFree)(void *ptr)
Definition: rz_list.h:11
int(* RzListComparator)(const void *value, const void *list_data)
Definition: rz_list.h:33
RZ_API char * rz_str_append(char *ptr, const char *string)
Definition: str.c:1063
#define rz_str_cpy(x, y)
Definition: rz_str.h:109
RZ_API char * rz_str_prepend(char *ptr, const char *string)
Definition: str.c:1027
RZ_API const char * rz_str_pad(const char ch, int len)
Definition: str.c:3236
#define RZ_NEW0(x)
Definition: rz_types.h:284
#define RZ_PERM_X
Definition: rz_types.h:95
#define RZ_NEWS0(x, y)
Definition: rz_types.h:282
#define UT64_MAX
Definition: rz_types_base.h:86
RZ_API void rz_pvector_free(RzPVector *vec)
Definition: vector.c:336
#define rz_pvector_foreach(vec, it)
Definition: rz_vector.h:334
static struct sockaddr static addrlen static backlog const void static flags void struct sockaddr socklen_t static fromlen const void const struct sockaddr to
Definition: sfsocketcall.h:125
#define b(i)
Definition: sha256.c:42
#define c(i)
Definition: sha256.c:43
#define a(i)
Definition: sha256.c:41
RzAnalysisRefline * r
Definition: reflines.c:17
bool is_from
Definition: reflines.c:16
RzAnalysisMetaType type
Definition: rz_analysis.h:302
RzList * reflines
Definition: rz_analysis.h:609
RzIOBind iob
Definition: rz_analysis.h:574
RzConsContext * context
Definition: rz_cons.h:502
RzCons * cons
Definition: rz_core.h:312
RzAnalysis * analysis
Definition: rz_core.h:322
RzIOIsValidOff is_valid_offset
Definition: rz_io.h:257
RzIO * io
Definition: rz_io.h:232
int pos
Definition: main.c:11
static struct Type metas[]
Definition: swift.c:39
Definition: dis.c:32
static int level
Definition: vmenus.c:2424
ut64(WINAPI *w32_GetEnabledXStateFeatures)()
static int addr
Definition: z80asm.c:58