BIOS and Hardware


BIOS Data Area

The BIOS Data Area (BDA) is a section of memory located at segment 0040h which stores many variables indicating information about the state of the computer.

These variables can be accessed through direct memory references by setting the segment register (DS or ES) to point to the BDA segment viz. 0040h. Some of the variables can be used to set attributes of the computer or perform useful tasks but the vast majority of the variables can only be read from.

There are many tasks that are duplicated in the interrupt list and in the BDA. The Equipment List is a common feature that can be accessed through interrupt 11h as well as by reading the word at offset 10h in the BDA. Although calling the interrupt is machine-independant, directly accessing the BDA is definitely faster.

Sample Program #10

; stopwatch
DOSSEG
.MODEL SMALL
.STACK 4096

.DATA
Instructions db 'STOPWATCH',13,10
db '---------',13,10
db ' (S)tart S(t)op (Q)uit',13,10
db 13,10,' $'
Blank db ' $'
StartRow db ? ; position on screen of timer
StartCol db ? ; position on screen of timer
Running db 0 ; is timer running ?
TimerStart dd ? ; start value of timer

TempStr db 1,2 ; temporary storage for STDIN input
t2 db 80 dup (0)
t3 db 13,10,'$'

.CODE

ProgramStart:
mov ax,SEG _DATA ; set up data segment
mov ds,ax

lea dx,Instructions ; output instructions
mov ah,9
int 21h

mov ah,3 ; get current position on screen
xor bh,bh
int 10h
mov StartRow,dh
mov StartCol,dl

startkeys: ; loop to get/process keystrokes
mov ah,1 ; check keyboard status
int 16h

jz updateloop ; update if no keys available

mov ah,0 ; get keystroke
int 16h

cmp al,'q' ; check for QUIT key
je theend

cmp al,'s' ; check for START key
jne checkstop ; jump to next check
call start
jmp updateloop ; update display

checkstop:
cmp al,'t' ; check for STOP key
jne startkeys ; continue loop
call stop

updateloop:
cmp Running,0
je startkeys ; continue loop if timer not running

call update ; update screen

jmp startkeys ; continue loop

theend:
lea dx,t3 ; move cursor to next line
mov ah,9
int 21h

mov ah,4ch ; terminate program
int 21h

update PROC ; procedure to update timer display
mov ax,0040h ; set ES to point to BIOS data area
mov es,ax
mov ax,es:[006Ch] ; get system clock value
mov dx,es:[006Eh]
sub ax,word ptr TimerStart ; get time difference
sbb dx,word ptr TimerStart+2

mov bx,65535
div bx ; get hours in AX
mov cx,ax ; save hours value in CX
xor ax,ax
xchg ax,dx ; swap remainder into AX
mov bx,1092
div bx ; get minutes in dx
mov si,ax ; save minutes value in SI
xor ax,ax
xchg ax,dx ; swap remainder into AX
mov bx,18
div bx ; get seconds in ax
push ax ; save seconds
push si ; save minutes
push cx ; save hours

xor bh,bh ; put cursor at old position
mov dh,StartRow
mov dl,StartCol
mov ah,2
int 10h

pop ax ; restore hours
call writesint ; output hours
mov dl,':' ; output colon
mov ah,2
int 21h

pop ax ; restore minutes
call writesint ; output minutes
mov dl,':' ; output colon
mov ah,2
int 21h

pop ax ; restore seconds
call writesint ; output seconds

lea dx,Blank ; erase any digits left from old time
mov ah,9
int 21h

ret
update ENDP

start PROC ; procedure to start timer
mov ax,0040h ; set ES to point to BIOS data area
mov es,ax
mov ax,es:[006Ch] ; get system clock value
mov dx,es:[006Eh]
mov word ptr TimerStart,ax ; store clock value in TimerStart
mov word ptr TimerStart+2,dx
mov Running,1 ; set timer flag
ret
start ENDP

stop PROC ; procedure to stop timer
mov Running,0 ; clear timer flag
ret
stop ENDP

writesint PROC ; outputs the integer in the AX register

push ax ; save all registers used
push bx
push cx
push dx
push si

mov bx,10 ; radix = decimal = 10
xor si,si ; initialize digit counter
startdiv:
xor dx,dx
div bx ; divide no by 10
push dx ; save remainder
inc si ; increment digit counter
cmp ax,0 ; check for end of divisions
jne startdiv

mov cx,si ; set digit counter
mov bx,0 ; initialize position counter
makeletters:
pop ax ; get digit
add al,48 ; make integer --> ascii value
mov [t2+bx],al ; insert digit into string
inc bx ; increment position counter
loop makeletters
mov [t2+bx],'$' ; put end-of-string marker

mov ah,09h ; output integer as string
lea dx,t2
int 21h

pop si ; restore registers
pop dx
pop cx
pop bx
pop ax

ret ; return to point of call

writesint ENDP

END ProgramStart

Hardware

Hardware is normally used to refer to the physical devices of the computer. From a low-level language perspective it refers to direct communication with such devices.

Direct communication must only be used when all else fails because the sequence of communication can be quite complex. As a result, this task is accomplished by DOS and BIOS routines almost exclusively. Sometimes, however, a function is required that is not available in the standard DOS/BIOS interrupt list.

Each device in the computer has a unique number or numbers that the CPU can use to send information to the device or receive information from the device. These unique numbers are called port numbers.

To send a byte of information to the device that corresponds to port number 60h, the following command is issued:

out 60h,al

This sends the value in the AL register to the device that uses port 60h (normally the keyboard). The device responds appropriately. There is however the restriction that the OUT command can only be used with the AL register.

To input a byte of information from a device, the corresponding command is the IN command:

in al,60h

This is identical to the OUT command except it reads data into AL from the device.

A typical reason to use hardware commands is to produce a sound through the PC speaker. There is no interrupt procedure in DOS/BIOS to do this so it has to be done using hardware.

Sample Program #11

; simple sound routine
DOSSEG
.MODEL SMALL
.STACK 4096

.DATA

.CODE

ProgramStart:
mov ax,SEG _DATA ; set up data segment
mov ds,ax

mov ax,6553 ; set up parameters and call sound routine
push ax
mov ax,3
push ax
call sound

mov ax,3000 ; set up parameters and call sound routine
push ax
mov ax,6
push ax
call sound

mov ah,4ch ; terminate program
int 21h

sound PROC ; procedure to produce sound
push bp ; save stack position
mov bp,sp

mov al,10110110b ; mode register bitset
out 43h,al ; set mode register
jmp $+2 ; waste time
mov cx,[bp+6] ; get frequency divider
mov al,cl
out 42h,al ; send frequency divider (low) to hardware
jmp $+2 ; waste time
mov al,ch
out 42h,al ; send frequency divider (high) to hardware
jmp $+2 ; waste time

in al,61h ; switch speaker on
or al,03h
out 61h,al

mov ax,0040h ; point ES to BIOS data area
mov es,ax
mov ax,es:[006Ch] ; get timer value
mov dx,es:[006Eh]
soundtimer: ; loop to wait for n seconds/18
mov bx,es:[006Ch] ; get timer value
mov cx,es:[006Eh]
sub bx,ax ; subtract from initial value
sbb cx,dx
cmp bx,[bp+4] ; check if time is up
jge soundstop ; if so, stop sound
jmp soundtimer ; otherwise, continue checking

soundstop:
in al,61h ; switch speaker off
and al,0fch
out 61h,al

mov sp,bp ; restore stack
pop bp
ret 4 ; pop parameters off stack and return
sound ENDP

END ProgramStart