Chapter 13
|
Modified: |
MSDOS is a single-user/single-tasking operating system, though largely replaced by MS-Windows on the desktop, still is used for single program applications (e.g. embedded) and provides useful insight into OS and hardware. Access to MSDOS functions is through interrupts.
Interrupts are used for multiple purposes on the PC system architecture. One use is for hardware devices such as a mouse click, a key press, etc. to interrupt the usual program execution flow and handle the hardware action. The effect is to interrupt a program's execution temporarily, perform some operation related to the hardware, then return to the interrupted program's normal execution.
A second use provides a standard software interface to widely different PC hardware. For example, all PC's run the same Windows, DOS, C++, your own software, etc. though the hardware is quite different (laptops, desktops, manufacturers, CPU's). From the development of the original PC, IBM defined a set of hardware generic operations that all PC compatible hardware must support. To access these operations, a program issues an interrupt numbered to correspond to the desired operation. For example, for a program to print something on the video screen, it would use interrupt number 10h (e.g. Int 10h), for printing use interrupt 17h (e.g. Int 17h). The interrupt number serves as an index into an array (interrupt vector) that holds the address of functions executed for each interrupt. Int 17h essentially indirectly calls a function using the address stored at index 17h. Since the address stored at index 17h can be changed dynamically, the function executed by Int 17h can also change dynamically. Normally the array is initialized with function addresses when the computer system boots. This numbering scheme has been used since the beginning of the PC architecture so programs that use this scheme can execute on all PC's.
It is important to note the alternative to using a fixed operation number would be to call a function name directly as has been done with WriteDec, etc. Directly calling the function by name breaks down when a different version is needed for different hardware or operating systems (NT, Windows 2000, DOS). For example, a C++ program that prints to the console should not need to know that the console is a LCD or CRT. The basic problem is that different hardware and operating systems need different software to talk to the hardware. If a program calls the Mouse$ function directly (e.g. Call Mouse$), the Mouse$ function address and code must be known when the program is linked (we're ignoring dynamically linked functions as not relevant to this discussion). The program now contains the hardware specific Mouse$ code and can only work on specific hardware platforms. Now consider implementing an operating system that must support many combinations of hardware, using the direct function call method different versions of the operating system would need be linked for every possible hardware combination. This creates obvious maintenance difficulties such as when changes must be made to accommodate new hardware.
Recall the general hardware design of a computer system that includes the PC architecture. The CPU and memory sections appear much the same to software, as long as the CPU supports the program instructions and enough memory exists the program can be executed. The Input/Output section connects the computer to the outside world and must operate with a wide variety of computer interfacing hardware (mice, touch pads, NIC, video, sounds, virtual reality headsets, gloves, etc., etc.) many not conceived when the computer rolled of the production line. Software must be adaptable to changes in the hardware attached to the system. On the PC architecture, these hardware differences are handled primarily by the BIOS (Basic Input/Output System) abstraction.

Interrupts serve two main purposes on PC-systems
- to respond to hardware events such as a key press, and
- to provide a uniform interface to anonymous software functions such as those of the BIOS (Basic Input Output System) or DOS (Disk Operating System).
Software functions may be organized into a hierarchy of layered operations where high-level operations make use of operations in the lower-layers of software/hardware. For example, consider the C++ statement:
The software/hardware layers involved are:
1) User program: cout << "Hello world"; 2) C++ : Call DOS video function 9 to output string "Hello world" 3) DOS: Call BIOS video function by Int 10h 4) BIOS: "Hello world" placed in hardware video memory 5) Video hardware: "Hello world" display from video memory |
This hierarchy also reflects hardware specificity or how generic operations are implemented across different hardware.
The layered list becomes:
1) User program Any machine with C++ compiler 2) C++ PC's, Mac's, Unix, mainframes, etc. 3) DOS All PC compatibles. 4) BIOS Same functions but specific for hardware. 5) Hardware Dependent upon whether laptop, VGA, SVGA, etc. |
Layered software, where each layer calls functions of the layer below, allows a degree of hardware independence at the higher layers.
The same C++ user program can execute on all DOS computers because cout calls only DOS functions directly. DOS can execute on all PC's because DOS directly calls only standard BIOS functions. BIOS actually accesses the hardware and its functions must be designed for the specific hardware, the BIOS of one computer is not generally compatible with that of another. Because of the independence of layering, MS-DOS can operate on any PC, but a BIOS is usually specific for a particular manufactures mother board and processor model. The great advantage of this scheme is to make higher-level programs independent of lower-level, hardware-specific details, supporting generic operations by isolating software and hardware function into distinct layers. The high-level operations are isolated from the differences of the hardware.
We will examine the interrupt mechanism more closely later.
IA-32 Processor Program Registers
General 8, 16 and 32 bit register
Pointer and index registers are 32 bit
32 bits addresses 4 Gb
Memory Segments - Real Address Mode
Segmented Memory4 Gb divided into 1 Mb segments.
Hardware prevents programs from accessing another's segment.
16-bit registers = 1 Mb. addressing
Segment registers
DS - Data segment
CS - Code segment
SS - Stack segment
ES - Extra segment
Segmented memory is limited to 220 addresses. To access a variable in memory, the stack, or to fetch an instruction requires a 20-bit address. But the stack pointer, Sp, variable operands, and the instruction pointer, Ip, are only 16-bits, not 20-bits, so cannot access all of physical memory directly using a 32-bit address.

Recall that Program = Algorithm + Data.
The processor supports several memory models, the segmented memory addressing model used by DOS divides memory into four separate segments for:
| CS
Code Segment (Algorithm) |
ES
Extra Segment |
| DS
Data Segment |
SS
Stack Segment |
To understand how segmented memory addressing works consider when a memory variable is accessed by the instruction:
Mov X, 74h
Assume physical memory, the location of X in the segment, and the Ds register are defined as below.
Ds register points to the start of the Data Segment where X is located.
Offset of X is the distance of the location of X from the start of the segment.
Compute the 20-bit segment address of X.
|
|
|
|
Physical Address X = Ds*10h + offset X = 1230h*10h +0048h = 12300h + 0048h = 12348h |
Physical Address of fetch = Cs*10h + Ip
Physical Address Push or Pop = Ss*10h + Sp
Question 1
Given Ds = 1234h and Y is at offset 45h.
What is the 20-bit physical segment address of Y?
Question 2
Given Ss = 5678h and Sp is at offset 00FFh.
What is the 20-bit physical segment address referenced by Sp?
Question 3
Given Ss = 5678h and Sp is at offset 00FFh and the following is executed.
Push Ax
What is the 20-bit physical segment address now referenced by Sp?
PC Memory Organization
Interrupts force the processor to stop its normal execution path and, temporarily execute another path. Once the interrupt execution path is completed, execution returns to the normal path that was interrupted.
The sequence of events for a typical
interruption are:
Computer
|
People
|
There are two origins of interrupts:
1) generated by the PC hardware
in response to some event such as division by zero, an external device such as a keypress or printer
signal, and
2) execution of the software instruction Int by the
CPU.
From the perspective of the CPU execution both interrupts are treated identically.
The Int instruction acts similar to a Call, the return address is saved on the stack and Cs:Ip point to the next instruction to fetch. Additionally, the flags are saved and the trap and interrupt flag are set to 0 to temporarily prevent further interrupts. The key difference between a Call and an Int is how the location of the next instruction to fetch is determined.
For the Call instruction the function address is part of the instruction itself. For example, Call Power would contain the address of the Power function. When executed the CPU saves the return address on the stack (the Cs:Ip registers), and sets the next instruction to fetch (the Cs:Ip registers) to the address of Power.
Interrupts allow hardware to signal the CPU to suspend the currently
executing algorithm and execute one for the interrupting hardware. In the figure
at
right:
For the Int instruction the function address is stored in an
array called an interrupt vector that holds the memory address of
functions. The Int instruction indexes the interrupt vector, for
example Int 5 indexes interrupt vector array entry 5. Each
function address occupies 4 bytes, the interrupt vector is an array
of function addresses, so each array entry is 4 bytes. When the Int
5 is executed the CPU saves the return address (Cs:Ip), and flags.
The address of the next instruction (Cs:Ip) to fetch is stored at
index 5 of the array. The CPU indexes into the interrupt vector
array for the function address at index 5 (each entry is 4 bytes, index
5 is 4*5 bytes from the start of the array (2010 = 1416
bytes). To execute the function address at index 5, the 4 bytes in the
array at index 5 are copied to Cs and Ip. In the following example Cs=432116
and
Ip= 002516, the address of the next instruction to execute,
the start of the function execution.
Int 5 Stack Result _____ |Flags| | Cs | | IP | |
1. Sp = Sp - 2 (Sp) = Flags IF=0, TF=0 2. Sp=Sp-2 (Sp) = Cs 3. Sp=Sp-2 (Sp) = Ip = Return Address 4. Ip=0000:[4*5] = 002516 5. Cs=0000:[4*5+2] = 432116 |
Interrupt Physical Vector Address | 1234 |4*3 =0000C16 | 5678 |4*3+2=0000E16 | 9ABC |4*4 =0001016 | DEF0 |4*4+2=0001216 Ip | 0025 |4*5 =0001416 Cs | 4321 |4*5+2=0001616 | CBA9 |4*6 =0001816 | 0FED |4*6+2=0001A16 |
For example, Interrupt 5 has been defined for the Print Screen operation. The addresses of functions are stored in the interrupt vector, most of those defined are stored into the interrupt vector during system boot. To better visualize how the interrupt vector is used consider only the interrupts 3, 4, and 5.
Suppose we had written interrupt functions for each and wanted the following :
Assuming that the functions
all are stored in code segment 9000 and the offset listed in the
following table at right, the partial interrupt vector would appear as
in the table at left:
| Segment 900016 | |
Index Interrupt Physical Vector Address 3 | 1234 |4*3 =0000C16 PrtBk__| 9000 |4*3+2=0000E16 4 | 9ABC |4*4 =0001016 PrtOF__| 9000 |4*4+2=0001216 5 | 0025 |4*5 =0001416 PrtSc | 9000 |4*5+2=0001616 6 | |4*6 =0001816 _______| |4*6+2=0001A16 |
Offset 1234 PrtBk proc Far 1234 CF Iret 1235 PrtBk endp 9ABC PrtOF proc Far 9ABC CF Iret 9ABD PrtOF endp 0025 PrtSc proc Far 0025 CF Iret 0026 PrtSc endp |
What interrupt routine would be executed by:
Int 4
What physical address(es) should hold the segment and offset of INT 8?
Offset 13 0000 B8 0000 Mov Ax, 0 ; Initialize interrupt 14 0003 8E D8 Mov Ds, Ax ; vector with PrintHelp 15 0005 BB 0014 Mov Bx, 5*4 ; function in place of 16 0008 FA Cli ; regular Print Screen 17 0009 C7 07 0025r Mov word ptr [Bx],offset PrintHelp ; [0001416]=offset PrintHelp 18 000D C7 47 02 0000s Mov word ptr [Bx]+2, Seg PrintHelp ; [0001616]=seg PrintHelp 19 0012 FB Sti |
33 0025 PrintHelp proc Far ; Interrupt function 34 0025 1E Push Ds ; required to save all 35 0026 56 Push Si ; registers modified 36 0027 50 Push Ax 37 0028 53 Push Bx 38 0029 51 Push Cx 39 002A 9C Pushf 40 41 002B B8 0000s Mov Ax, Seg Help ; Print Help! 42 002E 8E D8 Mov Ds, Ax 43 0030 BE 0000r Mov Si, Offset Help ; Interrupt Physical 44 0033 B9 0007 Mov Cx, 7 ; Vector Address 45 0036 AC Do2: Lodsb ; | 1234 |4*3 =0000C16 46 0037 B4 0E Mov Ah, 14 ; | 5678 |4*3+2=0000E16 47 0039 BB 0000 Mov Bx, 0 ; | 9ABC |4*4 =0001016 48 003C CD 10 Int 10h ; | DEF0 |4*4+2=0001216 49 003E E2 F6 While2: Loop Do2 ; Ip | 0025 |4*5 =0001416 50 ; Cs | 4321 |4*5+2=0001616 51 0040 9D Popf ; | CBA9 |4*6 =0001816 52 0041 59 Pop Cx ; | 0FED |4*6+2=0001A16 53 0042 5B Pop Bx 54 0043 58 Pop Ax 55 0044 5E Pop Si 56 0045 1F Pop Ds 57 0046 CF Iret ; Execute Interrupt Return 58 0047 PrintHelp endp |
Below is the code to initialize the interrupt vector to execute PrintHelp on executing INT 5.
Give the code to initialize the interrupt vector to execute ShutDown on executing INT 8.
Give the interrupt vector physical address(es) and its contents for INT 8.
Offset
13 0000 B8 0000 Mov Ax, 0 ; Initialize interrupt
14 0003 8E D8 Mov Ds, Ax ; vector with PrintHelp
15 0005 BB 0014 Mov Bx, 5*4 ; function in place of
16 0008 FA Cli ; regular Print Screen
17 0009 C7 07 0025r Mov word ptr [Bx],offset PrintHelp ; [0001416]=offset PrintHelp
18 000D C7 47 02 0000s Mov word ptr [Bx]+2, Seg PrintHelp ; [0001616]=seg PrintHelp
19 0012 FB Sti
Because interrupt functions are executed by Int instructions which
save the flags in addition to the return address, the Ret instruction
is not correct for returning from an interrupt function, as the
flags are not restored. Use Iret instead (see above example).
Iret
Ip = (Sp) |
Ret
Ip = (Sp) |
Enable and Disable Interrupts - Sti and Cli
It is sometimes necessary that the CPU execute a critical section of code without being interrupted. For example, when the processor controls potentially life threatening applications (weapons, transportation, safety systems).
Executing an interrupt automatically disables interrupts until an Iret is executed (recall that the If flag is cleared to 0 on interrupt execution and the Iret restores the flags).
The following two instructions control whether the CPU responds to external or hardware interrupts by the value of If (interrupt flag). The CPU will always execute software interrupts (e.g. Int 5) that are part of the program code.
Offset
13 0000 B8 0000 Mov Ax, 0 ; Initialize interrupt
14 0003 8E D8 Mov Ds, Ax ; vector with PrintHelp
15 0005 BB 0014 Mov Bx, 5*4 ; function in place of
16 0008 FA Cli ; regular Print Screen
17 0009 C7 07 0025r Mov word ptr [Bx],offset PrintHelp ; [0001416]=offset PrintHelp
18 000D C7 47 02 0000s Mov word ptr [Bx]+2, Seg PrintHelp ; [0001616]=seg PrintHelp
19 0012 FB Sti
INT number
executes the interrupt handler for interrupt number.
Interrupt Vector holds the segment and offset.
Int 5 Stack Result _____ |Flags| | Cs | | IP | |
1. Sp = Sp - 2 (Sp) = Flags IF=0, TF=0 2. Sp=Sp-2 (Sp) = Cs 3. Sp=Sp-2 (Sp) = Ip = Return Address 4. Ip=0000:[4*5] = 002516 5. Cs=0000:[4*5+2] = 432116 |
Interrupt Physical Vector Address | 1234 |4*3 =0000C16 | 5678 |4*3+2=0000E16 | 9ABC |4*4 =0001016 | DEF0 |4*4+2=0001216 Ip | 0025 |4*5 =0001416 Cs | 4321 |4*5+2=0001616 | CBA9 |4*6 =0001816 | 0FED |4*6+2=0001A16 |
Draw the stack after execution of INT 5 instruction below.
SS=110016, SP=001016
Offset 0009 C7 07 0010 Mov word ptr [Bx],10 000D CD 05 INT 5 000F B4 0E Mov Ah, 14 0025 PrintHelp proc Far 0025 CF Iret 0026 PrintHelp endp |
Interrupt Physical Vector Address | 1234 |4*3 =0000C16 | 5678 |4*3+2=0000E16 | 9ABC |4*4 =0001016 | DEF0 |4*4+2=0001216 Ip | 0025 |4*5 =0001416 Cs | 4321 |4*5+2=0001616 | CBA9 |4*6 =0001816 | 0FED |4*6+2=0001A16 |
DOS 'Hello world' - Software Interrupts
Disk Operating System (DOS) provides access to system resources and management of those resources. DOS provides resources accessed by interrupts, Int 21h being the primary interrupt used.
To access hardware, DOS calls the lower, BIOS layer functions.
1) DOS: Int 21h, function 9 - "Hello World" passed to BIOS video function 10h 2) BIOS: Int 10h - "Hello world" placed in hardware video memory 3) Video hardware: "Hello world" display from video memory |
The following displays the string "Hello world" using the main DOS interrupt (Int 21h).
The basic DOS function 21h use is to place the function number in Ah and other parameters in designated registers. From our study of parameter passing methods, passing parameters in registers generally preclude multiple instances of function invocation and recursion, hence DOS operations are not reentrant (recursive) and the DOS operating system cannot handle multiple tasks simultaneously as can Windows 95, NT, etc. using the same CPU.
The DOS definition (INT 21h list) of the function to display a string that ends with a '$' is:
The general use of this and most DOS operations is to place the number of the function in the Ah register and parameters in other registers.
Function 9 Display to standard output string terminated by '$'
Parameters
Ah 9
Ds Segment of string
Dx Offset of string
Returns
Nothing
The following uses DOS Int 21h, function 9 (print a $ terminated string) to print a string pointed to by the parameter in Dx.
Mov Ah, 9 ;; Function number Mov Dx, Offset string ;; Function parameter Int 21h ;; Execute function
Title Display "Hello world" to Screen using DOS interrupts
.model small
.code
Main Proc near
Mov Dx, @data ;; Initialize to program's Data Segment
Mov Ds, Dx
Mov Dx, Offset Hello ;; 'Hello world'
Mov Ah, 9 ;; DOS Function 9 to Display a string
Int 21h ;; Execute DOS Function
Mov Ah, 4ch
Int 21h ;; Return to Caller, operating system
Main Endp
.data
Hello db 'Hello world', '$'
End Main
|
| Function 8 Read character from standard input Parameters Ah 8 Returns Al = character read from standard input |
Function 2 Write character to standard
output Parameters Ah 2 Returns Dl = character to write to standard output |
Give the code to read and write one character using DOS interrupt 21h.
BIOS interrupt functions generally access the input/output hardware directly and are specific for each hardware characteristics, providing a standard interface layer for accessing hardware functions. Each different version of hardware requires different BIOS function code but still uses the same interrupt number (that's why the BIOS from one computer system is not compatible with another's). The BIOS function interrupt numbers and behavior was defined by IBM for the original PC architecture so any program (such as DOS itself) that uses the BIOS interrupts can ignore the specific hardware, BIOS function code is specific for specific hardware.
4) BIOS: Int 10h - "Hello world" placed in hardware video memory 5) Video hardware: "Hello world" display from video memory |
The following displays the string "Hello world" using the BIOS video interrupt (Int 10h). The basic use is to issue the interrupt number of the operation with Ah the device function and other parameters in registers. From our study of parameter passing methods, register parameters generally preclude recursion, hence BIOS operations are not reentrant (recursive). Generally BIOS functions use the Ah register to define the function and other registers for parameters. A function to display to video (or see INT 10H) output is:
Interrupt 10h Video
Parameters
Ah function number
Other registers used dependent upon the function
Returns Dependent upon the functionTo display a single character 'A' to video: Mov Ah, 14 ;; Function number Mov Al, 'A' ;; Character to display Mov Bl, 0 ;; Foreground color white Mov Bh, 0 ;; Display to video page 0 Int 10h ;; Execute video operation
Title Display "Hello world" using BIOS Interrupt routines
.model small
.code
Main Proc near
Mov Dx, @data
Mov Ds, Dx
Cld
Mov Si, offset Hello
@@While: Lodsb ;; While Hello[Si] != '$'
Cmp Al, '$'
Jne @@Do
Jmp @@EndWhile
@@Do: Mov Ah, 14 ;; Video operation in Ah to write
;; character in Al to screen.
Mov Bl, 0 ;; Foreground color 0 (white)
Mov Bh, 0 ;; Alpha page 0
Int 10h ;; Execute Bios video routine
Jmp @@While
@@EndWhile:
Mov Ah, 4ch ;; Return to Caller, operating system
Int 21h
Main Endp
.data
Hello db 'Hello world', '$'
End Main
|
| Interrupt 16h Keyboard Parameters Ah = 0 Returns Al = ASCII code |
Interrupt 10h Video Parameters AH = 14 AL = character to write BH = page number BL = foreground color (graphics modes only) Returns Nothing |
Give the code to read and write one character using BIOS interrupts.
Interrupt Handlers for Hardware Interrupt
DOS and BIOS interrupt routines already exist, have addresses stored in the interrupt vector, and can be invoked by the INT instruction. Creating an interrupt routine that can be executed the same as DOS or BIOS routines requires several steps. The following outlines an interrupt handler, the routine invoked when an interrupt occurs, and an example of a hardware caused interrupt, arithmetic overflow.
Overflow is a hardware event causing the CPU to execute a Type 4 interrupt but can also be executed by a software interrupt, the INT 4 instruction. In either case the interrupt vector must contain the segment and offset of the interrupt handler function to be executed, for a Type 4 interrupt, the function address is stored at vector index number 4. The following prints a message on overflow error. The INTO instruction should be placed after arithmetic operations that may overflow to execute an interrupt when OF=1.
Note that the example may not work correctly on protected operating systems such as Microsoft NT which limit general user access to the hardware. Try OverFlow interrupt on your machine.
Title Initialize and Display Message on Overflow Interrupt .model small .code Main Proc near ;__1__________________________________Initialize Interrupt Vector_______ Cli ;; Disable interrupts during vector
;__2___________________________Enable Interrupts________________________ Sti ;; Ready to handle interrupts | ;__3___________________________Generate Hardware Interrupt Type 4_______ Mov Al, -128 Sub Al, 1
;__3___________________________Generate Software Interrupt Type 4_______
Mov Ah, 4ch
Int 21h
Main Endp
;___4__________________________Interrupt Routine________________________
;; Overflow - Interrupt routine called when type 4 interrupt, OF=1 |
;; Parameters - None. Hardware interrupt routines have no parameters|
;; Returns - Nothing |
;; Registers - None. Interrupt routines should not alter registers |
;; |
Overflow Proc Far
Push Ax
Push Dx
Push Ds
Mov Dx, Seg Error ; Initialize to program's Data Segment
Mov Ds, Dx
Mov Dx, Offset Error ; 'Error Overflow'
Mov Ah, 9 ; DOS Function to Display a string
Int 21h ; Execute DOS Function
Pop Ds
Pop Dx
Pop Ax
IRet
Error db 'Error Overflow', 13, 10, '$'
Overflow Endp
End Main
|
Interrupt Execution - The INTO instruction at line 11 causes the following program to execute in the order of lines 1-11, 16-28, 12, ...
Question 8
What is the result of the INT 4 instruction at line 12?
|
|
Division by zero is a hardware event causing the CPU to execute a Type 0 interrupt but can also be executed by a software interrupt, the INT 0 instruction.
It is important to recognize that division by zero causes an internal CPU interrupt that executes the Division routine below. Note that rather than returning to the point of the division error, the Division routine exits program execution. Try division by 0 interrupt on your machine.
Divide Error Interrupt Handler
Title Initialize and Display Message on Divide Interrupt .model small .code WriteDec proto Far Main Proc near ;__1__________________________________Initialize Interrupt Vector_______ Cli ; Disable interrupts during vector
;__2___________________________Enable Interrupts________________________ Sti ;__3___________________________Generate Hardware Interrupt Type 0_______
Call WriteDec ; Never reached due to divide error
Mov Ah, 4ch
Int 21h ; Return to Caller, Operating System
Main Endp
;___4__________________________Interrupt Routine________________________
;; Divide - Interrupt routine called when type 0 interrupt |
;; Parameters - None. Hardware interrupt routines have no parameters|
;; Returns - Does not return. |
Divide Proc Far
Mov Dx, Seg Error ; Initialize to program's Data Segment
Mov Ds, Dx
Mov Dx, Offset Error ; 'Error Divide'
Mov Ah, 9 ; DOS Function to Display a string
Int 21h ; Execute DOS Function
Mov Ah, 4ch
Int 21h ; Exit program execution
Error db 'Error Divide', 13, 10, '$'
Divide Endp
End Main
|
Question 9
What interrupt vector index is changed and to what?
What is the order of instruction execution for the following, starting at line 1?
|
|
Print Screen Interrupt Handler
As another example of an interrupt routine, the following is an interrupt handler for the Print Screen interrupt 5.
Its effect is to print Help! each time the Shift Prt Sc Keys are pressed. Again, the program works on unprotected versions of Windows such as 95, 98, and ME but not on protected operating systems such as NT, XP, Vista, etc.
.model small
.code
Main proc near
;______________________________________1
Mov Ax,0 ; Initialize interrupt
Mov Ds, Ax ; vector with PrintHelp
Mov Bx,5*4 ; function in place of
Cli ; regular Print Screen
Mov word ptr [Bx],offset PrintHelp
Mov word ptr [Bx]+2, Seg PrintHelp
Sti
;______________________________________2
Mov Cx, 65000 ; Execute some sequence
Do1: ; of control. Int 5 will
Mov Al, 'Z' ; transfer control to
Mov Ah, 14 ; PrintHelp function.
Mov Bx, 0
Int 10h
While1: Loop Do1
Mov Ah, 4ch
Int 21h
Main endp
;______________________________________3
PrintHelp proc Far ; Interrupt function
Push Ds ; required to save all
Push Si ; registers modified
Push Ax
Push Bx
Push Cx
Mov Ax, @data ; Print Help!
Mov Ds, Ax
Mov Si, Offset Help
Mov Cx, 7
Do2: Lodsb
Mov Ah, 14
Mov Bx, 0
Int 10h
While2: Loop Do2
Pop Cx
Pop Bx
Pop Ax
Pop Si
Pop Ds
;______________________________________4
Iret ; Interrupt Return
PrintHelp endp
.data
Help db 'Help!', 10, 13
End Main
|
Printer Polling Using BIOS Interrupts
Polling is the process where the CPU queries a device of its status prior to inputting/outputting data through that device. In the Polled Input/Output Example, the hardware status is input directly from the hardware using the IN instruction and data is output to the hardware using the OUT instruction. Using hardware directly, programs are dependent upon specific hardware configurations and are not generally portable to other systems. The BIOS is designed to provide a standard set of functions to access hardware so that programs can be more portable, the programmer uses BIOS interrupt numbers versus hardware addresses.
The following procedure, PrintChar, uses the BIOS printer interrupt to poll the printer's status prior to outputting a character. The printer status is continually tested by the CPU to determine that the printer is ready to accept more characters to print. The CPU waits until the status indicates that the printer is ready, then the CPU outputs a character to the printer. Should the printer never be ready (out of paper. etc.) the CPU is left busy waiting for the device to become ready, possibly an eternity if the status never indicates device readiness, a pitfall of polling. See INT 17h for details.
Title Screen to Printer
CR Equ 13
LF Equ 10
.model small
.code
;; void PrintChar(char Al) - Procedure to output to printer
;; Parameters - Al Character to output
;; Returns - Nothing
;;
PrintChar Proc Near
Push Dx
Push Ax
PushF
Mov Dx, 0 ;; Assume Printer 0
@@do: Mov Ah, 2 ;; do
Int 17h ;; cin >> PrinterStatus
Test Ah,10000000B ;; while PrinterBusy
@@while:Jnz @@do
Mov Ah, 0 ;; Print Al
Int 17h
PopF
Pop Ax
Pop Dx
Ret
PrintChar Endp
Main Proc near
Mov Dx, 0 ;; Initialize Printer 0
Mov Ah, 1 ;; Reset function
Int 17h
Mov Dh, 0 ;; Row
@@ForRow:
Cmp Dh, 24 ;; for (Dh = 0; Dh<=24;Dh++) {
Jle @@doForRow ;; for (Dl = 0; Dl<=79; Dl++) {
jmp @@EndForRow ;; Video.cin >> Al
@@doForRow:
Mov Dl, 0 ;; printer.cout << Al
@@ForColumn: ;; }
Cmp Dl, 79 ;; printer.cout << "\n";
jle @@doForColumn ;; }
Jmp @@EndForColumn
@@doForColumn:
Mov Bh, 0 ;; Video Page 0
Mov Ah, 2 ;; Set Cursor Position to (Dl, Dh)
Int 10h
Mov Ah, 8 ;; Read character at cursor
Int 10h
Call PrintChar ;; Print Al
Inc Dl
Jmp @@ForColumn
@@EndForColumn:
Mov Al, CR
Call PrintChar
Mov Al, LF
Call PrintChar
Inc Dh
Jmp @@ForRow
@@EndForRow:
Mov Ah, 4Ch
Int 21h
Main Endp
End Main
|
Direct Hardware Polled Input/Output
Because the CPU is generally far faster than input and output devices (printers, monitors, disks) it can overwhelm the devices ability to send or receive data. Two input/output methods common to synchronize the CPU with external devices are interrupt driven, where the device signals its readiness by generating an interrupt to the CPU, and polling where the CPU executes an IN instruction to query the device status. But interrupts occur asynchronous to the CPU execution of a program which can induce conflicting demands on the CPU time, making programming somewhat difficult. Because polling occurs within the flow of program execution, the CPU is only performing a single task at a time, so programming is much simplified.
The following uses the IN and OUT instructions to directly access the hardware that composes the printer status, data, and control registers. At the hardware level of programming, one must understand how hardware devices communicate with one another. The communication path between the CPU and printer is connected through the printer interface which consists of 3 registers for data, control, and status.
The general approach to the CPU to communicate a character to the printer is captured in the following algorithm:
Title Use of Input/Output instructions and POLLING for printer control
.data
Message db 'Hello world', 10, 13, '$'
.code ;________________ ______
; Output Data | 378h | Printer I/O
PrintChar Proc Near ; Output Control | 37Ah | Addresses
Push Dx ; Input Status | 379h |
Push Ax ;________________|______|
PushF
Push Ax
Mov Dx, 379h ; Do Input and Test Printer Status
@@do: In Al, Dx ; While Busy, Paper Out, Not Selected
And Al, 10111000B ; Bits 7, 5, 4, 3
Cmp Al, 10010000B ; Busy=1, Paper=0, Select=1, Error=0
@@While:Jne @@do
Pop Ax ; Get character from stack to print
Mov Dx, 378h
Out Dx, Al ; Print( Al );
Mov Dx, 37ah
Mov Al, 0dh ; Signal printer character
Out Dx, Al ; available to print (Strobe) by
Mov Al, 0ch ; sending pulse __ on Bit 0
Out Dx, Al ; ________________| |_____________
; Bit 0 =0 =1 =0
PopF
Pop Ax
Pop Dx
Ret
PutChar Endp
Main Proc Near
Mov Ax, @data
Mov Ds, Ax
Lea Si, Message
@@While:Lodsb
Cmp Al, '$' ; While (Message[Si] != '$') {
Jne @@do ; PutChar(Message[Si]);
Jmp @@endWhile ; Si := Si + 1
@@do:
Call PutChar
Jmp @@While
@@endWhile:
Mov Ah, 4ch
Int 21h
Main Endp
End Main
|
File Input/Output Using DOS Interrupts
BIOS supports low-level access to input/output devices while DOS supports higher-level notions such as a file system with names, directories and routines for their manipulation. The following is an example of using DOS file functions to copy or display a file to the screen. Note the close similarity between DOS file operations and those used in C++.
DOS Int 21h subfunctions
| Function 9 Display
to standard output string terminated by '$'
Parameters Ds:Dx = Segment:Offset of string Returns Nothing |
| Function 3D
Open file
Parameters Ds:Dx = Segment: Offset of 0 terminated string containing pathname Al = access code 0 - open for read 1 - open for write 2 - open for read/write Returns CF = 0 and Ax = file handle OR CF = 1 and Ax = error code |
| Function 3F
Read from file
Parameters Bx = file handle Cx = number of bytes to read Ds:Dx = Segment: Offset of memory area for data read from file Returns CF = 0 and Ax = number of bytes read OR CF = 1 and Ax = error code |
The following example copies the file named copyfile.asm to the console using DOS interrupts.
Title Display File to Screen
.model small
.data
Filename db 'copyfile.asm', 0 ;; Name of file to copy
Buffer db 81 dup(?) ;; Read and write buffer
.code
Main Proc near
Mov Ax, @data
Mov Ds, Ax
;______________________________________Bx = fopen(Filename, "r")________
Mov Dx, Offset Filename ;
Mov Al, 0 ;; Open for Reading
Mov Ah, 3Dh ;; DOS Open File Function
Int 21h ;; Execute DOS function
Mov Bx, Ax ;; Ax contains file handle
;_______________________________________________________________________
@@do: ;; do {
;_________________________________________fgetline( Bx, Buffer, 80 )____
Mov Cx, 80 ;; Number bytes Read from file
Mov Dx, Offset Buffer ;; Read 80 chars into Buffer
Mov Ah, 3Fh ;; DOS Function to Read file
Int 21h ;; Execute DOS function
;_______________________________________________________________________
;_________________________________________cout << Buffer _______________
Mov Si, Ax ;; Ax contains number chars read
Mov Buffer[Si], '$' ;; Terminate string with '$'
Mov Ah, 9 ;; DOS Function Display string
Mov Dx, Offset Buffer ;; Display chars from Buffer
Int 21h ;; Execute DOS Function
;_______________________________________________________________________
Cmp Si, 0 ;; Quit when zero characters read
@@while:JnZ @@do ;; } while !eof()
Mov Ah, 4ch ;; Terminate program
Int 21h
Main Endp
End Main
|
Most operating systems (Unix, DOS, NT, etc.) and many languages support standard input and output. The basic idea is that a program that reads standard input and writes standard output can read the keyboard or a file, write the screen or a file, without any changes to the program.
This is performed by the operating system through redirection. For example, the following C++ and Assembler programs reads until a 'Z' is entered, converting characters 'A'-'Z' to lowercase and outputting (note that other characters (e.g. '0'-'9') will be corrupted.
Suppose the name of either program executable is lowcase.exe. Once compiled or assembled, the programs can be executed several ways.
- lowcase - The input is from the keyboard and output to the screen.
- lowcase < a2z - The input is from the file a2z and output to the screen.
- lowcase > z2a - The input is from the keyboard and output to the file z2a.
- lowcase < a2z > z2a - The input is from the file a2z and output to the file z2a.
#include <iostream.h>
void main(void) {
char c;
do {
cin >> c;
c = c 0x20;
cout << c;
} while (c != 'z');
}
|
.code Main Proc near do: Mov Ah, 8 ; Read standard input Int 21H ; into Al Or Al, 20h Mov Dl, Al ; Write standard output Mov Ah, 2 Int 21H while: Cmp Al, 'z' Jne do Mov Ah, 4ch Int 21H Main Endp End Main |
Function Inputs Outputs AH 1 Input one character with echo Al = character 2 Output one character Dl=character *6 Input one character from Dl=FFh Zf=1 (when no input available) standard input device. Zf=0 and Al=input 7 Input one character no echo Al = character 8 Input one character, handle Ctrl-C Al = character 9 Print a '$' terminated Ds=Segment string. Dx=Offset * Windows behavior is NOT identical to previous DOS/Windows versions in some applications. |
For input, functions 1, 7, and 8 are the simplest to use, differing by whether input is echoed (1) or Ctrl-C is handled (1 and 8), or no echo and Ctrl-C is ignored (7).
When reading from the keyboard, one key point to note is that the user may not have typed anything when the program executes the read causing the program to block for input (functions 1, 7, or 8). For that reason, it is sometimes necessary to verify that input is available before the read.
The standard input function 6 returns the zero flag to indicate the status, 0 means that input was read and is in Al register, 1 means that input was not available.
The wait for input is generally called polling and is an example of a busy/wait loop where the computer is busy waiting. The loop consists of:
Mov Ah, 6 ; Ah=6 and Dl=FFh do2: Mov Dl, 0ffh ; for standard input Int 21H ; into Al while2: Jz do2 ; Wait for input |