;
; ### System.asm by JM v 0.37 ###
;
; - Created 891107 by JM -
;
;
; Bugs: Yeah, just ask how many of 'em! :-)
;
;
; Edited:
;
; - 891107 by JM -> v0.01	- learning to use ALP.
; - 891108 by JM -> v0.05	- man, it works!
; - 891108 by JM -> v0.10	- code compressed, header added.
; - 891108 by JM -> v0.15	- fixed a bug which caused it think that
;				  the output buffer was full.
; - 891110 by JM -> v0.16	- trying to prevent stupid process names
;				  when there's no active command in CLI.
; - 891111 by JM -> v0.20	- CLI without a command produces «no command».
;				- background CLI produces 'bcli'.
;				- normal CLI process produces 'cli '.
;				- checks our own process too.
; - 891112 by JM -> v0.25	- tasks lists should be handled correctly now.
;				- output format changed.
; - 891123 by JM -> v0.26	- tasks number added to listing (because I
;				  have over 40 processes running just now)
; - 891124 by JM -> v0.30	- process times printed if the global
;				  messageport is found.  Currently only works
;				  for CLI processes.
; - 891125 by JM -> v0.31	- some code compressing.
; - 891207 by JM -> v0.32	- MAXCLI limit added.
; - 891228 by JM -> v0.33	- title length fixed.
; - 900218 by JM -> v0.36	- Disable() during the Exec list scan.
; - 900423 by JM -> v0.37	- this version for C=Lehti.
;
;

		include	"jmplibs.i"
		include	"exec/types.i"
		include	"exec/execbase.i"
		include	"exec/tasks.i"
		include	"exec/nodes.i"
		include	"exec/memory.i"
		include	"libraries/dos.i"
		include	"libraries/dosextens.i"
		include	"com.i"
		include	"relative.i"
		include	"strlib.i"
		include	"mathlib.i"
		include	"execlib.i"
		include	"util.i"
		include	"macros.i"
		include	"constants.i"
		include	"exec.xref"

		xref	_LVOOldOpenLibrary
		xref	_LVOAllocMem
		xref	_LVODisable
		xref	_LVOEnable
		xref	_LVOFindPort
		xref	_LVOOutput
		xref	_LVOWrite
		xref	_LVOFreeMem
		xref	_LVOCloseLibrary
		xref	_LVOFindTask


RELATIVE	equ	1		; we use relative addressing here	; we use relative addressing here

MAXCLI		equ	50		; maximum number of CLI processes	; maximum number of CLI processes

OBUF		equ	8192		; size of output buffer
PBUF		equ	1024		; size of pointer buffer
SBUF		equ	256		; size of string buffer
TBUF		equ	128		; size of temp buffer


	STRUCTURE GlobalPort,MP_SIZE
	 STRUCT gp_cntr,4*MAXCLI	; process CPU time counter
	 STRUCT gp_name,24		; port name
	 APTR	gp_oldcode		; pointer to old Switch() routine
	 STRUCT	gp_code,2048		; space for code
	LABEL	gp_sizeof

	STRUCTURE TaskProcessData,0
	 STRUCT	tpd_name,24		; space for task name
	 APTR	tpd_task		; space for task pointer
	 ULONG	tpd_pri			; space for priority
	 ULONG	tpd_type		; space for type
	 ULONG	tpd_cli			; space for CLI number
	 ULONG	tpd_state		; space for task state
	LABEL	tpd_sizeof


; define offsets for variables stored on stack:

.DosBase	equ	-4		; space for DosBase
cmdlin		equ	-8		; command line pointer
obufe		equ	-12		; end of output buffer
tasknum		equ	-16		; task number
MEM		set	4
memory		equ	-20
obuf		equ	-24		; output buffer
MEM		set	MEM+OBUF
pbuf		equ	-28		; pointer table
MEM		set	MEM+PBUF
sbuf		equ	-32		; string buffer
MEM		set	MEM+SBUF
tbuf		equ	-36		; temporary buffer
MEM		set	MEM+TBUF



