home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-10-23 | 112.8 KB | 2,440 lines |
- Atari ST Machine Specific Programming In Assembly
-
- Chapter 4: Initialization Algorithms, Part 1
-
-
- The prime directive of a computer system is to function
- as an algorithm executing environment. We can consider the
- unblemished environment, in which there is no operating
- system, no stored data and no stored applications, to be the
- fundamental environment. When we add an operating system to
- the fundamental environment, we inject a director of
- operations which dictates to the unblemished environment the
- manner in which it will fulfill its prime directive.
- Initially, the operating system is established as an
- unblemished subenvironment within the fundamental
- environment. When we cause an algorithm to be executed
- under the direction of this unblemished subenvironment, the
- algorithm is placed within an execution environment that is
- configured according to the operating system's prime
- directives. The configuration of the execution environment
- as it exists immediately after the algorithm is placed
- therein imposes a set of conditions under which the
- algorithm may attempt to perform its prime directives.
- The set of conditions which restrict the activities of
- the algorithm at the onset of execution are called initial
- conditions, simply because they exist before execution
- commences. These initial conditions must be distinguished
- from later conditions because the configuration of the
- execution environment is subject to change. In fact, the
- execution environment may be altered by the algorithm
- itself.
- It is possible, and probable, that the algorithm's
- first prime directive will consist of a set of instructions
- to establish new initial conditions. These new conditions
- may remove restrictions imposed by the operating system, or
- they may further restrict the activities of the algorithm's
- other prime directives. When an algorithm establishes new
- initial conditions, we say that it initializes the execution
- environment. And, as long as neither the initial conditions
- imposed by the operating system, nor those imposed by the
- algorithm prevent such activity, the algorithm is free to
- alter existing conditions at any time during its execution.
- It is via this power to alter execution environments
- that we are able to establish a variety of system
- configurations which we translate into system flexibility.
- A flexible system is able to perform any of hundreds of
- tasks equally well. As payment for this flexibility, we are
- forced to maintain the execution environment of all but the
- most trivial algorithms.
- In chapter two, I pointed out an Assempro debugger
- deficiency which forced me to develop an initialization
- algorithm that would prevent a program's first line of text
- output from overwriting the debugger screen. This algorithm
- established initial conditions in the program's execution
- environment, which had been established by AssemPro, to
- correct the deficiency; it was the compromise that I had to
- make with the debugger so that it would execute my program
- in a manner that I considered to be proper.
- While one would be justified in venting a certain
- amount of annoyance because of the bother, perhaps by booing
- and hissing for a few minutes, permitting this trifle to
- fester into something more than a minor irritant would only
- hinder productivity. This type of bother is not at all
- uncommon when working with computers. However, I am sure
- that you have already come to know this, even if your
- experience has been limited to executing programs rather
- than writing them.
- I think that one can justifiably view all program
- initialization algorithms as corrections to the execution
- environment. It seems that one should expect a modern
- operating system to handle such trivial, repetitive
- programming chores. However, we might wish to consider the
- probable inflexibility of such an operating system. Well,
- regardless of our wishes or opinions concerning the matter,
- for all but the most trivial, the code proper of each Atari
- ST program must be preceded by a sequence of instructions
- that establishes appropriate initial conditions before
- execution proper begins. Initial conditions for the more
- simple programs is the subject of this chapter.
- Its lack of rigidity is what I consider to be one of
- the most powerful attributes of the ST's programming
- environment. Nevertheless, it is the operating systems's
- flexibility which exacerbates the development of a program
- classification scheme that would permit one to assign
- specific initialization algorithms to specific program
- types. When one decides to explain the configuration of the
- ST operating system, one must discuss the interrelation of
- TOS, GEM, GEMDOS, BIOS, XBIOS, GDOS, VDI, AES, Line-A and
- Line-F routines. I am not even going to try to best those
- that have already accomplished this task admirably. Instead
- I refer you to an excellent article, Atari St Software
- Development, by Michael Rothman, Byte, September, 1986. You
- can obtain a copy from Byte or, perhaps, from a local
- library.
- Alternately, although the discussions are not nearly as
- lucid, you might see what you can get out of pages 2-3
- through 2-5 of the Katherine Peel book and pages 3 to 9 of
- the Compute! AES book, by Leemon. If you add to that which
- you obtain in knowledge from those two books, by reading
- chapter 1 of the Abacus GEM Programmer's Reference, you may
- be able to get by without the Rothman article.
- Note this, in case you don't see it elsewhere, the
- Line-F Emulator is used by the operating system, it is not
- available to users as is indicated on page 237 of the
- Internals book. The Internals book hints at this use on
- pages 238-239, but the hint is not sufficient; the book
- should warn readers that interference with this emulator
- will upset the AES portion of the operating system.
- Without trying to force a classification on any type of
- Atari ST program, I find it convenient to discuss them in
- terms of specific attributes which can be assumed from the
- following set of categories:
-
- 1. The suffix appended to the program name.
- 2. The initialization algorithm used.
- 3. The manner in which execution is initiated.
- 4. The method by which processor control is
- relinquished.
- 5. The algorithm used to terminate execution.
-
- To some extent, we can classify a program as a
- particular type by referring to one or more of its list of
- attributes drawn from the above categories. For example, if
- the suffix of a program is ACC, we call it a desk accessory.
- And the unblemished operating system, as it exists at the
- time I compose this sentence, expects a desk accessory to be
- executed in a particular manner, to reside in a particular
- environment, to contain a particular initialization
- algorithm, to relinquish processor control in a particular
- manner and to remain active until the computer is turned
- off.
- But I have one desk accessory, Multi Desk, which
- creates an environment in which I can ignore most operating
- system expectations for all other desk accessories,
- including additional copies of itself. Therefore, if I were
- to give you a list of required desk accessory attributes, I
- would have to imagine all sorts of exceptions, some of which
- may not even be known to me at this time. At best, all I
- can do is to give you examples of programs which will
- function as desk accessories in an unblemished operating
- system environment, while placing little or no restrictions
- on their execution in other environments.
- Nevertheless, in general, suffix differentiation serves
- as an indication of the operating system services required
- by the program and the level of user interaction with the
- program. The program suffixes recognized by the operating
- system are: PRG, TOS, TTP, and ACC. You have observed some
- of the differences in services that you can expect for
- programs that have a PRG or TOS suffix. But, remember,
- these differences were exhibited by an unblemished
- environment. There is no reason to assume that identical,
- or even opposite, services cannot be forced by some
- environment modifying agent. Thus, when assigning
- attributes to a program, we should not assume that they are
- tightly bound and not subject to change. We must remain as
- flexible as the computer system we are using. The best use
- to which we can apply descriptives is for the convenience of
- the moment. Then we shall not be surprised by behavior that
- we would otherwise view as deviations from the norm. With
- this viewpoint, we continue.
- Initialization algorithms tend to define the execution
- environment based on the task to be performed. For example,
- if a program is to produce graphic output, we can suppose
- that it will contain VDI initialization algorithms. And if
- the graphic output is directed to a window then we can
- suppose the necessity of AES initialization algorithms.
- Program execution may be initiated at boot time, from
- the desktop, from the menu line or by command of another
- program. An example of the latter process is the execution
- of a program from within a debugger. But, as I have
- indicated, with the introduction of utilities such as Multi
- Desk, it is no longer possible to define restrictions
- concerning program execution initiation. For example, Multi
- Desk permits a desk accessory to be loaded and executed
- after the system has booted.
- Primarily, the method of relinquishing processor
- control is determined by the memory residency status of a
- program. Desk accessories usually relinquish control with
- AES function $17, aka evnt_mesag. Load and stay resident
- (LSR) programs usually relinquish control with GEMDOS
- function $31, aka ptermres. When a program causes another
- to be loaded and executed, it relinquishes processor control
- by executing GEMDOS function $4B.
- The execution of a program may be terminated with
- GEMDOS function $0, which simply passes processor control
- back to the initiating agent, either the desktop or another
- program. Or it may be terminated with GEMDOS function $4C,
- which will pass a message back to the initiating agent. If
- the program is a desk accessory, it may not terminate until
- the computer is turned off, unless it has been executed via
- a utility such as Multi Desk.Now, it is with an enlightened
- unbiased frame of reference with which we shall proceed into
- a discussion of initialization algorithms for ST programs.
-
- Parameters for GEMDOS Function $4A
-
- The information contained on pages 137 - 138, GEMDOS
- function $4A, aka (also known as) setblock or m_shrink, and
- pages 145 - 148 of the Abacus Internals book becomes germane
- at this time. The discussion on page 37 of Compute!'s VDI
- book is also worth reading, but there are two critical
- errors on that page. SETBLOCK is a GEMDOS function, not an
- XBIOS function; and the second longword on the stack, not
- the second word, points to the start of the Transient
- Program Area.
- I would like you to suppress the portion of these
- discussions pertaining to the necessity of a program
- declared stack. I want you to realize that a default stack
- does exist, and it is perfectly reliable for programs with
- limited stack growth. The phrase limited stack growth is
- somewhat vague, but I will give you examples that exhibit
- such growth.
- Either the Compute! or the Abacus discussion (or both)
- should impress you with the following facts: one, a
- program's basepage precedes the program proper; two, at the
- onset of program execution, the first address of the
- basepage is stored at 4(a7); three, GEMDOS function $4A
- requires two parameters: the number of bytes occupied by the
- program and the value that is stored in 4(a7) at the onset
- of execution.
- While it may seem that three parameters are passed when
- function $4A is invoked, according to the examples you will
- see (One is called a meaningless word in the Abacus
- example.), that third parameter is simply one word of the
- function specification for the GEMDOS call. If you specify
- the function as a longword, it becomes $4A0000, and there is
- no meaningless word to be passed. This means that you
- specify the function using the instruction: move.l #$4A0000,
- -(sp). I want to impress you with the fact that the
- number of bytes occupied by a program is precisely the
- difference between the last address occupied by the program
- and that value at 4(a7). In program 9, a label is placed at
- the end of the program, just before the end statement.
- Then, the number of bytes occupied by the program is
- calculated by subtracting the basepage start address from
- the address of that label. Program 9 was written to verify
- that the value obtained by this method is equivalent to that
- obtained using the sum of program segments method discussed
- in the references.
- The verification procedure requires the use of a rather
- complex binary to ASCII decimal conversion algorithm. This
- subroutine is essential to the output of data, not only in
- program 9 but also in the programs to immediately follow. A
- discussion of program 9's algorithms follows its listing and
- execution results.
-
- Program 9. Comparing two methods of computing program
- length. Execution results follow the listing.
-
- ; Program Name: PRG_3AP.S
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a PRG and a TOS extension.
-
- ; Function:
-
- ; Verifies that two methods of calculating the amount of memory
- ; occupied by a program are equivalent.
-
- ; Execution Instructions:
-
- ; Double click on PRG_3AP.TOS from the desktop. A heading and two lines
- ; of data will be printed on the screen. Press the return key to terminate
- ; execution and return to the desktop.
-
- ; MAJOR NOTE: Because base page information is needed by this program,
- ; if it is to be executed in the AssemPro debugger, it must
- ; be loaded with the "Execute program" function.
-
- ; Note - The stack used in the program is small enough to permit easy
- ; access to its contents via the AssemPro debugger. The stack
- ; exhibits limited growth, and program execution is flawless with
- ; a system assigned, default stack.
-
- calculate_size_1: ; Sums data that is stored in basepage.
- movea.l 4(a7), a5 ; Copy basepage address in A5.
- move.l $C(a5), d7 ; Copy program text length in D7.
- add.l $14(a5), d7 ; Add data length.
- add.l $1C(a5), d7 ; Add bss length.
- add.l #$100, d7 ; Add basepage length.
-
- calculate_size_2: ; Subtracts last program address from first.
- lea program_end, a4 ; Put "end of program" address in A4.
- suba.l 4(a7), a4 ; Subtract basepage address from A4.
-
- mainline:
- lea stack, a7 ; Put address of label "stack" in A7.
- bsr.s print_newline ; Prevents damage to AssemPro screen and
- ; skips a line for printer output.
- lea heading, a0 ; Print heading for program's output. The
- bsr.s print_string ; print_string subroutine expects the address
- ; of the string to be in A0.
- print_size_1:
- lea message_1, a0 ; Print label to identify output data.
- bsr.s print_string ;
- move.l d7, d1 ; The bin_to_decimal subroutine expects D1 to
- bsr.s bin_to_dec ; contain the binary number to be converted.
- lea decimal, a0 ; Print program size, an ASCII string that is.
- bsr.s print_string ; stored in the array "decimal".
- lea end_msg, a0 ; Print "units" label for output data.
- bsr.s print_string
-
- print_size_2:
- lea message_2, a0 ; Same remarks as for "print_size_1".
- bsr.s print_string
- move.l a4, d1
- bsr.s bin_to_dec
- lea decimal, a0
- bsr.s print_string
- lea end_msg, a0
- bsr.s print_string
-
- wait_for_keypress: ; Halt execution so screen can be read.
- move.w #8, -(sp) ; Function = c_necin = GEMDOS $8.
- trap #1 ; GEMDOS call.
- addq.l #2, sp ; Reposition stack pointer at top of stack.
-
- terminate:
- move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
- trap #1 ; GEMDOS call.
-
- ;
- ; SUBROUTINES
- ;
-
- print_string: ; Expects address of string to be in A0.
- pea (a0) ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp ; Reposition stack pointer.
- rts
-
- print_newline: ; Prints a carriage return and linefeed.
- pea newline ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp
- rts
-
- ; This is a binary to ASCII decimal conversion subroutine. It converts a
- ; 32-bit binary number in register D1 to an ASCII string that is stored in
- ; an array called "decimal".
-
- ; The binary to ASCII decimal conversion subroutine uses an algorithm
- ; based on "repeated subtraction". The subtrahends are powers of ten
- ; which range from 10^1 to 10^9. These values were chosen to accommodate
- ; up to ten digit integers. The binary value passed to the subroutine is
- ; the initial current minuend. After a decimal digit is extracted from
- ; a current minuend, a new current minuend is produced.
-
- ; During the subtraction process, the minuend is gradually reduced to a
- ; negative number. The number of times that each power of ten can be
- ; subtracted from a current minuend before the minuend becomes negative
- ; yields a significant digit for that power of ten.
-
- ; As each digit is extracted, it is converted to its ASCII code by adding
- ; the ASCII code for decimal 0 to the accumulated subtractions count. The
- ; ASCII code for each digit is stored as a character in a string. The string
- ; is accumulated in the array called "decimal".
-
- ; After the current minuend becomes negative a "new" minuend is formed by
- ; adding the current subtrahend to the negative value. The process
- ; continues until the current subtrahend is 0.
-
- ; The algorithm discards leading zeroes using a loop that subtracts
- ; subtrahends from the number in D1, beginning with the largest, until a
- ; subtrahend which does immediately yield a negative difference is detected.
-
- ; NOTE: Register D2 is the subtractions counter. In it is accumulated the
- ; number of times that a subtrahend can be subtracted from a minuend
- ; before the minuend becomes negative. At the start of the subroutine
- ; D2 is initialized to zero to guarantee that the first count
- ; accumulated is correct.
-
- ; But observe that D2 is initialized to -1 in the section of the
- ; routine labeled "loop_setup". The reason: after registers are
- ; initialized in the loop_setup section, execution branches to the
- ; section labeled "subtract". The first instruction in that section
- ; adds 1 to the value in D2.
-
- ; I chose to add 1 to the subtractions counter at that particular
- ; location because it was a convenient point in the algorithm to do
- ; so upon exit from the "discard leading zeroes" loop. Upon exit from
- ; that loop, a valid subtraction has already been performed, therefore,
- ; it must be accumulated. Since D2 contains the initialized value 0
- ; at that point in the execution, adding 1 puts the correct count in
- ; D2.
-
- ; Because I want to branch to the same section upon exit from the loop
- ; setup section, I must pre-initialize D2 to -1. Then, after 1 is
- ; added to D2 via the first instruction in the subtract section, the
- ; subtractions counter is fully initialized to zero, as it should be.
-
- bin_to_dec:
- lea decimal, a0 ; Put address of array "decimal" in A0.
- lea subtrahend, a1 ; Put address of subtrahend table in A1.
- move.l (a1)+, d0 ; Put first subtrahend in D0.
- moveq #0, d2 ; Initialize subtractions counter to zero.
- get_sign:
- tst.l d1 ; Is binary number positive, negative or zero?
- beq.s zero_passed ; Branch if number is 0.
- bpl.s positive ; Branch if positive.
- move.b #$2D, (a0)+ ; Store a minus sign in array "decimal".
- neg.l d1 ; Change binary number from neg to pos.
- bra.s discard_leading_zeroes
- positive: ; Branch to here when number is positive.
- move.b #$20, (a0)+ ; Store a space in array decimal.
- discard_leading_zeroes: ; Subtract subtrahend from minuend.
- sub.l d0, d1 ; Loop till difference is positive,
- bpl.s subtract ; indicating that digit is not zero.
- add.l d0, d1 ; Restore minuend.
- move.l (a1)+, d0 ; Get next subtrahend.
- bra.s discard_leading_zeroes
- subtract:
- addq.b #1, d2 ; Increment subtractions counter.
- sub.l d0, d1 ; Subtract subtrahend from D1.
- bpl.s subtract ; Loop until D1 becomes negative.
- convert_to_ascii:
- addi.b #$30, d2 ; Converts binary number to ASCII code.
- move.b d2, (a0)+ ; Store the ASCII digit in array "decimal".
- loop_setup:
- add.l d0, d1 ; Restore the minuend.
- moveq #-1, d2 ; Pre-initialize subtractions counter to -1.
- move.l (a1)+, d0 ; Get next subtrahend.
- bne.s subtract ; Loop back until subtrahend = 0.
- move.b #0, (a0) ; Terminate decimal string with a null.
- rts
- zero_passed:
- move.b #$20, (a0)+ ; Store a space in array "decimal".
- move.b #$30, (a0)+ ; Store an ASCII zero in array "decimal".
- move.b #0, (a0) ; Terminate ASCII decimal string with a null.
- rts
-
- data
- subtrahend: dc.l $3B9ACA00,$5F5E100,$989680,$F4240,$186A0,$2710,$3E8
- dc.l $64,$A,$1,$0
- heading: dc.b $D,$A,'PRG_3AP Execution Results',$D,$A,$D,$A,0
- message_1: dc.b ' Program Size by Method One: ',0
- message_2: dc.b ' Program Size by Method Two: ',0
- end_msg: dc.b ' bytes.',$D,$A,0
- newline: dc.b $D,$A,0
- bss
- align ; Align storage on a word boundary.
- decimal: ds.l 3 ; Output buffer, NULL terminated.
- ds.l 24 ; Program stack.
- stack: ds.l 1 ; Address of program stack.
- program_end: ds.l 0
- ;
- ; NOTE: The declaration following the label "stack", above, can be
- ; ds.l 0, instead of ds.l 1. I take advantage of this in later
- ; programs. When the declaration is made in that manner, however,
- ; the label "program_end" does not appear in the debugger's
- ; output field. Instead, the label "stack" appears where you
- ; would expect "program_end", because, although the assembler
- ; accepts this label without issuing an error report, the label
- ; "program_end" will not be a part of the program whenever the
- ; declaration following "stack:" is ds.l 0.
- end
-
- PRG_3AP Execution Results
-
- Program Size by Method One: 728 bytes.
- Program Size by Method Two: 728 bytes.
-
-
- Discussion of Program 9
-
- Notice that I have declared a stack for the program. I
- have done so because I want you to be able to view the
- contents of the stack in the AssemPro debugger. Further,
- notice that it is of a size that is commensurate with
- necessity. Of course you can declare a stack size that
- represents the wildest possible guess, but there is no
- reason to do so.
- The system functions, GEMDOS $8 and GEMDOS $9 are
- adequately covered in the Abacus Internals book, however,
- there are two things I must say about the code for function
- $9 as it appears in that book. If a program in which this
- function is invoked is to be assembled in PC-relative mode,
- the address of the string must be pushed onto the stack with
- the pea instruction; the move.l #label, -(sp) instruction
- can be used if the program is to be assembled in Relocatable
- mode. Also, note that text is an AssemPro reserved word, so
- don't use it as a label.
- There is a word of caution to be observed when using
- GEMDOS function $9 as a subroutine. To do this, you might
- be tempted to push the address of the string onto the stack
- before each call to the subroutine. That will not work
- because the stack and its pointer will be altered by the
- call to the subroutine; the return address will be pushed
- onto the stack after your string address. The way to use
- GEMDOS function $9 as a subroutine is as follows: change the
- first function statement to pea (An), where An is any
- address register (other than A7, of course); then, just
- before you call the subroutine to print a string, use the
- instruction, lea string, An.
- My discussion of PRG_3AP will proceed much more
- smoothly if I refer to real addresses and data. Therefore,
- I will use a partial disassembly of the program's basepage,
- the front end of the program and the program stack, as it
- appeared in the AssemPro debugger when I executed the
- program. You can perform your own observations after
- assembling PRG_3AP and saving it with a TOS or PRG
- extension. Load it into the debugger with the Execute
- program function.
- You can obtain your own disassembly listing by clicking
- on the Disassembling option under the Debugger menu. Figure
- 4.1, a drawing of the Disassembling dialog box, illustrates
- the appearance of the dialog box fields, just before you
- click on the OK button. If you would rather have a hard
- copy, then click on PRT: instead of File :, but sending a
- disassembly listing directly to a printer is hazardous
- because the listing usually contains ASCII codes that will
- disrupt the printer. Furthermore, you must be cautious when
- you decide to use the File option also.
- If you have chosen to check the Back-up copy option, as
- illustrated in figure 2.4B of chapter two, then, if you now
- choose a file name for the disassembly listing that is
- already in use on disk, you will be presented with the File
- already exists ! dialog box shown in figure 2.2C of chapter
- two. But no matter which option you choose the system will
- freeze, and you will have to reboot. Avoid the use of
- duplicate file names when using AssemPro's Disassembly mode.
- If you choose a file in which AssemPro can save the
- disassembly listing, you can easily remove the ASCII codes
- that disrupt the printer from the file with the Tempus
- editor's Search and replace function, thereby assuring
- smooth printer operation when you print the file. After
- AssemPro has completed the disassembly, load the file into
- the Tempus editor.
- First, delete the Atari Logo from the first line of the
- listing. Then, on the Search below string: parameter line
- of the Search and replace dialog box, type ;*. Type nothing
- on the ...and replace by following parameter line. Select
- whole text as the search region, Start search at cursor,
- Direction down, Quantity total. When you click the Start
- button in the dialog box, Tempus will discard the semicolon
- and all characters after it on all lines containing same;
- this action will remove all characters which might cause the
- printer to scream and beg for mercy.
-
- Figure 4.1. The Disassembling dialog box; a slightly
- compressed representation. The from and to address that you
- use must be obtained from your debugger window. WARNING: Do
- not use a file name that is already in use on disk.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- The from address field requires the address from which
- disassembly is to begin; in this case, the basepage start
- address, calculated by subtracting $100 from the program
- start address, as it is shown in the debugger window. The
- to address field requires the address at which disassembly
- is to cease; neither the to address nor its content will be
- disassembled.
- To determine a suitable to address, you must have some
- knowledge about the last address you want disassembled.
- Fortunately, we have established a program_end address,
- therefore, we simply look for the value being loaded into
- register A4 (after consulting out source listing) as the
- first instruction in the calculate_size_2 algorithm. On my
- debugger screen, this value was $86080.
- Now, we think a little. If we want to see the
- program_end address and the longword contents at that
- address in the disassembly listing, then we must use an
- address beyond that one for the to address. We can simply
- add four to $86080 to obtain $86084 as the to address.
- Alternately, if we decide that we don't need the program_end
- address to show up on the listing, we can simply use it as
- the to address. Actually, that is the second benefit we
- derive from our use of the program_end label.
- Figure 4.2 is a listing of the first 28 bytes of
- PRG_3AP's basepage. The first address shown, $085DA8, is
- the basepage start address. This is the address that is
- copied from 4(A7) to A5 as the first step in the
- calculate_size_1 algorithm. The basepage start address is
- just $100 bytes before the program start address. The
- contents of the basepage start address is itself, $00085DA8.
- Within the calculate_size_1 algorithm, the basepage length,
- $100 bytes, is added to data located at specific offsets
- from the basepage address.
- The text length is the longword located at basepage
- start address + $C = $085DB4; it consumes $000000CE bytes.
- The data length is the longword located at basepage start
- address + $14 = $085DBC; it consumes $0000009A bytes.
- Finally, the bss length is the longword located at basepage
- start address + $1C = $085DC4 and consumes $00000070 bytes.
- The total length accumulated by the first algorithm is $100
- + $CE + $9A + 70 = $2D8 = 728 (decimal) bytes.
-
- Figure 4.2. Partial disassembly of program 9's basepage.
-
- PRG_3AP.TOS disassembly after execution.
-
- 085DA8 0008 DC.W 8
- 085DAA 5DA8000F SUBQ.L #6,$F(A0)
- 085DAE 8000 OR.B D0,D0
- 085DB0 0008 DC.W 8
- 085DB2 5EA80000 ADDQ.L #7,0(A0)
- 085DB6 00CE DC.W $CE
- 085DB8 0008 DC.W 8
- 085DBA 5F760000 SUBQ.W #7,0(A6,D0.W)
- 085DBE 009A00086010 ORI.L #$86010,(A2)+
- 085DC4 00000070 ORI.B #$70,D0
-
-
- The values used to calculate the total length by the
- calculate_size_2 algorithm can be seen in figure 4.3.
- Although there are no labels in that figure to guide us, we
- can determine which addresses contain the data we for which
- we are looking by referring to the program source code.
- As shown at memory address $085EBE, the address that is
- loaded into A4, $86080, is the address of the label
- program_end. The basepage start address is subtracted from
- that address; as shown at memory address $085EC2. The first
- address shown in figure 4.3, 085EA8, is the program start
- address. We can deduce the basepage start address from this
- knowledge because it is $100 less, which is $085DA8.
- A programmer's calculator proves to us that the
- program_end address minus the basepage start address =
- $86080 - $85DA8 = $2D8 = 728 (decimal). This is precisely
- the value obtained by the first algorithm. We conclude that
- the algorithms produce identical results, therefore we will
- choose to use the second algorithm, whenever possible,
- because it is faster and consumes less memory.
-
- Figure 4.3. Disassembled front end of program 9.
-
- 085EA8 2A6F0004 MOVEA.L 4(A7),A5
- 085EAC 2E2D000C MOVE.L $C(A5),D7
- 085EB0 DEAD0014 ADD.L $14(A5),D7
- 085EB4 DEAD001C ADD.L $1C(A5),D7
- 085EB8 DEBC00000100 ADD.L #$100,D7
- 085EBE 49FA01C0 LEA $86080(PC),A4
- 085EC2 99EF0004 SUBA.L 4(A7),A4
- 085EC6 4FFA01B4 LEA $8607C(PC),A7
-
-
- The final item of datum to be extracted from figure 4.3
- is the address of the program stack, shown as the value
- being loaded into register A7 at memory address 085EC6.
- Figure 4.4A is a view of the entire stack. The first
- address in that figure, 08600E, is not part of the stack;
- the first word of data there, 4 zeroes, was inserted by the
- align statement; at address 08010 is the contents of the
- buffer decimal, in which we stored the results computed by
- the binary to decimal conversion subroutine. You can see
- the ASCII value for the leading blank space that indicates a
- positive value, followed by the ASCII values for 728. By
- now, you are aware that the instructions at addresses 08600E
- and 086012, into which those ASCII values just happen to
- translate, are happenstance.
-
- Figure 4.4A. Program 9's stack, disassembled.
-
- NOTE: $86010 is the address of the label "decimal". The
- space reserved for decimal is three longwords. Therefore,
- the last byte of the stack is at address $8601C.
- Apparently, the last word of the stack that was used is at
- address $86040 (The contents there is $0005.). If so, the
- rest of the space declared for the stack is wastage.
-
- 08600E 00002037 ORI.B #$37,D0
- 086012 32380000 MOVE.W 0,D1
- 086016 00000000 ORI.B #0,D0
- 08601A 00000000 ORI.B #0,D0
- 08601E 00000000 ORI.B #0,D0
- 086022 00000000 ORI.B #0,D0
- 086026 00000000 ORI.B #0,D0
- 08602A 00000000 ORI.B #0,D0
- 08602E 00000000 ORI.B #0,D0
- 086032 00000000 ORI.B #0,D0
- 086036 00000000 ORI.B #0,D0
- 08603A 00000000 ORI.B #0,D0
- 08603E 00000005 ORI.B #5,D0
- 086042 F0C2 DC.W $F0C2
- 086044 0310 BTST D1,(A0)
- 086046 0008 DC.W 8
- 086048 0005F0C2 ORI.B #-$3E,D5
- 08604C 0314 BTST D1,(A4)
- 08604E 0008 DC.W 8
- 086050 5F0C SUBQ.B #7,A4
- 086052 00000000 ORI.B #0,D0
- 086056 FFFF DC.W $FFFF
- 086058 FFFF DC.W $FFFF
- 08605A 00000000 ORI.B #0,D0
- 08605E 00000000 ORI.B #0,D0
- 086062 00000000 ORI.B #0,D0
- 086066 00000000 ORI.B #0,D0
- 08606A 000002D8 ORI.B #-$28,D0
- 08606E 0008 DC.W 8
- 086070 607A BRA.S $860EC
- 086072 0008 DC.W 8
- 086074 5FA2 SUBQ.L #7,-(A2)
- 086076 00000000 ORI.B #0,D0
- 08607A 00000000 ORI.B #0,D0
- 08607E 00000000 ORI.B #0,D0
-
- NOTE: Initially, before there is any stack movement,
- $8607C is the top of the stack. The space that was
- reserved with the ds.l 1 following the label "stack" is not
- used because the stack address is always pre-decremented
- before movement to the stack occurs. To avoid that waste,
- you can simply declare ds.l 0 after the label "stack".
-
-
- Now, unless we were to view the stack in the AssemPro
- debugger after single stepping through each instruction (By
- the way, something you don't do is single step through
- system trap calls. Oops. I should have said that I have not
- been successful in doing so.) we can not be certain that the
- entire stack was never utilized by the program. However,
- looking at the memory locations towards the bottom of the
- stack (those nearest address 08601C), we see that the last
- nine longwords of the stack contain zeroes after program
- execution, and these locations appear not to have been used.
- We could test the theory by declaring the stack to be
- 15 longwords instead of 24, then by assembling and executing
- the program again. If our data written to the address of
- the label decimal were to be preserved after that execution,
- we could conclude that a stack length of 15 longwords (60
- bytes) is sufficient for this program. In the references,
- you will often see unreasonably long stacks being declared.
- I simply remind you that a byte wasted is a byte you can't
- use.
- A clearer picture of stack penetration is obtained if
- the variable decimal is loaded with #'s and the stack is
- loaded with !'s before execution. See figure 4.4B. There
- you can see that address $86040 was definitely the last
- stack location used during program execution.
-
- Figure 4.4B. A clear picture of program 9's stack
- penetration.
-
-
-
-
-
-
-
-
-
-
-
-
-
- After modifying program 9's stack declaration to be 15
- longwords, the after-execution picture shown in figure 4.4C
- develops. The first longword of decimal contains the ASCII
- code for the new program length; the second contains a null
- character and 3 #'s; the third contains 4 #'s. In
- preparation for this picture, I also stored ampersands in
- the longword declared following the label stack in order to
- more clearly delineate the 15 longwords of the stack.
- Figure 4.4C clearly shows that this new stack size if
- sufficient.
- Suppose that it were not enough. How could we tell?
- Depending on what values were overwritten into the area
- reserved for decimal, an insufficient stack size might not
- be apparent. Therefore, until you become accustomed to the
- stack requirements of the operating system, it is best to
- start with a large stack and reduce its size after you get
- some idea of the penetration. What is large? Is 512
- longwords sufficiently large? That is 2048 bytes, and it is
- large. When you are truly nervous, use 1024 longwords.
- Perhaps it has occurred to you by now that something has
- been left unsaid. A proper question at this time would be:
- "Why can't we tell what the stack penetration is from the
- program itself?". As you shall see when the supervisor mode
- is discussed in chapter 5, a program's stack is used by
- agents outside of the program also. And the penetration by
- the outside agents is not easily predetermined.
-
- Figure 4.4C. A picture of the reduced stack's
- penetration. Also shown is the longword declared at the
- label stack. It is filled with ampersands.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Using figure 4.4C as a guide, it is easy to see that if
- the stack size were 14 longwords, instead of 15, then the
- data occupying the longword at address $8601C would simply
- be stored at address $86018, and we would notice no
- detrimental effects because the data in decimal does not
- extend far enough to occupy that longword location. In
- fact, even if the stack were only 13 longwords, the program
- would report the correct value for decimal because, although
- stack data would occupy a key byte in the variable, that
- byte of stack data, a null, is identical to the variable
- datum which it would replace.
- So, it is not until the size of the stack is reduced to
- as few as 12 longwords, at which time the leading blank
- space for the value 692 would become null, that the variable
- decimal actually becomes corrupted enough to give an invalid
- value as output data. Of course, if we declare the stack
- space in words or bytes, corruption would take place as soon
- as the null at the end of the ASCII string 692 were replaced
- by the 05 in the stack longword 00 05 F0 C2.
- As the declared space for the stack is reduced further,
- the data in the stack overwrites more of the program's
- declared data. Soon it begins to overwrite the declared
- strings, and the problem becomes readily apparent simply by
- looking at the programs output statements. Although stack
- penetration for this program could never be deep enough to
- completely disrupt the object code, even if a zero stack
- size were declared, it is possible for such to occur when
- the declared stack size if insufficient.
- When that happens, the program may freeze the system,
- bombs may appear on the screen or, if the program is
- executed from the debugger, you may see a Bus error message.
- Whenever you have declared a stack size that is just
- sufficient for maximum penetration, suspect that stack size
- immediately if you see bombs from the desktop, or if the
- system freezes. It is easy enough to declare a large stack
- size while you are experimenting with the program, then to
- reduce the size to something more reasonable after the
- program is functional.
-
- The Binary to ASCII Decimal Conversion Algorithm
-
- To me, it seems reasonable, even mandatory, for a
- beginning assembly language programmer to question the
- necessity of conversion algorithms that convert a number
- from one base to another, and thence to ASCII character
- codes, before printing its value. The answer involves the
- functions that are used for printing and the parameters
- which they expect to be passed to them. The phrases of
- interest can be viewed on pages 107 - 110 of the Internals
- book.
- Within the explanations of the functions discussed on
- those pages, you see phrases such as The ASCII code of the
- pressed key is returned in the low byte of the low word and
- The ASCII value of the character to be printed must be in
- the low byte of the low word. Now, these discussions tell
- us nothing about converting from one base to another, nor do
- they tell us how to produce the ASCII characters that the
- functions find so desirable.
- The example of function $02 usage, on page 108, simply
- indicates that the decimal number 65, passed as a parameter
- will produce the letter A on the video screen. But suppose
- we want to print the number 65? Then we would have to call
- the function twice: once with the ASCII value 54, then
- again, with the ASCII value 53. Using the GEMDOS $02
- function in this manner, we can directly print any character
- that we wish to the screen. However, this usage is of
- minimal value to us.
- We need a way to pass parameters to these functions
- without this direct involvement in the passing of ASCII
- coded parameters. The binary to ASCII decimal conversion
- algorithm relieves us of this burden. The algorithm does
- more than that. When we accumulate a number to be printed,
- the result is stored in some register as a binary value. We
- could choose to print this binary value, if we so desired.
- However, we would still need to convert each binary digit to
- its ASCII equivalent before passing it to the output
- function.
- Doing that, we would end up with a long string of ones
- and zeroes on the screen. We don't really want that; we
- want to see something much easier to interpret. That's why
- we want to convert the number from binary to decimal. Well,
- the conversion algorithm does that also. In a two-stage
- process, the algorithm first converts the binary number to
- decimal digits, then it converts the decimal digits to ASCII
- characters.
- The conversion algorithm is amply documented within the
- program listing, so I shan't repeat the information here. I
- will say that this binary to ASCII decimal algorithm is not
- the one you will see most often in the references.
- Therefore, this algorithm will be compared to the one you
- are most likely to see in an upcoming program.
- I find the fact that all of this repetitive conversion
- is still required on modern computers to be disturbing. Yet
- there is little that we can do about it at this time, except
- to develop conversion algorithms that execute as rapidly as
- possible, while consuming as little memory as possible. In
- this spirit, I offer a modification to the bin_to_dec
- algorithm that reduces the execution time.
- Change the statement moveq #0,d2 (just before the
- get_sign label) to move.b #$30,d2. This new statement
- initializes the subtractions counter to the ASCII value for
- decimal 0. When the counter is incremented for a valid
- subtraction, the ASCII code in D2 will be valid for the
- accumulated count. If the number passed to the subroutine
- is zero, then the ASCII code for that value will already be
- in D2 when the branch is made to the zero_passed label;
- therefore, you can change the second statement at that label
- from move.b #$30,(a0)+ to move.b d2,(a0)+.
- In the loop_setup section of the algorithm, change the
- moveq #-1, d2 statement to move.b #$2F,d2 to initialize the
- subtractions counter to 1 less than the ASCII code for
- decimal 0. Delete the label convert_to_ascii: and the
- instruction addi.b #$30,d2 immediately following it. This
- conversion is no longer necessary because it is the ASCII
- code for the decimal digits that is being accumulated in D2.
-
- The Programs That Use GEMDOS Function $4A
-
- At times, and remember that I consider it to be the
- most powerful available for any computer, the behavior of
- the ST's operating system resembles that of a newborn child
- which requires burping after a feeding. This seems to be
- the role of GEMDOS function $4A (aka m_shrink, setblock and
- etc.). Any program, except those that are desk accessories
- and those that relinquish processor control with GEMDOS
- function $31, which does not begin with an initialization
- sequence that invokes this function ties up all available
- memory.
- That this is a default situation is appalling. Could
- we not rely on the loader to calculate the memory occupied
- by a program that it has just loaded and to assign to that
- program the memory required for its occupancy? The subject
- of ST memory management is adequately covered in the
- Internals book, and I have no reason to repeat its
- information. Frankly, I would find the task depressing
- because the part of the operating system that performs this
- function is so damn inefficient.
- I have included program 10 as a well documented example
- which features the use of function $4A. You should read the
- MAJOR NOTE included in the program before attempting to
- assemble and execute the program. To verify that this
- program functions correctly, do this: load PRG_3BP.TOS into
- the AssemPro debugger using the Execute program function;
- try to select the Save screen function--AssemPro will tell
- you that there is insufficient room in memory; install a
- breakpoint at the lea $C(A7),SP instruction and click on the
- Run program button; when execution stops at the breakpoint,
- you will now be able to select the Save screen function
- because the invocation of GEMDOS function $4A will have
- released all memory not occupied by the program.
-
-
- Program 10. A program that initializes with GEMDOS function
- $4A.
-
- ; Program Name: PRG_3BP.S
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a TOS extension.
-
- ; Program Function:
-
- ; Illustrates the use of GEMDOS function $4A to return to GEMDOS all
- ; memory except that required by this program.
-
- ; Execution Instructions:
-
- ; Double click on PRG_3BP.TOS from the desktop. After viewing the
- ; program's output, press the Return key to terminate execution.
-
- ; Because base page information is needed by this program, if it is to
- ; be executed in the AssemPro debugger, it must be loaded with the
- ; "Execute program" function.
-
- ; MAJOR NOTE:
-
- ; Whenever a program which processes base page information is to be
- ; executed in the AssemPro debugger, special attention must be paid to the
- ; process by which the program is loaded into the debugger.
-
- ; If the program is loaded into the debugger with the "Execute program"
- ; function, the entire program can be executed.
-
- ; If, however, the program is loaded into the debugger as a result of
- ; just having been assembled, the instructions which process basepage
- ; information cannot be executed because there is no basepage when a program
- ; is loaded by that process.
-
- ; For this particular program you would procede as follows: relocate the
- ; program by clicking on the Relocate button, then execute one, and only
- ; one, of the initialization instructions. That is instruction four of the
- ; program:
- ;
- ; lea stack, a7
-
- ; To accomplish this, move the PC cursor in the disassembly output field
- ; to the fourth instruction, and execute the instruction by single stepping.
- ; Then, move the PC cursor to the line containing the label "mainline".
- ; Now, execution may proceed via the "Run program" or "Single step" buttons.
-
- ; A program that used a default stack provided by the system would
- ; require only that the PC cursor be moved beyond the initialization
- ; sequence, which includes the return_memory algorithm.
-
- calculate_program_size:
- lea program_end, a0 ; Put "end of program" address in A0.
- movea.l 4(a7), a1 ; Put basepage address in A1.
- suba.l a1, a0 ; Subtract basepage address from program's
- ; ending address.
- lea stack, a7 ; Point A7 to this program's stack.
-
- return_memory: ; Return unused memory to operating system.
- move.l a0, -(sp) ; Store total program length in stack.
- move.l a1, -(sp) ; Store basepage address in stack.
- move.l #$4A0000, -(sp) ; Function = m_shrink = GEMDOS $4A0000.
- trap #1 ; GEMDOS call.
- lea $C(a7), sp ; Reset stack pointer to top of stack.
-
- ; Note: When the stack pointer must be moved 8 bytes or less, use
- ; addq.l #n, sp, where n is the number of bytes. When it must
- ; be moved more than 8 bytes, addq.l can't be used.
-
- ; Although you may be tempted to use adda.l #n, sp in that case, a
- ; better choice is lea n(a7), sp because it is twice as fast and
- ; requires 2 bytes less of memory. Program LEA_ADDA.TOS verifies
- ; these claims.
-
- mainline: ; Marks the beginning of program proper.
- lea newline, a0
- bsr.s print_string
-
- ; The above instructions prevent damage to the debugger screen and skip a
- ; line for printer output. Skipping a line on the printer separates the
- ; program output from a listing which precedes execution, or it will
- ; separate the results of repeated executions.
-
- print_declared_string:
- lea string, a0 ; Put address of the label "string" in A0.
- bsr.s print_string
-
- wait_for_keypress:
- move.w #8, -(sp) ; Function = c_necin = GEMDOS $8.
- trap #1
- addq.l #2, sp
-
- terminate:
- move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
- trap #1
-
- ; SUBROUTINES
-
- print_string: ; Expects address of string to be in A0.
- pea (a0) ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1
- addq.l #6, sp
- rts
-
- data
- newline: dc.b $D,$A,0 ; All strings must be NULL terminated because the
- ; function we are using to print them requires it.
-
- ; Note that the ASCII code for a NULL character is $0,
- ; which is equal to decimal 0.
-
- ; The ASCII code for a carriage return is $D; that for
- ; a linefeed is $A.
-
- string: dc.b 'This string will not overwrite the AssemPro debugger screen.'
- dc.b $D,$A,0 ; The string is continued on this line. Here, we
- ; declare a carriage return, linefeed and terminate
- ; the entire string with a NULL = $0 = 0.
-
- bss
- align ; Align storage on a word boundary.
- ds.l 16 ; Stack.
- stack: ds.l 0 ; Address of stack.
- program_end: ds.l 0 ; Marks the end of program memory.
- end
-
-
- Repeated Subtraction Versus Repeated Division
-
- The binary to ASCII decimal conversion algorithm that
- you are most likely to see in references is based on
- repeated division by decimal 10. Since I advocate the use
- of a repeated subtraction algorithm, similar to the one
- introduced in program 9, it seems reasonable to expect me to
- present a program that justifies my impudence (that's ud
- back there, not ot).
- Program 11 is a two-part program that verifies the
- identical accuracy of two binary to ASCII decimal conversion
- algorithms in the first part, then goes on to compare their
- execution speeds. The time required to execute each
- algorithm is too short for accurate measurement, therefore,
- each algorithm is executed 1000 times. The times reported
- by the program are the elapsed times for those 1000
- executions.
-
- The Get_time Algorithm
-
- I think that you shall find the program sufficiently
- documented, but the get_time subroutine deserves special
- attention. As you read about GEMDOS function $20 on pages
- 117 - 118 of the Internals book, compare the code there to
- that in program 11, and you will see that I did not find it
- necessary to save the original value of the supervisor stack
- pointer. This is so because the content of D0 was not
- altered between the two function $20 calls which are
- required.
- As you study other GEMDOS functions, you will see one
- labeled Get Time (GEMDOS function $2C), and you may wonder
- why that function was not used in this program. Then,
- later, you will probable run across the BIOS function
- Tickcal and wonder what it has to do with time, if anything.
- Well, the answers are simple. GEMDOS function $2C has a
- resolution of 2 seconds, which is too low for accurate
- execution speed measurement. As promising as the Tickcal
- function might appear to be at first sight, it actually does
- nothing but return the value 20 milliseconds every time you
- call it.
- Fortunately, we are able to access an ST system
- variable at memory location $4BA. This variable is called
- _hz_200, and it is described in the Internals book as being
- the Counter for 200 Hz system clock; in the Peel book it is
- described as being the Raw 200Hz timer tick. We gain access
- to this system variable by invoking GEMDOS function $20 to
- place the microprocessor in the supervisor mode. Refer to
- the subroutine within program 11 for details of my get_time
- algorithm's operation.
-
- Program 11. Comparison of two binary to ASCII decimal
- conversion algorithms.
-
- ; Program Name: PRG_3CP.S
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a TOS extension.
-
- ; Execution Instructions:
-
- ; Execute from the desktop. Terminate execution by pressing the Return
- ; key.
-
- ; Function:
-
- ; This program is divided into two parts. Part 1 verifies that the
- ; results from two binary to ASCII decimal conversion algorithms are
- ; identical. The first conversion algorithm is called the "repeated
- ; division" method; the number to be converted is repeatedly divided by 10.
- ; The second algorithm is called the "repeated subtraction" method; powers
- ; of ten are repeatedly subtracted from the number to be converted.
-
- ; Part 2 compares the two algorithms to determine which is the faster.
-
- ; Part 1 contains three sections. Section 1 confirms that both algorithms
- ; yield the same result for a positive number; section 2 confirms identical
- ; results for a negative number; section 3 confirms identical results for
- ; the number zero.
-
- ; A FEW NOTES:
-
- ; 1 - "clr.w Dn" is one of the fastest ways to clear only the lower word
- ; of a data register.
-
- ; 2 - The stack used in the program is small enough to permit easy access
- ; to its contents via the AssemPro debugger.
-
- ; 3 - In the "repeated division" algorithm, a null character must be stored
- ; in the array "reversed" for proper operation of the
- ; "reversed_to_decimal" loop when the program is executed from AssemPro.
- ; This is true because AssemPro will not necessarily clear the array to
- ; zeroes.
-
- ; When the program is executed from the desktop, however, the operating
- ; system will clear the array. Therefore, if the program were intended
- ; for use such as it is, the instruction which stores the null at the
- ; end of the array "reversed" could be eliminated.
-
- ; This is only one of the many types of adjustments
- ; which must be made when executing programs from debuggers.
-
- calculate_program_size:
- lea program_end, a0 ; Put "end of program" address in A0.
- movea.l 4(a7), a1 ; Put "basepage" address in A1.
- suba.l a1, a0 ; Subtract basepage address from A0.
- lea stack, a7 ; Point A7 to this program's stack.
-
- return_memory: ; Return unused memory to operating system.
- pea (a0) ; Store total program length in stack.
- pea (a1) ; Store basepage address in stack.
- move.l #$4A0000, -(sp) ; Function = m_shrink = GEMDOS $4A.
-
- ; NOTE: The above instruction is a combination of two that are often seen
- ; in references:
-
- ; move.w d0, -(sp) ; Dummy value, can be anything.
- ; move.w #$4a, -(sp) ; Function = m_shrink = GEMDOS $4A.
-
- trap #1 ; GEMDOS call.
- lea $C(sp), sp ; Reset stack pointer to top of stack.
-
- mainline: ; Marks the beginning of program proper.
- lea heading, a0 ; Print heading for program's output.
- bsr print_string
-
- ;
- ; PART 1, SECTION 1: Conversion of a positive binary number to ASCII decimal.
- ;
-
- lea part_1_head, a0 ; Print PART 1 heading.
- bsr print_string
- lea sect_1_head, a0 ; Print SECTION 1 heading.
- bsr print_string
-
- repeated_division_1:
- lea division_head, a0 ; Print heading for division results.
- bsr print_string
- move.l #2147483647, d1 ; Number to be converted must be in D1.
- bsr bin_to_dec_1 ; ASCII decimal string stored in "decimal".
- lea decimal, a0 ; Print ASCII decimal string.
- bsr print_string
-
- ; NOTE: Remember, although the number we store in D1 appears to our eyes
- ; to be a very familiar decimal number, the computer does not see
- ; it that way. It is the assembler that lets us see things that
- ; are palatable to us while we are programming, and which, during
- ; assembly, converts that which we like to something the computer
- ; likes. And the computer likes binary.
-
- repeated_subtraction_1:
- lea subtract_head, a0 ; Print heading for subtraction results.
- bsr print_string
- move.l #2147483647, d1 ; Number to be converted must be in D1.
- bsr bin_to_dec_2 ; ASCII decimal string stored in "decimal".
- lea decimal, a0 ; Print ASCII decimal string.
- bsr print_string
- bsr print_newlines
-
- ;
- ; PART 1, SECTION 2: Conversion of a negative binary number to ASCII decimal.
- ;
-
- lea sect_2_head, a0 ; Print SECTION 2 heading.
- bsr print_string
-
- repeated_division_2:
- lea division_head, a0 ; Print heading for division results.
- bsr print_string
- move.l #-7483647, d1
- bsr bin_to_dec_1
- lea decimal, a0
- bsr print_string
-
- repeated_subtraction_2:
- lea subtract_head, a0 ; Print heading for subtraction results.
- bsr print_string
- move.l #-7483647, d1
- bsr bin_to_dec_2
- lea decimal, a0
- bsr print_string
- bsr print_newlines
-
- ;
- ; PART 1, SECTION 3: Conversion of binary number zero to ASCII decimal.
- ;
-
- lea sect_3_head, a0 ; Print SECTION 3 heading.
- bsr print_string
-
- repeated_division_3:
- lea division_head, a0 ; Print heading for division results.
- bsr print_string
- move.l #0, d1
- bsr bin_to_dec_1
- lea decimal, a0
- bsr print_string
-
- repeated_subtraction_3:
- lea subtract_head, a0 ; Print heading for subtraction results.
- bsr print_string
- move.l #0, d1
- bsr bin_to_dec_2
- lea decimal, a0
- bsr print_string
- bsr print_newlines
-
- ;
- ; PART 2: Repeated division algorithm versus repeated subtraction algorithm
- ; execution speed comparision. Each algorithm is executed 1000 times.
- ;
-
- lea part_2_head, a0 ; Print PART 2 heading.
- bsr print_string
-
- repeated_division_method:
- lea div_time_head, a0
- bsr print_string
- move.l #9999, d7 ; D7 is counter for the push loop.
- bsr get_time ; Value of system clock returned in D5.
- move.l d5, d6 ; Save time in scratch register.
- division_loop:
- move.l #1928374650, d1 ; Number to be converted to ASCII decimal.
- bsr bin_to_dec_1
- dbra d7, division_loop ; Loop 1000 times.
- bsr get_time ; Get current value of system clock.
- sub.l d6, d5 ; Subtract beginning value from ending value.
- mulu #5, d5 ; Convert to milliseconds.
- move.l d5, d1 ; Transfer value for conversion to ASCII decimal.
- bsr bin_to_dec_2 ; Convert.
- lea decimal, a0 ; Print repeated division algorithm time.
- bsr.s print_string
- lea units_label, a0 ; Print time label.
- bsr.s print_string
-
- repeated_subtraction_method:
- lea sub_time_head, a0
- bsr.s print_string
- move.l #9999, d7 ; D7 is counter for the push loop.
- bsr get_time ; Value of system clock returned in D5.
- move.l d5, d6 ; Save time in scratch register.
- subtraction_loop:
- move.l #1928374650, d1 ; Number to be converted to ASCII decimal.
- bsr bin_to_dec_2
- dbra d7, subtraction_loop; Loop 1000 times.
- bsr get_time ; Get current value of system clock.
- sub.l d6, d5 ; Subtract beginning value from ending value.
- mulu #5, d5 ; Convert to milliseconds.
- move.l d5, d1 ; Transfer value for conversion to ASCII decimal.
- bsr bin_to_dec_2 ; Convert.
- lea decimal, a0 ; Print repeated division algorithm time.
- bsr.s print_string
- lea units_label, a0 ; Print time label.
- bsr.s print_string
-
- wait_for_keypress:
- move.w #8, -(sp) ; Function = c_necin = GEMDOS $8.
- trap #1 ; GEMDOS call.
- addq.l #2, sp ; Reposition stack pointer at top of stack.
-
- terminate:
- move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
- trap #1 ; GEMDOS call.
-
- ;
- ; SUBROUTINES
- ;
-
- print_string: ; Expects address of string to be in A0.
- pea (a0) ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp ; Reposition stack pointer.
- rts
-
- print_newlines: ; Prints 2 carriage returns and linefeeds.
- pea newlines ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp
- rts
-
- ; Get_time subroutine. Returns current value of system clock in D5.
-
- ; The get_time subroutine obtains the current value of the system variable
- ; _hz_200 (memory location $4BA). This variable is incremented every 200
- ; hertz, which means that the period between increments is 5 milliseconds
- ; (1/200 = .005). In turn, this means that the variable measures time with
- ; a resolution of 5 milliseconds.
-
- ; Use this subroutine to measure the elapsed time of an event as follows:
-
- ; 1. Read and store the content of $4BA at the beginning of the event.
- ; 2. At the conclusion of the event, read the content of $4BA again.
- ; 3. Subtract the first value from the second. This yields the number of
- ; 5 millisecond intervals which occurred during the event.
- ; 4. Multiply the difference by 5 to convert the elapsed time to milliseconds.
-
- get_time: ; Get number of 5 msec periods.
- move.l #0, -(sp) ; The zero turns on supervisor mode.
- move.w #$20, -(sp) ; Function = super = GEMDOS $20.
- trap #1 ; Go to supervisor mode.
- addq.l #6, sp ; Supervisor stack pointer returned in D0.
- move.l $4BA, d5 ; Copy system time into register D5
- move.l d0, -(sp) ; Restore supervisor stack pointer.
- move.w #$20, -(sp) ; Function = super = GEMDOS $20.
- trap #1 ; Go to user mode.
- addq.l #6, sp ; Reset stack pointer to top of stack.
- rts
-
- ; The binary to ASCII decimal conversion subroutine uses an algorithm
- ; based on the "repeated division" algorithm discussed in chapter 9 of the
- ; Ford & Topp book; however, the algorithm used here is not limited to a
- ; 16-bit binary number. There is a similar algorithm in the Atari section
- ; of appendix B in the Skinner book. The divisor is decimal 10.
-
- bin_to_dec_1: ; Converts 32-bit binary number in D1 to
- ; ASCII decimal.
- lea decimal, a0 ; Point to beginning of array "decimal".
- lea reversed + 10, a1 ; Point to end of array "reversed".
- move.b #0, (a1) ; Put a null at the end of the array.
- _get_sign:
- tst.l d1 ; Is binary number positive, negative or zero?
- beq.s _zero_passed ; Branch if number is 0.
- bpl.s _positive ; Branch if positive.
- move.b #$2D, (a0)+ ; Store a minus sign in array decimal.
- neg.l d1 ; Change number from negative to positive.
- bra.s _division_loop
- _positive: ; Branch to here when number is positive.
- move.b #$20, (a0)+ ; Store a space in array decimal.
- _division_loop:
- move.w d1, d2 ; Store lower word in temp register D2.
- clr.w d1 ; Clear lower word.
- swap d1 ; Move higher word to lower word.
- divu #10, d1 ; Divide full 32 bits by ten.
- move.w d1, d3 ; Store quotient in temp register D3.
- move.w d2, d1 ; Combine lower word with remainder.
- divu #10, d1 ; Divide full 32 bits by ten.
- swap d1 ; Swap quotient and remainder words.
- _convert_to_ascii: ; Convert digit to ASCII and store it.
- addi.b #$30, d1 ; Convert digit to ASCII.
- move.b d1, -(a1) ; Store the digit in array "reversed".
- move.w d3, d1 ; Bring in higher word quotient.
- swap d1 ; Swap high and low word quotients.
- tst.l d1 ; Is content of D1 zero yet?
- bne.s _division_loop ; Continue until content of D1 is zero.
- reversed_to_decimal: ; Transfer contents of "reversed" to "decimal".
- move.b (a1)+, (a0)+ ; Loop until the null is transfered.
- bne.s reversed_to_decimal
- rts
- _zero_passed:
- move.b #$20, (a0)+ ; Store a space in array "decimal".
- move.b #$30, (a0)+ ; Store the zero in array "decimal".
- move.b #0, (a0) ; Terminate the decimal string with a null.
- rts
-
- ; Conversion from binary to ASCII decimal using repeated subtraction.
- ; See documentation in program PRG_3AP.S.
-
- bin_to_dec_2:
- lea decimal, a0 ; Put address of array "decimal" in A0.
- lea subtrahend, a1 ; Put address of subtrahend table in A1.
- move.l (a1)+, d0 ; Put first subtrahend in D0.
- move.b #$30, d2 ; Initialize subtractions counter to ASCII zero.
- get_sign:
- tst.l d1 ; Is binary number positive, negative or zero?
- beq.s zero_passed ; Branch if number is 0.
- bpl.s positive ; Branch if positive.
- move.b #$2D, (a0)+ ; Store a minus sign in array "decimal".
- neg.l d1 ; Change binary number from neg to pos.
- bra.s discard_leading_zeroes
- positive: ; Branch to here when number is positive.
- move.b #$20, (a0)+ ; Store a space in array decimal.
- discard_leading_zeroes: ; Subtract subtrahend from minuend.
- sub.l d0, d1 ; Loop till difference is positive,
- bpl.s subtract ; indicating that digit is not zero.
- add.l d0, d1 ; Restore minuend.
- move.l (a1)+, d0 ; Get next subtrahend.
- bra.s discard_leading_zeroes
- subtract:
- addq.b #1, d2 ; Increment subtractions counter.
- sub.l d0, d1 ; Subtract subtrahend from D1.
- bpl.s subtract ; Loop until D1 becomes negative.
- move.b d2, (a0)+ ; Store the ASCII digit in array "decimal".
- loop_setup:
- add.l d0, d1 ; Restore the minuend.
- move.b #$2F, d2 ; Pre-initialize subtractions counter to $30-1.
- move.l (a1)+, d0 ; Get next subtrahend.
- bne.s subtract ; Loop back until subtrahend = 0.
- move.b #0, (a0) ; Terminate decimal string with a null.
- rts
- zero_passed:
- move.b #$20, (a0)+ ; Store a space in array "decimal".
- move.b d2, (a0)+ ; Store an ASCII zero in array "decimal".
- move.b #0, (a0) ; Terminate ASCII decimal string with a null.
- rts
-
- data
- subtrahend: dc.l $3B9ACA00,$5F5E100,$989680,$F4240,$186A0,$2710,$3E8
- dc.l $64,$A,$1,$0
- heading: dc.b 'PRG_3CP Execution Results',$D,$A,$D,$A,0
- part_1_head: dc.b ' Part 1: Conversion Verification',$D,$A,$D,$A,0
- sect_1_head: dc.b ' Section 1: Positive Number Conversion',$D,$A,$D,$A,0
- sect_2_head: dc.b ' Section 2: Negative Number Conversion',$D,$A,$D,$A,0
- sect_3_head: dc.b ' Section 3: Converson for Zero',$D,$A,$D,$A,0
- division_head: dc.b ' Decimal value by repeated division method: ',0
- subtract_head: dc.b $D,$A
- dc.b ' Decimal value by repeated subtraction method: ',0
- part_2_head: dc.b ' Part 2: Execution Speed Results',$D,$A,$D,$A,0
- time_head: dc.b ' Times for 1000 executions of each conversion method.',0
- div_time_head: dc.b ' Elapsed time for repeated division method: ',0
- sub_time_head: dc.b ' Elapsed time for repeated subtraction method: ',0
- units_label: dc.b ' milliseconds',$D,$A,0
- newlines: dc.b $D,$A,$D,$A,0
- bss
- align ; Align storage on a word boundary.
- reversed: ds.l 3 ; Temp buffer for the repeated division method.
- decimal: ds.l 3 ; Output buffer, must be NULL terminated.
- ds.l 24 ; Program stack, short enough for examination.
- stack: ds.l 0 ; Address of program stack.
- program_end: ds.l 0 ; Marks the end of program memory.
- end
-
-
- PRG_3CP Execution Results
-
- Part 1: Conversion Verification
-
- Section 1: Positive Number Conversion
-
- Decimal value by repeated division method: 2147483647
- Decimal value by repeated subtraction method: 2147483647
-
- Section 2: Negative Number Conversion
-
- Decimal value by repeated division method: -7483647
- Decimal value by repeated subtraction method: -7483647
-
- Section 3: Converson for Zero
-
- Decimal value by repeated division method: 0
- Decimal value by repeated subtraction method: 0
-
- Part 2: Execution Speed Results
-
- Elapsed time for repeated division method: 4760 milliseconds
- Elapsed time for repeated subtraction method: 2440 milliseconds
-
-
- The repeated division algorithm is much slower than the
- repeated subtraction algorithm just because it uses repeated
- division. Each division consumes 140 clock periods, but
- each subtraction consumes only 8 clock periods. I should
- mention that the elapsed time for the repeated division
- method can be reduced to 4695 milliseconds with the
- following modifications to the algorithm: insert move.w
- #10,d4 just before the _get_sign label, then change the two
- divu #10,d1 instructions to divu d4,d1.
- With these alterations, each division is 4 clock
- periods faster. The speed of the repeated division
- algorithm can be increased a little more by making the same
- changes for the ASCII code conversion as was suggested for
- the repeated subtraction algorithm (and implemented in
- program 11). I did not make the alterations discussed in
- this paragraph because I wanted you to see the repeated
- division algorithm as it most often appears in references.
-
- Load and Stay Resident Programs
-
- If only because of precedence, we have come to regard a
- particular type of program execution cycle as normal. To
- initiate this cycle, a user performs some action that causes
- the operating system to load a program into memory and give
- processor control to the program. For some finite length of
- time, the program is in control of some subset of the
- computer's total capacity. Eventually, the program
- accomplishes is designed task and returns processor control
- to the operating system. The operating system then removes
- the program from memory either by actually clearing the part
- of memory occupied by the program or simply by "forgetting"
- that it is there.
- But there is a type of program which does not follow
- the normal execution cycle. The execution of programs in
- this class may be initiated by the operating system, a user
- or another program. One characteristic of these programs
- that differentiates them from normal programs is their
- length of residency in memory; once loaded, they are not
- usually removed for the life of the power-up cycle. In
- addition, their execution need not proceed linearly; that
- is, once loaded into memory, it may be that only a portion
- of the program's instructions will be executed immediately
- and the balance may be executed at a later time, or, as is
- sometimes the case, a portion of the program may never be
- executed.
- Some references refer to this class of programs, in the
- most general sense, as terminate and stay resident (TSR)
- programs. I prefer the more accurate descriptive load and
- stay resident (LSR). The word terminate most naturally
- connotes end or finish; but, as you shall see, programs of
- this class need not terminate during the life of the power-
- up cycle.
-
- GEMDOS Function $31
-
- The easiest way to establish a program as type LSR is
- to exit the program with GEMDOS function $31 instead of
- function $0. For a complete discussion of function $31,
- please refer to pages 121-122 of the Internals book. In
- short, this function is used to relinquish processor
- control, but, simultaneously, it tells the operating system
- to keep the program in memory in such a manner that the
- program may seize, or be given, control of the processor at
- some later time.
- At the time that function $31 is executed, it is
- possible, and probable, that the program has not completed,
- or even initiated its intended function or functions. Most
- likely, at some time during the power-up cycle, the program
- will be given control of the processor so that it can
- perform the function for which it was designed. In the same
- manner that we do not think of the operating system as
- terminating when it relinquishes control to a program, we
- need not consider a program to have terminated when it
- executes function $31.
- Program 12 illustrates the use of GEMDOS function $31
- to establish permanent memory residency. When executed from
- the desktop, this program will be loaded into memory, where
- it will remain until the computer is powered down. At the
- onset of execution, the program prints its starting address
- and ending address on the video screen, then waits for a
- keypress so that you can write them down.
- After you record the addresses, press the Return key.
- When the desktop appears, execute ASSEMPRO.PRG and go to the
- debugger. Click on the from address button and type the
- first address (program_start) on the from address parameter
- line. The program's first instruction will appear as the
- first line in the output field. There you will see the
- second address (program_end) being loaded into register A3.
- Use this method of obtaining the location of an LSR
- program in memory whenever you wish to examine it via the
- debugger or whenever you want to obtain a disassembly
- listing of this type of program, especially after certain
- portions, or the entire program has been executed. After
- you gain sufficient knowledge, you will be able to attach
- appropriate instructions to LSR programs written by others
- so that you can locate them in memory with the debugger.
-
- Program 12. Establishing a LSR program with GEMDOS
- function $31. Note the warning given.
-
- ; Program Name: PRG_4AP.S
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a TOS suffix.
-
- ; Program Function:
-
- ; This program simply establishes itself in memory as a Load and Stay
- ; Resident (LSR) program and prints its location to the video screen. The
- ; addresses printed are the program start address (not the basepage address)
- ; and the program end address.
-
- ; Program Purpose:
-
- ; To illustrate the role of GEMDOS function $31 in establishing a
- ; program as LSR.
-
- ; Execution Instructions:
-
- ; Execute the program from the desktop. When the addresses appear on
- ; the screen, write them down. Terminate execution by pressing the Return
- ; key. In the AssemPro debugger, go to the first address using the
- ; "from address" function. On the first line in the output field, you
- ; should see the second address (program_end) being loaded into register A3.
-
- ; Once this program has been executed, it will remain in ram memory until
- ; the computer is rebooted.
-
- ; WARNING: If this program or any other LSR program is executed from within
- ; the AssemPro debugger, upon exit from AssemPro, the operating
- ; system will not be able to clear the program from memory.
-
- ; Because the program will be residing in an area of memory that
- ; was controlled by AssemPro, the environment will be corrupted.
- ; You can confirm this by trying to reexecute AssemPro after you
- ; exit. You will receive a Bus error message.
-
- ; Furthermore, you will not be able to assemble a program until
- ; you reset the system.
-
- ; There is only one thing you can do if you execute a LSR program
- ; from within AssemPro; you must reset the system by turning it
- ; completely off, then back on. You can try a warm reset, but
- ; all bets are off if you do that.
-
- program_start: ; Calculate program size and retain result.
- lea program_end, a3 ; Fetch address of last memory location occupied
- movea.l a3, a4 ; by the program. Copy into scratch register.
- suba.l 4(a7), a3 ; Subtract basepage address from program end
- ; address. After this, the basepage address
- ; is no longer needed in this program.
- fetch_stack_address:
- lea stack, a7
-
- print_memory_locations:
- lea load_message, a0
- bsr.s print_string
- lea program_start, a0
- move.l a0, d1 ; Transfer to D1 for binary to ASCII
- ; hexadecimal conversion.
-
- ; Note: Above, must load address into an address register first, then move
- ; to the data register for binary to hexadecimal conversion, in order to
- ; permit PC-relative assembly.
-
- bsr.s bin_to_hex ; bin_to_hex expects binary number in D1.
- lea hexadecimal, a0 ; Print the hexadecimal string.
- bsr.s print_string
- lea separator, a0 ; Print a separator between addresses.
- bsr.s print_string
- move.l a4, d1 ; Program end address is in scratch register.
- bsr.s bin_to_hex
- lea hexadecimal, a0
- bsr.s print_string
- bsr.s print_newline
-
- wait_for_keypress: ; Give time to write down memory addresses.
- move.w #8, -(sp) ; Function = c_necin = GEMDOS $8.
- trap #1 ; GEMDOS call.
- addq.l #2, sp
-
- ; Note: GEMDOS function $31 doesn't need the basepage address.
-
- relinquish_processor_control: ; Maintain memory residency.
- move.w #0, -(sp) ; See page 121 of Internals book.
- move.l a3, -(sp) ; Program size.
- move.w #$31, -(sp) ; Function = ptermres = GEMDOS $31.
- trap #1
-
- ;
- ; SUBROUTINES
- ;
-
- ; The binary to ASCII hexadecimal conversion routine expects a number to be
- ; passed as a longword in register D1. Beginning with the most significant
- ; nibble (a nibble = four bits), each nibble is converted to its ASCII
- ; hexadecimal equivalent and stored in "hexadecimal", a null terminated
- ; buffer. Maximum size of the binary number is 32 bits = 8 nibbles.
-
- ; The algorithm discards leading zeroes.
-
- ; The conversion from binary nibble to hex digit is accomplished by
- ; extracting the character in the hex table that is located at the position
- ; defined by the decimal value of the nibble. For example, if the nibble
- ; is "1111", the decimal value is 15; the 15th element of the hex table is
- ; the letter F. The location in the table is specified by an offset from
- ; the address of the first character of the table, which is stored in A1.
- ; The value of the offset is stored in register D0. The addressing mode
- ; used to locate the appropriate table entry is "address register indirect
- ; with offset".
-
- bin_to_hex: ; Expects binary number in D1.
- lea hexadecimal, a0 ; A0 is pointer to array "hexadecimal".
- tst.l d1 ; Test for contents = 0.
- beq.s zero_passed ; Branch if number is 0.
- lea hex_table, a1 ; A1 is pointer to array "hex_table".
- lea hex_table, a1 ; A1 is pointer to array "hex_table".
- moveq #7, d2 ; D2 is the loop counter for 8 nibbles.
-
- discard_leading_zeroes:
- rol.l #4, d1 ; Rotate most significant nibble to the
- ; least significant nibble position.
- move.b d1, d0 ; Copy least significant byte of D1 to D0.
- andi.b #$F, d0 ; Mask out most significant nibble of D0.
- bne.s store_digit ; Branch and store if not leading zero.
- dbra d2, discard_leading_zeroes
- continue:
- rol.l #4, d1 ; Rotate most significant nibble.
- move.b d1, d0 ; Copy least significant byte of D1 to D0.
- andi.b #$F, d0 ; Mask out most significant nibble of D0.
- store_digit:
- move.b 0(a1,d0.w), (a0)+ ; Store ASCII hexadecimal digit in buffer.
- dbra d2, continue ; Continue looping until D2 = -1.
- move.b #0, (a0) ; Terminate hexadecimal string with a null.
- rts
- zero_passed:
- move.b #$30, (a0)+ ; Store an ASCII zero in "hexadecimal".
- move.b #0, (a0) ; Terminate ASCII hexadecimal string with null.
- lea hexadecimal, a0
- rts
-
- print_string: ; Expects address of string to be in A0.
- pea (a0) ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp ; Reset stack pointer to top of stack.
- rts
-
- print_newline: ; Prints a carriage return and linefeed.
- pea newline ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp
- rts
-
- data
- hex_table: dc.b '0123456789ABCDEF'
- newline: dc.b $D,$A,0
- load_message: dc.b 'Installing PRG_4AP between hex addresses: ',0
- separator: dc.b ' - ',0
- align
- bss
- hexadecimal: ds.l 3 ; Output buffer. Must be NULL terminated.
- ds.l 16
- stack: ds.l 1
- program_end: ds.l 0
- end
-
-
- Program 12 Execution Results: The addresses you see will probably be very
- different.
-
- Installing PRG_4AP between hex addresses: 1CB10 - 1CC56
-
-
- As you can see, a new number conversion algorithm is
- introduced in program 12. The addresses printed by the
- program must be shown in ASCII hexadecimal so that they
- match the system in which addresses are shown in the
- debugger output field. Since the conversion from binary to
- hexadecimal is so simple the algorithm is not at all
- complex.
- The binary number passed to the subroutine is composed
- of eight nibbles, each nibble representing a four binary
- digit hexadecimal digit. Beginning with the most
- significant nibble, each nibble is rotated, in turn, to the
- least significant position and transferred to another
- register as the least significant nibble of a byte of data.
- The least significant byte of the new register is masked
- with $0F so that it can be used to fetch the least
- significant nibble's ASCII character from an array of
- hexadecimal characters. Masking is required because the
- smallest portion of data that can be transferred from one
- data register to another is one byte. But the upper nibble
- of that byte corrupts the element of data needed to access
- characters in the table. Therefore, we render that upper
- nibble impotent by anding it with 0.
- You will find similar algorithms in many of the
- references. These algorithms will also show you how to use
- the movem.l instruction to save the content of registers
- used by the subroutine whenever you find that to be
- desirable. I try to avoid that by judicious register
- control and variable assignments in my programs.
-
- Confirming the Validity of the Conversion Algorithm
-
- The limited use of the binary to ASCII hexadecimal
- algorithm in program 12 did not prove its validity with a
- variety of binary values. Program 14 was written to
- exercise the algorithm to at least some pertinent degree of
- confidence. But program 14 will not function until program
- 13 is installed as a LSR program. Therefore, program 14
- will follow program 13.
- Program 13 is designed to provide user traps for
- algorithms that will be used in many of the programs to be
- introduced. Invoking traps for the most often used routines
- will permit those programs to be smaller and less intricate.
- Some execution speed will be sacrificed, but the advantages
- gained compensate for the loss. In your own programs you
- will have to decide when the compromises are acceptable.
-
- Program 13. Using GEMDOS Function $31 to Install Custom
- Traps.
-
- ; Program Name: TRAPS.S
- ; Version 1.005
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a PRG extension.
-
- ; Program Function:
-
- ; This is a LSR program that establishes user defined traps. It may be
- ; executed from the desktop, but you may prefer to copy it to the AUTO
- ; folder of your boot partition or floppy disk so that it will execute
- ; automatically during boot.
-
- ; NOTE: If the program has been loaded into memory during boot, the
- ; operating system will not let you remove it from the AUTO folder.
-
- ; After an attempt is made to remove the file, an Alert box will
- ; appear with the message:
-
- ; ! An item with this name already exists in the
- ; directory, or this item is set to Read-only status.
-
- ; To remove a program from the AUTO folder after this message appears,
- ; click once on the file's icon, then select Show Info under the File menu.
- ; Change the filename's extension from PRG to PRX. The next time the
- ; system is booted you will be able to remove the file.
-
- ; The best method of dealing with programs in the AUTO folder and with
- ; desk accessories is via a program that permits you to select or deselect
- ; them from a displayed list at boot. I use MichTron's STSELECT, which
- ; is one of 21 utilities in their STuff package. When you deselect an AUTO
- ; folder program, STSELECT changes the program's suffix to PRX. The
- ; suffix change prevents the program from being executed until its suffix
- ; is altered manually, or via STSELECT, to PRG.
-
- ; The STSELECT utility is especially useful when you are not sure that
- ; a program in the AUTO folder or a desk accessory will execute properly.
- ; A malfunction in one of these programs at boot will prevent the system
- ; from booting, but will not prevent it from trying to boot, thereby putting
- ; the system in an infinite loop from which it cannot escape. With STSELECT
- ; you won't have to worry about that problem. If one of the programs disrupts
- ; the boot process, simply deselect it when STSELECT displays its list.
-
- ; The custom traps are installed so that the algorithms here will not
- ; have to be repeated in all of the programs in the book that use them, not
- ; because they offer any other advantages. I want you to know this because,
- ; in general, I prefer in-line code to the invocation of traps, except when
- ; they offer a clear cut speed advantage to in-line code, as does the
- ; custom trap #0. This trap is a significant improvement over GEMDOS $20
- ; when forcing the processor into the supervisor mode.
-
- program_start: ; Calculate program size and retain result.
- lea program_end, a3 ; Fetch program end address.
- suba.l 4(a7), a3 ; Subtract basepage address.
-
- enter_supervisor_mode:
- move.l #0, -(sp) ; The zero turns on supervisor mode.
- move.w #$20, -(sp) ; Function = super = GEMDOS $20.
- trap #1 ; Go to supervisor mode.
- addq.l #6, sp ; Supervisor stack pointer (SSP) returned in D0.
- movea.l d0, a5 ; Save SSP in scratch register.
-
- ; NOTE: Because the processor is now in supervisor mode, the system trap
- ; vectors may be referenced directly; that is, loading the starting
- ; value $80 in an address register and referencing all trap vectors
- ; as an offset to the starting value using "address register indirect
- ; with displacement addressing" is not really necessary. I am doing
- ; that only to show how it is done when it is necessary. A faster
- ; method is shown in the program CUSTOM.S.
-
- install_trap_0_routine: ; Trap #0 = set supervisor mode.
- lea $80, a0 ; Fetch trap #0 vector address.
- lea trap_0_routine, a1 ; Fetch custom trap #0 vector.
- move.l a1, (a0) ; Store custom trap #0 vector.
-
- install_trap_3_routine: ; Trap #3 = get time (contents of $4BA).
- lea trap_3_routine, a1 ; Fetch custom trap #3 vector.
- move.l a1, $C(a0) ; Store custom trap #3 vector.
-
- install_trap_4_routine: ; Trap #4 = convert binary number in D1 to
- lea trap_4_routine, a1 ; ASCII decimal, suppressing leading zeroes.
- move.l a1, $10(a0) ; Returns address of decimal string in A0.
-
- install_trap_5_routine: ; Trap #5 = convert binary number in D1 to
- lea trap_5_routine, a1 ; ASCII hexadecimal, suppressing leading zeroes.
- move.l a1, $14(a0) ; Returns address of hexadecimal string in A0.
-
- install_trap_6_routine: ; Trap #6 accepts a program's end address in
- lea trap_6_routine, a1 ; A0 and basepage address in A1. It computes
- move.l a1, $18(a0) ; the program size and returns unused memory.
-
- install_trap_7_routine: ; Trap #7 = convert binary number in D1 to
- lea trap_7_routine, a1 ; ASCII hexadecimal, retaining leading zeroes.
- move.l a1, $1C(a0) ; Returns address of hexadecimal string in A0.
-
- install_trap_8_routine: ; Terminate with GEMDOS $0 after a keypress
- lea trap_8_routine, a1 ; if program was not spawned by SPEEDTST.TTP,
- move.l a1, $20(a0) ; else terminate with GEMDOS $4C.
-
- enter_user_mode:
- pea (a5) ; Restore supervisor stack pointer.
- move.w #$20, -(sp) ; Function = super = GEMDOS $20.
- trap #1 ; Go to user mode.
- addq.l #6, sp ; Reset stack pointer to top of stack.
-
- relinquish_processor_control: ; Maintain memory residency.
- move.w #0, -(sp) ; See page 121 of Internals book.
- move.l a3, -(sp) ; Program size.
- move.w #$31, -(sp) ; Function = ptermres = GEMDOS $31.
- trap #1
-
- trap_0_routine: ; Sets bit 13 of status register.
-
- ; When trap #0 is invoked, the CPU pushes the calling program's return
- ; address onto the supervisor stack, then it pushes the calling program's
- ; status register contents onto the supervisor stack.
-
- ; This custom trap #0 handler sets bit #13 of the calling program's status
- ; register. The bset instruction can be used to set a bit in the first
- ; byte of the word on the top of the stack. Bit #13 of the status register
- ; is bit #5 of that byte. Note: to set a bit means to make it equal to 1.
-
- ; When the rte instruction is executed, the CPU will return to the calling
- ; program with the altered copy of its status register, and the calling
- ; program will now be executing in supervisor mode.
-
- ; Register A7 will contain the address of the supervisor stack. The calling
- ; program now has complete control of the system. But a decision about
- ; which stack is to be used must be made.
-
- ; The calling program can choose to continue with A7 pointing to the
- ; supervisor stack, or it can save the contents of A7 = SSP and load the
- ; address of a user stack into A7. Processing can continue in that manner
- ; until it is necessary to return to user mode. The return to user mode
- ; can be accomplished by reloading A7 with the SSP, then by resetting bit
- ; #13 of the status register. Note: resetting a bit means to make it equal
- ; to 0.
-
- bset #5, (sp) ; Places calling program in supervisor mode.
- rte
-
- trap_3_routine: ; Returns contents of $4BA in D0.
-
- ; The get_time subroutine obtains the current value of the system variable
- ; _hz_200 (memory location $4BA). This variable is incremented every 200
- ; hertz, which means that the period between increments is 5 milliseconds
- ; (1/200 = .005). In turn, this means that the variable measures time with
- ; a resolution of 5 milliseconds.
-
- ; Use this subroutine to measure the elapsed time of an event as follows:
-
- ; 1. Read and store the content of $4BA at the beginning of the event.
- ; 2. At the conclusion of the event, read the content of $4BA again.
- ; 3. Subtract the first value from the second. This yields the number of
- ; 5 millisecond intervals which occurred during the event.
- ; 4. Multiply the difference by 5 to convert the elapsed time to milliseconds.
-
- move.l $4BA, d0
- rte
-
- trap_4_routine: ; Binary to ASCII decimal conversion.
-
- ; This is a binary to ASCII decimal conversion subroutine. It converts a
- ; 32-bit binary number in register D1 to an ASCII string that is stored in
- ; an array called "decimal".
-
- ; The subroutine uses an algorithm based on "repeated subtraction". The
- ; subtrahends are powers of ten which range from 10^1 to 10^9. These values
- ; were chosen to accommodate up to ten digit integers. The binary value
- ; passed to the subroutine is the initial current minuend. After a decimal
- ; digit is extracted from a current minuend, a new current minuend is
- ; produced.
-
- ; During the subtraction process, the minuend is gradually reduced to a
- ; negative number. The number of times that each power of ten can be
- ; subtracted from a current minuend before the minuend becomes negative
- ; yields a significant digit for that power of ten.
-
- ; After the current minuend becomes negative a "new" minuend is formed by
- ; adding the current subtrahend to the negative value. The process
- ; continues until the current subtrahend is 0.
-
- ; Register D2 is the subtractions counter. In it is accumulated the number
- ; of times that a subtrahend can be subtracted from a minuend before the
- ; minuend becomes negative. At the start of the subroutine D2 is initialized
- ; to the ASCII code for decimal zero, therefore, as the first digit is
- ; extracted, it is the digit's ASCII code which is accumulated in D2.
-
- ; But observe that D2 is initialized to $2F in the section of the routine
- ; labeled "loop_setup". The reason: after registers are initialized in the
- ; loop_setup section, execution branches to the section labeled "subtract".
- ; The first instruction in that section adds 1 to the value in D2.
-
- ; I chose to add 1 to the subtractions counter at that particular location
- ; because it was a convenient point in the algorithm to do so upon exit from
- ; the "discard leading zeroes" loop. Upon exit from that loop, a valid
- ; subtraction has already been performed, therefore, it must be accumulated.
- ; Since D2 contains the initialized value $30 at that point in the execution,
- ; adding 1 puts the correct count in D2.
-
- ; Because I want to branch to the same section upon exit from the loop setup
- ; section, I must pre-initialize D2 to $2F, which is one less than $30.
- ; Then, after 1 is added to D2 via the first instruction in the subtract
- ; section, the subtractions counter is fully initialized to $30, as it
- ; should be.
-
- ; As each decimal digit is extracted from D1, its ASCII code is accumulated
- ; as part of a string in the array called "decimal".
-
- ; The algorithm discards leading zeroes using a loop that subtracts
- ; subtrahends from the number in D1, beginning with the largest, until a
- ; subtrahend which does immediately yield a negative difference is detected.
-
- bin_to_dec_2:
- lea decimal, a0 ; Put address of array "decimal" in A0.
- lea subtrahend, a1 ; Put address of subtrahend table in A1.
- move.l (a1)+, d0 ; Put first subtrahend in D0.
- move.b #$30, d2 ; Initialize subtractions counter to ASCII zero.
- get_sign:
- tst.l d1 ; Is binary number positive, negative or zero?
- beq.s zero_passed ; Branch if number is 0.
- bpl.s positive ; Branch if positive.
- move.b #$2D, (a0)+ ; Store a minus sign in array "decimal".
- neg.l d1 ; Change binary number from neg to pos.
- bra.s discard_leading_zeroes
- positive: ; Branch to here when number is positive.
- move.b #$20, (a0)+ ; Store a space in array decimal.
- discard_leading_zeroes: ; Subtract subtrahend from minuend.
- sub.l d0, d1 ; Loop till difference is positive,
- bpl.s subtract ; indicating that digit is not zero.
- add.l d0, d1 ; Restore minuend.
- move.l (a1)+, d0 ; Get next subtrahend.
- bra.s discard_leading_zeroes
- subtract:
- addq.b #1, d2 ; Increment subtractions counter.
- sub.l d0, d1 ; Subtract subtrahend from D1.
- bpl.s subtract ; Loop until D1 becomes negative.
- move.b d2, (a0)+ ; Store the ASCII digit in array "decimal".
- loop_setup:
- add.l d0, d1 ; Restore the minuend.
- move.b #$2F, d2 ; Pre-initialize subtractions counter to $30-1.
- move.l (a1)+, d0 ; Get next subtrahend.
- bne.s subtract ; Loop back until subtrahend = 0.
- move.b #0, (a0) ; Terminate decimal string with a null.
- lea decimal, a0
- rte
- zero_passed:
- move.b #$20, (a0)+ ; Store a space in array "decimal".
- move.b d2, (a0)+ ; Store an ASCII zero in array "decimal".
- move.b #0, (a0) ; Terminate ASCII decimal string with a null.
- lea decimal, a0
- rte
-
- trap_5_routine:
-
- ; The binary to ASCII hexadecimal conversion routine expects a number to be
- ; passed as a longword in register D1. Beginning with the most significant
- ; nibble (a nibble = four bits), each nibble is converted to its ASCII
- ; hexadecimal equivalent and stored in "hexadecimal", a null terminated
- ; buffer. Maximum size of the binary number is 32 bits = 8 nibbles.
-
- ; The algorithm discards leading zeroes.
-
- ; The conversion from binary nibble to hex digit is accomplished by
- ; extracting the character in the hex table that is located at the position
- ; defined by the decimal value of the nibble. For example, if the nibble
- ; is "1111", the decimal value is 15; the 15th element of the hex table is
- ; the letter F. The location in the table is specified by an offset from
- ; the address of the first character of the table, which is stored in A1.
- ; The value of the offset is stored in register D0. The addressing mode
- ; used to locate the appropriate table entry is "address register indirect
- ; with offset".
-
- bin_to_hex:
- lea hexadecimal, a0 ; A0 is pointer to array "hexadecimal".
- tst.l d1 ; Test for contents = 0.
- beq.s _zero_passed ; Branch if number is 0.
- lea hex_table, a1 ; A1 is pointer to array "hex_table".
- moveq #7, d2 ; D2 is the loop counter for 8 nibbles.
- omit_leading_zeroes:
- rol.l #4, d1 ; Rotate most significant nibble to the
- ; least significant nibble position.
- move.b d1, d0 ; Copy least significant byte of D1 to D0.
- andi.b #$F, d0 ; Mask out most significant nibble of D0.
- bne.s store_digit ; Branch if digit is not zero.
- dbra d2, omit_leading_zeroes
- continue:
- rol.l #4, d1 ; Rotate most significant nibble.
- move.b d1, d0 ; Copy least significant byte of D1 to D0.
- andi.b #$F, d0 ; Mask out most significant nibble of D0.
- store_digit:
- move.b 0(a1,d0.w), (a0)+ ; Store ASCII hexadecimal digit in buffer.
- dbra d2, continue ; Continue looping until D2 = -1.
- move.b #0, (a0) ; Terminate hexadecimal string with a null.
- lea hexadecimal, a0
- rte
- _zero_passed:
- move.b #$30, (a0)+ ; Store an ASCII zero in "hexadecimal".
- move.b #0, (a0) ; Terminate ASCII hexadecimal string with null.
- lea hexadecimal, a0
- rte
-
- trap_6_routine:
-
- ; Expects program_end address in A0 and basepage address in A1.
-
- ; This routine does several things. First, using the values passed in
- ; registers A0 and A1, it calculates the invoking program's size and
- ; returns excess memory to the operating system.
-
- ; Then it checks to see if the process invoking trap #6 was spawned. If
- ; it was, then a variable is set to true; if not, then the variable is set
- ; to false. In addition, if the process was spawned, the after_load time
- ; is saved in a variable so that it can be returned by trap #8.
-
- calculate_program_size:
- suba.l a1, a0
- prepare_stack_to_return_unused_memory:
- pea (a0) ; Push program length.
- pea (a1) ; Push basepage address.
- move.l #$4A0000, -(sp) ; Function = m_shrink = GEMDOS $4A.
-
- spawned_test:
- lea spawned, a0 ; Fetch boolean variable's address.
- move.l $2C(a1), a1 ; Fetch environmental string address.
- cmpi.l #'TERM', (a1) ; Compare environmental string to "TERM".
- seq (a0) ; Set spawned to FF if program was spawned.
- bne.s return_memory ; Branch if "TERM" string not present.
- save_after_load_time:
- lea after_load_time, a0 ; Fetch variable's address.
- move.w d0, (a0)
- return_memory:
- trap #1 ; Invoke GEMDOS exception.
- lea $C(a7), sp ; Reset stack pointer to top of stack.
- rte
-
- trap_7_routine:
-
- ; The binary to ASCII hexadecimal conversion routine expects a number to be
- ; passed as a longword in register D1. Beginning with the most significant
- ; nibble (a nibble = four bits), each nibble is converted to its ASCII
- ; hexadecimal equivalent and stored in "hexadecimal", a null terminated
- ; buffer. The leading zeroes are retained in this routine.
-
- bin_to_hex_with_zeroes:
- lea hexadecimal, a0 ; A0 is pointer to array "hexadecimal".
- tst.l d1 ; Test for contents = 0.
- beq.s _zero__passed ; Branch if number is 0.
- lea hex_table, a1 ; A1 is pointer to array "hex_table".
- moveq #7, d2 ; D2 is the loop counter.
- moveq #0, d0 ; D0 is not zero. That's proved in a later
- ; program, so it must be cleared before use.
- rotate_and_convert:
- rol.l #4, d1 ; Rotate most significant nibble to the
- ; least significant nibble position.
- move.b d1, d0 ; Copy least significant byte of D1 to D0.
- andi.b #$F, d0 ; Mask out most significant nibble of D0.
- move.b 0(a1,d0.w), (a0)+ ; Store ASCII hexadecimal digit in buffer.
- dbra d2, rotate_and_convert
- move.b #0, (a0) ; Terminate hexadecimal string with a null.
- lea hexadecimal, a0 ; Return address of hex string in A0.
- rte
- _zero__passed:
- move.b #$30, (a0)+ ; Store an ASCII zero in "hexadecimal".
- move.b #0, (a0) ; Terminate ASCII hexadecimal string with null.
- lea hexadecimal, a0
- rte
-
- trap_8_routine:
-
- ; When custom trap #8 is invoked by a program, the state of the boolean
- ; variable "spawned" is tested. If the state is true (equal to FF), the
- ; program invoking trap #8 is terminated with GEMDOS function $4C and the
- ; after-load time, which was saved by custom trap #6, is returned to the
- ; parent program in D0.
-
- ; If the state of "spawned" is false, GEMDOS function $8 is executed so
- ; that execution of the spawned program will pause for a keypress. When
- ; the keypress is received, GEMDOS function $0 is executed.
-
- ; In this manner, custom trap #8, working in conjunction with custom trap
- ; #6, eliminates the "wait for keypress" algorithm automatically when a
- ; program is spawned by one of the speed testing programs. This prevents
- ; the spawned program's computed execution time from being corrupted with
- ; a time period that involves a wait for keyboard input.
-
- lea spawned, a0 ; Fetch address of variable.
- tst.b (a0) ; Spawned test.
- beq.s not_spawned ; Branch if program not spawned.
- lea after_load_time, a0 ; Pass after-load time to parent.
- move.w (a0), -(sp) ; Push after-load time.
- move.w #$4C, -(sp) ; Function = p_term = GEMDOS $4C.
- trap #1 ; Terminate and return to parent.
- not_spawned:
- move.w #8, -(sp) ; Function = c_necin = GEMDOS $8.
- trap #1 ; GEMDOS call.
- addq.l #2, sp ; Reposition stack pointer at top of stack.
- terminate:
- move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
- trap #1 ; Terminate and return to desktop.
-
- data
- subtrahend: dc.l $3B9ACA00,$5F5E100,$989680,$F4240,$186A0,$2710
- dc.l $3E8,$64,$A,$1,$0
- hex_table: dc.b '0123456789ABCDEF'
- spawned: dc.b $0
- bss
- align ; Align storage on a word boundary.
- after_load_time: ds.w 1 ; Spawned program's after load time.
- decimal: ds.l 3 ; Output buffer. Must be NULL terminated.
- hexadecimal: ds.l 3 ; Output buffer. Must be NULL terminated.
- program_end: ds.l 0
- end
-
-
- Program 14. A program to test the validity of the binary to
- hexadecimal algorithm. Simultaneously, this program
- confirms that the algorithm has been installed as a custom
- trap by program 13.
-
- ; Program Name: HEX_TEST.S
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a TOS suffix.
-
- ; Program Function:
-
- ; Used to verify the accuracy of the binary to ASCII hexadecimal
- ; algorithm used in PRG_4AP.TOS and TRAPS.PRG. The hexadecimal algorithm
- ; is invoked with a trap #5 instruction.
-
- ; Execution Instructions:
-
- ; Execute from the desktop. Compare the inputs to the conversion
- ; algorithm to its outputs. Press the Return key to terminate the program.
-
- ; NOTE: The custom traps must be resident when this program is executed.
- ; This means that program TRAPS.PRG must be executed from the AUTO
- ; folder or from the desktop before program HEX_TEST.TOS is executed.
-
- release_excess_memory:
- lea program_end, a0 ; Put "end of program" address in A0.
- movea.l 4(a7), a1 ; Put "basepage" address in A1.
- trap #6 ; Calculate program size and release memory.
- lea stack, a7 ; Point A7 to this program's stack.
-
- mainline: ; Marks the beginning of program proper.
- lea heading, a0 ; Print heading for program's output.
- bsr.s print_string
-
- test_1:
- lea string_1, a3
- lea value_1, a4
- bsr.s test_routine
-
- test_2:
- lea string_2, a3
- lea value_2, a4
- bsr.s test_routine
-
- test_3:
- lea string_3, a3
- lea value_3, a4
- bsr.s test_routine
-
- test_4:
- lea string_4, a3
- lea value_4, a4
- bsr.s test_routine
-
- wait_for_keypress:
- move.w #8, -(sp) ; Function = c_necin = GEMDOS $8.
- trap #1 ; GEMDOS call.
- addq.l #2, sp ; Reposition stack pointer at top of stack.
-
- terminate:
- move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
- trap #1 ; GEMDOS call.
-
- ;
- ; SUBROUTINES
- ;
-
- test_routine:
- lea input_msg, a0
- bsr.s print_string
- lea (a3), a0
- bsr.s print_string
- bsr.s print_newline
- lea output_msg, a0
- bsr.s print_string
- move.l (a4), d1
- trap #5 ; Returns address of "hexadecimal" in A0.
- bsr.s print_string
- bsr.s print_newline
- rts
-
- print_string: ; Expects address of string to be in A0.
- pea (a0) ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp ; Reset stack pointer to top of stack.
- rts
-
- print_newline: ; Prints a carriage return and linefeed.
- pea newline ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp
- rts
-
- data
- string_1: dc.b 'ABCDEF01',0
- value_1: dc.l $ABCDEF01
- string_2: dc.b '23456789',0
- value_2: dc.l $23456789
- string_3: dc.b '00FC1000',0
- value_3: dc.l $00FC1000
- string_4: dc.b '00000000',0
- value_4: dc.l $00000000
- newline: dc.b $D,$A,0
- heading: dc.b 'HEX_TEST Execution Results',$D,$A,$D,$A,0
- input_msg: dc.b ' Input to algorithm: $',0
- output_msg: dc.b ' Ouput from algorithm: $',0
- align
- bss
- ds.l 16
- stack: ds.l 1
- program_end: ds.l 0
- end
-
-
- HEX_TEST Execution Results
-
- Input to algorithm: $ABCDEF01
- Ouput from algorithm: $ABCDEF01
- Input to algorithm: $23456789
- Ouput from algorithm: $23456789
- Input to algorithm: $00FC1000
- Ouput from algorithm: $FC1000
- Input to algorithm: $00000000
- Ouput from algorithm: $0
-
-
- Conclusion
-
- As you have seen so far, an initialization sequence for
- ST programs can range from nothing at all to those discussed
- in this chapter, which are not in the least intricate, but
- which can be complicated by an association with the method
- used to relinquish processor control. I shall get to the
- more complicated initialization procedures after suitable
- preparation.
- Program 13 represents an alteration to the operating
- system environment because it establishes algorithms which
- are treated as operating system commands by other
- algorithms. As such, it can be considered to be a set of
- initial conditions which supplements the unblemished
- operating system subenvironment. Indeed, its presence in
- memory is a prerequisite to the execution of program 14.
- Other programs that are executed at boot behave
- similarly to configure the operating system environment, the
- execution environment or both. Such programs may be located
- in the AUTO folder or in the root directory of the boot
- partition or disk. Remember that there are boot programs
- which permit you to choose a set of programs to be executed
- during boot.
-
-