; ---------------------------------------------------------------------

; Paul Overaa's Amiga Computing Assembler Series - code for instalment 5

; ---------------------------------------------------------------------

NULL			EQU	   0

TRUE			EQU	   1

NM_TITLE		EQU	   1

NM_ITEM			EQU	   2

NM_END			EQU	   0

IDCMP_MENUPICK		EQU     $00000100

IDCMP_RAWKEY		EQU	$00000400

MENUNULL		EQU	$FFFFFFFF
		
im_Class		EQU	 $14

im_Code			EQU	 $18

wd_UserPort		EQU	 $56

TAG_DONE		EQU	   0

TAG_END			EQU	   0

WA_BASE			EQU 	$80000063
WA_Left			EQU	WA_BASE+$01
WA_Top			EQU	WA_BASE+$02
WA_Width		EQU	WA_BASE+$03
WA_Height		EQU	WA_BASE+$04
WA_IDCMP		EQU	WA_BASE+$07
WA_Title		EQU	WA_BASE+$0B
WA_DragBar		EQU	WA_BASE+$1F
WA_PubScreen		EQU	WA_BASE+$16

MODE_OLDFILE		EQU	1005

MEMF_ANY		EQU	   0

DOS_FIB			EQU	   2

fib_Size		EQU	 124

rf_File			EQU	   4

rf_Dir			EQU	   8

IOSTD_SIZE		EQU	 $30

io_Data			EQU	 $28

io_Length		EQU	 $24

io_Command		EQU	 $1c

CONFLAG_DEFAULT		EQU	   0

window_SIZEOF		EQU	 $88

CMD_WRITE		EQU	  $3

LINEFEED		EQU	  $A

PAGELENGTH		EQU	  22

CURSORUP		EQU      $4C

CURSORDOWN		EQU      $4D


_AbsExecBase		EQU	   4

_LVOOpenLibrary		EQU	-552

_LVOCloseLibrary	EQU	-414

_LVODisplayBeep		EQU	 -96

_LVOLockPubScreen	EQU	-510

_LVOUnlockPubScreen	EQU	-516

_LVOOpenWindowTagList	EQU	-606

_LVOCloseWindow		EQU	 -72

_LVOGetVisualInfoA	EQU	-126

_LVOFreeVisualInfo	EQU	-132

_LVOCreateMenusA	EQU	 -48

_LVOFreeMenus		EQU	 -54

_LVOLayoutMenusA	EQU	 -66

_LVOSetMenuStrip	EQU	-264

_LVOClearMenuStrip	EQU	 -54

_LVOWaitPort		EQU	-384

_LVOGetMsg		EQU	-372

_LVOReplyMsg		EQU	-378

_LVOAllocFileRequest	EQU	 -30

_LVOFreeFileRequest	EQU	 -36

_LVOAslRequest		EQU	 -60

_LVOExamineFH		EQU	-390

_LVOAllocMem		EQU	-198

_LVOFreeMem		EQU	-210

_LVOOpen		EQU	 -30

_LVOClose		EQU	 -36

_LVOAllocDosObject	EQU	-228

_LVOFreeDosObject	EQU	-234

_LVORead		EQU	 -42

_LVOAddPart		EQU	-882

_LVOCreateMsgPort	EQU	-666

_LVODeleteMsgPort	EQU	-672

_LVOCreateIORequest	EQU	-654

_LVODeleteIORequest	EQU	-660

_LVOOpenDevice		EQU	-444

_LVOCloseDevice		EQU	-450

_LVODoIO		EQU	-456

; ---------------------------------------------------------------------

LINKLIB		MACRO
		move.l	a6,-(a7)
		movea.l	\2,a6
		jsr	\1(a6)
		move.l	(a7)+,a6
		ENDM

CALLSYS		MACRO
		LINKLIB	_LVO\1,\2
		ENDM

; ---------------------------------------------------------------------

_main		lea	function_stack,a5	for alloc/dealloc operations
		lea 	lib_names,a2
		lea 	_DOSBase,a3
		move.w	#(5-1),d3		loop counter
