home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-10-23 | 165.8 KB | 4,464 lines |
- Atari ST Machine Specific Programming In Assembly
-
- Chapter 8: Seizing Control of the System
-
- The If Onlies
-
- I think it would be very interesting to read a report
- of experiments conducted to determine the mean time between
- computer system purchase and the first utterance of if
- only... in connection with product design, construction or
- operating system. Some of my favorite ST if onlies are:
-
- 1. If only the operating system were written in
- assembly language instead of C.
-
- 2. If only the cartridge port data lines were
- bidirectional.
-
- 3. If only GDOS were more like G+PLUS.
-
- 4. If only the file selector function were more like
- Universal II.
-
- 5. If only we were not limited to six desk accessories.
-
- If only some manufacturer would design the perfect
- computer system and sell it to me at a price that I could
- afford. Well, that's not likely to happen any time soon,
- say not before hell freezes over; neither are the if onlies
- associated with any computer system likely to ever be
- eliminated. However, to the extent that manufacturers and
- programmers find it profitable to satisfy a majority of
- users, a continuous stream of improvements and corrections,
- in one form or another, is guaranteed. For ST users,
- Universal II (a better file selector), G+PLUS (an improved
- GDOS), MultiDesk (a desk accessory which eliminates the
- operating system limitation of six) and a method of getting
- data out of the cartridge port (See Cartridge Port
- Interface, by Randy Constan, ST LOG, January 1989.) are
- already available.
- In this chapter I shall begin to address the subject of
- if onlies elimination with user designed software. I am not
- going to dwell extensively on aspects of ST system design
- and construction which, in my opinion, or in the opinion of
- others, constitute errors or limitations, because, however
- much I might wish that certain details of that design and
- construction would conform to my own ideas of perfection,
- the fact is that I could not have done a better job than was
- done by the manufacturer. Yet it is possible for me and for
- you to redirect certain operating system activities, and/or
- to design surrogate functions that effectively tailor the
- ST's system controlling software so that it more accurately
- responds to our individual needs or tastes. Therefore, I
- shall offer suggestions that I believe are indicative of the
- methodology involved in altering some aspects of the
- operating system in order to effect subjective improvements.
-
- The Second MC68000 Flaw
-
- I discussed one of what I consider to be two serious
- flaws in the design of the MC68000 microprocessor in chapter
- 3. I was then, as I am now, forced to remind you that the
- qualification of these design characteristics as flaws is my
- response to the limitations imposed upon my programming
- efforts by those characteristics. In chapter 3, I spoke of
- the obstacle to storing data imposed by the addressing
- scheme that rejects pc-relative destination operands. Here
- I am concerned with the limitations imposed by the existence
- of two privilege states. I am not amused by the necessitity
- of having to go through the trouble of forcing the processor
- into the supervisor state in order to access system
- variables. I could speak of the limitations imposed by this
- attribute of the processor for hours; but I have promised
- not to dwell on such unpleasant topics. Instead, I shall
- propose methods of performing the transition between states
- as painlessly as possible. But I want to stress that it is
- an attribute that must be overcome in order to write
- efficient software. Furthermore, in order to illustrate
- that I know that I am not infallible, and that I realize
- that I could be completely wrong about everything that I
- have said concerning flaws in the design of the MC68000, I
- remind you that it is my opinion that these flaws exist.
-
- Normal Processor State Toggling
-
- Without interference from an outside agent, whenever a
- program is executed, the operating system relinquishes
- processor control to that program. During the program's
- execution cycle, it will probably return processor control
- to the operating system many times by invoking system
- functions. During these invocation cycles, through
- operating system directed activities, the functions perform
- services for the program. After each such invocation,
- processor control is returned to the program. In this
- chapter I shall discuss methods of seizing control of system
- functions so that their activities may be redirected by the
- invoking program. In addition, I shall discuss user defined
- functions that act as surrogates for system functions.
- Usually, the surrogate functions will access system
- variables. Those accesses must be performed while the
- processor is in the supervisor state.
-
- Absolute Power Corrupts Absolutely
-
- The first lesson to be learned about seizing control of
- anything is that control is inherently tenuous. Ask any
- deposed dictator. When you seize control of ST functions so
- that you can redirect their activities, or when you seize
- control of the processor state, you can remain in control as
- long as you prevent vital portions of the system from
- becoming corrupted. Here, the word system includes all
- hardware and resident software. I will try to remember to
- tell you as much as possible about maintaining compatibility
- between your applications and others, between your
- applications and the operating system and between your
- applications and hardware. However, I feel it prudent to
- cover my ass now, by saying that, even if I do manage to
- tell you all that I know, it will probably not be enough.
- All that I can really hope to do is to help you develop a
- certain wariness about potential compatibility problems
- between certain types of software/software and
- software/hardware combinations.
- Compounding the complexity of the task we face in
- understanding each other in this chapter is the
- intractability of the terminology required for the
- discussions to follow. This terminology can be perplexing,
- even to the most seasoned programmers. One of the reasons
- that this is so is that discussants can rarely be certain
- that everyone is familiar with the vocabulary involved, and
- even when terminological familiarity is not a problem, the
- congruency of definitions remains questionable. Some of the
- terms and definitions with which we must contend are to be
- found in the references under headings such as exceptions,
- exception vectors, exception handlers/handling, vectors,
- interrupts, interrupt structure, traps and, perhaps, some
- others, depending on the particular vernacular of the
- writers involved.
- My task is to describe required algorithms with
- lubricious phraseology so that you will understand (or at
- least be able to fake an understanding of) what you are
- doing, whether or not your references are comprehensible. I
- suggest that you read as much as you can concerning the
- topics mentioned above, but with particular concentration on
- exceptions caused by the Trap instruction and those caused
- by external hardware. I shall not be discussing exceptions
- involving resets, bus errors, address errors, and tracing.
-
- Accessing System Variables
-
- The ST's operating system variables are accessible only
- when the processor is in the supervisor mode. I pointed
- this out when I introduced the get_time subroutine in
- program 11. You can see a partial listing of system
- variables on pages 236-237 and 250-257 of the Internals
- book. The Peel book also contains a partial listing on
- pages A-2 through A-6. Each book contains some variables
- not listed by the other.
- A dissonance greater than disagreement between
- information contained in books exists, however. Atari does
- not guarantee that system variables located at addresses
- below $400 will not change in future versions of the
- operating system. In chapter 1, I pointed out that
- consumers must realize that the product they receive at the
- time of purchase is the product they should plan to possess,
- until a new purchase is made. There is no reason to expect
- that any alterations to future models of the product will
- have any effect on the model purchased. Certainly, there is
- no reason to expect that any effects will be beneficial.
- Therefore, as long as you are willing to learn to control
- the product you have, instead of anticipating enhancements
- by the manufacturer, you reduce the manufacturer's control
- over you by alleviating the impact of future modifications
- to the product. In retaliation, then, we adopt a pugnacious
- attitude and thumb our noses at malicious attempts to thwart
- our progress via the alteration of variables. We shall work
- with what we have.
- So, I continue with the mutual understanding that there
- is some finite chance, however slim, that operating system
- variables used in this book might not match those of your
- system, and, if such circumstances should occur, you will be
- forced to seek out knowledge about such differences from
- other references or by searching through system memory
- yourself. We do begin with at least one constant. The
- behavior of the MC68000's supervisor mode is not likely to
- change.
- In addition to permitting access to system variables,
- the supervisor mode permits the execution of certain
- instructions which cannot be executed in the user mode.
- Although it may not be immediately obvious, there need be no
- compelling reason not to execute ordinary instructions in
- the supervisor mode. (Instructions which must be executed
- in supervisor mode are called privileged instructions.)
- However, assuring that one is in user mode before exiting a
- program is prudent, to say the least.
-
- Forcing the Processor Into Supervisor Mode
-
- As you will learn from reading the references, there is
- only one method by which the MC68000 can be forced into the
- supervisor state. An exception generating instruction must
- be executed. Exception handlers usually return processor
- control to the exception generating source with the
- processor in the user state. The Atari operating system
- provides GEMDOS function $20 as an orderly way of locking
- the processor state in supervisor mode until the invoking
- program forces the processor back into user mode. Getting
- back into user mode can be accomplished in several ways
- (Refer to Privilege State Changes in the Motorola manual.),
- however, if the supervisor mode has been entered via GEMDOS
- function $20, then the return to user mode is usually
- accomplished via the same function.
- I remind you that the easiest way to force the
- processor into the supervisor state is by executing a trap
- instruction. You have seen this done in the program which
- installs custom traps. Once you are in a trap routine, the
- processor is in supervisor mode and it will remain in that
- state for the duration of the trap invocation unless bit 13
- of the status register is reset to zero. Furthermore, if
- bit 13 of the stack word that is the content of the status
- register, as it was before the trap invocation, is set to
- one, then the invoking program will be executing in
- supervisor mode when the return from exception instruction
- is executed by the trap handler.
-
- Stack Pointer Control
-
- When the processor is toggled between modes, one of the
- most compelling concerns of a programmer is stack pointer
- control. Without external interference, the processor will
- use the supervisor stack when it is in supervisor mode, and
- it will use the user stack when it is in user mode. The
- processor accomplishes this by placing the address of the
- supervisor stack in the supervisor stack pointer (SSP) and
- by placing the address of the user stack in the user stack
- pointer (USP). This is the default stack pointer control.
- Neglecting the issue of desirability, for the moment, it is
- important to realize that GEMDOS $20 transfers the address
- of the user stack to the supervisor stack pointer (SSP) when
- it is used to toggle the processor from the user state to
- the supervisor state. GEMDOS $20 expects that the address
- of the supervisor stack will be saved and returned to it
- when it is invoked to toggle back to user mode. At that
- time the function will reinstate the address from whence it
- came. During all of this, the content of the USP remains
- constant.
- If a programmer decides to force the processor into the
- supervisor state by some other method, perhaps by using a
- custom trap similar to trap #0, which is installed by
- CUSTOM.PRG, then the programmer assumes responsibility for
- stack control. The programmer can chose to continue
- execution with the SSP pointing to the supervisor stack, or,
- as is accomplished via GEMDOS $20, the address of the user
- stack may be loaded into the SSP. In any case, in addition
- to fulfilling the program's stack needs, the active stack
- must be large enough to accommodate all supervisor mode
- activity, which includes, but is not limited to, the storage
- of the program counter and status register during exception
- handling.
- I have designed three programs to assist me during this
- discussion. Program 42 installs a custom trap that
- immediately prints the content of A7 = the address of the
- supervisor stack, then sets bit 13 of the invoking program's
- status register so that the processor will be in the
- supervisor state when processor control is returned to the
- trap invoking program. During the course of its execution,
- program 43 invokes the custom trap installed by program 42.
- In addition, it also prints the content of A7 during other
- stages of its execution. Program 43 is divided into two
- parts. Part one is executed before a user stack is assigned
- within the program. This means that the USP contains the
- address of the system assigned default user stack. The
- content of A7 is printed three times during the execution of
- part one, as follows:
-
- 1. Before the custom trap is invoked.
-
- 2. During trap invocation, by the custom trap.
-
- 3. After trap invocation.
-
- Part two is executed after a user stack has been assigned.
- From that point on, the USP contains the address of the
- program assigned stack. The content of A7 is printed four
- times during the execution of part two, as follows:
-
- 1. After the processor has been toggled to user mode
- and the user stack has been assigned.
-
- 2. During trap invocation, by the custom trap.
-
- 3. After trap invocation.
-
- 4. After the processor has been toggled to user mode.
-
- The structure of program 44 is similar to that of
- program 43, but it does not use the custom trap installed by
- program 42 to force the processor into the supervisor state.
- Instead, it does this by invoking GEMDOS $20. Seven
- addresses are printed by program 44 also, but since I have
- no way to force the printing of A7's content during the
- execution of function GEMDOS $20, the value returned by the
- function in register D0 = address of supervisor stack is
- printed instead. This is not quite equivalent to that which
- is done in program 43 because GEMDOS $20 returns the top
- address of the supervisor stack, whereas the address printed
- during the custom trap invocation is the address to which
- the SSP has been decremented as a result of the generated
- exception. That address is 6 bytes lower than that of the
- top of the supervisor stack, because a return address (4
- bytes) and status register contents (2 bytes) has been
- pushed onto the stack. This means that the custom trap
- prints the current content of the SSP.
-
-
- Program 42. A LSR program that installs custom trap #11.
- This program must be executed before program 43 can be
- executed.
-
- ; Program Name: PRG_6AP.S
- ; Version: 1.002
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a TOS extension.
-
- ; Program Function:
-
- ; This is a LSR program that installs custom trap #11, which is invoked
- ; by PRG_6BP.TOS. This program uses traps installed by CUSTOM.PRG.
-
- 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:
- trap #0 ; Sets bit 13 of status register to 1.
-
- install_trap_11_routine: ; Note: pointer = vector = pointer.
- lea trap_11_routine, a0 ; Fetch address of trap #11 routine.
- move.l a0, $AC ; Store trap address at pointer address.
-
- enter_user_mode:
- andi.w #$DFFF, SR ; Sets bit 13 of status register to 0.
-
- relinquish_processor_control: ; Maintain memory residency.
- move.w #0, -(sp) ; Exit code.
- move.l a3, -(sp) ; Program size.
- move.w #$31, -(sp) ; Function = ptermres = GEMDOS $31.
- trap #1
-
- trap_11_routine:
-
- ; When trap #11 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 #11 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.
-
- print_current_SSP: ; SSP = address of supervisor stack.
- lea header_1, a0
- bsr.s print_string
- move.l a7, d1 ; Convert to ASCII hexadecimal.
- trap #5
- bsr.s print_string
- bsr.s print_newline
- bset #5, (sp) ; The invoking program will be executing in
- rte ; supervisor mode upon return.
-
- ;
- ; SUBROUTINES
- ;
-
- print_string:
- pea (a0)
- move.w #9, -(sp)
- trap #1
- addq.l #6, sp
- rts
-
- print_newline:
- lea newline, a0
- bsr.s print_string
- rts
-
- data
- newline: dc.b $D,$A,0
- header_1: dc.b ' During trap 11 invocation: ',0
- bss
- align
- program_end: ds.l 0
- end
-
-
- Program 43. This program prints the content of register A7
- during various stages of execution. Program 42 must be
- executed prior to the execution of this program.
-
- ; Program Name: PRG_6BP.S
- ; Version: 1.002
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a TOS extension.
-
- ; Execution Instructions:
-
- ; Before this program is executed, trap 11 must be installed via the
- ; execution of program PRG_6AP.TOS. In addition, this program uses traps
- ; installed by CUSTOM.PRG. Execute from the desktop or via SPAWN.TTP.
-
- ; Program Function:
-
- ; Prints the content of register A7 during various stages of execution
- ; while the state of the processor is varied from user mode to supervisor
- ; mode, from supervisor mode back to user mode, from user mode to supervisor
- ; mode a second time and from supervisor mode back to user mode a second time.
-
- ; In this program, custom trap #11 is used to toggle the state of the
- ; processor.
-
- calculate_program_size:
- lea -$102(pc), a1 ; Fetch basepage start address.
- lea program_end, a0 ; Fetch program end address.
- trap #6 ; Return unused memory to op system.
-
- print_heading:
- lea heading, a0
- bsr.s print_string
-
- ; NOTE: During this section of the program, no user stack has yet been
- ; assigned, therefore, the user stack address is that of the default
- ; user stack assigned by the system when execution commences.
-
- print_address_of_default_user_stack:
- lea header_1, a0
- bsr.s print_string
- move.l a7, d1 ; Convert address to ASCII hexadecimal.
- trap #5
- bsr.s print_string
- bsr.s print_newline
-
- invoke_custom_super_mode_trap:
- trap #11 ; Sets bit 13 of SR to 1 and prints SSP.
-
- print_current_SSP: ; SSP = address of supervisor stack.
- lea header_2, a0 ; Did invocation of trap #11 alter the address
- bsr.s print_string ; in the supervisor stack pointer?
- move.l a7, d1 ; Convert to ASCII hexadecimal.
- trap #5
- bsr.s print_string
- bsr.s print_newline
- bsr.s print_newline
-
- enter_user_mode:
- andi.w #$DFFF, SR ; Sets bit 13 of status register to zero.
-
- ; NOTE: During this section of the program, a user stack is assigned. The
- ; user stack address is that of the label "stack".
-
- print_address_of_assigned_user_stack:
- lea header_3, a0
- bsr.s print_string
- lea stack, a7
- move.l a7, d1 ; Convert to ASCII hexadecimal.
- trap #5
- bsr.s print_string
- bsr.s print_newline
-
- invoke_custom_super_mode_trap_again:
- trap #11 ; Sets bit 13 of SR to 1 and prints SSP.
-
- print_current_SSP_again: ; Did invocation of trap #11 alter the address
- lea header_4, a0 ; in the supervisor stack pointer?
- bsr.s print_string
- move.l a7, d1 ; Convert to ASCII hexadecimal.
- trap #5
- bsr.s print_string
- bsr.s print_newline
-
- enter_user_mode_again:
- andi.w #$DFFF, SR ; Sets bit 13 of status register to zero.
-
- print_address_of_assigned_user_stack_again:
- lea header_5, a0
- bsr.s print_string
- move.l a7, d1 ; Convert to ASCII hexadecimal.
- trap #5
- bsr.s print_string
- bsr.s print_newline
-
- terminate:
- trap #8
-
- ;
- ; SUBROUTINES
- ;
-
- print_string:
- pea (a0)
- move.w #9, -(sp)
- trap #1
- addq.l #6, sp
- rts
-
- print_newline:
- lea newline, a0
- bsr.s print_string
- rts
-
- data
- newline: dc.b $D,$A,0
- heading: dc.b 'PRG_6BP Execution Results => Content of A7:',$D,$A,$D,$A,0
- header_1: dc.b ' NO USER STACK ASSIGNED',$D,$A
- dc.b ' Start of program: ',0
- header_2: dc.b ' After trap 11 invocation: ',0
- header_3: dc.b ' USER STACK ASSIGNED',$D,$A
- dc.b ' After force to user mode: ',0
- header_4: dc.b ' After trap 11 invocation: ',0
- header_5: dc.b ' After force to user mode: ',0
- bss
- align
- ds.l 96
- stack: ds.l 0
- program_end: ds.l 0
- end
-
-
- PRG_6BP Execution Results => Content of A7:
-
- NO USER STACK ASSIGNED
- Start of program: F7FF8 = address of system assigned user stack
- During trap 11 invocation: 4DB2 = current content of SSP
- After trap 11 invocation: 4DB8 = address of supervisor stack
-
- USER STACK ASSIGNED
- After force to user mode: 1E254 = address of program assigned user stack
- During trap 11 invocation: 4DB2 = current content of SSP
- After trap 11 invocation: 4DB8 = address of supervisor stack
- After force to user mode: 1E254 = address of user stack
-
-
- Of special interest, in reviewing the data generated by
- program PRG_6BP is the content of A7 after the first and
- second trap 11 invocations. In both cases, the invoking
- program is executing in supervisor mode, therefore, the
- supervisor stack pointer is in use. Note, further, that the
- supervisor stack is active. Any stack activity generated by
- the program will affect that stack, as will any stack
- activity generated by the operating system, therefore, the
- stack must be large enough to accommodate both. If it is
- not, then a system crash is virtually assured. If extensive
- program stack activity is to take place during this period,
- the only way to insure that the active stack is large enough
- is to load the address of a program assigned stack into
- register A7. On the other hand, if the program is to
- generate little or no stack activity, then execution can
- continue with the active supervisor stack.
-
-
- Program 44. This program also prints the content of register
- A7 during various stages of execution, but it does not use
- the custom trap installed by program 43.
-
- ; Program Name: PRG_6CP.S
- ; Version: 1.002
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a TOS extension.
-
- ; Execution Instructions:
-
- ; Execute from the desktop or from SPAWN.TTP. This program uses traps
- ; installed by CUSTOM.PRG.
-
- ; Program Function:
-
- ; Prints the content of register A7 during various stages of execution
- ; while the state of the processor is varied from user mode to supervisor
- ; mode, from supervisor mode back to user mode, from user mode to supervisor
- ; mode a second time and from supervisor mode back to user mode a second time.
-
- ; In this program, GEMDOS function $20 is used to toggle the state of the
- ; processor. Refer to pages 117 and 118 of the Internals book, under the
- ; $20 SUPER heading. Aside from the fundamental methodology of function
- ; invocation, two important items of information are discussed there.
-
- ; 1. GEMDOS $20 transfers the address of the user stack to the supervisor
- ; stack pointer (SSP). This means that while the processor is in the
- ; supervisor state, if that state was forced by invocation of GEMDOS $20,
- ; then the user stack is active and the supervisor stack is inactive. This
- ; means that the user stack must be large enough to accomodate supervisor
- ; state activity. Among other things, this activity includes the storage of
- ; the program counter and status register during exception processing.
-
- ; 2. Invocation of GEMDOS $20 can corrupt, and usually does as far as I can
- ; determine, registers A1 and D1. Information in that section of the Internals
- ; book indicates that only this GEMDOS function can alter the values in those
- ; registers.
-
- calculate_program_size:
- lea -$102(pc), a1 ; Fetch basepage start address.
- lea program_end, a0 ; Fetch program end address.
- trap #6 ; Return unused memory to op system.
-
- print_heading:
- lea heading, a0
- bsr print_string
-
- ; NOTE: During this section of the program, no user stack has yet been
- ; assigned, therefore, the user stack address is that of the default
- ; user stack assigned by the system when execution commences.
-
- print_address_of_default_user_stack:
- lea header_1, a0
- bsr print_string
- move.l a7, d1 ; Convert address to ASCII hexadecimal.
- trap #5
- bsr print_string
- bsr print_newline
-
- invoke_gemdos_super_mode_function:
- move.l #0, -(sp) ; The zero turns on supervisor mode.
- move.w #$20, -(sp) ; Function = super = GEMDOS $20.
- trap #1 ; Supervisor stack pointer (SSP) returned in D0.
- addq.l #6, sp ; SSP = address of supervisor stack.
- movea.l d0, a5 ; Save SSP in scratch register.
-
- print_SSP_returned_by_GEMDOS_20:
- lea header_6, a0
- bsr print_string
- move.l a5, d1 ; Convert to ASCII hexadecimal.
- trap #5
- bsr print_string
- bsr print_newline
-
- print_current_SSP: ; Did GEMDOS $20 alter the address in the
- lea header_2, a0 ; supervisor stack pointer?
- bsr print_string
- move.l a7, d1 ; Convert to ASCII hexadecimal.
- trap #5
- bsr.s print_string
- bsr print_newline
- bsr print_newline
-
- enter_user_mode:
- pea (a5) ; Restore supervisor stack pointer.
- move.w #$20, -(sp) ; Function = super = GEMDOS $20.
- trap #1
- addq.l #6, sp
-
- ; NOTE: During this section of the program, a user stack is assigned. The
- ; user stack address is that of the label "stack".
-
- print_address_of_assigned_user_stack:
- lea header_3, a0
- bsr.s print_string
- lea stack, a7
- move.l a7, d1 ; Convert to ASCII hexadecimal.
- trap #5
- bsr.s print_string
- bsr.s print_newline
-
- invoke_gemdos_function_again:
- move.l #0, -(sp) ; The zero turns on supervisor mode.
- move.w #$20, -(sp) ; Function = super = GEMDOS $20.
- trap #1 ; Supervisor stack pointer (SSP) returned in D0.
- addq.l #6, sp
- movea.l d0, a5 ; Save SSP in scratch register.
-
- print_SSP_returned_by_GEMDOS_20_again:
- lea header_7, a0
- bsr print_string
- move.l a5, d1
- trap #5
- bsr print_string
- bsr print_newline
-
- print_current_SSP_again:
- lea header_4, a0
- bsr.s print_string
- move.l a7, d1 ; Convert to ASCII hexadecimal.
- trap #5
- bsr.s print_string
- bsr.s print_newline
-
- enter_user_mode_again:
- pea (a5) ; Restore supervisor stack pointer.
- move.w #$20, -(sp) ; Function = super = GEMDOS $20.
- trap #1
- addq.l #6, sp
-
- print_address_of_assigned_user_stack_again:
- lea header_5, a0
- bsr.s print_string
- move.l a7, d1 ; Convert to ASCII hexadecimal.
- trap #5
- bsr.s print_string
- bsr.s print_newline
-
- terminate:
- trap #8
-
- ;
- ; SUBROUTINES
- ;
-
- print_string:
- pea (a0)
- move.w #9, -(sp)
- trap #1
- addq.l #6, sp
- rts
-
- print_newline:
- lea newline, a0
- bsr.s print_string
- rts
-
- data
- newline: dc.b $D,$A,0
- heading: dc.b 'PRG_6CP Execution Results => Content of A7:',$D,$A,$D,$A,0
- header_1: dc.b ' NO USER STACK ASSIGNED',$D,$A
- dc.b ' Start of program: ',0
- header_2: dc.b ' After GEMDOS $20 invocation: ',0
- header_6: dc.b ' SSP returned by GEMDOS $20: ',0
- header_3: dc.b ' USER STACK ASSIGNED',$D,$A
- dc.b ' After force to user mode: ',0
- header_4: dc.b ' After GEMDOS $20 invocation: ',0
- header_7: dc.b ' SSP returned by GEMDOS $20: ',0
- header_5: dc.b ' After force to user mode: ',0
- bss
- align
- ds.l 96
- stack: ds.l 0
- program_end: ds.l 0
- end
-
-
- PRG_6CP Execution Results => Content of A7:
-
- NO USER STACK ASSIGNED
- Start of program: F7FF8 = address of system assigned user stack
- SSP returned by GEMDOS $20: 4DB8 = address of supervisor stack
- After GEMDOS $20 invocation: F7FF8 = address of user stack
-
- USER STACK ASSIGNED
- After force to user mode: 1E308 = address of program assigned user stack
- SSP returned by GEMDOS $20: 4DB8 = address of supervisor stack
- After GEMDOS $20 invocation: 1E308 = address of user stack
- After force to user mode: 1E308 = address of user stack
-
-
- The data generated by PRG_6CP illustrates the stack
- address transfer effected by GEMDOS $20. After the function
- invocation, the program is executing in supervisor mode, but
- the supervisor stack pointer does not contain the address of
- the supervisor stack; it contains the address of the user
- stack. During this period, the supervisor stack is inactive
- and the user stack is active. Because the SSP no longer has
- access to the supervisor stack, the address of that stack
- must be restored to register A7 before the processor is
- forced back into user mode, therefore, the value returned in
- D0 by GEMDOS $20, which is that address, must be stored by
- the invoking program. The invoking program can force the
- processor into user mode with GEMDOS $20, passing the
- supervisor stack address as a parameter, or it may simple
- store the address in register A7 and reset bit 13 of the
- status register to zero.
- Since the operating system provides a way to force the
- processor into the supervisor mode, the question of why a
- user defined function to accomplish the same task is
- desirable is certainly relevant. Well, a programmer may
- decide to dispense with the overhead involved when the
- GEMDOS $20 function is invoked. Also, it is not actually
- necessary to design a specific function to place the
- processor in the supervisor state. It enters that state
- whenever an exception occurs. One might choose to redirect
- an exception handler so that, in addition to its normal
- duties, it also returns processor control to the invoking
- program in supervisor mode rather than in user mode.
-
- Altering a System Variable
-
- The next program illustrates the manner in which a
- system variable may be altered while the processor is in
- supervisor mode. The byte length variable to be altered is
- composed of a group of flag bits, each of which controls one
- function. Therefore, the byte is composed of eight
- variables, each of which is one bit long, compressed into
- one byte of data. The address of this byte is $484. Only
- the first four bits of the byte are assigned. The bit
- assignments given in the Internals book is correct; those
- given in the Peel book are not.
- The particular bit to be altered is bit #0, that which
- controls the emission of a clicking sound when a key is
- pressed on the keyboard. I happen to find this noise
- extremely disturbing, therefore, I was very upset when I
- learned that the clicking sound emission was a default
- attribute after power up. I was even more distraught when I
- discovered that the only way I could disable this function
- was by altering a button in the window available via
- CONTROL.ACC, a desk accessory, my version of which consumes
- 20,080 bytes (decimal).
- Program 45 is one of several that accomplish the same
- goal. Each of the programs may be executed from the
- desktop, or they may be executed from the AUTO folder on a
- boot disk. The ability to execute programs of this type
- from the AUTO folder is a powerful convenience, however,
- there is a payment extracted in the form of the
- inconvenience associated with the debugging stage of
- programs that are being prepared for this type of service.
- You should, if at all possible, confirm the bug-free
- operation of programs before placing them in the AUTO
- folder, just as you should verify the validity of desk
- accessories before placing them into service. We have very
- little control over programs that are executed during the
- initial stages of power up, therefore, if a malfunction
- exists, the system may not be able to complete the boot
- cycle because it will be stuck in a mode from which it can't
- escape until the defective program (which can't be executed)
- is executed.
- Fortunately, there are programs available on public
- domain and commercial disks that permit us to deselect desk
- accessories and programs in AUTO folders during the very
- first stages of the boot cycle. From ST Informer, there is
- DESKMNG2.ARC on their PDM disk 688, discussed in the June
- issue of the newspaper. From MichTron, there is STSELECT,
- one of the programs on their STuff disk; this is the
- selector that I use. Using one of these utilities, when you
- discover that your new program is obstructing the boot
- cycle, you can deselect the offending program so that the
- system will stop trying to execute it.
-
- Program 45. Altering a System Variable.
-
- ; Program Name: PRG_6DP.S
- ; Version: 1.001
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a PRG extension.
-
- ; Execution Instructions:
-
- ; Execute from the desktop.
-
- ; Program Function:
-
- ; Turns off keyclick sound. Refer to page 254 of the Internals book. The
- ; system variable at address $484 is a byte length variable. The bits of
- ; this variable have the meanings as indicated in the Internals book. The
- ; bit of interest is #0. When this bit is a one, the computer emits a
- ; click each time a key is pressed. When the bit is a zero, these clicks
- ; are not emitted. A zero is placed in this bit by replacing the content
- ; of the byte at $484 (which is 7 before the replacement, if key click is
- ; enabled) with $6.
-
- ; If the object code for this program has a PRG extension, it may be placed
- ; in the AUTO folder of a boot disk or hard disk partition so that it will
- ; be executed during power up. The reason for doing this would be to
- ; disable the key click without the presence of the desk accessory CONTROL,
- ; thereby conserving the memory required by CONTROL.ACC.
-
- ; Of course, the program need not be executed at power up. It may be
- ; executed from the desktop whenever you wish to do so.
-
- ; Program Purpose:
-
- ; 1. Illustrate the use of GEMDOS function $20.
-
- ; 2. Illustrate the manner in which a system variable may be altered.
-
- ; 3. Show how the key click may be disabled without the presence of
- ; CONTROL.ACC as a desk accessory.
-
- ; 4. Show how to save a few lines of code by not repositioning the stack
- ; pointer after the first GEMDOS $20 call, then by using address register
- ; indirect with displacement addressing to store the SSP within the stack.
-
- mainline:
- lea stack, a7 ; Point A7 to this program's stack.
-
- ; NOTE: For many programs, an extensive initialization routine is not
- ; necessary nor desirable. Here, for instance, since this program
- ; will simply execute, remaining in memory for a very brief period,
- ; and since no other program will be executed in the mean time, there
- ; is no reason to calculate the program's size and return excess
- ; memory to the operating system.
-
- enter_supervisor_mode:
- move.l #0, -(sp) ; The zero turns on supervisor mode.
- move.w #$20, -(sp) ; Function = super = GEMDOS $20.
- trap #1 ; Content of SSP returned in D0.
- move.l d0, 2(sp) ; Store returned value in stack.
- ; Do not reposition stack pointer.
-
- disable_key_click:
- move.b #6, $484 ; Refer to page 254 of the Internals book.
-
- return_to_user_mode: ; Stack is already setup.
- trap #1
- addq.l #6, sp ; Now reposition stack pointer to top.
-
- terminate:
- move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
- trap #1 ; GEMDOS call.
-
- ds.l 24 ; Stack.
- stack: ds.l 0 ; Address of stack.
- program_end: ds.l 0
- end
-
-
- Altering Variables With XBIOS Function $26
-
- Actually, as if anticipating the ST user's
- unwillingness to be content to operate in the user mode,
- Atari has provided us with a method of executing a
- subroutine in supervisor mode without having to bother with
- the transference thereto. The extended bios function
- (XBIOS) $26 (dec 38), called superx or superexec by name,
- accepts the address of an executable block of code that is
- terminated by the rts instruction, and the operating system
- executes that block of code in the supervisor mode. Program
- 46 is an example which illustrates the correct use of the
- superexec function. The example given in the Internals book
- is flawed (See page 203.). The block of code used in the
- example is not executable, and the single instruction
- algorithm is not followed by the rts instruction.
-
- Program 46. Execution in supervisor mode via XBIOS function
- $26. As always, the SSP uses the program's stack after the
- switch to supervisor mode has been accomplished.
-
- ; Program Name: PRG_6EP.S
- ; Version: 1.001
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a PRG extension.
-
- ; Execution Instructions:
-
- ; Execute from the desktop.
-
- ; Program Function:
-
- ; Turns off keyclick sound. Refer to PRG_6DP.S for documentation.
-
- ; Program Purpose:
-
- ; 1. Illustrate the use of XBIOS function $26. This function forces a
- ; subroutine to be executed in the supervisor mode. The subroutine
- ; may be located at any address in memory. Function $26 expects the
- ; address of the subroutine to be on the stack.
-
- ; 2. Point out error in Internals book (The example in the Internals
- ; book is garbage). The "address" which must be pushed on the stack
- ; is the address of a subroutine that is terminated with an RTS
- ; instruction. See page 3-13 of the Peel book.
-
- ; 3. Illustrate the confusion created by writers of reference books when
- ; they are not consistent in their usage of number bases. To wit:
- ; in the Internals book, GEMDOS function numbers are presented in
- ; hexadecimal. The index of these functions are immediately followed
- ; by the BIOS and XBIOS functions, which are numbered in decimal. I
- ; will try remember to use the following notation to express the BIOS
- ; and XBIOS in both number bases so that it will be easier for you to
- ; look them up in the Internals book:
-
- ; Function = superexec = XBIOS $26 (dec 38).
-
- ; Where (dec 38) indicates that the digits 38 are the decimal
- ; equivalent of the hexadecimal number 26.
-
- mainline:
- lea stack, a7 ; Point A7 to this program's stack.
-
- execute_subroutine_in_supervisor_mode:
- pea turn_key_click_off ; Push address of subroutine onto stack.
- move.w #$26, -(sp) ; Function = superexec = XBIOS $26 (dec 38).
- trap #14 ; XBIOS call.
- addq.l #6, sp
-
- terminate:
- move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
- trap #1 ; GEMDOS call.
-
- turn_key_click_off: ; Subroutine to be executed in supervisor
- move.b #6, $484 ; mode.
- rts
-
- ds.l 24 ; Stack.
- stack: ds.l 0 ; Address of stack.
- program_end: ds.l 0
- end
-
-
- Of course, you need a program to toggle the keyclick
- function on so that you can verify the correctness of those
- that turn it off. Program 47 will do that for you. Just
- execute PRG_6FP.PRG from the desktop each time you want to
- active the keyclick function.
-
- Program 47. This program turns on the keyclick sound.
-
- ; Program Name: PRG_6FP.S
- ; Version: 1.001
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a PRG extension.
-
- ; Execution Instructions:
-
- ; Execute from the desktop.
-
- ; Program Function:
-
- ; Turns on keyclick sound so that the programs that turn it off can be
- ; tested. See programs PRG_6DP and PRG_6EP for further documentation.
-
- mainline:
- lea stack, a7 ; Point A7 to this program's stack.
-
- execute_subroutine_in_supervisor_mode:
- pea turn_key_click_on ; Push address of subroutine onto stack.
- move.w #$26, -(sp) ; Function = superexec = XBIOS $26 (dec 38).
- trap #14 ; XBIOS call.
- addq.l #6, sp
-
- terminate:
- move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
- trap #1 ; GEMDOS call.
-
- turn_key_click_on: ; Subroutine to be executed in supervisor
- move.b #7, $484 ; mode.
- rts
-
- ds.l 24 ; Stack.
- stack: ds.l 0 ; Address of stack.
- program_end: ds.l 0
- end
-
-
- The error in the Internals book, concerning the
- construction of the subroutine, is not the only source of
- false information pertaining to the XBIOS function $26
- usage. In another reference, the Peel book (page 3-13), a
- note appended to functions parameter data declares that the
- subroutine must not contain BIOS nor GEMDOS calls.
- Strangely enough, the same note is included in Atari's
- description of the function. Program 48, the function of
- which is to dispel those rumors, is included, for your
- perusal, below.
-
- Program 48. The impossible made trivial.
-
- ; Program Name: PRG_6GP.S
- ; Version: 1.001
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a TOS extension.
-
- ; Execution Instructions:
-
- ; Execute from the desktop.
-
- ; Program Function:
-
- ; Within a subroutine addressed by XBIOS function $26, does the following:
-
- ; 1. Turns off keyclick sound.
- ; 2. Prints a string with GEMDOS function $9.
- ; 3. Prints a string with BIOS function $3.
-
- ; See programs PRG_6DP and PRG_6EP for further documentation.
-
- ; Program Purpose:
-
- ; Illustrate the use of XBIOS function $26 to execute a subroutine in
- ; the supervisor mode, even though the subroutine contains GEMDOS and
- ; BIOS function calls. This example shows that these function calls
- ; can be executed within the subroutine, a contradiction to information
- ; contained in some of the references (See page 3-13 of the Peel book,
- ; for example.).
-
- mainline:
- lea stack, a7 ; Point A7 to this program's stack.
-
- execute_subroutine_in_supervisor_mode:
- pea subroutine ; Push address of subroutine onto stack.
- move.w #$26, -(sp) ; Function = superexec = XBIOS $26 (dec 38).
- trap #14 ; XBIOS call.
- addq.l #6, sp
-
- terminate:
- move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
- trap #1 ; GEMDOS call.
-
- subroutine:
- move.b #6, $484
- pea message ; Push address of first string.
- move.w #$9, -(sp) ; GEMDOS function $9 = c_conws.
- trap #1 ; Print first string.
- addq.l #6, sp
- lea message_2, a3 ; Load address of second string.
- print_string:
- move.b (a3)+, d3
- beq.s wait_for_keypress
- move.w d3, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp) ; BIOS function $3 = bconout.
- trap #13
- addq.l #6, sp
- bra.s print_string ; Branch until NULL detected.
- 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.
- rts
-
- data
- message: dc.b 'This string printed with GEMDOS function $9.',$D,$A,0
- message_2: dc.b 'This string printed with BIOS function $3.',$D,$A,0
- align
- bss
- ds.l 48 ; Stack. Must be large enough for system
- ; use when the switch to supervisor mode
- ; is accomplished by GEMDOS $26.
- stack: ds.l 0
- program_end: ds.l 0
- end
-
-
- Execution Results:
-
- This string printed with GEMDOS function $9.
- This string printed with BIOS function $3.
-
-
- An Unfortunate Operating System Bug
-
- While I was trying to prepare a shorter version of
- program 48, I discovered that the operating system corrupts
- the stack when it executes the bconout function (BIOS
- function #3). Were it not for this, the three stack pushes
- required to print the first character of a string could be
- reduced to one for each subsequent invocation to print each
- of the other characters in the string. Program 49
- illustrates the attempt to gain the advantage, and figure
- 8.1 shows how the bconout function corrupts the stack.
-
- Program 49. Illustrates the presence of an unfortunate
- operating system bug.
-
- ; Program Name: PRG_6HP.S
- ; Version: 1.001
-
- ; Assembly Instructions:
-
- ; Assemble in PC-relative mode and save with a TOS extension.
-
- ; Execution Instructions:
-
- ; Execute from the desktop.
-
- ; Program Function:
-
- ; A shorter version of PRG_6GP.S. In addition this program provides a way
- ; to verify that the trap #13 system function corrupts the stack.
-
- ; Just before the trap #13 function is invoked to print a character, three
- ; words of data must be pushed onto the stack. This having been done once,
- ; it should thenceforth be sufficient to simply move character data into the
- ; appropriate location within the stack to continue printing characters, as
- ; long as the stack pointer is not altered by the program.
-
- ; Unfortunately, the location at which the trap #13 function number is
- ; pushed or stored is corrupted by the trap when it is invoked, therefore,
- ; it is necessary to restore the function number each time, just before the
- ; trap is invoked again. So we lose an advantage.
-
- ; But the device to which the character is printed is not changed, so we
- ; can take advantage of that to store it once, before we start printing
- ; characters.
-
- ; Only after all characters are printed is the stack repositioned to the
- ; top of the stack. In fact, in this program, I wait until after the
- ; wait_for_keypress algorithm has been executed before repositioning the
- ; stack pointer.
-
- mainline:
- lea stack, a7 ; Point A7 to this program's stack.
-
- execute_subroutine_in_supervisor_mode:
- pea subroutine ; Push address of subroutine onto stack.
- move.w #$26, -(sp) ; Function = superexec = XBIOS $26 (dec 38).
- trap #14 ; XBIOS call.
- addq.l #6, sp
-
- terminate:
- move.w #0, -(sp) ; Function = p_term_old = GEMDOS $0.
- trap #1 ; GEMDOS call.
-
- subroutine:
- move.b #6, $484 ; Turn keyclick off.
- pea message ; Push address of first string.
- move.w #$9, -(sp) ; GEMDOS function $9 = c_conws.
- trap #1 ; Print first string.
- addq.l #6, sp
- lea message_2, a3 ; Load address of second string.
- SET_UP_STACK: ; DO THIS BEFORE CHARACTER PRINTING COMMENCES.
- MOVE.W #0, -(SP) ; MAKE ROOM FOR CHARACTER TO BE PRINTED.
- MOVE.W #2, -(SP) ; OUTPUT DEVICE = SCREEN.
- MOVE.W #3, -(SP) ; FUNCTION = BIOS #3.
- MOVE.W #3, D4 ; BECAUSE TRAP #13 CORRUPTS STACK.
- print_string:
- move.b (a3)+, d3
- beq.s wait_for_keypress
- move.w d4, (sp) ; Restore BIOS function number because
- move.w d3, 4(sp) ; Trap #13 corrupts stack.
- trap #13
- bra.s print_string ; Branch until NULL detected.
- wait_for_keypress:
- move.w #8, -(sp) ; Function = c_necin = GEMDOS $8.
- trap #1 ; GEMDOS call.
- addq.l #8, sp ; Reposition stack pointer at top of stack.
- rts
-
- data
- message: dc.b 'This string printed with GEMDOS function $9.',$D,$A,0
- message_2: dc.b 'This string printed with BIOS function $3.',$D,$A,0
- align
- bss
- ds.l 48 ; Stack. Must be large enough for system
- ; use when the switch to supervisor mode
- ; is accomplished by GEMDOS $26.
- stack: ds.l 0
- program_end: ds.l 0
- end
-
-
- Figure 8.1. Stack corruption by the system's BCONOUT
- function.
-
- PRG_6HP stack before trap #13 invoked.
-
- 06D68C 00030002 ORI.B #2,D3
- 06D690 005400FC ORI.W #$FC,(A4)
-
- PRG_6HP stack after trap #13 invoked.
-
- 06D68C 0786 BCLR D3,D6 ; This word has been corrupted.
- 06D68E 00020054 ORI.B #$54,D2
-
-
- Exception Handler Vectors
-
- Exceptions are interrupts. Exception handlers are
- nothing more than subroutines. Vectors are pointers.
- Pointers are memory locations that contain addresses.
- Therefore, exception handler vectors are memory locations
- that contain addresses to subroutines. The word pointer is
- used to describe these memory locations because it connotes
- the act of pointing to something. Thence to the word vector
- is not as straightforward.
- As an engineer, I can understand why the originator of
- the association between vectors and memory locations that
- contain addresses decided to form the association. When an
- engineer hears the word vector it connotes a straight line
- terminated with an arrowhead, similar to ->. In ST
- vernacular, however, the association is not pure. It is
- corrupted because some references apply the term vector to
- simple data. For example, on page 254 of the Internals
- book, the data stored in memory location $484 is called an
- attribute vector.
- Although they may not always be thought of as such, the
- 68000 registers are also locations in memory. When a
- register contains an address, it can be called a pointer or
- a vector. If the address that is stored in a register is a
- memory location that also contains an address, then the
- content of the register is a pointer or a vector also. Thus
- we have pointers to pointers, vectors to vectors, pointers
- to vectors, and etc. When a system variable contains data,
- that variable is not a vector unless the data is an address.
- I have reduced the scope of our interest in exceptions
- and interrupts to the extent that I can adjust Motorola's
- use of the words in the Programmer's Reference Manual to
- this: exceptions are generated by the trap instruction;
- interrupts are generated by hardware that is external to the
- 68000. Note, however, that because you and I both realize
- that the words exception and interrupt have identical
- meanings, I shall not have to worry about using the words
- interchangeably. The only reason we need be concerned, at
- all, by interpretations in the meanings of the two words is
- this: trap instructions are constituents of an exception
- group which has a lower execution priority than does the
- exception group of which hardware interrupts are
- constituents. In spite of this difference, however, both
- exception groups affect the supervisor stack in an identical
- manner--that's what we need to know.
-
- The Details of Storing Data in Memory
-
- At this point I must confirm that your knowledge of the
- manner in which data is stored in a block of memory
- locations to which we refer as a stack matches ST reality.
- Consider this: computer memory is constructed from single
- element constituents called bits. Now, the 68000
- microprocessor permits bit operations to be performed on
- data, however, when we store or retrieve data in ram, the
- smallest section of ram with which we may deal is eight
- bits, a grouping of bits for which we have a special name.
- We call this conglomerate a byte.
- The 68000 deals with two other conglomerates. These
- are called words and longwords. A word is composed of 16
- bits (two bytes), and a longword is composed of 32 bits
- (four bytes). But, even though a grouping of 32 bits is
- recognized as a legitimate conglomerate by the
- microprocessor, as far as the interface between the 68000
- and ram is concerned, only byte size and word size
- operations are permissible. The incongruency is resolved by
- separating a longword operation that involves ram into two
- word operations.
- As you probably know, we can even manipulate a single
- bit in ram, as long as we restrict the area containing the
- bit to a particular byte. That's what the Motorola manual
- means where it states that a bit operation is performed
- using the bit number, modulo 8, with zero referring to the
- least significant bit. See the btst and bset instructions,
- for example.
- Now, considering the relationship between permissible
- operations and hardware configuration, in order that we be
- able to manipulate the data stored in ram, we must know
- precisely how it is stored, bit by bit. Assume that you had
- the eight bits of data in the 8-bit register shown in figure
- 8.2. Do you suppose that the microprocessor would place the
- data in ram according to method 1, where the data contained
- in each bit position of the ram byte is a replica of that
- contained in the register, or according to method 2, where
- the data in ram has been reversed so that what is in bit
- position seven of the register is in bit position zero, and
- so on, of the ram byte?
-
- Figure 8.2. Storing the content of a register in ram.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- I agree that method 1 seems to be the most logical, but
- suppose that an engineer found method 2 to be the least
- expensive, or the faster, or the more reliable. Method 2 is
- at least a possible storage implementation. Then, when we
- consider word size and longword size operations, the
- discussion can be extended to possibilities involving the
- placement of bytes rather than bits. Will the highest byte
- of a register's word or longword content go into the highest
- byte of the ram word or longword, or will it go into the
- lowest? We must know the answer to this question because,
- very soon, we will be involved in the manipulation of one
- bit of word of data that will be located within a stack.
- Figure 8.3 illustrates two possible implementations,
- involving the storage of a data word.
-
- Figure 8.3. Some methods of storing a data word.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Using the first method, the system would store the
- least significant byte of register content in the next
- available byte of ram, then it would store the most
- significant byte of register content in the following ram
- byte. Using the second method, the system would store the
- most significant byte of register content, then it would
- store the least significant byte of register content. Note
- that we assume each byte of data to be stored in ram at the
- next lowest addressable ram byte, at all times; that is
- sequentially from byte 0 to the highest available location.
- To eliminate the necessity for further speculation
- concerning the method of storage used in the ST, I have
- provided program 50, which is followed by three figures that
- illustrate the debugger output field for the entire program.
- These figures show precisely how the ST stores data in ram.
- Specifically, the example illustrates the manner in which a
- stack pointer is pre-decremented by the number of bytes
- necessary to provide the necessary ram space, and the manner
- in which the stored data occupies that space.
-
-
- Program 50. Storing data in a stack.
-
- ; Program Name: PRG_6IR.S
- ; Version: 1.001
-
- ; Function:
-
- ; Illustrate the manner in which numbers are placed in the stack.
-
- ; Assembly Instruction:
-
- ; Assemble in Relocatable mode, go to the debugger and click on the
- ; relocate button.
-
- ; Execution Instructions:
-
- ; In the debugger, use the Single step button to execute only the
- ; instructions which load a stack address into register A7 and those which
- ; store values into the stack. Move the PC cursor manually to jump over
- ; the stack space declarations.
-
- text
- push_word_1:
- lea stack_1, sp
- move.w #5, -(sp)
- ds.l 1
- stack_1: ds.l 0
-
- text
- push_longword_1:
- lea stack_2, sp
- move.l #5, -(sp)
- ds.l 2
- stack_2: ds.l 0
-
- text
- push_word_2:
- lea stack_3, sp
- move.w #1770, -(sp)
- ds.l 1
- stack_3: ds.l 0
-
- text
- push_longword_2:
- lea stack_4, sp
- move.l #1234567890, -(sp)
- ds.l 2
- stack_4: ds.l 0
- end
-
-
- In figure 8.4, you can see the assembled program. The
- declared stack spaces, filled with zeroes, are highlighted
- by reversed video. The very last line in the output field
- is not a part of the program. Note that the Symbolic button
- has been clicked off, so that the entire program can fit
- within the field.
-
-
- Figure 8.4. Debugger output field with PRG_6IR.PRG ready for
- execution. Do not attempt to execute the statements which
- are declarations.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- The results of the execution are shown in figure 8.5.
- You can see that the ST stores the most significant byte of
- a data word in the least significant byte of a memory word.
- Then it stores the next data byte into the most significant
- memory byte. For example, when it stored the data word
- 0005, it placed the 00 in memory location $069C3C and the 05
- in memory location $069C3D. When it stored the data
- longword $499602D2, it placed the most significant byte,
- $49, in memory location $069C70. Then it placed the next
- data byte, $96, into memory location $069C71. From there,
- it placed decreasingly significant data bytes into
- increasingly significant memory bytes.
-
-
- Figure 8.5. Debugger output field showing PRG_6IR.PRG after
- execution.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Figure 8.6 is simply the normal disassembled display of
- the program. It is included to give you another view of the
- data, as it appears in ram. From the evidence presented in
- the three figures, we are able to compile the following
- assessment:
-
- 1. Before the ST stores data in a stack, the stack
- pointer is decremented by the number of bytes
- required to contain the data.
-
- 2. Then the data is stored, most significant byte
- first, into the space in memory that is highlighted
- by the decrementation.
-
- 3. The content of the stack pointer is the address of
- the most significant byte of the data. This
- knowledge is especially relevant whenever we want to
- examine particular bits of the data.
-
-
- Figure 8.6. PRG_6IR.PRG in normal disassembled format.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Of course, I have a handy example with which to begin.
- During my study of various computer languages, I found that,
- although the bulk of the output produced by programs was
- directed to the video screen, there were times when I wanted
- that output to be replicated on my printer. In addition, I
- often wanted a hardcopy of my keyboard inputs to a program.
- At first, I merely inserted the appropriate instructions in
- the program to direct the output as required. There came a
- time, however, when I began to find the task of providing
- temporary printer output instructions in my programs to be
- tedious.
- After careful study, I realized that all operating
- system controlled output to the video screen and the printer
- had to eventually be processed by BIOS (trap #13) functions.
- Therefore, I decided to design a program that would trap
- screen directed output, and, if the printer was on, redirect
- that output to both the printer and the screen. When it was
- completed, I used the program to provide hardcopy results at
- the end of program listings for myself and for programming
- class assignments. Later, I found that it was also useful
- during compilations because it provided a printer copy of
- compilation errors.
-
- Custom Trap Handlers
-
- Assume that the user stack is located within some block
- of memory and that the user stack pointer is pointing to
- (contains the address of) the top byte (the first 8 bits) of
- this stack. We will call this first byte of the stack
- relative location 0. We will refer to the second byte of
- the stack as relative location 1, the next as relative
- location 2, and so on. Further, assume that the processor
- is in user mode.
- To generate a BIOS function call, the bconout function,
- for example, we push the character (or its code) to be
- printed onto the user stack. Because we are stacking word
- data, the content of the user stack pointer (register A7) is
- pre-decremented by 2 (once for each byte of the word), then
- the pushed word (first byte equals zero, second byte equals
- the ASCII code for the character) will be stored at relative
- locations 0 and 1. The USP now points to relative location
- 0, the new top of the user stack.
- Next, we push the code for the device which is to be
- the recipient of the character onto the stack. Assume that
- the device is the screen, and the code is $2. After the
- push, the character code is located at relative locations 2
- and 3, and the device code is located at relative locations
- 0 and 1. As you can deduce, the stack pointer always points
- to relative location zero. All other stack data is
- referenced by an offset value, which is, precisely, the
- location of the data within the stack, relative to the
- location of the data byte that is stored at the address
- contained in the stack pointer register.
- Now, we push the BIOS function code ($3) onto the
- stack. The character is now at relative locations 4 and 5,
- the device code is at relative locations 2 and 3, and the
- function code is at relative locations 0 and 1. We now
- invoke the BIOS call with the trap #13 instruction.
- When this instruction is executed, by definition, an
- exception is forced. The exception is processed by the
- system in the following manner:
-
- l. An internal copy of the status register is
- generated.
-
- 2. The supervisor state is entered by setting (set to
- 1) the S bit of the status register.
-
- 3. The trace state bit is reset (set to 0).
-
- 4. The exception vector number is generated.
-
- 5. The content of the program counter (PC) register,
- which is the address of the next program instruction
- to be executed, is pushed onto the supervisor stack
- by the SSP.
-
- 6. The saved copy of the status register is pushed onto
- the supervisor stack by the SSP.
-
- 7. Using the vector number, the address of the trap
- handler is fetched and stored in the PC.
-
- 8. Execution continues at the content of the PC.
-
- Now, suppose that we want to handle such an exception
- ourselves, preempting the system's responsibility. Here is
- what must be accomplished:
-
- 1. Our trap handler must be memory resident.
-
- 2. The address of our trap handler must be that which
- is loaded into the PC during the system's processing
- of the exception.
- 3. Our trap handler must execute the code necessary to
- satisfy the expectations of the source that
- generated the exception.
- 4. Our trap handler must return control to the source
- generating the exception, if that is the normal
- expectation, otherwise it must relinquish control in
- whatever manner it is expected to do so by the
- exception generating source.
-
-
- Program 51. Installing a custom trap handler.
-
- ; PROGRAM NAME: PRG_6JC.S
- ; VERSION: 1.001
-
- ; Assembly Instructions:
-
- ; Assemble in Relocatable mode and save with a TOS suffix.
-
- ; Execution Instructions:
-
- ; Execute from the desktop. Some versions of the ST operating system
- ; may require the printer to be turned on for proper operation of this
- ; trap handler.
-
- ; Program Function:
-
- ; This program establishes itself in memory as a trap #13 handler. While
- ; the program is resident, it intercepts all trap #13 calls. If an
- ; intercepted call's function is $3, and if the device involved is the video
- ; screen, then the custom trap handler redirects the call to include output
- ; to the printer as well as to the video screen. In this manner all text
- ; output to the screen, which is accomplished by BIOS function $3 calls
- ; (GEMDOS calls $2 and $9 are included because these functions rely on
- ; BIOS function $3.) is sent to both the printer and the video screen.
-
- ; All redirected ASCII codes below $1B, except those for a carriage
- ; return and a linefeed, are filtered out of the data sent to the printer.
- ; This prevents certain ASCII codes that are suitable for the screen but
- ; which are undesirable for the printer from reaching the printer.
-
- ; MAJOR NOTE:
-
- ; If this program is to be used when a software print buffer is to be
- ; simultaneously resident, then the software print buffer program MUST be
- ; executed first. That is, the software print buffer must already be
- ; resident when this program is executed.
-
- ; Program Features:
-
- ; 1. Produces a hardcopy of program input and output that is sent to the
- ; screen. This program eliminates the necessity of providing statements
- ; in the program to accomplish that task. It permits the printer listing
- ; of a program to be followed by a printer listing of user/program
- ; interaction. Especially useful for providing the results of an
- ; execution for homework problems, or for programs under development.
-
- ; 2. When compiling, compiler output to the screen will be sent to the
- ; printer. Especially useful when debugging the error messages
- ; that appear on the screen. The printer output lets you go back to
- ; the editor with the error list hardcopy.
-
- ; 3. If the Show button is selected from the Show/Print/Cancel Desktop
- ; dialog box, when this program is resident, the data which appears
- ; on the video screen will also be sent to the printer.
-
- program_start: ; Calculate program size and retain result.
- lea program_end(pc), a3 ; Fetch program end address.
- movea.l 4(a7), a4 ; Fetch basepage address.
- suba.l a4, a3 ; Program size is in A3.
- lea stack(pc), a7 ; Provide a user stack.
-
- install_new_trap_13_vector:
- pea custom_trap_handler(pc) ; Push new trap handler address onto stack.
- move.w #$2D, -(sp) ; Push trap 13 vector number.
- move.w #5, -(sp) ; Function = setexec = BIOS $5.
- trap #13 ; Current trap handler vector returned in D0.
- addq.l #8, sp
- move.l d0, preempted_handler_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
-
- ; NOTE:
-
- ; The custom trap #13 handler is entered each time an application invokes
- ; the trap #13 call. If the call does not involve printing a character to the
- ; screen, then a jump is performed to the preempted trap #13 handler.
-
- ; If the call involves printing an escape sequence (An escape sequence
- ; is a two-character code, the first of which is $1B or 27 decimal. Escape
- ; sequences provide screen control--see section 3.6 "The Atari VT52 Emulator",
- ; pages 245-249 of the Internals book.) for screen control, then the sequence
- ; is sent to the screen via the preempted trap #13 handler.
-
- ; Otherwise, for each trap #13 call that is also a bconout invocation
- ; with device code #2, the custom_trap_13 routine is entered three times.
- ; The first time that the handler is entered, a variable is initialized, then,
- ; when it is entered subsequently, a jump, over the initialization sequence,
- ; to the preempted trap handler is performed. The custom trap handler is
- ; entered three times because the custom handler prints to both the screen
- ; and the printer by invoking its own trap #13 calls; those calls are also
- ; intercepted by the custom handler.
-
- ; The custom handler must be able to handle trap #13 calls made while
- ; the processor is in supervisor mode or user mode.
-
- custom_trap_handler:
- tst.b initialization_flag
- bne skip_initialization
-
- ; Processing the stack data:
-
- ; The location of the stack data that must be processed by this subroutine
- ; depends on the state of the processor when the exception occurs. If it was
- ; in the supervisor state, then the data will be stacked, as indicated, at the
- ; following relative locations:
-
- ; location - 11 = character to be printed, byte length
- ; Old Top of Stack: location - 10 = character to be printed, word length
- ; location - 8 = device to which character is to sent
- ; location - 6 = bios command to be executed
- ; location - 4 = program counter low word
- ; location - 2 = program counter high word
- ; SSP -> location - 0 = invoking program's status register content
-
- ; In each case, above, the location listed is the location of the most
- ; significant byte of the data listed. For example, relative location 10
- ; contains the most significant byte of the word which specifies the
- ; character to be printed. Relative location 11 contains the least
- ; significant byte of that word. Actually, relative location 11 is
- ; "the least significant byte" in computer vernacular, but, in fact, it
- ; is that byte which contains the character code. The byte at relative
- ; location 10 contains only zeroes.
-
- ; The supervisor stack pointer (SSP) will be pointing to the new top of
- ; stack; the data there will be the content of the most significant byte
- ; of the status register, as it was when the exception occurred. Now, as
- ; it turns out, this byte will be the one of significance, as far as the
- ; trap handler is concerned.
-
- ; If the processor was in the user state, then the data will be stacked,
- ; as indicated, at the following relative locations:
-
- ; Old Top of Stack: location - 4 = character to be printed
- ; location - 2 = device to which character is to sent
- ; USP -> location - 0 = bios command to be executed
-
- ; The user stack pointer (USP) will be pointing to the top of the stack;
- ; the data there will be the bios command to be executed.
-
- ; In order to process the stack data without regard to the processor state
- ; before invocation, if the processor was in user mode, then the offsets used
- ; to access the stack data with the USP as reference must be adjusted so that
- ; they match the offsets used to access the stack data with the SSP as
- ; reference. Therefore, if the processor was in user mode when the trap #13
- ; call was made, the value six is subtracted from the register that is used
- ; to access the data.
-
- ; Then, common offset values, which will access the data correctly regardless
- ; of the stack pointer used as reference can be used.
-
- ; When we begin, we know that the processor is now is supervisor mode,
- ; however, we must determine its state at the time of the exception. That
- ; processor state must be checked by testing bit 13 of the status register.
- ; We know that the SSP is now pointing to the content of the status register
- ; as it was at exception time. In fact, it is pointing to the most significant
- ; byte of the status register word.
-
- ; There are two ways a bit can be tested with the 68000 BTST instruction.
-
- ; 1 - If the destination operand is a data register, then any of 32 bits
- ; may be tested.
-
- ; 2 - If the destination operand is not a data register, then only 1 of
- ; 8 bits may be tested.
-
- ; In either case, the bit to be tested may be specified in a source data
- ; register or as immediate data.
-
- ; Since the bit we want to test is on the stack, we can only test a bit
- ; of a single memory byte. The bit we must test (status register bit 13) is
- ; bit 5 of the byte that is being addressed by the SSP.
-
- get_processor_status:
- movea.l sp, a0 ; Fetch address of current top of stack.
- btst #5, (sp) ; Supervisor mode test.
- bne.s supervisor_mode ; No adjustment is necessary if the
- ; processor was in supervisor mode.
- move.l usp, a0 ; Fetch address of current top of user stack.
- subq.l #6, a0 ; Adjust user data access address.
-
- user_mode:
- supervisor_mode: ; Processing for either mode follows.
- cmpi.w #3, 6(a0) ; Writing a character to a device?
- bne not_bconout_call
- cmpi.w #2, 8(a0) ; Is device the screen?
- bne.s not_screen
-
- ; NOTE:
-
- ; The information desired, at this point, is the ON/OFF status of the
- ; printer. My printer, the star NX-10 provides this information on pin 13
- ; of its parallel interface. Unfortunately, the ST does not permit the
- ; utilization of this data. This is an example of hardware/hardware
- ; incompatibility.
-
- ; Because of this ST deficiency, the printers BUSY/NOT BUSY signal,
- ; on pin 11 of the interface is forced into double duty. In addition to its
- ; normal function, this signal is used by the ST to determine if the printer
- ; is ON or OFF.
-
- ; We would like to be able to direct output to the printer, at will,
- ; simply by manually turning the printer ON or OFF.
-
- ; The problem here is this: because the response of the printer interface
- ; is so much slower than the video screen interface, we can't use the bcostat
- ; function (BIOS #8) to determine the printer status (before attempting to
- ; write to the printer) in between outputs to the screen. When we attempt to
- ; do that, we find that pin 11 indicates that the printer is busy (same as OFF
- ; signal) most of the time, even when the printer is turned on. Thus, the
- ; printer receives only some of the data sent to the screen.
-
- ; Therefore, the trap handler must just assume that the printer is on.
- ; This is no problem with some versions of the ST operating system. If the
- ; printer is off, nothing will be sent to it. But some versions of the
- ; operating system will wait (forever, it seems) for someone to turn on the
- ; printer. If this happens, then you must turn on the printer while this
- ; trap handler is resident.
-
- esc_sequence_test:
- tst.b esc_sequence_flag
- bne.s reset_esc_sequence_flag
- cmpi.w #$1B, 10(a0)
- bne.s not_esc_sequence
- move.b #1, esc_sequence_flag
- bra.s use_preempted_handler
- reset_esc_sequence_flag:
- move.b #0, esc_sequence_flag
- use_preempted_handler:
- movea.l preempted_handler_address(pc), a0
- jmp (a0) ; JUMP TO PREEMPTED TRAP #13 HANDLER.
-
- not_esc_sequence:
- move.b #1, initialization_flag
- move.w 10(a0), character ; Store character for printer.
- write_character_to_screen:
- move.w 10(a0), -(sp) ; Push character onto stack.
- move.w #2, -(sp) ; Device = screen.
- move.w #3, -(sp) ; Function = bconout = BIOS $3.
- trap #13
- addq.l #6, sp
-
- ascii_code_test: ; Filter out undesirable codes.
- move.w character(pc), d0
- cmpi.w #$1B, d0
- bgt.s write_character_to_printer
- cmpi.w #$A, d0
- beq.s write_character_to_printer
- cmpi.w #$D, d0
- bne.s undesirable_ascii
- write_character_to_printer:
- move.w d0, -(sp) ; Push character onto stack.
- move.w #0, -(sp) ; Device = printer.
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- undesirable_ascii:
- move.b #0, initialization_flag
- rte
-
- not_screen:
- not_bconout_call:
- skip_initialization:
- movea.l preempted_handler_address(pc), a0
- jmp (a0) ; JUMP TO PREEMPTED TRAP #13 HANDLER.
-
- bss
- character: ds.w 1
- preempted_handler_address: ds.l 1
- esc_sequence_flag: ds.b 1
- initialization_flag: ds.b 1
- align
- ds.l 48 ; Stack
- stack: ds.l 1 ; Address of stack.
- program_end: ds.l 0
- end
-
-
- Program 52. Another version of program 51.
-
- ; PROGRAM NAME: PRG_6KC.S
- ; VERSION: 1.001
-
- ; The function of this program is identical to that of PRG_6JC.S. I am
- ; including it because, in PRG_6JC's trap handler, I used an algorithm to
- ; access stack data which you are most likely to see in references. In this
- ; program I show you another method, the method that I prefer.
-
- program_start: ; Compute program size and retain result.
- lea program_end(pc), a3 ; Fetch program end address.
- movea.l 4(a7), a4 ; Fetch basepage address.
- suba.l a4, a3 ; Yields size of memory that must remain
- ; resident.
- load_stack_address:
- lea stack(pc), a7
-
- install_new_trap_13_vector:
- pea custom_trap_handler(pc)
- move.w #$2D, -(sp) ; Trap 13 vector number.
- move.w #5, -(sp) ; Function = setexec = BIOS $5.
- trap #13 ; Current trap handler vector returned in D0.
- addq.l #8, sp
- move.l d0, preempted_handler_address
-
- relinquish_processor_control: ; Maintain memory residency.
- move.w #0, -(sp) ; See page 121 of Internals book.
- move.l a3, -(sp) ; Size of memory to remain resident.
- move.w #$31, -(sp) ; Function = ptermres = GEMDOS $31.
- trap #1
-
- ; Here I illustrate another way to adjust one of the stack reference pointers
- ; so that common offset values may be used to access stack data. In this
- ; program the value six is added to the register that is used to access the
- ; data if the processor was in supervisor mode before the invocation.
-
- custom_trap_handler:
- tst.b initialization_flag
- bne skip_initialization
- move.l usp, a0 ; Load address of current top of user stack.
-
- get_processor_status:
- btst #5, (sp) ; User mode test.
- beq.s was_user_mode ; No adjustment is necessary if the
- ; processor was in user mode.
- movea.l sp, a0 ; Load current top of supervisor stack.
- addq.l #6, a0 ; Adjust SSP for user mode type data access.
-
- was_supervisor_mode:
- was_user_mode: ; Processing for either mode follows.
- cmpi.w #3, (a0) ; Writing a character to a device?
- bne.s not_bconout_call
- cmpi.w #2, 2(a0) ; Is device the screen?
- bne.s not_screen
-
- esc_sequence_test:
- tst.b esc_sequence_flag
- bne.s reset_esc_sequence_flag
- cmpi.w #$1B, 4(a0)
- bne.s not_esc_sequence
- move.b #1, esc_sequence_flag
- bra.s use_preempted_handler
- reset_esc_sequence_flag:
- move.b #0, esc_sequence_flag
- use_preempted_handler:
- movea.l preempted_handler_address(pc), a0
- jmp (a0) ; JUMP TO PREEMPTED TRAP #13 HANDLER.
-
- not_esc_sequence:
- move.b #1, initialization_flag
- move.w 4(a0), character ; Store character for printer.
- write_character_to_screen:
- move.w 4(a0), -(sp) ; Push character onto stack.
- move.w #2, -(sp) ; Device = screen.
- move.w #3, -(sp) ; Function = bconout = BIOS $3.
- trap #13
- addq.l #6, sp
-
- ascii_code_test: ; Filter out undesirable codes.
- move.w character(pc), d0
- cmpi.w #$1B, d0
- bgt.s write_character_to_printer
- cmpi.w #$A, d0
- beq.s write_character_to_printer
- cmpi.w #$D, d0
- bne.s undesirable_ascii
- write_character_to_printer:
- move.w d0, -(sp) ; Push character onto stack.
- move.w #0, -(sp) ; Device = printer.
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- undesirable_ascii:
- move.b #0, initialization_flag
- rte
-
- not_screen:
- not_bconout_call:
- skip_initialization:
- movea.l preempted_handler_address(pc), a0
- jmp (a0) ; JUMP TO PREEMPTED TRAP #13 HANDLER
-
- bss
- character: ds.w 1
- preempted_handler_address: ds.l 1
- esc_sequence_flag: ds.b 1
- initialization_flag: ds.b 1
- align
- ds.l 48 ; Stack
- stack: ds.l 1 ; Address of stack.
- program_end: ds.l 0
- end
-
-
- The intricacy of many exception handlers is greater
- than that of programs 51 and 52. However, I want the
- example given to help you to realize that complexity is not
- an exception handler requirement. Once you are able to
- handle even such rudimentary handlers, you will be able to
- force the computer to perform at your command. In the next
- chapter, I will present a more powerful handler, after the
- discussions about some of the microprocessor's peripheral
- hardware.
- I will now introduce a group of programs that are
- designed to provide information which is indispensable to
- the effort required when designing efficient programs,
- especially programs that function as desk accessories and
- exception handlers. Each program in this group reports
- alterations made to the contents of the 68000 data and
- address registers during an experiment. While the results
- of each experiment can only be considered valid for the
- conditions imposed during the experiment, the methods used
- can be applied to any set of conditions that you may wish to
- impose on your own experiments.
-
- Data and Address Register Corruption
-
- All applications which are executed on the Atari ST
- conduct most of their operations via the 68000 data and
- address registers. When several applications are resident
- and sharing system resources, the contents of the registers
- that are being used by one application must not be corrupted
- by another beyond the expectations of the first application.
- Furthermore, when designing our applications, we must be
- cognizant of the extent to which system calls inflict
- register corruption, so that we can avoid the consequences
- of using contaminated registers.
- I have developed a naming pattern for a new group of
- programs, which is to be used in a series of register
- contamination experiments, that is slightly different than
- that which I have been using. Each program in the group has
- the appellation REG_TSTn, where n is a digit. Some of the
- experiments to be performed require the execution of more
- than one program, and at least one experiment requires the
- installation of a previously introduced program in the root
- directory of a specific hard disk partition. If no hard
- disk is available, the path used in that experiment must be
- edited so that a new directory path is specified.
- This group of programs has been designed to provide the
- following information:
-
- 1. The extent to which the registers being used
- in a desk accessory are protected from corruption
- by the operating system, and the
- effect that such accessories have on the
- registers of an executing application.
-
- 2. The extent to which system exception handlers alter
- the registers of an executing application.
-
- 3. The extent to which the registers that are used in a
- LSR program that is not a desk accessory are
- protected from corruption by the operating
- system, and the effect that such programs have on
- the registers of an executing application.
-
- In a later chapter, I will present similar program which
- determine the extent to which AES and VDI arrays are
- corrupted by AES and VDI calls.
-
-
- Program 53. Experiment 1: System trap #13, BIOS
- function #$8 register contamination.
-
- ; PROGRAM NAME: REG_TST1.S
- ; VERSION: 1.001
-
- ; Assembly Instructions:
-
- ; Assemble in Relocatable mode and save assembled program with a TOS suffix.
-
- ; Program Function:
-
- ; This program is one of a group that is used to determine the extent of
- ; register alteration by exception handlers and to test the volatility
- ; of desk accessory registers.
-
- ; EXPERIMENT 1:
-
- ; This program will call a trap #13 handler. Before the call, values
- ; that were stored in a register array while the program was being loaded
- ; into ram are placed in each register.
-
- ; Immediately after the call, the content of each register is stored in
- ; a second register array. Then the content of each register, as it was
- ; before the call, is printed. Next, the content of the after_call array
- ; is compared to the content of the before-call array, and the values in
- ; the after-call array are printed; however, register values in the
- ; after-call array which do not match the before-call value for each
- ; corresponding register are printed in reverse colors on the video screen.
-
- ; In addition, if the trap #13 handler which redirects screen output to
- ; to both the printer and the screen is resident, and if the printer is on,
- ; then a hardcopy will be obtained, upon which the non-matching after_call
- ; values will be printed in boldface. To place the redirecting trap #13
- ; handler in residence, execute PRG_6JC.TOS or PRG_6KC.TOS.
-
- ; If you are going to execute this program with the printer on, the results
- ; you obtain will probably more closely match those I obtained if you turn on
- ; your printer before booting the ST and executing the redirecting handler
- ; mentioned above.
-
- program_start: ; Calculate program size and retain result.
- lea program_end(pc), a0
- movea.l 4(a7), a1
- suba.l a1, a0
-
- 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.w d0, -(sp) ; Dummy value, can be anything.
- move.w #$4a, -(sp) ; Function = m_shrink = GEMDOS $4A.
- trap #1 ; GEMDOS call.
- adda.l #$C, sp ; Reset stack pointer to top of stack.
-
- place_values_in_registers:
- movem.l registers_before(pc), d0-d7/a0-a6
- lea stack(pc), a7
- move.l a7, reg_A7_before
-
- make_trap_call: ; Get and save printer status.
- move.w #0, -(sp) ; Device = printer.
- move.w #8, -(sp) ; Function = BIOS $8 = bcostat.
- trap #13 ; If printer is on, -1 will be returned
- addq.l #4, sp ; in D0, 0 will be returned otherwise.
- move.b d0, printer_status
-
- store_after_call_contents:
- movem.l d0-d7/a0-a7, registers_after
-
- print_before_call_contents:
- bsr print_newline
- lea experiment_head(pc), a0
- bsr print_string
- lea before_msg(pc), a0
- bsr print_string
- lea register_id(pc), a3
- lea registers_before(pc), a4
- move.l #15, d4
- print_register:
- move.l (a4)+, d3 ; Convert content from binary to hex.
- bsr bin_to_hex
- movea.l a3, a0
- bsr print_string ; Print register number.
- add.l #24, a3
- lea hexadecimal(pc), a0
- bsr print_string ; Print register content.
- bsr print_newline
- dbra d4, print_register
- bsr print_newline
-
- _wait_for_keypress:
- move.w #8, -(sp)
- trap #1
- addq.l #2, sp
-
- print_after_call_contents:
- lea after_msg(pc), a0
- bsr print_string
- lea register_id(pc), a3
- lea registers_before(pc), a4
- lea registers_after(pc), a5
- move.l #15, d4
- compare_and_print:
- move.l (a5)+, d3 ; Load after_call value.
- cmp.l (a4)+, d3 ; Compare before_call to after_call.
- beq.s no_change ; Branch if no change occurred.
- tst.b printer_status ; Find out if printer is on or off.
- beq.s printer_off
- bsr bold_on ; Turn on printer bold print.
- printer_off:
- bsr reverse_on ; Turn on video reverse print.
-
- no_change:
- bsr bin_to_hex
- movea.l a3, a0
- bsr print_string
- adda.l #24, a3
- lea hexadecimal(pc), a0
- bsr print_string
- bsr print_newline
- tst.b printer_status ; Find out if printer is on or off.
- beq.s _printer_off
- bsr bold_off ; Turn printer bold print off.
- _printer_off:
- bsr reverse_off ; Turn video reverse print off.
- dbra d4, compare_and_print
-
- wait_for_keypress:
- move.w #8, -(sp)
- trap #1
- addq.l #2, sp
-
- terminate:
- move.w #0, -(sp)
- trap #1
-
- ; The binary to ASCII hexadecimal conversion routine expects a number to be
- ; passed as a longword in register D3. 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.
-
- bin_to_hex: ; Converts a 32-bit binary number in D3 to
- ; ASCII hexadecimal.
- lea hexadecimal(pc), a0 ; A0 is pointer to array "hexadecimal".
- lea hex_table(pc), 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 what we have
- ; proven so it must be cleared before use.
- discard_leading_zeroes:
- rol.l #4, d3 ; Rotate most significant nibble to the
- ; least significant nibble position.
- move.b d3, d0 ; Copy least significant byte of D1 to D0.
- andi.b #$F, d0 ; Mask out most significant nibble of D0.
- cmpi.b #0, d0 ; Is it zero?
- bne.s store_digit
- dbra d2, discard_leading_zeroes
- move.b 0(a1,d0.w), (a0)+ ; If we get here, last nibble has been
- ; processed, so store digit in buffer.
- move.b #0, (a0) ; Terminate hexadecimal string with a null.
- rts
-
- continue:
- rol.l #4, d3 ; Rotate most significant nibble.
- move.b d3, 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
-
- print_string: ; Expects address of string to be in A0.
- move.l a0, -(sp) ; 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(pc) ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp
- rts
-
- bold_on:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$47, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_off:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$48, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_on:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$70, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_off:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$71, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- data
- hex_table: dc.b '0123456789ABCDEF'
- newline: dc.b $D,$A,0
- experiment_head: dc.b 'EXPERIMENT 1: System Trap #13 Corruption',$D,$A
- dc.b ' Function = BIOS $8',$D,$A,$A,0
- before_msg: dc.b ' Register contents before call',$D,$A,0
- after_msg: dc.b ' Register contents after call',$D,$A,0
- printer_status: dc.b 0
- register_id: dc.b ' Data Reg 0: $',0
- dc.b ' Data Reg 1: $',0
- dc.b ' Data Reg 2: $',0
- dc.b ' Data Reg 3: $',0
- dc.b ' Data Reg 4: $',0
- dc.b ' Data Reg 5: $',0
- dc.b ' Data Reg 6: $',0
- dc.b ' Data Reg 7: $',0
- dc.b ' Add Reg 0: $',0
- dc.b ' Add Reg 1: $',0
- dc.b ' Add Reg 2: $',0
- dc.b ' Add Reg 3: $',0
- dc.b ' Add Reg 4: $',0
- dc.b ' Add Reg 5: $',0
- dc.b ' Add Reg 6: $',0
- dc.b ' Add Reg 7: $',0
- align
-
- ;bss MAJOR NOTE: Do not use the bss assembler op here. If you do, the
- ; value $44415441 will NOT be loaded into the elements
- ; of the registers_before array.
-
- trap_vector: ds.l 1 ; Store for custom trap handler's vector.
- hexadecimal: ds.l 3 ; Output buffer. Must be NULL terminated.
- registers_before: ds.l 15, $44415441
- reg_A7_before: ds.l 1
- registers_after: ds.l 16 ; Array for register contents after call.
- ds.l 48 ; Program stack.
- stack: ds.l 1 ; Address of program stack.
- program_end: ds.l 0
- end
-
-
- Figure 8.7. Program 53 Execution Results.
-
- Figure 8.7A. Register Contents before call.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Figure 8.7B. Register contents after call.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- A register preservation guide can be found on page 3-2
- of the Peel book. There, it implies that registers d0-
- d2/a0-a2 are subject to contamination during the execution
- of trap #13 (BIOS) calls. This guide does not provide any
- suggestion concerning possible function differentiations.
- From the information given, we can only assume that all BIOS
- functions corrupt with equal impudence. Page 152 of the
- Internals book (under the heading 3.2 The BIOS Functions)
- provides the same information.
- The results of our first experiment, however, indicate
- that only the registers d0/a0-a1 are contaminated during a
- BIOS $8 (bconout) call. But there is one other parameter
- imposed on the experiment. The device specified for the
- function call was the printer. The results might not be the
- same if another device were specified. Yet it is not my
- intent to explore the results of every conceivable system
- call. That, itself, would be material for a book. It is
- the method of obtaining experimental results and the
- contradiction between reference material and experimental
- evidence that I wish to bring to your attention.
- I prepared program 53 in as straight forward a manner
- as possible, in order to alleviate your comprehension of the
- algorithms involved. The program is rather long, when
- compared to its accomplishments, and the manner in which the
- results are displayed is not harmonious. Therefore, the
- rest of the programs in the group are shorter, referring to
- program 23 when necessary, and they present their
- information in a more compact display. Program 54 provides
- the same data as does program 53.
-
-
- Program 54. Experiment 1: System trap #13, BIOS function #$8
- register contamination.
-
- ; PROGRAM NAME: REG_TST2.S
- ; VERSION: 1.001
-
- ; Assembly Instructions:
-
- ; Assemble in Relocatable mode and save the assembled program with a TOS suffix.
-
- ; Program Function:
-
- ; Same as REG_TST1, except this one is designed to print the before and
- ; after values side by side. Also, the leading zero portion has been
- ; removed from the binary to hexadecimal conversion routine, so that
- ; numerical alignment is achieved on the output.
-
- program_start: ; Calculate program size and retain result.
- lea program_end(pc), a0
- movea.l 4(a7), a1
- suba.l a1, a0
-
- 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.w d0, -(sp) ; Dummy value, can be anything.
- move.w #$4a, -(sp) ; Function = m_shrink = GEMDOS $4A.
- trap #1 ; GEMDOS call.
- adda.l #$C, sp ; Reset stack pointer to top of stack.
-
- place_values_in_registers:
- movem.l registers_before(pc), d0-d7/a0-a6
- lea stack(pc), a7
- move.l a7, reg_A7_before
-
- make_trap_call: ; Get and save printer status.
- move.w #0, -(sp) ; Device = printer.
- move.w #8, -(sp) ; Function = BIOS $8 = bcostat.
- trap #13 ; If printer is on, -1 will be returned
- addq.l #4, sp ; in D0, 0 will be returned otherwise.
- move.b d0, printer_status
-
- store_after_call_contents:
- movem.l d0-d7/a0-a7, registers_after
-
- ; The following algorithm prints the before value for a register, then,
- ; on the same line, it prints the after value. This provides a more
- ; compact display, and one in which it is easier to compare the before
- ; and after values.
-
- print_all_contents:
- bsr print_newline
- lea experiment_head(pc), a0
- bsr print_string
- lea heading(pc), a0
- bsr print_string
- lea register_id(pc), a3
- lea registers_before(pc), a4
- lea registers_after(pc), a5
- move.l #15, d4
- print_register:
- move.l (a4)+, d3 ; Convert content from binary to hex.
- move.l d3, d5 ; Save for comparison with after-value.
- bsr bin_to_hex
- movea.l a3, a0
- bsr print_string ; Print register number.
- add.l #16, a3
- lea hexadecimal(pc), a0
- bsr print_string ; Print before-call register content.
- lea space(pc), a0
- bsr print_string
- compare_and_print:
- move.l (a5)+, d3 ; Load after_call value.
- cmp.l d5, d3 ; Compare before_call to after_call.
- beq.s no_change ; Branch if no change occurred.
- tst.b printer_status ; Find out if printer is on or off.
- beq.s printer_off
- bsr bold_on ; Turn on printer bold print.
- printer_off:
- bsr reverse_on ; Turn on video reverse print.
-
- no_change:
- bsr bin_to_hex
- bsr print_hex_sign
- lea hexadecimal(pc), a0
- bsr print_string ; Print after-call value.
- bsr print_newline
- tst.b printer_status ; Find out if printer is on or off.
- beq.s _printer_off
- bsr bold_off ; Turn printer bold print off.
- _printer_off:
- bsr reverse_off ; Turn video reverse print off.
- dbra d4, print_register
- bsr print_newline
-
- wait_for_keypress:
- move.w #8, -(sp)
- trap #1
- addq.l #2, sp
-
- terminate:
- move.w #0, -(sp)
- trap #1
-
- ; The binary to ASCII hexadecimal conversion routine expects a number to be
- ; passed as a longword in register D3. 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 zero portion has been removed from this routine.
-
- bin_to_hex: ; Converts a 32-bit binary number in D3 to
- ; ASCII hexadecimal.
- lea hexadecimal(pc), a0 ; A0 is pointer to array "hexadecimal".
- lea hex_table(pc), 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 what we have
- ; proved, so it must be cleared before use.
- rotate_and_convert:
- rol.l #4, d3 ; Rotate most significant nibble to the
- ; least significant nibble position.
- move.b d3, 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, rotate_and_convert
- move.b #0, (a0) ; Terminate hexadecimal string with a null.
- rts
-
- print_string: ; Expects address of string to be in A0.
- move.l a0, -(sp) ; 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(pc) ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp
- rts
-
- print_hex_sign:
- move.w #$24, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_on:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$47, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_off:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$48, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_on:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$70, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_off:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$71, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- data
- hex_table: dc.b '0123456789ABCDEF'
- newline: dc.b $D,$A,0
- experiment_head: dc.b 'EXPERIMENT 1: System Trap #13 Corruption',$D,$A
- dc.b ' Function = BIOS $8',$D,$A,$A,0
- heading: dc.b ' Before Call After Call',$D,$A,0
- space: dc.b ' ',0
- printer_status: dc.b 0
- register_id: dc.b ' Data Reg 0: $',0,' Data Reg 1: $',0
- dc.b ' Data Reg 2: $',0,' Data Reg 3: $',0
- dc.b ' Data Reg 4: $',0,' Data Reg 5: $',0
- dc.b ' Data Reg 6: $',0,' Data Reg 7: $',0
- dc.b ' Add Reg 0: $',0,' Add Reg 1: $',0
- dc.b ' Add Reg 2: $',0,' Add Reg 3: $',0
- dc.b ' Add Reg 4: $',0,' Add Reg 5: $',0
- dc.b ' Add Reg 6: $',0,' Add Reg 7: $',0
- align
-
- ;bss MAJOR NOTE: Do not use the bss assembler op here. If you do, the
- ; value $44415441 will NOT be loaded into the elements
- ; of the registers_before array.
-
- trap_vector: ds.l 1 ; Store for custom trap handler's vector.
- hexadecimal: ds.l 3 ; Output buffer. Must be NULL terminated.
- registers_before: ds.l 15, $44415441 ; Store this value in each before
- reg_A7_before: ds.l 1 ; array element during loading.
- registers_after: ds.l 16 ; Array for register contents after call.
- ds.l 48 ; Program stack.
- stack: ds.l 1 ; Address of program stack.
- program_end: ds.l 0
- end
-
-
- Figure 8.8. Program 54 Execution Results.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Program 55. Experiment 2: System trap #1, GEMDOS
- function #$4B register contamination.
-
- ; PROGRAM NAME: REG_TST3.S
- ; VERSION: 1.001
-
- ; Assembly Instructions:
-
- ; Assemble in Relocatable mode. Save the assembled program with a TOS suffix.
-
- ; Program Function:
-
- ; Similar to REG_TST2, except this one calls a system trap #1 handler.
-
- ; MAJOR NOTES:
-
- ; 1. PRG_6KC.TOS must be in the root directory of partition D of your
- ; hard disk, if you are to execute the program as prepared. But
- ; you can alter the variable "program_name" to suit your
- ; requirements.
-
- ; 2. Since this program loads and executes PRG_6KC.TOS, which installs
- ; a custom trap #13 handler, you should execute this program only
- ; once, otherwise each letter sent to the video screen will be
- ; printed twice on the printer.
-
- ; 3. PRG_6KC.TOS functions best when the printer is on line during
- ; system boot.
-
- ; 4. After executing this program, you should reset your system,
- ; preferably with a cold start, to clear the custom trap handler
- ; from RAM, unless you want it there for other purposes.
-
- ; 5. Do not execute this program from within AssemPro.
-
- ; EXPERIMENT 2:
-
- ; This program will call a system trap #1 handler. Since some
- ; trap #1 handlers call trap #13 handlers, as with the previous program,
- ; we specify a handler which is not processed by the custom trap handler
- ; of PRG_6KC.TOS, which may be resident. We do this because we want to
- ; avoid any possible complications imposed by a custom trap handler at
- ; this time.
-
- ; GEMDOS function $4B (aka p_exec) was chosen for the experiment.
- ; This function is discussed in the Internals book. Mode 0 is used in
- ; this program to cause the system to load and execute PRG_6KC.TOS.
-
- ; A couple of warnings: you would not want to execute this program if
- ; PRG_6KC.TOS were already resident. That program contains no provisions
- ; for determining its residency, therefore, it is possible for you to
- ; execute it repeatedly, thereby obtaining multiple copies of it in ram.
-
- ; When using GEMDOS function $4B to execute programs from within an
- ; executing application, you must set up both of the programs so that
- ; neither of them consumes all ram. That is, you must use one of
- ; the functions which releases excess memory back to the operating system.
-
- ; Note that this program uses GEMDOS function $4A to return unused
- ; memory to the operating system, while PRG_6KC.TOS uses GEMDOS function
- ; $31. In addition, of course, both programs require a legitimate
- ; termination algorithm. That is, they must relinquish processor control.
-
- program_start: ; Calculate program size and retain result.
- lea program_end(pc), a0
- movea.l 4(a7), a1
- suba.l a1, a0
-
- 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.w d0, -(sp) ; Dummy value, can be anything.
- move.w #$4a, -(sp) ; Function = m_shrink = GEMDOS $4A.
- trap #1 ; GEMDOS call.
- adda.l #$C, sp ; Reset stack pointer to top of stack.
-
- get_printer_status: ; Get and save printer status.
- move.w #0, -(sp) ; Device = printer.
- move.w #8, -(sp) ; Function = BIOS $8 = bcostat.
- trap #13 ; If printer is on, -1 will be returned
- addq.l #4, sp ; in D0, 0 will be returned otherwise.
- move.b d0, printer_status
-
- place_values_in_registers:
- movem.l registers_before(pc), d0-d7/a0-a6
- lea stack(pc), a7
- move.l a7, reg_A7_before
-
- make_the_trap_call: ; This is Experiment 2.
- pea environmental_string(pc)
- pea command_line(pc)
- pea program_name(pc) ; Must be the complete path to the file.
- move.w #0, -(sp)
- move.w #$4B, -(sp) ; Function = GEMDOS $4B = p_exec.
- trap #1 ; Load and execute program PRG_6KC.TOS.
- add.l #$10, sp
-
- store_after_call_contents:
- movem.l d0-d7/a0-a7, registers_after
-
- print_all_contents:
- bsr print_newline
- lea experiment_head(pc), a0
- bsr print_string
- lea heading(pc), a0
- bsr print_string
- lea register_id(pc), a3
- lea registers_before(pc), a4
- lea registers_after(pc), a5
- move.l #15, d4
- print_register:
- move.l (a4)+, d3 ; Convert content from binary to hex.
- move.l d3, d5 ; Save for comparison with after-value.
- bsr bin_to_hex
- movea.l a3, a0
- bsr print_string ; Print register number.
- add.l #16, a3
- lea hexadecimal(pc), a0
- bsr print_string ; Print before-call register content.
- lea space(pc), a0
- bsr print_string
- compare_and_print:
- move.l (a5)+, d3 ; Load after_call value.
- cmp.l d5, d3 ; Compare before_call to after_call.
- beq.s no_change ; Branch if no change occurred.
- tst.b printer_status ; Find out if printer is on or off.
- beq.s printer_off
- bsr bold_on ; Turn on printer bold print.
- printer_off:
- bsr reverse_on ; Turn on video reverse print.
-
- no_change:
- bsr bin_to_hex
- bsr print_hex_sign
- lea hexadecimal(pc), a0
- bsr print_string ; Print after-call value.
- bsr print_newline
- tst.b printer_status ; Find out if printer is on or off.
- beq.s _printer_off
- bsr bold_off ; Turn printer bold print off.
- _printer_off:
- bsr reverse_off ; Turn video reverse print off.
- dbra d4, print_register
- bsr print_newline
-
- wait_for_keypress:
- move.w #8, -(sp)
- trap #1
- addq.l #2, sp
-
- terminate:
- move.w #0, -(sp)
- trap #1
-
- ; The binary to ASCII hexadecimal conversion routine expects a number to be
- ; passed as a longword in register D3. 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 zero portion has been removed from this routine.
-
- bin_to_hex: ; Converts a 32-bit binary number in D3 to
- ; ASCII hexadecimal.
- lea hexadecimal(pc), a0 ; A0 is pointer to array "hexadecimal".
- lea hex_table(pc), 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 what we have
- ; proved, so it must be cleared before use.
- rotate_and_convert:
- rol.l #4, d3 ; Rotate most significant nibble to the
- ; least significant nibble position.
- move.b d3, 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, rotate_and_convert
- move.b #0, (a0) ; Terminate hexadecimal string with a null.
- rts
-
- print_string: ; Expects address of string to be in A0.
- move.l a0, -(sp) ; 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(pc) ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp
- rts
-
- print_hex_sign:
- move.w #$24, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_on:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$47, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_off:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$48, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_on:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$70, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_off:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$71, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- data
- hex_table: dc.b '0123456789ABCDEF'
- newline: dc.b $D,$A,0
- experiment_head: dc.b 'EXPERIMENT 2: System Trap #1 Corruption',$D,$A
- dc.b ' Function = GEMDOS $4B',$D,$A,$A,0
- heading: dc.b ' Before Call After Call',$D,$A,0
- space: dc.b ' ',0
- printer_status: dc.b 0
- register_id: dc.b ' Data Reg 0: $',0,' Data Reg 1: $',0
- dc.b ' Data Reg 2: $',0,' Data Reg 3: $',0
- dc.b ' Data Reg 4: $',0,' Data Reg 5: $',0
- dc.b ' Data Reg 6: $',0,' Data Reg 7: $',0
- dc.b ' Add Reg 0: $',0,' Add Reg 1: $',0
- dc.b ' Add Reg 2: $',0,' Add Reg 3: $',0
- dc.b ' Add Reg 4: $',0,' Add Reg 5: $',0
- dc.b ' Add Reg 6: $',0,' Add Reg 7: $',0
-
- program_name: dc.b 'D:\PRG_6KC.TOS',0
- environmental_string: dc.b 0
- command_line: dc.b 0
-
- align
-
- ;bss MAJOR NOTE: Do not use the bss assembler op here. If you do, the
- ; value $44415441 will NOT be loaded into the elements
- ; of the registers_before array.
-
- trap_vector: ds.l 1 ; Store for custom trap handler's vector.
- hexadecimal: ds.l 3 ; Output buffer. Must be NULL terminated.
- registers_before: ds.l 15, $44415441 ; Store this value in each before
- reg_A7_before: ds.l 1 ; array element during loading.
- registers_after: ds.l 16 ; Array for register contents after call.
-
- ;MAJOR NOTE: Note that this program requires a larger stack than does
- ; REG_TST1 and REG_TST2. If the stack size is left at 48 words
- ; here, 4 bombs will appear when this program is executed, just
- ; it attempts to print to the printer.
-
- ds.l 96 ; Program stack.
- stack: ds.l 1 ; Address of program stack.
- program_end: ds.l 0
- end
-
-
- Figure 8.9. Program 55 Execution Results.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- The results of experiment 2 agree with the assessment
- given in the Internals book, under the heading 3.1 The
- GEMDOS, on page 106. They do not even approach congruency
- with the information given in the Peel book on page 3-2.
- Performing these experiments will give us the verified data
- that we need in order to realize maximum speed via judicious
- register usage.
-
-
- Program 56. Experiment 3: System trap #14, XBIOS function
- #$2 register contamination.
-
- ; PROGRAM NAME: REG_TST4.S
- ; VERSION: 1.001
-
- ; Assembly Instructions:
-
- ; Assemble in Relocatable mode. Save the assembled program with a TOS suffix.
-
- ; Program Function:
-
- ; Similar to REG_TST2, except this one calls a system trap #14 handler.
-
- ; EXPERIMENT 3:
-
- ; This program will call a system trap #14 handler.
-
- ; XBIOS function $2 (aka physbase) was chosen for the experiment.
- ; This function is discussed in the Internals book, however, at the
- ; site of the function description, no mention is made of the register
- ; in which the desired address is returned. We will be able to see the
- ; address in one of the registers when this program is executed.
-
- program_start: ; Calculate program size and retain result.
- lea program_end(pc), a0
- movea.l 4(a7), a1
- suba.l a1, a0
-
- 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.w d0, -(sp) ; Dummy value, can be anything.
- move.w #$4a, -(sp) ; Function = m_shrink = GEMDOS $4A.
- trap #1 ; GEMDOS call.
- adda.l #$C, sp ; Reset stack pointer to top of stack.
-
- get_printer_status: ; Get and save printer status.
- move.w #0, -(sp) ; Device = printer.
- move.w #8, -(sp) ; Function = BIOS $8 = bcostat.
- trap #13 ; If printer is on, -1 will be returned
- addq.l #4, sp ; in D0, 0 will be returned otherwise.
- move.b d0, printer_status
-
- place_values_in_registers:
- movem.l registers_before(pc), d0-d7/a0-a6
- lea stack(pc), a7
- move.l a7, reg_A7_before
-
- make_the_trap_call: ; This is Experiment 3.
-
- ; We're going to use XBIOS function $2 to determine the register in
- ; which the ram address of the physical video screen is returned. I
- ; already know that it is returned in D0, but this program will verify
- ; that information.
-
- get_system_screen_address:
- move #2, -(sp) ; Function = XBIOS $2 = physbase.
- trap #14 ;
- addq.l #2, sp
-
- store_after_call_contents:
- movem.l d0-d7/a0-a7, registers_after
-
- print_all_contents:
- bsr print_newline
- lea experiment_head(pc), a0
- bsr print_string
- lea heading(pc), a0
- bsr print_string
- lea register_id(pc), a3
- lea registers_before(pc), a4
- lea registers_after(pc), a5
- move.l #15, d4
- print_register:
- move.l (a4)+, d3 ; Convert content from binary to hex.
- move.l d3, d5 ; Save for comparison with after-value.
- bsr bin_to_hex
- movea.l a3, a0
- bsr print_string ; Print register number.
- add.l #16, a3
- lea hexadecimal(pc), a0
- bsr print_string ; Print before-call register content.
- lea space(pc), a0
- bsr print_string
- compare_and_print:
- move.l (a5)+, d3 ; Load after_call value.
- cmp.l d5, d3 ; Compare before_call to after_call.
- beq.s no_change ; Branch if no change occurred.
- tst.b printer_status ; Find out if printer is on or off.
- beq.s printer_off
- bsr bold_on ; Turn on printer bold print.
- printer_off:
- bsr reverse_on ; Turn on video reverse print.
-
- no_change:
- bsr bin_to_hex
- bsr print_hex_sign
- lea hexadecimal(pc), a0
- bsr print_string ; Print after-call value.
- bsr print_newline
- tst.b printer_status ; Find out if printer is on or off.
- beq.s _printer_off
- bsr bold_off ; Turn printer bold print off.
- _printer_off:
- bsr reverse_off ; Turn video reverse print off.
- dbra d4, print_register
- bsr print_newline
-
- wait_for_keypress:
- move.w #8, -(sp)
- trap #1
- addq.l #2, sp
-
- terminate:
- move.w #0, -(sp)
- trap #1
-
- ; The binary to ASCII hexadecimal conversion routine expects a number to be
- ; passed as a longword in register D3. 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 zero portion has been removed from this routine.
-
- bin_to_hex: ; Converts a 32-bit binary number in D3 to
- ; ASCII hexadecimal.
- lea hexadecimal(pc), a0 ; A0 is pointer to array "hexadecimal".
- lea hex_table(pc), 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 what we have
- ; proved, so it must be cleared before use.
- rotate_and_convert:
- rol.l #4, d3 ; Rotate most significant nibble to the
- ; least significant nibble position.
- move.b d3, 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, rotate_and_convert
- move.b #0, (a0) ; Terminate hexadecimal string with a null.
- rts
-
- print_string: ; Expects address of string to be in A0.
- move.l a0, -(sp) ; 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(pc) ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp
- rts
-
- print_hex_sign:
- move.w #$24, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_on:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$47, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_off:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$48, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_on:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$70, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_off:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$71, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- data
- hex_table: dc.b '0123456789ABCDEF'
- newline: dc.b $D,$A,0
- experiment_head: dc.b 'EXPERIMENT 3: System Trap #14 Corruption',$D,$A
- dc.b ' Function = XBIOS $2',$D,$A,$A,0
- heading: dc.b ' Before Call After Call',$D,$A,0
- space: dc.b ' ',0
- printer_status: dc.b 0
- register_id: dc.b ' Data Reg 0: $',0,' Data Reg 1: $',0
- dc.b ' Data Reg 2: $',0,' Data Reg 3: $',0
- dc.b ' Data Reg 4: $',0,' Data Reg 5: $',0
- dc.b ' Data Reg 6: $',0,' Data Reg 7: $',0
- dc.b ' Add Reg 0: $',0,' Add Reg 1: $',0
- dc.b ' Add Reg 2: $',0,' Add Reg 3: $',0
- dc.b ' Add Reg 4: $',0,' Add Reg 5: $',0
- dc.b ' Add Reg 6: $',0,' Add Reg 7: $',0
- align
-
- ;bss MAJOR NOTE: Do not use the bss assembler op here. If you do, the
- ; value $44415441 will NOT be loaded into the elements
- ; of the registers_before array.
-
- trap_vector: ds.l 1 ; Store for custom trap handler's vector.
- hexadecimal: ds.l 3 ; Output buffer. Must be NULL terminated.
- registers_before: ds.l 15, $44415441 ; Store this value in each before
- reg_A7_before: ds.l 1 ; array element during loading.
- registers_after: ds.l 16 ; Array for register contents after call.
- ds.l 48 ; Program stack.
- stack: ds.l 1 ; Address of program stack.
- program_end: ds.l 0
- end
-
-
- Figure 8.10. Program 56 Execution Results.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- The Internals book does not provide register
- contamination information for XBIOS calls. The Peel book
- implies that registers d0-d2/a0-a2 are subject to
- contamination during such system calls. The results of
- experiment 3 suggest that, at least when the function
- specified is XBIOS $2, only registers d0/a0-a1 are corrupted
- during the call.
-
-
- Program 57. Experiment 4: Desk accessory register corruption
- by BIOS function $8.
-
- ; PROGRAM NAME: REG_TST5.S
- ; VERSION: 1.001
-
- ; Desk Accessory (No trap handler; no interrupt handler).
-
- ; Assembly Instructions:
-
- ; Assemble in Relocatable mode and save with a PRG extension. From the
- ; desktop, click on REG_TST5.PRG, click on Show Info under the File menu,
- ; backspace over the PRG on the name line, then type in ACC, thereby
- ; changing the object code file's name to REG_TST5.ACC. Finally, place the
- ; file, REG_TST5.ACC in the root directory of your boot disk. During the
- ; next power-up cycle, the program will be installed in memory as a desk
- ; accessory.
-
- ; Program Function: EXPERIMENT 4.
-
- ; Similar to REG_TST2, but this one is a desk accessory. It does not
- ; call a trap handler, as does the first 4 programs of the group.
-
- ; Furthermore, it does not copy values from the registers_before array
- ; into the registers. Instead, processor control is relinquished via the
- ; AES $17 (aka evnt_mesag) function, with values in the registers that are
- ; placed there during the desk accessory initialization procedure.
-
- ; You should exercise this experiment by selecting REG_TST5 from within
- ; a running application which allows access to the Desk Accessory menu;
- ; you can do this from within the AssemPro editor. Or you can simply
- ; select REG_TST5 as an accessory from the desktop.
-
- ; First, you can convince yourself that all registers are altered by
- ; executing REG_TST1, REG_TST2, or REG_TST3 from within AssemPro. Then
- ; select REG_TST5 from the Accessory menu.
-
- ; When this program is selected thus, a message will be sent to the
- ; program by the operating system, thereby forcing the message handling
- ; algorithm to execute. The message handler will print the register
- ; contents, as does the other programs in this group.
-
- ; Since this program does not produce its own screen, its output will
- ; obliterate the screen of the application from which it is selected. To
- ; recover, press the Return key to force this program to relinquish
- ; processor control, then move the mouse arrow around, near the spot where
- ; the applications menu line should be. When you do this, you will see
- ; items appear as they normally would. You can then select any function
- ; that will cause the screen to be drawn, such as Debugger, if you are
- ; working within AssemPro. Or you may be able to find the window close box
- ; in the upper left hand corner of the window. If you can't get the hang
- ; of it, just reset the computer manually (a cold reset is best most often).
-
- ; The knowledge we wish to gain from the results of this experiment is
- ; an answer to the question "Does the operating system protect desk
- ; accessory registers from corruption while the accessory is waiting to be
- ; activated?". That is, if we store the address of the control array in
- ; register a4 during the accessory initialization procedure, for example,
- ; will that address still be in register a4 when the accessory is activated.
-
- ; If the desk accessory registers are protected from corruption while
- ; it is waiting for a message, then we can store values in the registers
- ; during the initialization procedure and use them in the message handler,
- ; without taking the time to store the values during the execution of the
- ; message handler. That will permit faster message handler response.
-
- program_start:
- lea stack(pc), a7 ; A7 points to program's stack address.
- move.l a7, reg_A7_before ; Store stack address in register array.
- lea aes_pb(pc), a3 ; aes_pb = AES parameter block.
- lea control(pc), a4 ; A4 is pointer for array "control".
- move.w #$C8, d3 ; *** AES call number in D3.
-
- ; I am interested in the volatility of the above registers. Do they
- ; maintain their values until I alter them in this program, or are they
- ; corrupted when the desk accessory is entered via the evnt_mesag
- ; function?
-
- initialize_application:
- move.w #$A, (a4) ; Function = appl_init = AES $A.
- move.w #1, 4(a4) ; Return one int_out parameter.
- move.l a3, d1 ; A3 contains address of aes parameter block.
- move.w d3, d0 ; D3 contains AES call number.
- trap #2 ; apid returned in int_out[0] and global[2].
-
- menu_installation:
- move.w #$23, (a4) ; Function = menu_register = AES $23.
- move.w #1, 2(a4) ; Pass one int_in parameter.
- move.w #1, 6(a4) ; Pass one addr_in parameter.
- move.w int_out(pc), int_in ; Application identification to int_in[0].
- move.l #menu_text, addr_in ; Menu text address to addr_in[0].
- move.l a3, d1 ; Address of aes parameter block to D1.
- move.w d3, d0 ; D3 contains AES call number.
- trap #2 ; Menu identification number returned
- ; in int_out[0].
- move.w int_out, d4 ; Store menu identification number in D4.
-
- ; MAIN ACCESSORY LOOP
-
- wait_for_message: ; Relinquish processor control.
- move.w #$17, (a4) ; Function = evnt_mesag = AES $17.
- move.w #0, 2(a4)
- move.l #message, addr_in ; Address of message array to addr_in.
-
- store_register_contents:
- movem.l d0-d7/a0-a6, registers_before
-
- move.l a3, d1 ; Address of aes parameter block to D1.
- move.w d3, d0 ; AES call number to D0.
- trap #2 ; Message received is placed in array
- ; "message".
-
- ; Enters here only when a message is received.
-
- message_handler: ; Entrance point when message is received.
- cmpi.w #$28, message ; Compare AC OPEN code with message[0].
- bne.s wait_for_message ; Execute the evnt_mesag function.
- move.w message+8, d0 ; id_selected (message[4]) = menu ID of
- ; item user selected.
- cmp.w d4, d0 ; Was this application selected.
- bne.s wait_for_message ; Execute the evnt_mesag function.
-
- store_after_entrance_register_contents:
- movem.l d0-d7/a0-a7, registers_after
-
- get_printer_status: ; Get and save printer status.
- move.w #0, -(sp) ; Device = printer.
- move.w #8, -(sp) ; Function = BIOS $8 = bcostat.
- trap #13 ; If printer is on, -1 will be returned
- addq.l #4, sp ; in D0, 0 will be returned otherwise.
- move.b d0, printer_status
-
- ; The following algorithm prints the before value for a register, then,
- ; on the same line, it prints the after value.
-
- print_all_contents:
- bsr print_newline
- lea experiment_head(pc), a0
- bsr print_string
- lea heading(pc), a0
- bsr print_string
- lea register_id(pc), a3
- lea registers_before(pc), a4
- lea registers_after(pc), a5
- move.l #15, d4
- print_register:
- move.l (a4)+, d3 ; Convert content from binary to hex.
- move.l d3, d5 ; Save for comparison with after-value.
- bsr bin_to_hex
- movea.l a3, a0
- bsr print_string ; Print register number.
- add.l #16, a3
- lea hexadecimal(pc), a0
- bsr print_string ; Print before-call register content.
- lea space(pc), a0
- bsr print_string
- compare_and_print:
- move.l (a5)+, d3 ; Load after_call value.
- cmp.l d5, d3 ; Compare before_call to after_call.
- beq.s no_change ; Branch if no change occurred.
- tst.b printer_status ; Find out if printer is on or off.
- beq.s printer_off
- bsr bold_on ; Turn on printer bold print.
- printer_off:
- bsr reverse_on ; Turn on video reverse print.
-
- no_change:
- bsr bin_to_hex
- bsr print_hex_sign
- lea hexadecimal(pc), a0
- bsr print_string ; Print after-call value.
- bsr print_newline
- tst.b printer_status ; Find out if printer is on or off.
- beq.s _printer_off
- bsr bold_off ; Turn printer bold print off.
- _printer_off:
- bsr reverse_off ; Turn video reverse print off.
- dbra d4, print_register
- bsr print_newline
-
- wait_for_keypress:
- move.w #8, -(sp)
- trap #1
- addq.l #2, sp
-
- ; Now we have to restore the desk accessory registers because our printing
- ; algorithm has corrupted them. Also, we have learned that the system
- ; corrupts our stack pointer. We restore all registers, except A7, to the
- ; values they had when the accessory was activated. Register A7 retains
- ; the original stack address. During successive activations of the
- ; accessory, we will be able to see any changes.
-
- movem.l registers_after(pc), d0-d7/a0-a6
- bra wait_for_message
-
- ; The binary to ASCII hexadecimal conversion routine expects a number to be
- ; passed as a longword in register D3. 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 zero portion has been removed from this routine.
-
- bin_to_hex: ; Converts a 32-bit binary number in D3 to
- ; ASCII hexadecimal.
- lea hexadecimal(pc), a0 ; A0 is pointer to array "hexadecimal".
- lea hex_table(pc), 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 what we have
- ; proved, so it must be cleared before use.
- rotate_and_convert:
- rol.l #4, d3 ; Rotate most significant nibble to the
- ; least significant nibble position.
- move.b d3, 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, rotate_and_convert
- move.b #0, (a0) ; Terminate hexadecimal string with a null.
- rts
-
- print_string: ; Expects address of string to be in A0.
- move.l a0, -(sp) ; 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(pc) ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp
- rts
-
- print_hex_sign:
- move.w #$24, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_on:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$47, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_off:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$48, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_on:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$70, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_off:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$71, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- data
- hex_table: dc.b '0123456789ABCDEF'
- newline: dc.b $D,$A,0
- experiment_head: dc.b 'EXPERIMENT 4: Desk Accessory Corruption By',$D,$A
- dc.b ' Function = BIOS $8',$D,$A,$A,0
- heading: dc.b ' Before Call After Call',$D,$A,0
- space: dc.b ' ',0
- printer_status: dc.b 0
- register_id: dc.b ' Data Reg 0: $',0,' Data Reg 1: $',0
- dc.b ' Data Reg 2: $',0,' Data Reg 3: $',0
- dc.b ' Data Reg 4: $',0,' Data Reg 5: $',0
- dc.b ' Data Reg 6: $',0,' Data Reg 7: $',0
- dc.b ' Add Reg 0: $',0,' Add Reg 1: $',0
- dc.b ' Add Reg 2: $',0,' Add Reg 3: $',0
- dc.b ' Add Reg 4: $',0,' Add Reg 5: $',0
- dc.b ' Add Reg 6: $',0,' Add Reg 7: $',0
- menu_text: dc.b ' REG_TST5 ',0
- align ; Align storage on a word boundary.
- ;
- ; AES PARAMETER BLOCK
- ;
- aes_pb: dc.l control,global,int_in,int_out,addr_in,addr_out
- ;
- ; AES CONTROL TABLE
- ;
- bss
- control: ds.w 5
- global: ds.w 3
- ds.l 6
- int_in: ds.w 1 ; Input parameter.
- int_out: ds.w 1 ; Output parameter.
- addr_in: ds.l 1 ; Input address.
- addr_out: ds.l 1 ; Output address.
- ;
- ; OTHER VARIABLES
- ;
- message: ds.w 8
- trap_vector: ds.l 1 ; Store for custom trap handler's vector.
- hexadecimal: ds.l 3 ; Output buffer. Must be NULL terminated.
- registers_before: ds.l 15
- reg_A7_before: ds.l 1 ; Keep stack separate and constant.
- registers_after: ds.l 16 ; Array for register contents after call.
- ds.l 48
- stack: ds.l 0
- program_end: ds.l 0
- end
-
-
- Figure 8.11. Program 57 Execution Results.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- The results obtain via experiment 4 are significant.
- It is easy to see that the ST's limited multi-tasking
- environment includes the code required to protect accessory
- registers from contamination. Therefore, when the accessory
- is activated, the values placed in registers before the
- evnt_mesag function is executed are still valid. The AES
- functions used in program 57 are explained in the Abacus GEM
- and the COMPUTE! AES books.
-
-
- Program 58. Experiment 5A: Custom trap #13 handler
- corruption of calling application registers, and
- contamination of handler registers by the calling
- application.
-
- ; PROGRAM NAME: REG_TST6.S
- ; VERSION: 1.001
-
- ; Assembly Instructions:
-
- ; Assemble in Relocatable mode and save with a TOS extension.
-
- ; Program Function: EXPERIMENT 5A.
-
- ; Similar to REG_TST2, but this one installs a custom trap #13 handler.
- ; The handler intercepts all BIOS calls, passing all but one custom function
- ; on to the system trap #13 handler. Since there is no BIOS function $C, I
- ; use that number for the function to be processed by the custom handler.
-
- ; In this program, I do not copy values from the registers_before
- ; array into the registers. Instead, processor control is relinquished,
- ; via GEMDOS function $31 (aka ptermres), with values in the registers that
- ; are placed there during the load and stay resident (LSR) initialization
- ; procedure.
-
- ; Execute this program from the desktop. Then execute program REG_TST7,
- ; the program that is used to exercise this program.
-
- ; As with the desk accessory, REG_TST5, you may want to execute one of
- ; the first three programs of the group from within AssemPro to alter all
- ; registers. Then, from within AssemPro, you can execute REG_TST7, which
- ; calls the custom BIOS function $C.
-
- ; Since this program does not produce its own screen, its output will
- ; obliterate the screen of the application from which it is selected. To
- ; recover, press the Return key to force this program to relinquish
- ; processor control, then move the mouse arrow around, near the spot where
- ; the applications menu line should be. When you do this, you will see
- ; items appear as they normally would. You can then select any function
- ; that will cause the screen to be drawn, such as Debugger, if you are
- ; working within AssemPro. Or you may be able to find the window close box
- ; in the upper left hand corner of the window. If you can't get the hang
- ; of it, just reset the computer manually (a cold reset is best most often).
-
- ; The knowledge we wish to gain from the results of this experiment is
- ; an answer to the question "does the operating system protect the registers
- ; of custom traps handlers that are installed by a program that is resident
- ; via the ptermres function. This is the same kind of data we wanted to
- ; obtain with REG_TST5 for desk accessories.
-
- ; If the trap handler registers are protected from corruption, then we
- ; can store values in the registers during the initialization procedure and
- ; use them in the trap handler, without taking the time to store the values
- ; during trap interceptions. That would permit faster trap handler
- ; response.
-
- program_start: ; Compute program size and retain result.
- lea program_end(pc), a3
- movea.l 4(a7), a4
- suba.l a4, a3 ; Yields size of memory that must remain
- ; resident.
- load_stack_address:
- lea stack(pc), a7
-
- install_new_trap_13_vector:
- pea custom_trap_handler(pc) ; Push new trap handler address onto stack.
- move.w #$2D, -(sp) ; Trap 13 vector number.
- move.w #5, -(sp) ; Function = setexec = BIOS $5.
- trap #13 ; Current trap handler vector returned in D0.
- addq.l #8, sp
- move.l d0, preempted_handler_address
-
- store_register_contents:
- movem.l d0-d7/a0-a7, registers_before
-
- relinquish_processor_control: ; Maintain memory residency.
- move.w #0, -(sp) ; See page 121 of Internals book.
- move.l a3, -(sp) ; Size of memory to remain resident.
- move.w #$31, -(sp) ; Function = ptermres = GEMDOS $31.
- trap #1
-
- custom_trap_handler:
- move.l usp(pc), a0 ; Load address of current top of user stack.
-
- get_processor_status:
- btst #5, (sp) ; User mode test.
- beq.s was_user_mode ; No adjustment is necessary if the
- ; processor was in user mode.
- movea.l sp, a0 ; Load current top of supervisor stack.
- addq.l #6, a0 ; Adjust SSP for user mode type data access.
-
- was_user_mode: ; Processing for either mode follows.
- cmpi.w #$C, (a0) ; Calling the custom BIOS function?
- bne not_desired_call
-
- store_after_entrance_register_contents:
- movem.l d0-d7/a0-a7, registers_after
-
- get_printer_status: ; Get and save printer status.
- move.w #0, -(sp) ; Device = printer.
- move.w #8, -(sp) ; Function = BIOS $8 = bcostat.
- trap #13 ; If printer is on, -1 will be returned
- addq.l #4, sp ; in D0, 0 will be returned otherwise.
- move.b d0, printer_status
-
- print_all_contents:
- bsr print_newline
- lea experiment_head(pc), a0
- bsr print_string
- lea heading(pc), a0
- bsr print_string
- lea register_id(pc), a3
- lea registers_before(pc), a4
- lea registers_after(pc), a5
- move.l #15, d4
- print_register:
- move.l (a4)+, d3 ; Convert content from binary to hex.
- move.l d3, d5 ; Save for comparison with after-value.
- bsr bin_to_hex
- movea.l a3, a0
- bsr print_string ; Print register number.
- add.l #16, a3
- lea hexadecimal(pc), a0
- bsr print_string ; Print before-call register content.
- lea space(pc), a0
- bsr print_string
- compare_and_print:
- move.l (a5)+, d3 ; Load after_call value.
- cmp.l d5, d3 ; Compare before_call to after_call.
- beq.s no_change ; Branch if no change occurred.
- tst.b printer_status ; Find out if printer is on or off.
- beq.s printer_off
- bsr bold_on ; Turn on printer bold print.
- printer_off:
- bsr reverse_on ; Turn on video reverse print.
-
- no_change:
- bsr bin_to_hex
- bsr print_hex_sign
- lea hexadecimal(pc), a0
- bsr print_string ; Print after-call value.
- bsr print_newline
- tst.b printer_status ; Find out if printer is on or off.
- beq.s _printer_off
- bsr bold_off ; Turn printer bold print off.
- _printer_off:
- bsr reverse_off ; Turn video reverse print off.
- dbra d4, print_register
- bsr print_newline
-
- wait_for_keypress:
- move.w #8, -(sp)
- trap #1
- addq.l #2, sp
-
- ; Now we have to restore the LSR's registers because our printing
- ; algorithm has corrupted them. Of course, we will want to know
- ; if this values are received by the calling source. If they are, then
- ; the trap handler will have corrupted the calling source's registers.
-
- movem.l registers_before(pc), d0-d7/a0-a6
- rte
-
- not_desired_call:
- movea.l preempted_handler_address(pc), a0
- jmp (a0) ; JUMP TO PREEMPTED TRAP #13 HANDLER
-
- ; The binary to ASCII hexadecimal conversion routine expects a number to be
- ; passed as a longword in register D3. 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 zero portion has been removed from this routine.
-
- bin_to_hex: ; Converts a 32-bit binary number in D3 to
- ; ASCII hexadecimal.
- lea hexadecimal(pc), a0 ; A0 is pointer to array "hexadecimal".
- lea hex_table(pc), 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 what we have
- ; proved, so it must be cleared before use.
- rotate_and_convert:
- rol.l #4, d3 ; Rotate most significant nibble to the
- ; least significant nibble position.
- move.b d3, 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, rotate_and_convert
- move.b #0, (a0) ; Terminate hexadecimal string with a null.
- rts
-
- print_string: ; Expects address of string to be in A0.
- move.l a0, -(sp) ; 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(pc) ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp
- rts
-
- print_hex_sign:
- move.w #$24, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_on:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$47, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_off:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$48, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_on:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$70, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_off:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$71, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- data
- hex_table: dc.b '0123456789ABCDEF'
- newline: dc.b $D,$A,0
- experiment_head: dc.b 'EXPERIMENT 5A: System Trap #13 Corruption',$D,$A
- dc.b ' Function = BIOS $C = Custom',$D,$A,$A,0
- heading: dc.b ' Before Call After Call',$D,$A,0
- space: dc.b ' ',0
- printer_status: dc.b 0
- register_id: dc.b ' Data Reg 0: $',0,' Data Reg 1: $',0
- dc.b ' Data Reg 2: $',0,' Data Reg 3: $',0
- dc.b ' Data Reg 4: $',0,' Data Reg 5: $',0
- dc.b ' Data Reg 6: $',0,' Data Reg 7: $',0
- dc.b ' Add Reg 0: $',0,' Add Reg 1: $',0
- dc.b ' Add Reg 2: $',0,' Add Reg 3: $',0
- dc.b ' Add Reg 4: $',0,' Add Reg 5: $',0
- dc.b ' Add Reg 6: $',0,' Add Reg 7: $',0
- align
- preempted_handler_address: ds.l 1
- trap_vector: ds.l 1 ; Store for custom trap handler's vector.
- hexadecimal: ds.l 3 ; Output buffer. Must be NULL terminated.
- registers_before: ds.l 15, 0 ; Store this value in each before
- reg_A7_before: ds.l 1 ; array element during loading.
- registers_after: ds.l 16 ; Array for register contents after call.
- ds.l 48 ; Stack
- stack: ds.l 1 ; Address of stack.
- program_end: ds.l 0
- end
-
-
- Figure 8.12. Program 58 Execution Results.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Program 59. Experiment 5B: Custom trap #13 handler
- corruption of calling application registers, and
- contamination of handler registers by the calling
- application.
-
- ; PROGRAM NAME: REG_TST7.S
- ; VERSION: 1.001
-
- ; Assembly Instructions:
-
- ; Assemble in Relocatable mode and save with a TOS extension.
-
- ; Program Function: EXPERIMENT 5B.
-
- ; Similar to REG_TST2, but this one calls a custom trap #13 handler.
- ; The handler intercepts all BIOS calls, passing all but one custom function
- ; on to the system trap #13 handler. Since there is no BIOS function $C, I
- ; use that number for the function to be processed by the custom handler.
-
- ; Execution Instructions:
-
- ; Execute program REG_TST6 from the desktop. Then execute this program
- ; from the desktop or from within AssemPro.
-
- ; This program will call the custom trap #13 handler that must be
- ; already resident in ram, having been established by REG_TST6. Before the
- ; call, values that were stored in a register array, while the program was
- ; being loaded into ram, are placed in each register. The custom trap
- ; handler will alter all registers. We want to see if those alterations
- ; are passed back to this program.
-
- ; The custom handler will print its before and after register contents,
- ; the relinquish processor control back to this program.
-
- ; Immediately after the call, the content of each register is stored in
- ; a second register array. Then the content of each register, as it was
- ; before the call, is printed. Next, the content of the after_call array
- ; is compared to the content of the before-call array, and the values in
- ; the after-call array are printed; however, register values in the
- ; after-call array which do not match the before-call value for each
- ; corresponding register are printed in reverse colors on the video screen.
-
- program_start: ; Calculate program size and retain result.
- lea program_end(pc), a0
- movea.l 4(a7), a1
- suba.l a1, a0
-
- 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.w d0, -(sp) ; Dummy value, can be anything.
- move.w #$4a, -(sp) ; Function = m_shrink = GEMDOS $4A.
- trap #1 ; GEMDOS call.
- adda.l #$C, sp ; Reset stack pointer to top of stack.
-
- get_printer_status: ; Get and save printer status.
- move.w #0, -(sp) ; Device = printer.
- move.w #8, -(sp) ; Function = BIOS $8 = bcostat.
- trap #13 ; If printer is on, -1 will be returned
- addq.l #4, sp ; in D0, 0 will be returned otherwise.
- move.b d0, printer_status
-
- place_values_in_registers:
- movem.l registers_before(pc), d0-d7/a0-a6
- lea stack(pc), a7
- move.l a7, reg_A7_before
-
- make_trap_call:
- move.l #0, -(sp)
- move.w #$C, -(sp)
- trap #13
- addq.l #6, sp
-
- store_after_call_contents:
- movem.l d0-d7/a0-a7, registers_after
-
- ; The following algorithm prints the before value for a register, then,
- ; on the same line, it prints the after value. This provides a more
- ; compact display, and one in which it is easier to compare the before
- ; and after values.
-
- print_all_contents:
- bsr print_newline
- lea experiment_head(pc), a0
- bsr print_string
- lea heading(pc), a0
- bsr print_string
- lea register_id(pc), a3
- lea registers_before(pc), a4
- lea registers_after(pc), a5
- move.l #15, d4
- print_register:
- move.l (a4)+, d3 ; Convert content from binary to hex.
- move.l d3, d5 ; Save for comparison with after-value.
- bsr bin_to_hex
- movea.l a3, a0
- bsr print_string ; Print register number.
- add.l #16, a3
- lea hexadecimal(pc), a0
- bsr print_string ; Print before-call register content.
- lea space(pc), a0
- bsr print_string
- compare_and_print:
- move.l (a5)+, d3 ; Load after_call value.
- cmp.l d5, d3 ; Compare before_call to after_call.
- beq.s no_change ; Branch if no change occurred.
- tst.b printer_status ; Find out if printer is on or off.
- beq.s printer_off
- bsr bold_on ; Turn on printer bold print.
- printer_off:
- bsr reverse_on ; Turn on video reverse print.
-
- no_change:
- bsr bin_to_hex
- bsr print_hex_sign
- lea hexadecimal(pc), a0
- bsr print_string ; Print after-call value.
- bsr print_newline
- tst.b printer_status ; Find out if printer is on or off.
- beq.s _printer_off
- bsr bold_off ; Turn printer bold print off.
- _printer_off:
- bsr reverse_off ; Turn video reverse print off.
- dbra d4, print_register
- bsr print_newline
-
- wait_for_keypress:
- move.w #8, -(sp)
- trap #1
- addq.l #2, sp
-
- terminate:
- move.w #0, -(sp)
- trap #1
-
- ; The binary to ASCII hexadecimal conversion routine expects a number to be
- ; passed as a longword in register D3. 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 zero portion has been removed from this routine.
-
- bin_to_hex: ; Converts a 32-bit binary number in D3 to
- ; ASCII hexadecimal.
- lea hexadecimal(pc), a0 ; A0 is pointer to array "hexadecimal".
- lea hex_table(pc), 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 what we have
- ; proved, so it must be cleared before use.
- rotate_and_convert:
- rol.l #4, d3 ; Rotate most significant nibble to the
- ; least significant nibble position.
- move.b d3, 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, rotate_and_convert
- move.b #0, (a0) ; Terminate hexadecimal string with a null.
- rts
-
- print_string: ; Expects address of string to be in A0.
- move.l a0, -(sp) ; 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(pc) ; Push address of string onto stack.
- move.w #9, -(sp) ; Function = c_conws = GEMDOS $9.
- trap #1 ; GEMDOS call
- addq.l #6, sp
- rts
-
- print_hex_sign:
- move.w #$24, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_on:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$47, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- bold_off:
- move.w #$1B, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$48, -(sp) ; Your printer's code may be different.
- move.w #0, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_on:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$70, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- reverse_off:
- move.w #$1B, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- move.w #$71, -(sp)
- move.w #2, -(sp)
- move.w #3, -(sp)
- trap #13
- addq.l #6, sp
- rts
-
- data
- hex_table: dc.b '0123456789ABCDEF'
- newline: dc.b $D,$A,0
- experiment_head: dc.b 'EXPERIMENT 5B: Custom Trap #13 Corruption',$D,$A
- dc.b ' Function = BIOS $C = Custom',$D,$A,$A,0
- heading: dc.b ' Before Call After Call',$D,$A,0
- space: dc.b ' ',0
- printer_status: dc.b 0
- register_id: dc.b ' Data Reg 0: $',0,' Data Reg 1: $',0
- dc.b ' Data Reg 2: $',0,' Data Reg 3: $',0
- dc.b ' Data Reg 4: $',0,' Data Reg 5: $',0
- dc.b ' Data Reg 6: $',0,' Data Reg 7: $',0
- dc.b ' Add Reg 0: $',0,' Add Reg 1: $',0
- dc.b ' Add Reg 2: $',0,' Add Reg 3: $',0
- dc.b ' Add Reg 4: $',0,' Add Reg 5: $',0
- dc.b ' Add Reg 6: $',0,' Add Reg 7: $',0
- align
-
- ;bss MAJOR NOTE: Do not use the bss assembler op here. If you do, the
- ; value $44415441 will NOT be loaded into the elements
- ; of the registers_before array.
-
- trap_vector: ds.l 1 ; Store for custom trap handler's vector.
- hexadecimal: ds.l 3 ; Output buffer. Must be NULL terminated.
- registers_before: ds.l 15, $44415441 ; Store this value in each before
- reg_A7_before: ds.l 1 ; array element during loading.
- registers_after: ds.l 16 ; Array for register contents after call.
- ds.l 48 ; Program stack.
- stack: ds.l 1 ; Address of program stack.
- program_end: ds.l 0
- end
-
-
- Figure 8.13. Program 59 Execution Results.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Conclusion
-
- We have begun our journey into the belly of the beast.
- Learning to redirect the activities of the operating system
- is one of the more rewarding programming experiences. In
- the next chapter, we will become more involved with this
- process, and I am sure that you will be delighted with the
- examples I have chosen. However, I must impress you, not
- with the expanse of my knowledge, but, rather, with its
- limitations.
- I encourage you to perform your own experiments, using
- reference material when you can, exploring the unknown when
- you can't. The ST specific magazines are an especially
- valuable source of fresh ideas and information. In
- addition, the AssemPro debugger is a valuable tool that you
- can use to explore programs written by others. Furthermore,
- do not neglect the other programming languages as sources
- for ideas. Remember, no matter how much information I am
- able to pass on to you, it is merely a droplet.
-
-