main		link	a4,#-36			; allocate stack for variables
		movem.l	d0/a4,-(sp)
		moveq	#8,d0
10$		clr.l	-(a4)			; clear all variables
		dbf	d0,10$
		movem.l	(sp)+,d0/a4

		lea	.DosName(pc),a1
		move.l	$4,a6
		jsr	_LVOOldOpenLibrary(a6)	; open dos.library
		move.l	d0,.DosBase(a4)
		beq	cleanup
		move.l	#MEM,d0			; allocate memory for buffers
		move.l	#MEMF_CLEAR,d1
		move.l	$4,a6
		jsr	_LVOAllocMem(a6)
		move.l	d0,memory(a4)
		beq	cleanup

		move.l	d0,a0			; set buffer pointers into
		move.l	#MEM,(a0)+		;  the big buffer allocated
		move.l	a0,obuf(a4)
		lea	(OBUF)(a0),a0
		move.l	a0,pbuf(a4)
		lea	(PBUF)(a0),a0
		move.l	a0,sbuf(a4)
		lea	(SBUF)(a0),a0
		move.l	a0,tbuf(a4)

		move.l	obuf(a4),d0
		add.l	#(OBUF-64),d0		; get end address - 64
		move.l	d0,obufe(a4)		; save it

		move.l	$4,a6
		jsr	_LVODisable(a6)		; disable interrupts
		move.l	obuf(a4),a1
		bsr	ListOwn			; list this process data
		move.l	#'wait',d2		; process state = WAIT
		lea.l	TaskWait(a6),a2		; list header pointer
		bsr	ListTasks		; list processes waiting
		bcs.s	output			; -> if buffer full
		move.l	#'busy',d2		; process state = BUSY
		lea.l	TaskReady(a6),a2	; list header pointer
		bsr	ListTasks		; list processes ready to run
output		jsr	_LVOEnable(a6)		; enable interrupts again


; make a pointer table of process data entries for sort routine:

		move.l	obuf(a4),a0
		move.l	pbuf(a4),a1
1$		tst.l	tpd_task(a0)		; task==NULL?
		beq.s	2$			; yep -> end of task list
		move.l	a0,(a1)+		; store pointer
		add.w	#tpd_sizeof,a0
		bra.s	1$
2$


; alphabetic sort:

		move.l	pbuf(a4),a0		; address of pointer table
		strlib	tsorti			; sort the table


; check if time status message port exists:

		lea	portname(pc),a1		; name of port
		move.l	$4,a6			; ExecBase
		jsr	_LVOFindPort(a6)	; find it
		move.l	d0,a5			; save address
		tst.l	d0
		beq.s	noport			; branch if not found

yesport		moveq	#66,d3			; length of format string
		bra.s	dontcare		;  with time

noport		moveq	#56,d3			;  without time

dontcare	lea.l	header(pc),a0		; print header
		bsr	pout

		lea.l	linefeed(pc),a0
		moveq	#1,d3			; and LF
		bsr	pout

		move.l	pbuf(a4),a3
printloop	move.l	(a3)+,d1		; output loop
		beq	cleanup			; exit if no more processes
		addq.l	#1,tasknum(a4)

		move.l	tbuf(a4),a2
		move.l	tasknum(a4),(a2)+	; task number

		move.l	d1,(a2)+		; task name

		moveq	#tpd_type,d0
		add.l	d1,d0
		move.l	d0,(a2)+		; task type

		move.l	d1,a1
		move.l	tpd_cli(a1),(a2)+	; cli number

		moveq	#tpd_state,d0
		add.l	d1,d0
		move.l	d0,(a2)+		; task state (curr/busy/wait)

		move.b	tpd_pri(a1),d0
		ext.w	d0
		ext.l	d0
		move.l	d0,(a2)+		; priority

		move.l	tpd_task(a1),(a2)+	; task pointer


