Intel x86 instruction set architecture Graded assignment: hand-written resolution of exercise II 2) The exercises on this tutorial are targeted for the as86 assembler. This program is available in the dev86 package on Fedora, and in the bin86 package on Ubuntu. The following convention is used for the filenames:.s extension for assembly files (source code) and.bin extension for the final binary (executable) image. 1) Consider the following program: I VRAM_SEG=0xb800 ; Base address for etxt mode memory is 0xb8000 (see annex 1) ATTR=0x1f ; Text attributes (see annex 1) CHAR='A' entry start start: mov ax, #VRAM_SEG mov ds, ax mov [0],#CHAR mov [1],#ATTR loop1: jmp loop1 ; bootloader signature org 510 db 0x55,0xAA ;This is used to ensure the minimum image size required by qemu org 2047 db 0 Save the code above as file.s and compile it: as86 file.s -o file.o 2) Build the final binary image: ld86 -d file.o -o file.bin In order to automate the process, you may use the following Makefile: %.bin: %.o ld86 -d $^ -o $@ %.o: %.s as86 $< -o $@ 3) Test the program on QEMU: qemu-system-i386 file.bin The binary file is treated as storage device. Therefore, our program, resident on the first 512 bytes, will be treated as a bootloader It is possible to monitor the emulated machine execution. This can be done in two ways: Intel x86 instruction set architecture 1/9
a) By starting QEMU with the -monitor stdio option: qemu-system-i386 file.bin -monitor stdio In this case, the program will present a (qemu) prompt on the terminal. b) By pressing ctrl+alt+2 during execution on QEMU s window. The available options are the same in both case. For instance, it is possible to inspect the CPU registers contents using info registers (figure 1). Figure 1 Registers contents The output can be scrolled up and down using ctrl+up e ctrl+down, respectively. It is possible to obtain the contents of a single register using the print command. For instance: (qemu) print $eax 0xb800 The xp command is used to inspect the main memory. For instance: (qemu) xp 0xb8000 000b8000: 0xb0001f41 In this case, 0x41 is stored at address 0xb8000, 0x1f at address 0xb8001, etc. The output can be present in different numeral systems: x (hex), d (signed decimal), u (unsigned decimal), o (octal), c (char), i (asm instruction). With the xp command, the output can also be aligned: b (8 bits), h (16 bits), w (32 bits), g (64 bits). Figure 2 shows some examples of these modifiers Intel x86 instruction set architecture 2/9
Figure 2 xp and print commands More information is available at http://wiki.qemu.org/download/qemu-doc.html and http://en.wikibooks.org/wiki/qemu/monitor Intel x86 instruction set architecture 3/9
II 1) Due to compatibility reasons, and also for support of BIOS configuration software, the PC BIOS offers several services. One of them is the character printing service, interrupt 0x10: mov al,#0x30 ; ASCII code for 0 mov ah,#0xe mov bx,#7 int 0x10 ; impressão de carácter This service is similar to the C language putchar function, receiving the function argument (ASCII code of the desired character) through the AL register. Write a program that prints your student number using the PC BIOS character printing service, interrupt 0x10. Store your student number as a string and associate a label to it: ; Add this at the end of the source file numero_aluno:.ascii YOUR NUMBER db 13, 10, 0 org 510 db 0x55,0xAA org 2047 db 0 Remark: the labels are handled as offsets relative to the first instruction of the program. Since this program will be executed as a boot loader, the first instruction will be stored at the physical address 0000:7C00. Therefore, in order to use the labels as operands in memory operations, you must load DS with 0x7C0. It is also possible to use ES in order to easily address two segments of 64 kib. When using the as86 assembler, you must add the SEG ES directive before each instruction intended to use ES. This is illustrated in the program below: MOV AX, #VRAM_SEG MOV ES, AX SEG ES mov [0],#CHAR 2) Consider the following program: loop1: mov ah,#0 int 0x16 ; read char from keyboard mov ah,#0xe mov bx,#7 int 0x10 ; print char jmp loop1 Using the interrupt system, complete the program such that it periodically prints the string numero_aluno at a 1 Hz rate. In order to implement the desired temporization, define an interrupt service routine (ISR) for interruption 0x1C. This interruption is generated at a rate of 18.2 Hz (see annex 2 for more details). Intel x86 instruction set architecture 4/9
ISR pseudo-code: Decrement counter (counter is a global variable, initialized with 18) If counter is not 0, exit Print student number (last exercise) Reload counter with 18 The ISR should terminate with the instruction IRET (as opposed to the RET instruction, used in the standard routines). In the initialization phase, the program must configure the interrupt vector table. Use the CLI instruction to disable interruptions while changing the table, and STI afterward. The program skeleton should look like annex 3. 3) In general, the data lines of the PC s parallel port are mapped to the address 0x378 of the input/output address space (the address is stored at 0040:0008H). Change the previous program so that the state of the data lines of the parallel port is switched at each interruption. Intel x86 instruction set architecture 5/9
Annex 1 IBM PC graphics controller in text mode 80x25 matrix of words From B000:0000 for monochromatic systems. From B800:0000 for polychromatic systems. First address corresponds to the upper left corner position of the screen. Intel x86 instruction set architecture 6/9
Intel x86 instruction set architecture 7/9
Annex 2 IBM PC 0x1C interruption The 1.19MHz signal is divided by 65536 on the 8253. Intel x86 instruction set architecture 8/9
Annex 3 1 Hz timer TIME_DIV EQU 18 entry start start: loop1: mov ax,#0 mov ds,ax cli ; disable maskable interrupts mov [0x1C*4],#isr_timer mov [0x1C*4+2], #0x7C0 sti ; enable maskable interrupts hlt ; halts the CPU (until next interruption) jmp loop1 isr_timer: push ax push bx push ds mov ax, #0x7c0 mov ds, ax ;handles counter as a byte pointer, as opposed to the default word pointer dec BYTE counter jnz fim mov counter, #TIME_DIV ;do something fim: pop ds pop bx pop ax iret counter: dw TIME_DIV org 510 db 0x55,0xAA org 2047 db 0 Intel x86 instruction set architecture 9/9