.loop		movea.l	(a2)+,a1		library name pointer
		moveq	#0,d0			any version will do
		CALLSYS	OpenLibrary,_AbsExecBase
		move.l	d0,(a3)+		store returned base
		dbeq	d3,.loop

		beq.s	lib_error_exit				
		
		; all libraries are open and available for use.
						
		jsr	LockScreen
		beq.s	closedown

		jsr	GetVisInfo
		beq.s	closedown
		
		jsr	OpenWindow
		beq.s	closedown
		
		jsr	CreateMenu
		beq.s	closedown
		
		jsr	LayoutMenu
		beq.s	closedown

		jsr	InstallMenu
		beq.s	closedown		

		jsr	AllocFileReq
		beq.s	closedown

		jsr	SetupMsgPort
		beq.s	closedown
					
		jsr	SetupIOReq
		beq.s	closedown

		jsr	OpenDev
		beq.s	closedown

		; now everything is set up we can call the event handler!

		movea.l window_p,a1
		movea.l wd_UserPort(a1),a2	user port address
		jsr	EventHandler		handle user actions

closedown	move.l	(a5)+,d0		retrieve function pointer
		beq.s	lib_normal_exit
		move.l	d0,a0
		jsr	(a0)			and execute routine if it exists!
		bra.s	closedown


lib_normal_exit	lea	lib_base_end,a3		revised label - see text
		moveq	#5,d2			library count
		jsr	CloseLibs		close libraries
		moveq	#0,d0			clear d0 for O/S
		rts				and terminate program

lib_error_exit	moveq	#(5-1),d2		see part 1 mag text
		sub	d3,d2
		jsr	CloseLibs		close libraries
		moveq	#0,d0			clear d0 for O/S
		rts				and terminate program

; ---------------------------------------------------------------------		

; CloseLibs() On entry...

; 	a3 should hold address of the longword location just past 
; 	   that of the first library to close (this is because the 
;	   routine uses a backward reading loop).

; 	d2 should hold count of the number of libraries to close	

CloseLibs	tst.b	d2			test counter
		beq.s	loop_end		
		movea.l	-(a3),a1		get library base
		CALLSYS	CloseLibrary,_AbsExecBase
		subq.b	#1,d2
		bra.s	CloseLibs
loop_end	rts

; ---------------------------------------------------------------------					

; LockScreen() and UnlkScreen() on entry... need no register parameters!


LockScreen	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		lea	workbench_name,a0	pointer to screen name
		CALLSYS	LockPubScreen,_IntuitionBase
		move.l	d0,workbench_p		save returned pointer
		beq.s	.error
		move.l	#UnlkScreen,-(a5)	push deallocation routine address
.error		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

UnlkScreen	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.w	#NULL,a0		screen name not needed
		movea.l	workbench_p,a1		screen to unlock
		CALLSYS	UnlockPubScreen,_IntuitionBase
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

; ---------------------------------------------------------------------					

; OpenWindow() and ShutWindow() on entry... need no register parameters!


OpenWindow	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.w	#NULL,a0
		lea	tags,a1			start of  tag list
		CALLSYS	OpenWindowTagList,_IntuitionBase
		move.l	d0,window_p		save returned pointer
		beq.s	.error
		move.l	#ShutWindow,-(a5)	push deallocation routine address
.error		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

ShutWindow	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.l	window_p,a0		window to close
		CALLSYS	CloseWindow,_IntuitionBase
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

; ---------------------------------------------------------------------					

; GetVisInfo() and FreeVisInfo() on entry... need no register parameters!


GetVisInfo	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.l	workbench_p,a0		
		movea.w	#TAG_END,a1		no tags
		CALLSYS	GetVisualInfoA,_GadToolsBase
		move.l	d0,visual_info_p	save returned pointer
		beq.s	.error
		move.l	#FreeVisInfo,-(a5)	push deallocation routine address
.error		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

FreeVisInfo	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.l	visual_info_p,a0	
		CALLSYS	FreeVisualInfo,_GadToolsBase
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

; ---------------------------------------------------------------------					

; CreateMenu() and FreeMenu() on entry... need no register parameters!


CreateMenu	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		lea	menu,a0
		movea.w	#TAG_END,a1		no tags
		CALLSYS	CreateMenusA,_GadToolsBase
		move.l	d0,menu_p		save returned pointer
		beq.s	.error
		move.l	#FreeMenu,-(a5)		push deallocation routine address
.error		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