; handle time information if message port was found:

		move.l	tpd_cli(a1),d2		; cli number
		move.l	a5,d0
		beq.s	timenot
		cmp.l	#MAXCLI,d2		; cli number too high?
		bhs.s	timenot			; yup -> don't list time
		add.l	d2,d2			; convert BPTR to APTR
		add.l	d2,d2
		move.l	gp_cntr(a5,d2.l),d0	; time:
		moveq	#50,d1
		mathlib	divu			; convert time to seconds
		move.l	d0,(a2)+		; save seconds
		add.l	d1,d1
		move.l	d1,(a2)+		; 1/100 seconds

		lea	fyesport(pc),a0
		tst.l	d2			; a CLI process?
		bne.s	timetoo			; .ne = yup -> print time
timenot		lea	fnoport(pc),a0		; nope: short format
timetoo		move.l	sbuf(a4),a1		; buffer
		move.l	tbuf(a4),a2		; data stream
		execlib	format			; format it
		move.l	sbuf(a4),a0
		sub.l	a0,a1			; calculate length
		move.l	a1,d3

		tst.l	d2			; cli#
		bne.s	1$			; .ne -> skip
		move.b	#' ',34(a0)		; yes -> erase CLI number (0)
1$		bsr.s	pout			; print process data
		bra	printloop		; otherwise do next


; print routine:

pout		move.l	a0,d2			; string pointer
		move.l	.DosBase(a4),a6
		jsr	_LVOOutput(a6)		; get output file handle
		move.l	d0,d1
		beq.s	pout_e			; if NULL, printing impossible
		jsr	_LVOWrite(a6)		; non-NULL, print line
pout_e		rts

cleanup		move.l	memory(a4),d0		; deallocate buffers
		beq.s	1$
		move.l	d0,a1
		move.l	(a1),d0
		move.l	$4,a6
		jsr	_LVOFreeMem(a6)
		clr.l	memory(a4)
1$		move.l	.DosBase(a4),d0		; close dos.library
		beq.s	2$
		move.l	d0,a1
		move.l	$4,a6
		jsr	_LVOCloseLibrary(a6)
		clr.l	.DosBase(a4)
2$		moveq	#0,d0			; clear error flag
		unlk	a4
		rts


; list this process data (a1=output buffer):

ListOwn 	move.l	a1,a2			; save output buffer pointer
		sub.l	a1,a1
		move.l	$4,a6
		jsr	_LVOFindTask(a6)	; find this task
		move.l	a2,a1
		move.l	d0,a2
		bsr.s	copycliname		; copy process name
		move.l	a2,tpd_task(a1)		; save task*
		move.b	LN_PRI(a2),tpd_pri(a1)	; save task pri
		bsr.s	checkclitype		; check if RUN
		move.l	d0,tpd_type(a1)		; save type
		move.l	pr_TaskNum(a2),tpd_cli(a1)	; save CLI id
		move.l	#'curr',tpd_state(a1)	; save state
		lea	tpd_sizeof(a1),a1	; advance it, man!
		rts


; check if a background CLI and set type accordingly:

checkclitype	move.l	#' cli',d0
		move.l	pr_CLI(a2),a0		; bptr to CLI struct
		add.l	a0,a0
		add.l	a0,a0
		tst.l	cli_Background(a0)	; created by RUN?
		beq.s	clinotrun
		move.l	#'bcli',d0
clinotrun	rts				; return type in d0


; copy CLI process name into our structure:

copycliname	bsr.s	checkclitype		; check if background cli
		move.l	d0,d1			; save cli type
		move.l	cli_CommandName(a0),a0	; name (BSTR)
		add.l	a0,a0			; convert to APTR
		add.l	a0,a0
		lea.l	tpd_name(a1),a3		; dest for name
		moveq	#0,d0
		move.b	(a0)+,d0		; length of BSTR
		bne.s	cliprochasname		; -> there is a name!
		lea.l	nocommand(pc),a0	; no name -> use this string
		bra.s	cliproc_before
