Reputation:
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
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
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
Reputation:
Complete spoiler
0000h: 3e02 mvi a, 2h ; A = 20002h: 4f mov c, a ; C = 20003h: c604 adi 4h ; A = 60005h: 47 mov b, a ; B = 60006h: 114101 lxi d, 141h ; DE = 0141h0009h: 216900 lxi h, 069h ; HL = 69h000ch: 19 dad d ; HL = 69h + 141h = 1aah000dh: 76 hltA = 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
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
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
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
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
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