[comp.sys.amiga] Graphical Monitor of CPU-Blitter-Memory Use. 3 of 3

jvkelley@watcgl.waterloo.edu (Jeff Kelley) (10/30/87)

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by watcgl!jvkelley on Sat Oct 17 20:18:34 EDT 1987
# Contents:  README load.doc load.device.uue makefile asmsupp.i load.i macros.i
#	device.a load.h
 
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'
This is an Amiga device, called "load.device", which measures CPU, blitter,
and memory usage.  

Instructions:
   Casual User:
	Copy load.device to your "devs:" directory.

   Dedicated Programmer:
	Copy "load.h" and "load.i" to your "include:devices/" directory.
	Read "load.doc".	


Comments, suggestions, and bug reports welcome.
Jeff Kelley
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
UUCP:     {ihnp4,watmath}!watcgl!jvkelley |  Postal Address:
BITNET:   jvkelley@cgl.waterloo.bitnet    |     Jeff Kelley
INTERNET: jvkelley@cgl.waterloo.edu       |     Dept. of Computer Science
EAN:      jvkelley@cgl.waterloo.cdn       |     University of Waterloo
                                          |     Waterloo, Ont., Canada
                                          |     N2L 3G1
					  |  	tel:   (519) 578-4514
@//E*O*F README//
chmod u=rw,g=r,o=r README
 
echo x - load.doc
sed 's/^@//' > "load.doc" <<'@//E*O*F load.doc//'


TABLE OF CONTENTS

load.device/structure/LoadBase
load.device/command/Flush
load.device/command/Read
load.device/command/Reset
load.device/command/Set
load.device/command/Start
load.device/command/Stop
load.device/function/AbortIO
load.device/function/BeginIO
load.device/function/CloseDevice
load.device/function/OpenDevice


load.device/structure/LoadBase			  load.device/command/LoadBase

   NAME
       LoadBase - the load device data structure

   DESCRIPTION
       The io_Device field of the loadrequest structure points to the
       LoadBase structure.  This structure should not be modified except
       indirectly as a result of executing a device command.  It is
       permissible to read the data structure (using Forbid/Permit as
       appropriate), but the only values of any interest are probably
	    ld_max_chip - the maximum amount of chip memory available at
			  the time the load device was first loaded.
	    ld_max_fast - as above, but for fast memory.

   SEE ALSO
       devices/load.h
       devices/load.i

load.device/command/Flush			  load.device/command/Flush

   NAME
       Flush -- clear all queued I/O requests for the load task

   FUNCTION
       This command purges the read request queue for the load
       task.

   IO REQUEST
       io_Message      mn_ReplyPort initialized
       io_Device       set by OpenDevice
       io_Command      CMD_FLUSH

   RESULTS
       io_Error -- if the Flush succeded, then io_Error will be 0.
		   If the Flush failed, then the io_Error will be non-zero.
		   (No known reason for it to fail.)

   SEE ALSO
       load.device/command/Read

load.device/command/Read			   load.device/command/Read

   NAME
       Read -- read load values collected by load task

   FUNCTION
       This command reads the load values of the CPU, blitter, chip
       memory, and fast memory.

   IO REQUEST
       io_Message      mn_ReplyPort initialized
       io_Device       set by OpenDevice
       io_Command      CMD_READ
       io_Flags        IOF_QUICK if quick I/O desired

   RESULTS
       If the read succeeded, the following load values will be set:
       lr_load.lv_cpu	  -- The sum of the length of the ready queue as
			     measured at each tick during the interval.
			     This value may be greater than the number of
			     ticks in an interval, theoretically having no
			     upper bound (but try to tell that to your
			     poor little 68000!).
       lr_load.lv_blitter -- The number of ticks at which the blitter was
			     detected to be busy during the interval.
       lr_load.lv_chip	  -- The number of bytes of chip memory currently
			     in use.  To find the maximum chip memory
			     available, see the description of the
			     LoadBase structure.
       lr_load.lv_fast	  -- The number of bytes of fast memory currently
			     in use.  To find the maximum fast memory
			     available, see the description of the
			     LoadBase structure.

       io_Error -- If the Read succeeded, then io_Error will be 0.
		   If the Read failed, then the io_Error will be non-zero.
		   It may fail if IOF_QUICK is not set and a Flush or AbortIO
		   request is executed before the end of the current interval,
		   in which case the error will be IOERR_ABORTED.

   BUGS
       The length of the ready queue is measured, which may not be an
       accurate measurement of CPU usage.

       Time arithmetic is not currently used to ensure that an interval is
       really an interval.  That is, that due to delays between the time
       that the load.device task is signalled by the timer and when the
       task places its next timer request, the actual interval will end up
       being greater than the specified interval.

   SEE ALSO
       load.device/structure/LoadBase
       load.device/command/Flush
       load.device/function/AbortIO

load.device/command/Reset			  load.device/command/Reset

   NAME
       Reset -- reinitializes the load device parameters

   FUNCTION
       This command sets the interval time, the number of ticks per interval,
       and the priority of the load task to their default values.

   IO REQUEST
       io_Message      mn_ReplyPort initialized
       io_Device       set by OpenDevice
       io_Command      CMD_RESET

   RESULTS
       io_Error -- If the Reset succeeded, then io_Error will be 0.
		   If the Reset failed, then the io_Error will be non-zero.
		(No known reason for it to fail.)
       If the Reset succeeded, then the lr_interval, lr_ticks, and lr_pri
       fields of the loadrequest will contain the default settings of
       those parameters.

   SEE ALSO
       load.device/command/Set
       load.device/function/OpenDevice

