Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

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.

  • Beginner
  • 30 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free lesson, Pro answers: 30 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

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: ten table bytes added into R7:R6 ASM · 8051/sum.asm
; 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:

Interactive · stage by stage From sum.asm to a run in uCsim Step through the files and programs that sum.asm passes through, from the source text to the simulator.
From sum.asm to a run in uCsim, step 7 of 7. Showing sum.asm, asl, sum.p and sum.lst (30 bytes from 0000H), p2hex, sum.hex (3 lines, 30 bytes), s51 (uCsim) (30 words read). In focus: asl, refused.line refusedsum.asmIntel-syntax sourceaslAS assembles itsum.p and sum.lst30 bytes from 0000Hp2hexwrites HEX recordssum.hex3 lines, 30 bytess51 (uCsim)30 words readerror

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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:

AS and p2hex on sum.asm: the listing and two HEX files Python · build_session.py
"""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: 255 written two ways ASM · 8051/slip.asm
; 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
AS refuses one line of slip.asm Python · slip_session.py
"""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

  1. Question 1 of 2 The listing shows 90 00 14 for MOV DPTR,#TABLE at address 0. What are the bytes 00 and 14?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: The address of TABLE, 0014H, high byte first

    The listing pairs each source line with its address and bytes. 90H is the opcode of MOV DPTR,#data16, and the two bytes after it are the 16-bit value, here the address that AS gave the label TABLE: 0014H.

  2. Question 2 of 2 Which line did AS assemble to 74 FF in the recorded run of slip.asm?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: MOV A,#0FFH

    Intel's MCS-51 assembler guide requires a hexadecimal number to begin with a digit from 0 to 9, and AS follows it. Write 0FFH, 0C8H and 0E1H, never FFH, C8H or E1H.

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:

Interactive · step by step One Intel HEX record, field by field Step through the second line of sum.hex. The left column is the record's fields; the box on the right keeps the sum of the bytes read so far.
One Intel HEX record, field by field, step 7 of 7. Showing :, 0E, 0010, 00, DA F6 … 4B F0 (DA F6 … 4B F1), 5E, Sum of the bytes (total 01H: refused). In focus: DA F6 … 4B F0, Sum of the bytes.:record mark0Ebyte count: 140010load address00record type: dataDA F6 … 4B F0DA F6 … 4B F15EchecksumSum of the bytestotal 01H: refused

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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:

An Intel HEX line checker to edit and run Python · hexcheck/check_hex.py
"""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

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

  1. Question 1 of 2 A record loads the two bytes 9AH and 7FH at 2050H, so it starts :022050009A7F. Which two hex digits end it?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: 75

    Add every byte from the count to the last data byte modulo 256, then take the negative: 100H minus 8BH = 75H. The line p2hex wrote for add8.asm, :022050009A7F75, ends exactly so.

  2. Question 2 of 2 In the record :0E001000DAF680FE…, at which address does the byte DA go?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: 0010H

    The fields after the colon are the byte count (0E), the load address (0010), the type (00) and then the data, so DA goes to 0010H, where the listing shows DJNZ R2,LOOP.

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:

sum.hex in uCsim: the registers before the run and at the breakpoint Python · breakpoint_session.py
"""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

  1. Question 1 of 2 At the breakpoint uCsim showed R6 as 36 and R7 as 06. Which total did sum.asm compute?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: 0636H, which is 1590

    The ten bytes add up to 1590 = 0636H. R6 holds the low byte, 36H, and R7 the carries collected in the high byte, 06H.

  2. Question 2 of 2 The run of sum.asm took 1,512 clock periods. How many microseconds would it take with uCsim started with -X 12M, a 12 MHz crystal?

    Type a number.

    A hint

    Each microsecond holds twelve million of what, at 12 MHz?

    Show the answer: Question 2 of 2

    Answer: 126 µs

    1,512 periods divided by 12,000,000 periods a second is 0.000126 s, 126 µs. At 12 MHz a machine cycle of 12 periods lasts exactly 1 µs, and the run is 126 machine cycles.

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:

sum.asm with power-on seeds 1 and 7 Python · seed_session.py
"""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: R7 is never cleared ASM · 8051/sum_noclear.asm
; 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
sum_noclear.asm with seeds 1 and 7 Python · noclear_session.py
"""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

  1. Question 1 of 2 sum_noclear.asm runs on a board whose R7 happens to hold 10H at power-on. What does R7:R6 hold at DONE?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: 1636H

    The high byte ends at the power-on value of R7 plus 06H: 27H gave 2DH and 87H gave 8DH in the recorded runs, so 10H gives 16H, and R6 is 36H whatever happens.

  2. Question 2 of 2 Why did sum.asm end with 0636H under both seed 1 and seed 7?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: It writes R2, R6, R7 and DPTR before it reads them

    The random start bytes differ between seeds, but sum.asm never reads a register before it writes it, so its total does not depend on them. sum_noclear.asm reads R7 first, and its total moved with 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: two bytes added, the sum and the carry stored ASM · 8085/add8.asm
