mirror of
https://github.com/rizinorg/rizin
synced 2026-08-22 20:26:16 -04:00
librz/bin/coff: scale addresses on word-addressed TI targets
The C54x and C28x address 16-bit words, so their loadable section sizes and every address in the file count words. Rizin's address space is byte-based, so reading those unscaled mapped only half of each loadable section and placed every symbol at half its true offset: _main in the c54x emulateme object landed mid-instruction instead of on its prologue. Scale section VAs, loadable section sizes and symbol addresses by the target's address unit. Debug sections are byte streams even on these targets and keep a scale of one, matching the loadable mask already used for mapping. The C55x addresses program memory by byte and is left alone; the ids were checked against the objects in rizin-testbins by comparing each section's declared size against its extent in the file. Branch and call targets on the C54x count program words too, and reached analysis and RzIL unscaled. That went unnoticed while the sections were half-mapped: the emulateme RzIL test ran from a mid-instruction address whose decode happened to lift, so it pinned values produced by garbage. Scale those as well and drive the test the way its C55x sibling does, decrypting seckrit with the real key. With the sections fully mapped, analysis now finds every routine the c54x fixtures declare, so the function counts change.
This commit is contained in:
parent
f82d6e2104
commit
fee84c7f5c
8 changed files with 96 additions and 56 deletions
|
|
@ -116,6 +116,9 @@ static RzILOpPure *c54x_ea(const C55Operand *m) {
|
|||
}
|
||||
|
||||
// Byte address of the current stack top: SP is a 16-bit word pointer.
|
||||
// Program addresses count 16-bit words; the VM runs in the byte address space.
|
||||
#define C54X_WORD_BYTES 2
|
||||
|
||||
static RzILOpPure *c54x_sp_ea(void) {
|
||||
return MUL(UNSIGNED(24, VARG("sp")), UN(24, C54X_WORD_BYTES));
|
||||
}
|
||||
|
|
@ -1152,7 +1155,7 @@ RZ_IPI RzILOpEffect *c54x_lift(const C55Insn *insn, ut64 pc) {
|
|||
if (!pred) {
|
||||
return NULL;
|
||||
}
|
||||
return BRANCH(pred, JMP(UN(24, (ut32)op[0]->imm)), NOP());
|
||||
return BRANCH(pred, JMP(UN(24, (ut32)op[0]->imm * C54X_WORD_BYTES)), NOP());
|
||||
}
|
||||
|
||||
// conditional return
|
||||
|
|
@ -1185,7 +1188,7 @@ RZ_IPI RzILOpEffect *c54x_lift(const C55Insn *insn, ut64 pc) {
|
|||
}
|
||||
// Test the AR before its post-modify, which happens regardless of branch.
|
||||
RzILOpEffect *grab = SETL("take", BOOL_TO_BV(INV(IS_ZERO(VARG(ar))), 1));
|
||||
RzILOpEffect *br = BRANCH(NON_ZERO(VARL("take")), JMP(UN(24, (ut32)op[0]->imm)), NOP());
|
||||
RzILOpEffect *br = BRANCH(NON_ZERO(VARL("take")), JMP(UN(24, (ut32)op[0]->imm * C54X_WORD_BYTES)), NOP());
|
||||
RzILOpEffect *post = c55_post_effect(a, op[1]);
|
||||
return post ? SEQ3(grab, post, br) : SEQ2(grab, br);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -500,7 +500,10 @@ static ut64 c55_branch_target(const C55Insn *insn, ut64 pc) {
|
|||
if (o->abs_target) {
|
||||
// Absolute target: the operand is the destination address
|
||||
// itself (24-bit program space), not a pc-relative offset.
|
||||
return v & 0xffffff;
|
||||
// The C54x counts program words here, so scale the target
|
||||
// into the byte address space the analysis works in.
|
||||
ut64 target = v & 0xffffff;
|
||||
return insn->arch == C55_ARCH_C54X ? target * 2 : target;
|
||||
}
|
||||
st64 soff = o->reltarget_unsigned
|
||||
? (st64)v
|
||||
|
|
|
|||
|
|
@ -124,6 +124,30 @@ RZ_API bool rz_coff_supported_arch(RzBuffer *b) {
|
|||
return coff_guess_endianness(b, &big_endian);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Bytes per target address unit of \p obj.
|
||||
* \param obj COFF object
|
||||
* \return 1 for byte-addressed targets, 2 for the 16-bit word-addressed TI DSPs
|
||||
*
|
||||
* The C54x and C28x address 16-bit words, so their loadable section
|
||||
* sizes and every address in the file count words, not bytes. Rizin's address
|
||||
* space is byte-based, so those have to be scaled to line up with the section
|
||||
* data. Debug sections are byte streams even on these targets and are excluded
|
||||
* by the caller.
|
||||
*/
|
||||
RZ_API ut32 rz_coff_addr_scale(RZ_NONNULL struct rz_bin_coff_obj *obj) {
|
||||
rz_return_val_if_fail(obj, 1);
|
||||
switch (obj->target_id) {
|
||||
case COFF_FILE_TARGET_TI_TMS320C5400:
|
||||
case COFF_FILE_TARGET_TI_TMS320C2800:
|
||||
return 2;
|
||||
default:
|
||||
// The C55x addresses program memory by byte, so its objects need no
|
||||
// scaling despite being a fixed-point DSP.
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
RZ_API ut64 rz_coff_perms_from_section_flags(ut32 flags) {
|
||||
ut32 r = 0;
|
||||
if (flags & COFF_SCN_MEM_READ) {
|
||||
|
|
@ -375,11 +399,15 @@ static bool bin_coff_init_scn_va(struct rz_bin_coff_obj *obj) {
|
|||
CoffScnHdr *scn_hdr;
|
||||
rz_vector_enumerate (obj->scn_hdrs, scn_hdr, i) {
|
||||
if (is_exec) {
|
||||
obj->scn_va[i] = scn_hdr->s_vaddr;
|
||||
obj->scn_va[i] = (ut64)scn_hdr->s_vaddr * rz_coff_addr_scale(obj);
|
||||
continue;
|
||||
}
|
||||
obj->scn_va[i] = va;
|
||||
va += scn_hdr->s_size ? scn_hdr->s_size : 16;
|
||||
// Advance by the mapped byte length so the synthetic bases cannot
|
||||
// overlap once a word-counted section is scaled.
|
||||
const ut32 loadable = COFF_SCN_CNT_CODE | COFF_SCN_CNT_INIT_DATA | COFF_SCN_CNT_UNIN_DATA;
|
||||
const ut32 scale = (scn_hdr->s_flags & loadable) ? rz_coff_addr_scale(obj) : 1;
|
||||
va += scn_hdr->s_size ? (ut64)scn_hdr->s_size * scale : 16;
|
||||
va = RZ_ROUND(va, 16ULL);
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ struct rz_bin_coff_obj {
|
|||
};
|
||||
|
||||
RZ_API bool rz_coff_supported_arch(RzBuffer *b);
|
||||
RZ_API ut32 rz_coff_addr_scale(RZ_NONNULL struct rz_bin_coff_obj *obj);
|
||||
RZ_API ut64 rz_coff_perms_from_section_flags(ut32 flags);
|
||||
RZ_API struct rz_bin_coff_obj *rz_bin_coff_new_buf(RzBuffer *buf);
|
||||
RZ_API void rz_bin_coff_free(struct rz_bin_coff_obj *obj);
|
||||
|
|
|
|||
|
|
@ -82,8 +82,9 @@ static bool coff_fill_bin_symbol(RzBin *rbin, struct rz_bin_coff_obj *bin, size_
|
|||
if ((bin->hdr.f_flags & COFF_FLAGS_TI_F_EXEC) != 0) {
|
||||
const ut32 loadable = COFF_SCN_CNT_CODE | COFF_SCN_CNT_INIT_DATA | COFF_SCN_CNT_UNIN_DATA;
|
||||
if (sc_hdr->s_flags & loadable) {
|
||||
ptr->vaddr = s->n_value;
|
||||
ptr->paddr = sc_hdr->s_scnptr + (s->n_value - sc_hdr->s_vaddr);
|
||||
const ut32 scale = rz_coff_addr_scale(bin);
|
||||
ptr->vaddr = (ut64)s->n_value * scale;
|
||||
ptr->paddr = sc_hdr->s_scnptr + (ut64)(s->n_value - sc_hdr->s_vaddr) * scale;
|
||||
} else {
|
||||
// Symbol belongs to a non-loadable section (DWARF/debug, build
|
||||
// attributes, .pinit). Its n_value is not an address in the
|
||||
|
|
@ -97,9 +98,11 @@ static bool coff_fill_bin_symbol(RzBin *rbin, struct rz_bin_coff_obj *bin, size_
|
|||
ptr->paddr = sc_hdr->s_scnptr + s->n_value;
|
||||
}
|
||||
} else {
|
||||
ptr->paddr = sc_hdr->s_scnptr + s->n_value;
|
||||
const ut32 loadable = COFF_SCN_CNT_CODE | COFF_SCN_CNT_INIT_DATA | COFF_SCN_CNT_UNIN_DATA;
|
||||
const ut32 scale = (sc_hdr->s_flags & loadable) ? rz_coff_addr_scale(bin) : 1;
|
||||
ptr->paddr = sc_hdr->s_scnptr + (ut64)s->n_value * scale;
|
||||
if (bin->scn_va) {
|
||||
ptr->vaddr = bin->scn_va[s->n_scnum - 1] + s->n_value;
|
||||
ptr->vaddr = bin->scn_va[s->n_scnum - 1] + (ut64)s->n_value * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -300,8 +303,11 @@ static RzPVector /*<RzBinMap *>*/ *coff_maps(RzBinFile *bf) {
|
|||
return ret;
|
||||
}
|
||||
ptr->name = rz_coff_symbol_name(obj, (const ut8 *)hdr->s_name);
|
||||
ptr->psize = hdr->s_size;
|
||||
ptr->vsize = hdr->s_size;
|
||||
// Only loadable sections are counted in target address units; debug
|
||||
// sections are byte streams even on a word-addressed target.
|
||||
const ut32 scale = (hdr->s_flags & loadable) ? rz_coff_addr_scale(obj) : 1;
|
||||
ptr->psize = (ut64)hdr->s_size * scale;
|
||||
ptr->vsize = (ut64)hdr->s_size * scale;
|
||||
ptr->paddr = hdr->s_scnptr;
|
||||
if (obj->scn_va) {
|
||||
ptr->vaddr = obj->scn_va[i];
|
||||
|
|
@ -355,8 +361,10 @@ static RzPVector /*<RzBinSection *>*/ *coff_sections(RzBinFile *bf) {
|
|||
if (strstr(ptr->name, "data")) {
|
||||
ptr->is_data = true;
|
||||
}
|
||||
ptr->size = scn_hdr->s_size;
|
||||
ptr->vsize = scn_hdr->s_size;
|
||||
const ut32 loadable = COFF_SCN_CNT_CODE | COFF_SCN_CNT_INIT_DATA | COFF_SCN_CNT_UNIN_DATA;
|
||||
const ut32 scale = (scn_hdr->s_flags & loadable) ? rz_coff_addr_scale(obj) : 1;
|
||||
ptr->size = (ut64)scn_hdr->s_size * scale;
|
||||
ptr->vsize = (ut64)scn_hdr->s_size * scale;
|
||||
ptr->paddr = scn_hdr->s_scnptr;
|
||||
ptr->flags = scn_hdr->s_flags;
|
||||
if (obj->scn_va) {
|
||||
|
|
|
|||
|
|
@ -141,22 +141,22 @@ EXPECT=<<EOF
|
|||
type: nop
|
||||
size: 2
|
||||
type: jmp
|
||||
jump: 0x00000100
|
||||
jump: 0x00000200
|
||||
type: jmp
|
||||
jump: 0x00000100
|
||||
jump: 0x00000200
|
||||
type: call
|
||||
jump: 0x00000100
|
||||
jump: 0x00000200
|
||||
fail: 0x00000004
|
||||
stackptr: 2
|
||||
type: cjmp
|
||||
jump: 0x00000120
|
||||
jump: 0x00000240
|
||||
fail: 0x00000004
|
||||
type: ccall
|
||||
jump: 0x00000120
|
||||
jump: 0x00000240
|
||||
fail: 0x00000004
|
||||
stackptr: 2
|
||||
type: cjmp
|
||||
jump: 0x00000120
|
||||
jump: 0x00000240
|
||||
fail: 0x00000004
|
||||
type: ujmp
|
||||
fail: 0x00000002
|
||||
|
|
@ -248,7 +248,7 @@ direction: exec
|
|||
stackop: inc
|
||||
stackptr: 2
|
||||
type: call
|
||||
jump: 0x00000100
|
||||
jump: 0x00000200
|
||||
direction: exec
|
||||
EOF
|
||||
RUN
|
||||
|
|
@ -322,7 +322,7 @@ val: 0x00001234
|
|||
val: 0x00001234
|
||||
val: 0x0000000f
|
||||
type: jmp
|
||||
jump: 0x00002010
|
||||
jump: 0x00004020
|
||||
EOF
|
||||
RUN
|
||||
|
||||
|
|
@ -391,7 +391,7 @@ arch tms320
|
|||
cpu c54x
|
||||
bintype coff
|
||||
endian LE
|
||||
12
|
||||
13
|
||||
1
|
||||
EOF
|
||||
RUN
|
||||
|
|
@ -407,7 +407,7 @@ EOF
|
|||
EXPECT=<<EOF
|
||||
arch tms320
|
||||
cpu c54x
|
||||
8
|
||||
9
|
||||
EOF
|
||||
RUN
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
d "nop" 95f4 0x0 nop
|
||||
d "b 0x100" 73f00001 0x0 (jmp (bv 24 0x100))
|
||||
d "bd 0x100" 73f20001 0x0 (jmp (bv 24 0x100))
|
||||
d "call 0x100" 74f00001 0x0 (jmp (bv 24 0x100))
|
||||
d "calld 0x100" 74f20001 0x0 (jmp (bv 24 0x100))
|
||||
d "b 0x100" 73f00001 0x0 (jmp (bv 24 0x200))
|
||||
d "bd 0x100" 73f20001 0x0 (jmp (bv 24 0x200))
|
||||
d "call 0x100" 74f00001 0x0 (jmp (bv 24 0x200))
|
||||
d "calld 0x100" 74f20001 0x0 (jmp (bv 24 0x200))
|
||||
d "ret" 00fc 0x0 (seq (set sp (+ (var sp) (bv 16 0x1))) (jmp (cast 24 false (loadw 0 16 (* (cast 24 false (var sp)) (bv 24 0x2))))))
|
||||
d "retd" 00fe 0x0 (seq (set sp (+ (var sp) (bv 16 0x1))) (jmp (cast 24 false (loadw 0 16 (* (cast 24 false (var sp)) (bv 24 0x2))))))
|
||||
d "rete" ebf4 0x0 (seq (set ret_addr (loadw 0 24 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x2))) (jmp (var ret_addr)))
|
||||
|
|
@ -13,10 +13,10 @@ d "bacc a" e2f4 0x0 (jmp (cast 24 false (var a)))
|
|||
d "baccd a" e2f6 0x0 (jmp (cast 24 false (var a)))
|
||||
d "cala a" e3f4 0x0 (jmp (cast 24 false (var a)))
|
||||
d "calad a" e3f6 0x0 (jmp (cast 24 false (var a)))
|
||||
d "fb 0x2" 80f80200 0x0 (jmp (bv 24 0x2))
|
||||
d "fbd 0x2" 80fa0200 0x0 (jmp (bv 24 0x2))
|
||||
d "fcall 0xf" 80f90f00 0x0 (jmp (bv 24 0xf))
|
||||
d "fcalld 0xf" 80fb0f00 0x0 (jmp (bv 24 0xf))
|
||||
d "fb 0x2" 80f80200 0x0 (jmp (bv 24 0x4))
|
||||
d "fbd 0x2" 80fa0200 0x0 (jmp (bv 24 0x4))
|
||||
d "fcall 0xf" 80f90f00 0x0 (jmp (bv 24 0x1e))
|
||||
d "fcalld 0xf" 80fb0f00 0x0 (jmp (bv 24 0x1e))
|
||||
d "fret" e4f4 0x0 (seq (set ret_addr (loadw 0 24 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x2))) (jmp (var ret_addr)))
|
||||
d "fretd" e4f6 0x0 (seq (set ret_addr (loadw 0 24 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x2))) (jmp (var ret_addr)))
|
||||
d "frete" e5f4 0x0 (seq (set ret_addr (loadw 0 24 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x2))) (jmp (var ret_addr)))
|
||||
|
|
@ -89,13 +89,13 @@ d "frame #-2" feee 0x0 (set sp (+ (var sp) (bv 16 0xfffe)))
|
|||
d "frame #10" 0aee 0x0 (set sp (+ (var sp) (bv 16 0xa)))
|
||||
d "bitf *ar1, #0x1234" 81613412 0x0 (set st0 (| (& (var st0) (bv 16 0xefff)) (ite (! (is_zero (& (loadw 0 16 (* (cast 24 false (var ar1)) (bv 24 0x2))) (bv 16 0x1234)))) (bv 16 0x1000) (bv 16 0x0))))
|
||||
d "cmpm *ar1, #0x1234" 81603412 0x0 (set st0 (| (& (var st0) (bv 16 0xefff)) (ite (== (loadw 0 16 (* (cast 24 false (var ar1)) (bv 24 0x2))) (bv 16 0x1234)) (bv 16 0x1000) (bv 16 0x0))))
|
||||
d "bc 0x100, agt" 46f80001 0x0 (branch (! (sle (var a) (bv 40 0x0))) (jmp (bv 24 0x100)) nop)
|
||||
d "bc 0x100, aeq" 45f80001 0x0 (branch (is_zero (var a)) (jmp (bv 24 0x100)) nop)
|
||||
d "cc 0x200, bneq" 4cf90002 0x0 (branch (! (is_zero (var b))) (jmp (bv 24 0x200)) nop)
|
||||
d "bcd 0x100, agt" 46fa0001 0x0 (branch (! (sle (var a) (bv 40 0x0))) (jmp (bv 24 0x100)) nop)
|
||||
d "ccd 0x200, bneq" 4cfb0002 0x0 (branch (! (is_zero (var b))) (jmp (bv 24 0x200)) nop)
|
||||
d "banz 0x100, *ar1-" 896c0001 0x0 (seq (set take (ite (! (is_zero (var ar1))) (bv 1 0x1) (bv 1 0x0))) (set ar1 (- (var ar1) (bv 16 0x1))) (branch (! (is_zero (var take))) (jmp (bv 24 0x100)) nop))
|
||||
d "banzd 0x100, *ar1-" 896e0001 0x0 (seq (set take (ite (! (is_zero (var ar1))) (bv 1 0x1) (bv 1 0x0))) (set ar1 (- (var ar1) (bv 16 0x1))) (branch (! (is_zero (var take))) (jmp (bv 24 0x100)) nop))
|
||||
d "bc 0x100, agt" 46f80001 0x0 (branch (! (sle (var a) (bv 40 0x0))) (jmp (bv 24 0x200)) nop)
|
||||
d "bc 0x100, aeq" 45f80001 0x0 (branch (is_zero (var a)) (jmp (bv 24 0x200)) nop)
|
||||
d "cc 0x200, bneq" 4cf90002 0x0 (branch (! (is_zero (var b))) (jmp (bv 24 0x400)) nop)
|
||||
d "bcd 0x100, agt" 46fa0001 0x0 (branch (! (sle (var a) (bv 40 0x0))) (jmp (bv 24 0x200)) nop)
|
||||
d "ccd 0x200, bneq" 4cfb0002 0x0 (branch (! (is_zero (var b))) (jmp (bv 24 0x400)) nop)
|
||||
d "banz 0x100, *ar1-" 896c0001 0x0 (seq (set take (ite (! (is_zero (var ar1))) (bv 1 0x1) (bv 1 0x0))) (set ar1 (- (var ar1) (bv 16 0x1))) (branch (! (is_zero (var take))) (jmp (bv 24 0x200)) nop))
|
||||
d "banzd 0x100, *ar1-" 896e0001 0x0 (seq (set take (ite (! (is_zero (var ar1))) (bv 1 0x1) (bv 1 0x0))) (set ar1 (- (var ar1) (bv 16 0x1))) (branch (! (is_zero (var take))) (jmp (bv 24 0x200)) nop))
|
||||
d "stl a, *ar1(0xc)" e1800c00 0x0 (storew 0 (* (+ (cast 24 false (var ar1)) (bv 24 0xc)) (bv 24 0x2)) (cast 16 false (var a)))
|
||||
d "ld *ar1(0x1), a" e1100100 0x0 (set a (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var ar1)) (bv 24 0x1)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var ar1)) (bv 24 0x1)) (bv 24 0x2)))))
|
||||
d "ldm ar1, a" 1148 0x0 (set a (cast 40 false (var ar1)))
|
||||
|
|
@ -260,7 +260,7 @@ d "rc bgeq" 4afc 0x0 (branch (|| (! (sle (var b) (bv 40 0x0))) (== (var b) (bv 4
|
|||
d "xc 1, aeq" 45fd
|
||||
d "xc 2, aeq" 45ff
|
||||
d "xc 1, tc, c" 3cfd
|
||||
d "bc 0x1234, tc, c" 3cf83412 0x0 (branch (&& (! (is_zero (& (var st0) (bv 16 0x1000)))) (! (is_zero (& (var st0) (bv 16 0x800))))) (jmp (bv 24 0x1234)) nop)
|
||||
d "bc 0x1234, tc, c" 3cf83412 0x0 (branch (&& (! (is_zero (& (var st0) (bv 16 0x1000)))) (! (is_zero (& (var st0) (bv 16 0x800))))) (jmp (bv 24 0x2468)) nop)
|
||||
d "ldm imr, a" 0048 0x0 (set a (cast 40 false (var imr)))
|
||||
d "ldm al, a" 0848
|
||||
d "ldm ah, a" 0948
|
||||
|
|
|
|||
|
|
@ -347,29 +347,26 @@ ar4 = 0x30
|
|||
EOF
|
||||
RUN
|
||||
|
||||
NAME=C54x RzIL VM: emulateme _decrypt writes hex digits to the UART buffer
|
||||
# Key at word 0x280 (byte 0x500) in A, encrypted seckrit at _seckrit; the stack
|
||||
# is kept clear of .text, which the linker places at byte 0x100.
|
||||
NAME=C54x RzIL VM: emulateme _decrypt decrypts seckrit to "Hello from RzIL!"
|
||||
FILE=bins/tms320/emulateme_nostd.ccsv5.c54x.ticoff2.dbg.coff
|
||||
CMDS=<<EOF
|
||||
e io.cache=true
|
||||
wx 30003100320033003400350036003700380039006100620063006400650066 @ 0x900
|
||||
wx 0006 @ 0x9a4
|
||||
wx 00000000 @ 0x9a8
|
||||
s 0xec
|
||||
wx 510053004d0077005800140051005f0045006c0017007f006e0078007f001c000000 @ 0x9ac
|
||||
wx 41006e00790043006f006c006f007500720059006f0075004c0069006b006500 @ 0x500
|
||||
s 0x1d8
|
||||
aezi
|
||||
aezv a 0x5a
|
||||
aezv b 0x5
|
||||
aezv sp 0x100
|
||||
aezsu 0x136
|
||||
p8 4 @ 0xc00
|
||||
p8 2 @ 0x9aa
|
||||
aezv sp 0x800
|
||||
aezv a 0x280
|
||||
aezsu 0x270
|
||||
p8 32 @ 0x9ac
|
||||
ar a
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
a = 0x5a
|
||||
b = 0x5
|
||||
sp = 0x100
|
||||
35006100
|
||||
0200
|
||||
a = 0x0000000002
|
||||
sp = 0x800
|
||||
a = 0x280
|
||||
480065006c006c006f002000660072006f006d00200052007a0049004c002100
|
||||
a = 0x0000000001
|
||||
EOF
|
||||
RUN
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue