Table of Content
Experiment and Programming Environment
Refer to the bootstrap tutorial for the experiment and programming environment, and for how to compile and run the programs here.
Accessing I/O Device Controllers
There are three methods to access memory and registers in I/O device controllers, i.e.,
- via I/O ports,
- via memory mapping (i.e., memory-mapped I/O), and
- via hybrid method (i.e., via both in the above)
For examples using the I/O port method, please check out the examples in Section I/O Schemes. For the example using memory-mapped I/O, see the display example.
Example 1 Displaying Text via Accessing VGA Memory
;
; disp0.asm
;
; VGA
;   https://en.wikipedia.org/wiki/VGA-compatible_text_mode
;   https://en.wikipedia.org/wiki/Video_Graphics_Array#Color_palette
[org 0x7c00]
mov ax, 0xb800
mov es, ax
mov byte [es:0], 'H'
mov byte [es:1], 0x01
mov byte [es:2], 'e'
mov byte [es:3], 0x02
mov byte [es:4], 'l'
mov byte [es:5], 0x03
mov byte [es:6], 'l'
mov byte [es:7], 0x04
mov byte [es:8], 'o'
mov byte [es:9], 0x05
mov byte [es:10], ','
mov byte [es:11], 0x06
mov byte [es:12], 'W'
mov byte [es:13], 0x17
mov byte [es:14], 'o'
mov byte [es:15], 0x28
mov byte [es:16], 'r'
mov byte [es:17], 0x39
mov byte [es:18], 'l'
mov byte [es:19], 0x4a
mov byte [es:20], 'd'
mov byte [es:21], 0x5b
mov byte [es:22], '!'
mov byte [es:23], 0x6c
jmp $
times 510-($-$$) db 0
dw 0xaa55
I/O Schemes
We divide the pattern to access the device controller into 3 I/O schemes,
- polled I/O (or, the busy-waiting I/O method)
- interrupted I/O, and
- Direct Memory Access (DMA) We sometimes refer the first two as Programmed I/O since we must program to read or write every byte of data via CPU instructions. In contrast to Programmed I/O, once we program a device controller to use DMA, the device controller transfers to and from the main memory directly without involvement of the CPU.
Here we present examples for Programmed I/O. These examples use the subroutines defined in three other .asm files, print_line.asm, print_space.asm, and print_byte.asm whose source code we list below examples.
Example 2 Polled I/O for PS/2 Keyboard
;
; kbdpoll0.asm
;
; http://www-ug.eecg.toronto.edu/msl/nios_devices/datasheets/PS2%20Keyboard%20Protocol.htm
; http://bochs.sourceforge.net/techspec/PORTS.LST
; disable keyboard interrupt
mov al, 0x02
out 21h, al
mov cx, 0
KBD_READ:
    push cx 
LOOP:
    ; query keybard status
    in al, 0x64
    mov cl, al
    and al, 0x01
    jz LOOP
    ; read scan code
    in al, 0x60
    ; print scan code
    ; for PS/2 scan code table, see
    ;   http://www.vetra.com/scancodes.html
    mov cl, al
    call print_byte
    call print_space
    pop cx
    inc cx
    and cx, 0x01
    jnz KBD_READ
    call print_line
    jmp KBD_READ
jmp $
%include "print_byte.asm"
%include "print_space.asm"
%include "print_line.asm"
times 510-($-$$) db 0
dw 0xaa55
Example 3 Interrupted I/O for PS/2 Keyboard
;
; kbdint0.asm
;
[org 0x7c00]
; Keybaord interrupt is maskable. To Intel 8259 Programmable Interrupt
; Controller, the IRQ number of the keyboard interrupt is 1, which
; means if we were to mask the interrupt, we set the 1st bit of the
; mask (a byte) as 1, i.e, 0x02 (0000 0010 in binary). 
;
; For interrupt request number assignment on PC, see 
; https://en.wikibooks.org/wiki/X86_Assembly/Programmable_Interrupt_Controller
;
; Uncomment the following two statements, and run the code. Does
; the keyboard interrupt till work? why? 
; 
; mov al, 0x02
; out 21h, al
jmp START
KBD_ISR:
    push ax
    push cx
    ; read scan code
    in al, 0x60
    mov cl, al
    call print_byte
    call print_space
    mov byte ah, [line_counter]
    inc ah
    mov byte [line_counter], ah
    and ah, 0x01
    jz LINE_BREAK
    jmp EXIT_ISR
LINE_BREAK:
    call print_line
EXIT_ISR:
    pop cx
    pop ax
    iret
    
%include "print_byte.asm"
%include "print_space.asm"
%include "print_line.asm"
line_counter:
    db 0
START:
    ; Set interrupt number 0x16 (or 21 in decimal)'s interrupt
    ; service routine as ours. For interrupt 0x16, see
    ;   https://en.wikipedia.org/wiki/INT_16H
    cli
    cld
    mov ax, 0
    mov es, ax
    mov ax, KBD_ISR
    mov [es:21*4], ax
    mov [es:21*4+2], cs
    sti
    jmp $
POS:
    db 0, 0, 0, 0
times 510-($-$$) db 0
dw 0xaa55
Required Subroutines
;
; print_byte.asm
;
; print a byte in register cl to hex assuming ASCII encoding
;    void * print_byte(char c) 
print_byte:
    pusha ; Push AX, CX, DX, BX, original SP, BP, SI, and DI.
    mov ch, cl
    shr ch, 4
    and ch, 0x0f
    push cx
    mov cl, ch
    call print_low_4bits
    pop cx
    mov ch, cl
    and ch, 0x0f
    push cx
    mov  cl, ch
    call print_low_4bits
    pop cx
    popa
    ret
; print lower 4 bits in register cl to hex assuming ASCII encoding
;    void * print_byte(char c) 
print_low_4bits:
    pusha
    mov ah, 0x0e
    mov ch, cl
    and ch, 0x0f
    cmp ch, 0x09
    jg A_TO_F
    add ch, '0'
    jmp PRINT_CHAR
A_TO_F:
    sub ch, 0x0a
    add ch, 'A'
    jmp PRINT_CHAR
PRINT_CHAR:
    mov al, ch
    int 0x10
    popa
    ret
;
; print_space.asm
;
print_space:
    pusha
    mov ah, 0x0e
    mov al, ' '
    int 0x10
    popa
    ret
;
; print_line.asm
;
print_line:
    pusha
    ; line feed    
    mov ah, 0x0e
    mov al, 0x0a
    int 0x10
    ; carriage return
    mov ah, 0x0e
    mov al, 0x0d
    int 0x10
    popa
    ret