TITLE 16-bit XOR shift method PAGE 62, 132 ; ========================================================================== ; File: random3.asm ; Author: jonk ; ; Creation Date: Wed 20-Feb-2013 10:52:56 ; Last Modified: Wed 20-Feb-2013 10:52:56 ; ========================================================================== ; ; ; DESCRIPTION ; ; This file provides a short test of some specific code for the 16-bit xor ; shift method of generating random numbers. The web site for this comes ; from the following link: ; ; http://b2d-f9r.blogspot.com/2010/08/16-bit-xorshift-rng-now-with-more.html ; ; ; MEMORY ALLOCATION ; ; DOS 1.0 doesn't support memory management functions and it doesn't ; support .EXE programs. When DOS 1.0 loads a .COM program, it simply ; places the (last+1) paragraph of what DOS 1.0 believes is the end of ; the RAM memory into the pspLastPara entry of the PSP and then runs ; the loaded .COM program. In essence, this means that the .COM program ; owns all of the available memory up to that point and can use it as it ; sees fit. ; ; DOS 2.0 and beyond introduces a variety of changes, including the new ; .EXE file type and memory allocation functions. Because of these ; features and others, the DOS .EXE doesn't normally allocate all of the ; available memory to an .EXE program. In the case of .COM programs, ; these later versions of DOS allocate the largest memory block to the ; .COM program, in order to remain roughly compatible with DOS 1.0. ; ; This program could take steps to return memory that isn't needed by the ; program itself (and it's stack) to DOS and then use the DOS memory ; management functions to allocate memory for maze structures. But there ; is one important reason why this isn't done -- compatibility with first ; version of DOS, which doesn't support these functions. ; ; So, it builds on the natural organization of a COM program by simply ; adjusting the stack closer to the principal code/data area and then ; uses the remainder of the memory for the maze's data structures. And ; it uses pspLastPara to detect if these structures require more than is ; available. ; ; So, the first 256 bytes (starting at the segment defined by CS, DS, ; and SS (all which have the same value) is the PSP. The code then ; starts at offset 100h and continues until the end, where the fixed ; data variables exist. A specified amount of memory is then added ; for the stack area. Finally, the maze's arrays for cell-used flags, ; vertical and horizontal walls, and the path stack are added. The ; path stack gets all of the remaining memory once the space for the ; flags and walls have been allocated. Mazes that are very big, but ; within the available memory, may leave very little room for the path ; stack and this can cause a run-time error as the maze is calculated. ; ; ; RUNTIME ENVIRONMENTS ; ; A program is often the combined product of several translation units or ; modules, linked together in useful fashion. Whether a program is written ; in assembly language, C, Pascal, other languages, or some combination of ; them, the memory layout for the program usually follows a standardized ; template. All of the code is collected together, constants are collected ; together, static data which needs to be initialized is collected together, ; and static data which does not need any initialization is also collected. ; Automatic data is placed on the 'stack' and heap space grows from the end ; of the static data (initialized, uninitialized, and constant) up towards ; the stack while the stack grows downwards. ; ; Although linkers support many complex details to support building correct ; programs from separately compiled modules, the final, linked product tends ; to break down into six distinct sections: ; ; Section Description Access NV? Run-time Size ; --------------------------------------------------------------- ; Code Execute Yes Fixed/static ; Constants Read Yes Fixed/static ; Initialized Data Read/Write Yes Fixed/static ; Uninitialized Data Read/Write No Fixed/static ; Heap Read/Write No Variable, up ; Stack Read/Write No Variable, down ; ; (NV means "non-volatile" and indicates whether or not there needs to be ; some form of non-volatile storage. If yes, this can be on disk or in some ; hardware ROM or flash memory.) ; ; The code section is much like the constant data section, except that it is ; for program instructions. The size of the code space is usually set at ; link time and does not need to change during execution. ; ; The constant data section, as I use the term above, is meant for data ; which cannot be modified during execution. This region is different from ; initialized data because constants do not ever require write-access during ; run-time. Examples of this kind of data are PI and sine tables, both used ; in trigonometry. For some systems, these constants might be co-located ; with code, if the code space permits reading-as-data. ; ; The initialized data section is for a region of read/write memory, where ; the initial values must be preset to specific values before the program is ; allowed to start running. However, the initialized data area requires ; read/write access at run time. An example of this kind of data might be ; the seed for a random number generator, which is initialized to some ; default value before the program starts running. ; ; The unitialized data section is for data which does not require initial ; values. This means that the data could start out randomized and that this ; would not impact the correct behavior of the program. An example here ; may be a common, shared variable which is initialized from some command ; line parameter value after the program starts running. ; ; The heap section isn't static at run time. Normally, this section is set ; up having zero size to start and then grows and shrinks during execution. ; This is the area used by routines like C's malloc(), for example. In ; assembly programs, heap is not as often used as it is in C, but the idea ; is still valid for assembly programs and may be useful. Either way, a ; simple design for the heap has it growing upwards and away from the last ; memory location required by all the collected static regions, towards the ; stack. ; ; The stack section also isn't static at run time. Normally, this section ; also starts out with zero size and grows and shrinks during execution. It ; usually grows downward and away from the last possible memory location for ; the program and towards the changing end of the heap section. The result ; of the operation of the stack and heap is that there is an invisible area ; of read-write memory, a "no man's land" so to speak, between the heap and ; the stack; an area which often shrinks like a candle burning at both ends. ; If, during execution, the heap grows into the stack (or visa versa) then ; the program will probably fail to operate correctly. ; ; Most conventional programming languages are designed with the above model ; in mind, at run-time, with the above mentioned sections arranged in that ; order in memory. This allows rather simple program loaders and protection ; schemes. ; ; Some operating systems, like DOS, manage only the read/write, dynamic RAM ; available directly to their CPU, so the non-volatile portions mentioned ; before are often kept "on disk" as a file and loaded. DOS performs this ; function, for example, preparing and loading EXE and COM files into RAM ; before starting them. ; ; In von Neumann architectures, like the DOS-based PC, the operating system ; stores only the segments called CODE, CONST, and INIT on a disk, in a ; specially formatted, executable file. When the program is to be started, ; the operating system allocates enough volatile RAM from its own volatile ; RAM memory heap to hold the entire program with all the segments and then ; loads those three on-disk portions from the file into the associated ; segments in RAM. It then starts the program. ; ; In Harvard architectures, it's more difficult to arrange as any operating ; system trying to load the program must have access to the CODE segment as ; if it were a writable data segment. This is one reason why many Harvard ; style CPUs support code-memory-as-data instructions just for that purpose. ; ; In protected mode environments, such as what the x86 can provide, where ; code segments are not writable when configured as code memory, the ; operating system will forge writable data selectors as aliases to the code ; area of the program it is loading and use those instead. ; ; In the DOS case, two types of executable file formats are supported: the ; EXE and COM file types. The linker generating the COM file simply places ; the segments called used as CODE, CONST, and INIT adjacent to each other ; in the file, written out as a large, single block of data. DOS then ; simply reads the entire file directly into an allocated RAM region and ; then runs it. The EXE file format is more complex, including some special ; fields designed for DOS to allow adjustments of various code fragments ; which need to be modified, depending on where DOS decides to load the ; program. This extra support is required for code that includes hard-coded ; segment addresses, which most larger DOS programs do require. ; ; The EXE file was introduced in DOS, version 2, to acknowledge the fact ; that programs were becoming larger. As mentioned, a special header is ; included at the beginning of the file to provide some additional data used ; by DOS when loading the program into memory. Providing this extra data, ; instead of relying on a fixed set of assumptions, allows programs to have ; program code much larger than 64k-byte, while still allowing DOS to load ; the program into different parts of the computer's RAM. ; ; ; COMPILATION TOOLS ; ; I'm currently using the following tools: ; ; Microsoft ML, version 6.15.8803 ; Microsoft Segmented Executable Linker, version 5.60.339 ; Microsoft EXE File Header Utility, version 3.00 ; ; To validate these versions, enter the ML command without any options and ; look at the message it displays for the version number and enter the LINK ; command without any options and look at the message it displays for the ; version number. Also, get a version of EXEHDR.EXE, which allows you to ; modify the EXE headers, if you want to. ; ; If you do not have either of these tools, you can get them (or something ; closely equivalent.) The ML assembler is available as part of the MASM32 ; installation, at: ; ; www.masm32.com/masmdl.htm ; ; Install the tools and you will have the required elements. The versions ; from that site are: ; ; Microsoft ML, version 6.14.8444 ; Microsoft Segmented Executable Linker, version 5.12.8078 ; ; These are somewhat older than the ones I'm using. The update for the ; newer versions I use were once available directly from Microsoft, but they ; have since buried it into update tools which will only update existing, ; purchased products from Microsoft. I believe this is with careful thought ; because of the email conversations I had with Terry Leeper, who is/was the ; Program Manager of the Visual C++ group (that group owns MASM/ML), about ; this subject before they took action. However, the newer linker is still ; available from Microsoft at: ; ; download.microsoft.com/download/vc15/Update/1/WIN98/EN-US/Lnk563.exe ; ; No idea how long that will continue. But it is there, for now, and that ; probably means they have their reasons, since they certainly could have ; made changes to this also, when they acted to restrict ML. ; ; Keep in mind that Microsoft appears to have custom-built a wide variety of ; linkers, all with the exact same name, for their various compiler tools. ; It's frighteningly easy to have the wrong version available for use with ; ML (or MASM), so it's important that you verify which version of LINK.EXE ; runs when you just type in LINK at the DOS prompt. It's possible that ; your path specifies a directory containing the wrong version of the linker ; before it specifies the right version, so you may need to adjust the path ; to fix this. ; ; There are some caveats. Some of the later versions of ML, including the ; one I'm using, require Windows to be running. You cannot use these under ; a straight DOS boot environment. If memory serves, this requirement began ; with version 6.11c of their assemblers and has continued to this day. ; Versions 6.11b and prior (which include 6.11a, 6.11, 6.10, and going back ; to perhaps 6.00) require the presence of DOSXNT.EXE and DOSXNT.386 to be ; present in the 'path.' But they will run on DOS, if so. MASM preceded ML ; and the versions of MASM up to version 5.10B, at least, worked just fine ; on DOS without any visible extender to support them. ; ; Earlier versions of the assembler, going back to version 6.11, should work ; okay. I did try version 5.10B and it would not assemble this file, due to ; some newer directives in the source code. Some versions of the assembler ; tried to invoke the new linker I had with some old options which are no ; longer present, but this didn't prevent correct linking. ; ; To compile this file, enter: ; ; ML template.asm ; ; That's it. It should produce the template.com file for you (or else it ; will produce a template.exe file, if you've set the .MODEL statement so ; that it produces .EXE programs.) ; ; I also have a modest web page on getting these tools and docs at: ; ; http://users.easystreet.com/jkirwan/new/pctools.html ; http://users.easystreet.com/jkirwan/new/pcdocs.html ; ; Those may help out, as well. ; ; ; MODIFICATIONS ; ; Original source. ; ; ========================================================================== ; ------------------------------------------------------------------ ; Set up the program type. Using TINY means a .COM. Using SMALL ; means a .EXE. You can also use MEDIUM, LARGE, and COMPACT. For ; the other items you need to look them up in the documentation. ; ------------------------------------------------------------------ ; Order is important in the following two lines: .MODEL TINY, STDCALL, NEARSTACK .386 SUBTTL Some useful declarations PAGE STACKSIZE EQU 100 ; Required minimum stack size expressed ; in bytes, not paragraphs. Informs ; the assembler for .EXE programs. In ; .COM programs, it is used to adjust ; the stack pointer at run-time. USEDOSALLOC EQU 1 ; For .COM programs running under DOS 2.0 ; or later: 1 if DOS memory management ; is requested, 0 otherwise. ; @DB ; ---------------------------------------------------------------------- ; This macro helps define space for the given byte data and returns the ; generated label. The data is placed into the group, DGROUP. The ; invocation is something like, @DB(item1, item2, ... itemN), and the ; macro returns the label to the generated data. The items follow the ; general syntax requirements for a DB directive. ; ---------------------------------------------------------------------- @DB MACRO initializers:VARARG LOCAL msg LOCAL oldseg oldseg TEXTEQU @CurSeg .DATA msg LABEL BYTE FOR argc:REQ, db argc ENDM @CurSeg ENDS oldseg SEGMENT EXITM ENDM SUBTTL Program Segment Prefix Description PAGE ; PROGRAM SEGMENT PREFIX (PSP) DESCRIPTION ; ========================================================================== ; The PSP (program segment prefix) is always occupies the first part of ; a .COM program's memory segment. In the case of .EXE programs, it is ; the first (and only) part of the memory segment passed in DS and ES ; when the .EXE is started. Either way, 256 bytes (0x100) are used for ; this communication area. These entries describe the information found ; there. ; ; The use of a SEGMENT AT here just keeps the assembler from generating ; an instance of the data, while still assigning offsets. Reference the ; data here via an appropriate segment register which holds the correct ; segment value in it. ; ; For most programs, the only interesting part of the PSP is the buffer ; used to hold a copy of the command line. DOS copies the portion of ; the command line that follows the program name into pspCmdTailText ; and sets pspCmdTailCount to the number of characters placed there. ; ========================================================================== PSP SEGMENT AT 0 ORG 0h pspInt20 dw 1 DUP(?) ; INT 20h instruction. pspNextPara dw 1 DUP(?) ; segment addr of next paragraph. db 1 DUP(?) ; reserved. pspDispatcher db 5 DUP(?) ; long call to DOS. pspTermVector dd 1 DUP(?) ; Termination address (INT 22h). pspCtrlCVector dd 1 DUP(?) ; Control-C handler (INT 23h). pspCritVector dd 1 DUP(?) ; Critical error handler (INT 24h). dw 11 DUP(?) ; reserved. pspEnvironment dw 1 DUP(?) ; segment addr of environment. dw 23 DUP(?) ; reserved. pspFCB_1 db 16 DUP(?) ; default FCB #1. pspFCB_2 db 16 DUP(?) ; default FCB #2. dd 1 DUP(?) ; reserved. pspCmdTailCount db 1 DUP(?) ; count of chars in command tail. pspCmdTailText db 127 DUP(?) ; text, starts 20h, ends 0Dh. PSP ENDS SUBTTL START/MAIN/BEGIN: Program Initialization PAGE ; COM AND EXE PROGRAMS START HERE ; ========================================================================== ; DOS always starts a COM program at 0100h. The .STARTUP assembler command ; causes the assembler to position the default memory offset to begin at ; this point (although the previous PSP code would have the same effect.) ; ; DOS will also automatically start EXE programs at the point at which the ; .STARTUP assembler command appears. The difference is that DOS may place ; the code at an offset address other than 0100h. ; ; When DOS tries to run a COM program, it first allocates the largest ; available block of memory. (Under normal circumstances, there's only ; one block and this is almost always several hundreds of kilobytes, in ; size.) DOS then prepares the first 256 bytes of the allocated block, ; by filling in the elements of the PSP (noted above) and then loading ; the COM program file, starting just after the PSP. Once this is done, ; the registers are set up as noted below and DOS transfers control by ; setting the CS register to the base segment value of the allocated ; memory and setting the IP register to 0100h (both can be set at the ; same time by a "far jump.") ; ; DOS always places a double-byte 0 (word 0) at the end of the stack, ; in effect a PUSH 0, so that a RET (return from subroutine) instruction ; will go to offset 0 (in the PSP) where a special instruction exists to ; cause the COM program to terminate, properly. This is a convention ; that is not the prefered way, however, to exit programs, including COM ; programs. So don't use it. ; ; DOS starts the COM program with the following register defaults: ; ; CS, DS, ES, and SS ; This segment register is loaded with the segment address of the ; initial allocation block used to hold the program. This memory ; segment includes the PSP region. ; ; IP ; The starting address, which is always 0100h. ; ; SP ; Points to the end of the program area, less the word-0 pushed at ; the end. Often, this is 0FFFEh, but may be different if DOS was ; forced to allocate less than 64k. ; ; BX, CX ; Together, these two 16-bit registers are considered to be a ; single 32-bit integer, with BX being the upper 16 bits of this ; value and the CX register being the lower 16 bits. The value ; placed in this pair is the number of bytes loaded from the COM ; executable file. Do not depend on this, though. ; ; AX, DX, SI, DI ; These are always set to 0, upon startup. But it's probably ; best not to rely on this behavior. Just consider them as ; available for immediate use, instead. ; ; DOS starts the EXE program with the following register defaults: ; ; CS ; This segment register is loaded with the starting segment of ; the code, as indicated in the EXE header structure and adjusted ; by DOS. ; ; IP ; The starting address offset of the EXE program, also taken from ; the EXE header structure. ; ; SS ; This segment register is loaded with the segment of the stack, ; as indicated in the EXE header structure and adjusted by DOS. ; ; SP ; The starting address offset of the stack, as indicated in the ; EXE header structure. ; ; DS, ES ; These two segment registers are loaded with the PSP segment ; address. The offset for the PSP data, of course, is assumed to ; be 0. ; ; BX, CX ; Together, these two 16-bit registers are considered to be a ; single 32-bit integer, with BX being the upper 16 bits of this ; value and the CX register being the lower 16 bits. The value ; placed in this pair is the size of the program segment. Do not ; depend on this, though. ; ; AX, DX, SI, DI ; These are always set to 0, upon startup. But it's probably ; best not to rely on this behavior. Just consider them as ; available for immediate use, instead. ; ; DOS 1.0 only knew how to run .COM programs. In this first version of DOS, ; there were also no memory management functions and no concept of allocated ; or free blocks of memory. .COM programs were free to use any or all of ; the available memory, when they ran. Their stack was simply set up to the ; end of their program segment (there was only one such segment) or the end ; of memory, which ever came first, and then they were then simply started. ; DOS just loaded the .COM program into the first available memory segment ; and ran them. ; ; DOS 1.0 would function on a machine with only 16k of RAM -- the first PC ; from IBM provided a minimum of 16k, with an option to increase this to 64k ; provided on the motherboard and up to 256k with a memory expansion card. ; ; With the advent of the IBM PC/XT, standard RAM was increased to 128k, with ; an option to increase this to 256k on the motherboard and up to 640k, with ; memory expansion cards. The XT also added a 10Mb hard disk. Microsoft ; came out with DOS version 2.0 to accomodate this new machine and included ; a number of features, including extended support for larger disks with the ; FAT16 format, the .EXE program type, and memory allocation functions used ; to support them. ; ; For backward compatibility, while also now comforming to the requirements ; of the new memory allocation functions, DOS versions from 2.0 and beyond ; allocate the largest (usually, this means all of memory) memory block when ; starting .COM programs. This was the safer way to proceed, under the new ; guidelines. ; ; .COM programs which want to free up unused memory to DOS, at least those ; versions from 2.0 and beyond, need to add code designed for that purpose. ; For example, if a .COM program wishes to use DOS to load and run another ; program, it will need to free up some of the DOS memory, first. ; ; Finally, regardless of .COM or .EXE, the string direction flag is cleared ; prior to starting the program. ; ========================================================================== .CODE ; ------------------------------------------------------------------ ; Do basic setup before the program proper takes control. For .EXE ; programs, whether under DOS 1.0 or later versions, this amounts to ; just taking advantage of .STARTUP. But for .COM programs, only in ; the case of DOS 2.0 and later, this means computing actual needed ; starting size of the program and informing DOS so that it can free ; up the rest of memory for other uses. ; ------------------------------------------------------------------ ; The .STARTUP directive does nothing for .COM programs, but for ; .EXE programs it inserts some code that is designed to place ; the DS segment register at DGROUP and to adjust the stack ; segment that DOS provides so that DS=SS. This also means SP ; needs adjusting. So the code it inserts looks about like: ; ; mov ax, SEG DGROUP ; mov ds, AX ; ASSUME ds:DGROUP ; mov bx, ss ; sub bx, ax ; shl bx, 4 ; mov ss, ax ; add sp, bx ; ASSUME ss:DGROUP .STARTUP ; Now, tell the assembler what ES points to. ASSUME es:PSP ; Just in case, set the default string direction to 'forward.' cld ; Find out the DOS version. This may be needed later on. ; ; The DOS function here returns as follows: ; ; AL DOS major version number, or 0 if version < 2.0 ; AH DOS minor version number, if AL != 0. ; ; This value is saved in dosVersion for any appropriate use. ; ; The function was supported, starting with DOS 2.0, but it ; turns out that IBM PC-DOS 1.0 set AL to zero. Just in case, ; it's set to zero here, as well, on the idea that modern ; documentation on this fact is incorrect and DOS 1.0 leaves ; AL unchanged. mov ax, 3000h int 21h ; AL=0 if the version is < 2.0. mov dosVersion, ax ; Update .COM file memory allocations for DOS versions 2.0 and ; and greater. This is useful because all DOS versions assign ; all of the available memory to running a .COM program. For ; versions later than 2.0, they have the ability to manage the ; allocation of memory and it helps to free up the portion of ; memory not required for the code and static data areas so ; that the .COM program use use the DOS memory allocation ; functions instead of having to develop its own. ; Ensure some default value for segEndOfProgram. It is ; only meaningful for .COM programs running under DOS 2.0 ; and later. 0 is the default and it will be zero also ; if memory was freed from a .COM program to DOS. xor bx, bx mov segEndOfProgram, bx ; Test AL (major version) for 2 or greater. If not, this ; is a .COM program. However, nothing needs to be done in ; that case because there is no memory allocation feature ; in DOS 1.0 and all memory is already allocated for the ; program to operate. .IF ( al >= 2 ) ; We know it is DOS 2.0 or greater. So if CS == DS now, ; then we have verified that this is a .COM program and ; running under DOS 2.0+. mov ax, cs mov dx, ds .IF ( ax == dx ) ; Okay. We need to do some computation for the .COM ; program and inform DOS about our static memory ; requirements. Add the desired stack size to the last ; memory location in the .COM program. If that causes ; an overflow (carry), then just set the requested stack ; pointer up to its maximum possible value, 0FFFEh. If ; If that doesn't cause an overflow, then we should ; include all the bytes in the memory paragraph this new ; value points to, so round it up to point to the next ; paragraph, then subtract 2 to make it point at the ; last word in the paragraph to which it was pointing. ; ; Either way, this new requested stack pointer value ; must be checked against what DOS originally provided. ; DOS provides the largest possible value, which is ; usually just 0FFFEh. But it may have provided a ; smaller value if there wasn't enough real memory to ; use a larger value. If so, we don't want to override ; it. So compare our requested stack pointer with the ; one DOS provided and, if the requested pointer is less ; then we are safe to use it. If so, then apply it to ; SP, stick one word of '0' on the top of the stack (the ; DOS guarantee for .COM programs), and then make sure ; that this DOS version supports memory allocation ; before trying to update the program's memory size. mov bx, OFFSET endProgram add bx, STACKSIZE .IF (!CARRY?) add bx, 0Fh ; round up to the next nearest and bx, 0FFF0h ; paragraph; then adjust back sub bx, 2 ; to point to the last word. .ELSE mov bx, 0FFFEh ; maximum possible value .ENDIF mov dx, sp .IF (WORD PTR bx < dx) mov sp, bx ; set the stack and mov WORD PTR [bx], 0 ; place 0 on it. .ENDIF mov ah, 30h ; get the DOS version, int 21h ; (also destroys BX) .IF (al != 0) ; must be version 2.0+, if not 0. mov bx, sp ; now grab the stack pointer mov cl, 4 ; and compute the number of dec bx ; paragraphs which are included shr bx, cl ; from offset 0h to it's value. inc bx mov cx, cs ; compute the first segment after add cx, bx ; the end of this program. IF USEDOSALLOC mov ah, 4Ah ; tell DOS to adjust the size int 21h ; of the allocated block. ELSE mov segEndOfProgram, cx ENDIF .ENDIF ; That completes the .COM file stuff for DOS >= 2.0. .ENDIF .ENDIF SUBTTL Main Program PAGE ; ========================================================================== ; Main program starts here. ; ========================================================================== ; ------------------------------------------------------------------ ; At this point, everything is ready to go. We've set up our stack ; and computed the end segment for our program and may have told DOS ; to shrink our allocation space so that we can use DOS's allocation ; functions, now. ; ------------------------------------------------------------------ ; Get the time from DOS and initialize random from that. mov ah, 2Ch int 21h mov cs:[random_x], dx mov cs:[random_y], cx ; Loop 20 times. mov cx, 20 contin: call random call BinarytoASCII mov dx, OFFSET @DB(13, 10, '$') mov ah, 9 int 21h loop contin SUBTTL Program Ends Here PAGE ; ========================================================================== ; The program is done. Finish up properly. ; ========================================================================== ; If DOS is version 2.0 or greater, we use the traditional, ; recommended exit method. .IF ( dosVersion >= 2 ) .EXIT 0 ; Otherwise, this must be a .COM program running under DOS 1.0. .ELSE ret .ENDIF SUBTTL random PAGE random PROC NEAR USES cx dx ; ---------------------------------------------------------------------- ; ---------------------------------------------------------------------- mov dx, ax shl dx, 1 and dx, 7FFEh xor ax, dx mov cl, 14 shr ax, cl and al, 1 or ax, dx ret random ENDP SUBTTL BinarytoASCII PAGE BinarytoASCII PROC NEAR USES ax bx cx dx ; ---------------------------------------------------------------------- ; This routine converts a binary value in the AX register into a ; decimal ASCII value that is displayed on the screen. A DOS ; function call is used to display each character. ; ; Inputs: ; ; AX binary value to display ; ---------------------------------------------------------------------- sub cx, cx mov bx, 10 .REPEAT sub dx, dx div bx add dl, '0' push dx inc cx .UNTIL ax == 0 .REPEAT pop dx mov ah, 2 int 21h .UNTILCXZ ret BinarytoASCII ENDP SUBTTL Static Data Definitions PAGE ; ========================================================================== ; Static data for the program. ; ========================================================================== ; ---------------------------------------------------------------------- ; STATIC VARIABLES -- INITIALIZED ; ---------------------------------------------------------------------- .DATA ; ---------------------------------------------------------------------- ; STATIC VARIABLES -- UNINITIALIZED ; ---------------------------------------------------------------------- .DATA? segEndOfProgram dw 1 DUP(?) ; .COM programs running under DOS 2.0 ; or later may be resized so that ; DOS memory allocation functions ; may manage memory. This value ; will point to the first memory ; segment not needed by the .COM ; program in this case. Otherwise, ; if .EXE or DOS 1.0, it's zero. ; (Zero this if memory returned.) dosVersion dw 1 DUP(?) ; Major and minor DOS version. ; ---------------------------------------------------------------------- ; STACK ; ---------------------------------------------------------------------- .STACK STACKSIZE endProgram LABEL BYTE END