Search (using Google):  Web Karig

 

9 December 2003

(Revised 16 January 2004)

Hex dump, part 1

I haven't posted anything over the past few days because I was working on something relatively ambitious. I was trying to write code to call the BIOS to find out what memory the computer has that the system can use. This was relatively ambitious because (1) there are actually four BIOS routines for retrieving data on memory, (2) I was also writing code to store this data into the record segment, and (3) I was also writing ad-hoc code to display the bytes in the record segment to the screen so I could be sure all of my new code worked. Naturally, this big mess of code didn't work.

Now I know better than this. I know myself well enough to know that I should never add this much new code to anything without stopping to verify that each new piece works — especially when I don't have a friendly programming environment with a debugger or the ability to single-step through code and see the effects on variables. After all, I'm writing a boot sector, not an application in C++Builder or a macro in Visual Basic. I wrote enough new code that I didn't really know where the bugs were, and I'd have to write more code and run the boot sector again just to see what went wrong.

So I put that code aside and decided to write a hex-dump routine.

A hex-dump routine is one that "dumps" bytes to the screen as hexadecimal numbers. If my program is supposed to write data to memory, I can use the routine to verify that what gets written is what I want written.

Seeing as how (1) the hex dump requires a fair amount of new code, and (2) I haven't posted anything in about a week, I'm going to present only the "workhorse" routines here — code to clear the screen, move the cursor, print characters, and scroll the screen up when the bottom row gets full. I'll present the code to convert bytes into hex digits in a later entry.

BIOS routines

The code here is temporary. It exists only so that I can debug the real-mode code that I write. Once the system loader is complete, I don't expect to need it anymore. If I needed speed, I'd write to text-video memory myself, but I decided to use the BIOS routines because calling them is simpler than rolling my own screen-handling code.

The BIOS routines I use are:

  • INT 10h, AH=00h clears the screen. Technically it changes the graphics mode, but it clears the screen at the same time. Pass AL=03h.
  • INT 10h, AH=02h sets the cursor position. Pass BH=0, DH=row (0 to 24), DL=column (0 to 79).
  • INT 10h, AH=03h gets the cursor position. Pass BH=0. Returns DH=row, DL=column.
  • INT 10h, AH=06h scrolls the screen up. Pass AL=23, BH=07h, CH=0, CL=0, DH=24, DL=79.
  • INT 10h, AH=0Ah prints a character at the cursor position. Pass AL=ASCII value of the character, BH=0, CX=1.

Code

You can download the boot sector and try this code yourself.

The clear_screen routine clears the screen by changing the video mode to 80-by-25-character text mode (via INT 10h, AH=00h). This is a very slow way to clear the screen, but I call it only once, so it should be fine.

clear_screen:
		mov	ax, 3
		int	0x10

The routine also moves the cursor to the upper-left corner of the screen (row 0, column 0) (via INT 10h, AH=02h).

		xor	bh, bh
		xor	dx, dx
		mov	ah, 2
		int	0x10
		ret

The scroll_up routine moves rows 1 through 24 up by one row, so that row 0 is overwritten and row 24 is left blank (via INT 10h, AH=06h).

scroll_up:
		mov	bh, 7
		xor	cx, cx
		mov	dx, (24*0x100) + 79
		mov	ax, (0x0600 + 23)
		int	0x10
		ret

[UPDATE 2004-01-16: This routine has a bug. It is described and fixed here.]

The print_char routine is more complicated. It takes an ASCII character value in AL, prints the character at the current cursor location, then moves the cursor forward by one slot.

The first thing I do is to print the character (via INT 10h, AH=0Ah).

print_char: ;in AL
		xor	bh, bh
		xor	cx, cx
		inc	cx
		mov	ah, 0x0A
		int	0x10

Then I obtain the current cursor position (via INT 10h, AH=03h). The column number will be in DL, and the row number will be in DH.

		xor	bh, bh
		mov	ah, 3
		int	0x10

I increment the column number. If this pushes the cursor off the right edge of the row...

		inc	dl
		cmp	dl, 80
		jb	.1

...then I set the column number to zero and increment the row number. If this pushes the cursor off the bottom of the screen...

		xor	dl, dl
		inc	dh
		cmp	dh, 25
		jb	.1

...then I restore the row number and scroll the screen up a line.

		dec	dh
		push	dx
		call	scroll_up
		pop	dx

Then I pass the new row and column numbers to the function to set the cursor position (INT 10h, AH=02h), and the job is finished.

	.1:	xor	bh, bh
		mov	ah, 2
		int	0x10
		ret

This is the code to test the above routines. It fills the screen with enough characters to cause the screen to scroll up one line.

test:
		call	clear_screen
		mov	cx, 80*25 + 40
		xor	ax, ax
	.1:	push	cx
		push	ax
		call	print_char
		pop	ax
		pop	cx
		inc	al
		dec	cx
		jnz	.1

		jmp	$

Check the index for other entries.