FreeMenu	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.l	menu_p,a0		menu to free
		CALLSYS	FreeMenus,_GadToolsBase
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

; ---------------------------------------------------------------------					

; LayoutMenu() on entry... needs no register parameters!


LayoutMenu	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.l	menu_p,a0
		movea.l	visual_info_p,a1
		movea.w	#TAG_END,a2		no tags
		CALLSYS	LayoutMenusA,_GadToolsBase
		tst.l	d0			nothing to deallocate
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

; ---------------------------------------------------------------------					

; InstallMenu() and RemoveMenu() on entry... need no register parameters!


InstallMenu	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.l	window_p,a0
		movea.l	menu_p,a1
		CALLSYS	SetMenuStrip,_IntuitionBase
		tst.l	d0
		beq.s	.error
		move.l	#RemoveMenu,-(a5)	push deallocation routine address
.error		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

RemoveMenu	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.l	window_p,a0		target window
		CALLSYS	ClearMenuStrip,_IntuitionBase
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

; ---------------------------------------------------------------------	

; AllocFileReq() and FreeFileReq() on entry... need no register parameters!


AllocFileReq	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		CALLSYS	AllocFileRequest,_AslBase
		move.l	d0,file_request_p	save returned pointer
		beq.s	.error
		move.l	#FreeFileReq,-(a5)	push deallocation routine address
.error		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

FreeFileReq 	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.l	file_request_p,a0	requester to close
		CALLSYS	FreeFileRequest,_AslBase
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

; ---------------------------------------------------------------------					

; SetupMsgPort() and RemoveMsgPort() on entry... need no register parameters!

SetupMsgPort	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		CALLSYS	CreateMsgPort,_AbsExecBase
		move.l	d0,io_port_p		save returned pointer
		beq.s	.error
		move.l	#RemoveMsgPort,-(a5)	push deallocation routine address
.error		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

RemoveMsgPort 	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.l	io_port_p,a0		message port address
		CALLSYS	DeleteMsgPort,_AbsExecBase
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

; ---------------------------------------------------------------------					

; SetupIOReq() and RemoveIOReq() on entry... need no register parameters!

SetupIOReq	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		moveq	#IOSTD_SIZE,d0		size of std i/o structure
		movea.l	io_port_p,a0		reply port for messages
		CALLSYS	CreateIORequest,_AbsExecBase
		move.l	d0,io_request_p		save returned pointer
		beq.s	.error
		move.l	#RemoveIOReq,-(a5)	push deallocation routine address
		movea.l	d0,a0			io_request_p
		move.l	window_p,io_Data(a0)	store window pointer in request
		move.l	#window_SIZEOF,io_Length(a0)
.error		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

RemoveIOReq 	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.l	io_request_p,a0		I/O request address
		CALLSYS	DeleteIORequest,_AbsExecBase
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

; ---------------------------------------------------------------------					

; OpenDev() and CloseDev() on entry... need no register parameters!

; The Exec OpenDevice() function returns ZERO if successful. Because of
; this it is necessary to invert the state of the zero flag after testing 
; d0 in order to comply with our (self-imposed) allocation routine return 
; conditions (zero flag set is used to indicate an error)!

OpenDev		movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		moveq	#0,d0			unit number 0
		moveq	#CONFLAG_DEFAULT,d1	default flags
		lea	con_name,a0		device name
		movea.l	io_request_p,a1		i/o request pointer
		move.w	#CMD_WRITE,io_Command(a1) ALL commands will be writes!
		CALLSYS	OpenDevice,_AbsExecBase
		tst.l	d0			test for success	
		bne.s	.error
		move.l	#CloseDev,-(a5)		push deallocation routine address
		lea	AutoWrapOff,a0
		jsr	ConsoleCommand
		lea	TurnCursorOff,a0
		jsr	ConsoleCommand
		lea	SetColours,a0
		jsr	ConsoleCommand
		lea	ClearConsole,a0
		jsr	ConsoleCommand

		moveq	#1,d0			clear zero flag 
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

.error		moveq	#0,d0			set zero flag
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

CloseDev 	movem.l	a0-a1/d0-d1,-(a7)	preserve regs
		movea.l	io_request_p,a1		I/O request address
		CALLSYS	CloseDevice,_AbsExecBase
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

; ---------------------------------------------------------------------					
; ---------------------------------------------------------------------					

; Function name:     EventHandler()

; Purpose:           Handles window menu and raw keycode events

; Input Parameters:  Address of IDCMP user-port should be in a2. 

; Output parameters: None

; Register Usage:    a0: Used by WaitPort() and GetMsg()  
                     
;                    a1: Used by ReplyMsg()
                     
;                    a2: Holds user-port address

;                    d0: Used by WaitPort() and GetMsg()
                     
;                    d1: Unused but possibly altered by system functions

;                    d2: Used as an exit flag (quit when non-zero)

;		     d3: Used to hold message class field

;		     d4: Used to hold message code field

;		     d5: Loop counter in ShowFirstPage()

;		     d6: Used to hold line number in KeyHandler() 
;		         and ShowFirstPage() routines  

;		     d7: Used to hold copy of line number in DisplayLine()


; Other Notes:       Within EventHandler() all registers are preserved

; ---------------------------------------------------------------------

EventHandler   	movem.l	d0-d7/a0-a2,-(a7)	preserve registers
		clr.l	d2			clear exit flag
EventHandler2	movea.l	a2,a0			port address  
		CALLSYS	WaitPort,_AbsExecBase  
		jsr	GetMessage           
		cmpi.l	#TRUE,d2		exit flag set?
		bne.s	EventHandler2
		tst.l	buffer_p
		beq.s	.no_buffers	 	are buffers still allocated?
		movea.l	buffer_p,a1	
		move.l	buffer_size,d0
		CALLSYS	FreeMem,_AbsExecBase	free main buffer memory
		movea.l	lp_buffer_p,a1
		move.l	lp_size,d0
		CALLSYS	FreeMem,_AbsExecBase	free line pointer buffer

.no_buffers	movem.l	(a7)+,d0-d7/a0-a2	restore registers
		rts				logical end of routine

; ---------------------------------------------------------------------
                     
GetMessage	movea.l	a2,a0			get port address in a0
		CALLSYS	GetMsg,_AbsExecBase	get the message
		tst.l	d0
		beq.s	GetMessageExit		did it exist?
		movea.l	d0,a1			copy pointer to a1
		move.l	im_Class(a1),d3		copy message class
		move.w	im_Code(a1),d4		copy message code
		CALLSYS	ReplyMsg,_AbsExecBase	then send message back

		cmpi.l	#IDCMP_MENUPICK,d3 	check message class
		beq.s	MenuMessage           	handle menu messages
		
		cmpi.l	#IDCMP_RAWKEY,d3	check message class
		bne.s	GetMessage		ignore anything else
		jsr	KeyHandler		use raw keypress data
		bra.s	GetMessage	
		
MenuMessage	cmpi.w	#MENUNULL,d4
		beq.s	GetMessage		also skip if MENUNULL

		lsr.w	#5,d4			extract menu item number
		andi.b	#$3F,d4			(will be either 0 or 1)
		beq.s	GetFile
		moveq	#TRUE,d2		set QUIT signal to exit routine 
		bra.s	GetMessage
		
GetFile		jsr	FileHandler
		tst.l	buffer_p
		beq.s	GetMessage		is there a valid buffer?
		jsr	ShowFirstPage
		bra.s	GetMessage		check for more messages!

GetMessageExit	rts				d2 holds exit flag
                     
; ---------------------------------------------------------------------

; Routine to distinguish between cursor up and cursor down events
; register d4 holds key code as routine is entered

KeyHandler	tst.l	buffer_p
		beq	.error
		
.keycheck1	cmpi.w	#CURSORUP,d4		has user hit cursor up key?
		bne.s	.keycheck2
		
		tst.l	current_topline		yes but are we at top?
		beq	.error

		lea	GoToBottom,a0		no, so adjust display
		jsr	ConsoleCommand
		lea	DeleteLine,a0	
		jsr	ConsoleCommand
		lea	GoToTop,a0	
		jsr	ConsoleCommand
		lea	InsertLine,a0	
		jsr	ConsoleCommand
		subq.l	#1,current_topline	adjust line variables
		subq.l	#1,current_bottomline
		move.l	current_topline,d6
		bra.s	.displayline
		
.keycheck2	cmpi.w	#CURSORDOWN,d4		has user hit cursor down key?
		bne.s	.quietexit

		move.l	line_count,d6		yes, but are we at bottom?
		subq.l	#1,d6			
		cmp.l	current_bottomline,d6
		beq.s	.error

		lea	GoToBottom,a0		no, so adjust display
		jsr	ConsoleCommand
		lea	NextLine,a0	
		jsr	ConsoleCommand
		addq.l	#1,current_topline	adjust line variables
		addq.l	#1,current_bottomline
		move.l	current_bottomline,d6
		
.displayline	jsr	DisplayLine	
.quietexit	rts

.error		movea.w	#NULL,a0		flash screen to show error!
		CALLSYS	DisplayBeep,_IntuitionBase
		rts		

; ---------------------------------------------------------------------

; routine to display first 'screen page' of the file

ShowFirstPage	lea	ClearConsole,a0
		jsr	ConsoleCommand
		
		move.l	#0,current_topline	set initial topline
		move.l	line_count,d5
		cmpi.l	#PAGELENGTH,d5		is count<PAGELENGTH?
		bls.s	.displaypage
		moveq	#PAGELENGTH,d5		no, so display full page

.displaypage	subq.l	#1,d5			-1 for dbra loop counter
		move.l	d5,current_bottomline	set initial bottomline
		moveq	#0,d6			initialize line specifier
.displayloop	jsr	DisplayLine
		addq.l	#1,d6			next line to display
		dbra	d5,.displayloop
		rts
		
; ---------------------------------------------------------------------

; routine to display a line whose line number is provided in register d6

DisplayLine 	movea.l	lp_buffer_p,a0
		move.l	d6,d7			copy line number
		asl.l	#2,d7			multiply by 4
		add.l	d7,a0			and add to base address
		move.l	(a0)+,d7		pointer to start of line
		move.l	(a0),d1			start of NEXT line
		sub.l	d7,d1			length of line being printed
		movea.l	io_request_p,a1		i/o request address
		move.l	d7,io_Data(a1)
		move.l  d1,io_Length(a1)
		CALLSYS	DoIO,_AbsExecBase	No error check here!
		rts

; ---------------------------------------------------------------------					
; ---------------------------------------------------------------------

; Function name:     FileHandler()

; Purpose:           Handles the identification and loading of file

; Input Parameters:  None

; Output parameters: None

; Register Usage:    a0-a1/d0-d3: Used by various system calls
  
;		     d4: Used to hold pointer to FileInfoBlock

;		     d5: Used to hold file handle
		     
; Other Notes:       All registers are preserved

; ---------------------------------------------------------------------

FileHandler	movem.l	d0-d5/a0-a1,-(a7)	preserve registers

		movea.l	file_request_p,a0	asl requester address
		movea.w	#NULL,a1		no tags are used
		CALLSYS	AslRequest,_AslBase	bring up the requester

		; clear filename buffer (notice 
		;loop stops when a NULL is found)...

clear_filename	move.l	#filename_SIZEOF-1,d0	filename buffer size less 1
		lea	filename,a0		our filename buffer
.clear_loop	move.b	#NULL,(a0)+		
		tst.b	(a0)			have we reached a NULL?
		dbeq	d0,.clear_loop

		; now copy the ASL requester directory 
		; entry to our file name buffer...

		move.l	#filename_SIZEOF-1,d0	filename buffer size less 1
		movea.l	file_request_p,a0	ASL requester address
		movea.l	rf_Dir(a0),a0		get start of directory entry
		lea	filename,a1		our filename buffer
.copy_loop	move.b	(a0)+,(a1)+		
		tst.b	(a0)			have we reached a NULL?
		dbeq	d0,.copy_loop

		
		; finally add the filename to the 
		; filename buffer...

		movea.l	file_request_p,a0	ASL requester address
		move.l	#filename,d1		our filename buffer
		move.l	rf_File(a0),d2		ASL filename entry
		move.l	#filename_SIZEOF,d3	filename buffer size
		CALLSYS	AddPart,_DOSBase
		
