home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1996-02-15 | 209.3 KB | 7,580 lines
@a=fc0000 **************************************************************************** * * * Comments copyright (c) 1989 Markus Wandel * * * * Release date: February 3, 1989. * * * * The following is a complete disassembly of the Amiga 1.2 "exec", as * * found on a kickstart disk for an Amiga 1000. Everything is shown, * * right down to the padding introduced by the linker, and unused code * * fragments which probably made it in by accident. * * * * Thorough familiarity with the Rom Kernel Manual: Exec, with suitable * * updates to version 1.2, and the exec subdirectory of the include files, * * is assumed in all comments. Where existing documentation appears to * * be inadequate, or a particular section of code is judged to be more * * interesting than most, comments are more extensive. * * * * Absolutely no guarantee is made of the correctness of all information * * supplied below, nor of its usefulness. * * * * Note that virtually all references to "ROM" actually refer to the * * write-protected RAM which the kickstart disk loads into. The genuine * * ROMs on Amiga 500 and 2000 computers may not contain exactly the same * * code, although most of it should be similar. * * * * The completed disassembly file is not redistributable, and contains * * code which is copyrighted by Commodore-Amiga, and comments which are * * copyrighted by Markus Wandel. The source file used to make the * * disassembly is distributable under a limited set of conditions as * * outlined in the accompanying documentation. * * * * For Commodore-Amiga's copyright notice, see a few lines farther down. * * * **************************************************************************** ; This match word is used when looking for ROMs. @8p@w ; The ROM can be started by jumping to its base address plus 2, ; or by mapping it at location zero and resetting. Either way, ; the machine starts running at FC00D2 (in the latter case, because ; the 68000's PC is loaded from location 4 at cold start). @d @a?fc0008 @8p@28wGarbage. @8p@w ; The following version number doesn't appear to be used ; from within exec.library. @8p@w @8p@w ; The following version number appears in the exec.library ; node, and this copy is checked to ensure it is still valid. @8p@28wVersion. @8p@28wRevision. @8p@28lGarbage. ; The exec's ID string ; -------------------- @8p@,28s @8p@28lGarbage. ; Copyright notice. ; ----------------- @8p@,45s @8p@,43s @8p@,24s ; Library name ; ------------ @8p@,14s ; The RomTag structure. ; --------------------- @8p@28wRTC_MATCHWORD (start of ROMTAG marker) @8p@28lRT_MATCHTAG (pointer RTC_MATCHWORD) @8p@28lRT_ENDSKIP (pointer to end of code) @8p@28bRT_FLAGS (no flags) @8p@28bRT_VERSION (version number) @8p@28bRT_TYPE (NT_LIBRARY) @8p@28bRT_PRI (priority = 126) @8p@28lRT_NAME (pointer to name) @8p@28lRT_IDSTRING (pointer to ID string) @8p@28lRT_INIT (execution address) @a?fc00d0 @d ; We start running here. @36dSet stack pointer to top of first 128K. @d @36dDelay loop. @d ; If the ROM is also visible at F00000, or if there is another ; ROM there, jump there. @36dLoad base address of ROM we're in. @36dLoad (absolute address) F00000. @36dAre we at F00000? @36dIf so, don't execute the following. @36dThis is relative, i.e. always points 12 bytes down from where we are. @36dIf "1111" not found at F00000, then @36dcontinue running below, else start @36drunning at F00002. ; Set up port A on the first CIA (8520-A). @36dSet low two bits for output. @36dSet boot ROM off, power light dim. ; Disable interrupts and DMA. @36dBase address of custom chip area. @d @36dDisable all interrupts. @36dClear all pending interrupts. @36dDisable all DMA. ; Set a blank, dark gray display. @36dBPLCON0 = Blank screen. @36dBitplane 0 data = all zeros. @36dBackground colour = dark gray. ; Set up the Exception Vector Table. Vectors 2 through 47 ; (Bus Error through TRAP #15) are all all set to the initial ; exception handler. If any exception occurs now, the screen ; will turn yellow, the power light will flash, and the computer ; will be reset. @36dStart at address 8 (vector #2). @36dDo 46 vectors. @36dAddress of initial exception handler. @36dSet one vector @36dLoop back. ; See if the system wants a guru put up after reboot. ; This works as follows: If for some reason, a guru can't be put ; up in the normal fashion, the system writes "HELP" at location ; zero, writes the alert data (number and 32-bit parameter) at ; location $000100, and resets. ; Early in the startup code (right here), this "HELP" is checked ; for. If it is present, it is removed, and the data at location ; $000100 is loaded into registers D6 and D7. If no "HELP" is ; found, register D6 is loaded with -1. This data will later be ; put at ExecBase->LastAlert, once the ExecBase structure has been ; built. The following subroutine call does all this. @36dCheck for "HELP" at location 0. ; Check whether there is already a valid ExecBase data structure. ; This is important, since it indicates whether we need to clear and ; reconfigure memory (wiping out recoverable RAM disks and such), ; or whether we already know the memory configuration and can leave ; it untouched. ; Note that if the machine crashed in such a way that the ExecBase ; structure got clobbered, memory will be cleared. @36dGet pointer at location 4. @36dCheck if it is an odd address. @36dGo reconfigure memory if it is. @36dAssume we are pointing to ExecBase. @36dGet complement of ExecBase. @36dCheck it. @36dGo reconfigure memory if it didn't match. @d @36dChecksum the static part of the ExecBase @36ddata structure. @d @d @36dVerify the checksum. @36dGo reconfigure memory if not valid. ; If we get this far, we are reasonably confident that the ExecBase ; structure is OK, and run the cold start capture code if there ; is any. @36dGet the cold start capture vector. @36dBranch if it is zero. @d @36dWhere to come back afterward. @36dClear the cold start capture vector. @36dJump to the cold start capture code. ; We come here if the cold start capture vector was zero, or ; upon return from the cold-start capture code. We continue ; to verify the ExecBase structure. @36dFlip the power light to bright. @36dCheck the version/revision numbers @36dstored in ExecBase against those in ROM. @36dGo reconfigure memory if no match. @36dGet end address of chip memory. @36dGreater than 512K? @36dIf so, it must be invalid. @36dLess than 256K? @36dIf so, it must be invalid. @36dGet end address of $C00000 memory. @d @36dAll OK if no $C00000 memory. @36dCheck more than 1.5 meg (invalid). @36dGo reconfigure memory if so. @36dCheck if less than 256K (invalid). @36dGo reconfigure memory if so. @d @36dCheck that ends on a 256K boundary. @36dAll OK if it does. ; If we come here, it was decided that there is no valid ExecBase ; data structure. This means we have to figure out what the memory ; configuration of the machine is. ; First, calculate ExecBase based on the assumption that the ExecBase ; structure will end up in chip RAM. This would put it at $0676, ; just far enough past the exception vector table to make room for ; the jump table. @36dCalculate $0676. @36d(don't ask me why they do it like this). ; Now go and check for memory in the $C00000 - $DC0000 area. ; This allows for a maximum of 1.75 megabytes of non-chip memory ; to be automatically configured if located at $C00000. @36dLower bound for $C00000 memory. @36dHigh bound for $C00000 memory. @36dReturn address. @36dGo check how much we have. @36dDid we find any expansion memory? @36dIf not, skip the following. ; The machine has expansion RAM at $C00000. We put the ExecBase ; structure there to save chip memory. This puts it at $C00276. @36dCalculate $C00276. @d ; Now we clear the expansion memory to zeros. @36dGet end address of expansion memory. @36dGet start address of expansion memory. @36dSet return address. @36dGo clear the memory. ; Having figured out the end address of expansion memory (in A4), ; and the value to use for ExecBase (in A6), we now check how much ; chip memory we have. Any memory in the first 2 megabytes of ; address space is considered to be chip memory. Less than 256K ; of chip memory is considered a fatal error. @36dStart looking at location 0. @36dDon't look past 2 megabytes. @36dSet the return address. @36dGo check the memory. @36dDo we have at least 256K of chip memory? @36dBomb if not. ; Clear chip memory. Everything from $C0 (right after the end of ; the initial exception vector table we've set up) to the end of ; chip memory is cleared. @36dClear location 0. @d @36dSet start address to $C0 (end of vectors) @36dSet return address. @36dGo clear the chip memory. ; Since we have found less than 256K of chip memory, some of it ; must not be working. Turn the screen bright green, blink the ; power light, and reset. @d @d ; We continue here after we've figured out where the chip memory ; ends (256K or greater) and where the $C00000 memory ends ; (0 if none present). The two addresses are in A3 and A4, ; respectively. @36dPoint to base of custom chip area. @36dDisable all DMA. @36dSet BPLCON0 for a blank screen. @36dSet bitplane 0 data to zeros. @36dSet background colour to medium gray. ; Clear most of the ExecBase structure to zeros. @36dPoint at ExecBase->IntVects. @36dGet KickMemPtr, KickTagPtr, KickCheckSum. @d @36dClear all of ExecBase from IntVects @36dto the end of the structure. @d @36dRestore Kick variables saved above. ; Set up the ExecBase pointer at location 4, and its complement ; in the ExecBase structure. @36dInstall ExecBase pointer at location 4. @d @d @36dInstall ExecBase complement check value. ; Set up the system stack. @36dTry to put stack in $C00000 RAM. @36dDo we have any $C00000 RAM? @36dIf not, use chip RAM. @36dSet system stack pointer. @36dStore system stack upper bound. @36dAllow 6K bytes for system stack. @36dStore system stack lower bound. ; Store the memory configuration. Next reset will use this if ; still intact, and not clear memory. @36dStore top of chip memory. @36dStore top of $C00000 memory. ; Part 2 of the deferred-guru procedure. Long ago, we set up ; registers D6 and D7 with the data for ExecBase->LastAlert. ; The following call writes them there. Now all is ready for ; the "alert.hook" mechanism to put up the deferred guru, if ; one was wanted. @36dSetup ExecBase->LastAlert. @36dCheck CPU type, and if 68881 present. @36dOr the result into the Attention flags. ; Initialize the exec lists. This is driven from a data table which ; contains the offsets from ExecBase where the various lists are, ; and the list types. @36dPoint to the table. @36dGet a table entry. @36dZero marks the end. @36dAdd to ExecBase to get absolute address. @36dClear the list by setting its head @36dpointer to point to itself, @36dclearing its "Tail" field, @36dand setting up the "TailPred" pointer. @36dGet the list type. @36dPut it into the list header. @36dLoop back to do next list. @a?fc02d2 ; Table of list header offsets and types. @8p@5w@11wMemList (ExecBase + $142, type = NT_MEMORY) @8p@5w@11wResourceList (ExecBase + $150, type = NT_RESOURCE) @8p@5w@11wDeviceList (ExecBase + $15E, type = NT_DEVICE) @8p@5w@11wLibList (ExecBase + $17A, type = NT_LIBRARY) @8p@5w@11wPortList (ExecBase + $188, type = NT_MSGPORT) @8p@5w@11wTaskReady (ExecBase + $196, type = NT_TASK) @8p@5w@11wTaskWait (ExecBase + $1A4, type = NT_TASK) @8p@5w@11wIntrList (ExecBase + $16C, type = NT_INTERRUPT) @8p@5w@11wSoftInts[0] (ExecBase + $1B2, type = NT_SOFTINT) @8p@5w@11wSoftInts[1] (ExecBase + $1C2, type = NT_SOFTINT) @8p@5w@11wSoftInts[2] (ExecBase + $1D2, type = NT_SOFTINT) @8p@5w@11wSoftInts[3] (ExecBase + $1E2, type = NT_SOFTINT) @8p@5w@11wSoftInts[4] (ExecBase + $1F2, type = NT_SOFTINT) @8p@5w@11wSemaphoreList (ExecBase + $214, type = NT_SIGNALSEM) @8p@16wEnd of table marker. ; Table used to initialize the exec's library node. @8p@16bType = NT_LIBRARY. @8p@16bPriority = 0. @8p@16lName = pointer to "exec.library". @8p@16bFlags = LIBF_CHANGED | LIBF_SUMUSED. @8p@16bPad. @8p@16wNegSize (not set yet). @8p@16wPosSize. @8p@16wVersion. @8p@16wRevision. @8p@16lIdString = pointer to "exec ..." @8p@16lChecksum (not set yet). @8p@16wOpenCnt = 1. ; Names to use in the system free-memory lists. @8p@,12s @8p@,12s @36dGet address of task crash routine. @36dInstall in default trap code. @36dInstall in default exception code. @a?fc034a @40dSet the default task exit address. @40dPreallocate the lower 16 signals. @40dPreallocate TRAP #15 (for use in ROM-Wack breakpoints). @36dInitialize the exec.library node @36dup to and including the OpenCnt @36dfield, from the table above. @d @d @36dMake the library jump vector, using the @36dtable at $FC1A40, and the MakeFunctions() @36droutine. Setting A2 to the table address @36dsignals that the table is relative. @36dInstall library negative size. @36dSee if we have expansion memory. @36dBranch past this if we don't. ; Add expansion memory at $C00000 to the free memory lists. @36dFirst free location = ExecBase + $024C. @36dName = "Fast Memory". @36dPut in memory list at priority 0. @36dAttributes = MEMF_FAST | MEMF_PUBLIC. @36dGet end address of expansion memory. @36dSubtract address of first free location. @36dSubtract system stack size. @36dBuild free list and add it to system. @36dFree chip memory starts at $0400. @36dSpace for stack already reserved. @d ; Add chip memory to free memory lists. Enter here if there is ; no expansion memory, and ExecBase therefore resides at the bottom ; of chip memory. ; Note how chip memory is added to the system list at a lower ; priority than expansion memory. This causes it to be allocated ; only when specifically requested or when expansion memory is full. @36dFree chip memory is at ExecBase + $024C. @36dReserve 6K for system stack. ; Enter here if we do have expansion memory, with D0 and A0 set ; up as above. @36dAttributes = MEMF_CHIP | MEMF_PUBLIC. @d @36dName = "Chip Memory". @36dPriority = -10. @36dGet end address of free chip memory. @36dSubtract address of first free location. @36dBuild free list and add it to system. @36dGet ExecBase. @36dAdd the exec to the system library list. ; Set the exception vector table up for actual system operation. ; Up to this point, any interrupt or exception would have caused ; the screen to turn yellow and the computer to reset. ; The format of the data table used here is explained in comments ; at the front of the data table. @36dPoint to data table. @36dSet up base address for the offsets. @36dPoint to exception vector #2. @36dEnter the loop at the bottom. @36dConvert table entry to absolute address. @36dStore the handler address in the EVT. @36dGet the next data table entry. @36dLoop until end of table reached. @36dSee if we are running on a 68010/020. @d @36dSkip the following if not. ; Special initialization for machines using a 68010/020. @36dPoint at 68010/020 bus error handler. @d @36dFix the bus error vector. @36dFix the address error vector. @36d Use a different Supervisor() routine. ; Fix GetCC() for 68010/020 processors. ; We simply load the instruction sequence "MOVE.W CCR,D0 / RTS" into ; the place where the library jump vector to GetCC() normally is. @d ; Check if we have a 68881 numeric coprocessor, and if so, fix up ; some more vectors. This needs to be done since we also need to ; save the 68881's context when we switch tasks. @40dDo we have a 68881? If so, @d @40dUse a different Switch() function. @40dUse a different Dispatch() funciton. ; Regular 68000's continue here. @36dInitialize the exec interrupt handlers. @36dPoint to the custom chips. @36dEnable all DMA. @36dEnable the interrupt system. @36dSet the interrupt disable level to -1. @36dInitialize ROM-Wack. @36dChecksum the static part of the ExecBase @36ddata structure. @36dThis is used after a reset to see if @36dthe data structure has been clobbered. @d @d @36dStore the checksum. ; Now we are going to manufacture the very first task. ; We use AllocEntry() to obtain a block of memory. This is then ; used to hold the MemList from AllocEntry(), the task's stack, ; and the task descriptor. @36dPoint to the MemList. @36dAllocEntry() @36dGet the address of the MemList. ; It's assumed here that the allocated memory follows directly ; after the MemList. A safe assumption, since we still have ; unfragmented memory. We now create a task descriptor at the ; top of the allocated memory. The stack pointer for the task ; is initialized below the task descriptor. @36dPoint near the top of the memory block. @36dPoint to the future task descriptor. @d @36dStore stack lower bound. @36dStore stack upper bound. @36dSet the initial stack pointer image. @36dSet the stack pointer itself. @36dPriority = 0. @36dNode type = NT_TASK. @36dTask name = "exec.library". ; We initialize the task's memory list to empty, then enqueue ; the MemList holding all this memory there. This means that ; when the task dies, the memory will automatically be deallocated. @36dPoint to the task's memory list. @36dInitialize it to empty. @d @d @d @36dEnqueue the MemList from AllocEntry() @36don the task's memory list. @36dGet the task address back. ; Make this the current task, and make it ready to run. ; initialPC and finalPC are both initialized as zero, but no ; harm results, since the task can't start running yet. @36dMake this the current task. @36dClear A2. @36dClear A3. @36dAddTask() @36dGet the pointer to the task again. @36dMake the task state TS_RUN. @36dUnlink it from the TaskReady queue. ; A historic moment: We turn the supervisor mode flag off. ; Starting right now, we are running as a task named "exec.library", ; and the multitasking system is operational. @a?fc04be FC04BE and.w #0,SR Turn the supervisor bit off. @a=fc04c2 @36dForbid() @36dPermit() @d ; The MemList used to allocate memory for the initial task. @7p@9l@9l@9l@8wA dummy list node. @7p@35w1 block of memory desired. @7p@35lMEMF_PUBLIC | MEMF_CLEAR @7p@35l1124 bytes. ; Table of areas to look for RomTags in. I don't know why the ; FC0000 - 1000000 area is covered twice. The F00000 to F80000 ; area appears to be an alternate or additional place to put ROMs. @7p@9l@l @7p@9l@l @7p@9l@l @7p@35lEnd of list marker. ; Scan for RomTags, process the KickMemPtr and KickTagPtr ; variables, and build a table of all the resident modules found. ; The address of the table of resident modules is stored in ; the ExecBase data structure. @36dPoint to table of ROM address spaces. @36dScan for RomTags, etc. @36dStore the result in ExecBase->ResModules. @36dSet the power light to bright. ; Handle the "cool start" capture vector. Note that if we decided ; (much) earlier that ExecBase had been clobbered, it will have ; been rebuilt from scratch, and the cool start capture vector ; will be zero. Thus, we don't have to verify it further. @36dGet the "cool start" capture vector. @36dBranch past the following if zero. @d @36dCall the "cool start" capture code. ; Another historic moment. We call InitCode() to initialize the ; resident modules. This is where all the other stuff in the ROMs, ; stuff in RAM which survived the reboot, etc. comes online. We ; indicate that all those modules with the RTF_COLDSTART flag set ; should be initialized now. @36dRTF_COLDSTART flag must be set. @36dMinimum version is 0 (any will do). @36dInitCode(). ; Yet another capture vector, this time the "WarmCapture" one. @36dCheck the "WarmCapture" vector. @36dBranch past this if zero. @d @36dCall the warm start capture code. ; I assume that when the DOS came online, it took over. This ; task looks like it's heading into a dead end. ; Clear all the CPU registers except for ExecBase and the stack ; pointer. @38dPush 14 longwords of zero on @38dthe stack. @d @38dRead them off again into the registers. ; This is the end of the road. Do forever @38dDebug() @38dGet ExecBase. @36dEnd ; Determine CPU type and whether FPP is present. ; ---------------------------------------------- ; We need to know whether a non-68000 CPU is present for two ; reasons: First, on the 68000, at least one instruction ; (MOVE.W SR,<ea>) is available in user mode, whereas on the ; newer CPU's, it is privileged. Second, on the newer CPU's, ; when an exception occurs, more information is saved on the ; stack, and in the case of a bus error, the CPU's entire state ; is dumped there so that virtual memory computers (big UNIX ; boxes for example) can recover from page faults. ; Note that the 68020 can do everything the 68010 can, and thus, ; if a 68020 is detected, both the AFB_68010 and the AFB_68020 ; flags will be set. ; We need to know whether there's an FPP present since, for task ; switches, we want to save the FPP registers on the stack as ; as the CPU registers, so each task can think it has the FPP ; all to itself. @d ; We are going to try 68010/020 and 68881 instructions. These will ; cause error exceptions if the respective parts aren't present, so ; we set up to trap these. @36dSave "Illegal Instruction" error vector. @36dSave "1111 Opcode" error vector. @36dPoint to temporary exception handler. @36dInstall this address in both vectors. @d @36dSave the stack pointer. ; Initialize the flags to zero (D0), and point to address zero (D1). ; Then we try to set the 68010 Vector Base Register, which determines ; where the exception vector table is. In the 68000, this is hard ; wired at zero. If the 68010 is present, we set it to zero. @d @d @a?fc0564 FC0564 movec D1,VBR Set Vector Base Register to 0. @a=fc0568 ; If we're still here, the CPU is at least a 68010. We thus set ; the AFB_68010 flag. Then we try to access a 68020-specific ; feature, namely, we try to enable its instruction cache. @36dSet AFB_68010 flag. @d @a?fc056e FC056E movec D1,CACR Try enabling the 68020 cache. @a=fc0572 ; If we're still here, we have a 68020, and so we set the AFB_68020 ; flag. Then we see if we also have a 68881 FPP, by trying to ; access one of its registers. Note that this will not cause an ; exception if no FPP is present. @36dSet AFB_68020 flag. @a?fc0576 FC0576 fmove.l FPCR,D1 Try reading a 68881 register. @a=fc057a @36dDid it work? @d @36dIf so, set the AFB_68881 flag. ; We continue here either from above, or, on plain Amigas, by ; an error exception from one of the foreign instructions above. ; D0 contains all the flags which have been set along the way. ; We restore the two changed entries in the EVT and the stack ; pointer, then exit. @36dRestore the stack pointer. @36dRestore the exception vectors. @d @d @d ; Chip Memory Checking Routine ; ---------------------------- ; This routine checks for the presence of memory. It is used at ; startup to determine how much chip memory is available. Note ; that it can't be used to check for memory at $C00000, and a ; special routine is provided to do this further on. ; On entry, A0 is the lower bound of the area to check, and A1 is ; the high bound. Memory is checked in 4K blocks. @d @40dWrite a zero to the first location. @40dSave the first location. @40dUse this as a signature value. ; Main loop: We enter here to check each 4K block. @40dIncrement current location by 4K. @40dSee if upper bound reached. @40dIf so, exit from the loop. @40dWrite the signature into memory. ; Longword 0 of the block being checked was initially cleared to ; zero. If it is now no longer zero, we have "wrapped around", ; i.e. due to incomplete address decoding, we have written the ; signature value at the beginning of the block. When this ; occurs, we have reached the end of memory, even though the ; signature value would read back correctly. @40dCheck location 0. @40dExit if signature appears there. @40dSee if signature can be read back. @40dIf successful, go check more memory. ; Done, return the end address of memory to the user. Return ; via indirect jump through A5 since we don't have a stack yet. @d @d ; Error System Reset Routine ; -------------------------- ; This is the routine which blinks the power light, then resets ; the computer. It is called from the startup code if a failure ; of any sort is detected. The colour of the screen indicates ; the type of failure. ; This is the exception entry point. All vectors in the ; Exception Vector Table point here while the ROM kernel is ; initializing itself. A yellow screen means an unexpected ; exception has occurred. @40dColour number for yellow. ; This is the non-exception entry point. From here on down it's ; a general purpose routine which can be entered with a coulour ; number in D0. @40dPoint to the custom chips. @40dSet BPLCON0 for a blank screen. @40dSet bitplane 0 data to zeros. @40dSet background colour to yellow. @40dFor D1 = 1 to 10 do @42dSet delay to 1 time unit. @42dMake power light dim. @42dDelay. @42dSet delay to 0.5 time unit. @42dMake power light bright. @42dDelay. @40dEndfor ; Note: The "boot" and "ig" commands from ROM-Wack jump here. @40dDelay some more. @d @d @40dReset everything external to the CPU. ; Get the initial PC from the ROM (now mapped at zero due to the ; reset instruction) and start over. @d @d ; Memory Clear Subroutine ; ----------------------- ; This subroutine clears a block of memory. The start address is ; in A0, the end address is in D0. Since we may not have a stack, ; the return address is provided in A5. @40dValue to store in memory. @40dCompute number of bytes to clear. @40dDivide by 4 (number of longwords). @40dPut the low-order 16 bits in D0, @40dand the high-order ones in D1, and @40dstart at the bottom (for dbra's). @40dClear a longword. @40dLoop until current 256K block done. @40dLoop until everything done. @40dReturn to caller. ; $C00000 Expansion RAM Checker ; ----------------------------- ; The following routine checks for the presence of memory ; in the $C00000 - $DC0000 area. This is a nontrivial exercise, ; since if there is no memory there, we see images of the custom ; chip registers there instead, due to incomplete address decoding. ; ; This took a while to figure out, so I'm commenting it ; very heavily for my own satisfaction. ; Register A4 holds the end address of the block where we know ; RAM to reside. At first, we initialize this to the start address. ; Then, each time through the loop, we copy A4 to a temporary ; register, which we increment by 256K. @40dCopy start address into A4. @40dCopy A4 into temporary register. @40dAdd 256K to temporary register. ; Now we write to an address $0F66 bytes less than the temporary ; register. If there is RAM here, this will write into it near ; the top of the 256K block. If there isn't, this will write to ; the INTENA register and disable all interrupts. @40dWrite to RAM or INTENA. ; Now we read an address $0FE4 bytes below the temporary address. ; If there is memory here, this will read it. Otherwise, it will ; read the INTENAR register. If we find a non-zero value, it must ; be memory, since all bits in INTENAR were reset above. Otherwise, ; it could be either (memory could happen to contain zero). @40dRead RAM or INTENAR. @40dIf not zero, we've found memory. ; We got a zero. Make sure this isn't INTENAR by causing bits ; to be set in it. We set all of them except for the master ; interrupt enable. Again, if there's RAM here, this won't do ; anything to INTENAR, and we'll continue to see a zero. @40dWrite to RAM or INTENA. ; Read the same location as before. If this returns anything but ; $3FFF, it's fine. $3FFF means we're seeing INTENAR. If it's ; still zero, it's RAM. Anything else would be a fatal error, but ; that isn't checked for. @40dRead RAM or INTENAR. @40dExit from loop if INTENAR seen. ; Now we bump up A4 by copying the temporary register back. This ; means we have found a valid 256K block. We then go on looking ; until we've reached the upper limit of the address space to check. @40dUpdate A4 (means memory was found). @40dCompare to upper limit. @40dKeep looking if not reached. ; Continue here when we've reached the end of the space to check, ; or finally seen an image of the INTENAR register. We return the ; end address of the detected RAM in A4, or zero A4 if no RAM ; was found. @40dDisable all interrupts. @40dWas A4 ever updated? @40dIf so, return it. @40dZero A4 (indicates no memory). @40dReturn to caller. @8p@32wPadding --------------------------------------------------------------------------- AddDevice( device ) A1 --------------------------------------------------------------------------- @36dPoint to the system device list. @36dAdd the device to the list. @36dUpdate the device's vector checksum. @d --------------------------------------------------------------------------- RemDevice( device ) A1 --------------------------------------------------------------------------- ; NOTE: Some other part of the system, presumably DOS, will patch ; itself in ahead of this. @36dJust go to RemLibrary(). --------------------------------------------------------------------------- error = OpenDevice( devName, unitNumber, ioRequest, flags ) D0 A0 D0 A1 D1 --------------------------------------------------------------------------- ; NOTE: Some other part of the system, presumably DOS, will patch ; itself in ahead of this, presumably so it can pull devices off ; disk if they aren't already in the device list. @d @36dPoint to the ioRequest. @36dClear the io_Error field in it. @d @d @36dPoint to the system device list. @36dForbid() @36dFindName() @36dGet the pointer to the device. @d @36dStore it in the ioRequest. @36dReturn -1 if device not found. @36dClear the io_Unit pointer. @d @36dSave ExecBase. @36dPoint A6 to the device's base address. @36dCall the device's Open() function. @36dRestore ExecBase. @36dGet the io_Error (from the Open() call). @36dExtend to a longword for return to @36dthe caller. @36dPermit() @d @d ; Continue here if the device was not found. @36dSet the return value to -1. @36dSet the io_Error field to -1. @d --------------------------------------------------------------------------- CloseDevice( ioRequest ) A1 --------------------------------------------------------------------------- ; NOTE: Some other part of the system, presumably DOS, will patch ; itself in ahead of this. @36dForbid() @36dSave ExecBase. @36dGet the pointer to the device node. @36dCall the device's Close() function. @36dRestore ExecBase. @36dPermit() @d --------------------------------------------------------------------------- SendIO( ioRequest ) A1 --------------------------------------------------------------------------- ; The command is sent to device with the "quick I/O" bit not set. ; This means the device must always respond by sending the ; I/O request back as a reply message when done. @36dClear the "quick I/O" bit. @36dSave ExecBase. @36dGet pointer to device node. @36dCall the device's "BeginIO" entry point. @36dRestore ExecBase. @d --------------------------------------------------------------------------- error = DoIO( ioRequest ) D0 A1 --------------------------------------------------------------------------- ; The command is sent to the device with the "quick I/O" bit set. ; This allows the device to decide whether to process the request ; synchronously or asynchronously. @36dSave ioRequest pointer for later. @36dSet the "quick I/O" bit. @36dSave ExecBase. @36dGet pointer to device node. @36dCall the device's "BeginIO" entry point. @36dRestore ExecBase. @36dGet ioRequest pointer back. ; Now fall through to WaitIO. --------------------------------------------------------------------------- error = WaitIO( ioRequest ) D0 A1 --------------------------------------------------------------------------- ; If the "quick I/O" bit is set, then the call to the device ; in BeginIO() finished the I/O operation synchronously, and ; we can return to the caller. Otherwise, we must wait for ; the reply message from the device. @36dCheck the "quick I/O" bit. @36dIf still set, the I/O is complete. ; "quick I/O" bit was not (or no longer) set, so we need to ; wait for the reply message from the device. @36dSave A2. @36dSave pointer to the ioRequest. @36dGet pointer to the reply port. @36dGet the reply port's signal bit. @d @36dConvert to signal mask. @36dDisable() @d @36dCheck if the ioRequest became NT_REPLYMSG @d @36dWait for the signal. @36dGo back and check the ioRequest again. @36dUnlink the ioRequest from the reply @36dport's message queue. @d @d @d @36dEnable() @d @d @36dRestore pointer to the ioRequest. @36dRestore A2. @36dGet the error field from the ioRequest. @36dExtend to long word. @d @d --------------------------------------------------------------------------- result = CheckIO( ioRequest ) D0 A1 --------------------------------------------------------------------------- @a?fc074e @36dCheck the "quick I/O" bit. @36dIf set, request is done, so... @36dReturn address of ioRequest. @d @36dCheck if ioRequest type is NT_REPLYMSG @36dIf so, it is finished. @36dReturn zero. @d @36dReturn address of ioRequest. @d --------------------------------------------------------------------------- AbortIO( ioRequest ) A1 --------------------------------------------------------------------------- @36dSave ExecBase. @36dGet pointer to the device node. @36dCall the device's "AbortIO" entry point. @36dRestore ExecBase. @d ; Table used to set up the exception vector table. ; Each entry is a 16-bit offset from the table's base address ; to the address of the exception handler for that exception. ; The final longword of zeros marks the end of the table. ; The first entry means that the handler for exception number 2 ; (bus error) is at $FC0778 + $64. The next entry is for exception ; number 3, and so on through to the end of the TRAP instruction ; vectors. @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@l @7p@29wEnd of table marker. @7p@29wPadding. ; Exception entry points. The indicated entries in the Exception ; Vector Table are set to point here. ; The BSR instructions are used so that the stacked return address ; from the BSR can be used to find out which exception occurred. @d @d @36dBus error. @36dAddress error. @36dIllegal instruction. @36dDivide by zero. @36dCHK instruction. @36dTRAPV instruction. @d @36dTrace mode. @36d"1010" opcode. @36d"1111" opcode. @36dReserved vector #12. @36dReserved vector #13. @36dReserved vector #14. @36dReserved vector #15. @36dReserved vectors #16-23, spurious int. @36dTRAP #0 @36dTRAP #1 @36dTRAP #2 @36dTRAP #3 @36dTRAP #4 @36dTRAP #5 @36dTRAP #6 @36dTRAP #7 @36dTRAP #8 @36dTRAP #9 @36dTRAP #10 @36dTRAP #11 @36dTRAP #12 @36dTRAP #13 @36dTRAP #14 @36dTRAP #15 ; Handler for reserved exceptions #16-23 and spurious interrupts. ; These are dead ends (click for Guru). @a?fc081a FC081A or.w #$0700,SR Disable all maskable interrupts. @a=fc081e @36dAlert number (fatal). @36dFreeze the task. ; Another exception handler. This one is a dead end also. @a?fc0828 FC0828 or.w #$0700,SR Disable all maskable interrupts. @a=fc082c @d @36dMake alert number. @36dFreeze the task. ; Handler for bus and address errors (long stack frame). ; These two can be caught by the task if set up to do so. @36dUse the return address on the stack @36dto compute the exception number. @36dSee if error occurred in supervisor mode. @36dIf not, go to task's trap routine. @36dFreeze the task. ; Handler for miscellaneous other errors. ; These can be caught by the task. @36dUse the return address on the stack @36dto compute the exception number. @36dSee if error occurred in supervisor mode. @36dIf not, go to task's trap routine. @36dGuru time if in supervisor mode. ; Handler for TRAP instructions. @36dUse the return address on the stack @36dto compute the exception number. @36dSee if error occurred in supervisor mode. @36dGuru time if in supervisor mode. @36dElse go to task specific trap routine. ; Bus error handler for 68010/020 processors. These processors ; produce a more detailed stack frame when they hit a bus error ; (for use in virtual memory systems). The bus and address ; error exception vectors are set to point here at system startup ; if such a processor is being used. @36dPush a zero from the stack. @36dRead the exception number. @36dConvert it to a format compatible @36dwith the numbering scheme used @36delsewhere. @36dProcess the exception normally. ; We get here if various exceptions occurred in user mode. ; This looks up an exception handler address in the current task's ; descriptor, and jumps there. This allows each task to specify ; what is to happen if it causes an exception or executes a ; TRAP instruction. ; The task can clean up the stack and continue running if it ; wants to. All its registers are preserved. @36dReserve 4 words on stack and save A0. @36dGet ExecBase @36dGet the current task pointer. @36dGet current task's tc_TrapCode address and put it on the stack for the RTS. @36dRestore A0. @d --------------------------------------------------------------------------- Supervisor( code_to_execute ) A5 --------------------------------------------------------------------------- ; This routine is used to run things with the CPU in supervisor ; mode. The address of the code to execute once we're in supervisor ; mode is passed in A5. ; First, we tamper with the status register (attempt to set the ; supervisor mode bit). If successful, we are already in supervisor ; mode. If not, there will be a privilege violation, handled by ; the exception handler below. @a?fc08aa FC08AA or.w #$2000,SR Tamper with the status register. @a=fc08ae ; Are we still here? If so, we are already in supervisor mode. ; Fake an exception by pushing status register and PC on the stack. @36dPush a fake program counter. @36dPush the status register. @36dGo execute the caller's code. @d ; The following is 68010/020 version of the above. It is used ; if the startup code has detected such a processor and pointed ; the Supervisor() vector here. @a?fc08ba FC08BA or.w #$2000,SR Tamper with the status register. @a=fc08be @36dMake room on the stack. @36dStore status register. @36dStore fake program counter. @36dStore 68010/020 stack frame type. @36dGo execute the caller's code. ; Privilege violation exception handler. ; First, we find out where the CPU was when it got the privilege ; violation. @36dWas it the 68000 Supervisor() function? @d @36dWas it the 68010/020 version? @d @36dYes, fix return address, and go @36dexecute the caller's code. ; If we get here, it was a real privilege violation, i.e. not one ; of the intentionally caused ones above. @a?fc08f0 FC08F0 or.w #$0700,SR Disable all maskable interrupts. @a=fc08f4 @36dMake alert number. @36dFreeze the current task or guru. @8p@28wPadding. ; ROMTAG Scanner and "KickMemPtr/KickTagPtr" Processor ; ---------------------------------------------------- ; The routines in this section do two things. One, they scan a ; section of the CPU addressing space for resident modules, each ; flagged by a "RomTag". For an example of what a RomTag looks ; like, check out the one at the very beginning of the exec. ; The start and end addresses of each piece of CPU address space ; to look for RomTags in are given in a table, to which A0 must ; point on entry. ; A temporary list is built, containing nodes which each point to ; a RomTag. Each resident module has a name, and no two modules ; with the same name are allowed. If there is a conflict, the ; one with the higher version number or priority wins and the ; other one is discarded. ; After the list of RomTags has been compiled, we process something ; called the KickMemPtr and the KickTagPtr, found in the ExecBase ; structure. The former points to a chain of MemLists. The latter ; points to an odd type of chain containing pointers to RomTags. ; If you really need to know the format of this chain, read the ; comments at the appropriate code. ; All the MemList structures, and tables of RomTag addresses, pointed ; to by these pointers, have a checksum, also stored in ExecBase. ; If the checksum we calculate doesn't match that checksum, we ; discard all the data (assume it has been damaged in a system crash ; before the reboot). ; If the checkum matches, then we try to allocate all the pieces ; of memory pointed to by the KickMemPtr MemLists. If this is ; successful, we add all the KickTagPtr RomTags to the RomTag list. ; Presumably, the KickMemPtr list will indicate where RAM-resident ; modules are located, and if we were able to reclaim them before ; the memory got allocated for something else, we add them to the ; system again after a reboot. ; Finally, we process the RomTag list. This takes the form of ; building a table of resident module addresses, terminated with ; a zero, in memory allocated for that purpose. The RomTag list is ; deallocated. Note that the table of module addresses has the ; same format as one of the entries in the KickTagPtr list. ; Each resident module can have a flag set in its RomTag which ; which causes it to be automatically initialized later. @d ; First, create a temporary list structure to hold the found RomTags ; in. The list header is put on the stack. @36dReserve 14 bytes on the stack (enough @36dfor a list header), and get the address. @36dClear the list to empty. @d @d @d ; Now process the table of areas to look for RomTags in. Call the ; RomTag scanning routine with the data from each table entry to ; find all the RomTags in that section of address space. @36dPoint to the start of the address table. @36dSee if end of table reached. @36dNegative value indicates end. @36dGet the start address and the end address @36dof an area to look for RomTags in. @36dScan the area for RomTags. @36dLoop until end of list reached. ; Now process the "Kick" variables in ExecBase. These apparently ; exist to allow a set of resident modules (such as a RRD) to ; survive a system reboot. If the checksum of all the data tables ; is still valid, we claim the memory and add the RomTags to ; the list. @36dSumKickData() @36dVerify the checksum against the old one. @36dSkip the following if not valid. @36dTry to reallocate all the MemLists. @36dDid we get all the memory back? @36dIf not, don't bother with the KickTags. @36dProcess the list of "KickTags". @36dBuild a table of RomTag addresses. @36dDeallocate the RomTag list header. @d @d ; This routine scans an area indicated by A4 (start address) and ; D4 (end address) for RomTag structures, and puts any found RomTags ; on the RomTag list. If there is more than one version of any ; given RomTag, the newest one wins, the older ones are discarded. @d @36dLoad the RomTag matchword. @36dGet the end address. @36dSubtract the start address. @36dIf less than zero, return. @36dDivide the result by 2 (number of words). @36dDecrement by 1 (for DBcc instruction). @36dSplit into two halves (since DBcc can @36donly use 16-bit counters). @d @36dLook for the RomTag matchword. @d @36dLoop until entire area scanned. @36dExit if end reached. ; We've found a RomTag matchword. A4 points to it. Check if ; this is really a RomTag by verifying the pointer directly ; after the matchword. @36dPoint to the matchword. @36dSee the longword directly after the matchword points to it. @36dNot a valid RomTag if not. ; We are pointing to a valid RomTag. Add it to the RomTag list. @36dAdd RomTag to list of found ones. ; Each RomTag has in it a pointer (RT_ENDSKIP) which points to ; the address where to start looking for the next RomTag, i.e. ; the end of whatever code/data is associated with it. @36dSkip to the end of the module. @36dStart looking for the next one. @d @d ; This subroutine adds a RomTag to the list of found RomTags. ; If the current RomTag has the same name as a RomTag already in ; the list, the older (or if same age, lower priority) version ; is discarded. Memory is allocated to hold each node as needed. @36dPoint to the temporary list on the stack. @36dGet the name of this RomTag. @36dFindName() @36dDid we find a node with this name? @36dIf not, go and add the tag to the list. @36dPoint to the node we found. @36dCompare the version numbers of the list @36dnode and the newly found RomTag to find @36dout which is more recent. @36dDiscard current RomTag if older. @36dUse current one if newer. @36dVersion numbers match, so compare @36dthe priority fields. @36dDiscard current RomTag if lower priority. ; This RomTag supersedes a previous one, which is already in the ; list. Therefore we discard the one in the list. @36dPoint to the node in the list. @36dUnlink it from the list. @36dKeep a pointer to the node's memory. @36dGo and make a new node out of it. ; We enter here if this RomTag doesn't supersede some other one. ; This means we have to allocate some memory for a list node to ; store information about it. @36dNo particular memory requirements. @36d14 bytes. @36dGo and allocate the memory. @36dDid we get the memory? @36dIf not, skip the following. @36dPoint to the memory. ; We have obtained 14 bytes of memory one way or another, and we ; now build a list node and add it to the list of found RomTags. @36dCopy the RomTag's priority. @36dCopy the RomTag's name. @36dStore the RomTag's address. @d @36dAnd enqueue on the RomTag list. @d ; RomTag list to resident module table converter. ; This routine scans the temporary RomTag list, and builds a table ; of resident module addresses from it. The list itself is ; deallocated. @36dStart with 1 longword needed. @36dStart at the head node of the list. @36dGet pointer to the current node. @36dGet pointer to the next node. @36dEnd of list reached if zero. @36d4 longwords needed for this node. @36dScan the rest of the list. @36dMEMF_PUBLIC | MEMF_CLEAR. @36dAllocate memory. @36dGet pointer to allocated memory. @36dSave a copy of it. @36dStart at the head of the RomTag list. @36dGet pointer to current node. @36dGet pointer to next node. @36dEnd of list reached if zero. @36dStore pointer to this RomTag. @36d14 bytes. @36dFreeMem() the list node. @36dGo process next list node. @36dMark end of "KickTag" list. @36dReturn its base address. @d ; KickTagPtr processor. ; This routine steps through the table(s) of RomTag addresses ; pointed to by the KickTagPtr, and adds all the RomTags pointed ; to by these addresses to the list of found RomTags. @d @36dGet the KickTagPtr. @36dJust exit if it is zero. @36dPoint to the first tag. @36dRead a longword from the current tag. @36dExit if end of list reached. @36dIf this is a link, handle it. @36dData must be a pointer to a RomTag. @36dAdd the RomTag to the list. @36dContinue with current node. @36dStrip high bit from pointer to next @36dnode in the list of "KickTags". @36dGo process the next node. @d @d --------------------------------------------------------------------------- SumKickData() --------------------------------------------------------------------------- ; This routine computes the KickCheckSum by checksumming all the ; tables associated with the KickMemPtr and KickTagPtr pointers. ; The result should is the KickCheckSum. @a?fc0a3c @d @36dPoint to the KickMemPtr in ExecBase. @36dGet the KickMemPtr and the KickTagPtr. @36dClear both to zero in the ExecBase @36dstructure. @36dStart checksum at -1. @36dStart at the old KickMemPtr. @36dEnd of list reached? @36dExit from loop if so. @36dPoint to current MemList. @36dGet pointer to next MemList. @36dGet the number of entries in the MemList. @36dDouble it and add 4 more longwords, to @36dget the size (in longwords) of this list. @36dChecksum the MemList. @36dGo process next MemList, if any. @36dGet the old KickTagPtr. @36dSkip the following if none. @36dPoint to the start of the list. @36dStart checksumming the list. @36dAdd a longword to the checksum. @36dGet first data word at this node. @36dExit if end of list reached. @36dIf high bit clear, add the data to the checksum and go on to the next longword. @36dClear the high bit. @36dUse result as pointer to next node. @36dContinue processing the list. @36dPut KickMemPtr/KickTagPtr back. @d @d ; Subroutine to checksum all the entries in a MemList. @d @d @d ; The KickMemPtr points to a list of MemLists, each pointing to ; some number of chunks of memory. This routine steps through ; all the MemLists, allocating all those chunks. If any can't ; be allocated (memory already grabbed by someone else), it returns ; zero. If successful, it returns 1. @36dStart where the KickMemPtr points. @36dEnd of list reached? @36dIf so, return 1 and exit. @36dPoint to the current MemList. @36dGet pointer to the next MemList. @36dAdvance past the MemList header. @36dGet number of entries in this MemList. @36dFake "successful allocation" return code. @36dEnter the loop. ; Allocate all the pieces of memory indicated by this MemList. @36dGet the address of this entry. @36dGet the size of this entry. @36dAllocAbs() @36dSuccessful? @36dIf so and moreLamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!Lamer!!! D0 A1 --------------------------------------------------------------------------- @d @36dGet pointer to the resident module table. @d ; Outer loop: Step through the table(s) of RomTags, looking ; for one whose name matches the wanted one. @36dGet a RomTag address from the table. @36dReturn zero if end of table reached. @36dIf table entry is a link then @38dclear its high bit. @38duse it as a new table pointer. @38dGo to the top of the loop. @36dEndif @36dGet pointer to desired name. @36dPoint to RomTag's name field. ; Inner loop: Compare the wanted name with that in the RomTag. @36dCompare a character. @36dBack to top of loop if not equal. @36dDid we reach the terminating zeros? @36dCompare more characters if not. ; Fall through with the module's address in D0. @d @d --------------------------------------------------------------------------- InitCode( startClass, version ) D0 D1 --------------------------------------------------------------------------- ; This function initializes modules from the resident module list. ; The resident module list is one or more tables of RomTag addresses. ; Such tables can be linked together by including the address of the ; next table, with the high bit set, as the last address in a table. ; We are given a set of required flags in D0, and a minimum ; version number in D1. Modules which have flags not set whose bits ; appear in D0, or are of a lower version than D1, will not be ; initialized. @d @40dGet the ResModules pointer. @40dGet forbidden flags. @40dGet cutoff version number. @40dRead a module address from the table. @40dExit if end of table reached. @40dIs the high bit set? If so, it's @40da pointer to another table, so strip @40dthe high bit off, point to the next @40dtable, and process it. @40dCheck the module's version number @40dagainst the cutoff. @40dIgnore module if too low. @40dGet the module's flags. @40dCheck against mask. @40dIgnore it if required ones not set. @d @40dInitResident() @40dProcess next resident module. @d @d --------------------------------------------------------------------------- InitResident( resident, segList ) A1 D1 --------------------------------------------------------------------------- ; This initializes a resident module. The pointer to the module ; is in register A1. If the RT_FLAGS byte at offset 10 from ; the module pointer has the RTF_AUTOINIT flag set, we let the ; module initialize itself by calling its initialization code. ; Otherwise we assume it's a library type of thing, and call ; MakeLibrary with a set of parameters taken from the module. ; If the MakeLibrary succeeds, we add the module to the appropriate ; system list. @40dCheck the RTF_AUTOINIT flag. @d @40dGet the RT_INIT address. @d @40dGet the segList pointer. @40dCall the module's init code. @d ; The RTF_AUTOINIT flag was not set, so we MakeLibrary() the ; module instead. Note that the address at RT_INIT points to ; a data structure containing parameters for MakeLibrary(). @d @40dGet the initialization address. @40dGet parameters for MakeLibrary. @40dMakeLibrary() @d @40dStore the library pointer. @40dIf zero, just return it and exit. ; The library has been created successfully. @40dGet the library pointer. @40dGet the module's node type. @40dIf the node type is NT_DEVICE then @d @42dAddDevice(). @d @40dElse if it is NT_LIBRARY then @d @42dAddLibrary(). @d @40dElse if it is NT_RESOURCE then @d @42dAddResource(). @40dEndif. @d ; InitStruct entry point to copy one byte several times (-186). @40dGet the byte initially. @40dCopy it the required number of times. @d @40dBack to main loop. ; Entry ponint for invalid InitStruct commands (-176). Guru time. @40dSave registers. @40dAlert number (fatal). @40dGet ExecBase @40dPut up the Alert. @40dThis never gets executed. ; InitStruct entry point to copy one longword several times (-154). @40dRound A1 up to next even address. @d @d @d @40dGet the longword initially. @40dCopy it the required number of times. @d @40dBack to main loop. ; InitStruct entry point to copy one word several times (-134). @40dRound A1 up to next even address. @d @d @d @40dGet the word initially. @40dCopy it the required number of times. @d @40dBack to main loop. --------------------------------------------------------------------------- InitStruct( initTable, memory, size ) A1 A2 D0 --------------------------------------------------------------------------- @36dGet structure base address. @36dDivide size by 2 (get size in words) @d @36dLoop: Clear the structure's memory. @d @36dGet structure base address again. @d @36dRead a byte from the initTable. @36dIf it is zero, we're done. @36dCheck and reset offset flag. @36dBranch if it was not set. ; Process an offset command (10ssnnnn or 11ssnnnn). @36dCheck and reset byte/rptr flag. @36dBranch if it was not set. @36dBack up over command byte. @36dGet cmd byte + 24 bit data. @36dMask out the command byte. @d ; Process an offset/byte command (10ssnnnn). @d @36dGet next byte from table into D1. ; Continue here for both offset commands. In each case, A1 now ; points at the next even address in the initTable, and D1 contains ; the offset we have just fetched from it, as a longword. @36dGet structure base address. @36dAdd offset just computed. ; Continue here for all commands, A0 now holds the absolute ; address where the next operand should go, D0 contains what is ; left of the command byte, and A1 points at the current even ; address in the table. @38dGet the command byte @38dShift and mask it so it becomes @38d2 * (sdd) @38dGet the jump offset from the table. @38dMask all but (nnnn) out of the command. @38dJump to the right routine. ; Entry point for copying multiple bytes (-42). @36dCopy one byte. @36dLoop until required number done. @36dRound A1 up to next even address. @d @d @d @36dGo back to main loop. ; Entry point for copying multiple longwords (-24). ; Simply falls through to word copy routine, set up to copy twice as ; many words. An extra 1 must be added to compensate for the effects ; of the "dbra" instruction. @36dD0 = (2 * D0) + 1. @d ; Entry point for copying multiple words (-20). @36dRound A1 up to next even address. @d @d @d @36dMove the data. @d @36dGo back to top of main loop. @d ; Dispatch table for InitStruct. @8p@10w-24 (sdd = 000) longword, count. @8p@10w-20 (sdd = 001) word, count. @8p@10w-42 (sdd = 010) byte, count. @8p@10w-176 (sdd = 011) Invalid. @8p@10w-154 (sdd = 100) longword, repeat. @8p@10w-134 (sdd = 101) word, repeat. @8p@10w-186 (sdd = 110) byte, repeat. @8p@10w-176 (sdd = 111) Invalid. @8p@28w Padding. ; The following is used to bail out of an interrupt which we should ; have ignored. @d @d ; Level 1 Autovector interrupt entry point. ; ----------------------------------------- @d @36dPoint to custom chip register area. @36dGet ExecBase. @36dGet interrupt enable register. @36dCheck master interrupt enable. @36dBail out if no interrupts enabled. @36dCheck for pending & enabled interrupts. @36dSerial port transmit interrupt? @d @36dGet IntVects[0] handler data. @36dPush address of ExitIntr() @d @36dDisk block finished interrupt? @d @36dGet IntVects[1] handler data. @36dPush address of ExitIntr() @d @36dSoftware generated interrupt? @d @36dGet IntVects[2] handler data. @36dPush address of ExitIntr() @d @36dBail out if nothing found. ; Level 2 Autovector interrupt entry point. ; ----------------------------------------- @d @36dSee level 1 for comments. @d @d @d @d @d @36dI/O port or timer interrupt? @d @36dGet IntVects[3] handler data. @36dPush address of ExitIntr() @d @36dBail out if nothing found. ; Level 3 Autovector interrupt entry point. ; ----------------------------------------- @a?fc0cd8 @d @36dSee level 1 for comments. @d @d @d @d @d @36dBlitter finished? @d @36dGet IntVects[6] handler data. @36dPush address of ExitIntr() @d @36dStart of vertical blank? @d @36dGet IntVects[5] handler data. @36dPush address of ExitIntr() @d @36dCopper interrupt? @d @36dGet IntVects[4] handler data. @36dPush address of ExitIntr() @d @36dBail out if nothing found. ; Level 4 Autovector interrupt entry point. ; ----------------------------------------- @d @36dSee level 1 for comments. @d @d @d @d @d @36dAudio channel 1? @d @36dGet IntVects[8] handler data. @36dUse special ExitIntr() below. @d @a?fc0d62 @36dAudio channel 3? @d @36dGet IntVects[10] handler data. @36dUse special ExitIntr() below. @d @36dAudio channel 0? @d @36dGet IntVects[7] handler data. @36dUse special ExitIntr() below. @d @36dAudio channel 2? @d @36dGet IntVects[9] handler data. @36dUse special ExitIntr() below. @d @36dBail out if nothing found. ; This routine allows a single invocation of the level 4 interrupt ; handler to service all level 4 interrupts which are pending or ; become pending while one is serviced. @36dPoint at custom chip register area. @36dGet ExecBase. @36dMask for all level 4 interrupt bits. @d @36dFind enabled and pending level 4 ints. @36dIf any still pending, go service them. @36dOtherwise, do ExitIntr() ; Level 5 Autovector interrupt entry point. ; ----------------------------------------- @d @36dSee level 1 for comments. @d @d @d @d @d @36dDisk sync interrupt? @d @36dGet IntVects[12] handler data. @36dPush address of ExitIntr() @d @36dSerial port receive interrupt? @d @36dGet IntVects[11] handler data. @36dPush address of ExitIntr() @d @36dBail out if nothing found. ; Level 6 Autovector interrupt entry point. ; ----------------------------------------- @d @36dSee level 1 for comments. @d @d @d @d @d @36dSpecial copper interrupt? @d @36dGet IntVects[14] handler data. @36dPush address of ExitIntr() @d @36dExternal level 6 interrupt? @d @36dGet IntVects[13] handler data. @36dPush address of ExitIntr() @d @36dBail out if nothing found. ; Level 7 Autovector interrupt entry point. ; ----------------------------------------- @d @d @36d Get IntVects[15] handler data. @d @d @d --------------------------------------------------------------------------- ExitIntr() --------------------------------------------------------------------------- ; This routine is called after an interrupt handler has finished. ; It checks if a task switch is necessary. If not, it returns ; from the interrupt. Otherwise, it drops into the scheduler. ; The initial check for supervisor mode is very important. If ; the CPU was in supervisor mode, then there either was no task ; running (CPU stopped), or the task had already been interrupted ; by a lower priority interrupt. In either case, we may not do ; a task switch. In the former case, it will be done after the ; CPU comes out of the STOP instruction in the dispatcher, and ; in the latter, it will be taken care of by the lowest priority ; interrupt handler (which interrupted the CPU while it was not ; in supervisor mode). @38dCheck if CPU was in supervisor mode. @38dIf so, just return from interrupt. @38dGet ExecBase. @38dSee if task switching is disabled. @38dIf so, just return from interrupt. @38dCheck the scheduling attention flag. @38dReturn from interrupt if not set. @a?fc0e7a FC0E7A move.w #$2000,SR Enable all interrupts. @a=fc0e7e @d @d @d --------------------------------------------------------------------------- Schedule() --------------------------------------------------------------------------- ; This is the scheduler. It is called with the current task's ; registers saved on the system stack, as put there by the interrupt ; entry point, and PC/SR as put there by the interrupt itself. ; ; The scheduler checks if the task should be suspended and another ; task run in its place. The following decision is made: ; ; IF the current task has an exception pending THEN ; reschedule the current task, so that it will be redispatched, ; so that the dispatcher can process the exception. ; ELSEIF the TaskReady queue is empty THEN ; the current task is the only runnable task in the system, so ; let it continue running. ; ELSEIF the TaskReady queue contains a higher priority task THEN ; do a task switch. ; ELSEIF the current task's time slice has expired THEN ; reschedule the current task (it may be redispatched right away, ; with a new time slice). ; ELSE ; let the current task keep running. ; ENDIF @d @a?fc0e8a FC0E8A move.w #$2700,SR Mask all maskable interrupts. @a=fc0e8e @38dReset the scheduling attention flag. @38dGet the current task pointer. @38dCheck the task's TB_EXCEPT flag. @d @38dPoint to the TaskReady queue. @38dCheck if the queue is empty. @38dIf so, return from interrupt. @38dGet the queue's head node. @38dGet the head node's priority. @38dCompare to the current task's priority. @38dSwitch tasks if higher priority. @38dCheck the time slice expired flag. @38dIf not expired, return from interrupt. ; If we get this far, it is necessary to put the current task on ; the TaskReady queue and then run the one at the head of the queue. ; This may be the same one if a TB_EXCEPT is being processed. @38dPoint to the TaskReady queue. @38dEnqueue the current task. @38dMake the task's state TS_READY. @a?fc0ecc FC0ECC move.w #$2000,SR Enable all interrupts. @a=fc0ed0 ; Get the current task's saved registers back. They may have been ; put on the stack when an interrupt occurred, or at the start of ; this routine. @d @38dInsert some space in the system stack. @38dPut in the address of Switch(). @38dGet A6 from the stack. @38dGo to Switch(). --------------------------------------------------------------------------- Switch() [Version for machines without a numeric coprocessor] --------------------------------------------------------------------------- ; The ExecBase "Switch()" vector points here if no FPP is installed. ; This routine does a task switch. It stores the context of ; the currently running task, then drops into the dispatcher. ; The routine stores the current CPU context on the user ; stack. The status register and program counter are popped from ; the supervisor stack. The current interrupt disable nesting ; level and the user stack pointer are then saved in this task's ; task descriptor. ; If the TB_SWITCH bit is set in the task descriptor, the task's ; TC_SWITCH function is called. @a?fc0ee0 FC0EE0 move #$2000,SR Enable all interrupts. @a=fc0ee4 @40dStack A5 on the supervisor stack. @40dGet the user stack pointer. @40dStack all registers on user stack. @40dGet ExecBase @40dGet current interrupt disable level. @40dSet interrupt disable level to -1. @40dEnable interrupts. @40dFix A5 in the saved register set. @40dSave the status register. @40dSave the program counter. @40dAddress of context-restore routine for machines without an FPP. @40dFind the current task descriptor. @40dStore interrupt disable level. @40dStore user stack pointer. @40dCheck the TB_SWITCH bit @d @40dIf TB_SWITCH bit was set, then @40dcall this task's TC_SWITCH function. @d --------------------------------------------------------------------------- Dispatch() [Version for machines without a numeric coprocessor] --------------------------------------------------------------------------- ; This routine dispatches the next runnable task, if there is one. ; If the TaskReady queue is empty, the routine waits, stopping ; the processor and checking the queue again each time it resumes ; running (after an interrupt). ; When a runnable task is found, it is dispatched. Its context ; and interrupt disable level are restored, and the TB_LAUNCH ; and TB_EXCEPT flags are checked. If either is set, they are ; handled. @40dAddress of context-restore routine for machines without an FPP. @40dInitialize interrupt disable level @40dat -1, and enable interrupts. @40dPoint at the TaskReady queue. @a?fc0f40 FC0F40 move #$2700,SR Mask all maskable interrupts. @a=fc0f44 @40dCheck for a task descriptor at @40dthe front of the queue. @d ; TaskReady queue is empty. Halt the processor until the next ; interrupt, then check for runnable tasks again. @40dIncrement the idle counter. @40dSet the rescheduling attention flag. @a?fc0f54 FC0F54 stop #$2000 Enable all interrupts and stop. @a=fc0f56 @d @40dGo back and check for runnable tasks. ; We have a runnable task. @40dUnlink the task descriptor from @40dthe TaskReady queue. @d @40dIncrement the dispatch counter. @40dSet the current task pointer. @40dInitialize the time-slice counter. @40dReset the time slice expired flag. @40dSet the task's state to TS_RUN. @40dRestore the interrupt disable level. @d @40dDisable interrupts if interrupt @40ddisable level >= 0. @a?fc0f90 FC0F90 move #$2000,SR Enable all interrupts. @a=fc0f94 @40dGet the task's flags. @40dCheck for TB_EXCEPT or TB_LAUNCH. @d @40dProcess the flags if either was set. @40dGet the user stack pointer. @40dRestore the CPU context. ; Context Restore routine (non-FPP version) ; ----------------------------------------- ; Pop the task's CPU context off the user stack, and start it ; running. The program counter and status register are set by ; storing them on the supervisor stack for the "RTE" instruction. @40dGet the user stack pointer. @40dSet it. @40dGet the program counter. @40dGet the status register. @40dRestore all the other registers. @40dAnd start running the task. ; Subroutine to handle TB_LAUNCH and TB_EXCEPT. @40dCheck the TB_LAUNCH bit. @d @40dIf it was set, call the task's @40dTC_LAUNCH routine. @d @40dRestore D0. @40dCheck the TB_EXCEPT bit. @d @40dReturn if it was clear. --------------------------------------------------------------------------- Exception() --------------------------------------------------------------------------- ; This routine handles task-level exception processing. ; It checks whether any signals have occurred which should cause ; a software exception. If so, it calls the task's exception ; processing routine. After this returns, it restores everything ; so that the task itself can be dispatched. @40dReset the TB_EXCEPT bit. @40dDisable() @d @40dGet received signals. @40dCompare with exception signals. @40dIf any exception signals were set, @40dreset them in both places. @40dEnable() @d @d @40dGet the user stack pointer. ; Store tc_Flags, tc_State, tc_IDNestCnt, tc_TDNestCnt on user stack. @d ; If interrupts have been disabled exactly once, then enable them. @d @d @d @d @d ; Put the address of an exception post-processing routine on the ; user stack. This gets executed when the exception routine ; does an RTS. @40dPut return address on user stack. @40dSet user stack pointer. @40dSee if this is a plain 68000. @d @40dMake 68010/020 extended stack frame. @40dPut exception code and a fake status @40dword on the supervisor stack. @40dPoint to the exception handler's data segment. @d ; Continue here after the user's exception handling routine has run. @40dGet ExecBase. @40dWhere to go in supervisor mode. @40dGo to supervisor mode. @40dAddress of context restore routine. @40dIs this a 68010/020? @d @40dIf so, handle extended stack frame. @40dDo we have a 688881? @d @40dIf so, use the other context-restore. @40dPop PC and SR from stack. @40dGet current task descriptor. @40dSet the exception signals up again. @40dGet the user stack pointer. ; Restore tc_Flags, tc_State, tc_IDNestCnt, tc_TDNestCnt from ; user stack. @d @40dSet USP image in task descriptor. @40dRestore interrupt disable level. @d @d @40dDisable interrupts if needed. @d --------------------------------------------------------------------------- Switch() [Version for machines with a 68881 FPP] --------------------------------------------------------------------------- @a?fc108a FC108A move #$2000,SR Enable all interrupts. @a=fc108e @40dStore A5 on the supervisor stack. @40dGet the user stack pointer. @40dSave all registers on user stack. @40dGet ExecBase. @40dGet current interrupt disable level. @40dSet interrupt disable level to -1. @40dEnable interrupts. @40dFix A5 in the saved register set. @40dSave the status register. @40dSave the program counter. ; 68020/68881 specific material. Without documentation for those ; processors, I can't comment this. @a?fc10b4 FC10B4 fsave -(A5) @a=fc10b6 @d @d @d @d @d @d @d @d @d @d @d @d @d @a?fc10d6 FC10D6 fmovem.x FP0-FP7,-(A5) FC10DA fmovem.l FPCR/FPSR/FPIAR,-(A5) @a=fc10de @d @40dPoint to 68881 context restore. ; The rest of the processing for Switch() is the same, so enter ; the "plain" version in the middle. @d --------------------------------------------------------------------------- Dispatch() [Version for machines with a 68881 FPP] --------------------------------------------------------------------------- @40dPoint to 68881 context-restore. ; The regular Dispatch() can be used for the rest. @d ; Special 68881 FPP compatible context restore ; -------------------------------------------- ; 68020/68881 specific material. Without documentation for those ; processors, I can't comment this. @d @d @d @a?fc10f6 FC10F6 fmovem.l (A5)+,FPCR/FPSR/FPIAR FC10FA fmovem.x (A5)+,FP0-FP7 @a=fc10fe @d @d @d @d @d @d @d @a?fc1110 FC1110 frestore (A5)+ @a=fc1112 @40dThis part is the same as the regular @40dcontext-restore routine. @d @d @d @d --------------------------------------------------------------------------- oldSR = SetSR( newSR, mask) D0 D0 D1 --------------------------------------------------------------------------- @d @36dWhat to execute in supervisor mode. @36dEnter supervisor mode. @d @36dNow in supervisor mode. Restore A5. @36dGet status register from stack. @36dMask unwanted bits out of newSR. @d @36dGet unchanged bits from old status reg. @36dOr in the bits to be changed. @d @36dReturn old status codes in D0. @36dLoad status reg. and return to user mode. --------------------------------------------------------------------------- conditions = GetCC() D0 [Version for 68000 machines only] --------------------------------------------------------------------------- ; The ExecBase vector will have been changed not to point here ; unless the CPU is a 68000, since the following instruction would ; bomb with a privilege violation on 68010/020 processors. @a?fc1140 FC1140 move SR,D0 Get the status register. @a=fc1142 @36dClear all but the condition codes. @d --------------------------------------------------------------------------- oldSysStack = SuperState() D0 --------------------------------------------------------------------------- @d @36dWhat to execute in supervisor mode. @36dEnter supervisor mode. @d @36dContinue here. Restore A5. @d @36dSet supervisor bit in status word. @36dDo nothing more if it was already set. @a?fc115e FC115E move (SP)+,SR Get status register from stack. @a=fc1160 @36dSave supervisor stack pointer. @36dLoad user stack pointer. @36dCheck if running on a plain 68000. @d @36dPop 68010/020 exception info from stack. @36dPop exception return address from stack. @36dReturn to user. @d --------------------------------------------------------------------------- UserState( sysStack ) D0 --------------------------------------------------------------------------- @d @36dWhere to go in supervisor mode. @36dEnter supervisor mode (just in case). @36dRestore A5. @36dGet status register from stack. @36dCopy stack pointer to user stack pointer. @36dSet the system stack pointer. @36dClear the supervisor mode bit. @a?fc118a FC118A move D1,SR @a=fc118c @36dReturn to the user. --------------------------------------------------------------------------- oldInterrupt = SetIntVector( intNumber, interrupt ) D0 D0 A1 --------------------------------------------------------------------------- ; This routine makes an interrupt node the active interrupt node ; for a given interrupt number. This node corresponds to an ; interrupt handler. There can only be one node associated with ; each interrupt number. ; Note: Never try to SetIntVector() one of the interrupts which are ; dispatched as chains of servers. The handler which does this ; does not use regular interrupt vectors or nodes, and it would not ; be possible to hook it up to the interrupt again if it became ; disconnected. @36dCompute address of @36dExecBase->IntVects[intNumber] @36dDisable() @d @36dGet pointer to current interrupt node. @36dInstall pointer to new interrupt node. @36dIf there is a new node, then @36d Install interrupt data pointer. @36d Install interrupt code pointer. @36dElse @36d Set code and data pointers to -1. @d @36dEndif @36dEnable() @d @d @d --------------------------------------------------------------------------- AddIntServer( intNumber, interrupt ) D0 A1 --------------------------------------------------------------------------- ; Adds an interrupt server to the server chain for a given ; interrupt number. The interrupt number must be one of the ; set for which interrupt server lists have been set up. ; The interrupt for which the server is being added will be enabled ; if it is not already enabled. @d @36dSave interrupt number. @d @36dCompute @36dExecBase->IntVects[intNumber].is_Node @d @36dDisable() @d @36dEnqueue the interrupt server. @36dHardware-enable the interrupt @36dcorresponding to the server just added. @d @36dEnable() @d @d @d @d --------------------------------------------------------------------------- RemIntServer( intNumber, interrupt ) D0 A1 --------------------------------------------------------------------------- ; This routine takes an interrupt server off the server chain for ; a given interrupt. The interrupt is disabled if this leaves the ; server chain empty. @d @36dCompute @36dExecBase->IntVects[intNumber].is_Node @d @36dSave interrupt number and node. @d @36dDisable() @d @36dUnlink interrupt server from the chain. @d @36dCheck if the chain is now empty. @d @36dChain is empty. Disable the interrupt @36dcorresponding to this node. @d @36dEnable() @d @d @d @d ; The following routine is used at system startup to initialize ; the exec's internal interrupt handlers. ; It initializes the server chains used to dispatch servers for ; I/O and timer, Copper, vertical blank, external level 6, and ; non-maskable interrupts, and establishes vectors to the exec's ; built-in interrupt handlers. ; For each server chain, a 22-byte data structure is built, ; consisting of a list header and 4 words of control data. The ; 5 server chain headers are put into a contiguous 110-byte ; section of memory. The server chains are initialized to empty, ; and the rest of the data structure is initialized with the ; data required to assert, deassert, and cancel the interrupt. @d @36d110 bytes needed. @36dAttributes = MEMF_PUBLIC | MEMF_CLEAR. @36dRequest memory. @36dSee if we got the memory. @d @36dIf not, it's guru time! @36dUse this alert number. @36dGet ExecBase. @36dGo put up the alert. @36dNever reached. @36dLoop 5 times (4 + 1 for dbra). @36dGet pointer to allocated memory. @36dPoint to the data table below. ; Loop: Divide the 110 byte data area up into five data ; structures, each consisting of a list header and some control ; words. @36dSave pointer to current node. @36dInitialize the node's server chain @36dto empty. @d @d @36dPoint to the node's data area. @36dGet interrupt number from table. @36dMake the control word needed to disable @36dthis interrupt. @36dWrite it in the node's data area. @36dMake the control word needed to enable @36dthis interrupt and store it also. @36dGet the cancel interrupt control word. @36dMove a word of pad data. @36dPoint to the generic interrupt handler. ; Set up this interrupt in the exec table. The interrupt vector ; is at ExecBase + $54 + 12 * interrupt number. We write the code ; and data addresses only; we don't bother with the node address. @d @d @36dLoop until all interrupts done. ; Install the address of the software interrupt handler. The ; software interrupt lists have already been cleared earlier in ; the system startup code. @d ; Now enable interrupts and exit. Note that only the software ; interrupts are enabled. The interrupts handled by the handler ; below will be enabled if and when servers are put on their ; chains. Interrupts handled external to the exec must be enabled ; by whoever provides the handler. @d @d @d ; Interrupt initialization table. Each line consists of an ; interrupt number, and a control word which is used to cancel ; the interrupt if pending. The last column appears to be padding. @8p@5w@5w@w @8p@5w@5w@w @8p@5w@5w@w @8p@5w@5w@w @8p@5w@5w@w ; Generic Interrupt Handler ; ------------------------- ; Each logical interrupt (0-15) has a handler. Some logical ; interrupts are accessible to the user not as handlers, but rather, ; as chains of servers. Most of these (all but the Blitter one) ; use this code, which acts as a handler and dispatches the servers. ; A data structure set up by the previous routine exists for each ; interrupt to store the list header for the server chain, and some ; control words. @d @d @35dCheck the head of the server chain. ; Main loop: Here we dispatch a server from the queue. @35dIs this the end of the chain? @35dIf so, exit. @35dGet the server's code and data addresses. @35dCall it. ; Servers return with the zero flag indicating whether they have ; taken care of the interrupt. We keep calling servers until ; we either run out or until one returns the zero flag clear. @35dIf the zero flag is clear, exit. @35dGo to next node in the queue. @35dGo back to top of loop. ; Either we ran out of servers, or one of them accepted the ; interrupt. Either way, clear the interrupt and exit. @d @35dClear the interrupt. @d --------------------------------------------------------------------------- Cause( interrupt ) A1 --------------------------------------------------------------------------- @36dDisable() @d @36dCheck if node type is already NT_SOFTINT. @36dIf so, do nothing. @36dMake node type NT_SOFTINT. @d @36dGet node priority. @36dTruncate to a multiple of 16. @36dSign extend to a word quantity. @36dPoint at middle of ExecBase->SoftInts @36dGet address of SoftInts entry correspon- ding to this priority (-32,-16,0,16,32). ; Now enqueue the node on the SoftIntList for this priority. @d @36dGet old TailPred pointer. @36dSet TailPred pointer to new node. @36dSet forward pointer of new node. @36dSet back pointer of new node. @d @36dSet forward pointer of old tail node. @36dSet the software interrupt pending flag. @36dGenerate a level 1 "software" interrupt. @36dEnable() @d @d @d ; Software Interrupt Handler ; -------------------------- ; This is the interrupt handler for logical interrupt #2, which ; is used for software interrupts. It will be called as soon after ; the above routine as its interrupt priority will allow. @36dClear the interrupt. @36dClear the software int. pending flag. @36dIf it wasn't set, ignore the interrupt. @d @36dDisable further software interrupts. @36dGo and scan the SoftInts table. ; This code is called (from below) when a non-empty SoftIntList ; has been found. The pointer of the list will be in A0. @a?fc139c FC139C move.w #$2700,SR Mask all maskable interrupts. @a=fc13a0 @36dGet the pointer to the first list node. @d @d @36dUnlink the node from the list. @d @d @d @36dMake the node type NT_INTERRUPT. @a?fc13b6 FC13B6 move.w #$2000,SR Enable all interrupts. @a=fc13ba @36dGet the interrupt code and data. @36dCall the node's interrupt handler. ; The following scans the SoftInts[] table for non-empty ; SoftIntLists. If it finds one, it executes the code above. @36dLoop 5 times (1 less for dbra). @36dStart at SoftInts[4] (priority 32). @36dClear the software interrupt. @36dSee if there are any nodes on this list. @36dIf so, process the list. @36dOtherwise, go to next lower priority, @36dand loop back. ; No (more) nodes found. @a?fc13de FC13DE move #$2100,SR @a=fc13e2 @36dReenable software interrupts and exit. @d --------------------------------------------------------------------------- Disable() --------------------------------------------------------------------------- ; Hardware disable all interrupts, and log the fact that this ; was done in the interrupt disable nesting level counter. @d @d @d --------------------------------------------------------------------------- Enable() --------------------------------------------------------------------------- ; Decrement the interrupt level nesting counter, and if it goes ; negative, enable interrupts again. @d @d @d @d @8p@28wPadding. --------------------------------------------------------------------------- AddLibrary( library ) A1 --------------------------------------------------------------------------- @36dPoint to system library list. @36dAdd the library to the list. @36dUpdate the library vector checksum. @d --------------------------------------------------------------------------- error = RemLibrary( library ) D0 A1 --------------------------------------------------------------------------- ; NOTE: Some other part of the system, presumably DOS, will patch ; itself in ahead of this. @d @36dForbid() @36dSave ExecBase. @d @36dCall the library's Expunge() function. @36dRestore ExecBase. @36dPermit() @d --------------------------------------------------------------------------- library = OldOpenLibrary( libName ) D0 A1 --------------------------------------------------------------------------- ; This is just OpenLibrary() without the "version" parameter. @36dAny version of the library will do. @36dOpenLibrary() @d --------------------------------------------------------------------------- library = OpenLibrary( libName, version ) D0 A1 D0 --------------------------------------------------------------------------- ; NOTE: Some other part of the system, presumably DOS, will patch ; itself in ahead of this, presumably so it can pull libraries off ; disk if they aren't already in the library list. ; Note how several versions of a library can be in the system ; library list (although the initial RomTag system of putting them ; there will only take the newest one), and this routine will keep ; looking for one new enough to satisfy the request if it finds ; one which is too old. @d @d @36dForbid() @36dPoint to the system library list. @36dFindName() @36dDid we find the library? @36dReturn zero and exit if not. @d @36dCheck the version number. @36dTry to find another one if too old. ; We have an acceptable library. @36dSave ExecBase. @36dPoint A6 at the library base address. @36dCall the library's Open() function. @36dRestore ExecBase. @36dPermit() @d @d --------------------------------------------------------------------------- CloseLibrary( library ) A1 --------------------------------------------------------------------------- ; NOTE: Some other part of the system, presumably DOS, will patch ; itself in ahead of this. @36dForbid() @36dSave ExecBase. @d @36dCall the library's Close() function. @36dRestore ExecBase. @36dPermit() @d --------------------------------------------------------------------------- oldFunc = SetFunction( library, funcOffset, funcEntry ) D0 A1 A0.W D0 --------------------------------------------------------------------------- @36dIndicate that library is modified. @36dCompute address of jump instruction. @36dGet the old jump destination address. @36dInstall a "JMP" instruction. @36dInstall the new destination address. @36dRe-checksum the library. @36dReturn the previous jump destination. @d --------------------------------------------------------------------------- SumLibrary( library ) A1 --------------------------------------------------------------------------- @36dCheck the "sum used" flag. @36dIf sum not used, then don't bother. @d @36dClear the "changed" flag. @d @36dIf the flag was set, clear the checksum. @d @36dGet the negative size. @36dDivide by 2 (gives size in words). @36dStart the sum at zero. @d ; Loop: Add up all the 16-bit words in the library's jump vector. @d @d @36dGet the old checksum. @d @36dOld checksum wasn't zero, so compare @36dit to the new checksum. ; Checksums didn't match, so put up an alert. @d @36dLibrary checksum alert number. @d @36dCall the alert function. @d @36dStore the new checksum in the library. @36dEnable multitasking. @d --------------------------------------------------------------------------- library = MakeLibrary( vectors, structure, init, dataSize, segList ) D0 A0 A1 A2 D0 D1 --------------------------------------------------------------------------- @d @36dSize of library data area. @36dPointer to library vector table. @36dPointer to initialization structure. @36dFunction to call after creating library. @36dSegList (for DOS use). @36dGet pointer to vector table. @36dSkip the following if no vectors. @d @d @36dCheck if vector data is absolute or @36drelative (first word = -1). @d @36dRelative vectors. Skip over the flag. @36dCount the vectors up to the terminating @36dflag. D3 holds -(number of vectors + 1) @d @36dAbsolute vectors. Count as above. @d @36dMake number of vectors positive. @36dMultiply by 6. @d @36dAdd size of data area. @36dMEMF_PUBLIC | MEMF_CLEAR @36dRequest memory for library node. @36dSee if the memory was allocated. @d @36dDidn't get any memory. Fail by returning @36da nil pointer to the caller. @36dPoint to start of allocated memory. @36dSkip over jump vector area. @36dInstall negative size in library node. @36dInstall positive size. @d @36dClear A2. @36dPoint at vector table again. @36dCheck if vectors are relative. @d @36dIf vectors are relative, skip over flag, @36dand set A2 to vector table address. @36dInstall the jump vector. @36dCheck if there is an initialization @36dstructure. Skip the following if not. @d @d @36dDo not clear memory before initializing. @36dProcess the initialization structure. @36dGet pointer to library node. @36dCheck if there is initialization code. @36dSkip the following if not. @36dGet the address of the code. @36dGet the address of the segList @36dCall the initialization code. @d @d --------------------------------------------------------------------------- size = MakeFunctions( vectors, offset ) D0 A1 A2 --------------------------------------------------------------------------- @d @36dClear size counter. @36dCheck if vector table is relative. @d ; Process a relative vector initialization table. @36dGet a relative table entry. @36dCheck if end of table marker. @d @36dCompute absolute vector address. @36dInstall in jump vector. @36dInstall "JMP" instruction. @36dIncrement size counter. @d ; Process an absolute vector initialization table. @36dGet an absolute table entry. @36dCheck if end of table marker. @d @36dInstall it in the jump table. @36dInstall "JMP" instruction. @36dIncrement size counter. @d @36dDone, return to caller. @d --------------------------------------------------------------------------- Insert( list, node, listNode ) A0 A1 A2 --------------------------------------------------------------------------- @32dCheck if node to insert after is zero. @32dIf so, do an AddHead instead. @32dSee if list node indicates end of list. @32dBranch if so. ; If we get this far, A2 must point to a list node. @d @32dSet forward and back pointers of new node. @32dSet back pointer of next node. @32dSet forward pointer of previous node. @d ; The "Successor" field of the listNode was zero, i.e. listNode ; is really the end of list marker. In this case, insert just ; before listNode, i.e. at the end of the list (undocumented). @32dSet forward pointer of new node to end. @32dGet old TailPred pointer @32dLink previous end node to new node. @32dSet the TailPred pointer to the new node. @32dLink this node to the previous end node. @d --------------------------------------------------------------------------- AddHead( list, node ) A0 A1 --------------------------------------------------------------------------- @32dGet old pointer to first node. @32dMake the first node the new node. @32dSet forward and back pointers in new node. @d @32dFix back pointer in previous first node. @d --------------------------------------------------------------------------- AddTail( list, node ) A0 A1 --------------------------------------------------------------------------- @32dPoint A0 to list's "Tail" field. @32dSave old value of TailPred field. @32dMake the new node the last node. @32dSet new node's back pointer to list header. @32dSet new node's back pointer. @d @32dLink new node to previous last node. @d --------------------------------------------------------------------------- Remove( node ) A1 --------------------------------------------------------------------------- @32dGet pointer to successor node. @32dGet pointer to predecessor node. @32dPoint predecessor to successor. @32dPoint successor to predecessor. @d --------------------------------------------------------------------------- node = RemHead( list ) D0 A0 --------------------------------------------------------------------------- @32dGet list's head pointer. @32dGet first node's successor. @32dIf zero, list is empty, so return zero. @32dPoint head pointer past removed node. @d @32dPoint new head node back at list header. @d --------------------------------------------------------------------------- node = RemTail( list ) D0 A0 --------------------------------------------------------------------------- @a?fc161e @32dGet list's TailPred pointer. @32dCheck if the last node has a predecessor. @32dIf not, the list is empty, so return zero. @32dPoint TailPred pointer at second-last node. @d @32dPoint second-last node at the list header. @32dIncrement to point at "Tail" field. @d --------------------------------------------------------------------------- Enqueue( list, node ) A0 A1 --------------------------------------------------------------------------- @32dGet the priority field of the node. @32dGet the head pointer of the queue. ; Loop: Keep skipping nodes until the end of the list is ; reached or the next node has a lower priority. @32dPoint at the current node. @32dCheck if there is a successor. @32dIf not, we are at the end of the list. @32dCompare priorities. @32dLoop until priority is lower than new node's. ; A0 now points at the node to insert before, or at the end ; of the list (i.e. the list header plus 4). @32dGet old value of back pointer (or TailPred pointer of list header). @32dChange it to point to the new node. @32dSet new node's forward pointer. @32dSet new node's back pointer to last node skipped, or list header. @d @32dSet list's head field, or forward pointer of previous node, to new node. @d --------------------------------------------------------------------------- node = FindName( start, name ) D0 A0 A1 --------------------------------------------------------------------------- @d @36dPoint to list header or first node. @36dSave address of match string in D1. @d @36dExit if list is empty. ; Main loop: Check if node pointed to by D0 has the same name ; as the string pointed to by D1. @36dGet the address of the node to check. @d @36dExit if this is the end of the list. @36dPoint A0 at name of this node. @36dPoint A1 at match string. ; Inner loop: Compare the strings. @36dCompare one character. @36dSkip to next node if not equal. @36dWas this the terminating zero? @36dContinue comparing if not. @36dGot a match - point D0 to the node. @36dRestore registers and exit. @d @d ; Protected enqueue and dequeue routines ; -------------------------------------- @36dForbid() @36dEnqueue() @36dPermit() @d @36dForbid() @36dRemove() @36dPermit() @d --------------------------------------------------------------------------- memoryBlock = Allocate( freeList, byteSize ) D0 A0 D0 --------------------------------------------------------------------------- @36dZero bytes requested? @36dIf so, just return. @d @36dRound D0 up to nearest multiple of 8. @d @d @36dCompare D0 to total number of bytes free. @36dIf greater, return zero. @36dPoint at the link to the first MemChunk. ; Loop: Scan the free list for a MemChunk big enough to satisfy ; the request. @36dGet the pointer to the next MemChunk. @36dIf no more MemChunks, return zero. @d @36dCheck D0 against size of this chunk. @36dIf smaller or equal, take it. @36dOtherwise, go to next MemChunk. @36dContinue searching. ; This is where memory fragmentation occurs. If the block we've ; found is bigger than what we need, we take what we need and make ; a smaller block out of the rest. @36dIf sizes are not the same, then @36d Compute address of leftover block. @36d Move the MemChunk pointer up. @36d Get the current size. @36d Subtract the allocated amount. @36d Put it into the new MemChunk. @36d Link to previous MemChunk (or header). @36dElse @36d Unlink this MemChunk. Endif @36dDecrease free memory value in header. @36dGet address of allocated block. @36dReturn zero or address of block. @d @d ; Routine to put up a "Corrupt Free List" dead-end alert. @a?fc16ee @d @36dAlert number. @36dGet ExecBase. @36dPut up the alert. @36dNever reached. --------------------------------------------------------------------------- Deallocate( freeList, memoryBlock, byteSize ) A0 A1 D0 --------------------------------------------------------------------------- @36dSee if byte size is zero. @36dDo nothing if so. @d @d @36dTruncate block address to nearest @36dmultiple of 8. @36dFind out by how much that decreased @36dthe address, then add this amount to @36dthe number of bytes to free. @36dNow round number of bytes to free up to @36dnearest multiple of 8. @36dExit if the result is zero bytes. ; Block address and size are now nice multiples of 8, so we can ; go ahead and put the block into the free list. @36dPoint at the link to the first MemChunk. @36dGet the address of the first MemChunk. @36dBranch if there are no MemChunks. ; Main loop: Find the first MemChunk with an address greater than ; that of our memory block. @36dCompare addresses. @36dExit loop if right MemChunk found. @36dIf addresses are equal, guru time! @d @36dGet address of next MemChunk. @36dContinue searching. ; A2 now points at the link which comes directly before the position ; where our block must be inserted. First, we check whether this ; is the start of the list. If so, we just link it in. @36dCompute address of MemHeader's link. @d @36dSee if A2 still points there. @36dIf so, insert block at head of the list. ; We are somewhere other than at the start of the list. It is ; possible that we can join the block to the previous MemChunk. ; Try this now. @36dCompute first address after the @36dprevious MemChunk. @36dCompare to address of our new block. @36dIf equal, join the two together. @36dIf greater, free list is corrupt. ; Enter here to make a new MemChunk and link it into the free list. @36dSet new MemChunk's link field. @36dPoint previous link at the MemChunk. @36dPut the block size into the MemChunk. @d ; Enter here to attach the new block to the previous MemChunk. @36dAdd to previous MemChunk's size. @36dBack pointer up to that MemChunk. ; Now the block is taken care of. Either we have built a MemChunk ; for it, or we have attached it to a MemChunk directly before the ; block. Either way, the address of the MemChunk where the block ; now is is in A1. It remains to check whether we can join this ; MemChunk with the next one down the line. @36dSee if there is a next MemChunk. @36dExit if not. @36dGet our MemChunk's size. @36dAdd to its base address. @36dCompare to address of next MemChunk. @36dIf higher, free list is corrupt. @36dIf not equal, we can't join them. ; If we get this far, we can join this MemChunk to the next one down ; the line, so we delete the next one and add its size to this one. @36dGet pointer to next MemChunk. @36dFetch its link and put in our MemChunk. @36dGet its size. @36dAdd to size of our MemChunk. ; This is where all the paths meet again. @36dAdd size of freed block to free counter. @d @d ; Error exit routine. Generate a dead-end alert. @d @36dAlert number. @36dGet ExecBase. @36dGo put up the alert. @36dNever reached. --------------------------------------------------------------------------- memoryBlock = AllocMem( byteSize, requirements ) D0 D0 D1 --------------------------------------------------------------------------- ; NOTE: Some other part of the system, presumably DOS, will patch ; itself in ahead of this, presumably so it can throw out unused ; libraries, devices, fonts, etc. when memory runs low. @36dForbid() @d @d @d @36dPoint to the system free memory list. ; Loop: Step through the system free memory list looking for ; a MemHeader which can satisfy the memory request. @36dGet pointer to a MemHeader. @36dEnd of memory list reached? @d @36dIf so, return a null pointer and exit. @d @36dGet the MemHeader's attributes. @36dCheck against needed attributes. @d @36dGo to next MemHeader if not present. ; The current MemHeader has the right attribute flags, now try ; to get a block of the required size out of its free list. @36dPoint to the MemHeader. @36dIndicate size of block required. @36dAllocate() @36dDid we get the memory? @36dIf not, go to next MemHeader. ; We got a memory block. Clear it if necessary. @36dWas the MEMF_CLEAR flag set? @36dJust return if not. @d @36dConvert number of bytes to nearest number @36dof longwords. @36dPoint to the memory block. @36dEnter the loop at the bottom. ; Loop: Clear the memory block. @36dClear one longword. @36dLoop back until none remain. @36dDBRA instruction only uses low 16 bits @36das program counter, so if any remain @36dset in the upper half of D3, decrement @36dthem manually and loop back. @d @d @36dPermit() @d @d --------------------------------------------------------------------------- FreeMem( memoryBlock, byteSize ) A1 D0 --------------------------------------------------------------------------- @36dForbid() @36dIs number of bytes to free zero? @36dIf so, just exit. @36dPoint to system free memory list. ; Loop: Look for the right MemHeader to put the block back into. @36dPoint to the next MemHeader. @36dEnd of list reached? ; If no MemHeader can be found to put this block on, something ; is wrong, so it's guru time. @36dGuru time if end of list reached. ; See if the block falls into the address range covered by this ; MemHeader. @36dDoes block start before mh_Lower? @36dIf so, try next MemHeader. @36dDoes block start after mh_Upper? @36dIf so, try next MemHeader. ; The block belongs to this MemHeader, so go link it back into ; the free list. @36dDeallocate() @36dPermit() @d --------------------------------------------------------------------------- attributes = TypeOfMem( address ) D0 A1 --------------------------------------------------------------------------- @36dForbid() @36dPoint to system free memory list. @d @36dPoint to next MemHeader. @36dEnd of list reached? @36dIf so, return zero and exit. @36dCheck if the block is in the address @36drange covered by this MemHeader. @d @36dIf it is, return the contents of the @36dMemHeader's attribute field. @36dPermit() @d --------------------------------------------------------------------------- memoryBlock = AllocAbs( byteSize, location ) D0 D0 A1 --------------------------------------------------------------------------- @36dForbid() @d @36dGet the start address. @36dTruncate down to nearest multiple of 8. @d @36dAdd any difference to number of bytes @36dwanted, then round number of bytes @36dup to nearest multiple of 8. @36dPoint to the system free memory list. ; Loop: Scan through all the MemHeaders in the free list. @36dPoint to next MemHeader. @36dEnd of list reached? @36dIf so, allocation failed, return zero. ; Check whether the block we want is covered by this MemHeader. @36dIf block starts before this MemHeader's @36darea, go on to the next one. @36dIf block starts after this MemHeader's @36darea, go on to next one. ; The desired block starts in the address range covered by this ; MemHeader, so it's either this one or nothing. ; Quick check: If the total number of bytes free in this MemHeader's ; free list is less than the size of the block wanted, then the ; allocation is going to fail anyway. @37dCheck block size against mh_Free. @37dAllocation failed if higher. @37dGet start address of block wanted. @d @37dCompute end address of block wanted. @37dPoint to link to first MemChunk. ; Loop: Find the MemChunk in the free list which contains the ; wanted block. @37dGet pointer to next MemChunk. @37dAllocation failed if no more MemChunks. @d @37dGet the MemChunk's size. @37dCompute its end address. @37dCheck against needed end address. @37dExit loop if equal or higher. @d @37dGo to the next MemChunk. ; We have a MemChunk whose end address is greater than or equal ; to the end address of the block we want. It's either this ; MemChunk or none at all. @37dCheck start addresses. @37dAllocation failed if needed block starts before this MemChunk. ; The block we want is entirely within this MemChunk, so now we ; cut it out and make zero, one or two MemChunks out of what's left. @37dRemove block size from free count. @37dCompare end addresses. @d ; The end addresses of the allocated block and the MemChunk it ; was in were exactly equal. @d @d ; Memory was left over between the allocated block and the end of ; the MemChunk. Make a new MemChunk out of this and link it into ; the free list. @37dCompute base address of new MemChunk. @37dStore pointer to next MemChunk. @37dLink to previous MemChunk. @37dStore number of bytes in MemChunk. ; At this point, whatever was left after the block was taken ; care of, and a pointer to the next MemChunk after the block is ; in A0. @37dCompare start addresses. @d ; The block started after the beginning of this MemChunk, so we ; just truncate the MemChunk to contain the leftover memory before ; the allocated block. @d @37dCompute remaining size. @37dStore in the MemChunk. @37dAll done. ; The block started exactly at the beginning of the MemChunk, so ; the MemChunk must be deleted. @37dUnlink this MemChunk from the chain. @37dReturn address of allocated block. @d @37dPermit() @d @37dReturn zero if allocation failed. @d --------------------------------------------------------------------------- size = AvailMem( requirements ) D0 D1 --------------------------------------------------------------------------- @d @36dInitialize free memory counter. @36dPoint at the ExecBase->MemList. @36dForbid() @d @36dGet address of first MemHeader. @36dExit if list is empty. @36dGet this MemHeader's attributes. @36dMask with required attributes. @36dCheck if all attributes are satisfied. @36dGo to next MemHeader if not. @36dAre we looking for the largest contiguous block of memory? @d @36dIf not, just add the size, @36dand loop back. ; We are looking for the largest contiguous block of memory. @36dGet address of first MemChunk. @36dLoop back if there is no MemChunk. @d @36dSee if this MemChunk is the biggest yet. @d @36dIf it is, keep its base address (why?), @36dand its size. @d @36dGo to next MemChunk. @36dPermit() @36dReturn the accumulated or largest size. @d @d --------------------------------------------------------------------------- memList = AllocEntry( memList ) D0 A0 --------------------------------------------------------------------------- @d @36dSave pointer to request MemList. @36dClear loop counter. @d @36dGet the number of MemEntries. @d @36dCompute the size of the MemList needed @36dto keep track of the MemEntries. @36dSet MEMF_CLEAR. @36dAllocate that amount of memory. @36dIf we didn't get it, go exit. @d @36dPoint to base of new MemList structure. @36dPut in the number of MemEntries. @36dPoint to MemEntries in request MemList. @36dPoint to MemEntries in result MemList. For each MemEntry do @36d Get its required attributes. @36d Get its required size. @36d Store the size in the result list. @36d If size is not zero then @36d Allocate the memory. @36d Go exit if unsuccessful. @36d Endif @36d Store memory address in result list. @36d Go to next request MemEntry. @36d Go to next result MemEntry. @36d Increment loop counter. @36d Compare to number of MemEntries. @36dEndfor. @d @36dCompute -(size of MemEntry list). @36dPoint A2 back at the request MemList. @36dPoint A3 back at the result MemList. @36dReturn the address of the result MemList. @d @d ; Enter here if one of the MemEntries could not be allocated. @36dSave size of the allocate which failed. @36dTest number of allocated MemEntries. @36dExit if none remaining. @36dDecrement number of MemEntries. @36dPoint to the allocated MemEntry. @36dGet the address and size of the block @36dcorresponding to it. @36dDeallocate the block. @36dGo back and check for more MemEntries. ; Enter here if the AllocEntry failed, after deallocating anything ; which was allocated before the failure. @36dGet size of the allocate which failed. @d @36dSet the high bit. @36dReturn it to the user. --------------------------------------------------------------------------- FreeEntry( memList ) A0 --------------------------------------------------------------------------- @d @36dPoint to the MemList. @36dPoint to the first MemEntry. @36dGet number of MemEntries. @36dFor each MemEntry in the MemList do @36d Get the address of the memory block. @36d Get the sizde of the memory block. @36d If size is not zero then @36d Deallocate the block. @36d Endif @36dEndfor @d @36dGet the number of MemEntries again. @d @36dCompute the size of the MemList. @36dPoint to the base of the MemList. @36dDeallocate it. @d @d --------------------------------------------------------------------------- error = AddMemList( size, attributes, pri, base, name ) D0 D0 D1 D2 A0 A1 --------------------------------------------------------------------------- ; This routine takes a block of memory, builds a MemHeader at the ; start of it, and puts the rest of the block into the MemHeader's ; free list. Then it adds the MemHeader to the system free memory ; list. @36dStore the name in the MemHeader. @36dCompute first address after MemHeader. @36dMemHeader's node type is NT_MEMORY. @36dSet the MemHeader's priority. @36dSet the memory attributes. @d @36dRound the base address of the block up @36dto the nearest multiple of 8. @d @36dCompute by how much it changed, @36dand add this amount to the length. @36dTruncate the length to a multiple of 8. @36dSubtract the size of the MemHeader. @36dStore base address as lower bound of @36dmemory and link to first MemChunk. @d @36dCompute upper bound address. @36dStore it in the MemHeader. @36dStore the amount of free memory. @36dBuild a MemChunk at the start of free @36dmemory (clear link, store size). @36dGet base address of MemHeader @36dPoint to the system free memory list. @36dPut the MemHeader on the list. @d @8p@28wPadding. ; Data table used to build the exec jump vector table. ; There are 105 entries, corresponding to the 105 exec functions, ; followed by a -1 table end marker. Each entry is a 16-bit ; relative offset from the start of the table. @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@9l@9l@9l@l @7p@w @7p@29wEnd of table marker. @7p@29wLooks like garbage. @7p@w --------------------------------------------------------------------------- AddPort( port ) A1 --------------------------------------------------------------------------- @36dAddress of port's message list. @36dInitialize the list's head pointer. @d @36dInitialize the list's Tail field. @36dInitialize the list's TailPred pointer. @36dAddress of the public message port list. @36dSafely add port to the list. --------------------------------------------------------------------------- RemPort( port ) A1 --------------------------------------------------------------------------- @a?fc1b30 ; Call a general purpose subroutine which unlinks something from ; its queue while disabling multitasking. @d --------------------------------------------------------------------------- PutMsg( port, message ) A0 A1 --------------------------------------------------------------------------- @36dMake sure message node type is NT_MESSAGE @d @36dCompute address of port's message list @36dDisable() @d ; Enqueue the message at the end of the port's message list @36dPoint A0 at port's Tail field. @36dSave pointer to last node in list @36dMake new node the last node @36dNew node has no next node @36dLink new node back to old last node @d @36dLink old last node to new node ; Do whatever needs to be done on message arrival. @d @36dGet address of message port's task. @36dExit if port has no task. @36dGet port's flag bits. @d @36dDecode the flag bits. @d @d ; Processing for PA_SOFTINT @d @36dCause the software interrupt. @d ; If PA_IGNORE, do nothing. @d @d ; (Undocumented?) processing for mp_Flags = 3. Apparently, ; calls a subroutine at mp_SigTask. @d @d @d ; Processing for PA_SIGNAL @36dGet SigBit. @d @36dCompute corresponding signal mask. @d @d @36dSignal the task. @36dEnable() @d @d @d --------------------------------------------------------------------------- message = GetMsg( port ) D0 A0 --------------------------------------------------------------------------- @36dPoint A0 at head of port's message list. @36dDisable() @d ; Try to dequeue a message from the message list. @36dGet head pointer into A1. @d @36dIf list is empty, just return zero. @d @36dUnlink the message from the list. @d ; Reenable interrupts if necessary and exit. @36dEnable() @d @d @d --------------------------------------------------------------------------- ReplyMsg( message ) A1 --------------------------------------------------------------------------- @36dGet address of reply port. @d @36dNo reply port. Set node type to @36dNT_FREEMSG and exit. @36dSet node type to NT_REPLYMSG. @d @36dSend the message via the reply port. --------------------------------------------------------------------------- message = WaitPort( port ) D0 A0 --------------------------------------------------------------------------- @36dGet head pointer of port's message list. @36dCheck if list is empty. @36dIf not empty, return right away. @36dList was empty. Get signal bit. @36dPoint A0 at message list. @d @36dCompute signal mask. @d @d @36dWait() @d @36dCheck message list. @36dIf still empty, go back and wait again. @d @36dReturn first message in the list. @d --------------------------------------------------------------------------- port = FindPort( name ) D0 A1 --------------------------------------------------------------------------- @36dPoint to the public port list. @36dSearch it for a port of the given name. @d --------------------------------------------------------------------------- AddResource( resource ) A1 --------------------------------------------------------------------------- @36dPoint to the resource list. @36dAdd the resource to the list. --------------------------------------------------------------------------- RemResource( resource ) A1 --------------------------------------------------------------------------- @36dUnlink the resource from the list. --------------------------------------------------------------------------- resource = OpenResource( resName ) D0 A1 --------------------------------------------------------------------------- @36dPoint to the resource list. @36dForbid() @36dFindName() @36dPermit() @d @8p@28wPadding. --------------------------------------------------------------------------- AddTask( task, initialPC, finalPC ) A1 A2 A3 --------------------------------------------------------------------------- @d @36dMake the task state TS_ADDED. @36dClear the task's flags. @36dSet IDNestCnt and TDNestCnt to -1. @d @36dTask is not waiting for any signals. @36dTask has not received any signals. @36dTask does not have any exception signals. @36dSet allocated traps. @36dNo traps are enabled. @36dInstall default system exception handler @36dif the user has not provided one. @d @d @d @d @36dGet the initial stack pointer. @36dPut the exit code address on the stack. @d @36dUse system default if no exit code. @36dClear 15 longwords below the process's @36dinitial stack pointer, so all registers @36dwill be zero when initially loaded. @36dClear the initial status register. @36dPut the initial PC on the stack. @36dIf we have a 68881, then put another @36dlongword on the stack. @d @d @36dPut the initial stack pointer back. @d @36dDisable() @d @36dMake the task state TS_READY. @36dPut the task on the TaskReady queue. @d @36dEnable() @d @d @36dIf the task ended up at the front of the @36dTaskReady queue, then @36dReschedule() @d ; System default task exit function ; --------------------------------- @36dGet ExecBase. @d @36dAsk for current task to be removed. ; fall through into RemTask. --------------------------------------------------------------------------- RemTask( task ) A1 --------------------------------------------------------------------------- @d @36dCheck address of task to be removed. @36dBranch if not zero. @36dIf zero, use the current task. @d @36dIf not zero, check if it was the current @36dtask anyway. ; The following is executed only if the task to be removed is not ; the current one. This is because the currently running task isn't ; on a queue anywhere. @36dDisable() @d @36dUnlink the task from its queue. @d @36dEnable() @d @36dGet the address of the task back. @36dMake the task TS_REMOVED. @36dCheck if it is the current task. @d @36dIf so, disable task switching now. @36dPoint to allocated memory list. @d @36dExit if the head pointer is zero, @36dor if the tail pointer points back at @36dthe list header. @36dZero the list head pointer. ; Loop: Release all the memory blocks which the task had allocated. @36dGet pointer to current list node. @36dGet pointer to next list node. @36dExit if the end of the list was reached. @36dFreeEntry() this list node. @36dBack to the head of the loop. @36dCheck if the current task was removed. @36dIf not, indicate success and exit. @d @36dGo to supervisor mode. @36dPop exception data from the stack. @36dDispatch() a new task. @36dIf the current task was not removed, @36dindicate success and return. @d --------------------------------------------------------------------------- task = FindTask( name ) D0 A1 --------------------------------------------------------------------------- @36dSee if a name was given. @d @36dIf not, use this task. @d @36dPoint at the TaskReady queue. @36dDisable() @d @36dCall FindName() to look for the task. @36dCheck if found, return address if so. @d @36dTask not found, try TaskWait queue. @36dCall FindName() to look for the task. @36dCheck if found, return address if so. @d @36dStill not found, compare name with that @36dof the current task. @d @36dIf names differ, return zero (not found). @d @d @36dTask was the current task. @36dEnable() @d @d @d --------------------------------------------------------------------------- oldPriority = SetTaskPri( task, priority ) D0 A1 D0 --------------------------------------------------------------------------- @36dDisable() @d @36dSave the task's current priority. @36dSet the task's new priority. @36dCheck if it was the current task. @36dIf so, go reschedule it. @36dCheck if the task was TS_READY. @36dIf not, we're done. @d @36dTake the task out of the TaskReady queue. @d @36dAnd put it back in the right place @36dcorresponding to its new priority. @36dSee if it ended up at the front. @36dIf not, we're done. ; If we get here, either we've changed the priority of the current ; task, or we've moved another task to the front of the TaskReady ; queue. Either way, we have to check if the current task should ; be preempted. @36dReschedule() @d @36dEnable() @d @d @36dReturn the previous priority. @d --------------------------------------------------------------------------- oldSignals = SetExcept( newSignals, signalMask ) D0 D0 D1 --------------------------------------------------------------------------- @36dGet pointer to current task. @36dPoint to its SigExcept longword. @36dDrop into SetSignal. --------------------------------------------------------------------------- oldSignals = SetSignal( newSignals, signalMask ) D0 D0 D1 --------------------------------------------------------------------------- @36dGet pointer to current task. @36dPoint to its SigRecvd longword. ; This code is common to SetExcept and SetSignal. @36dMask out signals to be left alone. @36dDisable() @d @36dSave current value of signal word. @36dMake mask of singnals to leave alone. @36dGet those signals only. @36dOr in the changed signals. @36dPut the result back in the task. ; Now we drop into Signal(), which will process any new signals ; we may have set in the SigRecvd longword. @36dGet the SigRecvd data. @36dDrop into Signal. --------------------------------------------------------------------------- Signal( task, signals ) A1 D0 --------------------------------------------------------------------------- @36dGet the set of received signals. @36dDisable() @d @36dStore the signals. @36dOr the new signals into the old ones. ; The code from here down is common to SetExcept, SetSignal, and ; Signal. It checks if the task whose signals are being modified ; should be awakened. @36dCheck the exception signals, @36dand see if any of them have become set. @36dIf so, go process them. @36dSee if the task is in the TS_WAIT state. @36dIf not, we're done. @36dSee if a signal being waited for has been @36dset. If not, we are done. ; We have set a signal which the task was currently waiting for, ; so we must awaken it. First, we take it out of the TaskWait ; list then we make it TS_READY, then we put it on the TaskReady ; queue and check if it should preempt the current task. @d @36dSave pointer to the task. @36dGet pointer to the next task in the list. @36dGet pointer to the previous task. @36dUnlink from the previous task. @36dUnlink from the next task. @36dRestore pointer to the task. @36dMake the task TS_READY. @36dPoint to the TaskReady queue. @36dEnqueue the task in the right place. @36dCheck if it was put at the head of the @36dTaskReady queue, and exit if not. @d @36dEnable() @d @36dReturn old signals to caller eventually. @36dReschedule() so the task can run. ; Enter here if we have set an exception signal, or if a previously ; set signal has become an exception signal. @36dSet the task's TB_EXCEPT flag. @36dSee if it is in the TS_WAIT state. ; If the task for which the exception occurred was in the TS_WAIT ; state, make it TS_READY and move it from the TaskWait list to ; the TaskReady queue. Otherwise, just dispatch it. @d @d ; Exit here if the current task was allowed to keep running (i.e. ; the other task did not preempt it). @36dEnable() @d @d @36dReturn old signals. @d --------------------------------------------------------------------------- signals = Wait( signalSet ) D0 D0 --------------------------------------------------------------------------- @36dGet the pointer to the current task. @36dRecord which signals we are waiting for. @36dDisable() @d @36dEnter the loop at the bottom. ; Main loop: We stay here until we have a signal we want. @36dMake the task state TS_WAIT. @36dPoint to the TaskWait list. @d @36dUnlink the task from the TaskReady @36dqueue, and put it in the TaskWait @36dlist. @d @d @d ; Here we block. Calling Switch() gives control to any other ; process. Eventually, someone else doing a Signal() will put us ; back on the TaskReady list, and then we will return from the ; Switch() when our priority comes up again. @36dSave A5. @36dGet ready to call Switch(), but first... @36dEnter Supervisor mode. ; We're back. See if we now have a signal we want. @36dRestore A5. @36dGet pointer to current process. @36dGet the signals we are waiting for. ; We initially enter here to see if we have to block at all, and ; again each time we have been unblocked to see if we can now ; continue running. @36dGet the signals which are currently set. @36dCheck against signals to wait for. @36dIf not, go back and block again. ; Eventually, we end up here. We now have one or more signals ; that were being waited for. We take these out of the set of ; received signals, since we will return them to the caller. @36dUpdate the set of received signals. @d @36dEnable() @d @36dReturn the signals. @d --------------------------------------------------------------------------- Reschedule() --------------------------------------------------------------------------- ; This function is used when it is possible that a task should ; be preempted and another task run in its place. It sets the ; scheduling attention flag to force the scheduler to do its thing ; as soon as possible. If multitasking is disabled, that's all ; it does, since Permit() will do the rest when multitasking is ; turned on again. If interrupts are disabled, it sets the software ; generated interrupt. Since this is not accompanied by setting ; the software interrupt pending flag, it will do nothing but ; run ExitIntr() as soon as the interrupt level allows it. ; ExitIntr() will then cause the outstanding scheduling operation ; to be done. ; Note that part of Permit() is used to switch into supervisor ; mode and call the scheduler. @36dSet the scheduling attention flag. @36dSave its previous state. @36dCheck if multitasking enabled. @36dIf not, exit. @36dCheck if interrupts enabled. @36dIf so, go and do the scheduling. @36dCheck the (old) scheduling attn flag. @36dExit if it was already set. @36dAssert the software generated interrupt. @d --------------------------------------------------------------------------- Forbid() --------------------------------------------------------------------------- @36dIncrement the TDNestCnt. @d --------------------------------------------------------------------------- Permit() --------------------------------------------------------------------------- @36dDecrement the TDNestCnt. @36dExit if still positive. @36dCheck if interrupts disabled. @36dExit if yes. ; The current task has just reenabled multitasking. If the system ; is waiting to switch tasks, now is the time to do it. @36dCheck the scheduling attention flag. @36dExit if not pending. @36dSave A5. @36dSet address to go to in supervisor mode. @36dEnter supervisor mode. @36dRestore A5 and return. @d ; This is executed in supervisor mode. @36dCheck caller's supervisor mode flag. @36dIf the caller was in supervisor mode, @36dthen don't switch tasks. ; Eventually, this task will be dispatched again, using the program ; counter and status register left on the stack by the supervisor ; mode call. Then it continues running above, pops A5 from the ; stack, and returns to the caller. @36dGo and do the scheduling. --------------------------------------------------------------------------- trapNum = AllocTrap( trapNum ) D0 D0 --------------------------------------------------------------------------- @36dGet pointer to the current task. @36dGet the task's TrapAlloc flags. @36dSee if we want a particular trap number. @d @36dIf so, set this trap number's flag bit. @36dIf it wasn't set, indicate success. @36dIt was already set, indicate failure. ; Look for a free trap number. @36d16 traps to check. Start at #15. @36dTry to set this trap's flag bit. @36dIf not set before, indicate success. @36dLoop until all flags checked. @36dIndicate failure. @36dPut updated trap flags back. @d --------------------------------------------------------------------------- FreeTrap( trapNum ) D0 --------------------------------------------------------------------------- @36dGet pointer to the current task. @36dGet the task's TrapAlloc flags. @36dClear the specified flag. @36dPut the flags back. @d --------------------------------------------------------------------------- signalNum = AllocSignal( signalNum ) D0 D0 --------------------------------------------------------------------------- @36dGet pointer to the current task. @36dGet the task's SigAlloc flags. @36dSee if we want a particular signal. @d @36dIf so, try to set its flag. @36dIndicate success if it was clear. @36dIndicate failure if already set. ; Look for a free signal. @36d32 Signals to check. Start at 31. @36dTry to set the signal's flag. @36dIf it wasn't already set, success. @36dOtherwise, keep looking. @36dIndicate failure. @d @36dSuccess. Update allocated signal flags. @d @36dSet all but this signal bit in D1. @36dMake sure this signal isn't set. @36dMake sure it's not an exception signal. @36dIndicate that we are not waiting for it. @d --------------------------------------------------------------------------- FreeSignal( signalNum ) D0 --------------------------------------------------------------------------- @36dGet pointer to the current task. @36dGet the SigAlloc flags. @36dClear the indicated signal bit. @36dPut the flags back. @d ; Subroutines for RawDoFmt() ; -------------------------- ; Find the length of a null-terminated string pointed to by A0. ; For efficiency, the length is counted up as a negative number. @a?fc200c @40dStart the length at -1. @40dGet string character, test for end. @40dCount, and loop until end of string. @d @40dConvert length to correct +ve value. @d ; Evaluate a decimal numeric constant pointed to by A4. @40dStart the result at 0. @d @40dGet a character from the input. @a?fc2020 FC2020 cmp.b #'0',D2 @a=fc2024 @d FC2026 cmp.b #'9',D2 If not a numeric digit, exit. @a=fc202a @d @40dMultiply previous result by 10. @d @d @d @d @a?fc2036 FC2036 sub.b #'0',D2 Convert digit to number 0 - 9. @a=fc203a @40dAdd to result. @40dGo evaluate next character. ; Finish up by pointing back at the non-digit character. @40dBackspace the input pointer. @d ; Convert the number in D4 to its decimal ASCII representation. @40dIs the number zero? @40dIf so, just output "0" and return. @40dIs it negative? @40dIf not, make it negative. @d @a?fc204c FC204C move.b #'-',(A5)+ If so, output a "-". @a=fc2050 ; The minus sign is taken care of, and negative the number to ; be output is in D4. @40dPoint at table of divisors. @40dClear the "non-zero digit" flag. @40dGet a divisor from the table. @40dIf zero, output last digit and exit. ; Subtract the current divisor from the number as many times as ; possible (actually, add to its negative). @40dStart the counter at -1. @40dTry to add the divisor to D4. @40dLoop until D4 is greater than zero. @40dMake the number less than zero again. @40dIncrement the counter by 1. ; D0 now contains negative the current digit to output. Discard ; it if it is a leading zero. @40dIf the current digit is zero then @40dif no non zero digits have been @40doutput, then discard it. ; Output the digit. @40dSet "non-zero digit" flag. @40dMake D0 positive. @a?fc2072 FC2076 add.b #'0',D0 Convert to an ASCII digit. @a=fc2076 @40dPut it into the buffer. @40dGo do the next digit. ; Enter here to output the last digit, or the single "0" if the ; number is zero. @40dMake D0 positive. @a?fc207c FC207C add.b #'0',D4 Convert to an ASCII digit. @a=fc2080 @40dPut it into the buffer. @d ; Table of divisors. @8p@32l1000000000 decimal. @8p@33l100000000 decimal. @8p@34l10000000 decimal. @8p@35l1000000 decimal. @8p@36l100000 decimal. @8p@37l10000 decimal. @8p@38l1000 decimal. @8p@39l100 decimal. @8p@40l10 decimal. @8p@41l0 decimal. ; Convert the number in D4 to its hexadecimal ASCII representation. @a?fc20ac @40dIs the number zero? @40dIf so, just output "0" and exit. @40dClear "non-zero digit" flag. @40dIs the data 32 bits long? @d @40dIf not, output 4 digits, @40dfrom the high word of D4. @d @40dIf so, output 8 digits. ; Digit output loop. @40dRotate leftmost digit into bits 0-3. @d @40dMask out all but bits 0-3. ; Skip leading zeros. @40dIf no non-zero digit has been @40dencountered yet, and this is a zero, @40dskip it. @40dSet "non-zero digit" flag. @40dIs the digit greater than 9? @d @a?fc20d6 FC20D6 add.b #'0',D0 If not, convert to ASCII numeral. @a=fc20DA @d FC20DC add.b #$37,D0 If so, convert to upper case letter. @a=fc20e0 @40dPut digit in buffer. @40dLoop until all digits done. @d --------------------------------------------------------------------------- RawDoFmt( FormatString, DataStream, PutChProc, PutChData ) A0 A1 A2 A3 --------------------------------------------------------------------------- @d @40dReserve stack space for buffer. @40dStore data stream pointer. @40dPoint to the format string. @40dGet a byte from the format string. @40dCheck for end and exit if found. @a?fc20f8 FC20F8 cmp.b #'%',D0 Check for format specifier. @a=fc20fc @40dProcess it if found. @40dOtherwise, output the character, @40dand go on to the next one. @40dOutput the terminating zero. @40dDeallocate the output buffer. @d @d ; Enter here if the "%" format specifier was found. Next we check ; for the characters "-", signaling that the printed item should ; be left-aligned in its field, and "0", signaling that leading ; zeros should be attached. @40dPoint to the output buffer. @40dClear the option flags. @a?fc2112 FC2112 cmp.b #'-',(A4) Left alignment desired? @a=fc2116 @d @40dIf so, set the corresponding flag, @40dand go on to the next character. FC211E cmp.b #'0',(A4) Zero fill desired? @a=fc2122 @d @40dIf so, set the corresponding flag. @40dGet the field width. @40dStore it in D6. @40dAssume no maximum length. @a?fc2130 FC2130 cmp.b #'.',(A4) Maximum length specifier? @a=fc2134 @d @40dIf so, go on to the next character, @40dand get the maximum length. @40dStore maximum length. FC213E cmp.b #'l',(A4) 32 bit data? @a=fc2142 @d @40dIf so, set the corresponding flag, @40dand go on to the next character. ; At this point, we have interpreted a format string of the format ; "%-0xxx.yyyl". In D3, bit 0 is set if the "-" was present, bit 1 ; is set if the "0" was present, and bit 2 is set if the "l" ; was present. D6 contains the value of "xxx" (numeric constant), ; and D5 contains the value of "yyy" if present, or 0 if not. ; Now we process the actual format characters: "s" for string, "c" ; for character, "d" for decimal, "x" for hex. In all cases, the ; goal is to build a null-terminated string, pointed to by A0, ; containing the formatted output. @40dGet the next character. @a?fc214c FC214C cmp.b #'d',D0 Decimal output? @a=fc2150 @40dSkip to next option if not. @40dGet data item. @40dFormat as decimal number. @40dOutput the buffer. FC215A cmp.b #'x',D0 Hexadecimal output? @a=fc215e @40dSkip to next option if not. @40dGet data item. @40dFormat as hexadecimal number. @40dOutput the buffer. ; Subroutine to get a number from the data stream. We get either ; a word or a longword, depending on whether an "l" was in the ; format specification. @40dIf data size is 16 bits then @d @40d Get data stream pointer. @40d Get a word from the data stream. @40d Put data stream pointer back. @40d Extend to longword. @40dElse @40d Get data stream pointer. @40d Get a longword from data stream. @40d Put data stream pointer back. @40dEndif ; Continue here if not formatting a number. @a?fc2188 FC2188 cmp.b #'s',D0 String output? @a=fc218c @40dIf not, skip to next. @40dGet data stream pointer. @40dPoint directly at the string. @40dPut data stream pointer back. @40dOutput the string. FC2196 cmp.b #'c',D0 Single character output? @a=fc219a @40dIf not, discard format specifier. @40dGet character from input stream. @40dPut it into the buffer. ; Numeric and character output, having put their formatted argument ; into the buffer, meet here. @40dZero-terminate the buffer. @40dPoint back to its start. ; All output options continue here. A5 now points to a ; null-terminated string to output. @d @40dFind the length of the string. @40dWas a maximum length specified? @d @40dIf so, and if the output string is @40dlonger, set the length to the @40dmaximum length. @40dCompute amount of padding needed. @d @40dSet to zero if negative. @40dWas left alignment desired? @d @40dIf not, output padding first. @d @40dLoop to copy the string to the output @40duntil its end or the given maximum @40dnumber of characters. @40dWas left alignment desired? @d @40dIf so, output padding now. @40dContinue with format string. ; Subroutine to output the right amount of padding. @a?fc21de FC21DE move.b #' ',D2 Assume padding with spaces. @a=fc21e2 @40dWas zero-fill desired? @d FC21E8 move.b #'0',D2 If so, pad with zeros. @a=fc21ec @d @40dLoop to output the required number @40dof padding characters. @d @d --------------------------------------------------------------------------- RawIOInit() --------------------------------------------------------------------------- ; Set the serial port for receiving 8 bit data at 9600 bps. @40dSet up the SERPER register. @d --------------------------------------------------------------------------- RawMayGetChar() --------------------------------------------------------------------------- @d @40dRead SERDATR. @40dIs a byte in the receive buffer? @40dIf not, return -1 and exit. @40dClear the serial receive interrupt. @40dGet the received character. @40dReturn it to the caller. @d ; Subroutine to wait for a character on the serial port. @40dRawMayGetChar() @40dDid we get a character? @40dContinue waiting if not. @40dReturn the character. ; This looks like a C entry point for RawPutChar. @40dGet the first C parameter. --------------------------------------------------------------------------- RawPutChar() --------------------------------------------------------------------------- ; Don't output code 0, and expand newlines to CRLF. @40dIs the character to send zero? @40dIf so, don't send it. @40dSave the character. @40dIs it a newline? @d @40dIf so, send a carriage return first. @d @40dGet the character back. ; Enter here to send the character in D0 on the serial port. @40dRead SERDATR. @40dTransmitter ready? @40dWait until true. @40dMask out all but bits 0-7. @40dSet the stop bit. @40dWrite to SERDAT. ; Handle XON/XOFF and/or escape into the debugger if DEL pressed. @40dRawMayGetChar() @40dDid we get an XOFF? @d @40dIf yes, wait for any other character. @d @40dDid we get a DEL? @40dReturn if not. @40dIf so, Debug() @40dOn return, check for XOFF again. @d ; C compatible routine to print a string. @40dGet first C parameter (string addr.) ; Assembly language entry point (address in A0). @40dGet a string character. @40dExit if is the terminating zero. @40dIs it a newline? @d @40dIf so, output a CR first. @d @d @40dOutput the character. @40dGo on to next character. @d ; C compatible function to output a hex number. First argument ; is the number, second one is the number of digits. The number ; will be output with one space following. @40dGet C parameters. ; Assembly language entry point. @d @d @d @40dCompute 8 - number of digits. @40dLoop that many times, rotating the @40dnumber left by a digit each time, @40dto left-align the number in D2. @40dGet number of digits. @d @40dShift current digit into bits 0-3. @d @40dExtract bits 0-3 from the number. @40dNumeric or alphabetic digit? @d @40dIf alphabetic, add ('A'-'9'). @a?fc22ba FC22BA add.b #'0',D0 Convert to ASCII digit. @a=fc22be @40dOutput the digit. @40dLoop until all digits done. @d @40dOutput a space. @d @d ; Entry point to do a RawDoFmt() to the serial port. @40dSave A2. @40dPoint A2 to RawPutChar() function. @40dRawDoFmt() @40dRestore A2. @d --------------------------------------------------------------------------- Open() --------------------------------------------------------------------------- ; This gets executed if someone actually opens "exec.library". @40dReturn ExecBase. @40dIncrement exec.library open count. @d --------------------------------------------------------------------------- Close() --------------------------------------------------------------------------- ; This gets executed if someone tries to close "exec.library". @40dDecrement exec.library open count. --------------------------------------------------------------------------- Expunge() --------------------------------------------------------------------------- ; This gets executed if someone tries to Expunge() the exec. ; Needless to say, we won't do anything of the sort, but we'll make ; the caller happy by returning zero. ; The reserved jump vector (at ExecBase - 24) also points here. @d @d @a?fc22f0 ; ROM-Wack ; -------- ; This is the Amiga's ROM resident mini-debugger. The following ; string is sent out the serial port when it starts up. @8p@,10s ; ROM-Wack has a private, 236 byte data area at $000200. The ; following is a memory map of this area. Addresses are given as ; hex offsets from $000200. ;------------------------------------------------------------------- ; 00 (32 bit) Pointer to current key bindings. ; 04 (32 bit) Saved key binding pointer if not using main ones. ; 08 (32 bit) Value of last number entered by the user. ; 0C (32 bit) The "current address" for all operations. ; 10 - 13 (not used). ; 14 (32 bit) The current "frame size". ; 18 (32 bit) The upper limit address for searches and fills. ; 1C (16 bit) Number of characters in the input buffer. ; 1E (8 bit) Flag indicating whether the "frame" should be ; redisplayed after a command has executed. ; 1F (8 bit) Flag indicating whether we are in "alter" mode. ; 20 (16 bit) Flag indicating whether there is unprocessed data ; in the buffer (0 if yes, 1 if no). ; 22 (16 bit) Number of digits in the number most recently entered. ; 24 (16 bit) Flag indicating whether a number is being gathered as ; a parameter to a command, or being entered unprompted ; (in which case it becomes the current address). ; 26 (32 bit) Indirection stack pointer (for following pointers). ; 2A - 4F (not used). ; 50 - 81 Input buffer for user commands. ; 82 (16 bit) Last character typed by the user. ; 84 (32 bit) Pointer to data area on the stack, holding CPU and ; process related information (map farther down) ; 88 (16 bit) Instruction to use for breakpoints (TRAP #15). ; 8A - E9 Breakpoint table. Each entry consists of an address ; where an instruction was replaced with TRAP #15, and ; the word which had been there before. ; EA (16 bit) Value to be written to INTENA to restore serial ; port interrupts to their original state. ;------------------------------------------------------------------- ; This gets called from the exec initialization code. @40dSave ExecBase. @40dPoint to ROM-Wack's data area. @40dInitialize it. @40dRestore ExecBase. @40dInstall default ExecBase->DebugEntry. @40dInstall default Debug() vector. @40dRawIOInit() @d ; Special exception handlers for ROM-Wack ; --------------------------------------- ; ROM-Wack trace exception handler. @40dPush "Trace" exception number. @40dEnter the debugger. ; ROM-Wack breakpoint exception handler. A "TRAP #15" instruction ; is used for the breakpoint. @40dPush "TRAP #15" exception number. @40dEnter the debugger. --------------------------------------------------------------------------- Debug() --------------------------------------------------------------------------- ; This entry point is used when an actual function call to Debug() ; is made. We set up the supervisor stack to look like it would ; after an exception handled by the exec exception entry points. @40dSave A5. @40dWhere to go in supervisor mode. @40dSupervisor() ; We now have an exception stack frame from the Supervisor() call. @40dPop the return address from the @40dDebug() call from the user stack. @d @40dGet the return address back. @40dFake exception number zero. ; Now the supervisor and user stacks look as if an exception had ; occurred and was handled by the exec, with the difference that ; the original value of A5 is on the supervisor stack, and A5 now ; contains the return address from the Debug() call. ; Exception entry point. ; ---------------------- ; The debugger is entered here with the stacks already set up as ; above (by the system exception handler). First, we verify if ; the stack is even working (pointing at RAM). @40dPut a signature on the stack. @40dCheck if it can be read back. @d ; A signature pushed on the supervisor stack could not be read ; back, so we are in serious trouble. Initialize the stack at ; the top of the first 256K of chip memory. Put a fake exception ; stack frame on the new stack. @40dStart stack at 256K. @40dFake program counter. @40dFake status register. @40dPush exception number -1. @d ; We now have a working supervisor stack with the exception ; number and stack frame on it. @40dSave most of the CPU registers. @40dPoint A5 at the exception number. @40dReserve 22 bytes of stack space, @40dand point A4 to the bottom of this. @d @40dGet the exception number. @40dStore it. @40dStore supervisor stack pointer. @d @40dStore user stack pointer. ; When a 680x0 hits an exception, it pushes the program counter, then ; the status register, onto the supervisor stack. For bus and ; address errors, however, more information is saved. On 68010 and ; 68020 processors, this comes before the program counter and status ; register, i.e. the stack pointer after an exception always points ; at these. On the 68000, however, 8 bytes of other information ; are pushed on the stack AFTER the PC and SP. The following code ; compensates for this. @40dCheck CPU/FPP configuration. @40dIs it a plain vanilla 68000? @d @40dWas it an address error? @d @40dWas it a bus error? @d @40dIf either, skip past bus error info. ; For all CPUs, A5 now points to where the status register and ; program counter are stored on the supervisor stack. @40dCheck the supervisor mode bit. @40dIf it was set, @40dget ExecBase, @40dand get the current task pointer. @40dGet and store the status register. @40dGet and store the program counter. ; Time for a summary (so I don't get confused). A4 currently ; points at a data structure, on the supervisor stack, containing ; the following: ; (60 bytes) Register dump (D0-D7, A0-A6). ; (32 bit) Current task pointer or zero. ; (32 bit) Number of the exception that got us here (see below). ; (32 bit) Saved supervisor stack pointer. ; (32 bit) Saved user stack pointer. ; (16 bit) Saved status register (from exception stack frame). ; (32 bit) Saved program counter (from exception stack frame). ; If the exception number is zero, Debug() was used to get here. ; If the exception number is -1, we somehow got here, but the ; supervisor stack pointer was clobbered and not pointing to RAM. ; In this case, we have just set it to 256K, and the saved ; program counter and status register are invalid. @40dPoint to ROM-Wack's data area. ; Disable serial port interrupts, and make a control word which, ; when written to INTENA, will return them to their original status. @40dStore interrupt enable status. @40dDisable serial port interrupts. @d @40dMake the control word to be used @40dto restore those two interrupts. @40dRawIOInit() @d @40dPrint a newline and "rom-wack". @40dSave pointer to stack-resident data. @40dMake the saved exception program @40dcounter even (if not already so), @40dand put it back. @40dSet the "current address" there. @40dDid a TRAP #15 get us here? @40dSkip the following if not. ; Special handling for breakpoints. Back the PC up by 2 (over ; the TRAP #15 instruction used for the breakpoint), and clear ; the breakpoint (restore the original instruction). @40dBack up to breakpoint address. @40d"clear" the breakpoint. @40dUpdate the saved program counter. @40dPoint to the stack-resident data. ; Install the exception vectors needed for breakpoints and ; single-stepping. @40dInstall TRAP #15 exception vector. @40dInstall "Trace" exception vector. @40dSet the search limit to 16 megabytes. @40dDisplay the ROM-Wack register frame. @40dEnter the ROM-Wack main loop. ; Entry point for <Tab> command. ; Entry point to run a single instruction in trace mode. This does ; everything required to resume running as below, but with the trace ; mode bit set in the saved status register, so only a single ; instruction will be executed after the RTE (and then we come back ; in at the top). @40dPoint to data on system stack. @40dGet the saved status register, set @40dthe trace mode bit, and store it. @d ; Set up to continue running after exit from ROM-Wack. Note that ; to compensate for the different exception stack frame formats of ; bus error and regular exceptions, we don't even try to compensate ; for the format of the exception stack frame, just build one ; containing a simple status register and program counter right ; above the register dump. This allows the RTE instruction to ; conveniently resume running whatever called ROM-Wack, but ; some garbage may be left on the stack. ; Entry point for "go" command. ; This starts running wherever the current address is pointing. @40dPoint to data on system stack. @40dMake PC the current address. ; Entry point for "^D" and "resume" commands. ; This continues running where it left off. @d @40dGet the saved status register. @40dClear the trace mode flag and put it on the stack. @40dRestore serial port interrupt status. @40dPut the program counter on the stack. @40dGet the saved user stack pointer, @40dand restore it. @40dPoint to the register dump. @40dRestore all the other registers. @40dPop the exception number. @40dReturn from the exception. ; Subroutine to initialize ROM-Wack's data area. @40dPoint to data area. @d @40dClear 236 bytes. @d @40dUse primary key bindings. @40dFrame size = 16 bytes. @40dUse "TRAP #15" for breakpoints. @d @8p@32wPadding. ; A lot of the functions in ROM-Wack have C compatible entry points ; (which get the parameters from the stack), and most of them have ; C compatible return values (in D0). I guess this is to interface ; them to other functions, written in C, in the bigger versions of ; Wack. Likewise, the following are probably C interface functions, ; but they (and most of the C entry points) aren't used anywhere. ; "Peek" function for C. Takes address, returns 16-bit contents. @a?fc2498 @d @d @d ; I have no idea why this is here twice. @d @d @d ; "Poke" function for C. Takes address and a word, and stores the ; word at the address given. @d @d @d ; Entry point for "user" command. ; Takes all the data currently resident on the supervisor ; stack (exception stack frame, exception number, register dump, ; ROM-Wack data area, and any other stuff), and moves it onto the ; user stack. Then puts the CPU into user mode. ; In effect, this switches ROM-Wack from running as part of the ; exec kernel (in supervisor mode) to running as a plain, ordinary ; task, along with other tasks. The good side is that the system ; can now go and clean up (flush disk buffers, etc), while the ; user can go on playing with ROM-Wack. The bad side is that the ; task which did Debug() or trapped into ROM-Wack is stuck there ; forever. "go", and "resume" commands will no longer work. @40dPoint to data area on stack. @40dWas CPU in supervisor mode? @40dIf not, exit. @40dGet the user stack pointer. @40dReserve 92 bytes on user stack. @40dMake this the new data area. @40dPoint to top of new data area. @40dPoint to top of old data area. @40dSave the supervisor stack pointer. @d @d @40dCopy exception stack frame, register @40ddump, data area, and any other stuff @40dto the user stack. @40dBump supervisor stack pointer up. @40dBump user stack pointer down. @d @40dGet the saved status register. @40dPrint a newline. @d ; String compare function. This checks if a command entered by ; the user, pointed to by A1, matches a command, pointed to by A0, ; from the command table. It returns zero if so, else it returns ; the character number at which the mismatch occurred. ; C style entry point. @d ; Assembler entry point. @d @40dGet a byte from reference string. @40dEnd of string? @40dCompare to byte from second string. @40dLoop while strings are equal. @40dCompute number of equal characters. @40dExit. @40dCheck if other string ends also. @d @40dReturn zero (strings match). @d ; Entry point for <Return> command (Redisplay frame). @40dRequest redisplay of current frame. @d ; I don't know what this is for. It's not referenced anywhere. @d @d @d @d ; Entry point for ">", <Space> commands (Move forward a word). @40dIncrement current address by 1 word. @40dRequest frame display. @d @d @40dPrint a newline. @d @d @d ; Entry point for "<", <Backspace> commands (Move back a word). @40dDecrement current address by 1 word. @40dRequest frame display. @d @d @40dPrint a newline. @d @d @d ; Entry point for "." command (move forward a frame). @40dGet frame size. @40dAdd to current address. @40dRequest frame display. @d ; Entry point for "," command (move back a frame). @40dGet frame size. @40dSubtract from current address. @40dRequest frame display. @d ; Entry point for "[" command. ; This follows a pointer at the current address. @40dGet indirection stack pointer. @40dGet current location. @40dStore in indirection stack. @40dPut indirection stack pointer back. @40dMake the current location even. @40dGet the pointer. @40dPut it in the current location. @40dRequest display of frame. @d ; Entry point for "]" command. ; This walks undoes a "[" command, good for walking back along ; singly linked lists or backing out of nested structure pointers. @40dGet indirection stack pointer. @40dGet address from indirection stack. @40dRequest frame display. @40dUpdate the indirection stack pointer. @d ; Entry point for "+" command. @40dEcho "+" on the user's terminal. @d @40dRead a number from the keyboard. @d @40dIf none, print newline and exit. @40dGet the number which was entered. @40dAdd to current address. @40dRequest frame redisplay. @d ; Entry point for "-" command. @40dEcho "-" on the user's terminal. @d @40dRead a number from the keyboard. @d @40dIf none, print newline and exit. @40dGet the number which was entered. @40dSubtract from the current address. @40dRequest frame redisplay. @d ; The following isn't referenced anywhere. Perhaps an outdated ; bit of code to set the current address to the last number entered. ; This is now unnecessary. @d @d @d ; Entry point for ":" command (set frame size). @40dEcho ":" on the terminal. @d @40dGet a number from the terminal. @40dSet the frame size. @40dRequest frame redisplay. @d ; Routine to print a prompt for memory-modify commands. @40dGet the current address. @40dPrint it (6 digits). @40dIs the frame size zero? @40dIf frame size is not zero, then @d @40d Get word at current address. @40d Print it (4 digits). @40d Print "=". @d @40dElse @40d Print "xxxx =" @40dEndif @d ; Entry point for "=" command (modify one word of memory). ; This is also used by the "alter" routine below. It returns 1 ; if a value was entered, and 0 if not. @40dPrint a prompt. @40dGet a number from the terminal. @40dDid we get one? @d @40dIf so, update the word pointed @40dto by the current address with the @40ddata which was entered, and return @40da non-zero value. @40dAre we in "alter" mode? @d @40dIf not, request frame redisplay. @d @8p@,8s ; Entry point for "alter" command. @40dSet "alter mode" flag. @40dPrint a newline. @40dDo an "=" command. @d @40dIf a value was entered, increase the @40dcurrent location by 2, and loop back. @d @40dClear "alter mode" flag. @40dPrint a newline. @d ; I don't know what this is for. Perhaps a remnant from an old ; version of ROM-Wack, or something from a bigger version of Wack. ; It's not called from anywhere. @a?fc2692 @40dPoint to the string below. @40dPrint it. @d @8p@,22s ; Entry point for "list" command. ; This will traverse a standard exec list, displaying information ; about each node. It should be called when the current address is ; either that of the list header, or of any node. @d @40dGet the current address. @40dList header ("Tail" field zero)? @d @40dIf yes, go to first node. @40dSee if now at end of list (or empty). @40dIf no more nodes, exit. @40dGet the node's name. @40dIf name pointer is null, replace @40dit with a pointer to a zero. @40dSave name pointer on stack. @d @40dGet priority field. @40dPut it on the stack. @40dGet type field. @40dPut it on the stack. @40dPut node address to stack. @40dPoint to data on stack. @40dPoint to format string below. @40dRawDoFmt() to serial port. @40dPop data back off the stack. @40dMove to next node. @40dLoop until no more nodes. @40dPrint a newline. @d @d @a?fc26fc @8p@,36s ; Subroutine to display a "frame" of data from memory. On entry, ; the address of the frame is given in D0, and its size in D1. ; Data is displayed in lines of up to 8 words, and as an address, ; followed by hex words, followed by their ASCII character ; equivalents. @d @40dReserve 40 bytes of stack space. @40dPrint a newline. @d @40dSet number of bytes remaining. @40dJust exit if frame size is zero. ; Outer loop: Display one line of the frame. @a?fc2732 @d @40dPrint the address. @40dMaximum 8 words per line. @40dPoint to reserved stack space. ; Inner loop: Display one word of data. @40dGet a word from memory. @40dPrint it. @d @40dGet high 8 bits of current word. @40dConvert to printable data. @40dAdd to character string. @d @40dDo likewise for low 8 bits. @d @40dDecrement number of bytes remaining. @40dExit if zero, @40dotherwise continue until line full. ; End of line reached. @40dMark end of character string. @40dPoint to beginning. @40dPrint the string and a newline. @40dGo and do next line. ; End of frame reached. @40dMark end of character string. @40dPoint to beginning. @40dPrint the string and a newline. @d @d @d ; Subroutine to display an 8-digit hex number. @d @40d8 digits. @d ; Subroutine to display a 6-digit hex number (address). @d @40d6 digits. @d ; Subroutine to display a 4-digit hex number. @d @40d4 digits. @40dGo print the number. @d @d ; Subroutine to print the "DR:", "AR:", and "SF:" portions of ; the ROM-Wack register frame. @d @d @40d7 Data registers. @40dPoint to "\nDR: " string. @40dDisplay data registers. @40d6 Address registers. @40dPoint to "\nAR: " string. @40dDisplay address registers. @40dSkip exception number on stack. @40dSupervisor mode? @d @40dIf not, get saved USP. @d @40dPoint to "\nSF: " string. @40dPrint it. @d @40dDisplay 14 words of stack data. @d @d @40dPrint a newline. @d @d ; Subroutine to print a string pointed to by A0, followed by (D2) ; longwords from memory pointed to by A2. @40dPrint the string. @d @40dLoop and print all the data. @d @d ; Subroutine to print the first ("PC: ...") line of the register ; frame, followed by the rest of the register frame via the routine ; above. Note, we just give RawDoFmt() a format string describing ; the data structure on the supervisor stack ; Also entry point for "regs" command. @40dGet pointer to stack-resident data. @d @40dPoint to format string. @40dFormat the data. @d @40dPoint to the register dump area. @40dPrint it. ; Text strings used to display register frames. The second one ; isn't used anywhere. @a?fc27fc @8p@,52s @8p@,19s @8p@,33s @8p@,6s @8p@,6s @8p@,6s ; Memory fill routine. Fills (D1) words, starting at (D0), with ; the value in D2. Not used anywhere. @d @d @d @d @d @d @d ; Subroutine to find a breakpoint in the breakpoint table, given ; its address. If successful, returns address of breakpoint table ; entry, if not, returns zero. @a?fc2886 @40dPrint a newline. @40dPoint to breakpoint table. @d @40dScan breakpoint table for a @40dbreakpoint address matching A1. @d @d @40dNot found, so return zero. @d @40dReturn address of table entry. @d ; Entry point for "clear" command. ; Deactivate the breakpoint at the current program address, ; if there is one. @40dGet current address. @40dSee if it is a breakpoint address. @d @40dIf so, deactivate it and write the @40doriginal instruction back. @d ; Entry point for "reset" command. ; Deactivates all breakpoints. @40dPoint to ROM-Wack's breakpoint table. @40dSize of breakpoint table - 1. @40dGet breakpoint address. @40dIf breakpoint in use then @d @40d Clear breakpoint address. @40d Fix instruction at breakpoint. Endif @40dGo to next table entry. @40dLoop until whole table done. @40dPrint a newline. @d ; Entry point for "set" command. ; Activates a breakpoint at the current address. @40dGet current address. @40dSee if it is a breakpoint. @40dIf so, do nothing. @d @40dScan the breakpoint table for an @40dunused entry. @d @d @d ; No unused breakpoint table entry found. @40dPoint at "too many" string. @40dPrint it. @40dReturn. ; Unused entry in the breakpoint table found. Activate a breakpoint. @40dSave instruction at breakpoint. @40dReplace with TRAP #15. @40dSave the breakpoint address. @d @8p@,12s ; Entry point for "show" command. ; Lists all active breakpoints. @40dPoint to breakpoint table. @40dLoop for 16 entries. @40dGet current breakpoint address. @40dSkip if zero. @40dPrint a newline. @40dPrint the breakpoint address. @40dGo to next table entry. @40dLoop until done. @40dPrint a newline. @d @40dGarbage. ; Subroutine to get a character and echo it to the user. @40dGet a character. @d @40dEcho it back. @d @d ; Entry point for "!" command (Modify a register). @d @40dPrint "!". @d @40dGet a character. @40dConvert to upper case. @40dGet pointer to stack-resident data. @40dPoint to data register dump area. @40dMaximum data register # = 7. @a?fc2952 FC2952 cmp.b #'D',D0 [D]ata register? @a=fc2956 @d @40dPoint to address register dump. @40dMaximum address register # = 6. FC295E cmp.b #'A',D0 [A]ddress register? @a=fc2962 @d @40dPoint to user stack pointer. FC2968 cmp.b #'U',D0 [U]SP? @a=fc296c @40dIf so, go change it. @40dOtherwise, exit. ; Enter here with A0 pointing to a dump area containing sequential ; images of the address or data registers, and D2 containing the ; maximum register number. @40dGet a character. @40dBackspace? @40dIf so, exit. @40dIs it a digit? @40dIf not, exit. @40dConvert to number. @40dGreater than maximum? @40dIf so, exit. @40dCompute offset to given register. @40dCompute address of register. ; Enter here with A2 pointing to a longword containing the image ; of the register to modify. @40dGet the current value. @40dPrint a space. @40dDisplay value as 8 hex digits. @40dPrint "=" @d @40dGet the new value from the user. @40dWas a value entered? @40dIf not, exit. @40dUpdate the register. @40dPrint a newline. @d @d ; Entry point for "^" and "limit" commands. ; This copies the current address to the "limit" address, i.e. the ; address to stop searching or filling memory at. @40dCopy current address to limit. @40dRequest frame redisplay. @d ; Entry point for "find" command. @d @40dGet the search pattern. @40dExit if none entered. @40dDo the searching. @40dExit if not found. @40dRound result address down to even. @40dStore it in current address. @40dRequest frame redisplay. @d @d ; Search routine for the "find" command. @d @40dStart searching at start address - 2. @40dSet the number of bytes to match. @40dSet the search pattern address. @40dGo to next address. @d @40dSee if limit address reached. @40dIf so, return false. @40dCompare one byte. @40dGo to next address if mismatch. @40dDecrement byte counter. @40dLoop until all matched. @40dFound: return the address where. @d @40dReturn false. @d @d ; Search/fill pattern input routine. Prompts for a pattern, ; accepts it, and returns the following: ; D0: Number of bytes in search pattern. ; D1: True if pattern entered, false if aborted. ; A0: Lower bound for search. ; A2: Upper bound for search. ; A3: Address of search pattern. @40dPoint to "pattern? " string. @40dPrint it. @40dGet a number from the terminal. @40dDid we get one? @40dIf not, return false. @d @40dCompute 4-(d+1)/2, where d is the @40dnumber of digits entered. @d @40dGet the number entered. @40dGet the current address, @40dand the limit address. @40dReturn true. @d @a?fc2a2c @8p@,12s ; Entry point for "fill" command. @d @40dGet the fill pattern. @d @40dIf one entered, go do the fill. @40dRequest frame redisplay. @d @d ; Fill routine for the "fill" command. @40dAdjust number of bytes for "dbra". @40dInitialize loop counter. @40dInitialize pattern address. @40dUpper limit reached? @40dExit if so. @40dCopy one byte from the fill pattern. @40dLoop until pattern done. @40dLoop back and do pattern again. @d ; Subroutine to print a single space. @d @40dCharacter code for a space. @40dRawPutChar() @d @d ; Subroutine to print a single newline. @d @40dPoint to string below. @40dPrint it. @d @d @8p@,2s ; Subroutine to print string at address pointed to by A0, followed ; by a newline. @d @40dPrint the string. @40dPrint a newline. @d @d ; I don't know what this is for. It's not referenced anywhere. @d @d ; Subroutine to get a character. Returns with the zero flag clear ; if a character was received, set otherwise. @d @d @d @d @d ; Isdigit() type of function. Enter with a character in D0, returns ; with the zero flag set if the character is a numeric digit. @a?fc2aac FC2AAC cmp.b #'0',D0 @a=fc2ab0 @d FC2AB2 cmp.b #'9',D0 @a=fc2ab6 @d @d @d ; Subroutine to convert an 8-bit code in D0 to a 2 character ; printable ASCII version. ; Codes 0 and 128-255 return "..", control codes are shown as "^A" ; to "^\", other characters are shown as a space, then the actual ; character. @d @40dMove ".." into D2. @40dIs the code zero? @40dIf so, return ".." and exit. @40dIs the high bit set? @40dIf so, return ".." and exit. @40dMove space and null into D2. @d @40dIf the character is a control code, @d @40d Move "^" and null into D2. @a?fc2adc FC2ADC or.b #$40,D0 Convert to printable character. @a=fc2ae0 @40d Put it into D2. Endif @40dReturn D2. @d @d ; ROM-Wack command execution function. Calls the command function ; given in A0, and on return, displays the current "frame" if ; requested by the command function. @d @40dGet the command function address. @40dExecute the function. @40dNeed to display frame? @40dExit if not. @40dGet current address. @40dGet frame size. @40dDisplay the frame. @d ; Entry point for "?" command (Display multi-character commands). @d @40dPoint to ROM-Wack command table. @40dGet a character. @d @40dIf not zero, print it, and loop @40dback to do the next one. @40dPrint a space. @40dAnother zero? @40dIf not, loop back & print next cmd. @40dPrint a newline. @d @d ; ROM-Wack command dispatch table scanner. ; Enter with a character code in D0. This routine runs through the ; command table, and returns either the address of the command table ; entry matching the character, or zero. ; Each command table entry either corresponds to exactly one ; character, or to a range of characters. This may sound confusing, ; but I've commented the command table itself, so you can just look ; there and it will be perfectly obvious. @d @40dPoint to command table. @d @40dGet link to next table entry. @40dStrip off high 8 bits. @40dExit if no next entry. @40dPoint ot next entry. @40dSee if command matches table entry. @40dExit if it does. @40dGo to next entry if smaller. @40dCheck if in range. @40dGo to next entry if not. @40dReturn table entry address. @d ; ROM-Wack command dispatcher. Given a command character, looks it ; up in the command dispatch table and executes the command if found. ; C style entry point. @d ; Assembler entry point. @40dLook command up in command table. @40dFound it? @40dExit if not. @d @40dGet execution address from table and store it on the stack. @40dExecute the function. @40dPop address back off the table. @d ; Multi-character command lookup function. ; This takes a pointer to a command string in A0, and looks it up ; in the table of multi-character commands. It returns the address ; of the table entry corresponding to the command, or zero if the ; command was not found. ; C style entry point. @d ; Assembler entry point. @d @d @40dPoint at command dispatch table. @d @40dGet pointer to next table entry. @40dExit if null (end of table). @d @40dGet pointer to string. @40dPoint to string to match. @40dCompare them. @40dGot a match? @40dKeep looking if not. @40dReturn table address. @d @d ; ROM-Wack main loop: Gets a character from the user, then ; interprets it, then gets next characer, etc. @40dGet a character from the user. @40dStore it. @40dProcess it. @40dGo get next character. ; Entry point for letters, numbers and underline characters typed ; at the ROM-Wack command level. This function collects them in ; a buffer. @40dAddress of secondary key bindings. @40dAlready using them? @d ; This is the first character of a multi character command, so ; clear the buffer and point to the other set of key bindings. @40dZero characters in buffer so far. @40dSave address of primary key bindings. @40dPoint at secondary key bindings. @a?fc2bbe FC2BBE cmp.b #' ',$82(A6) Was the key a space? @a=fc2bc4 @40dIf so, ignore it (kludge, see below). @40dGet number of chars in buffer @40d50 characters already? @40dIf equal or more, ignore this one. @40dGet command character. @40dEcho it to the user. @40dPoint to buffer. @40dGet offset into buffer. @40dStore character in buffer. @40dIncrement buffer count. @40dThere is unprocessed data in the @40dbuffer, so set the flag. ; Entry point to get a number from the user. @a?fc2bf0 FC2BF0 move.b #' ',$82(A6) Start using secondary key bindings. @a=fc2bf6 @d @40d Indicate that input is being gathered for a specific function. ; Loop to read the digits. We stay in this loop, using the ; secondary (input gathering) key bindings until some command ; function switches us back to the primary ones. That means ; the user either canceled the line, or pressed RETURN. @40d Using secondary key bindings now? @40d Exit loop if not. @40d Get a key from the user. @40d Store it in "last key typed". @40d Dispatch it as a command character. @40d Loop. ; The input line has been processed. Either it was a command ; (and was executed), or it was a number, or it was an error. @40d Clear "parameter input" flag. @d @40d Get number of digits. @40d If no useful data in buffer (line @40d canceled, command already executed, @40d bad symbol...), return zero digits. @d ; Entry point for <Space> while using the secondary key bindings. @d ; Entry point for CTRL-U and CTRL-X. Point back to the original ; set of key bindings, and set the buffer to empty. @40d-1 characters in buffer. @40dRestore top-level key bindings. @40dPrint a linefeed. @40dBuffer contains no useful data. @d ; Entry point for <Backspace>. This deletes one charaacter from ; the input buffer. @40dAny characters in the buffer? @40dIf none, cancel input. @40dPoint to buffer. @40dGet buffer offset. @40dClear last character in buffer. @40dDecrement buffer count. @40dPoint to string below. @40dPrint it. @40dIs buffer now empty? @40dIf so, cancel input. @d ; String used to erase one character from the user's terminal. @8p@,4s ; Entry point for <Return>. @40dRestore previous key bindings. @40dPoint to input buffer. @40dGet number of characters in it. @d @40dIndicate that no data is in the buffer waiting for processing. @d @d ; There was data in the buffer, now interpret it. @40dZero-terminate the buffer. @40dPoint to it. @40dLook command up in table. @40dBranch if not found. @d @40dIndicate that buffer was processed. @40dGet the command table offset. @40dGet execution address from table. @40dExecute the command. @40dClear frame redisplay flag. @40dPop execution address of stack. @d ; Continue here if the buffer contained a string not found ; in the command table. For a full Wack, this means it would ; either be a number, or a symbol. For ROM-Wack, it's either ; a number or an error. @40dPoint to the string in the buffer. @40dPoint to input number location. @40dInterpret input buffer as a number. @40dStore number of digits. @d @40dIf 0 digits (bad hex number), print @40d"unknown symbol", indicate that no @40ddata is waiting in the buffer, and @40dexit. ; Continue here if number of digits was non-zero. If the number ; wasn't being gathered as a parameter to another command, make it ; the current location. @40dParameter being gathered? @40dExit if so. @40dRound number down to even. @d @40dSet the current location to it. @40dRequest (re)display of frame. @d @8p@,18s ; Hex number interpreting routine. @d @40dStart result at zero. @40dStart with "-1 digits". @d @40dJump into the loop. @40dEntry point for hex digits: Add 10. @d @40dShift previous number left by 4 bits. @40dAdd in new digit. @40dIncrement digit counter. @40dGet a digit from the input string. @40dExit if end of string reached. @a?fc2d14 FC2D14 sub.b #'0',D0 Subtract '0'. @a=fc2d18 @40dExit if result less than zero. @40dGreater than 9? @40dBranch if not. @40dSubtract 17. @40dExit if result less than zero. @40dBranch if less than 7. @d @a?fc2d2c FC2D2C sub.b #$20,D0 Subtract ('a' - 'A'). @a=fc2d30 @40dBranch if less than zero. @d @40dBranch if less than 7. ; If we branch here, the number entered contained an invalid ; hexadecimal digit. @40dReturn "0 digits". ; Branch here if end of string reached with no invalid digits. @40dStore result where indicated. @40dReturn number of digits. @d @d ; Some sort of format string. Not used anywhere. @a?fc2d42 @8p@,6s ; Toupper() type of function. Converts character in D0 to upper ; case if necessary. @a?fc2d48 FC2D48 cmp.b #'a',D0 Less than 'a'? @a=fc2d4c @d @a?fc2d4e FC2D4E cmp.b #'z',D0 Greater than 'z'? @a=fc2d52 @d FC2D54 sub.b #$20,D0 If neither, convert to upper case. @a=fc2d58 @d @8p@32wPadding. --------------------------------------------------------------------------- result = Procure( semaphore, bidMessage ) D0 A0 A1 --------------------------------------------------------------------------- ; A Procure/Vacate semaphore is a message port structure plus a ; counter. A task can "lock" the semaphore, by calling Procure(). ; Further attempts to lock the semaphore won't succeed until the ; original locker unlocks the semaphore by Vacate(). ; A sm_Bids value of -1 indicates that the semaphore is free, zero ; indicates it's locked and no one is waiting, 1 means one task is ; waiting, etc. ; Whenever Procure() is called, a pointer to a message must be ; given. This message is enqueued on the semaphore's port if the ; semaphore is not currently available, and returned when the ; caller's turn comes up to get the lock. If the semaphore was ; available (sm_Bids = -1), the message is not used. @40dIncrement the sm_Bids field. @40dCheck if the semaphore was free. @40dIf it was, store pointer to the current locker's message @40dand return TRUE. @d @40dIf it wasn't, enqueue the message @40don the semaphore's port and return @40dFALSE. --------------------------------------------------------------------------- Vacate( semaphore ) A0 --------------------------------------------------------------------------- ; A task which has successfully obtained a semaphore via Procure() ; calls this to release it again. The sm_Bids field is decremented. ; If it was zero and ends up at -1, nobody else was waiting to use ; the semaphore. If it ends up positive, someone else is waiting ; and we must send his bid message back to let him know he has it ; now. The first message enqueued on the semaphore's port is the ; task which has waited longest and gets it. @40dClear pointer to locker's message. @40dDecrement sm_Bids field. @40dReturn if it is now -1. @d @40dSave semaphore pointer. @40dDequeue the oldest bid message. @40dRestore semaphore pointer. @40dStore pointer to the message. @40dExit if no message (error). @d @40dReplyMsg() to inform the waiting @40dtask, then exit. --------------------------------------------------------------------------- InitSemaphore( signalSemaphore ) A0 --------------------------------------------------------------------------- @40dPoint to the waiting task list. @40dInitialize it to empty. @d @d @d @40dClear the ss_Owner field to null. @40dClear the ss_NestCount to zero. @40dSet the ss_QueueCount to -1. @d --------------------------------------------------------------------------- ObtainSemaphore( signalSemaphore ) A0 --------------------------------------------------------------------------- @40dForbid() @40dIncrement the ss_QueueCount. @d ; If the ss_Queuecount is now 0, then the semaphore was free and ; we got it. Set the pointer to the owning task, set the nesting ; level to 1, and exit. @40dStore pointer to owning task. @d ; The ss_Queuecount was not -1, so the semaphore was already owned ; by someone. Check if it is the calling task. @d @40dGet current task pointer. @40dDo we own the semaphore already? @40dIf so, increment nest count and exit. ; Another task owns the semaphore at the moment, so we must block. ; Since all non-running tasks must be on the TaskWait queue, we ; can't just enqueue the task on the semaphore. Instead, we build ; a temporary node on the stack, and enqueue that. Then we clear ; signal #2, and wait for someone to set it, which will occur when ; the current owner of the semaphore releases it. When we wake up, ; we deallocate the temporary node, and return to the user. @40dReserve 12 bytes of stack space. @40dStore pointer to this task. @40dClear signal #2. @40dPoint to waiting task list. @d @40dEnqueue the temporary list node. @d @40dWait for signal #2. @40dRelease the reserved stack space. @d @40dIncrement the nesting count. @40dPermit() @d --------------------------------------------------------------------------- ReleaseSemaphore( signalSemaphore ) A0 --------------------------------------------------------------------------- @40dDecrement the nesting count. @40dIf zero, release the semaphore. @40dIf it went negative, guru city! @40dOtherwise, just decrement the @40dss_QueueCount and exit. ; Continue here when the nesting count went to zero, and we ; therefore don't want the semaphore any more. @40dForbid() @40dDecrement the ss_QueueCount. @40dIf now -1, nobody was waiting. ; The ss_Queuecount is still positive, so someone is blocked on ; the semaphore and waiting to be awakened. The first (oldest) ; entry on the semaphore's queue gets it. @d @40dSave semaphore pointer. @40dPoint to semaphore's queue. @40dRemHead() @40dIf the queue was empty, guru city! @d ; We have a pointer (in D0) to a node which identifies the ; waiting process (the temporary one allocated on that process's ; stack). @d @d @40dGet pointer to waiting task. @40dMake this task the ss_Owner. @d @40dSet waiting task's signal #2. @d @40dExit. ; Continue here if nobody was waiting for the semaphore. In this ; case just clear the semaphore's owner field. @40dClear ss_Owner to null. @40dPermit() @d ; Put up an alert if the semaphore's nest count went negative ; (should never happen, we give away the semaphore when it reaches ; zero), or if the semaphore's queue was empty even though the ; ss_QueueCount said it isn't. @d @40dAlert number (fatal). @40dGet ExecBase. @40dPut up the alert. @40dNever executed. @d --------------------------------------------------------------------------- success = AttemptSemaphore( signalSemaphore ) D0 A0 --------------------------------------------------------------------------- @40dGet pointer to current task. @40dForbid() @40dIncrement the ss_QueueCount. @40dBranch if now zero. @40dDo we own the semaphore already? @40dBranch if true. @40dDecrement the ss_QueueCount again. @40dPermit() @40dReturn FALSE. @d ; Continue here if we were able to get the semaphore. @40dInstall pointer to owning task. @40dIncrement nest count. @40dPermit() @40dReturn TRUE. @d --------------------------------------------------------------------------- ObtainSemaphoreList( list ) A0 --------------------------------------------------------------------------- ; This function is given a linked list of semaphore structures, and ; obtains them all. This is done in two passes. On the first pass, ; a SemaphoreRequest is enqueued on all semaphores which aren't free, ; and all the free ones are grabbed. ; On the second pass, the list is traversed again. This time, if ; we already have a semaphore, we just pass it, otherwise, we wait ; for it. ; Note: Whereas ObtainSemaphore() builds its SemaphoreRequest node ; on the task's stack, this one uses the single copy inside the ; semaphore structure. This means that a crash is sure to result ; if more than one task tries to ObtainSemaphoreList and several ; end up waiting for the same semaphore. The documentation has ; a bit more to say about this. @d @40dClear the "need to wait" flag. @40dGet pointer to current task. @40dForbid() @d @40dPoint to first semaphore in list. @d @40dEnd of list reached? @40dExit loop if so. @40dIncrement current semaphore's count. @40dBranch if now zero. @40dCheck if the semaphore is ours. @40dBranch if true. @40dUse the ss_MultipleLink structure @40dto enqueue our semaphore request on @40dthe semaphore's wait queue. @d @40dIndicate that we need to wait. @40dGo to next semaphore in list. ; We got a semaphore. @40dMake us the owner of this semaphore. @40dIncrement its nest count. @40dTry next semaphore in the list. ; The end of the list was reached. @40dDid we get all the semaphores? @40dIf so, exit. ; We have to wait for at least one semaphore in the list. @40dGo to the start of the list. @d @40dEnd of list reached? @40dIf so, exit. @40dDo we own this semaphore? @d @40dIf we do, and its nest count is not @40dzero, go on to the next one. @40dIncrement the nest count. @40dGo on to the next one. ; Wait for a semaphore. @d @40dWait for signal #2. @40dGo back and check if we have it now. @40dPermit() @d @d --------------------------------------------------------------------------- ReleaseSemaphoreList( list ) A0 --------------------------------------------------------------------------- @d @40dGo to the start of the list. @40dGo to first/next node. @40dEnd of list reached? @40dIf so, exit. @40dReleaseSemaphore() @40dLoop until all done. @d @d --------------------------------------------------------------------------- AddSemaphore( signalSemaphore ) A1 --------------------------------------------------------------------------- @40dInitSemaphore() @40dPoint to global semaphore list. @40dAdd the semaphore to the list. --------------------------------------------------------------------------- RemSemaphore( signalSemaphore ) A1 --------------------------------------------------------------------------- @40dUnlink semaphore from whatever list it's in. --------------------------------------------------------------------------- signalSemaphore = FindSemaphore( name ) D0 A1 --------------------------------------------------------------------------- @40dPoint to global semaphore list. @40dFindName() @d @8p@32wPadding. --------------------------------------------------------------------------- CopyMemQuick( source, dest, size ) A0 A1 D0 --------------------------------------------------------------------------- ; The caller guarantees that the source and destination are even, ; and that the transfer count is a multiple of 4. This allows us ; to skip some of the strategy below. @40d0 bytes left over after longword copy. @40dGo copy (at least) as longwords. --------------------------------------------------------------------------- CopyMem( source, dest, size ) A0 A1 D0 --------------------------------------------------------------------------- ; This is an interesting tutorial on efficient 68000 memory moving. ; Note that this is not necessarily the best way to move memory on ; a 68010 or 68020, since both of these processors have expanded ; features designed to make memory moving more efficient, and all ; the computational overhead of choosing the right copying method ; might not be worth it. @40dLess than 12 bytes to copy? @d @40dIf so, just copy byte by byte. ; The following bit of code handles odd/even source and destination ; addresses. The following is done: ; Source and destination even: Just continue. ; Source and destination odd: Copy 1 byte. Both addresses are now ; even, so we can continue. ; Source even, dest. odd: Copy byte by byte. ; Source odd, destination even: Copy one byte, then copy the rest ; byte by byte. ; Note that there is nothing we can do if the source and destination ; differ by an odd number, i.e. are "out of phase". In such a case, ; we must copy byte by byte. @d @40dIs the source address odd? @40dBranch past the following if not. @40dCopy one byte. @40dDecrement transfer count. @d @40dIs the destination address odd? @40dIf so, transfer byte by byte. ; Both the source and destination addresses are (now) even. ; The worst that can happen now is that we have to move the data ; as individual longwords. First, compute how many bytes will ; be left over after we move the data as longwords, and save this ; value. @40dFind how many bytes will be left @40dif data is copied as longwords. @40dSave this number. ; The "move multiple" approach below copies 48 bytes of data with ; only two instructions. But to use it, 48 bytes of registers need ; to be saved and restored, and so it's not worth it unless we have ; at least two 48 byte "batches" to copy. @d @40d96 or more bytes to move? @40dIf not, copy as longwords. @40dSave 12 registers on the stack. @40dGet 48 bytes with one instruction. @40dMove them to the destination. @d @40dAdd 48 to destination address. @40dSubtract 48 from transfer count. @40dCopy another batch if 48 or more @40dbytes remain to be copied. @40dRestore the 12 registers. ; Whatever the case, D0 now contains the number of bytes left over. ; Copy as many of these as possible as longwords. @40dSee how many longwords to copy. @40dBranch if none. @40dAdjust for dbra. @40dSplit up into two 16-bit values. @d @40dCopy the longwords. @d @d ; Done copying longwords. Get the number of bytes left over (0-3) ; from the stack and fall into the byte copy routine. @40dRestore number of bytes left over. @40dExit if none. @40dHigh 16 bits of byte count = 0. @40dEnter the byte copy loop. ; Copy as individual bytes, either to clean up odds and ends after ; copying in larger chunks, or because copying in larger chunks ; wasn't worth it. @40dSplit transfer count up into two @40d16-bit sections. @40dEnter loop at bottom (for dbra). @40dCopy the bytes. @d @d @d ; System exception alert entry point ; ---------------------------------- ; If an exception occurs or a TRAP instruction is executed while the ; CPU is in supervisor mode, we jump here, since we have no task to ; blame it on. If a task was running, we use its tc_TrapCode vector ; instead, but at powerup, the exec default for this vector is also ; initialized to point here. Later, it is stolen by some other ; part of the system so the "Software error - task held" window can ; be put up before the guru. @40dDump the registers. @40dPoint at exception data on the stack. @40dGet ExecBase. @40dIs it even? @d @40dIf so, point where the current task @40dpointer probably is. @40dGet the exception number. @40dMask out any garbage. ; Fall into Alert(). alertNum is the exception number from the ; stack, and A5 points either to the current task pointer, or to ; the exception data on the stack. --------------------------------------------------------------------------- Alert( alertNum, parameters ) D7 A5 --------------------------------------------------------------------------- ; This routine has a relatively failsafe mechanism for getting an ; alert message up on the screen. I call this the "deferred guru". ; Right away, a signature ("HELP") is installed at location 0, and ; the guru data is stored at location $000100. If the system hangs ; now, the user will get the guru after he/she reboots manually ; via CTRL-Amiga-Amiga. If for any reason it decides it's in ; serious trouble, it will reset itself, with the same effect. @40dDisable all interrupts. @d ; If location 0 ALREADY contains "HELP", something is wrong, and ; no matter what the requested alert was, we reset the computer. @40dSee if "HELP" is at location zero. @40dIf so, unrecoverable crash. ; Store our own "deferred guru" data at 0 and $000100. Then see ; if we should use it. If ExecBase has been clobbered, or the ; stack is not working, or the unrecoverable alert bit is set, ; we blink the power light, and reset the computer. The guru will ; come up during the reboot. @40dMove "HELP" at location 0 now. @40dPoint at location $000100. @40dStore alert number there. @40dStore the parameter. @40dGet ExecBase. @d ; Without checking, I would assume that this is where the famous ; "Recoverable alert doesn't work with expansion memory" bug is. ; Namely, if the ExecBase structure isn't in the first 64K of ; memory, we assume that it isn't OK. With $C00000 memory, it ; will be at $C00276, and KABOOM, this fails, and the system ; resets, doing the unrecoverable alert thing via the "deferred ; alert" mechanism discussed earlier. @40dIs ExecBase in the first 64K? @40dIf not, unrecoverable crash. @d @40dCheck ExecBase complement pointer. @d @40dUnrecoverable crash if not valid. @40dCheck if the stack is working by @40dpushing a signature and trying to @40dpop it off again. @40dUnrecoverable crash if not working. @40dTest high bit of alert number. @40dBranch if unrecoverable. ; Processing for recoverable alerts. If we get this far, we have ; verified that the "dead end alert" flag was not set, that the ; ExecBase pointer and structure are probably OK, and that the ; stack is working (stack pointer pointing to RAM). @40dPoint to ExecBase->LastAlert. @40dStore alert number. @40dStore parameter. @40dCall the guru alert routine. @40dWas the subsystem ID field zero? @40dIf not, return to the caller. ; The alert number was of the form 0000xxxx. This means that it ; must have been a system exception. Presumably, the user has had ; the option of pressing the left or right mouse button, reflected ; in D0 as returned from the guru routine. Accordingly, we ; reset or go to the debugger. @40dTest the guru routine's return code. @40dRestore the registers. @40dReset if zero flag not set. @40dElse go to ROM-Wack. @40dEnable interrupts if they are @40dsupposed to be enabled. @d @d ; Unrecoverable system crash entry point ; -------------------------------------- ; This routine blinks the power light slowly 6 times, and checks ; whether the user presses DEL on a terminal attached to the ; serial port. If DEL is received, it jumps to ROM-Wack. If ; DEL is not received, it resets the computer. This may or may ; not lead to a guru, depending on how locations 0 and $000100 were ; set up before this was called. ; Entry point for when the stack is not working (stack pointer ; clobbered). Set the stack pointer to 256K, and build a fake ; exception stack frame below it. @40dSet stack pointer to 256K @40dBuild fake exception stack frame. @d ; Entry point for when the stack was working. @40dSet CIA data direction register. @40dSet power light to bright. ; Force the CPU into supervisor mode. If the MOVE.W #$2700,SR ; instruction bombs the first time, it will work the second time. @40dSet privilege violation vector. @a?fc3076 FC3076 move.w #$2700,SR Mask all maskable interrupts. @a=fc307a ; Blink the power light slowly 6 times and look for a DEL character ; coming in through the serial port at 9600 bps. If such a character ; is received, go to the debugger. @40dSet loop counter to 5. @40dSet serial port for 8 bits, 9600 bps. @d @40dSet power light to dim. @40dDelay. @40dSet power light to bright. @40dDelay. @40dRead the serial port. @40dClear serial port interrupt bit. @d @40dDid we receive a DEL character? @40dIf not, keep blinking the light. @40dIf DEL not pressed, reset. ; The user pressed DEL. Push the exception number on the stack ; and enter the debugger. @d @40dGo to ROM-Wack. ; "Deferred Guru" support routines ; -------------------------------- ; The following routine is called early in the startup code. Since ; there is no stack at that point, it returns via a jump, rather ; than an RTS. It removes the "HELP" at zero, if present, and ; loads D6 and D7 with the data for ExecBase->LastAlert. @40dLoad -1 into D6 @40dSee if location 0 contains "HELP". @40dIf not, go back to init. code. @40dClear location 0. @40dLoad D6 and D7 from location $0100. @40dGo back to init. code. ; The following subroutine is called later, after the ExecBase ; structure has been built. It writes the data determined above ; (still in D6 and D7) into ExecBase->LastAlert. @d @d ; Guru Alert Routine ; ------------------ ; This routine puts the big red "Guru" message up on the screen, ; waits for the user to click the mouse button, then returns. ; The routine is called with the alert number and parameters stored ; at LastAlert in the ExecBase structure. This routine reads the ; alert number from there and decides what to do with it. ; An alert number of -1 means that no alert was outstanding, and ; therefore, the routine returns right away. This means that if ; it is called via the "alert.hook" mechanism, and no alert was ; pending from before the reboot, nothing will happen. ; If the alert number is not -1, an alert will be generated. The ; following algorithm is used to decide what the first string in ; the alert should say. ; IF the alert number is of the form xxxx01xxxxxxxx THEN ; Make the alert message "Not enough memory". ; ELSEIF the high bit in the alert number is clear, and the ; "general error" field is not zero THEN ; Make the alert message "Recoverable Alert". ; ELSE ; Make the alert message "Software Failure". ; ENDIF ; When the alert is finished (the user pressed the mouse button), ; the LastAlert field in ExecBase is cleared to -1, and the longword ; at location 0 is cleared to zero. @d @40dDelay for a while. @d @d @d @40dGet the alert number. @40dCompare to -1 (indicates no alert). @d @40dExit if equal. @40dReserve 200 bytes of stack space. @40dPoint to base of reserved area. ; Decide which alert message to use. @40dPoint to "Software Failure" string. @40dGet the alert number. @40dGet the general error number. @40dIs it an AG_NoMemory type of alert? @40dIf so, point to "Not enough Memory" @40dstring. @d @40dIf otherwise, check the dead-end @40dalert flag. If it is clear, but @40dthe Subsystem/General error fields @40dare not zero, then point to the @40d"Recoverable Alert" string. @40dCopy the string into the buffer. @40dPoint to "Press left mouse button..." @40dCopy the string into the buffer. @40dPut a zero into the buffer. @40dPoint to "Guru Meditation..." string. @40dPoint to alert data. @40dPoint to character output routine. @40dRawDoFmt() @40dPoint to "intuition.library". @40dMinimum version is 0 (any). @40dOpenLibrary() @40dDid it open OK? @40dIf not, skip the following. @40dSave ExecBase. @40dGet IntuitionBase. @40dTell intuition that the alert # is 0. @40dPoint to the alert string. @40dAlert should be 40 video lines high. @40dDisplayAlert() @d @40dGet IntuitionBase. @40dRestore ExecBase. @40dCloseLibrary() @40dDeallocate string space on stack. @40dRemove "HELP" at 0, if present. @d @40dSet LastAlert to -1. ; Set the return code. If intuition.library didn't open, A2 contains ; a non-zero address, so a non-zero return value results. If ; intuition could be called, this is the return code from the ; DisplayAlert() call. @40dSet return code. @d @d ; Character output routine for RawDoFmt() while putting together ; the "Guru Meditation #..." message. @40dPut character in buffer. @40dZero-terminate the buffer. @d ; Routine to copy a given string to the output buffer while ; building the guru message. @40dPut a zero into the buffer. @40dCopy the given string. @d @40dTerminate buffer with $FF. @d ; Strings used for the guru message. @8p@,22s @8p@,21s @8p@,22s @8p@,39s @8p@,31s ; Name used to open intuition to put up the alert. @8p@,18s ; The "alert.hook" mechanism ; -------------------------- ; This is a RomTag. At system startup, this will be found and added ; to the resident module list. Since it has the RTW_COLDSTART flag ; set, it will be initialized along with the other libraries, ; devices, etc. The RTF_AUTOINIT flag is not set, therefore, the ; code at RT_INIT is called. ; The code at RT_INIT is the Guru routine. This reads the LastAlert ; field in ExecBase. If this is not -1, then an alert is still ; outstanding (from before the reboot), and this puts it up, lets ; the user click the mouse button, then returns. The resident ; module initialization then continues, the DOS boots, and the ; system comes up. ; The purpose of all this is to allow the system to defer display ; of a guru message until after the system has been cold-started. @8p@,13s @a?fc323a @8p@24wRTC_MATCHWORD (start of ROMTAG marker) @8p@24lRT_MATCHTAG (pointer RTC_MATCHWORD) @8p@24lRT_ENDSKIP (pointer to end of code) @8p@24bRT_FLAGS (RTW_COLDSTART) @8p@24bRT_VERSION (33 decimal) @8p@24bRT_TYPE (NT_UNKNOWN) @8p@24bRT_PRI (priority = 5) @8p@24lRT_NAME (pointer to name) @8p@24lRT_IDSTRING (pointer to ID string) @8p@24lRT_INIT (execution address) ; ROM-Wack command dispatch tables ("Key bindings"). ; -------------------------------------------------- ; ROM-Wack can be in one of two modes when a key is pressed. It can ; be waiting for a command, or it can be in the process of gathering ; a multi character command. The two tables below decide what to do ; with the key in each case. ; Each table entry has 4 fields. These are, address of next table ; entry to try if key doesn't match this one, lowest key value for ; this table entry, highest key value (if several) or zero, address ; to jump to. ; Primary command dispatch table. @8p@9l@3b@3b@17l^D @8p@9l@3b@3b@17l<Return> @8p@9l@3b@3b@17l<Tab> @8p@9l@3b@3b@17l? @8p@9l@3b@3b@17l. @8p@9l@3b@3b@17l, @8p@9l@3b@3b@17l> @8p@9l@3b@3b@17l< @8p@9l@3b@3b@17l<Backspace> @8p@9l@3b@3b@17l<Space> @8p@9l@3b@3b@17l[ @8p@9l@3b@3b@17l] @8p@9l@3b@3b@17l: @8p@9l@3b@3b@17l+ @8p@9l@3b@3b@17l- @8p@9l@3b@3b@17l= @8p@9l@3b@3b@17l! @8p@9l@3b@3b@17l^ @8p@9l@3b@3b@17l_ @8p@9l@3b@3b@17l0 - 9 @8p@9l@3b@3b@17la - z @8p@9l@3b@3b@17lA - Z @8p@32lEnd of table marker. ; Secondary command dispatch table (used while a command ; and/or address is being typed). @8p@9l@3b@3b@17l<Backspace> @8p@9l@3b@3b@17l<Return> @8p@9l@3b@3b@17l<CTRL-X> @8p@9l@3b@3b@17l<CTRL-U> @8p@9l@3b@3b@17l> @8p@9l@3b@3b@17l< @8p@9l@3b@3b@17l<Space> @8p@9l@3b@3b@17l_ @8p@9l@3b@3b@17l0 - 9 @8p@9l@3b@3b@17la - z @8p@9l@3b@3b@17lA - Z @8p@32lEnd of table marker. ; Table of multi-character ROM-Wack commands. @8p@,22s @8p@,22s @8p@,22s @8p@,12s ; Dispatch table for multicharacter commands. ; The fields are link to next table entry, address of command string, ; an unused value (presumably from a larger version of Wack), and ; the address to jump to for that command. @a?fc33f4 @8p@9l@9l@5w@13l"alter" @8p@9l@9l@5w@13l"boot" @8p@9l@9l@5w@13l"clear" @8p@9l@9l@5w@13l"fill" @8p@9l@9l@5w@13l"find" @8p@9l@9l@5w@13l"go" @8p@9l@9l@5w@13l"ig" @8p@9l@9l@5w@13l"limit" @8p@9l@9l@5w@13l"list" @8p@9l@9l@5w@13l"regs" @8p@9l@9l@5w@13l"reset" @8p@9l@9l@5w@13l"resume" @8p@9l@9l@5w@13l"set" @8p@9l@9l@5w@13l"show" @8p@9l@9l@5w@13l"user" @8p@l @8p@32wPadding. ; That's it. The next RomTag (for the audio.device) comes right ; after the two bytes of padding shown above.