Stdlib Miscellaneous Files


UCR StdLib: Miscellaneous Routines
11.1 - Interface
11.2 - ARGC and ARGV
11.2.1 - Calling Conventions and Assertions
11.2.2 - Syntax & Examples
11.3 - GetEnv
11.3.1 - Calling Conventions and Assertions
11.3.2 - Syntax & Examples
11.3.3 - Alternate Syntax
11.4 - DOS
11.4.1 - Calling Conventions and Assertions
11.4.2 - Syntax & Examples
11.5 - ExitPgm
11.5.1 - Calling Conventions and Assertions
11.5.2 - Syntax & Examples
11.6 - Random, Randomize
11.6.1 - Calling Conventions and Assertions
11.6.2 - Syntax & Examples
11.7 - CPUIdent
11.7.1 - Calling Conventions and Assertions
11.7.2 - Syntax & Examples


UCR StdLib: Miscellaneous Routines

The Miscellaneous package includes several one-of-a-kind and other routines that defy categorization.


11.1 Interface

To access the routines in the declarations package, your assembly language module must include the file "misc.a" during assembly. You can accomplish this with either of the following include statements in your assembly code:




	include	misc.a
or
	include	ucrlib.a

The 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.


11.2 ARGC and ARGV

Like their C/C++ counterparts, these two functions provide access to the DOS command line parameters. Argc returns the number of command line parameters in the CX register. Argv expects a parameter number in AX (1..n, when there are n parameters), it returns a pointer to the specified command line parameter in ES:DI.

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
                 .
                 .
                 .

11.2.1 Calling Conventions and Assertions


11.2.2 Syntax & Examples

Argc and Argv require no operand field parameters. The following (complete) program completes the example found in the earlier code snippet.




                .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

11.3 GetEnv

GetEnv returns specific environment strings from the DOS environment area. The environment area is a buffer in memory where users can store often-used parameter information. Examples include the directory search path (PATH), the include file path (INCLUDE), and other variables you initialize at the DOS command line prompt with the SET command (type SET by itself at the DOS command line prompt to see some examples).

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.


11.3.1 Calling Conventions and Assertions


11.3.2 Syntax & Examples

The following example assumes there are three environment variables ("PATH", "LIB", and "INCLUDE") defined in the global environment space. It also assumes that there is no environment variable named "GARBAGE" present in the system.




                .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

11.3.3 Alternate Syntax

GetEnv provides an alternate syntax where you specify an operand field parameter. Without a parameter, GetEnv simply calls the GetEnv routine. With a parameter, GetEnv calls one of the following routines:




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"



11.4 DOS

DOS is a macro provided by the stdlib package that emits an INT 21h instruction (call DOS service). Without parameters, it simply emits this instruction. If you supply a single parameter, it copies that parameter's value into the AH register (the DOS function number) before calling INT 21h.


11.4.1 Calling Conventions and Assertions


11.4.2 Syntax & Examples

The following examples show the two forms of the DOS function- one version without parameters and a couple versions that use the operand field parameter to load AH:




	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.)

11.5 ExitPgm

This is a simple macro that emits the int 21h AH=4Ch call (terminate program execution). It returns control to MS-DOS.

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.


11.5.1 Calling Conventions and Assertions


11.5.2 Syntax & Examples

ExitPgm does not require any parameters:




	exitpgm

11.6 Random, Randomize

The Random function computes a 16-bit pseudo-random number on each call and returns this value in the AX register. If you are treating this value as an unsigned value, this corresponds to a value in the range 0..65535; for signed values, -32768..+32767.

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.


11.6.1 Calling Conventions and Assertions


11.6.2 Syntax & Examples

Random and Randomize do not require any operand field parameters. The following example demonstrates how to call each of them:




	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

11.7 CPUIdent

The CPUIdent function identifies the microprocessor. Currently it will identify 8086, 80286, 80386, 80486, and Pentium processors. For those devices beyond the 80486, this program is only known to work with Intel processors; results are not guaranteed on other manufacturers' parts.

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).


11.7.1 Calling Conventions and Assertions


11.7.2 Syntax & Examples

Programs that use the Standard Library must have an 80386 or later CPU since many of the library routines use 386 code. You can use the CPUIdent function to determine if the program is actually running on a 386 or later processor using code like the following:




	cpuident
	cmp	ax, 386
	jae	GoodCPU
	<print a message telling the user why this program is quitting>
	ExitPgm

GoodCPU: