Assembly (x86-64, AArch64, RISC-V) Module 18 – 8-bit assembly: the 8051 microcontroller and the 8085
8-bit toolchains: assemblers, HEX and simulators
Assemble 8051 and 8085 programs with two assemblers, read and check the Intel HEX records they write, and run them to a breakpoint in two simulators.
What you will learn
- Choose an assembler and a simulator for an 8051 or 8085 lab program and say what each one checks
- Assemble Intel-syntax source with AS and decode the Intel HEX records it writes, checksum included
- Run a program to a breakpoint in uCsim and read its registers, flags, memory and run time
- Explain why a lab program sets every value it reads, from runs with two power-on seeds
- Compare the bytes of one program from two assemblers and its results on two simulators
Before you start
What you’ll learn · Before you start
What you will learn
- Choose an assembler and a simulator for an 8051 or 8085 lab program and say what each one checks
- Assemble Intel-syntax source with AS and decode the Intel HEX records it writes, checksum included
- Run a program to a breakpoint in uCsim and read its registers, flags, memory and run time
- Explain why a lab program sets every value it reads, from runs with two power-on seeds
- Compare the bytes of one program from two assemblers and its results on two simulators
Before you start
On this page
Twenty lines of 8051 assembly become 30 bytes before anything can run them, and one wrong digit among those bytes is enough to break the program. 8-bit toolchains make the bytes and let you watch them work: an assembler translates the source and writes a listing, a converter packs the bytes into Intel HEX: A text file format for machine-code bytes, one record a line. Each record carries a byte count, a 16-bit load address, a type (00 data, 01 end of file), the data bytes and a checksum chosen so that all its bytes add up to 00H modulo 256., a text format that a loader checks line by line, and a simulator loads the HEX file, runs it to a breakpoint and shows the registers, flags and memory.
Below, an 8051 program and an 8085 program go through the Macroassembler AS and the uCsim simulators, the Intel HEX lines are read field by field, and the 8085 program is built and run a second time with Sim8085’s own assembler and simulator, so that two 8-bit toolchains check each other’s work.
AS turns 20 lines into 30 bytes and a listing
sum.asm adds the ten bytes of a table kept in code memory and leaves the 16-bit total in two registers, R7 for the
high byte and R6 for the low one. It is written in Intel’s syntax: a label ends in a colon, a hexadecimal constant
ends in H, and the directive: A line that gives an order to the assembler instead of naming a processor instruction: switch section, export a symbol, put bytes into the data, define a constant, pad to a boundary. GNU as directives start with a dot (.data, .globl, .byte); NASM writes section, global, db and equ. More in “Assembler syntax: NASM, GNU as, Intel and AT&T” ORG, DB and END set the address, place data bytes
and end the source. CPU 8051 is a directive of AS alone, which picks the instruction set:
; sum.asm: add the ten bytes of TABLE (code memory) into a 16-bit total, R7 high, R6 low.
CPU 8051
ORG 0000H
START: MOV DPTR,#TABLE ; DPTR = address of the first table byte
MOV R2,#10 ; R2 counts the bytes still to add
CLR A
MOV R6,A ; total, low byte = 0
MOV R7,A ; total, high byte = 0
LOOP: CLR A ; MOVC adds A to DPTR, so A must be 0 first
MOVC A,@A+DPTR ; A = the byte DPTR points at
ADD A,R6 ; low byte + table byte, CY = carry out
MOV R6,A
CLR A
ADDC A,R7 ; high byte + CY
MOV R7,A
INC DPTR ; move to the next table byte
DJNZ R2,LOOP ; repeat until R2 reaches 0
DONE: SJMP DONE ; park here: a breakpoint on DONE ends a run
TABLE: DB 0C8H, 96H, 0FAH, 64H, 7DH, 0AFH, 32H, 0E1H, 4BH, 0F0H
END An assembler: A program that reads assembly-language text and writes the matching bytes to an object file, with any labels, constants and directives the source used. clang, nasm and GNU as are three widely used assemblers. More in “What assembly language is and why it matters” turns such text into machine code: The stream of bytes a processor really reads and runs, written to the rules of an ISA. Assembly language is a human-readable spelling of the same bytes; a disassembler reads the bytes and prints the spelling back. More in “What assembly language is and why it matters”. The
Macroassembler AS is a cross-assembler: An assembler that runs on one kind of computer but writes machine code for another, for example a program on an Apple silicon Mac that writes an x86-64 object file. More in “Set up assemblers, QEMU and debuggers” for many 8-bit and 16-bit processors, and two
of its programs do the work here: asl translates the source into its own code file, sum.p, and with -L also writes
the assembler listing: A text file the assembler writes beside the object that pairs each source line with its offset and the bytes it produced, including the lines each macro use expanded to. nasm -l writes one., sum.lst; p2hex then writes the code file as Intel HEX. Step through
the path the bytes take:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- One text file. sum.asm is plain text in Intel syntax: labels end in a colon, hexadecimal constants end in H, and the directives CPU, ORG, DB and END steer the assembler. No processor can run it yet.
- AS reads every line. asl, run with -cpu 8051, checks each line against the 8051 instruction set and works out the address of every label. Here it finds nothing to refuse in the 20 lines.
- Two files come out. sum.p is the code file of AS: 30 bytes and the addresses they belong at. sum.lst, the listing, pairs each source line with its address and its bytes, which is the file to read when a byte looks wrong.
- p2hex packs the bytes. p2hex reads sum.p and writes the bytes as Intel HEX records: at most 16 data bytes a line by default, each line with its own address and checksum, then an end-of-file record.
- A file any loader reads. sum.hex holds three lines of text: 16 bytes for 0000H, 14 bytes for 0010H and the end record. Simulators and chip programmers read this format, whichever assembler wrote it.
- The simulator loads it. s51 puts the bytes into the code memory of its 8051 model and reports 30 words read from sum.hex. From address 0000H it runs them one instruction at a time, until a breakpoint stops it.
- A refused line goes back. slip.asm writes 255 as FFH. AS takes FFH for a name it does not know, reports error symbol undefined for line 4 and ends with exit status 2. Nothing should be loaded until the source is fixed and assembled again.
The session below runs both programs, prints the listing, and prints the HEX file twice, once as p2hex writes it by default and once with 8 bytes a line:
"""Assemble sum.asm with AS and print its listing, then the Intel HEX file p2hex writes, with 16 and with 8 bytes a line."""
import pathlib
import sys
sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import eightbit
hexfile = eightbit.asm51("8051/sum.asm") # asl writes sum.p and the listing sum.lst; p2hex turns sum.p into sum.hex
eightbit.listing("sum.lst") # each source line with its address and the bytes it became
eightbit.run(f"""
cat {hexfile}
p2hex -q -l 8 sum.p sum8.hex
cat sum8.hex
""")
eightbit.compare_hex(hexfile, "sum8.hex") # other lines, but do they load the same bytes? Output
$ asl -q -cpu 8051 -L -olist sum.lst -o sum.p 8051/sum.asm
$ p2hex -q sum.p sum.hex
# sum.lst: line/address, bytes, source
1/ 0 : ; sum.asm: add the ten bytes of TABLE (code memory) into a 16-bit total, R7 high, R6 low.
2/ 0 : CPU 8051
3/ 0 : ORG 0000H
4/ 0 : 90 00 14 START: MOV DPTR,#TABLE ; DPTR = address of the first table byte
5/ 3 : 7A 0A MOV R2,#10 ; R2 counts the bytes still to add
6/ 5 : E4 CLR A
7/ 6 : FE MOV R6,A ; total, low byte = 0
8/ 7 : FF MOV R7,A ; total, high byte = 0
9/ 8 : E4 LOOP: CLR A ; MOVC adds A to DPTR, so A must be 0 first
10/ 9 : 93 MOVC A,@A+DPTR ; A = the byte DPTR points at
11/ A : 2E ADD A,R6 ; low byte + table byte, CY = carry out
12/ B : FE MOV R6,A
13/ C : E4 CLR A
14/ D : 3F ADDC A,R7 ; high byte + CY
15/ E : FF MOV R7,A
16/ F : A3 INC DPTR ; move to the next table byte
17/ 10 : DA F6 DJNZ R2,LOOP ; repeat until R2 reaches 0
18/ 12 : 80 FE DONE: SJMP DONE ; park here: a breakpoint on DONE ends a run
19/ 14 : C8 96 FA 64 7D AF 32 E1 TABLE: DB 0C8H, 96H, 0FAH, 64H, 7DH, 0AFH, 32H, 0E1H, 4BH, 0F0H
1C : 4B F0
20/ 1E : END
$ cat sum.hex
:100000009000147A0AE4FEFFE4932EFEE43FFFA37F
:0E001000DAF680FEC896FA647DAF32E14BF05E
:00000001FF
$ p2hex -q -l 8 sum.p sum8.hex
$ cat sum8.hex
:080000009000147A0AE4FEFFEF
:08000800E4932EFEE43FFFA388
:08001000DAF680FEC896FA64DE
:060018007DAF32E14BF068
:00000001FF
# compare the bytes sum.hex and sum8.hex load
same 30 bytes at the same addresses (0000H-001DH)
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 build_session.py
Read the listing from the left: the source line number, the address in hexadecimal, the bytes, then the source text.
MOV DPTR,#TABLE at address 0 became 90 00 14, the opcode: The group of bits inside an instruction that tells the processor which operation to run, with the remaining bits of the instruction selecting registers, immediates and modes. The ISA fixes where each opcode sits and what it means. More in “What assembly language is and why it matters” 90H followed by the address of
TABLE, 0014H, high byte first: AS worked out where the label falls before it wrote a byte. DJNZ R2,LOOP at 0010H
became DA F6, and F6H is not an address but a distance, −10 counted from the next instruction at 0012H, which lands
on LOOP at 0008H. Lines such as CPU and ORG give orders to the assembler and leave no bytes. Both HEX files load
the same 30 bytes at 0000H to 001DH, as the last line of the run says: the line length changes only how the bytes are
packed.
The assembler is also the first check a program gets. Intel’s assembler guide for the MCS-51 requires a hexadecimal number to begin with a digit from 0 to 9, and AS keeps that rule. slip.asm writes 255 twice:
; slip.asm: one constant written two ways.
CPU 8051
ORG 0000H
MOV A,#FFH ; no leading 0: AS reads FFH as a name
MOV A,#0FFH ; leading 0: AS reads the number 255
END """Assemble slip.asm, where one hexadecimal constant lacks its leading 0, and read AS's answer."""
import pathlib
import sys
sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import eightbit
eightbit.run("""
asl -q -cpu 8051 -L -olist slip.lst -o slip.p 8051/slip.asm
""")
eightbit.listing("slip.lst") # the line AS refused, and the one it assembled Output
$ asl -q -cpu 8051 -L -olist slip.lst -o slip.p 8051/slip.asm
> > > slip.asm(4):20: error: symbol undefined
[exit status 2]
# slip.lst: line/address, bytes, source
1/ 0 : ; slip.asm: one constant written two ways.
2/ 0 : CPU 8051
3/ 0 : ORG 0000H
> > > slip.asm(4):20: error: symbol undefined
4/ 0 : MOV A,#FFH ; no leading 0: AS reads FFH as a name
5/ 0 : 74 FF MOV A,#0FFH ; leading 0: AS reads the number 255
6/ 2 : END
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 slip_session.py
Writing FFH for 255
Without the leading 0, AS reads FFH as a name, reports error: symbol undefined for line 4 and ends with exit
status 2: fix the line before you load anything. With it, MOV A,#0FFH assembled to 74 FF. Give every
hexadecimal constant that starts with A to F a leading 0: 0FFH, 0C8H, 0E1H.
Checkpoint
Check: From source to HEX with AS and p2hex
Every Intel HEX line carries its address and a checksum
The second line of sum.hex is :0E001000DAF680FEC896FA647DAF32E14BF05E. Intel described this format for its 8080
development systems: a record starts with a colon, and every field after it is made of pairs of hex digits, one byte
each.
| Field | In this line | What it holds |
|---|---|---|
| Record mark | : |
The start of a record |
| Byte count | 0E |
The number of data bytes: 14 |
| Load address | 0010 |
Where the first data byte goes, high byte first |
| Record type | 00 |
00 for data, 01 for the end of the file |
| Data | DA F6 … 4B F0 |
The 14 bytes |
| Checksum | 5E |
The byte that brings the sum of the record to 00H |
Intel defines the checksum as the negative of the sum of the bytes from the byte count to the last data byte, modulo 256, so that every byte of a correct record, checksum included, adds up to 00H. A loader adds them and knows at once whether the line arrived intact. Step through the sum for this line:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- The line p2hex wrote. The second line of sum.hex is :0E001000DAF680FEC896FA647DAF32E14BF05E. After the colon, every two hex digits are one byte: 19 bytes in all.
- Byte count. The first byte, 0EH, says that 14 data bytes follow. A loader uses it to find where the data ends and the checksum begins.
- Load address. The next two bytes, 00H and 10H, are the address of the first data byte, high byte first: 0010H. The 14 bytes fill 0010H to 001DH. Both bytes join the sum: 0EH + 00H + 10H = 1EH.
- Record type. Type 00 marks a data record. The last line of the file, :00000001FF, has type 01, end of file, and no data. Adding 00H leaves the sum at 1EH.
- The data bytes. DA F6 is DJNZ R2,LOOP, 80 FE is SJMP DONE, and C8H to F0H are the ten table bytes. The 14 bytes add up to 84H modulo 256, so the running sum is 1EH + 84H = A2H.
- Checksum. The last byte is chosen so that the total comes out at 00H: 100H minus A2H is 5EH, the negative of A2H modulo 256. Every byte of a correct record, checksum included, adds up to 00H.
- One digit typed wrong. Type the last table byte as F1 instead of F0 and the total becomes 01H. A loader that adds the bytes refuses the line rather than load a wrong byte into memory.
In numbers: 0EH + 00H + 10H + 00H = 1EH; the 14 data bytes add 84H, which makes A2H; and 100H − A2H = 5EH. The end
record follows the same rule: :00000001FF holds 00H, 00H, 00H and 01H, which add up to 01H, and 100H − 01H = FFH.
To add up the bytes of a line of your own in hexadecimal, the
programmer calculator does the sums.
The program below makes the same check in Python: it splits each line into its fields and adds all its bytes. Its fourth line is line 2 again with one digit changed, the last table byte F0 typed as F1. Press Edit, change a digit of another line, and run it again:
"""Check Intel HEX lines: split each record into its fields and test its checksum."""
LINES = [
":100000009000147A0AE4FEFFE4932EFEE43FFFA37F", # the three lines p2hex wrote for sum.asm
":0E001000DAF680FEC896FA647DAF32E14BF05E",
":00000001FF",
":0E001000DAF680FEC896FA647DAF32E14BF15E", # line 2 again, with the table's last byte F0 typed as F1
]
KINDS = {0: "data", 1: "end of file"}
def check(line):
"""Return a short report on one record."""
if not line.startswith(":"):
return "no record mark"
raw = bytes.fromhex(line[1:])
count, address, kind = raw[0], raw[1] << 8 | raw[2], raw[3]
data = raw[4:-1]
if len(data) != count:
return f"the length field says {count} bytes, the line holds {len(data)}"
total = sum(raw) % 256
verdict = "sum 00H, line accepted" if total == 0 else f"sum {total:02X}H, line refused"
return f"{count} bytes at {address:04X}H, type {kind:02X} ({KINDS.get(kind, 'other')}), {verdict}"
for line in LINES:
print(line)
print(" ", check(line)) Output
:100000009000147A0AE4FEFFE4932EFEE43FFFA37F 16 bytes at 0000H, type 00 (data), sum 00H, line accepted :0E001000DAF680FEC896FA647DAF32E14BF05E 14 bytes at 0010H, type 00 (data), sum 00H, line accepted :00000001FF 0 bytes at 0000H, type 01 (end of file), sum 00H, line accepted :0E001000DAF680FEC896FA647DAF32E14BF15E 14 bytes at 0010H, type 00 (data), sum 01H, line refused
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 check_hex.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
One changed digit moved the total from 00H to 01H, and a loader that checks sums refuses such a line instead of putting a wrong byte into memory. Any change to a single byte changes the sum, so the checksum catches every single-byte error; two changes that cancel out, +1 in one byte and −1 in another, leave the total as it was and go unseen.
Key idea
An Intel HEX record is a byte count, a load address, a type, the data and a checksum, and all its bytes add up to 00H modulo 256: a line whose total is anything else is refused.
Checkpoint
Check: The fields and the checksum of an Intel HEX record
A breakpoint stops the run where registers can be read
sum.asm ends with DONE: SJMP DONE, a jump to itself. A microcontroller program has no operating system to return
to, so it parks in such a loop, and a simulator running it needs a breakpoint: An address, usually of an instruction, where a debugger stops the program just before that instruction runs. A breakpoint can be set by a function's name, which the debugger looks up in the symbol table, or by the address itself. More in “GDB and LLDB for assembly” on DONE,
at 0012H in the listing, or the run never ends. uCsim’s 8051 simulator, s51, reads its commands from a file, the way
a debugger: A program that runs another program under its control, so it can stop it, run it one instruction at a time and read or change its registers and memory. GDB and LLDB are the common ones. More in “GDB and LLDB for assembly” does in batch mode: Running a debugger with no interactive prompt: it reads its commands from the command line or a file, runs them in order and exits. gdb -batch and lldb --batch both do it, which makes a session repeatable. More in “GDB and LLDB for assembly”, and echoes the commands it
read before their results:
"""Load sum.hex into uCsim's 8051 model, stop at DONE, and read the registers, memory and time of the run."""
import pathlib
import sys
sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import eightbit
hexfile = eightbit.asm51("8051/sum.asm", show=False)
eightbit.s51(hexfile, """
info registers
break 0x0012
run
dump /h iram 0x00 0x07
state
""") Output
$ s51 -t 8051 -R 1 sum.hex < sum.s51
info registers
break 0x0012
run
dump /h iram 0x00 0x07
state
quit
R0 R1 R2 R3 R4 R5 R6 R7
fd fa fc 27 e9 1a d9 27
@R0 00 . ACC= 0x00 0 . B= 0x00
@R1 00 . PSW= 0x00 CY=0 AC=0 OV=0 P=0
SP 0x07 -> 27 d9 1a e9 27 fc fa fd
DPTR= 0x00000000 @DPTR= 0xe1 225 .
0x0000 90 00 14 MOV DPTR,#0x0014
Breakpoint 1 at 0x000012 (cond="")
0x0012 F 80 fe SJMP 0x0012
Simulation started, PC=0x000000
30 words read from sum.hex
Stop at 0x000012: (104) Breakpoint
R0 R1 R2 R3 R4 R5 R6 R7
fd fa 00 27 e9 1a 36 06
@R0 00 . ACC= 0x06 6 . B= 0x00
@R1 00 . PSW= 0x00 CY=0 AC=0 OV=0 P=0
SP 0x07 -> 06 36 1a e9 27 00 fa fd
DPTR= 0x0000001e @DPTR= 0x09 9 .
0x0012 F 80 fe SJMP 0x0012
F 0x000012
Simulated 1512 ticks (1.367e-04 sec)
0x00 fd fa 00 27 e9 1a 36 06 ...'..6.
CPU state= OK PC= 0x000012 frequency= 11059200 HZ
Operation since last reset= 180 vclks
Inst= 96 Fetch= 108 Read= 50 Write= 22
Total time since last reset= 0.000136718750000 sec (1512 clks)
Time in isr = 0.000000000000000 sec (0 clks) 0.00%
Time in idle= 0.000000000000000 sec (0 clks) 0.00%
Most value of stack pointer= 0x000007
Simulation: stopped
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 breakpoint_session.py
The first info registers shows the state before any instruction ran. break 0x0012 and run then start the
program at 0000H, and it stops just before the instruction at 0012H. The second register display is the result:
| uCsim printed | What it means |
|---|---|
R6 36 and R7 06 |
The total, R7:R6 = 0636H = 1590, the sum of the ten table bytes |
R2 00 |
The counter ran down from 10 to 0 |
PSW= 0x00 CY=0 |
No flag left set: the last ADD carried into R7, and the ADDC after it left no carry |
DPTR= 0x0000001e |
One past the last table byte, 001DH |
0x00 fd fa 00 27 e9 1a 36 06 |
R0 to R7 again, read as the internal RAM bytes 00H to 07H |
1512 clks at 11059200 HZ |
1,512 clock periods: 136.7 µs |
R0 to R7 are not separate from memory: with register bank 0 selected they are the first eight bytes of the 8051’s
internal RAM, which is why dump /h iram 0x00 0x07 shows the same bytes. The time can be checked against Intel’s
MCS-51 manual, which gives the length of every instruction in oscillator periods; a
machine cycle: The unit in which 8051 instructions are timed: 12 periods of the oscillator, so its length depends on the crystal a board uses. More in “The 8051: memory map, registers and SFRs” is 12 of them. The five instructions before the loop take 6 cycles and
each pass of the loop takes 12, so the run lasts 6 + 10 × 12 = 126 cycles, or 1,512 periods, and at the default
crystal of uCsim, 11.0592 MHz, that is 1,512 ÷ 11,059,200 s = 136.7 µs, the figure state printed.
Stop on the last instruction, not after a count
Put the breakpoint on the address of the program’s last instruction, read from the listing: DONE at 0012H here. A run limited by a number of steps stops wherever that number runs out, often inside the loop, and the registers then show a total that is not finished.
Checkpoint
Check: Running to a breakpoint and reading the state
Power-on bytes are random, so a program sets what it reads
Before sum.asm ran, uCsim showed R0 to R7 as fd fa fc 27 e9 1a d9 27. The program had written nothing yet: those
bytes stand for the power-on state: The values registers and memory hold the moment a chip powers up, before any program runs. Intel's manuals say that the 8051's internal RAM, R0 to R7 included, is indeterminate at power-up and kept through a reset, and that a reset does not set the 8085's registers A to L or its flags. of a real chip. Intel’s MCS-51 manual says that
the internal RAM, where R0 to R7 live, is not affected by reset and holds indeterminate values at power-up, and its
MCS-80/85 manual says that a reset does not set the 8085’s registers A to L or its flags. uCsim fills them with random
values drawn from a seed, set with -R; every run here passes one, so the same bytes come back each time. Run
sum.asm with seeds 1 and 7:
"""Run sum.asm twice in uCsim with two power-on seeds: other starting bytes, the same total in R7:R6."""
import pathlib
import sys
sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import eightbit
hexfile = eightbit.asm51("8051/sum.asm", show=False)
for seed in (1, 7):
eightbit.s51(hexfile, """
dump /h iram 0x00 0x07
break 0x0012
run
""", seed=seed) Output
$ s51 -t 8051 -R 1 sum.hex < sum.s51
dump /h iram 0x00 0x07
break 0x0012
run
quit
0x00 fd fa fc 27 e9 1a d9 27 ...'...'
Breakpoint 1 at 0x000012 (cond="")
0x0012 F 80 fe SJMP 0x0012
Simulation started, PC=0x000000
30 words read from sum.hex
Stop at 0x000012: (104) Breakpoint
R0 R1 R2 R3 R4 R5 R6 R7
fd fa 00 27 e9 1a 36 06
@R0 00 . ACC= 0x06 6 . B= 0x00
@R1 00 . PSW= 0x00 CY=0 AC=0 OV=0 P=0
SP 0x07 -> 06 36 1a e9 27 00 fa fd
DPTR= 0x0000001e @DPTR= 0x09 9 .
0x0012 F 80 fe SJMP 0x0012
F 0x000012
Simulated 1512 ticks (1.367e-04 sec)
$ s51 -t 8051 -R 7 sum.hex < sum.s51
dump /h iram 0x00 0x07
break 0x0012
run
quit
0x00 5d 1e 84 f3 dd e6 81 87 ].......
Breakpoint 1 at 0x000012 (cond="")
0x0012 F 80 fe SJMP 0x0012
Simulation started, PC=0x000000
30 words read from sum.hex
Stop at 0x000012: (104) Breakpoint
R0 R1 R2 R3 R4 R5 R6 R7
5d 1e 00 f3 dd e6 36 06
@R0 76 v ACC= 0x06 6 . B= 0x00
@R1 2c , PSW= 0x00 CY=0 AC=0 OV=0 P=0
SP 0x07 -> 06 36 e6 dd f3 00 1e 5d
DPTR= 0x0000001e @DPTR= 0x51 81 Q
0x0012 F 80 fe SJMP 0x0012
F 0x000012
Simulated 1512 ticks (1.367e-04 sec)
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 seed_session.py
Seed 7 starts R0 to R7 at 5d 1e 84 f3 dd e6 81 87, different in every byte, and the run still ends with R7:R6 = 0636H. sum.asm writes R2, R6, R7 and DPTR before it reads them, so whatever they held at power-on is gone by the time it matters. R0, R1, R3, R4 and R5 keep their power-on bytes, which is harmless while nothing reads them.
sum_noclear.asm is the same program without the line MOV R7,A. Every line of it is valid 8051 assembly, so nothing
in the build hints at the missing line:
; sum_noclear.asm: sum.asm with one line left out, so R7 (the high byte) is never cleared.
CPU 8051
ORG 0000H
START: MOV DPTR,#TABLE ; DPTR = address of the first table byte
MOV R2,#10 ; R2 counts the bytes still to add
CLR A
MOV R6,A ; total, low byte = 0
; (MOV R7,A is missing: R7 keeps its power-on value)
LOOP: CLR A
MOVC A,@A+DPTR ; A = the byte DPTR points at
ADD A,R6 ; low byte + table byte, CY = carry out
MOV R6,A
CLR A
ADDC A,R7 ; high byte + CY, starting from whatever R7 held
MOV R7,A
INC DPTR
DJNZ R2,LOOP
DONE: SJMP DONE ; one byte earlier than in sum.asm: 0011H
TABLE: DB 0C8H, 96H, 0FAH, 64H, 7DH, 0AFH, 32H, 0E1H, 4BH, 0F0H
END """Run sum_noclear.asm, which never clears R7, with the same two seeds: the total now depends on the seed."""
import pathlib
import sys
sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import eightbit
hexfile = eightbit.asm51("8051/sum_noclear.asm", show=False)
for seed in (1, 7):
eightbit.s51(hexfile, """
dump /h iram 0x00 0x07
break 0x0011
run
""", seed=seed) Output
$ s51 -t 8051 -R 1 sum_noclear.hex < sum_noclear.s51
dump /h iram 0x00 0x07
break 0x0011
run
quit
0x00 fd fa fc 27 e9 1a d9 27 ...'...'
Breakpoint 1 at 0x000011 (cond="")
0x0011 F 80 fe SJMP 0x0011
Simulation started, PC=0x000000
29 words read from sum_noclear.hex
Stop at 0x000011: (104) Breakpoint
R0 R1 R2 R3 R4 R5 R6 R7
fd fa 00 27 e9 1a 36 2d
@R0 00 . ACC= 0x2d 45 - B= 0x00
@R1 00 . PSW= 0x00 CY=0 AC=0 OV=0 P=0
SP 0x07 -> 2d 36 1a e9 27 00 fa fd
DPTR= 0x0000001d @DPTR= 0x2a 42 *
0x0011 F 80 fe SJMP 0x0011
F 0x000011
Simulated 1500 ticks (1.356e-04 sec)
$ s51 -t 8051 -R 7 sum_noclear.hex < sum_noclear.s51
dump /h iram 0x00 0x07
break 0x0011
run
quit
0x00 5d 1e 84 f3 dd e6 81 87 ].......
Breakpoint 1 at 0x000011 (cond="")
0x0011 F 80 fe SJMP 0x0011
Simulation started, PC=0x000000
29 words read from sum_noclear.hex
Stop at 0x000011: (104) Breakpoint
R0 R1 R2 R3 R4 R5 R6 R7
5d 1e 00 f3 dd e6 36 8d
@R0 76 v ACC= 0x8d 141 . B= 0x00
@R1 2c , PSW= 0x00 CY=0 AC=0 OV=0 P=0
SP 0x07 -> 8d 36 e6 dd f3 00 1e 5d
DPTR= 0x0000001d @DPTR= 0xd6 214 .
0x0011 F 80 fe SJMP 0x0011
F 0x000011
Simulated 1500 ticks (1.356e-04 sec)
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 noclear_session.py
With seed 1, R7 ends at 2DH; with seed 7, at 8DH. Each is the power-on byte plus the 06H that the carries added: 27H + 06H = 2DH, and 87H + 06H = 8DH. On a chip the same program would give different wrong answers on different boards, and the right one only when R7 happened to start at 0. In the 8085 runs of the next section, the core of Sim8085 starts registers A to L and every memory byte at 00H, which would hide such a bug; the random start of uCsim shows it at the first change of seed.
Key idea
A lab program sets every register, pointer, counter and result byte that it reads, and the stack pointer too if it uses the stack; running it with two seeds and getting the same result shows that it does.
Checkpoint
Check: Power-on bytes and the seed
Two assemblers and two simulators check each other
An assembler or a simulator can make a mistake that nothing in a single run reveals: a wrong encoding looks like any other byte, and a wrong flag like any other value. differential testing: A way of finding faults by giving the same input to two independent implementations and comparing what they produce: a difference points at a bug in one of them, so the technique needs no reference answer written in advance. finds such faults by giving the same input to two independent implementations and comparing what they produce. For 8-bit code that means two assemblers that share no code, compared on the bytes they load, and two simulators, compared on the registers, memory and time they report. add8.asm, an 8085 program, adds the bytes at 2050H and 2051H and stores the sum and the carry:
; add8.asm: add the bytes at 2050H and 2051H, store the sum at 2052H and the carry at 2053H.
ORG 0000H
LDA 2050H ; A = first byte
MOV B,A ; keep it in B
LDA 2051H ; A = second byte
ADD B ; A = A + B, CY = carry out of bit 7
STA 2052H ; the sum, low 8 bits
MVI A,00H
ACI 00H ; A = 00H + 00H + CY: the carry as a byte
STA 2053H ; the carry, 00H or 01H
HLT ; both simulators end the run here
ORG 2050H
DB 9AH, 7FH ; the two numbers to add Follow both paths before reading the run:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- One source, two translations. add8.asm adds the bytes at 2050H and 2051H and stores the sum and the carry. Two assemblers that share no code each translate it.
- AS writes add8.hex. asl -cpu 8085 and p2hex give 21 bytes: 19 of code from 0000H to 0012H, and the two numbers 9AH and 7FH at 2050H and 2051H.
- Sim8085 writes its own. Sim8085 has its own assembler, the one its browser app uses. It reads the same source file and writes add8-sim.hex.
- Same bytes, same addresses. The comparison reads both files as memory images, not as text, and finds the same 21 bytes at the same addresses. Had the two encoded a line differently, it would print that address with both values.
- Sim8085 runs it. The Sim8085 core starts with registers A to L at 0 and halts after 9 instructions and 79 T-states, with 19H at 2052H and the carry 01H at 2053H: 9AH + 7FH = 119H.
- uCsim runs it. ucsim_i8085 starts from random registers instead, and still halts after 79 simulated ticks with 9a 7f 19 01 at 2050H, because add8.asm writes every register it reads.
- The results agree. Same memory and the same time from two simulators that share no code, and the same 79 T-states as the 8085A timing table of the Intel manual gives. A slip of one tool in an instruction this program uses would have shown up as a difference here.
"""Assemble add8.asm with AS and with Sim8085's assembler, compare the bytes, then run it on two 8085 simulators."""
import pathlib
import sys
sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import eightbit
hexfile = eightbit.asm85("8085/add8.asm") # AS gives add8.hex, Sim8085's assembler add8-sim.hex
eightbit.sim8085(hexfile, "--dump 2050:4") # Sim8085's core runs to HLT, then shows 2050H-2053H
eightbit.ucsim85(hexfile, """
run
dump /h rom 0x2050 0x2053
""") Output
$ asl -q -cpu 8085 -L -olist add8.lst -o add8.p 8085/add8.asm $ p2hex -q add8.p add8.hex $ sim8085-asm 8085/add8.asm add8-sim.hex # compare the bytes add8.hex and add8-sim.hex load same 21 bytes at the same addresses (0000H-2051H) $ sim8085-run --dump 2050:4 add8.hex halted at 0012H after 9 instructions, 79 T-states A=01 B=9A C=00 D=00 E=00 H=00 L=00 SP=FFFF flags S=0 Z=0 AC=0 P=0 CY=0 2050: 9A 7F 19 01 $ ucsim_i8085 -R 1 add8.hex < add8.u85 run dump /h rom 0x2050 0x2053 quit Simulation started, PC=0x000000 21 words read from add8.hex Stop at 0x000013: (101) Halted F 0x000013 Simulated 79 ticks (7.900e-05 sec) 0x2050 9a 7f 19 01 ....
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 add8_session.py
AS and the assembler of Sim8085 gave the same 21 bytes at the same addresses. The comparison reads each file as the
memory it fills, address by address, so two HEX files with different line lengths, like the two of sum.asm, still
compare equal when they load the same bytes. Both simulators then left 19H and 01H at 2052H and 2053H: 9AH + 7FH =
119H, a low byte of 19H and a carry out of bit 7. Both counted 79 T-state: One period of the processor clock, the unit in which Intel's tables time 8085 instructions. How long a T-state lasts depends on the clock a board runs at. More in “The 8085 for assembly programmers: a bridge”, and so does
Intel’s 8085A timing table, a third reference that neither simulator wrote: 13 for each of the two LDA and the two
STA, 4 for MOV B,A and for ADD B, 7 for MVI and for ACI, and 5 for HLT, so 4 × 13 + 2 × 4 + 2 × 7 + 5 =
79.
A disagreement is the useful outcome, because it says where to look. Different bytes from the two assemblers point at one address, and the listing names its line. Different results from the two simulators have two usual causes: the program reads something it never set, so the starting bytes decide, as with sum_noclear.asm, or one simulator models an instruction differently from Intel’s manual, and the manual settles which. The 8085 lesson reproduces a difference of the second kind, in a flag after a decimal adjust. Agreement shows that the tools did the same thing with the source; whether the source does what the lab asked for is a separate test.
Checkpoint
Check: Two assemblers and two simulators
Which simulator fits which lab
The runs on this page use uCsim and the core of Sim8085 because both can be driven from commands and give the same output on every run. When a program is stepped by hand, what a tool shows on screen counts for more. Here are five tools for 8085 and 8051 programs, as their own pages describe them:
- Sim8085 is a development environment for the 8085 that runs in a browser, with an editor, an assembler and a debugger that steps through a program and shows its flags, registers and memory. It can be installed as an app and works offline once loaded; its documentation lists four CPU test suites it completes without an error and says that no differential test against physical 8085 silicon was performed.
- GNUSim8085 is a graphical desktop program for the 8085 that combines a simulator, an assembler and a debugger. It can write a program listing and lets you change memory and I/O ports while a program runs; its repository is marked “Needs maintainer”.
- uCsim is an instruction-set simulator: A program that executes another processor's instructions one by one in software, keeping its registers, flags, memory and cycle count, so that a program meant for a chip can be run and inspected on the host machine instead. that runs in a terminal and
covers the MCS-51 family and the MCS-80 and MCS-85 families among many other chips. It reads its commands from the
keyboard or a file, takes a crystal frequency with
-Xand a seed with-R, and shows ports and the serial port as text displays. - EdSim51DI is an 8051 simulator with a virtual board. Its page lists LEDs, switches, a keypad, four multiplexed 7-segment displays, a DC motor, an ADC, a comparator, a UART and a DAC drawn on an oscilloscope, so the effect of each line on the hardware can be watched as you step.
- Keil’s C51 evaluation tools are a compiler, an assembler, a linker and a debugger for the 8051 family. Keil’s limits page says they handle at most 2 Kbytes of object code and that programs start at offset 0x0800, so their output cannot go into a chip with less than 2 KB of on-chip ROM.
Answer the questions in the table for your own lab, and it marks the tools that fit:
Answer the questions above: the table then marks what fits your case and says why the rest does not.
| Tool | Chips it runs | What you watch | Same run again from a script | Limits to know |
|---|---|---|---|---|
| Sim8085 Runs in: a browser, or installed as an app; works offline once loaded | Mixed the 8085 only; no 8051 | Good an editor, then registers, flags and memory as you step | Poor its debugger is driven by hand in the browser | Good runs CPU exercisers without an error; its docs say it was not compared with real 8085 chips |
| GNUSim8085 Runs in: a desktop window | Mixed the 8085 only | Good a listing, plus memory and I/O ports you can change while it runs | Poor a graphical program, driven by hand | Mixed its repository is marked Needs maintainer |
| uCsim Runs in: a terminal, from typed commands or a command file | Good the MCS-51 family, the 8080 and the 8085, among many others | Mixed registers and memory as text; ports and the UART as text displays | Good reads its commands from a file; a fixed -R seed repeats the run byte for byte | Mixed no board picture: you read numbers |
| EdSim51DI Runs in: a desktop window with a virtual board | Mixed the 8051 only | Good LEDs, switches, keypad, 7-segment displays, motor, ADC and DAC on screen | Poor stepped by hand in its window | Mixed only the virtual devices its page lists |
| Keil C51 evaluation tools Runs in: the Keil tools for the 8051 family | Mixed the 8051 family only | Mixed its debugger, for programs of 2 KB or less | Mixed not covered by its limits page | Poor at most 2 KB of object code, and programs start at 0x0800 |
How the answers decide
- “Which chip does your lab program run on?” The 8085: rules out EdSim51DI and Keil C51 evaluation tools. EdSim51 and the C51 tools are for the 8051 family, and an 8085 program is not 8051 code.
- “Which chip does your lab program run on?” The 8051: rules out Sim8085 and GNUSim8085. Sim8085 and GNUSim8085 run 8085 code only.
- “Which chip does your lab program run on?” The 8051 and “Do you want LEDs, switches or a keypad drawn on screen while the program runs?” Yes, I want to see the devices: adds a caveat to uCsim. uCsim shows ports and the serial port as text displays; EdSim51 draws the LEDs, switches and keypad themselves.
- “Must the same run repeat exactly, from a script, so that you can compare its output with another?” Yes, it must repeat: adds a caveat to Sim8085, GNUSim8085 and EdSim51DI. These are driven by hand in a window. uCsim reads its commands from a file and, with the same -R seed, gives the same output on every run.
- “Can the program grow past 2 KB of object code?” Yes, it may: rules out Keil C51 evaluation tools. Keil's limits page says the C51 evaluation tools will not compile, assemble or link more than 2 Kbytes of object code.
- “Which chip does your lab program run on?” The 8051 and “Can the program grow past 2 KB of object code?” No, it stays small: adds a caveat to Keil C51 evaluation tools. Its programs start at 0x0800, so they cannot go into a chip with less than 2 KB of on-chip ROM.
In short: Sim8085 or GNUSim8085 for 8085 programs stepped by hand, EdSim51 when an 8051 program drives LEDs, switches or a display, and uCsim when runs must repeat exactly or be compared with another tool.
What GTU and AKTU lab lists name
GTU’s B.E. course 3160712, Microprocessor and Interfacing, lists a “Modern desktop PC with open source 8085 Simulator” as major equipment, and its diploma course 4341101 has a practical to “Use 8051 Simulation tool / Trainer kit for running ASM programs”. AKTU’s BEC-552 lab marks several of its 8085 experiments “Through Virtual Lab Link”. The tools above run such programs, each for its own chip; look in your course’s lab manual for the one its lab uses.
Checkpoint
Check: Choosing a simulator for a lab
Practise
The exercise turns the checker around: instead of reading records, you write them, and its sample tests expect exactly the lines that p2hex wrote for sum.asm, with 16 and with 8 bytes a line, and the line that holds the two numbers of add8.asm.
Exercise · Easy · Python
Write Intel HEX records and their checksums
An Intel HEX file is lines of text, one record a line. Write the two Python functions that make such lines, as p2hex does:
record(address, data, kind=0)returns one record as a string: a colon, then, in upper-case hexadecimal with two digits a byte, the byte count (the number of data bytes), the 16-bit loadaddresswith its high byte first, the record typekind(0 for data, 1 for the end of the file), the data bytes, and the checksum. The checksum is the byte that makes all the bytes from the count to the checksum add up to 0 modulo 256.datais abytesobject or a list of whole numbers from 0 to 255, at most 255 of them, andaddressis from 0 to 0xFFFF.hex_file(start, data, per_line=16)returns the list of lines of a whole file that loadsdataat consecutive addresses fromstart: data records ofper_linebytes each (the last one may be shorter), then the end record:00000001FF.
So record(0x2050, [0x9A, 0x7F]) is ":022050009A7F75", the line that the two numbers of add8.asm became, and hex_file(0x0000, data) for the 30 bytes of sum.asm gives the three lines of sum.hex. The sample tests use the lines that p2hex wrote in the lesson's runs.
Starter code · intel_hex.py
def record(address, data, kind=0):
"""Return one Intel HEX record: ':', the count, the address, the type, the data and the checksum, in upper case."""
return "" # replace with your code
def hex_file(start, data, per_line=16):
"""Return the lines of an Intel HEX file that loads data from start, per_line bytes a line, then the end record."""
return [] # replace with your code The sample tests · test_intel_hex.py
"""Sample tests: the lines that p2hex wrote for sum.asm and add8.asm in the lesson's runs."""
from intel_hex import hex_file, record
SUM_ASM = bytes.fromhex(
"9000147A0AE4FEFFE4932EFEE43FFFA3" # 0000H-000FH: the code up to INC DPTR
"DAF680FE" # 0010H-0013H: DJNZ R2,LOOP and SJMP DONE
"C896FA647DAF32E14BF0" # 0014H-001DH: the ten table bytes
)
def test_first_line_of_sum_hex():
assert record(0x0000, SUM_ASM[:16]) == ":100000009000147A0AE4FEFFE4932EFEE43FFFA37F"
def test_second_line_of_sum_hex():
assert record(0x0010, SUM_ASM[16:]) == ":0E001000DAF680FEC896FA647DAF32E14BF05E"
def test_end_record():
assert record(0x0000, b"", 1) == ":00000001FF"
def test_the_two_numbers_of_add8_at_2050h():
assert record(0x2050, [0x9A, 0x7F]) == ":022050009A7F75"
def test_sum_hex_with_16_bytes_a_line():
assert hex_file(0x0000, SUM_ASM) == [
":100000009000147A0AE4FEFFE4932EFEE43FFFA37F",
":0E001000DAF680FEC896FA647DAF32E14BF05E",
":00000001FF",
]
def test_sum_hex_with_8_bytes_a_line():
assert hex_file(0x0000, SUM_ASM, per_line=8) == [
":080000009000147A0AE4FEFFEF",
":08000800E4932EFEE43FFFA388",
":08001000DAF680FEC896FA64DE",
":060018007DAF32E14BF068",
":00000001FF",
] A hint
Build a list of the record's bytes before you write any text: the count, the two bytes of the address (high byte first), the type, then the data. The checksum follows from their sum; think about what it must be when that sum is already a multiple of 256. For hex_file, walk through data per_line bytes at a time and keep the address in step with the offset.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Three more to work out on paper, then check with the tools above:
- Work out the checksum of a record that loads the two bytes 3EH and 05H at 8000H, then add the line to
LINESin the checker and run it. - Change
MOV R2,#10in sum.asm toMOV R2,#9. Which byte of the listing changes, and what would R7:R6 hold at the breakpoint? - An 8085 program reads the byte at 2060H before it ever stores one there. Which of the two simulators of the add8 run would show the bug as a result that changes with the seed, and what would the other one show?
The questions below cover all six sections, from the bytes of a listing to the choice of a simulator, and each one explains the slip behind a wrong choice:
Practice that adapts to you
Practise this lesson
14 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
What does each field of an Intel HEX record hold, and how is its checksum checked?
After the colon that starts a record come pairs of hex digits, one byte each: the byte count, the load address with
its high byte first, the record type (00 for data, 01 for the end of the file), the data bytes and the checksum. The
checksum is the two’s complement of the sum of every byte from the count to the last data byte, modulo 256, so all
the bytes of a correct record, checksum included, add up to 00H. A loader adds them and refuses a line whose total is
anything else. In :022050009A7F75, the line that the two numbers of add8.asm became, the bytes 02H, 20H, 50H, 00H,
9AH and 7FH add up to 18BH, whose low byte is 8BH, and 100H − 8BH = 75H, the last byte of the line.
Why does a simulator such as uCsim start a program with random register and RAM values, and what should a lab program do about it?
A real chip does not start from zero either. Intel’s MCS-51 manual says that the internal RAM of the 8051, where R0
to R7 live, holds indeterminate values at power-up and is not changed by a reset, and its MCS-80/85 manual says that
a reset does not set the 8085’s registers A to L or its flags. uCsim imitates that with random bytes drawn from a
seed, which -R sets, so that a run can still be repeated exactly. A lab program should therefore write every
register, pointer, counter and memory byte before it reads it, and set the stack pointer itself if it uses the stack.
Two seeds make a quick test: a version of sum.asm that never cleared R7 ended with 2D36H under seed 1 and 8D36H under
seed 7, while the full program gave 0636H under both.
Two questions go further, and their first steps and follow-ups are in the module’s bank: a program moved by editing its HEX file, and a fault that two agreeing simulators both miss.
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to senior
A lab asks you to move a 19-byte 8085 program from 0000H to 8000H by editing only the load address of each Intel HEX record. The program jumps to its own label, loads the address of a byte inside itself and stores to 2052H. Does that edit move the program? Show the bytes.
-
Fresher to senior
A classmate's add8.asm stores the carry byte at 2053H only when there is one: JNC skips MVI A,01H and STA 2053H. For the lab's 9AH and 7FH, both assemblers give the same 22 bytes and both simulators store 19H and 01H in 79 T-states. Is the program right? Show what fails, and fix the check.
Flashcards · 8 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
How does the AS assembler route turn 8051 or 8085 source into a file a loader takes?
AS translates the Intel-syntax source into a code file and a listing that pairs every line with its address and bytes, and p2hex writes those bytes out as Intel HEX.
-
Why does AS refuse the constant FFH, and how is it written instead?
A hexadecimal constant must begin with a digit, or it reads as a name, so FFH is refused and 0FFH is the accepted form.
-
What does an Intel HEX record hold, and how is its checksum checked?
After the colon: a byte count, a 16-bit load address with its high byte first, a record type (00 data, 01 end of file), the data bytes and a checksum. Every byte of a correct record, checksum included, adds up to 00H modulo 256; a loader refuses any line whose total differs.
-
How was the checksum of the record :022050009A7F75 worked out?
The bytes 02, 20, 50, 00, 9A and 7F add up to 18BH, whose low byte is 8BH, and 100H minus 8BH is 75H, the record's last byte.
-
What does uCsim show at a breakpoint, and how did its clock count for sum.asm compare with Intel's timing?
Registers, flags, internal RAM and the clock count. For sum.asm its 1,512 clock periods matched Intel's timing: 126 machine cycles of 12 periods, 136.7 microseconds at 11.0592 MHz.
-
Why does uCsim start a program from random register and RAM values?
Real chips hold unknown bytes at power-on, so the simulator starts from values chosen by a seed. A program that sets every register, pointer and byte it reads gives the same result for every seed; one that does not changes with the seed.
-
How do two assemblers and two simulators check each other?
Compare the assemblers on the bytes they produce and the simulators on registers, memory and time for the same program. A disagreement exposes a fault in one of the tools and points at the line or starting state that differs.
-
Which tools fit which 8-bit lab?
Sim8085 and GNUSim8085 run 8085 programs in a window, EdSim51 shows an 8051 on a virtual board, uCsim runs both kinds of chip from command files, and Keil's C51 evaluation tools stop at 2 KB of object code.
Key takeaways
- AS translates Intel-syntax source into a code file and a listing that pairs every line with its address and bytes, p2hex writes those bytes as Intel HEX, and AS refuses a hexadecimal constant such as FFH that lacks a leading digit.
- An Intel HEX record holds a byte count, a load address, a record type, the data and a checksum chosen so that all of its bytes add up to 00H modulo 256, and a loader refuses any line whose total differs.
- uCsim runs a HEX file to a breakpoint and shows registers, flags, internal RAM and the clock count, and for sum.asm its 1,512 clock periods matched Intel’s timing: 126 machine cycles, 136.7 µs at 11.0592 MHz.
- Registers and RAM hold unknown bytes at power-on, so uCsim starts from random values chosen by a seed; a program that sets everything it reads gives the same result for every seed, and one that does not changes with it.
- Two assemblers compared on the bytes they load and two simulators compared on registers, memory and time expose a fault in either tool, and a disagreement points at the line or the starting state that differs.
- Sim8085 and GNUSim8085 run 8085 programs in a window, EdSim51 shows an 8051 on a virtual board, uCsim runs both kinds of chip from command files, and Keil’s C51 evaluation tools stop at 2 KB of object code.
Terms in this lesson
- differential testing
- A way of finding faults by giving the same input to two independent implementations and comparing what they produce: a difference points at a bug in one of them, so the technique needs no reference answer written in advance.
- instruction-set simulator
- A program that executes another processor's instructions one by one in software, keeping its registers, flags, memory and cycle count, so that a program meant for a chip can be run and inspected on the host machine instead.
- Intel HEX
- A text file format for machine-code bytes, one record a line. Each record carries a byte count, a 16-bit load address, a type (00 data, 01 end of file), the data bytes and a checksum chosen so that all its bytes add up to 00H modulo 256.
- power-on state
- The values registers and memory hold the moment a chip powers up, before any program runs. Intel's manuals say that the 8051's internal RAM, R0 to R7 included, is indeterminate at power-up and kept through a reset, and that a reset does not set the 8085's registers A to L or its flags.
References
- MCS-51 Microcontroller Family User's Manual (memory organisation, reset values, instruction timing) (Intel Corporation)
- MCS-51 Macro Assembler User's Guide (number syntax, ORG, DB and END) (Intel Corporation)
- MCS-80/85 Family User's Manual (RESET IN, 8085A instruction timing) (Intel Corporation)
- Utilities User's Guide for 8080-Based Development Systems (Appendix A, the hexadecimal object format) (Intel Corporation)
- Macroassembler AS: releases and user manual (asl, P2HEX) (Alfred Arnold)
- uCsim: instruction set simulator for microcontrollers (Daniel Drotos)
- uCsim source and documentation (invocation, commands, VCD output) (Daniel Drotos)
- Sim8085: README (Debjit Biswas)
- Sim8085 documentation: Accuracy and CPU Validation (Debjit Biswas)
- GNUSim8085: README (GNUSim8085 project)
- EdSim51DI: the 8051 simulator for students and educators (James Rogers)
- Keil product limits: C51 evaluation tools (Arm Keil)
- GTU syllabus 3160712: Microprocessor and Interfacing (Gujarat Technological University)
- GTU syllabus 4341101: Microprocessor and Microcontroller (Gujarat Technological University)
- AKTU B.Tech ECE third-year syllabus (BEC-552 Microprocessor and Microcontroller Lab) (Dr. A.P.J. Abdul Kalam Technical University)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress