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.
.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
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.
.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