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