Using data conversions How we can ‘peek’ at regions of system memory, such as the ROM-BIOS DATA AREA Our programming tools • Last time we got some in-class practice at using our software development tools: – The Linux assembler: as – The Linux linker: ld – The low-level copying utility: dd – The UNIX visual text-editor: vi • We applied these tools to the creation of a ‘boot-sector replacement’ (something we’ll need to know about for writing a mini OS) X86 ‘real-mode’ memory ROM-BIOS Expansion ROMs Video BIOS VRAM Extended BIOS Data Area EBDA 1-MB DRAM BOOT_LOCN CS:IP Interrupt Vector Table ROM-BIOS DATA AREA RBDA IVT Disk Storage Numerical conversions • While developing and debugging our own miniature operating system, we will often need to display some numerical values • The ROM-BIOS does not provide ‘helper’ functions for directly displaying raw data • But assembly language programmers learn to code efficient algorithms which perform frequently needed conversions Binary-to-Decimal • We can use a procedure that converts a value found in register AX into its representation as a string of decimal numerals, suitable for output to the terminal screen or to a printing device • It’s based on repeatedly dividing the AX value by ten (the radix of our decimal-system) to generate a stream of remainders, which are converted to ascii-codes and then displayed in reverse order Algorithm’s implementation ax2dec: # converts value in AX to a string of decimal digits at DS:DI pusha # save contents of registers mov $10, %bx # decimal-system base in BX xor %cx, %cx # initialize CX as digit-counter nxdiv: xor %dx, %dx # extend AX for next division div %bx # divide (DX,AX) by the base push %dx # save remainder on the stack inc %cx # and update our digit-counter or %ax, %ax # was the quotient nonzero? jnz nxdiv # yes, then do another division nxdgt: pop %dx # recover the newest remainder add $’0’, %dl # convert number to a numeral mov %dl, (%di) # store numeral in output-buffer inc %di # advance output-buffer index loop nxdgt # process all the remainders popa # restore contents of registers ret Left-versus-Right justification • The foregoing assembly language routine draws the string of decimal-digits using a “left-justified” display-convention: # left-justified 34567 963 2468 # right-justified 34567 963 2468 • But sometimes we prefer the appearance of a “right-justified” display-convention Our ‘memsize.s’ demo • We’ve written a boot-sector replacement program that reports the amount of realmode memory available (in kilobytes) • It uses a ROM-BIOS function (int 0x12) to obtain this memory-size in the AX register • Then it uses repeated division by ten to convert the AX value into a right-justified string of decimal numerals for display Can avoid ‘interrupt-0x12’ • It’s possible for a program to find out the upper limit of real-mode memory without using the ‘int $0x12’ ROM-BIOS function • The memory-size is among the variables stored in the ROM-BIOS data-area – so we can fetch it directly if we know where it’s located, namely at address 0x00413 • ROM-BIOS is only useful in Real Mode Fetching value from 0x00413 .code16 # for execution in x86 ‘real-mode’ # # Here’s real-mode code that returns the ‘memsize’ value in register AX # get_memory_size: push %ds # preserve value in DS register mov $0x0040, %ax # address ROM-BIOS DATA-AREA mov $%ax, %ds # using the DS segment-register mov %ds:0x13, %ax # load the ‘memsize’ value into AX pop %ds # recover the former DS value ret # return control to the caller NOTE: The ‘int $0x12’ instruction can be stored in just two bytes: 0xCD, 0x12 (which is good to know if your program-code needs to fit within a small space) Binary-to-Hexadecimal • Our ability to see a computer’s binary-data in an understandable format will be vital in exploring hardware or debugging software • 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 numeral-characters 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 leftward-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 Our ‘viewrbda.s’ demo • We can use this binary-to-hex algorithm to view the contents of regions in memory, such as the ROM-BIOS DATA-AREA • The RBDA includes a number of variables whose values change with system activity • For a dynamic view of this memory area, we need to draw and redraw its contents Direct-Drawing to VRAM • Our demo-program will perform its 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 within video memory (at offsets 0, 2, 4, …, 158) control the top row (row number 0) on the screen, in 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 first gets programmed at startup for ‘standard’ text-mode ‘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 Several regions of interest • • • • • • • 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) In-class exercise #1 • Can you revise our ‘memsize.s’ program so that it would get its word-value directly from the ROM-BIOS DATA AREA, rather than by using an ‘int $0x12’ instruction? In-class exercise #2 • There is a ROM-BIOS service (int 0x11) which returns a word-value in register AX that is known as the ‘equipment_list’ flag • It’s a collection of bit-fields, rather than a single number, and gives us information about some of the hardware attached to the system (e.g., floppy drives, mouse) • Can you display it in hexadecimal format? The ‘Equipment-List’ flag 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 No of serial-ports (0..4) Modem installed (1=yes, 0=no) No of floppy drives (if bit 0 is set to 1) 00=one, 01=two, 10=three, 11= four No of parallel-ports (0..3) Initial video display mode 00 = EGA/VGA 01 = color 40x25 10 = color 80x25 11 = mono 80x25 Mouse port installed on system board (1=yes, 0=no) Math coprocessor installed (1=yes, 0=no) Bootable floppy-drive installed (1=yes, 0=no) 0