; 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:

Interactive · step by step add8.asm through two assemblers and two simulators Step through both paths. Each comparison box says what was compared and what the run found.
add8.asm through two assemblers and two simulators, step 7 of 7. Showing add8.asm, AS and p2hex (21 bytes), Sim8085 (add8-sim.hex), Compare bytes (same 21 bytes), Sim8085 core (19H, 01H, 79 T), ucsim_i8085 (19H, 01H, 79 T), Compare results (all the same). In focus: Compare results.add8.asmone 8085 sourceAS and p2hex21 bytesSim8085add8-sim.hexCompare bytessame 21 bytesSim8085 core19H, 01H, 79 Tucsim_i808519H, 01H, 79 TCompare resultsall the same

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.

add8.asm with AS and Sim8085, run on Sim8085 and ucsim_i8085 Python · add8_session.py
"""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

  1. Question 1 of 2 Two HEX files of one program differ as text: one has 3 lines, the other 5. What does that tell you about the program?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Nothing yet: compare the bytes the two files load

    A HEX file is packaging. Compare the memory images the files fill, address by address, as the comparison of the add8 run did.

  2. Question 2 of 2 Sim8085 and uCsim end an 8085 program with different bytes at 2060H, and the uCsim result changes when the seed changes. What is the likeliest cause?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The program reads a register or a byte it never set

    Of the two usual causes of a disagreement, a seed-dependent result points at the program: it reads state that it never set. Fix the program so that it writes the value first, then both simulators agree for every seed.

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 -X and 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:

Interactive · answer and compare Which simulator fits your lab? Answer four questions about your lab. The table marks the tools that fit and says why the others do not.
Which chip does your lab program run on?
Do you want LEDs, switches or a keypad drawn on screen while the program runs?
Must the same run repeat exactly, from a script, so that you can compare its output with another?
Can the program grow past 2 KB of object code?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

Five simulators and tool sets for 8085 and 8051 lab programs, against what decides between them
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

  1. Question 1 of 2 An 8051 lab asks for a pattern on eight LEDs that changes with eight switches, and no board is at hand. Which tool lets you watch it?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: EdSim51DI

    The choice follows the chip first (8051) and then what you need to see (devices on a board): EdSim51DI. uCsim would run the same program and print the port values.

  2. Question 2 of 2 An 8051 program grows to 3 KB of object code. What do Keil's C51 evaluation tools do with it?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: They do not compile, assemble or link it

    The C51 evaluation tools stop at 2 Kbytes of object code, and their programs start at 0x0800. A larger program needs another assembler, such as AS, or the full tools.

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:

  1. 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 load address with its high byte first, the record type kind (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. data is a bytes object or a list of whole numbers from 0 to 255, at most 255 of them, and address is from 0 to 0xFFFF.
  2. hex_file(start, data, per_line=16) returns the list of lines of a whole file that loads data at consecutive addresses from start: data records of per_line bytes 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.

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:

  1. Work out the checksum of a record that loads the two bytes 3EH and 05H at 8000H, then add the line to LINES in the checker and run it.
  2. Change MOV R2,#10 in sum.asm to MOV R2,#9. Which byte of the listing changes, and what would R7:R6 hold at the breakpoint?
  3. 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.

  1. Question 1 of 14 What does p2hex do in the AS toolchain?

    Choose one answer.

    Show the answer: Question 1 of 14

    Answer: It writes the bytes of the code file as Intel HEX records

    asl turns the source into a code file and a listing; p2hex turns the code file into Intel HEX, the text that simulators and chip programmers load.

  2. Question 2 of 14 DJNZ R2,LOOP sits at 0010H and assembles to DA F6, with LOOP at 0008H. If LOOP were at 0006H instead, which second byte would AS write?

    Choose one answer.

    Show the answer: Question 2 of 14

    Answer: F4

    DJNZ stores a signed 8-bit distance from the address of the next instruction. For LOOP at 0008H that was 0008H - 0012H = -10 = F6H; for 0006H it is -12 = F4H.

  3. Question 3 of 14 Which line ends the Intel HEX files that p2hex wrote in the recorded runs?

    Choose one answer.

    Show the answer: Question 3 of 14

    Answer: :00000001FF

    The end-of-file record p2hex writes has the type 01 and no data: :00000001FF. The AS manual notes that the literature describes the last line in more than one way, and p2hex writes this one by default.

  4. Question 4 of 14 A record loads the single byte 76H, an 8085 HLT, at 0012H, so it starts :0100120076. Which checksum ends it?

    Choose one answer.

    Show the answer: Question 4 of 14

    Answer: 77

    Sum the count, both address bytes, the type and the data modulo 256 (89H), then take the negative: 77H. All six bytes of the line then add up to 00H.

  5. Question 5 of 14 Two data bytes of a record arrive swapped: 23H where 32H belongs and 32H where 23H belongs. What does the checksum test do with the line?

    Choose one answer.

    Show the answer: Question 5 of 14

    Answer: It accepts the line, because the sum is the same

    The checksum catches every change to a single byte, but changes that cancel, such as a swap of two bytes, pass unseen. That is the price of a one-byte sum.

  6. Question 6 of 14 Without break 0x0012, what would s51 do after run on sum.hex?

    Choose one answer.

    Show the answer: Question 6 of 14

    Answer: Run SJMP DONE again and again, without stopping on its own

    A microcontroller program ends in a loop, so a simulator needs a breakpoint on it, here on DONE, to stop and show the registers.

  7. Question 7 of 14 At the breakpoint DPTR read 0x001e. Why not 001DH, the address of the last table byte?

    Choose one answer.

    Show the answer: Question 7 of 14

    Answer: INC DPTR ran after the last byte was read, so DPTR points one past it

    DPTR started at 0014H and the loop ran 10 times, adding 1 each time: 0014H + 10 = 001EH.

  8. Question 8 of 14 What does Intel's MCS-51 manual say about the internal RAM at power-up and after a reset?

    Choose one answer.

    Show the answer: Question 8 of 14

    Answer: Its content is indeterminate at power-up, and a reset does not change it

    R0 to R7 are bytes of the internal RAM, so they start with whatever the chip holds. uCsim models that with random bytes from a seed.

  9. Question 9 of 14 How would you show, with uCsim, that an 8051 lab program never reads a register it did not set first?

    Choose one answer.

    Show the answer: Question 9 of 14

    Answer: Run it with several seeds and check that the result is the same every time

    A dependence on power-on bytes shows up as a result that changes with the seed. Two or more seeds with the same result are the quick test the lesson ran on sum.asm and sum_noclear.asm.

  10. Question 10 of 14 What does differential testing compare?

    Choose one answer.

    Show the answer: Question 10 of 14

    Answer: The outputs of two independent implementations given the same input

    Independent implementations rarely make the same mistake, so their agreement is evidence and their disagreement says where to look.

  11. Question 11 of 14 Intel's 8085A table gives LDA and STA 13 T-states, MOV r,r and ADD r 4, and HLT 5. How many T-states does add8.asm take without its MVI, ACI and second STA lines?

    Type a number.

    A hint

    Six instructions remain: two loads, a move, an add, one store and the halt.

    Show the answer: Question 11 of 14

    Answer: 52 T-states

    13 + 4 + 13 + 4 + 13 + 5 = 52: LDA, MOV B,A, LDA, ADD B, STA and HLT. The full program adds MVI (7), ACI (7) and the second STA (13), which gives the 79 that both simulators counted.

  12. Question 12 of 14 Two assemblers give different bytes for the line at 0105H. Both simulators were run on the HEX file of the first assembler only, and they agree. What do you do next?

    Choose one answer.

    Show the answer: Question 12 of 14

    Answer: Compare that line in both listings with the encoding in Intel's manual

    A difference between assemblers names an address. Read that line in both listings, look the instruction up in Intel's encoding tables, then run the HEX file of the right assembler in both simulators.

  13. Question 13 of 14 Which two of the five tools run 8085 programs in a window that you step through by hand?

    Choose one answer.

    Show the answer: Question 13 of 14

    Answer: Sim8085 and GNUSim8085

    For the 8085 the windowed choices are Sim8085, in a browser or installed as an app, and GNUSim8085, a desktop program; uCsim covers the 8085 too, from a terminal.

  14. Question 14 of 14 You must run 40 students' 8051 programs and compare every output with a reference, without clicking through each one. Which tool fits?

    Choose one answer.

    Show the answer: Question 14 of 14

    Answer: uCsim, with a command file and a fixed -R seed

    Runs that repeat byte for byte from a command file, as every uCsim run of this lesson did, are what a comparison by script needs.

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.

  1. 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.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. 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.

    Model answer, Pro. First step and follow-ups

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.