D-Bug ULSv3 (c) 2005-2009 D-Bug
Introduction1.1 What is ULS?1.2 System Requirements Understanding ULS2.1 Concept2.2 ULSv3 Functions 2.3 Assembly equates 2.4 Patching into a game Trouble Shooting3.1 Troubleshooting crashes & errors3.2 Known problems 3.3 Revision List
1.1 What is ULS?U.L.S. (Universal Loading System) is a means by which Atari ST applications can request disk access, or system function calls when TOS is not available, as is the case with most games and demos, and therefor run from any media (Including Hard Disks). Late in 2005 D-Bug released the first few ULS patched games, and then the system went into a bit of a slumber as we continued with Falcon patching. In August 2008, the ULS code was completely re-written from the ground up, adding new functionality, using less memory, running quicker, with more stability and making it much easier to implement.It is hoped that the release of this code will kick-start the ST hard disk adaption scene, in much the same way WHDLoad has done for the Amiga. Indeed, ULS can perform many of the functions of WHDLoad, and offer serveral exciting new ones that were previously only seem in emulators!
1.2 System RequirementsThe basic system requirements are:An Atari of any type (ST/STF/STFm/STe/Mega/Mega2/MSTe/F030/TT) An ST disk media of any type (ACSI, SCSI, IDE, Floppy....) A minimum of 1mb of memory (some titles will require more)
2.1 ConceptThe basic concept of ULS is "Let GEMDOS do it all". At the start of the stubloader a "snapshot" of the lower chunk of memory is taken along with several system variables and hardware registers. ULS is then installed which then takes care of all disk access from that point on using standard GEMDOS commands to replace the low level access in the application. Hard disk access is performed via the installed driver, making ULS work with multiple drivers without requiring repartitioning or device setup.
2.2 ULSv3 FunctionsThere are 16 basic function calls in ULSv3. These are accessed by passing parameters to them via registers (or, in some cases, using the filebuffer) and calling the function as an offset from the install address, much the same way as SNDH music is called.The functions are as follows:
Function: uls_setup
This function initialises the ULSv3 system. It takes a "snapshot" of the current state of the enviromnet, and returns parameters required for the stubloader to call other ULS functions. The call will return with the system in 320x200x4bpp ST-Low resolution. If run on a Falcon it will detect RGB/VGA and automatically set the VIDEL accordingly, and also put the machine in STE Bus Emulation Mode. On 68030 based systems (F30/TT) the PMMU table is moved to high memory so that all low RAM can be accessed without errors.
Call A0.L: RAMtop for ULSUnlike most applications, ULS will allocate its memory downwards from the top end of RAM. This is because nearly all games and demos that trash TOS will occupy the lower 512-1024k of memory, and we don't want any of our code or workspace to get over written. The easiest way to call uls is to load A0 with RAMtop from $42e.w, making sure your code (and the stack) are well clear of anything it will wipe over.
Call D0.L: 16Mhz RequestSet D0.L to $00000001 and ULS_init will return with the system in 16Mhz if available (MSTe/F030)Set D0.L to $FFFFFFFF and ULS_init will return in 8Mhz
Call D1.L: Cache RequestSet D1.L to $00000001 and ULS_init will return with the system cache on if available (MSTe/F030/TT)Set D1.L to $FFFFFFFF and ULS_init will return with the system cache disabled
Call D6.L: 4 char ASCII of RAMdisk folderThis defaults to #"DATA" but was added in order to support multiple data folders.
Call D7.L: AutoMagic Ramdisk FlagSet D1.L to #'RAMD' and ULS_init will take the following two parameters and create and fill a ramdisk of all the files requestedSet D1.L to anything else to disable this feature.
Call A6.L: Size of RamdiskSet A6.L to the size (in bytes) required to cache all the files that the filespec (below) will match.This value must be: Size in bytes of all files+(20*(number of files+1))
Call A5.L: Pointer to a filespec for RamdiskSet A5.L to the address of a filespec to use to build the ramdisk, eg, "*.*" for all files or "*." for all files without extenders.You do not need to specify the "DATA\" in this filespec.
Returns A0.L: Pointer to ULS JMP TableThis is the base address for all the ULS function calls. They are accessed by loading this into an address register and then calling the function via an offset, egmove.l uls_base(pc),a6 jsr uls_setread(a6)
Returns A1.L: Pointer to ULS FilebufferThis is the address that uls_file_io will load all data through, before sending it either to disk (write) or back to the application (read). It is also where you must leave a register dump before calling uls_statesave.
Returns A2.L: Load address of stubloader / A3.L ULS low memory copy addressThese values are returned so that you can "work around" any segmented memory issues while code called via uls_execute is running (more info in that function description)
Returns D0.L: Machine TypeThe value is one of: 0 (ST), 1 (STE), 2 (MSTE), 3 (F30) or 4 (TT)
Returns D1.L: Hard Disk flagReturns D1=#"!HD!" if the code is not being executed from A:\ or B:\
Returns D2.W: VGA Detect Flag (Falcon)Returns (1) if VGA or (2) if RGB on a Falcon
Function: uls_setreadCall this function to put the uls_file_io function into READ mode
Function: uls_setwriteCall this function to put the uls_file_io function into WRITE mode
Function: uls_file_ioThis is the main function call that will handle all your disk IO, it accepts and returns parameters as follows:
Note: If the file requested is in the Ramdisk then ULS will not be called at all, and the file will be byte copied to the destination address. The ramdisk routines will work with the offset and bytes to IO values as per the normal call. You cannot write to the ramdisk. If the file being written was stored in the ramdisk, then it will be devalidated from the ramdisk index and any further read request for that file will result in it being read from disk.
Call A0.L: Pointer to filenameThe filename can be in the format "filename.ext" or "filenameext" with or without the "." - if the "." is omitted, then the filename must be space padded (ie, in directory format) Filenames must always be null terminated (ie, #0 at the end)
Call A1.L: Load addressQuite simply, the address you want the data loaded to, or written from.
Call D0.L: bytes to IOThe ammount of bytes you want to read or write. Setting D0=$FFFFFFFF will load the entire file in READ MODE, and set it to write the number of bytes in the already existing file if in WRITE MODE
Call D1.L: Seek offset into file (Must be 0 if using file overwrite mode)READ MODE: Set to 0 to start reading from the beginning of the file, or the offset into the file to start reading from.WRITE MODE: If using file overwrite this must be set to 0, otherwise set to 0 to write bytes from the start of the file, or set to the offset into the file to start writing from.
Call D7.L: IO Data Transfer ModeREAD MODE: Set to 0 to load the file as normal, or to -1 to load it only to the ULS filebuffer.WRITE MODE: Set to -1 to create a new file (or replace existing file), or set to anthing else to overwrite file (if size=$ffffffff then bytes written will be same as file size)
Returns D0.L: Packed File Length (or error)If the file loaded is packed using a known packer, then D0 will return the length of the UNPACKED file. If it is unpacked, or an unknown packer, it will return the actual file length. If there was a file error (file not found) then D0 will return $ffffffff.
Returns D0.L: Actual File LengthThis will return the actual length of the file loaded.
Function: uls_terminateThis function will return the system back to the state it was in before the stubloader was called. All memory, hardware registers and system vectors will be restored.
Note: If D0.L<>#'RTS!' the call will restore the system and terminate back to the desktop. If the return address is specified (A0.L) and the flag is set (D0.L) then a JUMP will be performed. The system will be in the same state as the entry point of uls_init - It is your responsibility to make sure the stack and anything else is set up correctly. ULS will most likely still be sitting in high memory, but if you wish to call any of its functions again you should execute another uls_init call first.
Function: uls_dumpscreenThis function will save the current screenbuffer as a PI1 (Degas Elite Uncompressed) file. The file will be written to the same directory the stubloader was launched from with the name ULS_0000.PI1 - the number will increment (in hex) each time the function is called. If the application was launched from drive A or drive B (ie, floppy) then the function returns without doing anything.
Function: uls_execute
Function: uls_statesaveNote: The following is still true, but you will be taken to the State Management screen first to select a state-save slot, which will alter the filename accordingly.
Call D0.L: Ramtop for memory dumpThe end of memory required to be dumped by the application. For most games this would be $80000 (512k) but in some cases it might be more.Call D1.L: Resume JMP addressThe address that uls_stateload will jmp to upon resuming the memory snapshot.Call uls_filebuffer Register DumpIn order for statesave/load to work, the registers have to be restored along with the memory and hardware settings. These need to be dumped into the uls_filebuffer when uls_statesave is called. Please note: the stack pointer (a7) MUST be correct at register dumptime for resuming to work, and the stack MUST be within the memory range to be dumped. If the application was launched from drive A or drive B (ie, floppy) then the function returns without doing anything. The following code illustrates the uls_filebuffer register dump format required. The file "DBUGULSV.III" will be created in the stubloader directory.
move.l a0,-(a7) ; save a0 Once the state file is saved, program execution will continue as normal.
Function: uls_stateloadNote: The following is still true, but you will be taken to the State Management screen first to select a state-load slot, which will alter the filename accordingly.Call this function to resume from the saved state. If no state is found on the disk, then normal execution continues. If the application was launched from drive A or drive B (ie, floppy) then the function returns without doing anything.
Function: uls_setpathSets the name of the subfolder that will be used for file access by uls_file_io.
If you wish to have multiple folders because, for example, a file with the same name exists on a different disk, but has different content, you can use this function to get ULS to use another directory for its data files. This can also be used in conjunction with uls_update_rdsk to drastically reduce the memory footprint of a game, by allocating the ramdisk to contain only a single disk of a multi-disk game, and re-populating it on a "disk swap" request.
Function: uls_req_rd_addrReturns the address of a file's table header in the RAMdisk.
Call A0.L: Pointer to filenameThis is the same format as used for uls_file_io
Returns A0.L: Pointer to file's header in RAMdisk.The structure of the header is as follows:
Bytes 00-11: Filename (8) + '.' + extender (3)
Function: uls_update_rdskAllows the ULS ramdisk to be updated (append mode) or repopulated (replace mode)
Call D0.L/A0.LD0.L and A0.L are the same as required by D6.L and A5.L respectivly for the uls_init function
Call D1.W: Update Mode FlagSet D1=-1 (negative) and the existing RAMdisk will be completely replaced with the new data.Set D1=+1 (positive) and the existing RAMdisk will be appended. This is requires for multi directory games if you wish to cache both directories at setup time, as only one path can be specified in uls_init for ramdisk creation. Note: The updated RAMdisk *MUST* fit inside the parameters specified in uls_init.
Function: uls_F30set200Sets the screen to 320x200x16 colours on a Falcon - autodetects VGA/RGB
Function: uls_F30set240Sets the screen to 320x240x16 colours on a Falcon - autodetects VGA/RGB
Function: uls_F30set200TCSets the screen to 320x200x65536 colours on a Falcon - autodetects VGA/RGB
Function: uls_searchAllows memory search/replace calls to be made easily. Handy for patching.
No description really necessary for this one, again if you need it explained you shouldn't be reading this ;-)
2.3 Assembly equatesThere are two equates that ULS requires to be in the source file for the stubloader, these are "max_filesize" and "adv_debug" and are described below.max_filesizeThis must be set to the size of the biggest file you will want to either read or write via ULS
adv_debugDefine this to enable the debugging screen in ULS, disable this for the final loader.The debug screen looks like this:
and will be displayed each time ULS is called. The ULS Execution line at the bottom updates as ULS execution flow progresses, and is very useful for tracing hang faults in loaders. If you think you have fixed something, please reprot it to us on the ULS forum on our website!
2.4 Patching into a gameThe best place to patch in the ULS loader is in the DMA-File loading routine that the original cracker put in the title. Most of them will have something likeoriginal code:
lea filename,a0 for ULS this should be patched to:
lea filename,a0 As can be seen, all you are doing is replacing the load routine with the ULS handler. As far as the title is concerned, its still loading as normal and then depacking as normal. You will also have to patch the ULS handler so it can tell if a read/write is requested, or a header only read is requested (For example, Barbarian II's loader sets $31.b to -1 if a file is to be written, else its a read, and F1GP's loader sets $96.w to $5555 for a header only request) You may also have to patch the exit routine with a value to confirm the load was ok. (For example, Leavin' Teramis required $3000.w to be cleared when load was completed or it assumed there was a disk error) At the bottom of this page you will find the source code (DevPac2 format) for some of the loaders we have released.
3.1 Troubleshooting crashes & errorsThe first thing to do here is define the "adv_debug" label in the source and run the loader again. This will give you an idea of what is going on when ULS is called. You can see the registers, and also get the base addresses for the ULS code, your stubloader, and the segmented RAM. You might have to disable the Ramdisk if you set it up for this, as the debug screen is not called for Ramdisk functions. Secondly, we strongly recommend you use Steem Debug's Boiler room, put a breakpoint at your ULS_Handler address, and trace through your code until you see what the issue is. Happy Hunting!
3.2 Known problemsThere is an issue where the ULS_core will "hang" in "SAVING GEM MFP (TIM)" in some situations (eg, Robocop III) - we are investigating this and hope to have a fix in the future. Currently this has only occured in this one title (and then only on the Falcon).
3.3 Revision ListFor v3.1
*Added State Management Menu (10 states/comments) For v3.11cf (22/01/2009)
* Ramdisk bug fixed For v3.12 (27/01/2009)
* Fixed crashing if RAMdisk set to off on F30/TT (Hurrah!) For v3.12f (28/01/2009)
* Added multiple DATA folder support and RAMdisk updates (uls_update_rdsk) For v3.12g (06/02/2009)
* Bugfixed MSTE cache init problem (Thanks Shw!) For v3.12h (27/02/2009)
* Added the uls_search function For v3.12i (16/03/2009)
* Fixed bug in copy_file_down where it was copying the unpacked length to memory instead of the packed length. For v3.12j (17/03/2009)
* Fixed RAMdisk handler whereby odd length files were returning a rounded up even file length if read from RAMdisk.
This software is released as Open Source and may be used, viewed or modified by anyone. D-Bug accept no responsibility for any loss of data or damage to your equipment from using this code or any of our patches (Although, of course, we don't expect there ever to be any!) |