include misc.a or include ucrlib.aThe misc.a include file exports several symbols. The UCR Standard Library prefaces all "private" names with a dollar sign ("$"). You should not call any routine in this package that begins with this symbol unless otherwise advised. To avoid name conflicts, you should not define any symbols in your programs that begin with a dollar sign ("$"). Note that future versions of the stdlib (that remain compatible with this release) may change "private" names. To remain compatible with future releases, you must not refer to these "private" names within your programs.
Source code appearing in this chapter is current as of Version Two, Release 40. There may be minor changes between this source code and the current release.
Argv and Argc generally use whitespace to separate parameters. However, if you enclose a parameter within quotation marks or apostrophes, these routines treat that string as a single command line parameter.
As a general rule, you should always ensure that the value you pass to Argv is between one and the value returned by Argc. If you specify a value outside this range, the Argv function will raise a $RangeErr exception (assuming exceptions are active). If exceptions are not active, Argv returns with the carry flag set if there was an error.
Argv allocates storage on the heap for the string it returns. Therefore, any error that malloc can return, Argv could also return. Note that if you call Argv twice with the same parameter number, Argv will allocate two separate strings on the heap. If you want a single array of pointers to command line parameters, you can easily generate the array using code like the following:
maxArgs = 50 wp textequ <word ptr> var string *args[maxArgs] integer ArgCnt endvar . . . mov bx, 0 mov ax, bx argc jcxz NoArgs argLp: inc ax argv mov wp args[bx], di mov wp args[bx+2], es add bx, 4 loop argLp NoArgs: mov ArgCnt, ax . . .
.xlist include ucrlib.a includelib ucrlib.lib .listall maxArgs = 50 wp textequ <word ptr> var string *args[maxArgs] integer ArgCnt endvar cseg segment para public 'code' assume cs:cseg, ds:dseg Main proc mov ax, dseg mov ds, ax mov es, ax MemInit InitExcept EnableExcept mov ax, 0 mov bx, ax argc jcxz NoParms CXLP: inc ax argv mov wp args[bx], di mov wp args[bx+2], es add bx, 4 loop CXLP NoParms: mov ArgCnt, ax mov bx, 0 mov ax, 1 mov cx, ArgCnt jcxz Done Lp2: print "Parameter " puti print " = " les di, args[bx] puts putcr add bx, 4 inc ax loop Lp2 Done: CleanUpEx ExitPgm ;DOS macro to quit program. Main endp cseg ends sseg segment para stack 'stack' stk db 16384 dup (?) sseg ends zzzzzzseg segment para public 'zzzzzz' LastBytes db 16 dup (?) zzzzzzseg ends end Main
GetEnv expects a pointer to a string in the ES:DI register pair. This string should contain the name of a DOS environment variable. GetEnv will search the the programs' environment space for a variable that begins with this name. If it finds the variable, it will return the string immediately following the variable's name in the environment space. This string begins with an equal sign ("=") and is followed by whatever data appears in the environment space.
Note that GetEnv makes a copy of the string in the environment space and places this copy on the heap. Therefore, you can make modifications to the string that GetEnv returns without fear of modifying the original environment variable. Since GetEnv allocates storage for this new string on the heap, you should call FREE to return this storage to the free list when you are done using it.
.xlist include ucrlib.a includelib ucrlib.lib .listall dseg segment para public 'dseg' path byte "PATH",0 incl byte "INCLUDE",0 lib byte "lib",0 garbage byte "garbage",0 dseg ends cseg segment para public 'code' assume cs:cseg, ds:dseg Main proc mov ax, dseg mov ds, ax mov es, ax MemInit lesi incl getenv print "Include " puts free putcr lesi path getenv print "Path " puts free putcr lesi lib getenv print "lib " puts free putcr lesi garbage getenv jnc FoundIt print "Could not find garbage" jmp GDone FoundIt: print "garbage " puts free putcr GDone: ExitPgm ;DOS macro to quit program. Main endp cseg ends sseg segment para stack 'stack' stk db 16384 dup (?) sseg ends zzzzzzseg segment para public 'zzzzzz' LastBytes db 16 dup (?) zzzzzzseg ends end Main
Path byte "path",0 npPath word Path fpPath dword Path . . . lesi Path GetEnv ;Left as-is ; The following generates: ; push seg path ; push offset path ; GetEnvStk GetEnv Path ; The following generates: ; push DS ; push npPath ; GetEnvStk GetEnv npPath ; The following generates: ; pushd fpPath ; GetEnvStk GetEnv fpPath ; The following generates: ; GetEnvL ; byte "path" ; byte 0 GetEnv "path"
mov ah, 2ah ;Read system DATE DOS . ;Date is now in AL, CX, and DX . ; (see an appropriate DOS reference manual . ; for details). . DOS 2Ch ;Read system time. (Loads AH with 2Ch.) . . . DOS ByteVar ;Executes DOS function whose code is in ByteVar. . ; (Loads AH with Bytevar before call.)
Warning: if you've initialized exceptions via the InitExcept call, you must be sure to call CleanUpEx before your program terminates. ExitPgm does not do this for you. Failure to observe this rule may cause the system to hang sometime after your program quits.
exitpgm
Although each call to Random generates a (seemingly) random number, the unaided Random function always generates the same sequence of pseudo random numbers. That is, the first time you call Random in any given application, it will always return the same value. Likewise, the second time you call Random always returns the same value (that is, the second call to Random in any given program or execution of a program, always returns the same value). Some naive users may think this is terrible- after all, there's nothing Random about the same sequence coming through on any given execution of a program. There is, however, a big advantage to always generating the same sequence- your programs are repeatable. While this might seem like an undesirable trait for a game or other Monte Carlo simulation, imagine how hard it would be to debug a program that was truly non-deterministic.
Recognizing that some programs (e.g., games) would be less playable if they always generated the same sequence, the stdlib package provides a "Randomize" routine. The Randomize routine reads the current value from the time of day clock and then scrambles the Random's internal "seed" table using the current time. By changing the seed values, you can ensure that the Random function will generate a different sequence on each execution of your program.
Note that you should never call the Randomize routine more than once in your program. If you read a sequence of values from the time of day clock, they are not random (e.g., they are usually ascending values). Repeated calls to Randomize will actually make the Random function generate less random values. The only purpose for calling Randomize is to change the sequence of numbers that Random generates; it does not, in any way, improve the "randomness" of the sequence. Once Random generates a different sequence, there is no need to change the sequence again during program execution since Random is a fairly high quality pseudo-random number generator. Once again, repeated calls to Randomize will only hurt the quality of the sequence that Random produces.
randomize ;Only once per program! random ;Get our first random number.Okay, you don't like the way Randomize scrambles the random number generator seeds? You can always do something like the following. This code continuously calls Random until the user presses a key (a fairly random event given computer speeds). This slides the Random function along into the sequence of values it generates.
; Flush the keyboard buffer. jmp IntoLp Lp: getcBIOS IntoLp: tstKbd jne LP print "Randomizing... Press any key to continue:" RandomizeLp: random tstKbd jz RandomizeLp . . .
Here are some additional random number functions you may be interested in.
; Need a 32-bit random number? ; Here's one: Random32 proc Random shl eax, 16 Random ret Random32 endp ; Need a random number generate that generates ; values in the range 0..n? Here's one that ; returns a random value in the range 0..AX. ; Assertion: AX <> 0. RngRandom proc push dx push bx mov bx, ax inc bx random xor dx, dx div bx mov ax, dx ;Return remainder. pop bx pop dx ret RngRandom endp
CPUIdent returns one of the following values in AX:
86- If a stock 8086 or 8088 microprocessor
286- If an 80286 microprocessor.
386- If an 80386 microprocessor.
486- If an 80486 or other post-386 CPU that does not have a CPUID instruction.
586- For Intel Pentium processors.
CPUIdent also returns one of the following values in BX:
87- If an 8087 FPU is available.
287- If an 80287 FPU is available.
387- If an 80387 FPU is available.
487- If an 80486 CPU or 80486SX/80487 combo is available.
587- If the user has a Pentium microprocessor (FPU is assumed present).
cpuident cmp ax, 386 jae GoodCPU <print a message telling the user why this program is quitting> ExitPgm GoodCPU: