Chapter 13
16-bit MSDOS
Programming

Modified
© Ray Wisman
Overview

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.

Discussion

Interrupts serve two main purposes on PC-systems

  1. to respond to hardware events such as a key press, and
  2. 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:

when executed on a PC under DOS.

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

4 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

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.

Memory Addressing

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:

  1. program algorithm or code segment - CS
  2. data segment - DS
  3. stack segment - SS
  4. extra segment - ES
The starting location of each segment in memory is determined by the value of the segment registers, Cs, Ds, Es, and Ss.
 
Memory Segments
       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. 

X offset = 0048h
DS = 1230h
Physical Address X = Ds*10h + offset X = 1230h*10h + 0048h = 12348h
Physical Memory Before
Mov X, 34h
20-bit address 8-bit Contents
12345 42
12346 00
12347 00
12348 X 00
12349 00
1234A 00
1234B 19
Physical Memory After
Mov X, 34h
20-bit address 8-bit Contents
12345 42
12346 00
12347 00
12348 X 74
12349 00
1234A 00
1234B 19
Data Segment starts at 12300h when DS=1230h
16-bit Offset 8-bit Contents
0045 42
0046 00
0047 00
0048 X 74
0049 00
004A 00
004B 19
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

 

Interrupt Mechanism

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:
 

Interrupt Sequence
Computer 
  1. Perform some 'normal' program task.
  2. Interrupts occurs.
  3. Save program state.
  4. Handle interruption.
  5. Restore program state.
  6. Continue with program .
People
  1. Perform work on C335 homework 9 assignment.
  2. Phone rings.
  3. Note what you are working on.
  4. Answer phone, blah, blah, blah, and hangup.
  5. Remember where to continue.
  6. Continue with homework 9.

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:

  1. A key press causes Int 9 (Interrupt 9) to be sent to the CPU. 
  2. The CPU saves the IP (return address of 1006) and flags on the the stack.
  3. The CPU sets IP=5763, the value of the interrupt vector at index 9.
  4. The interrupt handler is executed until IRET, which restores the return address (1006) and flags from the stack.
  5. The interrupted algorithm is continued.

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.
 
Detailed Execution of the Int 5 Instruction
 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  

Interrupt Vector Assignments

Chapter 13 and 15 of the text lists some PC architecture interrupt assignments.

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 :

The interrupt vector would need to contain the address of function PrtBk at index 3, the address of PrtOF at index 4, etc.

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

Question 4

What interrupt routine would be executed by:

Int 4

What physical address(es) should hold the segment and offset of INT 8?

 

Initializing the Interrupt Vector

The interrupt vector must be initialized before any interrupts can be handled. This normally is done during system boot but can be performed by user programs also. The following defines a function PrintHelp and initializes interrupt vector index entry 5 with the segment and offset of the PrintHelp function. As discussed above, when an Int 5 instruction is executed, the CPU will indirectly execute the PrintHelp function located at offset 002516(see below listing) and segment 432116.
 
Initialize Entry 5 with Address of PrintHelp Function
  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
 

Question 5a

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

 

Iret Interrupt Return

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)
Sp = Sp+2
Cs=(Sp)
Sp=Sp+2
Flags = (Sp)
Sp=Sp+2
Ret
Ip = (Sp)
Sp = Sp+2
Cs=(Sp)
Sp=Sp+2

 

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.

Notice in lines 16 through 19 of the code below, interrupts are disabled when the interrupt vector is manipulated. If an interrupt Int 5 occurred when the vector was only partially initialized the results would be unpredictable.
  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


13.1.4 INT Instruction

INT    number   

executes the interrupt handler for interrupt number.

Interrupt Vector holds the segment and offset.

Detailed Execution of the Int 5 Instruction
 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  

Question 5b

Draw the stack after execution of INT 5 instruction below.

SS=110016, SP=001016

INT 5 trace
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:

Function 9     Display to standard output string terminated by '$'
     Parameters
               Ah   9
               Ds   Segment of string
               Dx   Offset of string
     Returns
               Nothing
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.

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
Example of "Hello world" using DOS 
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
 

Question 6

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 Display 'Hello world'

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 function
To 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
Example of "Hello world" in BIOS
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
 

Question 7

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.
Overflow Interrupt Handler
Title    Initialize and Display Message on Overflow Interrupt

.model	small
.code
Main     Proc          near

;__1__________________________________Initialize Interrupt Vector_______
	Cli                       	;; Disable interrupts during vector  
	Mov      Ax, 0            	;; modifications.                    
	Mov      Ds, Ax           	;; Address segment 0 as Data Segment 
                                  	;;                                   
	Mov      Bx, 4 * 4        	;; Interrupt type * 4 vector address  
                                  	;; Initialize interrupt vector with   
                                  	;; value of CS:IP on type 4 interrupt 
	Mov      Word Ptr [Bx], Offset Overflow                       
	Mov      Word Ptr [Bx+2], Seg Overflow   
