CLOSE

We have been late for this topic, as it was not important but informational thing. So In this chapter we will use the BIOS routines for the key input in the stage 2 of our boot code.

There are two ways of doing so:

  1. Using Interrupts
  2. Without Using Interrupts

1️⃣ Using Interrupts:

In it we will use the functions provided by the BIOS to get the key input from the keyboard. We will write all this code in stage 2 before jumping to the protective mode.

The interrupt which we will be using for getting the pressed key is int 16h. You can read more about it from here: 

We will first display a string stating Press any Key to Continue: _, and after pressing the key that key will be displayed in place of cursor _ and a newline will be added.

The interrupt for getting the pressed key is 0x16:

For capturing keyboard input, the BIOS interrupt 0x16 is commonly used. This interrupt provides a simple way to read key presses from the keyboard.

Function 0x00 of int 0x16: Function 0x00 of this interrupt is used to read a key from the keyboard buffer. This function waits for a key press and returns the ASCII code and the scan code of the pressed key in the registers.

  • AH Register: Receives the scan code of the key.
  • AL Register: Receives the ASCII code of the key.

Let's use this interrupt in our boot code:

stage2/includes/keyboard.inc:

%ifndef __KEYBOARD_INC__
%define __KEYBOARD_INC__

GetKeyInputWithBIOS:
	pusha
	mov si, sPressAnyKeyStatement
	call PrintString16BIOS
	
	;; Get key input from the keyboard
	mov ah, 0x00	; Function to read key
	int 0x16		; Wait for key stroke and read
	
	;; when key is pressed
	;; This interrupt would return:
	;; AH = key scan code
	;; AL = ASCII character or zero if special function key.
	
	;; Display the pressed key
	mov ah, 0x0E
	; al already got the pressed key ascii character
	int 0x10
	
	call PrintNewline	; \n
	
	popa
ret

;; variables
sPressAnyKeyStatement: db 'Press Any Key to Continue.: ', 0

%endif

2️⃣ Without Using Interrupts:

As we all know that we can directly interact with the hardware all we need to know is their I/O port, if they are port mapped devices else their addressable memory, if they are memory mapped devices (graphics card).

Keyboard is a port-mapped I/O device. This means that the CPU communicates with the keyboard controller via specific I/O ports. These ports are used to send and receive data to and from the keyboard.

Key I/O Ports for the Keyboard:

Ⅰ Data Port (0x60):

  • This port is used to read scan codes from the keyboard and to send commands or data to the keyboard.

Ⅱ Status Port (0x64):

  • This port is used to read the status of the keyboard controller. It provides information about the current state of the controller and the keyboard, such as whether the input buffer is full or if there is an error.

Capturing Keyboard Input

To capture keyboard input in real mode, you typically do the following:

  • Enable Interrupts: Ensure interrupts are enabled so that your program can respond to keyboard events.
  • Polling the Keyboard Controller: In real mode, you often need to poll the keyboard controller directly to check if a key has been pressed.
poll_keyboard:
    in al, 0x64   ; Read status port
    test al, 1    ; Test if the output buffer is full
    jz poll_keyboard ; If not full, continue polling

    in al, 0x60   ; Read scan code from data port
    mov ah, 0     ; Clear AH for the BIOS interrupt call (optional)

    ; At this point, AL contains the scan code of the key pressed
    ; You can process the scan code here or convert it to ASCII if needed

    ; Example: Print the scan code to the screen (for demonstration purposes)
    call print_scan_code