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