.alloc_fib	moveq	#DOS_FIB,d1		object type
		moveq	#NULL,d2		no tags
		CALLSYS	AllocDosObject,_DOSBase
		move.l	d0,d4			save pointer to fib
		beq	.error0
		
		move.l	#filename,d1		filename start address
		move.l	#MODE_OLDFILE,d2		
		CALLSYS	Open,_DOSBase
		move.l	d0,d5			save file handle for closing
		beq	.error1

		move.l	d5,d1
		move.l	d4,d2
		CALLSYS	ExamineFH,_DOSBase
		tst.l	d0			ExamineFH() OK?
		beq	.error2

		tst.l	buffer_p
		beq.s	.no_buffer	 	is a buffer still allocated?

		movea.l	buffer_p,a1		
		move.l	buffer_size,d0
		CALLSYS	FreeMem,_AbsExecBase	free allocated buffer memory
		move.l	#NULL,buffer_p		clear buffer pointer
		
.no_buffer	move.l	d4,a0			file info block address
		move.l	fib_Size(a0),d0		size of selected file 

		addq.l	#1,d0			space for extra linefeed!
		move.l	d0,buffer_size		store identified size
		move.l	d0,d3			needed for Read() call
		moveq	#MEMF_ANY,d1		any memory will do
		CALLSYS	AllocMem,_AbsExecBase
		move.l	d0,buffer_p		did we get any memory?
		beq.s	.error2

		move.l	d5,d1			file handle pointer
		move.l	d0,d2			buffer_p from AllocMem()
		CALLSYS	Read,_DOSBase 		copy file into memory
		
		move.l	d5,d1			file handle pointer
		CALLSYS	Close,_DOSBase		close file

		moveq	#DOS_FIB,d1		object type
		move.l	d4,d2			fib pointer
		CALLSYS	FreeDosObject,_DOSBase	free fib
	
		; now ensure that there is a terminal linefeed
		
		movea.l	buffer_p,a0
		add.l	buffer_size,a0
		subq.l	#1,a0			last (extra) byte in buffer
		move.b	#LINEFEED,(a0)		set to an extra linefeed	
		
		jsr	CountLines		build line pointer list
		beq.s	.error0
		
.normal_exit	movem.l	(a7)+,d0-d5/a0-a1	restore registers
		rts				normal exit

.error2		move.l	d5,d1
		CALLSYS	Close,_DOSBase		close file
		
.error1		moveq	#DOS_FIB,d1		object type
		move.l	d4,d2			fib pointer
		CALLSYS	FreeDosObject,_DOSBase	free fib
		
.error0		movea.l	buffer_p,a1	
		move.l	buffer_size,d0
		CALLSYS	FreeMem,_AbsExecBase	free main buffer memory
		move.l	#NULL,buffer_p		clear pointer
		
		movea.w	#NULL,a0		flash screen to show error!
		CALLSYS	DisplayBeep,_IntuitionBase
		
		movem.l	(a7)+,d0-d5/a0-a1	restore registers
		rts				error exit
				
; ---------------------------------------------------------------------

; This routine returns with zero flag set if memory allocation error occurs.
; Only scratch registers are used and these are preserved at higher level.
; If successful the routine produces a list of pointers to the text lines!

CountLines	tst.l	buffer_p
		beq.s	.no_lp_buffer
		movea.l	lp_buffer_p,a1
		move.l	lp_size,d0
		CALLSYS	FreeMem,_AbsExecBase	free allocated lp_buffer memory
		move.l	#NULL,lp_buffer_p	clear pointer

.no_lp_buffer	moveq	#0,d0			linefeed count
		move.l	buffer_size,d1
		subq.l	#1,d1			buff size-1
		movea.l	buffer_p,a0
.loop0		cmpi.b	#LINEFEED,(a0)+
		bne.s	.ignorebyte0
		addq.l	#1,d0			increment count
.ignorebyte0	dbra	d1,.loop0
		move.l	d0,line_count		number of text lines
		addq.l	#1,d0			space for pointer to first line
		asl.l	#2,d0			multiply by 4!
		
		move.l	d0,lp_size		store identified size
		moveq	#MEMF_ANY,d1		any memory will do
		CALLSYS	AllocMem,_AbsExecBase
		move.l	d0,lp_buffer_p		did we get any memory?
		beq.s	.error

		move.l	d0,a1			lp_buffer_p
		move.l	buffer_p,(a1)+		start of first text line
		move.l	buffer_size,d1
		subq.l	#1,d1			buff size-1

		move.l	buffer_p,a0
