Upon a cold or warm boot, microprocessors in the 680x0 series load the initial supervisor stack pointer from the first longword in memory ($0) and begin execution at the PC found in the second longword ($4). The location this points to is the base initialization point for Atari computers.
Every Atari computer follows a predefined set of steps to accomplish system initialization. The following illustrates these steps leaving out some hardware initialization which is specific to the particular computer line (ST, TT, Falcon, etc.).
The Interrupt Priority Level (IPL) is set to 7 and the OS switches to supervisor mode.
A RESET instruction is executed to reset external hardware devices.
The presence of a diagnostic cartridge is determined. If one is inserted, it is JMP'ed to with a return address in register A6.
If running on a 68030, the CACR, VBR, TC, TT0, and TT1 registers are initialized.
If a floating-point coprocessor is present it is initialized.
If the memvalid ($420), memval2 ($43A), and memval3 ($51A) system variables are all valid, a warm boot is assumed and the memory controller is initialized with the value from memcntrl ($424).
The initial color palette registers are loaded and the screen base is initialized to $100000.
Memory is sized if it wasn't from a previous reset.
System variables and the cookie jar are initialized.
The BIOS initialization point is executed.
Installed cartridges of type 2 are executed.
The screen resolution is programmed.
Installed cartridges of type 0 are executed.
Interrupts are enabled by lowering the IPL to 3.
Installed cartridges of type 1 are executed.
The GEMDOS initialization point is executed.
On systems running TOS 2.06 or TOS 3.06 and above, the Fuji logo is displayed and a memory test and hard disk spin-up sequence is executed.
If at least one floppy drive is attached to the system, the first sector of the first floppy drive is loaded, and if executable, it is called.
If at least one hard disk or other media is attached to the system, the first sector of each is loaded in succession until one with an executable sector is found or each has been tried.
If a hard disk sector was found that was executable, it is executed.
The text cursor is enabled.
All "\AUTO\*.PRG" files found on the boot disk are executed.
If _cmdload ($482) is 0 then an environment string is created and the AES is launched, otherwise "\COMMAND.PRG" is loaded.
If the AES ever terminates, the system is reset and system initialization begins again.
The address of the start of operating system is stored in the system variable _sysbase ($4F2). The beginning of the operating system contains a table with contents as follows:
Some versions of AHDI (the Atari Hard Disk Interface) contain a bug which copies the system header to RAM and then corrupts some portions of it. The following 'C' structure definition defines the OSHEADER structure. The function GetROMSysbase() can be used to return an OSHEADER pointer to the code in ROM. GetROMSysbase() will execute properly in either user or supervisor mode.
typedef struct _osheader { UWORD os_entry; UWORD os_version; VOID *reseth; struct _osheader *os_beg; char *os_end; char *os_rsv1; char *os_magic; LONG os_date; UWORD os_conf; UWORD os_dosdate; /* Available as of TOS 1.02 */ char **p_root; char **p_kbshift; char **p_run; char *p_rsv2; } OSHEADER; #define _sysbase ((OSHEADER **)0x4F2) OSHEADER * GetROMSysbase( VOID ) { OSHEADER *osret; char *savesp = (Super(SUP_INQUIRE) ? NULL : Super(SUP_SET)); osret = (*_sysbase)->os_beg; if( savesp ) Super( savesp ); return osret; }
|
Country |
---|---|
|
USA |
|
Germany |
|
France |
|
United Kingdom |
|
Spain |
|
Italy |
|
Sweden |
|
Switzerland (French) |
|
Switzerland (German) |
|
Turkey |
|
Finland |
|
Norway |
|
Denmark |
|
Saudi Arabia |
|
Holland |
|
Czechoslovakia |
|
Hungary |
|
All countries are supported. As of TOS 4.0 the OS is compiled with text for all languages and switches between them based on the country code stored in non-volatile RAM. Use the '_AKP' cookie to determine the actual language in use. |
typedef struct { /* $87654321 if GEM present */ LONG gem_magic; /* End address of OS RAM usage */ LONG gem_end; /* Execution address of GEM */ LONG gem_entry; } MUPB;
GEM is only launched at system startup if gem_magic is $87654321. The XBIOS call Puntaes() also uses this information to restart the operating system after clearing GEM (only if disk-based). It verifies that gem_magic was valid and that GEM was in RAM, then it modifies gem_magic and restarts the operating system.
The OS header entry p_kbshift provides a method of reading the state of the keyboard shift state variables more quickly than with Kbshift(). This header entry did not exist in TOS 1.0. The following code provides an acceptable method for accessing this variable in all TOS versions:
#define Kbstate *p_kbshift char *p_kbshift; VOID init_kbshift( VOID ) { /* See above for GetROMSysbase() definition. */ OSHEADER *os = GetROMSysbase(); if ( os->os_version == 0x0100) p_kbshift = (char *)0xE1BL; else p_kbshift = *(char **)os->p_kbshift; }
The OS header entry _p_run is used to locate the of the basepage of the currently running process. This entry has only existed as of TOS 1.02 and should never be modified. The following routine returns the address of the basepage of the currently running process in all versions of TOS:
#define SPAIN 4 typedef long PID PID * get_run() { OSHEADER *os = GetROMSysbase(); if(os->os_version < 0x0102) { if(( os->os_conf >> 1 ) == SPAIN) return (PID *)0x873C; else return (PID *)0x602C; } else return (PID *)(os->p_run); }
The location of the cookie jar is determined by the address contained in the system variable _p_cookies ($5A0). If no cookie jar has been allocated yet, this entry will contain NULL (0).
The variable _p_cookies points to multiple COOKIE structures as defined below:
typedef struct { LONG cookie; LONG value; } COOKIE;
The structure member cookie contains a value that hopefully uniquely identifies the cookie. cookie values are 4-byte packed longword identifiers (often a 4 letter ASCII code word). Entries with the high byte equal to $5F, the underscore character, are reserved for use by Atari.
The structure member value may contain any value meaningful to an application or no value at all. In some cases a cookie won't have a meaningful value and its presence simply signals the existence of another process or system feature. TSR's often use value to store a pointer to an internal structure. The operating system uses cookies to signal the availability of hardware devices or system features.
The end of the cookie jar is signaled with a final entry with the value for cookie equaling NULL. The value entry for this final cookie contains the number of entries possible without reallocating the jar.
WORD getcookie( target, p_value ) LONG target; LONG *p_value; { char *oldssp; COOKIE *cookie_ptr; oldssp = (Super(SUP_INQUIRE) ? NULL : Super(1L)); cookie_ptr = *(COOKIE **)0x5A0; if(oldssp) Super( oldssp ); if(cookie_ptr != NULL) { do { if(cookie_ptr->cookie == target) { if(p_value != NULL) *p_value = cookie_ptr->value; return 1; } } while((cookie_ptr++)->cookie != 0L); } return 0; }
To place a cookie, the TSR must first locate the current location of the cookie jar. It is possible that a cookie jar does not exist ( _p_cookies == 0 ). In that case, a new jar should be allocated.
In most instances, the cookie jar should be allocated in increments of 8 slots (though it is not a requirement). In addition, if the process installs a new cookie jar in a TOS version lower than 1.06 it is also the processes responsibility to remove it upon a warm reset. Calling the following code after installing the cookie jar for the first time will ensure that the cookie jar pointer is properly reset on a warm boot.
RESMAGIC equ $31415926 _resvalid equ $426 _resvector equ $42A _p_cookies equ $5A0 .globl _unjar _unjar: move.l _resvalid,valsave move.l _resvector,vecsave move.l #reshand,_resvector move.l #RESMAGIC,_resvalid rts reshand: clr.l _p_cookies move.l vecsave,_resvector move.l valsave,_resvalid jmp (a6) .bss vecsave: .ds.l 1 valsave .ds.l 1
After determining the location of the cookie jar, the application should search for the first empty slot in the jar by looking for a NULL in the cookie field of a slot. Next, the application must determine if this is the last slot in the jar by comparing the entry in the value field of the current cookie to the number of the actual slot you are comparing. For instance, if you have found NULL as the value for cookie in slot 16 and value is equal to 16, the jar is full and must be reallocated.
If the slot found is not the last one, the application can simply copy the current slot to the next slot and insert its own cookie.
If the jar must be reallocated, you should allocate enough memory to increase the size of the cookie jar, copy the old entries to the new jar, insert your entry as the last cookie in the jar, and finally terminate the jar with a cookie containing a NULL and the new number of slots you have allocated.
Though not mentioned previously, it is also advisable to ensure that your cookie isn't already in the jar before placing it to avoid two cookies for multiple executions of the same application to appear.
|
value |
---|---|
|
The low WORD of the CPU cookie
contains a number representing the processor installed in
the system as follows: Value Processor
0 68000 10 68010 20 68020 30 68030 |
|
This cookie represents the revision of the
video shifter present. The low WORD represents the
minor revision number and the high WORD represents
the major revision number. Currently valid values
are: Major Minor Shifter
0 0 ST 1 0 STe 2 0 TT030 3 0 Falcon030 |
|
This cookie identifies the presence of
floating-point math capabilities in the system. A non-zero
low WORD indicates the presence of software floating
point support (no specific values have yet been assigned).
The high WORD indicates the type of coprocessor
currently connected to the system as follows:
Value Meaning
0 No FPU is installed. 1 SFP004 2 68881 or 68882 3 68881 or 68882 and SFP004 4 68881 5 68881 and SFP004 6 68882 7 68882 and SFP004 8 68040 Internal 9 68040 Internal and SFP004 |
|
This cookie indicates the capability of the
currently connected floppy drive. The lowest three bytes is
a code indicating the origin of the unit ('ATC' is an Atari
unit). The upper byte is a value indicating the highest
density floppy present as follows: Value
Density
0 360 Kb/ 720 Kb 1 1.44 Mb 2 2.88 Mb |
|
This cookie contains a bitmap of sound
features available to the system as follows:
Bit Feature
0 GI Sound Chip (PSG) 1 Stereo 8-bit Playback 2 DMA Record (w/XBIOS) 3 16-bit CODEC 4 DSP |
|
This cookie indicates the machine type with
the major revision number in the high WORD and the
minor revision number in the low WORD as
follows: Major Minor Shifter
0 0 ST 1 0 STe 1 8 ST Book 1 16 Mega STe 2 0 TT030 3 0 Falcon030 |
|
On machines that contain internal configuration dip switches, this value specifies their positions as a bitmap. Dip switches are generally used to indicate the presence of additional hardware which will be represented by other cookies. |
|
This cookie is present when alternative RAM is present. It points to a 64k buffer that may be used by DMA device drivers to transfer memory between alternative RAM and ST RAM for DMA operations. |
|
The presence of this cookie indicates that file and record locking extensions to GEMDOS exist. The value field is a version number currently undefined. |
|
This cookie indicates the presence of
networking software. The cookie value points to a structure
which gives manufacturer and version information as
follows:
struct netinfo { LONG publisher; LONG version; }; |
|
This cookie defines the currently configured
date and time format, Bits #0-7 contain the ASCII code of
the date separator. Bits #8-11 contain a value indicating
the date display format as follows: Value
Meaning
0 MM-DD-YY 1 DD-MM-YY 2 YY-MM-DD 3 YY-DD-MMBits #12-15 contain a value indicating the time format as follows: Value Meaning 0 12 hour 1 24 hourNote: The value of this cookie does not affect any of the internal time functions. It is intended for informational use by applications only. |
|
This cookie indicates the presence of an Advanced Keyboard Processor. The high word of this cookie is currently reserved. The low word indicates the language currently used by TOS for keyboard interpretation and alerts. See the explanation for the country code in the OS header earlier in this chapter for valid values.If this cookie is present on TOS 5.0 and higher then the system supports soft-loaded keyboard tables. |
|
This cookie indicates the presence of
FSM or SpeedoGDOS. Its value field is
a pointer to a structure as follows:typedef struct
{ LONG gdos_type; UWORD version; WORD quality; } GDOS_INFO;The gdos_type field determines the variety of GDOS. '_FSM' represents Imagen font-based FSM whereas '_SPD' represents Bitstream font-based FSM. version specifies the current GDOS version.quality determines the output quality of v_updwk(). The default setting is QUAL_DEFAULT (0xFFFF) which causes the driver to use the setting last set in the driver configuration accessory or CPX. This default setting may be overridden by placing a value of QUAL_DRAFT (0x0000) or QUAL_FINAL (0x0001) at this location. The quality setting should be restored to QUAL_DEFAULT at the end of each print job. |
|
This cookie indicates the presence of System Audio Manager and the XBIOS extensions it provides. The value field is currently reserved for internal use. |
|
This cookie indicates the presence of MiNT (MultiTOS) and its value field is the current version number (ex: MiNT 1.02 has a value field of 0x00000102). |
The BIOS provides access to six default devices (numbered 0-5). In addition, TOS 2.00 provides the ability to add extra devices with the XBIOS Bconmap() function (see the XBIOS overview for more information). Device assignments higher than device five are dependent upon the machine and any third-party enhancements. The following list indicates the device assignments which remain constant:
Name |
|
|
Meaning |
---|---|---|---|
DEV_PRINTER |
|
|
Centronics Parallel Port |
DEV_AUX |
|
|
Default Serial Device (this device number could actually refer to any serial device connected to the system depending on which was mapped with Bconmap() ) |
DEV_CON |
|
|
Console (screen device) |
DEV_MIDI |
|
|
MIDI Ports |
DEV_IKBD |
|
|
Intelligent Keyboard Controller |
DEV_RAW |
|
|
Console (no interpretation) |
Output via BIOS device #5 causes all characters to be output literally to the screen without interpretation.
To send an escape sequence, one of the following codes (and possibly additional characters) must be sent following the ESCAPE character (ASCII 27):
|
|
Effect |
---|---|---|
|
|
Move the cursor up one line. If the cursor is on the top line this does nothing. |
|
|
Move the cursor down one line. If the cursor is on the bottom line this does nothing. |
|
|
Move the cursor right one line. If the cursor is on the far right of the screen this does nothing. |
|
|
Move the cursor left one line. If the cursor is on the far left of the screen this does nothing. |
|
|
Clear the screen and place the cursor at the upper-left corner. |
|
|
Move the cursor to the upper-left corner of the screen. |
|
|
Move the cursor up one line. If the cursor is on the top line, the screen scrolls down one line. |
|
|
Erase the screen downwards from the current position of the cursor. |
|
|
Clear the current line to the right from the cursor position. |
|
|
Insert a line by scrolling all lines at the cursor position down one line. |
|
|
Delete the current line and scroll lines below the cursor position up one line. |
|
|
Position the cursor at the coordinates given by the following two codes. The screen starts with coordinates ( 32, 32 ) at the upper-left of the screen. Coordinates should be presented in reverse order, Y and then X. |
|
|
This code is followed by a character from which the lowest four bits determine a new text foreground color. |
|
|
This code is followed by a character from which the lowest four bits determine a new text background color. |
|
|
Erase the screen from the upper-left to the current cursor position. |
|
|
Enable the cursor. |
|
|
Disable the cursor. |
|
|
Save the current cursor position. (Only implemented as of TOS 1.02) |
|
|
Restore the current cursor position. (Only implemented as of TOS 1.02) |
|
|
Erase the current line and place the cursor at the far left. |
|
|
Erase the current line from the far left to the current cursor position. |
|
|
Enable inverse video. |
|
|
Disable inverse video. |
|
|
Enable line wrap. |
|
|
Disable line wrap. |
The BIOS function Mediach() returns the current media-change status of the drive specified. This state is used to determine if a disk has been changed in removable media drives (floppies, removable hard drives, etc.
The Getbpb() incorrectly resets the media change state. Failure to properly reset this state after calling Getbpb() can cause data loss. The function _mediach(), shown below, forces the Mediach() function to return a 'definitely changed' state and should always be called after calling Getbpb() on removable media drives.
/* * _mediach(): force the media 'changed' state on a removable drive. * * Usage: errcode = _mediach( devno ) - returns 1 if an error occurs * * Inputs: devno - (0 = 'A:', 1 = 'B:', etc...) * */ .globl _mediach _mediach: move.w 4(sp),d0 move.w d0,mydev add.b #'A',d0 move.b d0,fspec ; Set drive spec for search loop: clr.l -(sp) ; Get supervisor mode, leave old SSP move.w #$20,-(sp) ; and "Super" function code on stack. trap #1 addq.l #6,sp move.l d0,-(sp) move.w #$20,-(sp) move.l $472,oldgetbpb move.l $47e,oldmediach move.l $476,oldrwabs move.l #newgetbpb,$472 move.l #newmediach,$47e move.l #newrwabs,$476 ; Fopen a file on that drive move.w #0,-(sp) move.l #fspec,-(sp) move.w #$3d,-(sp) trap #1 addq.l #8,sp ; Fclose the handle tst.l d0 bmi.s noclose move.w d0,-(sp) move.w #$3e,-(sp) trap #1 addq.l #4,sp noclose: moveq #0,d7 cmp.l #newgetbpb,$472 ; still installed? bne.s done move.l oldgetbpb,$472 ; Error, restore vectors. move.l oldmediach,$47e move.l oldrwabs,$476 trap #1 ; go back to user mode addq.l #6,sp ; restore sp moveq.l #1,d0 ; 1 = Error rts done: trap #1 ; go back to user mode addq.l #6,sp ; from stack left above clr.l d0 ; No Error rts /* * New Getbpb()...if it's the target device, uninstall vectors. * In any case, call normal Getbpb(). */ newgetbpb: move.w mydev,d0 cmp.w 4(sp),d0 bne.s dooldg move.l oldgetbpb,$472 ; Got target device so uninstall. move.l oldmediach,$47e move.l oldrwabs,$476 dooldg: move.l oldgetbpb,a0 ; Go to real Getbpb() jmp (a0) /* * New Mediach()...if it's the target device, return 2. Else call old. */ newmediach: move.w mydev,d0 cmp.w 4(sp),d0 bne.s dooldm moveq.l #2,d0 ; Target device, return 2 rts dooldm: move.l oldmediach,a0 ; Call old jmp (a0) /* * New Rwabs()...if it's the target device, return E_CHG (-14) */ newrwabs: move.w mydev,d0 cmp.w 4(sp),d0 bne.s dooldr moveq.l #-14,d0 rts dooldr: move.l oldrwabs,a0 jmp (a0) .data fspec: dc.b "X:\\X",0 mydev: ds.w 1 oldgetbpb: ds.l 1 oldmediach: ds.l 1 oldrwabs: ds.l 1 .end
If your process needs to do cleanup in the event of a warm reset (see "Placing a Cookie" earlier in this chapter) the following code installs a user routine to accomplish this.
_resvalid equ $426 _resvector equ $42A RESMAGIC equ $31415926 .text installres: move.l _resvalid,oldvalid move.l _resvector,oldvector move.l #myresvec,_resvector move.l #RESMAGIC,_resvalid rts myresvec: * * Insert user code here * move.l oldvector,_resvector move.l oldvalid,_resvalid jmp (a6) .bss oldvector: ds.l 1 oldvalid: ds.l 1 .end
As of TOS 1.06, the OS jumps through the address contained in the system variable bell_hook ($5AC) to ring the system bell. It is possible for a custom routine to hook into this vector to alter the bell sound. The user routine may modify registers D0-D2/A0-A2 and may chain to the old bell handler if desired. It is also safe to make BIOS and XBIOS calls following the procedure for calling from an interrupt (when not running under MultiTOS). The routine should either jump to the old handler or execute an RTS statement.
Similar to the system bell vector, another vector is called each time a keyclick sound is generated. This vector is stored in system variable kcl_hook ($5B0) and is entered with the keycode (not the ASCII code) of the key struck in the low byte of D0. Registers D1-D2/A0-A2 may be modified, however, all other registers including D0 must be maintained. The replacement handler may either chain to a new handler or RTS.
The system variable _frclock is incremented.
The system variable vblsem is tested. If 0, the vertical blank handler exits immediately.
All registers are saved.
The system variable _vbclock is incremented.
If the system is currently in a high resolution video mode and a low-resolution monitor is detected, the video resolution is adjusted and the vector found at system variable swv_vec is called.
The text cursor blink routine is called.
If a new palette has been selected since the last vertical blank, it is loaded.
If a new screen base address has been selected since the last vertical blank, it is selected.
Each of the "deferred" vertical blank routine handlers is called.
If the system variable prt_cnt is greater than -1, the vector at system variable scr_dump is called.
Saved registers are restored and processing continues.
To install a routine to be called as a "deferred" vertical blank handler, you must inspect the list of handler vectors at vblqueue for a NULL slot, replace it with your vector and initialize the next slot to NULL. The system variable nvbls indicates the number of slots pointed to by vblqueue. If the vertical blank handler list is filled, you may allocate a new area, copy the old list of handlers with your handler, and update the pointer vblqueue and nvbls.
Many applications that add functionality to the system do so by 'hooking' themselves into one or more interrupt or pass-through vectors (usually with Setexc()). Most vector handlers work by executing the relevant code when the interrupt is called and then calling the original vector handler. When several applications handle one vector, a vector 'chain' is created. This chain makes it difficult for debuggers or the process itself to 'unhook' itself from the chain.
The XBRA protocol was designed so that processes that wish to be able to unhook themselves may and so that debuggers can trace the 'chain' of vector handlers. Following the protocal is simple. Prior to the first instruction of the vector handler, insert three longwords into the application as follows:
The following code example shows how to correctly use the XBRA protocol in a routine designed to supplement the 680x0 TRAP #1 vector (GEMDOS):
instl_trap1: move.l #my_trap1,-(sp) move.w #VEC_GEMDOS,-(sp) move.w #Setexc,-(sp) trap #13 addq.l #8,sp move.l d0,old_handler rts DC.L 'XBRA' DC.L 'SDS1' ; Put your cookie here old_handler DC.L 0 my_trap1: movem.l d2-d7/a2-a6,-(sp) ; ; Your TRAP #1 handler goes here. ; movem.l (sp)+,d2-d7/a2-a6 move.l old_handler,-(sp) ; Fake a return rts ; to old code.
The following 'C' function is an example of how to use the XBRA protocol to unhook a vector handler from the XBRA chain. This function will only work if all installed vector handlers follow the XBRA protocol. It takes a Setexc() vector number and an XBRA application id cookie as a parameter. It returns the address of the routine that was unhooked or 0L if unsuccessful.
typedef struct xbra { LONG xbra_id; LONG app_id; VOID (*oldvec)(); } XBRA; LONG unhook_xbra( WORD vecnum, LONG app_id ) { XBRA *rx; LONG vecadr, *stepadr, lret = 0L; char *savessp; vecadr = Setexc( vecnum, VEC_INQUIRE ); rx = (XBRA *)(vecadr - sizeof( XBRA )); /* Set supervisor mode for search just in case. */ savessp = Super( SUP_SET ); /* Special Case: Vector to remove is first in chain. */ if( rx->xbra_id == 'XBRA' && rx->app_id == app_id ) { Setexc( vecnum, rx->oldvec ); return vecadr; } stepadr = (LONG *)&rx->oldvec; rx = (XBRA *)((LONG)rx->oldvec - sizeof( XBRA )); while( rx->xbra_id == 'XBRA' ) { if( rx->app_id == app_id ) { *stepadr = lret = (LONG)rx->oldvec; break; } stepadr = (LONG *)&rx->oldvec; rx = (XBRA *)((LONG)rx->oldvec - sizeof( XBRA )); } Super( savessp ); return lret; }
The BIOS may utilize registers D0-D2 and A0-A2 as scratch registers and their contents should not be depended upon at the completion of a call. In addition, the function opcode placed on the stack will be modified. The BIOS places return codes in D0.
The following example for Bconout() illustrates calling the BIOS from assembly language:
move.w #char,-(sp) move.w #dev,-(sp) move.w #$03,-(sp) trap #13 addq.l #6,sp
A 'C' binding for a generic BIOS handler would be as follows:
_bios: ; Save the return code from the stack move.l (sp)+,trp13ret trap #13 move.l trp13ret,-(sp) rts .bss trp13ret: .ds.l 1
With the above code, you could easily design a 'C' macro to add BIOS calls to your compiler as in the following example for Bconout():
#define Bconout( a ) bios( 0x02, a )
The BIOS is re-entrant to three levels, however there is no error checking performed so interrupt handlers should avoid intense BIOS usage. In addition, no disk or printer usage should be attempted from the system timer interrupt, critical error, or process-terminate handlers.
savptr equ $4A2 savamt equ $23*2 myhandler: sub.l #savamt,savptr ; BIOS calls may be performed here add.l #savamt,savptr rte ; (or rts?)
This method is not valid under MultiTOS.