Worm Memory Test for 68000, 68010.
Jan Steinman
jans at tekecs.UUCP
Sat Sep 6 03:38:51 AEST 1986
#! /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 ::::::
More information about the Comp.sources.unix
mailing list