.loop1		cmpi.b	#LINEFEED,(a0)+
		bne.s	.ignorebyte1
		move.l	a0,(a1)+
.ignorebyte1	dbra	d1,.loop1

		moveq	#1,d0			clear zero flag 

.error		rts

; ---------------------------------------------------------------------

; This routine expects the start address of the console command in a0
; see notes at end of program about command format used by routine!

ConsoleCommand	movem.l	a0-a1/d0-d1,-(a7)	preserve regs

		movea.l	io_request_p,a1		i/o request address
		move.l	(a0)+,io_Length(a1)
		move.l  a0,io_Data(a1)
		CALLSYS	DoIO,_AbsExecBase
		
		movem.l	(a7)+,a0-a1/d0-d1	restore regs
		rts

; ---------------------------------------------------------------------

_DOSBase	ds.l 1
_GfxBase	ds.l 1
_IntuitionBase	ds.l 1
_GadToolsBase 	ds.l 1
_AslBase	ds.l 1
lib_base_end					;end of library base variables

window_p	ds.l 1
visual_info_p	ds.l 1
menu_p		ds.l 1
file_request_p	ds.l 1
buffer_size	ds.l 1
buffer_p	ds.l 1
io_port_p	ds.l 1
io_request_p	ds.l 1
lp_buffer_p	ds.l 1
lp_size		ds.l 1
line_count	ds.l 1
current_topline	ds.l 1
current_bottomline	
		ds.l 1

tags		dc.l	WA_PubScreen
workbench_p	ds.l	1
		dc.l	WA_Left,0
		dc.l	WA_Top,0
		dc.l	WA_Width,640
		dc.l	WA_Height,200
		dc.l	WA_DragBar,TRUE
		dc.l	WA_IDCMP,IDCMP_MENUPICK|IDCMP_RAWKEY
		dc.l	WA_Title,window_name
		dc.l	TAG_DONE,NULL

stack_space	ds.l	10			space will grow as required
function_stack	dc.l	NULL			top of function stack

menu		dc.b	NM_TITLE,0
		dc.l	menu_title,NULL
		dc.w	0
		dc.l	0,NULL
		
		dc.b	NM_ITEM,0
		dc.l	item0,commkey0
		dc.w	0
		dc.l	0,NULL
		
		dc.b	NM_ITEM,0
		dc.l	item1,commkey1
		dc.w	0
		dc.l	0,NULL

		dc.b	NM_END,0
		dc.l	NULL,NULL
		dc.w	0
		dc.l	0,NULL
		

lib_names	dc.l lib1,lib2,lib3,lib4,lib5

lib1		dc.b 'dos.library',NULL
lib2		dc.b 'graphics.library',NULL
lib3		dc.b 'intuition.library',NULL
lib4		dc.b 'gadtools.library',NULL
lib5		dc.b 'asl.library',NULL

workbench_name	dc.b 'Workbench',NULL

window_name	dc.b 'Amiga Computing Assembler Series by Paul Overaa',NULL

menu_title	dc.b 'PROJECT',NULL

item0		dc.b 'Select File...',NULL

commkey0	dc.b 'S',NULL

item1		dc.b 'Quit to Workbench!',NULL

commkey1	dc.b 'Q',NULL

filename	ds.b 256

filename_SIZEOF EQU *-filename

con_name	dc.b 'console.device',NULL

; ---------------------------------------------------------------------

; The ConsoleCommand() routine expects console device commands 
; to be written using this format...

;		dc.l LONGWORD BYTE COUNT 
;		dc.b COMMAND BYTES
		
AutoWrapOff	dc.l 4
		dc.b $9B,$3F,$37,$6C

TurnCursorOff	dc.l 4
		dc.b $9B,$30,$20,$70

ClearConsole	dc.l 1
		dc.b $0C
		
GoToTop		dc.l 5
		dc.b $9B,'0',';','0',$48
		
InsertLine	dc.l 2
		dc.b $9B,$4C
	
GoToBottom	dc.l 6
		dc.b $9B,'22',';','0',$48

NextLine	dc.l 1	
		dc.b $85
		
DeleteLine	dc.l 2
		dc.b $9B,$4D

SetColours	dc.l 10				white text on black b/g
		dc.b $9B,'31;','42;','>2',$6D
		
		END

; ---------------------------------------------------------------------


		