230 likes | 239 Views
Explore memory areas in a computer system using hexadecimal displays for debugging. Learn how to convert 16-bit numbers to hexadecimal in assembly language.
E N D
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 Lo-nybble BX = ? ? ? ? ? ? ? ? 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 = ‘4’ DS:DI = &buf 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 # save general registers mov $4, %cx # setup digit-count nxnyb: rol $4, %ax # rotate hi-nybble into AL mov %al, %bl # copy the nybble into BL and $0xF, %bx # isolate the nybble’s bits mov hex(%bx), %dl # lookup nybble’s ascii-code mov %dl, (%di) # store numeral into buffer inc %di # advance the buffer index loop nxnyb # generate remaining digits popa # restore saved registers ret # return control to the caller hex: .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 Byte at even-numbered offset Background color Foreground color 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 $0xB800, %ax # address VRAM segment mov %ax, %es # using the ES register xor %di, %di # point ES:DI at top-left cell movb $’A’, %es:0(%di) # draw character-code to VRAM movb $0x07, %es:1(%di) # 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 $0x0003, %ax # set standard 80x25 textmode int $0x10 # invoke BIOS video service mov $0x1112, %ax # load 80x50 character-glyphs int $0x10 # 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’ 0x0369CF25 EAX = Power-PC processor uses ‘big-endian’ convention 0 Intel x86 CPU’s use ‘little endian’ storage contention 0 25 03 1 1 CF 69 2 2 69 CF 3 3 03 25 ‘Little endian’ convention (least-significant byte is at lowest memory-address) ‘Big endian’ convention (most-significant byte is at lowest memory-address)
Example: convert vector 0 to hex buf: .ascii “xxxxxxxx” # room for 8 hex-digits Vector0: xor %bx, %bx # address bottom of memory mov %bx, %fs # using register-pair FS:BX mov %fs:0(%bx), %ax # fetch vector’s low-word lea buf+4, %di # point DS:DI to position call ax2hex # convert AX to hex-string mov %fs:2(%bx)m %ax # fetch vector’s high-word lea buf+0, %di # point DS:DI to position call ax2hex # convert AX to hex-string # OK, ‘buf’ now holds the 8-digit hex-string representing vector 0
‘Real-Mode’ Memory Map ROM-BIOS Vendor’s Firmware 64+ kbytes No installed memory Video-ROM Video Display Memory VRAM 128 kbytes Top of RAM (= 0x000A0000) Extended BIOS Data 1-MB Volatile Program Memory RAM 0x00007E00 BOOT_LOCN 512 bytes 0x00007C00 0x00000500 256 bytes RBDA 0x00000400 IVT 1024 bytes 0x00000000
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’