Memory Dump
Part of Programming Fundamentals
A memory dump displays the raw contents of computer memory in hexadecimal, providing direct visibility into program state for debugging and system examination.
Why This Matters
When a program misbehaves and you have no high-level debugger, the memory dump is your window into what the computer actually contains. A variable that should hold 42 but contains 255. A string that should say βHELLOβ but contains garbage. A return address on the stack that points nowhere useful. All of these show up in a memory dump before any analysis or guesswork.
For rebuilders working on primitive hardware with no operating system, no symbolic debugger, and no error messages, the memory dump is often the only diagnostic tool available beyond LED blinkers. Knowing how to read one, write one, and interpret one is an essential survival skill for low-level programming.
The memory dump also serves a more constructive purpose: understanding exactly how your data is laid out in memory, confirming that your assembler or compiler produced the binary you expected, and inspecting the state of a running system at any point in time.
Reading a Memory Dump
A standard memory dump displays memory in 16-byte rows, each row showing:
- The starting address of the row (in hexadecimal)
- The 16 bytes of content (in hexadecimal, space-separated or grouped in pairs)
- The same 16 bytes interpreted as ASCII characters (printable characters shown, others replaced with a period)
Example:
1000: 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 0D 0A 00 00 Hello, World....
1010: 01 00 03 FF 12 34 56 78 9A BC DE F0 00 00 00 00 .....4Vx........
1020: 4C 44 20 41 2C 20 42 0D 43 41 4C 4C 20 49 4E 49 LD A, B.CALL INI
Reading this dump:
- Address 0x1000: The bytes
48 65 6C 6C 6Fare ASCII βHβ,βeβ,βlβ,βlβ,βoβ β a string βHello, Worldβ followed by CR (0x0D), LF (0x0A), and two zero bytes. - Address 0x1010: Binary data β
01 00is the little-endian 16-bit value 1,03might be a count,FFmight be a sentinel,12 34 56 78 9A BC DE F0is patterned test data. - Address 0x1020:
4C 44 20 41 2C 20 42 0Dβ ASCII text βLD A, B\rβ β possibly assembly source text in memory.
To interpret a dump, you must know what should be there. From your programβs design or memory map, you know: address 0x1000 holds the message string, address 0x1010 holds the configuration block, address 0x1020 holds the source buffer. Without that context, the bytes are meaningless.
Writing a Memory Dump Routine
A memory dump subroutine needs to:
- Print the starting address of each row in hexadecimal
- Print each of the 16 bytes on the row in hexadecimal
- Print the ASCII interpretation
- Advance to the next 16-byte row and repeat
Subcomponents needed:
PRINT_HEX_BYTE: Converts an 8-bit value to two hexadecimal digits and outputs themPRINT_HEX_WORD: Converts a 16-bit address to four hexadecimal digitsPRINT_ASCII_BYTE: Outputs a byte as its ASCII character if printable (0x20-0x7E), or β.β otherwisePRINT_CHAR: The lowest-level output β writes one character to the serial port or display
Implementing PRINT_HEX_BYTE:
PRINT_HEX_BYTE:
; A = byte to print
PUSH AF
; print high nibble
RRCA
RRCA
RRCA
RRCA
AND 0x0F
CALL NIBBLE_TO_CHAR
CALL PRINT_CHAR
; print low nibble
POP AF
AND 0x0F
CALL NIBBLE_TO_CHAR
CALL PRINT_CHAR
RET
NIBBLE_TO_CHAR:
; A = nibble (0-15), returns ASCII char in A
CP 10
JP C, DIGIT ; less than 10 β digit '0'-'9'
ADD 'A' - 10 ; >= 10 β letter 'A'-'F'
RET
DIGIT:
ADD '0'
RET
The full dump loop:
DUMP_MEMORY:
; HL = start address, BC = byte count
DUMP_ROW:
; print address
CALL PRINT_HEX_WORD ; HL = address
LD A, ':'
CALL PRINT_CHAR
LD A, ' '
CALL PRINT_CHAR
; print 16 hex bytes
PUSH HL
LD DE, 16 ; 16 bytes per row
DUMP_HEX:
LD A, (HL)
CALL PRINT_HEX_BYTE
LD A, ' '
CALL PRINT_CHAR
INC HL
DEC DE
; insert extra space at midpoint for readability
JP NZ, DUMP_HEX
; print ASCII column
POP HL
LD DE, 16
DUMP_ASCII:
LD A, (HL)
CALL PRINT_ASCII_BYTE
INC HL
DEC DE
JP NZ, DUMP_ASCII
; newline
LD A, 0x0D
CALL PRINT_CHAR
LD A, 0x0A
CALL PRINT_CHAR
; advance and check if done
DEC BC
; ... check BC != 0, loop for next row
RET
Stack Dumps
The stack is particularly useful to dump when debugging crashes. If a program crashes (jumps to an invalid address or halts unexpectedly), dumping the memory around the stack pointer often reveals:
- Return addresses pushed by CALL instructions β these tell you which functions were active
- Local variables pushed by the program
- Saved register values
- The last few values computed before the crash
Read stack entries from top (current SP) upward. On Z80, each PUSH stores 2 bytes; each CALL pushes 2 bytes (the return address). So dumping 32 bytes above SP shows the last 8 return addresses or saved register values.
Interpret return addresses by looking them up in your assembly listing. An address that points into the middle of a subroutine tells you where in that subroutine execution will continue when the function returns.
Checksum Verification
When loading a program from paper tape, serial line, or ROM, errors can corrupt individual bytes. A checksum can verify the loaded data matches the expected content.
After computing the expected checksum of your program before transmission, dump the loaded memory, sum the bytes manually (or with a checksum subroutine), and compare. A mismatch means at least one byte loaded incorrectly.
A simple XOR checksum subroutine:
CHECKSUM:
; HL = start, BC = count, returns checksum in A
XOR A ; A = 0
CHECKSUM_LOOP:
XOR (HL) ; XOR current byte into running total
INC HL
DEC BC
LD D, B
OR E
JP NZ, CHECKSUM_LOOP
RET
Practical Notes for Rebuilders
Implement a memory dump routine as one of your first utility programs. Before you can debug anything else, you need the ability to see what is in memory. Keep the routine small (under 100 bytes of code is achievable) so it can be loaded even on severely memory-constrained systems.
Memorize the ASCII table for the range 0x20-0x7F. When you see a block of bytes in a dump that looks like it might be text, you can read the hex values directly without consulting a chart. The ranges are: 0x30-0x39 = β0β-β9β, 0x41-0x5A = βAβ-βZβ, 0x61-0x7A = βaβ-βzβ, 0x20 = space. These are enough to recognize strings in a dump at a glance.
When examining a dump after a crash, pay attention to any pattern breaks. If a buffer that should contain your programβs data suddenly contains a run of identical bytes or a known pattern from a different part of memory, you have found a buffer overrun or misdirected copy operation. The pattern itself is the clue.