Document last modified:
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 PutDec, etc. Directly calling the function by name breaks down when a different version were 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.

1) User program: cout << "Hello world"; 2) C++ : Call DOS video function 9 to output string "Hello world" Page 464 of text 3) DOS: Call BIOS video function by Int 10h Page 448-450 of text 4) BIOS: "Hello world" placed in hardware video memory 5) Video hardware: "Hello world" display from video memory |
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. |
Computer
|
People
|
There are two origins of interrupts 1) generated by the PC hardware in response to some event such as division by zero, 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 |
Index Interrupt Physical Vector Address 3 | 1234 |4*3 =0000C16 PrtBk__| 9999 |4*3+2=0000E16 4 | 9ABC |4*4 =0001016 PrtOF__| 9999 |4*4+2=0001216 5 | 0025 |4*5 =0001416 PrtSc | 9999 |4*5+2=0001616 6 | |4*6 =0001816 _______| |4*6+2=0001A16 |
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 |
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 |
Iret
Ip = (Sp) |
Ret
Ip = (Sp) |
3) DOS: Int 21h, function 9 - "Hello World" passed to BIOS video function 10h 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 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 (text page 464 or 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 NothingThe general use of this and most DOS operations is: to place the number of the function in the Ah register and other registers with parameters. 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
; v:\common\user\c335\assembler
; ml helloworld.asm
; helloworld
.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 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
|
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. The text definition (page 450) of the 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 is: 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
; v:\common\user\c335\assembler
; ml helloworld.asm
; helloworld
.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
|
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 ; v:\common\user\c335\assembler ; ml overflow.asm ; overflow .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 1-11, 16-28, 12, ...
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. 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 ; v:\common\user\c335\assembler ; ml zerodiv.asm /link io.lib ; zerodiv .model small .code PutDec 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 PutDec ; 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
|
Interrupt Execution - What is the order of instruction execution for the following?
|
|
; v:\common\user\c335\assembler
; ml printscreen.asm
; printscreen
; Shift Print Screen
.model small
.code
Main proc near
;______________________________________1
Mov Ax,0 ; Initialize interrupt
Mov Ds, Ax ; vector with PrintHelp
Mov Bx,16h*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
|
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 page 460, Table 9.6 and 9.7 (or INT 17h) for details.
Title Screen to Printer
; v:\common\user\c335\assembler
; ml printscreen.asm
; printscreen
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
|
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. For details, see text page 460, Table 9.7 and page 466 Direct Input/Output.
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
; v:\common\user\c335\assembler
; ml poll.asm
; poll
.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
|
DOS Int 21h subfunctions (page 463 of text)
| 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 |
Title Display File to Screen
; v:\common\user\c335\assembler
; ml copyfile.asm
; copyfile
.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
Int 21h
Main Endp
End Main
|
Suppose the name of either program executable is lowcase.exe. Once compiled or assembled, the programs can be executed several ways.
#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 2000 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 |
Document last modified: