====== IBM PC Memory Poking with DEBUG and CHKDSK ======
Reading, writing and testing memory on the IBM PC (5150) and PC/XT (5160)
from the DEBUG prompt, plus the memory map, the system-board RAM banks and
the common I/O ports. Written for ''DEBUG'' from PC DOS / MS-DOS 2.x.
**Everything in DEBUG is hex. Never type an H suffix — ''10'' means sixteen.**
**Examples assume a 64-256K system board with an expansion RAM card from
40000 up. For testing, only write to RAM that DOS is not using.**
===== Quick Reference =====
^ Task ^ Command ^
| Start DEBUG | ''DEBUG'' |
| Start with a file loaded | ''DEBUG FILE.COM'' |
| Quit | ''Q'' |
| Dump 128 bytes | ''D 4000:0000'' |
| Dump 16 bytes | ''D 4000:0000 L10'' |
| Carry on dumping | ''D'' |
| Write bytes | ''E 4000:0000 55 AA 12'' |
| Fill a 64K bank | ''F 4000:0000 FFFF 55'' |
| Compare two ranges | ''C 4000:0000 L8000 4000:8000'' |
| Find a byte | ''S 4000:0000 L8000 AA'' |
| Copy a block | ''M 4000:0000 L100 5000:0000'' |
| Hex sum and difference | ''H 5000 4000'' |
| Show registers | ''R'' |
| Disassemble the reset vector | ''U FFFF:0000'' |
| BIOS date | ''D F000:FFF5 L8'' |
| Model byte | ''D F000:FFFE L1'' |
| Memory size the BIOS counted | ''D 0040:0013 L2'' |
| Turn off parity NMI | ''O A0 00'' |
| Read an I/O port | ''I 61'' |
| Pause a long dump | ''Ctrl-S'' or ''Ctrl-NumLock'' |
| Stop a long dump | ''Ctrl-C'' or ''Ctrl-Break'' |
| What DOS counted | ''CHKDSK'' |
===== Starting and Addressing =====
**# Start DEBUG with nothing loaded:**
DEBUG
The prompt is a single ''-''. A dash means DEBUG is waiting.
**# Start DEBUG with a file loaded at CS:100:**
DEBUG FILE.COM
**# Quit back to DOS:**
Q
==== Segment:offset ====
Every address is ''SEGMENT:OFFSET''. The physical address is
**(segment x 10h) + offset**.
^ DEBUG address ^ Physical ^
| ''4000:0000'' | 40000 |
| ''4000:FFFF'' | 4FFFF |
| ''5000:0000'' | 50000 |
| ''4000:8000'' | 48000 — **the same byte** as ''4800:0000'' |
| ''F000:FFF0'' | FFFF0 — the same byte as ''FFFF:0000'' |
Leave the segment off and DEBUG picks one for you — **DS** for ''D'', ''E'',
''F'', ''C'', ''S'', ''M'' and **CS** for ''U'', ''A'', ''G'', ''T''.
==== Ranges and lists ====
**# Range as start plus END OFFSET, same segment:**
D 4000:0000 FFFF
**# Range as start plus L and a LENGTH:**
D 4000:0000 L8000
Maximum length is ''L FFFF''. To cover a full 64K segment, use the end-offset
form.
**# A list — bytes and quoted text, mixed freely — for E, F and S:**
E 4000:0000 01 02 FF "HELLO" 00
===== Look at Memory =====
**# Dump 128 bytes (8 lines) as hex and ASCII:**
D 4000:0000
Each line is the address, 16 hex bytes, then the ASCII. A ''.'' means not
printable.
**# Dump exactly 16 bytes:**
D 4000:0000 L10
**# Carry on from where the last dump stopped:**
D
**# Find every AA in the first half of the bank:**
S 4000:0000 L8000 AA
Prints each address where the pattern was found.
**# Find the IBM copyright string in the BIOS:**
S F000:E000 FFFF "IBM"
===== Change Memory =====
**# Write three bytes, no questions asked:**
E 4000:0000 55 AA 12
**# Edit byte by byte, interactively:**
E 4000:0000
DEBUG shows each byte in turn and waits.
^ Keys ^ Action ^
| new value, then ''Space'' | Store it, move to the next byte |
| ''Space'' alone | Skip, keep the byte as it is |
| ''-'' | Back one byte |
| ''Enter'' | Finish |
**# Fill a whole 64K bank with 55:**
F 4000:0000 FFFF 55
**# Fill with a repeating pattern — 01 02 01 02 ...:**
F 4000:0000 L100 01 02
**# Copy a block:**
M 4000:0000 L100 5000:0000
**# Fill a CGA 80x25 screen with the letter A — proof you found video RAM:**
F B800:0000 LFA0 41 07
**# Same thing on a monochrome (MDA) adapter:**
F B000:0000 LFA0 41 07
Each character cell is two bytes — the character, then the attribute. 07 is
normal white on black.
===== Compare and Do Hex Math =====
**# Compare two ranges and print every byte that differs:**
C 4000:0000 L8000 4000:8000
**No output at all means identical.** Each line of output is a mismatch, in
the order address 1, byte 1, byte 2, address 2.
**# Sum and difference of two hex numbers:**
H 5000 4000
Prints ''9000 1000''.
===== Registers and Code =====
**# Show all registers and the next instruction:**
R
**# Change AX — DEBUG prompts with a colon:**
R AX
**# Disassemble the reset vector — a far jump into the BIOS power-on test:**
U FFFF:0000
**# Type in 8088 instructions at offset 100:**
A 100
A blank line ends assembly.
**# Run from CS:IP:**
G
**# Start at 100, stop at a breakpoint at 110:**
G=100 110
**# Run one instruction, then show the registers:**
T
**# Run five instructions:**
T 5
==== Reboot from DEBUG ====
**# Set the warm-boot flag so the power-on test skips the memory count:**
E 0040:0072 34 12
**# Jump to the reset vector:**
G=FFFF:0000
Skip the first command for a full cold boot with the memory test.
===== Files =====
**# Name the file to work on:**
N TEST.COM
**# Load the named file at CS:100:**
L
**# Write the named file back to disk:**
W
''W'' with sector numbers writes **raw sectors** and can wreck a disk —
including the hard disk. Do not use ''W'' unless you mean it.
**# List the commands:**
?
Later versions only. DOS 2.x DEBUG answers with an error.
==== DEBUG keys ====
^ Keys ^ Action ^
| ''Ctrl-S'' or ''Ctrl-NumLock'' | Pause a long dump |
| ''Ctrl-C'' or ''Ctrl-Break'' | Stop it |
===== IBM PC Memory Map =====
^ Physical ^ DEBUG ^ What is there ^
| 00000-003FF | ''0000:0000'' | 8088 interrupt vectors |
| 00400-004FF | ''0040:0000'' | BIOS data area |
| 00500-005FF | ''0050:0000'' | DOS and BASIC data area |
| 00600-9FFFF | ''0060:0000'' | IBMBIO.COM, IBMDOS.COM, COMMAND.COM, programs — **conventional RAM, up to 640K** |
| A0000-AFFFF | ''A000'' | Reserved — EGA/VGA graphics RAM if fitted |
| B0000-B0FFF | ''B000'' | MDA text RAM, 4K — Hercules uses B0000-B7FFF |
| B1000-B7FFF | — | Images of the MDA RAM |
| B8000-BBFFF | ''B800'' | CGA video RAM, 16K |
| BC000-BFFFF | — | Image of the CGA RAM |
| C0000-C7FFF | ''C000'' | EGA/VGA BIOS ROM — empty with MDA or CGA |
| C8000-C9FFF | ''C800'' | XT hard disk controller ROM |
| CA000-DFFFF | ''CA00'' | Other adapter ROMs, EMS page frame |
| E0000-EFFFF | ''E000'' | Reserved — empty on a PC or XT |
| F0000-F5FFF | ''F000'' | Reserved — spare ROM socket on the 5150 at F4000 |
| F6000-FDFFF | ''F600'' | ROM BASIC (Cassette BASIC), 32K |
| FE000-FFFFF | ''FE00'' | BIOS, 8K — reset starts at FFFF0 |
The late XT BIOS (1986) fills all of F0000-FFFFF with two 32K ROMs. BASIC and
the BIOS entry points stay at the same addresses.
==== The address that is the opposite of the HP 150 ====
**# 1. B800 really is video RAM here.**
Writing to ''B800'' (CGA) or ''B000'' (MDA) changes the screen and nothing
else. Harmless. The dangerous writes on a PC are **below 0600** — vectors and
BIOS data — and anywhere DOS or DEBUG is living.
==== Useful BIOS locations ====
^ DEBUG ^ Size ^ What it holds ^
| ''0040:0010'' | word | Equipment word — drives, video type, ports |
| ''0040:0013'' | word | Memory size in K |
| ''0040:0017'' | byte | Keyboard shift flags |
| ''0040:0049'' | byte | Video mode — 07 is MDA, 03 is CGA 80 column |
| ''0040:006C'' | 4 bytes | Timer ticks since midnight |
| ''0040:0072'' | word | Reset flag — 1234 means warm boot |
| ''F000:FFF5'' | 8 bytes | BIOS date, ASCII |
| ''F000:FFFE'' | byte | Model — FF is PC, FE is XT, FD is PCjr, FC is AT |
Words are stored low byte first. Memory size ''80 02'' is 0280 hex, which is
640.
**# Read the memory size the BIOS counted:**
D 0040:0013 L2
^ Dump shows ^ Hex ^ Memory ^
| ''40 00'' | 0040 | 64K |
| ''00 01'' | 0100 | 256K |
| ''00 02'' | 0200 | 512K |
| ''80 02'' | 0280 | 640K |
**# Read the BIOS date:**
D F000:FFF5 L8
==== Adapter ROMs ====
**# Check for an adapter ROM — the hard disk controller lives here:**
D C800:0000 L10
A ROM starts with ''55 AA''. The third byte is its length in 512-byte blocks.
''FF'' all the way across means nothing is there.
**# Start the low-level format routine in many XT controller ROMs:**
G=C800:5
**Destroys everything on the hard disk.** The entry offset depends on the
controller — 5 is the Xebec and Western Digital habit, not a standard.
===== System Board RAM =====
Every bank is **nine chips** — eight data bits plus parity.
==== 5150 with the 16-64K board ====
^ Bank ^ Physical ^ Chips ^
| 0 | 00000-03FFF | 16K (4116), soldered |
| 1 | 04000-07FFF | 16K |
| 2 | 08000-0BFFF | 16K |
| 3 | 0C000-0FFFF | 16K |
==== 5150 or 5160 with the 64-256K board ====
^ Bank ^ Physical ^ Chips ^
| 0 | 00000-0FFFF | 64K (4164) |
| 1 | 10000-1FFFF | 64K |
| 2 | 20000-2FFFF | 64K |
| 3 | 30000-3FFFF | 64K |
Everything from **40000 to 9FFFF** is on expansion cards.
==== 5160 with the 256-640K board ====
^ Bank ^ Physical ^ Chips ^
| 0 | 00000-3FFFF | 256K (41256) |
| 1 | 40000-7FFFF | 256K (41256) |
| 2 | 80000-8FFFF | 64K (4164) |
| 3 | 90000-9FFFF | 64K (4164) |
==== The three gotchas that will waste your afternoon ====
**# 1. Reading RAM the BIOS never counted can halt the machine.**
The power-on test writes every byte it counts, which sets the parity bits.
RAM that is fitted but **above the counted size** has random parity. Dump it
and the parity circuit fires an NMI:
PARITY CHECK 2
The machine is now dead until you power-cycle. ''PARITY CHECK 1'' is the
system board, ''PARITY CHECK 2'' is an expansion card. Fill before you read.
**# Fill the range first so the parity bits are valid:**
F 4000:0000 FFFF 00
**# Or turn the NMI off while you test:**
O A0 00
**# Turn the NMI back on afterwards:**
O A0 80
With NMI off, a bad bit shows up in the dump instead of killing the machine
— which is what you want when hunting it.
**# 2. On a 5150 the switches decide how much RAM exists.**
SW1 positions 3-4 set the system-board RAM. SW2 sets the total. Fit a card
and leave SW2 alone, and the BIOS never looks at it. The XT is different —
SW1 positions 3-4 cover the system-board banks and the BIOS finds expansion
RAM by testing for it.
**# 3. A 5150 BIOS older than 10/27/82 cannot count past 544K.**
No switch setting fixes it. Check the date before blaming the card.
D F000:FFF5 L8
===== I/O Ports =====
**# Read an I/O port:**
I 61
**# Write an I/O port:**
O A0 00
^ Port ^ Device ^
| ''20''-''21'' | 8259 interrupt controller |
| ''40''-''43'' | 8253 timer |
| ''60''-''63'' | 8255 — keyboard, switches, speaker gate |
| ''A0'' | NMI mask — 80 on, 00 off |
| ''201'' | Game port |
| ''278'', ''378'' | Parallel ports |
| ''2F8'' | COM2 |
| ''320'' | XT hard disk controller |
| ''3B0''-''3BB'' | MDA |
| ''3BC'' | Parallel port on the MDA card |
| ''3D0''-''3DF'' | CGA |
| ''3F0''-''3F7'' | Floppy controller |
| ''3F8'' | COM1 |
===== Test Recipes =====
Bank at 40000 shown throughout — a 64K block. Change the segment for other
banks. Turn the NMI off first with ''O A0 00'' if you want to see bad bits
rather than a parity halt.
**Stay out of DOS's way.** DEBUG sits in low memory and COMMAND.COM keeps its
transient half in the top 20K or so of RAM. Everything between is fair game.
Overwrite the top and DOS reloads COMMAND.COM from disk when you quit.
==== 1. Data lines ====
**# Write a walking one, then a walking zero:**
E 4000:0000 01 02 04 08 10 20 40 80 FE FD FB F7 EF DF BF 7F
**# Read it back — it must match exactly as written:**
D 4000:0000 L10
==== 2. Address lines A0-A15 ====
One value per address line. None may overwrite another.
**# Write a different value at each power-of-two offset:**
E 4000:0000 00
E 4000:0001 01
E 4000:0002 02
E 4000:0004 03
E 4000:0008 04
E 4000:0010 05
E 4000:0020 06
E 4000:0040 07
E 4000:0080 08
E 4000:0100 09
E 4000:0200 0A
E 4000:0400 0B
E 4000:0800 0C
E 4000:1000 0D
E 4000:2000 0E
E 4000:4000 0F
E 4000:8000 10
**# Read each address back with L1 and check it holds its own value:**
D 4000:0100 L1
Repeat for every offset in the list. A wrong value names the stuck or
shorted address line.
==== 3. Whole bank ====
Fill, compare the halves, repeat with the opposite pattern.
**# Fill the bank with 55:**
F 4000:0000 FFFF 55
**# Compare the two halves — no output means good:**
C 4000:0000 L8000 4000:8000
**# Fill the bank with AA:**
F 4000:0000 FFFF AA
**# Compare the two halves again:**
C 4000:0000 L8000 4000:8000
==== 4. Bank select ====
A write to the next bank must not land in this one.
**# Mark this bank:**
E 4000:0000 11
**# Mark the next bank:**
E 5000:0000 22
**# Check this bank — it must still be 11:**
D 4000:0000 L1
==== 5. Empty space ====
**# See what the bus returns where nothing answers:**
D E000:0000 L20
Usually ''FF'' across the line. That is what a missing bank looks like too.
==== After DEBUG ====
**# Check what DOS counted:**
CHKDSK
The ''bytes total memory'' line is the number that matters.
^ Memory ^ bytes total memory ^
| 64K | 65536 |
| 128K | 131072 |
| 256K | 262144 |
| 384K | 393216 |
| 512K | 524288 |
| 640K | 655360 |
===== Troubleshooting =====
^ Symptom ^ Cause ^ Fix ^
| ''PARITY CHECK 2'' and a dead machine | read expansion RAM with bad or never-set parity | fill before reading, or ''O A0 00'' first |
| ''PARITY CHECK 1'' and a dead machine | same, on the system board | same — then test that bank bit by bit |
| ''201'' with a number at power-on | memory test failed | the number points at the bank and bit — test that bank |
| Card fitted, memory total unchanged (5150) | SW2 not set for the new total | set SW2, power-cycle |
| 5150 stops counting at 544K | BIOS older than 10/27/82 | check ''D F000:FFF5 L8'' — needs the later ROM |
| Dump shows all ''FF'' | nothing decodes there | wrong segment, or the bank is not selected |
| Screen fills with junk after a write | wrote to ''B000'' or ''B800'' | harmless — ''CLS'' after quitting |
| Machine hangs after an ''E'' or ''F'' | wrote below 0600 or over DOS | reboot — keep writes in free RAM |
| DOS asks for the COMMAND.COM disk on quit | fill ran over the top of RAM | harmless — put the DOS disk in |
| ''C'' prints lines of addresses | the two ranges differ | bad chip, data line or address line — run recipes 1 and 2 |
| Recipe 4 reads 22 instead of 11 | bank select not decoding | check the card's address switches or decoder |
| Same byte at two addresses | segment overlap | not a fault — ''4000:8000'' is ''4800:0000'' |
| Value lands sixteen times too big | typed decimal or an H suffix | everything is hex, no suffix |
| Command ran on the wrong memory | segment left off | DEBUG used DS or CS — type the full ''SEGMENT:OFFSET'' |
| Disk unreadable after DEBUG | ''W'' with sector numbers | do not use raw ''W'' unless you mean it |
| ''?'' gives an error | DOS 2.x DEBUG lacks it | use the Quick Reference above |
Rule of thumb: **read ROM and video freely, fill RAM before you read it, and
never write below 0600**.