load.device/command/Set 			      load.device/command/Set

   NAME
       Set -- change load device parameters

   FUNCTION
       This command sets the interval time, number of ticks per interval,
       and priority of the load task to values specified in the load IO
       request.

   IO REQUEST
       io_Message      mn_ReplyPort initialized
       io_Command      LD_SET
       lr_interval     A word indicating the sampling period time (in seconds),
		       during which a number of samples may be taken.
       lr_ticks        A word indicating the number of samples to be taken
		       during each interval.
       lr_pri	       A byte indicating the priority of the load task.
	    NOTE: The last three fields are initialized when OpenDevice
		  is called to indicate the devices current configuration.

   RESULTS
       io_Error -- 0 if the command succeeded, otherwise an error number.
		   (No known reason for it to fail.)

   SEE ALSO
       load.device/command/Reset
       load.device/function/OpenDevice

load.device/command/Start			  load.device/command/Start

   NAME
       Start -- restart the paused load task.

   FUNCTION
       Restarts the paused load task, but not if other Stop commands
       have been performed without corresponding Start commands.

   IO REQUEST
       io_Message      mn_ReplyPort initialized
       io_Device       set by OpenDevice
       io_Command      CMD_START

   RESULTS
       io_Error -- 0 if the start succeeded, an error number otherwise.
		   The only possible error is LDERR_NOT_STOPPED, if the
		   device was not currently in the stopped state.

   SEE ALSO
       load.device/command/Stop

load.device/command/Stop			   load.device/command/Stop

   NAME
       Stop -- stop the load task from executing

   FUNCTION
       Stops the load task at the end of the current interval if it is not
       already stopped, otherwise increments the number of Stop commands
       that must be cancelled by Start commands before the load task will
       begin executing again.

   IO REQUEST
       io_Message      mn_ReplyPort initialized
       io_Device       set by OpenDevice
       io_Command      CMD_STOP

   RESULTS
       io_Error -- 0 if the Stop succeeded, an error number otherwise.
		   (No known reason for it to fail.)

   SEE ALSO
       load.device/command/Start

   BUGS
       If 255 Stop commands are already outstanding, executing
       another Stop command will cause a counter to wrap around, and
       undesirable behaviour may result.

load.device/function/AbortIO		       load.device/function/AbortIO

   NAME
       AbortIO -- abort an I/O request

   SYNOPSIS
       AbortIO(ioRequest)

   FUNCTION
       This function aborts a specified read request
       If the request is queued, it is painlessly removed.

   INPUTS
       iORequest  -- pointer to the IORequest Block that is to be aborted.

   RESULTS
       io_Error -- IOERR_ABORTED if the AbortIO succeeded.
		   0 if the AbortIO failed (the request was not in the queue).

   SEE ALSO
	load.device/function/BeginIO

