[Next] [Art
of Assembly][Randall Hyde]
Art of Assembly Language: Chapter Nineteen
- Chapter 19 - Processes, Coroutines, and
Concurrency
- 19.1 - DOS Processes
- 19.1.1 - Child Processes in DOS
- 19.1.1.1 - Load and Execute
- 19.1.1.2 - Load Program
- 19.1.1.3 - Loading Overlays
- 19.1.1.4 - Terminating a Process
- 19.1.1.5 - Obtaining the Child Process
Return Code
- 19.1.2 - Exception Handling
in DOS: The Break Handler
- 19.1.3 - Exception Handling
in DOS: The Critical Error Handler
- 19.1.4 - Exception Handling
in DOS: Traps
- 19.1.5 - Redirection of I/O
for Child Processes
- 19.2 - Shared Memory
- 19.2.1 - Static Shared Memory
- 19.2.2 - Dynamic Shared Memory
- 19.3 - Coroutines
- 19.3.1 - AMAZE.ASM
- 19.3.2 - 32-bit Coroutines
- 19.4 - Multitasking
- 19.4.1 - Lightweight and HeavyWeight
Processes
- 19.4.2 - The UCR Standard
Library Processes Package
- 19.4.3 - Problems with Multitasking
- 19.4.4 - A Sample Program
with Threads
- 19.5 - Synchronization
- 19.5.1 - Atomic Operations,
Test & Set, and Busy-Waiting
- 19.5.2 - Semaphores
- 19.5.3 - The UCR Standard
Library Semaphore Support
- 19.5.4 - Using Semaphores
to Protect Critical Regions
- 19.5.5 - Using Semaphores
for Barrier Synchronization
- 19.6 - Deadlock
Copyright 1996 by Randall Hyde
All rights reserved.
Duplication other than for immediate display through a browser is prohibited
by U.S. Copyright Law.
This material is provided on-line as a beta-test of this text. It is for
the personal use of the reader only. If you are interested in using this
material as part of a course, please contact
rhyde@cs.ucr.edu
Supporting software and other materials are available via anonymous ftp
from ftp.cs.ucr.edu. See the "/pub/pc/ibmpcdir" directory for
details. You may also download the material from "Randall Hyde's Assembly
Language Page" at URL:
http://webster.ucr.edu
Notes:
This document does not contain the laboratory exercises, programming assignments,
exercises, or chapter summary. These portions were omitted for several reasons:
either they wouldn't format properly, they contained hyperlinks that were
too much work to resolve, they were under constant revision, or they were
not included for security reasons. Such omission should have very little
impact on the reader interested in learning this material or evaluating
this document.
This document was prepared using Harlequin's Web Maker 2.2 and Quadralay's
Webworks Publisher. Since HTML does not support the rich formatting options
available in Framemaker, this document is only an approximation of the actual
chapter from the textbook.
If you are absolutely dying to get your hands on a version other than HTML,
you might consider having the UCR Printing a Reprographics Department run
you off a copy on their Xerox machines. For details, please read the following
EMAIL message I received from the Printing and Reprographics Department:
Hello Again Professor Hyde,
Dallas gave me permission to take orders for the Computer Science 13 Manuals.
We would need to take charge card orders. The only cards we take are: Master
Card, Visa, and Discover. They would need to send the name, numbers, expiration
date, type of card, and authorization to charge $95.00 for the manual and
shipping, also we should have their phone number in case the company has
any trouble delivery. They can use my e-mail address for the orders and
I will process them as soon as possible. I would assume that two weeks would
be sufficient for printing, packages and delivery time.
I am open to suggestions if you can think of any to make this as easy as
possible.
Thank You for your business,
Kathy Chapman, Assistant
Printing and Reprographics
University of California
Riverside
(909) 787-4443/4444
We are currently working on ways to publish this text in a form other than
HTML (e.g., Postscript, PDF, Frameviewer, hard copy, etc.). This, however,
is a low-priority project. Please do not contact Randall Hyde concerning
this effort. When something happens, an announcement will appear on "Randall
Hyde's Assembly Language Page." Please visit this WEB site at http://webster.ucr.edu
for the latest scoop.
Art of Assembly Bug Report Submissions
Did you find an error in The Art of Assembly Language Programming?
You can let me know by using the form below to report the error to me so
that I can correct the error for the next beta version. Thank you.
The Submission Form
Please provide your name and e-mail address so I can contact you if
I have any questions regarding your submission.
Chapter 19 Processes, Coroutines, and Concurrency
When most people speak of multitasking, they usually mean the ability
to run several different application programs concurrently on one machine.
Given the structure of the original 80x86 chips and MS-DOS' software design,
this is very difficult to achieve when running DOS. Look at how long it's
taken Microsoft to get Windows to multitask as well as it does.
Given the problems large companies like Microsoft have had trying to get
multitasking to work, you might thing that it is a very difficult thing
to manage. However, this isn't true. Microsoft has problems trying to make
different applications that are unaware of one another work harmoniously
together. Quite frankly, they have not succeeded in getting existing DOS
applications to multitask well. Instead, they've been working on developers
to write new programs that work well under Windows.
Multitasking is not trivial, but it is not that difficult when you write
an application with multitasking specifically in mind. You can even write
programs that multitask under DOS if you only take a few precautions. In
this chapter, we will discuss the concept of a DOS process, a coroutine,
and a general process.
19.1 DOS Processes
Although MS-DOS is a single tasking operating system, this does not
mean there can only be one program at a time in memory. Indeed, the whole
purpose of the previous chapter was to describe how to get two or more programs
operating in memory at one time. However, even if we ignore TSRs for the
time being, you can still load several programs into memory at one time
under DOS. The only catch is, DOS only provides the ability for them to
run one at a time in a very specific fashion. Unless the processes are cooperating,
their execution profile follows a very strict pattern.
19.1.1 Child Processes in DOS
When a DOS application is running, it can load and executing some other
program using the DOS EXEC function. Under normal circumstances, when an
application (the parent) runs a second program (the child), the child process
executes to completion and then returns to the parent. This is very much
like a procedure call, except it is a little more difficult to pass parameters
between the two.
MS-DOS provides several functions you can use to load and execute program
code, terminate processes, and obtain the exit status for a process. The
following table lists many of these operations.
DOS Character Oriented Functions Function
#
(AH) | Input
Parameters | Output
Parameters | Description |
---|
4Bh | al - 0
ds:dx - pointer to program name.
es:bx - pointer to LOADEXEC structure. | ax - error
code if carry set. | Load and execute program |
4Bh | al -
1
ds:dx - pointer to program name.
es:bx - pointer to LOAD structure. | ax - error
code if carry set. | Load program |
4Bh | al - 3
ds:dx - pointer to program name.
es:bx - pointer to OVERLAY structure. | ax - error
code if carry set. | Load overlay |
4Ch | al - process
return code | | Terminate execution |
4Dh | | al -
return value
ah - termination method. | Get child process return value |
19.1.1.1 Load and Execute
The "load and execute" call requires two parameters. The first,
in ds:dx, is a pointer to a zero terminated string containing the pathname
of the program to execute. This must be a ".COM" or ".EXE"
file and the string must contain the program name's extension. The second
parameter, in es:bx
, is a pointer to a LOADEXEC data structure.
This data structure takes the following form:
LOADEXEC struct
EnvPtr word ? ;Pointer to environment area
CmdLinePtr dword ? ;Pointer to command line
FCB1 dword ? ;Pointer to default FCB1
FCB2 dword ? ;Pointer to default FCB2
LOADEXEC ends
Envptr
is the segment address of the DOS environment block
created for the new application. If this field contains a zero, DOS creates
a copy of the current process' environment block for the child process.
If the program you are running does not access the environment block, you
can save several hundred bytes to a few kilobytes by pointing the environment
pointer field to a string of four zeros.
The CmdLinePtr
field contains the address of the command line
to supply to the program. DOS will copy this command line to offset 80h
in the new PSP it creates for the child process. A valid command line consists
of a byte containing a character count, a least one space, any character
belonging to the command line, and a terminating carriage return character
(0Dh). The first byte should contain the length of the ASCII characters
in the command line, not including the carriage return. If this byte contains
zero, then the second byte of the command line should be the carriage return,
not a space. Example:
MyCmdLine byte 12, " file1 file2",cr
The FCB1
and FCB2
fields need to point at the
two default file control blocks for this program. FCBs became obsolete with
DOS 2.0, but Microsoft has kept FCBs around for compatibility anyway. For
most programs you can point both of these fields at the following string
of bytes:
DfltFCB byte 3," ",0,0,0,0,0
The load and execute call will fail if there is insufficient memory to load
the child process. When you create an ".EXE" file using MASM,
it creates an executable file that grabs all available memory, by default.
Therefore, there will be no memory available for the child process and DOS
will always return an error. Therefore, you must readjust the memory allocation
for the parent process before attempting to run the child process.
There are other possible errors as well. For example, DOS might not be able
to locate the program name you specify with the zero terminated string.
Or, perhaps, there are too many open files and DOS doesn't have a free buffer
available for the file I/O. If an error occurs, DOS returns with the carry
flag set and an appropriate error code in the ax
register.
The following example program executes the "COMMAND.COM" program,
allowing a user to execute DOS commands from inside your application. When
the user types "exit" at the DOS command line, DOS returns control
to your program.
; RUNDOS.ASM - Demonstrates how to invoke a copy of the COMMAND.COM
; DOS command line interpreter from your programs.
include stdlib.a
includelib stdlib.lib
dseg segment para public 'data'
; MS-DOS EXEC structure.
ExecStruct word 0 ;Use parent's Environment blk.
dword CmdLine ;For the cmd ln parms.
dword DfltFCB
dword DfltFCB
DfltFCB byte 3," ",0,0,0,0,0
CmdLine byte 0, 0dh ;Cmd line for program.
PgmName dword filename ;Points at pgm name.
filename byte "c:\command.com",0
dseg ends
cseg segment para public 'code'
assume cs:cseg, ds:dseg
Main proc
mov ax, dseg ;Get ptr to vars segment
mov ds, ax
MemInit ;Start the memory mgr.
; Okay, we've built the MS-DOS execute structure and the necessary
; command line, now let's see about running the program.
; The first step is to free up all the memory that this program
; isn't using. That would be everything from zzzzzzseg on.
;
; Note: unlike some previous examples in other chapters, it is okay
; to call Standard Library routines in this program after freeing
; up memory. The difference here is that the Standard Library
; routines are loaded early in memory and we haven't free up the
; storage they are sitting in.
mov ah, 62h ;Get our PSP value
int 21h
mov es, bx
mov ax, zzzzzzseg ;Compute size of
sub ax, bx ; resident run code.
mov bx, ax
mov ah, 4ah ;Release unused memory.
int 21h
; Tell the user what is going on:
print
byte cr,lf
byte "RUNDOS- Executing a copy of command.com",cr,lf
byte "Type 'EXIT' to return control to RUN.ASM",cr,lf
byte 0
; Warning! No Standard Library calls after this point. We've just
; released the memory that they're sitting in. So the program load
; we're about to do will wipe out the Standard Library code.
mov bx, seg ExecStruct
mov es, bx
mov bx, offset ExecStruct ;Ptr to program record.
lds dx, PgmName
mov ax, 4b00h ;Exec pgm
int 21h
; In MS-DOS 6.0 the following code isn't required. But in various older
; versions of MS-DOS, the stack is messed up at this point. Just to be
; safe, let's reset the stack pointer to a decent place in memory.
;
; Note that this code preserves the carry flag and the value in the
; AX register so we can test for a DOS error condition when we are done
; fixing the stack.
mov bx, sseg
mov ss, ax
mov sp, offset EndStk
mov bx, seg dseg
mov ds, bx
; Test for a DOS error:
jnc GoodCommand
print
byte "DOS error #",0
puti
print
byte " while attempting to run COMMAND.COM",cr,lf
byte 0
jmp Quit
; Print a welcome back message.
GoodCommand: print
byte "Welcome back to RUNDOS. Hope you had fun.",cr,lf
byte "Now returning to MS-DOS' version of COMMAND.COM."
byte cr,lf,lf,0
; Return control to MS-DOS
Quit: ExitPgm
Main endp
cseg ends
sseg segment para stack 'stack'
dw 128 dup (0)
sseg ends
zzzzzzseg segment para public 'zzzzzzseg'
Heap db 200h dup (?)
zzzzzzseg ends
end Main
19.1.1.2 Load Program
The load and execute function gives the parent process very little control
over the child process. Unless the child communicates with the parent process
via a trap or interrupt, DOS suspends the parent process until the child
terminates. In many cases the parent program may want to load the application
code and then execute some additional operations before the child process
takes over. Semiresident programs, appearing in the previous chapter, provide
a good example. The DOS "load program" function provides this
capability; it will load a program from the disk and return control back
to the parent process. The parent process can do whatever it feels is appropriate
before passing control to the child process.
The load program call requires parameters that are very similar to the load
and execute call. Indeed, the only difference is the use of the LOAD structure
rather than the LOADEXEC structure, and even these structures are very similar
to one another. The LOAD data structure includes two extra fields not present
in the LOADEXE structure:
LOAD struct
EnvPtr word ? ;Pointer to environment area.
CmdLinePtr dword ? ;Pointer to command line.
FCB1 dword ? ;Pointer to default FCB1.
FCB2 dword ? ;Pointer to default FCB2.
SSSP dword ? ;SS:SP value for child process.
CSIP dword ? ;Initial program starting point.
LOAD ends
The LOAD command is useful for many purposes. Of course, this function provides
the primary vehicle for creating semiresident programs; however, it is also
quite useful for providing extra error recovery, redirecting application
I/O, and loading several executable processes into memory for concurrent
execution.
After you load a program using the DOS load command, you can obtain the
PSP address for that program by issuing the DOS get PSP address call. This
would allow the parent process to modify any values appearing in the child
process' PSP prior to its execution. DOS stores the termination address
for a procedure in the PSP. This termination address normally appears in
the double word at offset 10h in the PSP. If you do not change this location,
the program will return to the first instruction beyond the int 21h instruction
for the load function. Therefore, before actually transferring control to
the user application, you should change this termination address.
19.1.1.3 Loading Overlays
Many programs contain blocks of code that are independent of one other;
that is, while routines in one block of code execute, the program will not
call routines in the other independent blocks of code. For example, a modern
game may contain some initialization code, a "staging area" where
the user chooses certain options, an "action area" where the user
plays the game, and a "debriefing area" that goes over the player's
actions. When running in a 640K MS-DOS machine, all this code may not fit
into available memory at the same time. To overcome this memory limitation,
most large programs use overlays. An overlay is a portion of the program
code that shares memory for its code with other code modules. The DOS load
overlay function provides support for large programs that need to use overlays.
Like the load and load/execute functions, the load overlay expects a pointer
to the code file's pathname in the ds:dx
register pair and
the address of a data structure in the es:bx
register pair.
This overlay data structure has the following format:
overlay struct
StartSeg word ?
RelocFactor word 0
overlay ends
The StartSeg
field contains the segment address where you want
DOS to load the program. The RelocFactor
field contains a relocation
factor. This value should be zero unless you want the starting offset of
the segment to be something other than zero.
19.1.1.4 Terminating a Process
The process termination function is nothing new to you by now, you've
used this function over and over again already if you written any assembly
language programs and run them under DOS (the Standard Library ExitPgm
macro executes this command). In this section we'll look at exactly what
the terminate process function call does.
First of all, the terminate process function gives you the ability to pass
a single byte termination code back to the parent process. Whatever value
you pass in al to the terminate call becomes the return, or termination
code. The parent process can test this value using the Get Child Process
Return Value call (see the next section). You can also test this return
value in a DOS batch file using the "if errorlevel" statement.
The terminate process command does the following:
Unless you really know what you're doing, you should not change the values
at offsets 0Ah, 0Eh, or 12h in the PSP. By doing so you could produce an
inconsistent system when your program terminates.
19.1.1.5 Obtaining the Child Process Return Code
A parent process can obtain the return code from a child process by
making the DOS Get Child Process Return Code function call. This call returns
the value in the al
register at the point of termination plus
information that tells you how the child process terminated.
This call (ah
=4Dh) returns the termination code in the al register.
It also returns the cause of termination in the ah register. The ah
register will contain one of the following values:
Termination Cause Value in AH | Reason
for Termination |
---|
0 | Normal termination (int 21h, ah=4Ch) |
1 | Terminated
by ctrl-C |
2 | Terminated by critical error |
3 | TSR termination
(int 21h, ah=31h) |
The termination code appearing in al
is valid only for normal
and TSR terminations.
Note that you can only call this routine once after a child process terminates.
MS-DOS returns meaningless values in AX after the first such call. Likewise,
if you use this function without running a child process, the results you
obtain will be meaningless. DOS does not return if you do this.
- 19.1 - DOS Processes
- 19.1.1 - Child Processes in DOS
- 19.1.1.1 - Load and Execute
- 19.1.1.2 - Load Program
- 19.1.1.3 - Loading Overlays
- 19.1.1.4 - Terminating a Process
- 19.1.1.5 - Obtaining the Child Process
Return Code
- 19.1.2 - Exception Handling
in DOS: The Break Handler
- 19.1.3 - Exception Handling
in DOS: The Critical Error Handler
- 19.1.4 - Exception Handling
in DOS: Traps
- 19.1.5 - Redirection of I/O
for Child Processes
- 19.2 - Shared Memory
- 19.2.1 - Static Shared Memory
- 19.2.2 - Dynamic Shared Memory
- 19.3 - Coroutines
- 19.3.1 - AMAZE.ASM
- 19.3.2 - 32-bit Coroutines
- 19.4 - Multitasking
- 19.4.1 - Lightweight and HeavyWeight
Processes
- 19.4.2 - The UCR Standard
Library Processes Package
- 19.4.3 - Problems with Multitasking
- 19.4.4 - A Sample Program
with Threads
- 19.5 - Synchronization
- 19.5.1 - Atomic Operations,
Test & Set, and Busy-Waiting
- 19.5.2 - Semaphores
- 19.5.3 - The UCR Standard
Library Semaphore Support
- 19.5.4 - Using Semaphores
to Protect Critical Regions
- 19.5.5 - Using Semaphores
for Barrier Synchronization
- 19.6 - Deadlock
Art of Assembly: Chapter Nineteen - 29 SEP 1996
[Next] [Art of Assembly][Randall
Hyde]