;__2___________________________Enable Interrupts________________________
	Sti                       	;; Ready to handle interrupts         |

;__3___________________________Generate Hardware Interrupt Type 4_______
	Mov	Al, -128						
	Sub	Al, 1							
	Into	
;__3___________________________Generate Software Interrupt Type 4_______
	Int	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?

 
  1. Main         Proc          near
  2.     Cli                      
  3.     Mov        Ax, 0            
  4.     Mov        Ds, Ax
  5.     Mov        Bx, 4 * 4        
  6.     Mov        Word Ptr [Bx], Offset Overflow
  7.     Mov        Word Ptr [Bx+2], Seg Overflow
  8.     Sti  
  9.     Mov        Al, -128
  10.     Sub        Al, 1
  11.     Into
  12.     Int        4
  13.     Mov        Ah, 4ch
  14.     Int        21h           
  15. Main       Endp
  1. Overflow Proc Far
  2.      Push     Ax
  3.      Push     Dx
  4.      Push     Ds
  5.      Mov      Dx, Seg Error
  6.      Mov      Ds, Dx 
  7.      Mov      Dx, Offset Error
  8.      Mov      Ah, 9
  9.      Int      21h  
  10.      Pop      Ds
  11.      Pop      Dx
  12.      Pop      Ax
  13.      IRet
  14.      Error db 'Error Overflow', 13, 10, '$'
  15. Overflow      Endp
  16.      End     Main

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   
	Mov      Ax, 0            ; modifications.                     
	Mov      Ds, Ax           ; Address segment 0 as Data Segment  
                                                                    
	Mov      Bx, 0 * 4        ; Interrupt type * 4 vector address  
                                ; Initialize interrupt vector with   
                                ; value of CS:IP on type 0 interrupt 
	Mov      Word Ptr [Bx], Offset Divide  
	Mov      Word Ptr [Bx+2], Seg Divide
;__2___________________________Enable Interrupts________________________
	  Sti                       
;__3___________________________Generate Hardware Interrupt Type 0_______
	  Mov		Cx, 0		
        Div		Cx		
	  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?

  1. Main    Proc     near
  2.     Cli
  3.     Mov      Ax, 0
  4.     Mov      Ds, Ax
  5.     Mov      Bx, 0 * 4 
  6.     Mov      Word Ptr [Bx], Offset Divide  
  7.     Mov      Word Ptr [Bx+2], Seg Divide
        Sti
  8.     Mov      Cx, 0
  9.     Div      Cx
  10.     Call     WriteDec
  11.     Mov      Ah, 4ch
  12.     Int      21h 
  13. Main       Endp
  1. Divide    Proc Far
  2.     Mov      Dx, Seg Error
  3.     Mov      Ds, Dx  
  4.     Mov      Dx, Offset Error
  5.     Mov      Ah, 9 
  6.     Int      21h
  7.     Mov      Ah, 4ch
  8.     Int      21h
  9.     Error    db  'Error Divide', 13, 10, '$'
  10. Divide     Endp
  11.   End       Main

 

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.

Print Screen Interrupt Handler
.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.

 
BIOS Printer Output - Int 17h

PRINTER - WRITE CHARACTER

AH = 00h
AL = character to write
DX = printer number (00h-02h)

Return:   AH = printer status

 

PRINTER - INITIALIZE PORT

AH = 01h
DX = printer number (00h-02h)

Return:   AH = printer status

 

PRINTER - GET STATUS

AH = 02h
DX = printer number (00h-02h)

Return:  AH = printer status

Bitfields for printer status:

Bit(s)  Description     
7      not busy when 1
6      acknowledge
5      out of paper when 1
4      selected when 1
3      I/O error when 1
2-1    unused
0      timeout

 
Printer Polling Using BIOS Interrupts
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:

  1. Input printer status register
  2. If printer status indicates not ready go to 1
  3. Output character to printer data register
  4. Output signal to printer control register that new data is available
  5. Go to 1
The purpose of Step 4 is to signal the printer that new data has been placed in the interface data register. To see the necessity of this step consider the scenario where AAAAAA characters were sent to the printer by each being output in turn to the data register. From the printer's point of view, the data register never changed, it remained the character A. Step 4 causes a strobe signal, meaning that the voltage on the connection (wire perhaps) between the CPU and printer changes from a logic 0 to 1 to 0. The printer detects this change or strobe to recognize that new data is available on the connection.
 
Direct Hardware Polled Input/Output
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.

File Input/Output 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
 

Standard Input and Output

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.

  1. lowcase - The input is from the keyboard and output to the screen.
  2. lowcase < a2z - The input is from the file a2z and output to the screen.
  3. lowcase > z2a - The input is from the keyboard and output to the file z2a.
  4. lowcase < a2z > z2a - The input is from the file a2z and output to the file z2a.
Convert to Lowercase with Standard Input and Output
#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
DOS Standard Input and Output Description
         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