jans@tekecs.UUCP (Jan Steinman) (09/05/86)
#! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # README # article.lp # worm.s # This archive created: Fri Sep 5 10:30:46 1986 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'README'" '(691 characters)' if test -f 'README' then echo shar: "will not over-write existing file 'README'" else sed 's/^X //' << \SHAR_EOF > 'README' X This version of Worm will require modification on whatever system you X put it on. Most multi-user or multi-tasking systems (including X garden-variety Unixes) will not let you turn off interrupts, unless X they have real-time features. X X The Tektronix 4404 that this originally ran on had a modified OS for X the purpose. If you want to run this on a stock 4404, you can simply X comment out the lines labeled "System Dependent" in the Worm routine X -- the Worm will then run an amazing length of time, considering there X are at least 60 interrupts each second. X X In any event, the Worm in user space in a virtual memory system is X simply a curiosity. To be useful, it should work with real addresses. SHAR_EOF fi echo shar: "extracting 'article.lp'" '(11850 characters)' if test -f 'article.lp' then echo shar: "will not over-write existing file 'article.lp'" else sed 's/^X //' << \SHAR_EOF > 'article.lp' X (C) 1986 Jan W. Steinman and M&T Publishing, Inc. This article may be X copied for PERSONAL USE by any means, as long as credit is given to X the author and this copyright notice is included with the material. X M&T Publishing retains worldwide publication rights to this material. X X X X X X TTTThhhheeee WWWWoooorrrrmmmm MMMMeeeemmmmoooorrrryyyy TTTTeeeesssstttt X X X Jan W. Steinman X X 2002 Parkside Court X West Linn, OR 97068 X _t_e_k_t_r_o_n_i_x!_t_e_k_e_c_s!_j_a_n_s X 503/657-7703 (h) X 503/685-2956 (w) X X X X No, this is not a method for quantifying the mental X X retentive powers of certain long, cylindrical invertebrates, X X but a test that could help to diagnose certain types of X X computer memory errors. The Worm memory test uses a X X dynamically executing program as the actual test data. X X Unlike previous memory test programs of this type, this Worm X X has a special twist -- it is able to overlay itself _w_h_i_l_e _i_t X X _i_s _e_x_e_c_u_t_i_n_g, thanks to the MC68000 _p_r_e_f_e_t_c_h register. X X X SSSSoooommmmeeee FFFFeeeettttcccchhhhiiiinnnngggg FFFFaaaaccccttttssss X X X Never heard of the prefetch register? To understand X X how the Worm works, it might help to review the way that the X X MC68000 fetches and executes instructions. The MC68000 uses X X instruction pipelining in order to speed execution. There X X is, in effect, a sixteen-bit register between the data bus X X and the instruction decoding logic. (The MC68010 has 32 X X bits of prefetch, and the 68020 has a 64 entry instruction X X cache, but the results should be similar.) When an X X instruction is executed, the opcode for that instruction is X X first loaded into the prefetch register (often while the X X previously fetched instruction is being executed), then the X X X X X X X X X X - 2 - X X X instruction is moved into the instruction decoding register, X X where it is executed. The net effect is that the processor X X _u_s_u_a_l_l_y has a handle on the next thing it is supposed to do. X X X Prefetch works fine most of the time, but does slow X X things down during certain operations. If the instruction X X being executed causes a non-sequential instruction to be X X executed, execution may be either faster or slower. In the X X case of a conditional branch instruction, a branch _t_a_k_e_n is X X quite fast, because the prefetch register already holds the X X displacement that must be added to the program counter in X X order to fetch the next non-sequential instruction. A X X branch _n_o_t _t_a_k_e_n, however, will be a bit faster if it is a X X short branch, because the next instruction is already in the X X prefetch register, and the two clocks needed to add a X X displacement to the program counter can be saved. The worst X X case happens when a branch is _n_o_t _t_a_k_e_n and the branch X X displacement is sixteen bits. In this case, the processor X X has useless information in the prefetch register, and must X X flush that information before it can fetch the next X X instruction. X X X Other non-sequential instructions cause an immediate X X flush of the prefetch register, and use an extra four clocks X X simply to restart the pipeline. One exception is the X X decrement-and-branch instruction which, like the taken X X branches, benefits from having the branch displacement X X handy. (The MC68010, with its 32 bit prefetch register, X X X X X X X X X X X - 3 - X X X actually executes many 16 bit instructions out of the X X prefetch register if they precede a decrement-and-branch X X instruction.) X X X HHHHoooowwww TTTThhhheeee WWWWoooorrrrmmmm CCCCrrrraaaawwwwllllssss X X X The Worm depends on these characteristics of pipelining X X in order to overlay itself _w_h_i_l_e _i_t _i_s _r_u_n_n_i_n_g, but it needs X X some management and control in order to be useful -- a Worm X X on the loose would quickly destroy all of memory! Besides X X the Worm, a complete memory test requires two additional X X parts: an initialization sequence, and a routine for X X controlling the Worm and reporting its results. X X X The initialization routine has some special X X characteristics, and includes most of the system X X dependencies. It is only executed once, at the beginning, X X and is therefore throw-away code. This is why it is placed X X last -- the Worm actually crawls right over its X X initialization code in this implementation. The registers X X are set up to the specifications of the Worm, and several X X important system functions are performed. In particular, it X X is important that page faulting does not occur in systems X X that support virtual memory, and if special hocus-pocus is X X needed to turn off interrupts, it should be done here. X X X The Worm Manager exercises control over the Worm, and X X is responsible for communicating errors it discovers, and X X displaying progress messages, if desired. When the Manager X X X X X X X X X X X - 4 - X X X is entered upon completion of a Worm pass, it must decide if X X it has been entered because of an error, or simply as a X X point of control. If there has been an error, the Worm is X X no longer runnable, so the Manager will have to report the X X error and terminate. If no error is detected, the Manager X X must check the progress of the Worm to keep it from X X consuming all of memory. At this point, the Manager can X X decide enough memory has been checked to warrant a progress X X report of some kind. X X X The real heart of the whole thing is, after all, the X X Worm. The Worm simply replicates itself, one long-word X X lower in memory, while comparing the new copy of itself X X against the original, which never executes. The Worm may be X X the heart of the Worm test, but the three instructions X X starting at Crawl are where the magic happens. This loop X X starts at the beginning of Worm, and copies the first long X X word down to Worm-4. It continues with each additional long X X word, until it gets to the long word at Crawl+4, which is a X X DBNE instruction with its 16 bit displacement. The X X preceding MOVE.L and CMP.L have already been copied down. X X X At this point, it becomes a little difficult to keep X X track of what is data and what is code. When the MOVE.L is X X in the instruction decode register, ready to be executed, X X the following CMP.L is in the prefetch register, waiting its X X turn to be executed. When the MOVE.L at Crawl executes, it X X moves the DBNE instruction into the location it and the X X X X X X X X X X X - 5 - X X X following CMP.L, are currently occupying. The processor has X X no way of knowing it has just invalidated its prefetch X X register, so it continues, moving the CMP.L instruction into X X the instruction decode register, and moving the following X X DBNE into the prefetch register. The CMP.L executes, X X comparing the DBNE just moved against the original, while X X moving the branch displacement for the DBNE into the X X prefetch register. X X X Assuming the compare was successful, the DBNE executes, X X decrementing D0 and branching backward four bytes to where X X the MOVE.L used to be. The prefetch register is flushed X X because of the branch, so the value at that location is X X loaded into the prefetch register, and immediately into the X X instruction decode register. But what is loaded? A _c_o_p_y of X X the DBNE, complete with the same negative displacement X X value. The condition codes have not changed, and the count X X register D0 should not be anywhere near zero, so the copy of X X the DBNE gets executed identically to its predecessor, which X X still resides in the next long word. The DBNE copy branches X X to the MOVE.L copy, and the loop continues moving the code X X down four bytes. X X X When the count register D0 underflows, the DBNE copy X X drops through, interrupts are enabled, the Worm's dynamic X X image pointer A5 is adjusted to point to the new Worm copy, X X and the Worm reports back to its Manager. _N_o_t_e _t_h_a_t _n_o_n_e _o_f X X _t_h_e _W_o_r_m _c_o_d_e _i_s _e_v_e_r _e_x_e_c_u_t_e_d _b_e_f_o_r_e _i_t _h_a_s _b_e_e_n _c_o_m_p_a_r_e_d X X X X X X X X X X X - 6 - X X X _a_n_d _v_e_r_i_f_i_e_d. X X X ____________________________________________________________ X Before After X X Crawl-4 ... move.l (a0)+,(a1) X Crawl-2 ... cmp.l (a1)+,(a2)+ X Crawl move.l (a0)+,(a1) dbne d0,-6 <--+ X Crawl+2 cmp.l (a1)+,(a2)+ | X Crawl+4 dbne d0,-6 ---------------------------+ X ____________________________________________________________ X X X It is vitally important to disable interrupts when the X X MOVE.L overlays itself and the following CMP.L. An X X interrupt at this point causes the prefetch to be flushed X X when the interrupt is serviced. Upon return from the X X interrupt, the displacement part of the DBNE (Hex FFFA) will X X be fetched as an instruction. This will cause a "line 1111 X X emulator exception" unless your system has a coprocessor X X with an id code of 7, but either way, _t_h_e _W_o_r_m _w_i_l_l _b_e X X _b_r_o_k_e_n _a_n_d _t_h_e _m_e_m_o_r_y _t_e_s_t _w_i_l_l _f_a_i_l. And of course, it is X X important that the Worm length remain a multiple of four if X X you decide to modify it! X X X BBBBuuuutttt WWWWhhhhaaaatttt GGGGoooooooodddd IIIIssss IIIItttt???? X X X I originally developed the MC68000 Worm test for an X X embedded processor application which was having dynamic RAM X X refresh problems. It was discovered that conventional RAM X X tests, which move smoothly up through consecutive addresses, X X were masking the problem by unintentionally providing X X software refresh. The Worm is not long enough to cause a X X complete cycle of all of a dynamic RAMs _r_o_w-_a_d_d_r_e_s_s-_s_t_r_o_b_e X X X X X X X X X X X - 7 - X X X (RAS) lines, and was able to help diagnose the problem. X X X In the form presented, this implementation of the Worm X X is primarily useful as an illustrative example of position- X X independent coding, modular design, and of course, a unique X X use of the prefetch register. It could be put to practical X X use in a number of ways. X X X The best use of the Worm might be to have it running X X continuously, as a very low priority task. The Manager X X would have to take some of the responsibility of Init by X X allocating test memory and restarting the Worm when it X X finishes testing a buffer. The interrupt disabling code may X X be simpler on systems without virtual memory -- on the Amiga X X it is a simple memory store. X X X Virtual memory systems would also need to add code to X X branch around the interrupt disabling code on the copy of X X the first long word only, which would allow the Worm to X X generate page faults whenever it first crosses a page X X boundary. To make it practical in such systems, the Manager X X would have to access the memory management hardware in order X X to map faulty virtual locations to broken chips. X X X The Worm routine itself can hold much more code if X X desired. I originally had much of the Manager's decision X X code in the Worm, which did speed it up at the expense of X X simplicity. In a message-based system, such as the Amiga, X X the Manager could be totally deleted. The Worm could X X X X X X X X X X X - 8 - X X X contain all the task code, merrily crawling through any X X available RAM it could find and sending error reports X X through inter-task messages, all with minimal impact on the X X user. X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X SHAR_EOF fi echo shar: "extracting 'worm.s'" '(17616 characters)' if test -f 'worm.s' then echo shar: "will not over-write existing file 'worm.s'" else sed 's/^X //' << \SHAR_EOF > 'worm.s' X INFO $Header: listing.1,v 1.1 86/07/29 17:59:32 jans Exp $ X **** The Worm Memory Test ****************************************** X * Author: Jan W. Steinman, 2002 Parkside Ct., West Linn, OR 97068. X * X * (C) 1986 Jan W. Steinman and M&T Publishing, Inc. This article may be X * copied for PERSONAL USE by any means, as long as credit is given to X * the author and this copyright notice is included with the material. X * M&T Publishing retains worldwide publication rights to this material. X * X * The Worm memory test has three parts. Init sets up the registers for the X * Worm. The Display Manager interacts with the Worm each pass and periodically X * Displays the Worm's progress. The Worm itself Worms itself through memory, X * from high to low, checking memory against a copy of itself. The Droppings X * form a pattern through memory when the test is complete. X * X * This version runs on the Tektronix 4404 under Uniflex. System dependent code X * is mostly segregated to the Init, Display, Disable and Enable routines. Two X * instructions in the Worm routine are system dependent, for enabling and X * disabling interrupts. X * X * Register usage: X * D0 scratch register. X * D1 scratch register. X * D2 scratch register. X * D3 scratch register. X * D4 X * D5 address mask for determining if time to show progress. X * D6 base of memory area under test. X * D7 length of Worm in long words. X * A0 scratch register. X * A1 scratch register. X * A2 scratch register. X * A3 pointer to Display manager for position independent access. X * A4 pointer to permanent Worm image for comparison. X * A5 pointer to crawling Worm image. X * A6 X * A7 stack pointer. X * X * These included files contain system definitions and interrupt (signal) X * numbers for the Uniflex operating system. Don't bother to list these. X * X OPT lis X DEFINE (This makes all labels global for debug.) X * X * Set D_MASK with the bits which are zero at each progress report. X * X D_MASK EQU $00003FC Report each boundary passed. X REL_SIZ EQU 4 Relocation is four bytes at a time. X MEM_SIZ EQU $2000*REL_SIZ Test a 32k chunk. X DISABLE EQU 2 Trap number for Disable routine. X ENABLE EQU 3 Trap number for Enable routine. X CR EQU $0D Carriage return. X LF EQU $0A Line feed. X * X * Uniflex will not allow inter-section math, so put all the code in the DATA X * section, and don't use TEXT or BSS at all! X * X DATA Assemble into writable data section. X MemBeg EQU * X X **** Hexidecimalize ****************************************************** X * Hexidecimalize converts a long word to eight ASCII hexidecimal characters. X * This routine is machine and OS independent. It uses a simple table look-up X * to generate the hexidecimal string. X * X * Entry: d0 -- Long word to be converted to hex. X * a0 -- Pointer to buffer where hex characters will go. X * X * Exit: d2 -- -1. (Just in case someone cares!) X * d0 -- unchanged. X * -8(a0) -- points to eight ASCII characters. X * X * Uses: d3 -- nybble mask: constant $0F. X * d2 -- nybble counter. X * d1 -- current nybble to convert is LSN. X * X CharTab DC.B '0123456789ABCDEF' Where we keep our hex characters. X Hexidecimalize X move.l #7,d2 Bytes to make - 1. X move.l #$0F,d3 Nybble mask. X HexLoop rol.l #4,d0 Shift the next nybble into the LSN, <--------+ X move.l d0,d1 make a copy for masking, | X and.l d3,d1 mask out all but least significant nybble, | X * index into char table and store result. | X move.b CharTab(pc,d1),(a0)+ | X dbra d2,HexLoop Repeat until done, and when done, -----------+ X rts hit the road, Jack. --> X X **** Manager ************************************************************* X * Manager checks the Worm's progress, and periodically reports to the Display. X * This routine is also entered if an error is encountered. X * X * Entry: d0 -- W_LONGS complement of pass count if error, else -1. X * a1 -- test address pass/fail value. X * X * Exit: via direct jump to Worm at (A5). X * X * Uses: d3, d2, d1, d0, a7, a1, a0 X * X * Stack: one level, plus needs of Display. X * X ErrMsg DC.B CR,'Worm reports memory error at ' X ErrAddrMsg X DC.B '00000000 on pass ' X ErrCountMsg X DC.B '00000000.',CR X E_SIZ EQU *-ErrMsg X DoneMsg DC.B CR,'Worm tested memory from ' X DoneBegAddrMsg X DC.B '00000000 through ' X DoneEndAddrMsg X DC.B '00000000 successfully.',CR X D_SIZ EQU *-DoneMsg X ProgMsg DC.B '00000000',CR X P_SIZ EQU *-ProgMsg X EVEN (Stay on legal instruction boundary.) X Manager tst.w d0 Was loop exited by error, or countdown? X bpl.s GetErrMsg Error, go report it. -----------------------+ X cmp.l a5,d6 Countdown, so are we done yet? | X beq.s GetDoneMsg Yes. Go finish up. --------------------+ | X move.l a5,d0 No, put the new source where we can | | X and.l d5,d0 look at the bottom bits: on boundary? | | X beq.s Report Yes, set up for progress report. ---|+| X jmp (a5) No. Keep on Crawlin'... --> ||| X * Finish up. Get the pointer to start addr, ||| X GetDoneMsg lea DoneBegAddrMsg(pc),a0 <----------------------------------+|| X move.l a1,d0 and the value to plug in, || X bsr Hexidecimalize which gets converted, likewise, get || X lea DoneEndAddrMsg(pc),a0 || X move.l #MEM_SIZ,d0 the end address and its value, || X bsr Hexidecimalize also converted to hexAscii. || X lea DoneMsg(pc),a0 Get pointer to complete done message, || X move.l #D_SIZ,d3 length of the done message, || X pea Exit(pc) push a return pointer, || X bra.s Display and go display the message. --------------+|| X * Make an error report. Get message ptr, ||| X GetErrMsg lea ErrCountMsg(pc),a0 <-------------------------------------||+ X sub.b #W_LONGS-1,d0 convert worm count to a pass count, || X bsr Hexidecimalize make it hex for Display. <--> || X * Get addr of ASCII error addr, || X lea ErrAddrMsg(pc),a0 || X move.l #-4,d0 get bad long addr to display, || X add.l a1,d0 less four to account for postincrement, || X bsr Hexidecimalize make it hex for Display. <--> || X lea ErrMsg(pc),a0 Get pointer to whole err msg, || X move.l #E_SIZ,d3 the size for the write, || X pea Exit(pc) push a return pointer, || X bra.s Display and Display the message. -----------------+| X * Progress report. Get message ptr, || X Report lea ProgMsg(pc),a0 <-----------------------------------------|+ X move.l a5,d0 load the checked address, | X bsr Hexidecimalize make it hex for Display. <--> | X sub.l #8,a0 Regain pointer to the message, | X move.l #P_SIZ,d3 get the size for the write, | X pea (a5) push a return ptr to the new Worm, | X * and drop through into Display. v X X **** Display ************************************************************** X * Display is an implementation dependent scheme for reporting the Worm's X * progress. Upon entry, A0 contains a pointer to a string to Display, and D3 X * contains the length of the string to Display. X * X * Entry: d3 -- number of bytes to display. X * a0 -- address of a string to display. X * X * Uses: d0 -- file descriptor of stdout. X * a1 -- scratch register for pointing to SysCall param block. X * X * Stack: as needed by system call. X * X ******** B E G I N S Y S T E M - D E P E N D E N T C O D E ******** X Display move.l d3,-(a7) Load the byte count, <---------------------+ X move.l a0,-(a7) the actual string pointer, X move.w #write,-(a7) and the system call index, X move.l a7,a0 point to the syscall parameter block, X move.l #1,d0 load file descriptor for stdout, X SYS indx and write the message. <--> X add.l #10,a7 Remove the params from the stack, and X rts return somewhere. --> X * X * For lack of a better place to put it, the system dependent exit code is here. X * X Exit SYS term Terminate this program. (System dependent.) X ******** E N D S Y S T E M - D E P E N D E N T C O D E ******** X X **** Disable, Enable ******************************************************* X * These routines provide the exclusion mechanism for the non-interruptible code X * in Worm at Crawl. These routines must execute in supervisor state, therefore X * they are executed via the TRAP exception instruction. Enable requires that X * D1 be preserved from the preceding Disable. X * X * Uses: SR -- interrupt mask is raised and lowered. X * d2 -- scratch register for restoring original interrupt mask. X * d1 -- scratch register storage place for old interrupt mask. X * X ******** B E G I N S Y S T E M - D E P E N D E N T C O D E ******** X Disable move sr,10 Grab the status register, X and.w #$0300,d1 keep only the interrupt bits, X and #$0300,sr and disable all interrupts X SYS cpint,SIGTRAP2,Disable <--> X rtr before entering critical code region. --> X X Enable move sr,d2 Regain the status register, X or.w d1,d2 reset the previous interrupt level, X move d2,sr and enable the proper interrupts X SYS cpint,SIGTRAP3,Enable <--> X rtr before exiting critical code region. --> X ******** E N D S Y S T E M - D E P E N D E N T C O D E ******** X X **** Worm ************************************************************** X * Worm is a self-modifying, self-relocating procedure which starts at some X * location in high memory and works its way down to its end address, X * periodically reporting its progress. X * X * The loop at Crawl depends strongly on the 68000 prefetch mechanism. This X * loop will not work on a 68020 machine (which has a 64 entry cache), nor on X * most simulators (which often do not bother to simulate prefetch accurately). X * This loop will also not work with the TRACE bit set, and must be protected X * from all interrupts, including page faults in virtual memory systems. X * X * When this loop moves the DBNE long word at Crawl+4, it overlays the MOVE.L X * and the CMPM.L at Crawl. The CMPM.L is in the prefetch queue, so it gets X * executed even though its memory image has just been clobbered. The DBNE is X * fetched, and its execution flushes the prefetch queue as is the case with all X * branches. Execution continues with the copy of the DBNE just moved, which X * executes again, branching to Crawl-4, the new loop location. Note that the X * loop count gets decremented twice in this scenario, removing the need for the X * usual predecrement before entering the loop. X * X * X * Entry: d7 -- length of Worm in long words. X * d6 -- base of memory area to test. X * d5 -- address mask for display boundary. X * a5 -- first long word address of Worm at present. X * a4 -- first long word address of Worm's original image. X * a3 -- display manager's address. X * X * Exit: d0 -- W_LONGS complement of pass count if error. X * a5 -- entry value less relocation, i.e.: next pass entry value. X * a1 -- address pass/fail report value. X * X * Uses: d0 -- decrementing Worm length. X * a2 -- incrementing COMPARE address. X * a1 -- incrementing TO address. X * a0 -- incrementing FROM address. X * X * Unused: d4, d3, a7, a6. X * X Worm move.w d7,d0 Restore the Worm's length, X move.l a5,a0 its starting point, X move.l a4,a2 and its original address. X lea -4(a5),a1 Get the destination for this pass. X ******** B E G I N S Y S T E M - D E P E N D E N T C O D E ******** X trap #DISABLE Don't interrupt this critical passage! <--> X ******** E N D S Y S T E M - D E P E N D E N T C O D E ******** X Crawl move.l (a0)+,(a1) Move a long word piece of Worm, <-------+ X cmp.l (a1)+,(a2)+ and check it against the original, | X dbne d0,Crawl one long word at a time. -------------+ X ******** B E G I N S Y S T E M - D E P E N D E N T C O D E ******** X trap #ENABLE Allow interrupts -- critical section over. <--> X ******** E N D S Y S T E M - D E P E N D E N T C O D E ******** X sub.l #REL_SIZ,a5 Update the new Worm address, X nop keep the whole thing on long boundary, X jmp (a3) report to the Manager. --> X * X * The following pattern (which is notoriously hard on 16 bit dynamic RAM X * memories) gets left in memory and can be checked later if desired. X * X Droppings X DC.L $5555AAAA Pattern to be left in RAM. X W_SIZ EQU *-Worm Length of self-relocating code, in bytes X W_LONGS EQU W_SIZ/4 and longs. X X **** Init ************************************************************** X * Init performs system-dependent initialization and sets up registers for use X * of Worm and Manager. Init then copies the Worm into the top of test memory X * and starts the Worm crawling. X * X * Entry: not applicable. X * X * Exit: a5 -- Worm's test image address at top of memory to be tested. X * a4 -- Worm's permanent image address. X * a3 -- Manager routine pointer. X * d7 -- length of Worm in long words. X * d6 -- base of memory area to test. X * d5 -- address mask for testing display boundary. X * X Ovrly EQU * This area will be overlaid with the worm. X LogMsg DC.B 'Worm memory tester, ' X DC.B '$Header: listing.1,v 1.1 86/07/29 17:59:32 jans Exp $' X DC.B CR,'Memory checked down to location:',CR X L_SIZ EQU *-LogMsg X X EVEN X GLOBAL Init X Init X * X * First, perform some system-dependent initialization: set up the TRAPs needed X * to protect the Worm from interrupts, protect the area to be tested from page X * faults, and write a welcome message. X * X ******** B E G I N S Y S T E M - D E P E N D E N T C O D E ******** X SYS cpint,SIGTRAP2,Disable Set up the exception handlers for the X SYS cpint,SIGTRAP3,Enable interrupt exclusion routines. X SYS memman,1,MemBeg,MemEnd Protect memory image from page faults. X move.l #1,d0 Prepare and write a stdout X SYS write,LogMsg,L_SIZ welcome message. X ******** E N D S Y S T E M - D E P E N D E N T C O D E ******** X * X * Next, set up registers that will be used by the Worm and Manager. X * X move.l #D_MASK,d5 Get the Display address boundary mask. X lea Ovrly(pc),a0 Load the lowest address to test X move.l a0,d6 into a data register for comparison, X lea Manager(pc),a3 get the Display Manager's address, X lea Worm(pc),a4 the Worm's non-crawling image address, X move.l #MemEnd-W_SIZ,a5 and the high-mem Worm start address. X move.w #W_LONGS,d7 Get the Worm's length in longs. X * X * Finally, move the Worm to the top of memory to be tested. X * X move.l a4,a0 Get a copy of Worm's permanent image pointer, X move.l a5,a1 its test image pointer, X move.w d7,d0 and its length in longs. X sub.w #1,d0 X MoveWorm move.l (a0),(a1) Move, and compare <-------------+ X cmp.l (a0)+,(a1)+ a long word of the Worm | X dbne d0,MoveWorm at a time. -------------------+ X tst.w d0 Exit loop by error, or countdown? X bpl Manager Error, go Report it. --> X jmp (a5) Countdown. Start Crawling! --> X C_SIZ EQU *-MemBeg (Size of non-relocating code.) X X DS.B MEM_SIZ-C_SIZ X MemEnd EQU * X ENDDEF X END Init (Set transfer address to the Init.) SHAR_EOF fi exit 0 # End of shell archive -- :::::: Artificial Intelligence Machines --- Smalltalk Project :::::: :::::: Jan Steinman Box 1000, MS 60-405 (w)503/685-2956 :::::: :::::: tektronix!tekecs!jans Wilsonville, OR 97070 (h)503/657-7703 ::::::