[net.sources] Worm Memory Test for 68000, 68010.

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 ::::::