cliprochasname	subq.w	#1,d0
		cmp.w	#22,d0			; name too long?
		bls.s	cliproc_cona		; no -> skip next instruction
cliproc_before	moveq	#22,d0			; get max length of name
cliproc_cona	move.b	(a0)+,(a3)+		; copy CLI process name
		dbeq	d0,cliproc_cona
		move.l	d1,d0			; return CLI type in d0
		rts


*************************************************************************
*									*
* List one task queue.							*
* Entry: a2 = ExecBase->TaskReady or ExecBase->TaskWait			*
*	 a1 = output buffer, must be EVEN aligned!			*
*	 d2 = state (wait or rdy)					*
*									*
*************************************************************************

ListTasks	move.l	LH_HEAD(a2),a2		; first task list node*

LT_loop		tst.l	LN_SUCC(a2)		; empty or end-of list?
		beq.s	LT_end			; .eq -> yeah

		move.l	a2,tpd_task(a1)		; save task*
		move.b	LN_PRI(a2),tpd_pri(a1)	; save task pri

		move.b	LN_TYPE(a2),d0		; get type
		cmp.b	#NT_TASK,d0		; is it a task?
		bne.s	LT_nottask		; .ne -> nope
; it's a TASK:
		bsr.s	copytname
		move.l	#'task',d0		; put type: task
		bra.s	LT_typedone

LT_nottask	cmp.b	#NT_PROCESS,d0		; is it a process then?
		bne.s	LT_notproc		; .ne -> nope (strange!)
; it's a PROCESS:
		move.l	pr_TaskNum(a2),tpd_cli(a1)	; is it a CLI process?
		bne.s	LT_cliproc		; .ne -> yup, spec! spec!
		bsr.s	copytname		; copy the name
		move.l	#'proc',d0		; put type: process
		bra.s	LT_typedone


***************************************************************************
*									  *
* this one is here just because I wanted to use short branches to call it *
*									  *
***************************************************************************

; copy task name:

copytname	move.l	LN_NAME(a2),a0		; name pointer
		lea.l	tpd_name(a1),a3		; space for name in our struct
		tst.b	(a0)			; is there a name?
		bne.s	CN_taskhasname		; .ne = yes, copy it
		lea.l	noname(pc),a0		; no name -> so tell that
CN_taskhasname	moveq	#22,d0			; get max length of name
CN_copytaskname	move.b	(a0)+,(a3)+		; copy task name
		dbeq	d0,CN_copytaskname
		rts

*************************************************************************


LT_cliproc	bsr	copycliname		; copy CLI name
		bra.s	LT_typedone
LT_notproc	lea.l	tpd_name(a1),a0		; there's a NULL here :-)
		bsr.s	copytname		;  so it says '«no name»'
		move.l	#' ?? ',d0		; who knows what it is?
LT_typedone	move.l	d0,tpd_type(a1)		; put type into buffer

		move.l	d2,tpd_state(a1)	; put state

		cmp.l	obufe(a4),a1		; check if buffer full
		bhi.s	LT_error
		add.w	#tpd_sizeof,a1		; increment dest pointer
		move.l	LN_SUCC(a2),a2		; get next node
		bra.s	LT_loop
LT_end		andi	#~1,ccr
		rts
LT_error	ori	#1,ccr			; buffer full, return C=1
		rts


; macro libraries:

		execlib
		strlib
		mathlib

portname	dc.b	'WatchmanPort',0
header		dc.b	'num taskname               type  id'
		dc.b	' state  pri task ptr       time',0
fnoport		dc.b	'%3.3ld %-22.22s %4.4s %3ld'
		dc.b	'  %4.4s %4ld %08lx',LF,0
fyesport	dc.b	'%3.3ld %-22.22s %4.4s %3ld'
		dc.b	'  %4.4s %4ld %08lx %7.7ld.%02ld'
linefeed	dc.b	LF
null		dc.b	0

noname		dc.b	'« no name »',0
nocommand	dc.b	'« no command »',0

.DosName	dc.b	'dos.library',0

		end

