This 3rd post on BootBoot my x86 Assembly helloworld will be shorter than Part 1 and Part 2. We are going to be waiting for a key to be pressed to switch from the cyan screen of Part 1 to the purple screen of Part 2.
Code additions:
x86 assembly has two special instructions to read from and write to I/O devices. This is because some processors might use different address spaces for the memory and the devices (ARM for example uses one address space for all).
We are going to implement .key_wait
using these instructions to wait until a key is pressed before we load the 2nd sector we’ve seen in Part 2.
1 2 3 4 5 6 |
.key_wait: in al, 64h and al, 1 jz .key_wait in al, 60h |
Let’s see what’s going on in .key_wait
:
According to this documentation on the PS/2 controller thee PS/2 Controller itself uses 2 IO ports (IO ports 0x60 and 0x64). Like many IO ports, reads and writes may access different internal registers.
To read the status register we must use the 0x64 port. The Status Register contains various flags that show the state of the PS/2 controller. The meanings for each bit are demonstrated in section “Status Register” of [7].
We first set the 64h instruction in al to check the keyboard status.
Then we use and al, 1 to check if the keyboard controller output buffer is full. If it is, and between the 1st bit of al and 1 will be 1. So can then use jz to loop when the result is zero and the buffer is not full.
When we exit the loop we call in al, 60h to read the buffer. In this program we don’t care about the buffer content (any key pressed is fine) but we still need to read the key event to change the buffer flag and to prevent it from eternally showing pending data.
Results:
Running the program with make run (explained in Part 1) and pressing a random key (space in the gif below) changes the color:
Code:
bb.asm:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
org 7c00h bits 16 start: mov sp, 7c00h mov ax, 0 mov ds, ax mov es, ax mov ss, ax mov [saved_drive_num], dl mov ax, 13h int 10h mov ax, 3 call clearscreen .key_wait: in al, 64h and al, 1 jz .key_wait in al, 60h mov ax, 0 mov ds, ax mov ah, 02h mov al, 1 mov ch, 0 mov cl, 2 mov dh, 0 mov dl, [saved_drive_num] mov bx, sector_2 int 13h jnc sector_2 .inf_loop: jmp .inf_loop clearscreen: push ax mov ax, 0a000h mov ds, ax mov di, 0 pop ax mov cx, 64000 .loop_begin: mov [di], al inc di dec cx jnz .loop_begin ret saved_drive_num: db 0 times 510-($-$$) db 0 dw 0aa55h sector_2: mov ax, 5 call clearscreen .inf_loop: jmp .inf_loop |
the Makefile and .gdbinit are the same we’ve used in previous posts.
Links:
Previous posts on BootBoot:
[BootBoot] Part 1: Boot from a floppy drive and clear the screen
[BootBoot] Part 2: Boot from a floppy drive, load the 2nd sector, jump to it
Other:
[1]: Ralf Brown’s Interrupt List
[2]: Mode 13h in Wikipedia
[3]: X86 memory segmentation
[4]: NASM – The Netwide Assembler
[5]: The Netwide Disassembler, NDISASM
[6]: Int 13/AH=02h: Read disk sectors into memory
[7]: “8042” PS/2 Controller