user259600
user259600

Reputation:

Can anyone solve this 8080 assembly code 'puzzle'?

A friend of mine was given 8080 assembly code as part of a puzzle he's trying to solve. This is the code:

3E 02
4F
C6 04
47
11 41 01
21 69 00
19
76

He needs the values of B, DE, C and HL

Can anyone solve this or point me in the right direction on how to run this?

Update

Spoiler: The solution seems to be:

C = 02, B = 06, D = 01, E = 41, H = 01, L = AA

Upvotes: 0

Views: 4299

Answers (8)

NinjaDarth
NinjaDarth

Reputation: 393

The 8080/8085/Z80 and 80x86 families are best understood by treating the opcodes as octal, since the coding tends to group octal digits together in a 2+3+3 pattern, with only a few exceptions. The exceptions accumulated in later off-shoots of the 80x86 on or after 80486, when later developers forgot or lost track of the older legacy. That's why the source code for the 80x86 NASM assembler also originally used octal internally for its 80x86 opcode tables. (It, too, has lost sight of that legacy, as new developers took it over.)

The templates in question, written in more "Intel"-like syntax (i.e. with "mov"-s and explicit names for registers, and (⋯) for pointers, instead of names like "ld⋯", "st⋯", # for numeric constants) are:

    \0r1 Dw mov Rw, Dw      (r:0,2,4,6 ⇔ Rw:BC,DE,HL,SP)
    \0R1    add HL, Rw      (R:1,3,5,7 ⇔ Rw:BC,DE,HL,SP)
    \0d6 Db mov Rd, Db      (d:0-7 ⇔ Rd:B,C,D,E,H,L,(HL),A)
    \1ds    mov Rd, Rs      ((d,s) ≠ (6,6), d:0-7 ⇔ Rd:B,C,D,E,H,L,(HL),A; s:0-7 ⇔ Rs:B,C,D,E,H,L,(HL),A)
    \166    hlt
    \3p6 Db Op A, Db        (p:0-7 ⇔ Op:add,adc,sub,sbb,and,xor,or,cmp)

Applied to the case on hand, this yields:

    \076 02         mov A, #0x02    \0d6 Db  (mov Rd, Db; d:7⇔Rd:A, Db:0x02)
    \117            mov C, A        \1ds     (mov Rd, Rs; d:1⇔Rd:C, s:7⇔A)
    \306 04         add A, #0x04    \3p6 Db  (Op A, Db; p:0⇔Op:add, Db:0x04)
    \107            mov B, A        \1ds     (mov Rd, Rs; d:0⇔Rd:B, s:7⇔A)
    \021 41 01      mov DE, #0x0141 \0r1 Dw  (mov Rw, Dw; r:2⇔Rw:DE, Dw:0x0141)
    \041 69 00      mov HL, #0x0069 \0r1 Dw  (mov Rw, Dw; r:4⇔Rw:HL, Dw:0x0069)
    \031            add HL, DE      \0R1     (add HL, Rw; R:3⇔Rw:DE)
    \166            hlt             \166     (hlt)

or in pseudo-C:

    C = A = 0x02, B = A += 0x04, DE = 0x0141, HL = 0x0069 + DE, abort();

This implies that C ends up with 0x02, that A and B both end up with the sum 0x02 + 0x04 = 0x06, DE ends up with 0x0141, and that HL ends up with the sum 0x0141 + 0x0069 = 0x01aa and is, thus equivalent (apart from actions on flags and timing) to:

    C = 0x02, B = A = 0x02 + 0x04, DE = 0x0141, HL = 0x0069 + 0x0141, abort();

Since DE and HL are at all times, respectively, maintained as DE = 0x100×D + E and HL = 0x100×H + L, this equivalently sets D = 0x01, E = 0x41, H = 0x01, L = 0xaa.

The older mnemonic names "ld⋯", "st⋯" come from the early 1970's before Intel remade its mnemnonics in more standard form, like they did with the 80x86, or 8048, 8051/8052, and 8096 micro-controllers. The Z80 had mostly the same opcodes for all the 8080 operations, deviated from the 8085 extensions, and tended to use more regular and standard names for the mnemonics ... actually because Intel claimed the older 8080/8085 mnemonics as IP.

If I were writing an assembler or reverse assembler for any of the 8080/8085/Z80 CPU's, I'd adhere to more regular syntax, like the 8048, 8051/8052 and 8096 micro-controllers or 80x86 family. Among other things, it makes the transition between 8080 and 8086 (which Intel originally promised) easier.

For the 8080 and 8085, to the best I'm able to verify, the full set of templates in more regular syntax (naming the carry flag "CY" and program status / flags byte-sized word "PSW") would be:

    \000    nop
    \0r1 Dw mov Rw, Dw      (r:0,2,4,6 ⇔ Rw:BC,DE,HL,SP)
    \0R1    add HL, Rw      (R:1,3,5,7 ⇔ Rw:BC,DE,HL,SP)
    \0r2    mov (Rw), A     (r:0,2 ⇔ Rw:BC,DE)
    \0R2    mov A, (Rw)     (R:1,3 ⇔ BC,DE)
    \042 Aw mov (Aw), HL
    \052 Aw mov HL, (Aw)
    \062 Aw mov (Aw), A
    \072 Aw mov A, (Aw)
    \0r3    inc Rw          (r:0,2,4,6 ⇔ Rw:BC,DE,HL,SP)
    \0R3    dec Rw          (R:1,3,5,7 ⇔ Rw:BC,DE,HL,SP)
    \0d4    inc Rd          (d:0-7 ⇔ Rd:B,C,D,E,H,L,(HL),A)
    \0d5    dec Rd          (d:0-7 ⇔ Rd:B,C,D,E,H,L,(HL),A)
    \0d6 Db mov Rd, Db      (d:0-7 ⇔ Rd:B,C,D,E,H,L,(HL),A)
    \007    rlc A
    \017    rrc A
    \027    rl A
    \037    rr A
    \047    da A
    \057    cpl A
    \067    set CY
    \077    cpl CY
    \0d0    nop             [8080] illegal (d ≠ 0)
    \010    sub HL, BC      [8085]
    \020    sar HL          [8085]
    \030    shl DE          [8085]
    \040    mov A, IM       [8085]
    \050 Db mov DE, HL+Db   [8085]
    \060    mov IM, A       [8085]
    \070 Db mov DE, SP+Db   [8085]
    \1ds    mov Rd, Rs      ((d,s) ≠ (6,6), d:0-7 ⇔ Rd:B,C,D,E,H,L,(HL),A; s:0-7 ⇔ Rs:B,C,D,E,H,L,(HL),A)
    \166    hlt
    \2ps    Op A, Rs        (p:0-7 ⇔ Op:add,adc,sub,sbb,and,xor,or,cmp; s:0-7 ⇔ Rs:B,C,D,E,H,L,(HL),A)
    \3c0    rCC             (c:0-7 ⇔ CC:nz,z,nc,c,po,pe,p,n)
    \3c2 Aw jCC Aw          (c:0-7 ⇔ CC:nz,z,nc,c,po,pe,p,n)
    \3c4 Aw cCC Aw          (c:0-7 ⇔ CC:nz,z,nc,c,po,pe,p,n)
    \3r1    pop Rw          (r:0,2,4,6 ⇔ BC,DE,HL,PSW)
    \311    ret
    \331    ret             [8080] illegal
    \331    mov (DE), HL    [8085]
    \351    mov PC, HL
    \371    mov SP, HL
    \303 Aw jmp Aw
    \313 Aw jmp Aw          [8080] illegal
    \313    cv 0x0040       [8085]
    \323 Ab out Ab, A
    \333 Ab in A, Ab
    \343    xch (SP), HL
    \353    xch DE, HL
    \363    di
    \373    ei
    \3r5    push Rw         (r:0,2,4,6 ⇔ Rw:BC,DE,HL,PSW)
    \315 Aw call Aw
    \3R5 Aw call Aw         [8080] illegal (R=3,5,7)
    \335 Aw jk Aw           [8085]
    \355    mov HL, (DE)    [8085]
    \375 Aw jnk Aw          [8085]
    \3p6 Db Op A, Db        (p:0-7 ⇔ Op:add,adc,sub,sbb,and,xor,or,cmp)
    \3n7    rst n

The K and V bits (referred to by \335, \375 and \313) are 8085-only. Word-sized data Dw and memory address Aw operands are sequenced low-order byte first, as already seen with the cases:

    \021 41 01      mov DE, 0x0141  \0r1 Dw  (mov Rw, Dw; r:2⇔Rw:DE)
    \041 69 00      mov HL, 0x0069  \0r1 Dw  (mov Rw, Dw; r:4⇔Rw:HL)

The byte-sized operands are data operands Db or addresses Ab in the I/O port address space.

Upvotes: 1

jmc
jmc

Reputation: 41

3E 02      ;mvi a, 02h    -- load A with 02h
4F         ;mov c,a       -- move A into C (A remains 02h)
C6 04      ;adi 04h       -- a = a + 04h  (A now contains 06h)
47         ;mov b,a       -- BC pair now contains 0602h
11 41 01   ;lxi d, 0414h  -- DE now contains 0141h
21 69 00   ;lxi h, 0069h  -- HL now contains 0069h
19         ;dad d         -- HL = HL + DE
76         ;hlt           -- halt processing

Upvotes: 1

user781847
user781847

Reputation:

Complete spoiler

0000h: 3e02         mvi a, 2h      ; A = 2
0002h: 4f           mov c, a       ; C = 2  
0003h: c604         adi 4h         ; A = 6  
0005h: 47           mov b, a       ; B = 6  
0006h: 114101       lxi d, 141h    ; DE = 0141h 
0009h: 216900       lxi h, 069h    ; HL = 69h  
000ch: 19           dad d          ; HL = 69h + 141h = 1aah  
000dh: 76           hlt  
A = 6, B = 6, C = 2, D = 1, E = 41h, H = 1, L = 0aah

An 8080 online disassembler here.
An 8080 online instruction set reference here.

Upvotes: 2

Igor Skochinsky
Igor Skochinsky

Reputation: 25318

For reference, here's the disassembly:

 3E 02      mvi     a, 2     ; Move o1 <- immediate data
 4F         mov     c, a     ; Move o1 <- o2
 C6 04      adi     4        ; Add immediate to A
 47         mov     b, a     ; Move o1 <- o2
 11 41 01   lxi     d, 141h  ; Load register pair with immediate data
 21 69 00   lxi     h, 69h   ; Load register pair with immediate data
 19         dad     d        ; Add register pair to HL
 76         hlt              ; Halt

Upvotes: 4

Will Hartung
Will Hartung

Reputation: 118774

You need to disassemble it.

That is, convert the hex op codes in to their mnemonics.

Here's an example reference.

You can see from this that 3E is "MVI A, d8", so that looks like 3E 02 is putting the value 02 in to the A register.

Once you've decoded the mnemonics, you can look up what they actually mean and figure out the program.

Easy enough to do by hand.

Upvotes: 2

t0mm13b
t0mm13b

Reputation: 34592

Judging by this, your best bet would be to do a search for an 8080 emulator and run it against the emulator, and get the answer from it.

Hope this helps, Best regards, Tom.

Upvotes: 1

anon
anon

Reputation:

You don't need to run it - you just need to translate it. A table of 8080 opcodes like this, 10 minutes work and you will have disassembled the code. You can then simulate it mentally to work out the answer.

Upvotes: 1

Ben Zotto
Ben Zotto

Reputation: 71048

Here's a guide to the 8080 instruction set: http://www.comsci.us/cpu/8080/isindex.html Your hex listing looks like an instruction stream; you should be able to go from there. How delightfully old-school!

Good luck.

Upvotes: 4

Related Questions