Viewing 8086 memory-areas A peek into the video display memory, the real-mode Interrupt Vector Table, and the ROM-BIOS DATA AREA Hexadecimal displays • The ability to exhibit computer-data in a form that’s understandable will be vital for exploring (and for debugging) our system • The easiest scheme for doing it will be to show binary values in hexadecimal format • Let’s look at a straightforward algorithm to convert any 16-bit number into a string of (four) hexadecimal numerals Our ‘ax2hex’ procedure • We create an array of the ascii-codes for the sixteen hexadecimal digit-characters hex: .ascii “0123456789ABCDEF” • Our procedure expects the binary value to be in register AX and the memory-address for the hex-string is in registers DS:DI • Our procedure will preserve the contents of the CPU’s registers (no ‘side-effects’) A 4-bit left-rotation Hi-nybble AX = 0 1 0 0 0 1 0 1 0 1 1 0 0 1 1 1 (Before rotation) AX = 0 1 0 1 0 1 1 0 0 1 1 1 0 1 0 0 (After rotation) Lo-nybble A bitwise ‘AND’ opration BL is copied from AL Unknown bits in BH BX = Lo-nybble ? ? ? ? ? ? ? ? 0 1 1 1 0 1 0 0 (Before masking) & & & & & & & & & & & & & & & & $0xF = 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 (bitmask-value) = = = = = = = = = = = = = = = = BX = 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 (After masking) Lo-nybble Thus the lo-nybble (4-bits) gets ‘zero-extended’ to its equivalent 16-bit value Array ‘lookup’ operation hex: ‘0’ ‘1’ ‘2’ ‘3’ ‘4’ ‘5’ ‘6’ ‘7’ ‘8’ ‘9’ ‘A’ ‘B’ ‘C’ ‘D’ ‘E’ ‘F’ hex( %bx ) = ‘4’ mov hex( %bx ), %dl BX = 0004 DL = DS:DI = &buf ‘4’ mov %dl, %ds:( %di ) buf: ‘4’ So the number 4 in BX gets converted to the numeral ‘4’ in the ‘buf’ array-cell at DS:DI Algorithm implementation ax2hex: # converts the word-value in AX to a hex-string at DS:DI pusha mov $4, %cx # save general registers # setup digit-count nxnyb: hex: rol mov and mov mov inc loop popa ret $4, %ax %al, %bl $0xF, %bx hex(%bx), %dl %dl, (%di) %di nxnyb # rotate hi-nybble into AL # copy the nybble into BL # isolate the nybble’s bits # lookup nybble’s ascii-code # store numeral into buffer # advance the buffer index # generate remaining digits # restore saved registers # return control to the caller .ascii “0123456789ABCDEF” # array of hex numerals Algorithm applications • We can use this binary-to-hex algorithm to view the contents of two memory-regions which ROM-BIOS startup-code initializes: – The table of ‘real-mode’ interrupt vectors – The values in the ROM-BIOS DATA-AREA • Two ‘boot-sector’ demo-programs are named ‘viewivt.s’ and ‘viewrbda.s’ • They are on the CS 630 website: <http://cs.usfca.edu/~cruse/cs630> Direct-Drawing to VRAM • Both demo-programs perform their display by writing character-codes directly into the text-mode video-display memory-region (it starts at memory-address 0x000B8000) • Each onscreen character is controlled by a pair of adjacent bytes in the video memory Byte at odd-numbered offset Background color Foreground color Byte at even-numbered offset ASCII character-code Drawing ‘A’ in top-left corner • Here’s a fragment of assembly-language code that draws an uppercase letter ‘A’ in the top-left corner of the screen (using the normal ‘white-on-black’ color-attributes): mov mov xor movb movb $0xB800, %ax %ax, %es %di, %di $’A’, %es:0(%di) $0x07, %es:1(%di) # address VRAM segment # using the ES register # point ES:DI at top-left cell # draw character-code to VRAM # draw attribute-codes to VRAM Organization of VRAM • The first 80 byte-pairs in video memory (at offsets 0, 2, 4, …, 158) control the top row (row 0) on the screen (left-to-right order) • Then the next 80 byte-pairs (offsets 160, 162, 164, …, 318) control the next row of text on the screen (i.e., row number 1) • Altogether there are 25 rows of text, with 80 characters per row, when the display is programmed at startup for ‘standard’ text-mode We need more rows for IVT • The real-mode Interrupt Vector Table has room for 256 ‘pointers’ (each pointer being a doubleword segment-and-offset value) • Not enough cells in the 80x25 text mode to view all 256 of the ‘vectors’ simultaneously • We need 9 characters for each vector (i.e., 8 hex-digits, plus a space for separation), but 256 x 9 is greater than 80 x 25 =2000 Solution • We can invoke a ROM-BIOS function that reprograms the display-hardware to show twice as many rows (in smaller-size text) • Here’s a code-fragment to accomplish it: mov int $0x0003, %ax $0x10 # set standard 80x25 textmode # invoke BIOS video service mov int $0x1112, %ax $0x10 # load 80x50 character-glyphs # invoke BIOS video service Code ‘reuse’ • Our earlier ‘ax2hex’ procedure converts a word into a string of hexadecimal digits • But each interrupt-vector is a doubleword! • We could use a new procedure: ‘eax2hex’ • But it’s easier if we just call ‘ax2hex’ twice • This requires us to clearly understand the Pentium’s scheme for addressing memory (i.e., the ‘little-endian’ storage convention) ‘Little endian’ versus ‘Big endian’ EAX = 0x0369CF25 Power-PC processor uses ‘big-endian’ convention 0 1 2 3 25 CF 69 03 ‘Little endian’ convention (least-significant byte is at lowest memory-address) Intel x86 CPU’s use ‘little endian’ storage contention 0 1 2 3 03 69 CF 25 ‘Big endian’ convention (most-significant byte is at lowest memory-address) Example: convert vector 0 to hex “xxxxxxxx” buf: .ascii # room for 8 hex-digits xor mov %bx, %bx %bx, %fs # address bottom of memory # using register-pair FS:BX mov lea call %fs:0(%bx), %ax buf+4, %di ax2hex # fetch vector’s low-word # point DS:DI to position # convert AX to hex-string mov lea call %fs:2(%bx)m %ax buf+0, %di ax2hex # fetch vector’s high-word # point DS:DI to position # convert AX to hex-string Vector0: # OK, ‘buf’ now holds the 8-digit hex-string representing vector 0 ‘Real-Mode’ Memory Map Vendor’s Firmware ROM-BIOS 64+ kbytes No installed memory Video-ROM Video Display Memory Top of RAM (= 0x000A0000) Volatile Program Memory 0x00007E00 0x00007C00 0x00000500 0x00000400 0x00000000 VRAM 128 kbytes Extended BIOS Data 1-MB RAM BOOT_LOCN 512 bytes RBDA IVT 256 bytes 1024 bytes Tool for exploring 8086 memory • We have written a Linux device-driver, and a companion application-program, that lets you view the bottom megabyte of memory • First you have to compile, and then install, the ‘8086.c’ device-driver kernel-object: $ mmake 8086 $ /sbin/insmod 8086.ko • Then you can execute our ‘fileview’ tool: $ ./fileview /dev/8086 ‘fileview’ commands • You use arrow-keys to navigate memory, or <ENTER> to enter specific addresses • You can adjust the hexadecimal formatting (‘B’ = byte, ‘W’ = word, ‘D’ = doubleword) • You can quit by hitting the <ESCAPE>-key Examples • • • • • • • View ROM-BIOS code at 0xF0000 View Interrupt Vectors at 0x00000 View ROM-BIOS DATA at 0x00400 View Text-mode VRAM at 0xB8000 View Video ROM-BIOS at 0xC0000 View the BOOT_LOCN at 0x07C00 (Note: Linux may have ‘overwritten’ some areas that ROM-BIOS startup-code set up) Question • The Extended BIOS Data Area resides in a portion of RAM that’s usually just below the VRAM memory-area (at 0x000A0000) • This portion of RAM is subtracted from the total RAM available to operating systems • So where’s the top of the ‘unused’ portion of the installed RAM? BIOS ‘Get MemSize’ function • There’s a function your real-mode code can call to find out where ‘top-of-ram’ is • This function requires no arguments; it merely returns a value in the AX register • That value gives the size of the memory (in kilobytes) that lies below ‘top-of-ram’ • You call this routine using: int $0x12 Our ‘memsize.s’ demo • If you assemble, link, and install our demo, it will show you the size of ‘free’ memory when you reboot your workstation $ cp /home/web/cruse/cs630/memsize.s . $ as memsize.s –o memsize.o $ ld memsize.o -T ldscript -o memsize.b $ dd if=memsize.b of=/dev/sda4 Now boot from the GRUB ‘CS 630 partition’