load.device/function/BeginIO		       load.device/function/BeginIO

   NAME
       BeginIO -- start up an I/O process

   FUNCTION
       This function initiates a I/O request made to the load
       device. Other than read, the functions are performed
       synchronously, and do not depend on any interrupt handling
       logic (or it's associated discontinuities), and hence, if so
       selected, can be performed as IO_QUICK.
       Read requests that are not flagged as IO_QUICK are queued and
       replied to by the load task at the end of the next interval.
       Completion is signalled via the standard ReplyMsg routine.

   INPUTS
       iORequest  -- pointer to an I/O Request Block of size
	   sizeof(struct loadrequest) (see devices/load.h for
	   definition), containing a valid command in io_Command
	   to process, as well as the command's other required parameters.
	   The io_Device field must have been initialized during the
	   OpenDevice call to point to the load.device.

   RESULTS
       Error -- if the BeginIO succeded, then Error will be 0.
		If the BeginIO failed, then the Error will be non-zero.
		Possible errors:
		    IOERR_NOCMD   - Invalid command attempted.
		    IOERR_ABORTED - The request was aborted before completion.

   SEE ALSO
	devices/load.h
	devices/load.i

load.device/function/CloseDevice	   load.device/function/CloseDevice

   NAME
       CloseDevice -- close the load device

   SYNOPSIS
       CloseDevice(ioRequest)

   FUNCTION
       Close the device previously opened by calling OpenDevice.

   INPUTS
       ioRequest - pointer to a load request initialized by a call to
		   OpenDevice.

   SEE ALSO
       load.device/function/OpenDevice

load.device/function/OpenDevice 	    load.device/function/OpenDevice

   NAME
       OpenDevice -- Request an opening of the load device.

   SYNOPSIS
       error = OpenDevice(loadname, unit, ioRequest, flags)

   FUNCTION
       Open the load device and initialize the ioRequest.

   INPUTS
       loadname 	- pointer to literal string "load.device"
       unit		- ignored (should be 0).
       ioRequest	- pointer to an ioRequest block of size
			  sizeof(loadrequest) (see devices/load.i,h
			  for size/definition) to be
			  initialized by the OpenDevice routine.
       flags		- LDF_OPEN_EXCL if exclusive access to the load
			  device is desired, othewise 0.

   RESULTS
       error	 - 0 if the OpenDevice succeeded, an error number otherwise.
		    Possible errors:
			IOERR_OPENFAIL - generic
			LDERR_ACCESS_DENIED - the device is currently opened
			   by a task with exclusive access.
			LDERR_IN_USE - an attempt was made to obtain exclusive
			   access to the device but it has already been
			   opened by another task.

       If the OpenDevice succeeded, the io_Device field of the ioRequest
       structure will be initialized to point to the load.device.  The
       three load device parameters will contain their current settings:
	   lr_interval - The number of seconds in each interval.
	   lr_ticks    - The number of ticks in each interval.	Measurements
			 of CPU usage (ready queue length) and blitter usage
			 are taken at each tick during an interval, and at
			 the end of the interval queued read requests are
			 replied to.
	   lr_pri      - The priority of the load.device task.	Note that
			 CPU usage of tasks running at a priority greater
			 than this cannot be measured, and those at an
			 equal priority are not accurately measured.

   SEE ALSO
	load.device/function/CloseDevice
	devices/load.h
	devices/load.i

@//E*O*F load.doc//
chmod u=rw,g=r,o=r load.doc
 
echo x - load.device.uue
sed 's/^@//' > "load.device.uue" <<'@//E*O*F load.device.uue//'
begin 644 load.device
M   #\P         !              %G   #Z0   6=P $YU2OP    $   %
MG( ! P     K    -P   %IT:6UE<BYD979I8V4 ;&]A9"YD979I8V4 ;&]A
M9"YD979I8V4@,2XP("@Q-B!/8W1O8F5R(#$Y.#<I#0    (.    :@   (8 
M  "R   !A    >0   (2         EX   )^_____^    @# ,    H    K
MX   #@8 T   %  !T   %@  P   &    #<     +PTJ0"M( > K3@'D0>T 
MFB%\    *P *$7P !  ($7P  @ .0>@ %""(6)!"J  $(4@ "#M\  4!Z#M\
M #T!ZB\.+$UA  -,+%]![0 B3J[]TD'M ").KOW,(&X!0B(09QX@*  8D*@ 
M% @H  $ #V<&T:T!]& $T:T!^"!!8-Y#[0"\$WP  0 ($WP &0 )(WP    K
M  I![0$8(T@ .M'\    R"-( #XC2  V0>D 2B"(6)!"J  $(4@ "$7Z Q@F
M?     !.KO[F( TJ7TYU""X   (-9E ( 0  9Q9*;@ @9PH3>?____L 'TYU
M".X   (-4FX ( QN  $ (&800>X (B\.+&X!Y$ZN_<8L7S-N >@ ,#-N >H 
M,A-N ,4 -$(I !]@!A-\ /H 'TYU0JD %%-N "!F($'N "(O#BQN >1.KOW,
M+%\(K@   @T(+@ #  YG F $< !.=2\-*DXL;0'D2FT (&<*".T  P .< !@
M+B\M > B34ZN_P1#[0"\3J[^X$/M %!.KOX^<  B33 M !"2P-!M !).KO\N
M(!\L32I?3G5"*0 ?,"D '.5 0?H 9M# L?P   +X901!^@!8(%!.T"\.+&X!
MY$ZN_WPL7R(N *X@02(09RJSR&<"8/0B"2!1(FD !"*((4D !"\.+&X!Y$ZN
M_W8L7R)!$WP _@ ?3G4O#BQN >1.KO]V+%]"*0 ?3G4   +X   #Z@   P  
M  +X   "^    O@   ,T   #:    Z8   /\$WP _0 ?3G4(*0   !YG&B-N
M ?P ("-N @  ("-N @0 *"-N @@ +& 00>X FB\.+&X!Y$ZN_I(L7TYU+PXL
M;@'D3J[_?"Q?2BX"#&800>X (B\.+&X!Y$ZN_<PL7U(N @PO#BQN >1.KO]V
M+%].=4HN @QF"!-\ /D 'V N+PXL;@'D3J[_?"Q?4RX"#&800>X (B\.+&X!
MY$ZN_<8L7R\.+&X!Y$ZN_W8L7TYU+PXL;@'D3J[_?"Q?0>X FB\.+&X!Y$ZN
M_HPL7TJ 9Q8B0!-\ /X 'R\.+&X!Y$ZN_H8L7V#6+PXL;@'D3J[_=BQ?3G4S
M?  % # S?  ] #(3?  9 #0O"4'N "(O#BQN >1.KOW,+%\B7SUI # !Z#UI
M #(!ZF$F$"D -$/N +PO#BQN >1.KO[4+%]![@ B+PXL;@'D3J[]QBQ?3G4O
M G( ,BX!Z#0N >K"_  4POS#4#!!0D%(08+"( %(03 (@,(R $*N >P,@0 /
M0D!M# 2!  ]"0%*N >Q@["U! ? D'TYU+'D    $2^_^)$7M ")'[0!X%WP 
M!  (%WP    .</].KOZV%T  #T'M +PG2  00>L %""(6)!"J  $(4@ "$GM
M % 9?  %  @I2P ..7P *  20?K[/B),< %R $ZN_D0@2DZN_<PT+0'J=@!X
M #E\  D '"EM >P ("EM ?  )"),3J[^.$ZN_X@B;@&6(!%G!E*#(D!@]DZN
M_X(P.0#?\ ((   .9P)2A%-"9P)@OB(\     DZN_R@J+0'TFH B/     1.
MKO\H+"T!^)R *T,!_"M$ @ K10($*T8""$'M )I.KOZ,2H!G&") (T, ("-$
M "0C10 H(T8 +$ZN_H9@W$'M ").KOW&8 #_4@   ^P    =          8 
M   *    $@   !8    :    7@   &(   !F    :@   &X   !R    =@  
M 'H   !^    D    *H   #$   !1    G    +0   "U    M@   +<   "
=X    N0   +H   "[    O    +T         _+0
 
end
@//E*O*F load.device.uue//
chmod u=rw,g=r,o=r load.device.uue
 
echo x - makefile
sed 's/^@//' > "makefile" <<'@//E*O*F makefile//'
load.device: device.o
	blink FROM device.o TO load.device LIB lib:amiga.lib

device.o: device.a include:devices/load.i
	asm -NL -Iinclude: device.a

include:devices/load.i: assem:include/devices/load.i
	cp assem:include/devices/load.i include:devices/load.i
	cp assem:include/devices/load.h include:devices/load.h

@//E*O*F makefile//
chmod u=rw,g=r,o=r makefile
 
echo x - asmsupp.i
sed 's/^@//' > "asmsupp.i" <<'@//E*O*F asmsupp.i//'
CLEAR	MACRO
	moveq.l #0,\1
	ENDM

CALLSYS MACRO
	jsr	_LVO\1(a6)
	ENDM

LINKSYS MACRO
	move.l	a6,-(sp)
	movea.l LD_sysbase(a6),a6
	jsr	_LVO\1(a6)
	movea.l (sp)+,a6
	ENDM

XLIB	MACRO
	XREF	_LVO\1
	ENDM

@//E*O*F asmsupp.i//
chmod u=rw,g=r,o=r asmsupp.i
 
echo x - load.i
sed 's/^@//' > "load.i" <<'@//E*O*F load.i//'
*----------------------------------------------------------------
*
*   Load Device Structure
*
*----------------------------------------------------------------
LOAD_STACK_SIZE      EQU 200
    STRUCTURE	LD,DD_SIZE
	STRUCT	LD_Semaphore,SS_SIZE
	STRUCT	LD_TimerRequest,IOTV_SIZE
	STRUCT	LD_TimerPort,MP_SIZE
	STRUCT	LD_Port,MP_SIZE
	STRUCT	LD_TaskCB,TC_SIZE
	STRUCT	LD_Stack,LOAD_STACK_SIZE
	ULONG	LD_seglist
	ULONG	LD_sysbase
	UWORD	LD_Interval	  ; Interval in seconds
	UWORD	LD_Ticks	  ; Number of measurements / time interval
	ULONG	LD_secs 	  ; seconds between ticks
	ULONG	LD_micro	  ; microseconds between ticks
	ULONG	LD_Max_Chip	  ; absolute maximum chip memory available
	ULONG	LD_Max_Fast	  ; absolute maximum fast memory available
	ULONG	LD_CPU
	ULONG	LD_BLITTER
	ULONG	LD_CHIP
	ULONG	LD_FAST
	UBYTE	LD_stop_count
	UBYTE	LD_flags
    LABEL	LD_SIZE

*----------------------------------------------------------------
*
*   Load Request Structure
*
*----------------------------------------------------------------

    STRUCTURE	LR,IO_SIZE
	ULONG	    LV_CPU
	ULONG	    LV_BLITTER
	ULONG	    LV_CHIP
	ULONG	    LV_FAST
	UWORD	    LV_INTERVAL
	UWORD	    LV_TICKS
	BYTE	    LV_PRI
    LABEL	LR_SIZE

LOADNAME    MACRO
	    DC.B    'load.device',0
	    ENDM

	BITDEF	    LD,OPEN_EXCL,0
LDERR_IN_USE	    EQU -5
LDERR_ACCESS_DENIED EQU -6
LDERR_NOT_STOPPED   EQU -7

	DEVINIT
	DEVCMD	LD_SET
@//E*O*F load.i//
chmod u=rw,g=r,o=r load.i
 
echo x - macros.i
sed 's/^@//' > "macros.i" <<'@//E*O*F macros.i//'
IF	    MACRO
ifok	    SET     0
	    IFC     \1,eq
	    bne.s   else\2
ifok	    SET     1
	    ENDC
	    IFC     \1,ne
	    beq.s   else\2
ifok	    SET     1
	    ENDC
	    IFC     \1,ge
	    blt.s   else\2
ifok	    SET     1
	    ENDC
	    IFC     \1,gt
	    ble.s   else\2
ifok	    SET     1
	    ENDC
	    IFC     \1,le
	    bgt.s   else\2
ifok	    SET     1
	    ENDC
	    IFC     \1,lt
	    bge.s   else\2
ifok	    SET     1
	    ENDC
	    IFEQ    ifok
	    FAIL    'Bad IF'
	    ENDC
	    ENDM

ELSE	    MACRO
	    bra.s   fi\1
else\1:
	    ENDM

FI	    MACRO
	    IFND    else\1
else\1:
	    ENDC
fi\1:
	    ENDM

DO	    MACRO
do\1:
	    ENDM

OD	    MACRO
	    bra.s   do\1
od\1:
	    ENDM

ODL	    MACRO
	    bra     do\1
od\1:
	    ENDM

WHILE	    MACRO
whilok	    SET     0
	    IFC     \1,eq
	    bne.s   od\2
whilok	    SET     1
	    ENDC
	    IFC     \1,ne
	    beq.s   od\2
whilok	    SET     1
	    ENDC
	    IFC     \1,ge
	    blt.s   od\2
whilok	    SET     1
	    ENDC
	    IFC     \1,gt
	    ble.s   od\2
whilok	    SET     1
	    ENDC
	    IFC     \1,le
	    bgt.s   od\2
whilok	    SET     1
	    ENDC
	    IFC     \1,lt
	    bge.s   od\2
whilok	    SET     1
	    ENDC
	    IFEQ    whilok
	    FAIL    'Bad WHILE'
	    ENDC
	    ENDM

UNTIL	    MACRO
	    b\1.s   od\2
	    ENDM

QUIF	    MACRO
	    b\1.s   od\2
	    ENDM
@//E*O*F macros.i//
chmod u=rw,g=r,o=r macros.i
 
echo x - device.a
sed 's/^@//' > "device.a" <<'@//E*O*F device.a//'
*
* Load Device	  by Jeff Kelley
*
*   You may give this software to anyone you please.  If you sell it for
*  a profit, you'll just have to live with your conscience.
*
*	     (in W.W. Howe V1.0.2 Assembler)
*
LOAD_TASK_PRIORITY  EQU 25
LOAD_PRIORITY	    EQU 0
MICROSPERSEC	EQU 1000000
INTERVAL	EQU 5  ; Default time interval length in seconds (sample period)
TICKS		EQU 61 ; Default number of ticks (when samples are taken)  per interval

	INCLUDE "exec/types.i"
	INCLUDE "exec/nodes.i"
	INCLUDE "exec/lists.i"
	INCLUDE "exec/ports.i"
	INCLUDE "exec/libraries.i"
	INCLUDE "exec/devices.i"
	INCLUDE "exec/memory.i"
	INCLUDE "exec/io.i"
	INCLUDE "exec/resident.i"
	INCLUDE "exec/tasks.i"
	INCLUDE "exec/errors.i"
	INCLUDE "exec/initializers.i"
	INCLUDE "exec/semaphores.i"
	INCLUDE "exec/execbase.i"
	INCLUDE "hardware/dmabits.i"
	INCLUDE "hardware/custom.i"
	INCLUDE "devices/timer.i"
	INCLUDE "devices/load.i"
	INCLUDE "asmsupp.i"
	INCLUDE "macros.i"

	XLIB	OpenDevice
	XLIB	OpenLibrary
	XLIB	AddDevice
	XLIB	AddTask
	XLIB	CloseDevice
	XLIB	Signal
	XLIB	Remove
	XLIB	RemTask
	XLIB	PutMsg
	XLIB	GetMsg
	XLIB	ReplyMsg
	XLIB	Wait
	XLIB	DoIO
	XLIB	AvailMem
	XLIB	FreeMem
	XLIB	Disable
	XLIB	Enable
	XLIB	Forbid
	XLIB	Permit
	XLIB	AllocSignal
	XLIB	InitSemaphore
	XLIB	ObtainSemaphore
	XLIB	ReleaseSemaphore
	XLIB	SetTaskPri
	XREF	_AbsExecBase
	XREF	_custom

	SECTION load.device,CODE

FirstAddress:
	CLEAR	d0
	rts

initDDescrip:
	DC.W	RTC_MATCHWORD
	DC.L	initDDescrip
	DC.L	EndCode
	DC.B	RTF_AUTOINIT
	DC.B	VERSION
	DC.B	NT_DEVICE
	DC.B	LOAD_PRIORITY
	DC.L	LoadName
	DC.L	idString
	DC.L	Init

VERSION EQU	1
REVISION EQU	0

TimerName   TIMERNAME
LoadName    LOADNAME

idString:
	DC.B	'load.device 1.0 (16 October 1987)',13,0

Init:
	DC.L	LD_SIZE
	DC.L	funcTable
	DC.L	dataTable
	DC.L	initRoutine

funcTable:
	DC.L	Open
	DC.L	Close
	DC.L	Expunge
	DC.L	FirstAddress	; Null Command (reserved for expansion)

	DC.L	BeginIO
	DC.L	AbortIO

	DC.L	-1

dataTable:
	INITBYTE    LN_TYPE,NT_DEVICE
	INITLONG    LN_NAME,LoadName
	INITBYTE    LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
	INITWORD    LIB_VERSION,VERSION
	INITWORD    LIB_REVISION,REVISION
	INITLONG    LIB_IDSTRING,idString
	DC.L	    0

*********************************************************************
*
* InitRoutine - initialize the load device structure
*
*	  D0 = Device Pointer
*	  A0 = Segment List
*	  A6 = SysBase
*	  If this routine returns non-zero, it will be linked into system
*	  device list.


initRoutine:
	move.l	a5,-(sp)
	movea.l d0,a5		; get device pointer into safe register

	move.l	a0,LD_seglist(a5)
	move.l	a6,LD_sysbase(a5)

*   Initialize the Message Port.
	lea	LD_Port(a5),a0
	move.l	#LoadName,LN_NAME(a0)
	move.b	#NT_MSGPORT,LN_TYPE(a0)
	move.b	#PA_IGNORE,MP_FLAGS(a0)
	lea	MP_MSGLIST(a0),a0
	NEWLIST a0

	move.w	#INTERVAL,LD_Interval(a5)
	move.w	#TICKS,LD_Ticks(a5)
	move.l	a6,-(sp)
	movea.l a5,a6
	bsr	ComputeDelay
	move.l	(sp)+,a6

*   Initialize the semaphore.
	lea	LD_Semaphore(a5),a0
	CALLSYS InitSemaphore	    ; InitSemaphore(signalSemaphore)
				    ;		    A0
	lea	LD_Semaphore(a5),a0
	CALLSYS ObtainSemaphore     ; ObtainSemaphore(signalSemaphore)
				    ;		      A0

*   Compute the maximum MEMF_CHIP and MEMF_FAST
	move.l	MemList+LH_HEAD(a6),a0 ; get pointer to first MemHeader node
	DO	addmemory
	    move.l  (a0),d1	       ; check LN_SUCC
	UNTIL	eq,addmemory
	    move.l  MH_UPPER(a0),d0
	    sub.l   MH_LOWER(a0),d0
	    btst    #MEMB_CHIP,MH_ATTRIBUTES+1(a0)
	    IF	    ne,chipmemory
		add.l	d0,LD_Max_Chip(a5)
	    ELSE    chipmemory
		add.l	d0,LD_Max_Fast(a5)
	    FI	    chipmemory
	    move.l  d1,a0
	OD	addmemory

* Create the Load Device task.
	lea	LD_TaskCB(a5),a1
	move.b	#NT_TASK,LN_TYPE(a1)
	move.b	#LOAD_TASK_PRIORITY,LN_PRI(a1)
	move.l	#LoadName,LN_NAME(a1)
	lea	LD_Stack(a5),a0
	move.l	a0,TC_SPLOWER(a1)
	add.l	#LOAD_STACK_SIZE,a0
	move.l	a0,TC_SPUPPER(a1)
	move.l	a0,TC_SPREG(a1)
	lea	TC_MEMENTRY(a1),a0
	NEWLIST a0
	lea	Task_Start,a2
	movea.l #0,a3
	CALLSYS AddTask 	    ; AddTask(taskCB, initialPC, finalPC)
				    ;	      A1      A2	 A3
	move.l	a5,d0		    ; Return device pointer

	move.l	(sp)+,a5	    ; restore a5
	rts

***************************************************************************
*
* Open
*
*	  A6 = Device
*	  A1 = IOB
*	  D0 = Unit Number
*	  D1 = Flags
*  Return: Set IO_ERROR Field to indicate success/errno

Open:
	btst	#LDB_OPEN_EXCL,LD_flags(a6)
	IF	eq,shared_access_ok
	    btst    #LDB_OPEN_EXCL,d1
	    IF	    ne,want_exclusive
		tst.w	LIB_OPENCNT(a6)
		IF	ne,device_in_use
		    move.b  LDERR_IN_USE,IO_ERROR(a1)
		    rts
		FI	device_in_use
		bset	#LDB_OPEN_EXCL,LD_flags(a6)
	    FI	    want_exclusive
	    addq.w  #1,LIB_OPENCNT(a6)
	    cmpi.w  #1,LIB_OPENCNT(a6)
	    IF	    eq,startup
		lea	LD_Semaphore(a6),a0
		LINKSYS ReleaseSemaphore    ; ReleaseSemaphore(signalSemaphore)
					    ;		       A0
	    FI	    startup
	    move.w  LD_Interval(a6),LV_INTERVAL(a1)
	    move.w  LD_Ticks(a6),LV_TICKS(a1)
	    move.b  LD_TaskCB+LN_PRI(a6),LV_PRI(a1)
	    clr.b   IO_ERROR(a1)
	ELSE	shared_access_ok
	    move.b  #LDERR_ACCESS_DENIED, IO_ERROR(a1)
	FI	shared_access_ok
	rts

***************************************************************************
*
* Close
*
*	  A6 = Device
*	  A1 = IOB
*  Return: If the device is no longer open and a delayed expunge is pending,
*	   do the expunge and return the segment list.	Else return NULL.

Close:
	clr.l	IO_DEVICE(a1)
	subq.w	#1,LIB_OPENCNT(a6)
	IF	eq,do_close
	    lea     LD_Semaphore(a6),a0
	    LINKSYS ObtainSemaphore	; ObtainSemaphore(signalSemaphore)
					;		  A0
	    bclr    #LDB_OPEN_EXCL,LD_flags(a6)
	    btst    #LIBB_DELEXP,LIB_FLAGS(a6)
	    IF	    ne,go_expunge
		bra.s	Expunge
	    FI	    go_expunge
	FI	do_close
	CLEAR	d0
	rts

****************************************************************************
*
* Expunge
*
*	  A6 = Device
*  Return: If the device is no longer open return seg list. Else set
*	   delayed Expunge flag and return NULL.
Expunge:

	move.l	a5,-(sp)
	move.l	a6,a5
	move.l	LD_sysbase(a5),a6
*	 Check to see if anyone has us open

	tst.w	LIB_OPENCNT(a5)
	IF	ne,still_open

*	It is still open, set delayed Expunge flag

	    bset    #LIBB_DELEXP,LIB_FLAGS(a5)
	    CLEAR   d0

	ELSE	still_open

*	Go ahead and get rid of us.

	    move.l  LD_seglist(a5),-(a7)    ; We need to return this value

*	Unlink from device list.
	    movea.l a5,a1
	    CALLSYS Remove		    ; Remove(node)
					    ;	     A1
*	Remove the load.device task
	    lea     LD_TaskCB(a5),a1
	    CALLSYS RemTask		    ; RemTask(taskCB)
					    ;	      A1
*	Close the timer device.
	    lea     LD_TimerRequest(a5),a1
	    CALLSYS CloseDevice 	    ; CloseDevice(ioRequest)
					    ;		  A1
*	Free the memory allocated to the library.
	    CLEAR   d0
	    movea.l a5,a1
	    move.w  LIB_NEGSIZE(a5),d0
	    sub.w   d0,a1
	    add.w   LIB_POSSIZE(a5),d0
	    CALLSYS FreeMem		    ; FreeMem(memBlock, byteSize)
					    ;	      A1	D0
	    move.l  (a7)+,d0
	FI  still_open
	move.l a5,a6
	move.l (sp)+,a5
	rts

*************************************************************************
*
* BeginIO
*
*	  A6 = Device
*	  A1 = IOB

BeginIO:
	clr.b	IO_ERROR(a1)
	move.w	IO_COMMAND(a1),d0
	asl.w	#2,d0		    ; Compute displacement
	lea	cmdtable(pc),a0
	add.w	d0,a0
*	cmpa.l	#cmdtable_end,a0    ; Check to make sure it's in range.
* (Assembler doesn't handle it right, so...)
	dc.w	$B1FC
	dc.l	cmdtable_end
	bcs.s	cmd_ok		    ; branch if a0 < #cmdtable_end
	lea	cmdtable(pc),a0     ; Get the address of CMD_INVALID
cmd_ok:
	movea.l (a0),a0
	jmp	(a0)

****************************************************************************
*
* AbortIO
*
*	  A6 = Device
*	  A1 = IOB
AbortIO:
* Scan list for the IOB node
	LINKSYS Forbid
	move.l	LD_Port+MP_MSGLIST+LH_HEAD(a6),d1
	DO	scanlist
	    movea.l d1,a0
	    move.l  (a0),d1	      ; get LN_SUCC
	    beq.s   not_found
	    cmpa.l  a0,a1
	WHILE	ne,scanlist
	OD	scanlist

	move.l	a1,d1
	REMOVE	a1		    ; Remove the IOB node from the list.
	LINKSYS Permit
	movea.l d1,a1
	move.b	#IOERR_ABORTED,IO_ERROR(a1)
	rts
not_found:
	LINKSYS Permit
	clr.b  IO_ERROR(a1)	    ; Indicate an error occurred.
	rts

cmdtable:
	DC.L	Invalid
	DC.L	Reset
	DC.L	Read
	DC.L	Write
	DC.L	Update
	DC.L	Clear
	DC.L	Stop
	DC.L	Start
	DC.L	Flush
	DC.L	Set
cmdtable_end:

*******************************************************************
*
*	A6 = device
*	A1 = IOB

Write:
Update:
Clear:
Invalid:
	move.b	#IOERR_NOCMD,IO_ERROR(a1)
	rts

Read:
* If IO_QUICK is set, get the most recent values and return, do not reply.
* Otherwise, queue the IO request. Return.  Reply will come later.

	btst	#IOB_QUICK,IO_FLAGS(a1)
	IF	ne,do_quickio
	    move.l  LD_CPU(a6),LV_CPU(a1)
	    move.l  LD_BLITTER(a6),LV_CPU(a1)
	    move.l  LD_CHIP(a6),LV_CHIP(a1)
	    move.l  LD_FAST(a6),LV_FAST(a1)
	ELSE	do_quickio
	    lea     LD_Port(a6),a0
	    LINKSYS PutMsg		; PutMsg(msgPort, message)
	FI	do_quickio		;	 A0	  A1
	rts
Stop:
	LINKSYS Forbid
	tst.b	LD_stop_count(a6)
	IF	eq,stop_load_task
	    lea     LD_Semaphore(a6),a0
	    LINKSYS ObtainSemaphore
	FI	stop_load_task
	addq.b	#1,LD_stop_count(a6)
	LINKSYS Permit
	rts
Start:
	tst.b	LD_stop_count(a6)
	IF	eq,not_stopped
	    move.b  #LDERR_NOT_STOPPED,IO_ERROR(a1)
	ELSE	not_stopped
	    LINKSYS Forbid
	    subq.b  #1,LD_stop_count(a6)
	    IF	    eq,start_load_task
		lea	LD_Semaphore(a6),a0
		LINKSYS ReleaseSemaphore
	    FI	    start_load_task
	    LINKSYS Permit
	FI	not_stopped
	rts

* Reply to all pending requests, setting IOERR_ABORTED.
Flush:
	LINKSYS Forbid
	DO	flushrequests
	    lea     LD_Port(a6),a0
	    LINKSYS GetMsg	    ; message = GetMsg(msgPort)
	    tst.l   d0		    ; D0	       A0
	UNTIL	 eq,flushrequests
	    movea.l d0,a1
	    move.b  #IOERR_ABORTED,IO_ERROR(a1)
	    LINKSYS ReplyMsg	    ; ReplyMsg(message)
				    ;	       A1
	OD	flushrequests
	LINKSYS Permit
	rts

Reset:
;   Set default values.
	move.w	#INTERVAL,LV_INTERVAL(a1)
	move.w	#TICKS,LV_TICKS(a1)
	move.b	#LOAD_TASK_PRIORITY,LV_PRI(a1)
;   FALLTHROUGH into the Set command

Set:
	move.l	a1,-(sp)
	lea	LD_Semaphore(a6),a0
	LINKSYS ObtainSemaphore     ; ObtainSemaphore(signalSemaphore)
				    ;		      A0
	move.l	(sp)+,a1
	move.w	LV_INTERVAL(a1),LD_Interval(a6)
	move.w	LV_TICKS(a1),LD_Ticks(a6)
	bsr.s	ComputeDelay
	move.b	LV_PRI(a1),d0
	lea	LD_TaskCB(a6),a1
	LINKSYS SetTaskPri	    ; oldPriority = SetTaskPri(taskCB, newpriority)
				    ; D0.b		       A1      D0.b
	lea	LD_Semaphore(a6),a0
	LINKSYS ReleaseSemaphore    ; ReleaseSemaphore(signalSemaphore)
				    ;		       A0
	rts

***************************************************************************
*
* ComputeDelay subroutine - From the LD_Interval and LD_Ticks, compute the
*			    delay in seconds and microseconds between ticks.
*
*   Input:   A6 - Load Device
*	     LD_Interval(a6) - Interval in seconds
*	     LD_Ticks(a6)    - Number of Ticks per Interval
*
*   Output:  LD_secs(a6)     - delay in seconds
*	     LD_micro(a6)    - delay in microseconds
*
*   Destroys: a0,d0,d1
*

ComputeDelay:
	move.l	d2,-(a7)
	CLEAR	d1
	move.w	LD_Interval(a6),d1	    ; INTERVAL in seconds
	move.w	LD_Ticks(a6),d2 	    ; number of TICKS per INTERVAL
	mulu	#20,d1			    ; multiply d0 by a MICROSPERSEC
	mulu	#50000,d1		    ; (in 2 steps, since can't mult by >65536)

*	d1.l = d1.l / d2.w

	movea.w d1,a0
	clr.w	d1
	swap	d1
	divu	d2,d1
	move.l	d1,d0
	swap	d1
	move.w	a0,d0
	divu	d2,d0
	move.w	d0,d1

*	LD_secs  = d1.l / MICROSPERSEC
*	LD_micro = d1.l % MICROSPERSEC

	clr.l	LD_secs(a6)
	DO	incrsecs
	    cmp.l #MICROSPERSEC,d1
	WHILE	ge,incrsecs
	    sub.l #MICROSPERSEC,d1
	    addi.l #1,LD_secs(a6)
	OD	incrsecs
	move.l	d1,LD_micro(a6)

	move.l	(a7)+,d2
	rts

********************************************************************
*
* Load Task
*
* Register Usage:   a6 : SysBase
*		    a2 : Semaphore
*		    a3 : TimerPort
*		    a4 : TimerMessage
*		    a5 : LoadDevice
*
*		    d2 : Current Tick count
*		    d3 : Ready Queue counter
*		    d4 : Blitter Use counter
*		    d5 : Chip Mem available
*		    d6 : Fast Mem available
*		    d7 : <Free Safety>

*  To get a pointer to the Load Device structure, use a7 as a base.
*  It points to the last longword of the stack (which contains the
*  address of a cleanup routine).
*									a7
*  |	<---------- LD_Stack ----------->   | <- LOAD_STACK_SIZE - 4 ->  |
*  |----------------------------------------------------------------------|
*  | Load Device   | TimerRequest | etc.    | Stack			  |
*  |----------------------------------------------------------------------|

Task_Start:
	move.l	_AbsExecBase,a6
	lea	-LD_Stack-LOAD_STACK_SIZE+4(a7),a5   ; Get Load Device ptr

	lea	LD_Semaphore(a5),a2

*   Initialize the Timer Port.
	lea	LD_TimerPort(a5),a3
	move.b	#NT_MSGPORT,LN_TYPE(a3)
	move.b	#PA_SIGNAL,MP_FLAGS(a3)
	moveq.l #-1,d0
	CALLSYS AllocSignal		; signalNum = AllocSignal(signalNum)
	move.b	d0,MP_SIGBIT(a3)	; D0			  D0
	lea	LD_TaskCB(a5),a0
	move.l	a0,MP_SIGTASK(a3)
	lea	MP_MSGLIST(a3),a0
	NEWLIST a0

*   Initialize the timer IO request
	lea	LD_TimerRequest(a5),a4
	move.b	#NT_MESSAGE,LN_TYPE(a4)
	move.l	a3,MN_REPLYPORT(a4)
	move.w	#IOTV_SIZE,MN_LENGTH(a4)

*   Open the timer.device
	lea	TimerName,a0
	movea.l a4,a1
	moveq.l #UNIT_VBLANK,d0
	CLEAR	d1
	CALLSYS OpenDevice    ; error = OpenDevice(Name,Unit,ioRequest,flags)
			      ; D0		   A0	D0   A1        D1
	DO	mainloop
	    move.l  a2,a0
	    CALLSYS ObtainSemaphore	; ObtainSemaphore(signalSemaphore)
	    move.w  LD_Ticks(a5),d2	;		  A0
	    moveq.l #0,d3
	    moveq.l #0,d4

*	    'tickloop' is done once for each 'tick'.
	    DO	    tickloop
		move.w	#TR_ADDREQUEST,IO_COMMAND(a4)
		move.l	LD_secs(a5),IOTV_TIME+TV_SECS(a4)
		move.l	LD_micro(a5),IOTV_TIME+TV_MICRO(a4)
		movea.l a4,a1
		CALLSYS DoIO		    ; error = DoIO(ioRequest)
					    ; D0	   A1
*		Compute System Statistics:

*		Measure length of ready queue
*		Note: LoadTask is not in this queue, since we are running.

		CALLSYS Disable
		movea.l TaskReady+LH_HEAD(a6),a1  ; Get Pointer to first
		DO	readycount		  ; node into a1.
		    move.l  (a1),d0		  ; Look ahead.
		UNTIL	eq,readycount
		    addq.l  #1,d3
		    movea.l d0,a1
		OD	readycount
		CALLSYS Enable

*		Check if blitter busy

*		move.w	_custom+dmaconr,d0	  ; Assembler can't handle this...
		move.w	$DFF002,d0
		btst	#DMAB_BLTDONE,d0
		IF  ne,busy
		    addq.l  #1,d4
		FI  busy

		subq.w	#1,d2		 ; Decrement tick count
	    UNTIL   eq,tickloop
	    OD	    tickloop

*	    Compute available memory

	    move.l  #MEMF_CHIP,d1
	    CALLSYS AvailMem		    ; size = AvailMem(requirements)
	    move.l  LD_Max_Chip(a5),d5	    ; D0	      D1
	    sub.l   d0,d5

	    move.l  #MEMF_FAST,d1
	    CALLSYS AvailMem		    ; size = AvailMem(requirements)
	    move.l  LD_Max_Fast(a5),d6	    ; D0	      D1
	    sub.l   d0,d6
	    move.l  d3,LD_CPU(a5)
	    move.l  d4,LD_BLITTER(a5)
	    move.l  d5,LD_CHIP(a5)
	    move.l  d6,LD_FAST(a5)

	    DO	    reply
		lea	LD_Port(a5),a0
		CALLSYS GetMsg		    ; message = GetMsg(msgPort)
		tst.l	d0		    ; D0	       A0
	    UNTIL    eq,reply
		movea.l d0,a1
		move.l	d3,LV_CPU(a1)
		move.l	d4,LV_BLITTER(a1)
		move.l	d5,LV_CHIP(a1)
		move.l	d6,LV_FAST(a1)
		CALLSYS ReplyMsg	    ; ReplyMsg(message)
	    OD	    reply		    ;	       A1

	    lea     LD_Semaphore(a5),a0
	    CALLSYS ReleaseSemaphore	    ; ReleaseSemaphore(signalSemaphore)
	ODL	mainloop		    ;		       A0

EndCode:
	END
@//E*O*F device.a//
chmod u=rw,g=r,o=r device.a
 
echo x - load.h
sed 's/^@//' > "load.h" <<'@//E*O*F load.h//'
#ifndef DEVICES_LOAD_H
#define DEVICES_LOAD_H
#ifndef DEVICES_TIMER_H
#include <devices/timer.h>
#endif DEVICES_TIMER_H
#ifndef EXEC_DEVICES_H
#include <exec/devices.h>
#endif
#ifndef EXEC_SEMAPHORES_H
#include <exec/semaphores.h>
#endif
#define LOAD_STACK_SIZE 200
struct LoadBase {
    struct Device ld_device;
    struct SignalSemaphore ld_semaphore;
    struct timerequest ld_timerequest;
    struct MsgPort ld_timerport;
    struct MsgPort ld_port;
    struct Task ld_taskcb;
    char   ld_stack[LOAD_STACK_SIZE];
    ULONG  ld_seglist;
    ULONG  ld_sysbase;
    UWORD  ld_interval;
    UWORD  ld_ticks;
    ULONG  ld_secs;
    ULONG  ld_micro;
    ULONG  ld_max_chip;
    ULONG  ld_max_fast;
    ULONG  ld_cpu;
    ULONG  ld_blitter;
    ULONG  ld_chip;
    ULONG  ld_fast;
    UBYTE  ld_stop_count;
    UBYTE  ld_flags;
};
typedef struct _loadval {
    ULONG   lv_cpu;
    ULONG   lv_blitter;
    ULONG   lv_chip;
    ULONG   lv_fast;
} loadval;
typedef struct _loadrequest {
    struct IORequest lr_node;
    struct _loadval  lr_load;
    UWORD	     lr_interval;
    UWORD	     lr_ticks;
    BYTE	     lr_pri;
} loadrequest;
#define LDB_OPEN_EXCL	    0
#define LDF_OPEN_EXCL	    (1<<LDB_OPEN_EXCL)
#define LDERR_IN_USE	    -5
#define LDERR_ACCESS_DENIED -6
#define LDERR_NOT_STOPPED   -7
#define LD_SET	CMD_NONSTD

@//E*O*F load.h//
chmod u=rw,g=r,o=r load.h
 
exit 0