
; This is nanoTerm ver. 3.0
; by Jason Fonseca
; with additional support from Joep Grooten and Daniele Forsi

	INCDIR	asminclude:
	INCLUDE exec/exec.i
	INCLUDE exec/exec_lib.i
	INCLUDE intuition/intuition.i
	INCLUDE intuition/intuition_lib.i
	INCLUDE devices/serial.i
	INCLUDE devices/console.i
	INCLUDE nanoTerm.i


; To make code "pure" there can be no variable space declared at assemble time.
; Here I link some memory for global variables off the stack and put a pointer
; to it in a5.

;***********************************************
;****		    OPENING		    ****
;***********************************************

GT	link	a5,#-(GT_VarLength)     ; Get the global variable memory.
	suba.l	#GT_VarLength,a5

	lea.l	IntuiName(pc),a1        ; Put the Intuition name in a1
	moveq	#37,d0			; Must be 2.04 or higher
	movea.l 4.w,a6			; Load ExecBase in a6
	jsr	_LVOOpenLibrary(a6)     ; Open it.
	move.l	d0,_IntuitionBase(a5)   ; Move the base pointer into the global var.
	beq	KillMem 		; If not free mem.

	lea.l	windowdef(pc),a0        ; Put the window tags into a1
	movea.l _IntuitionBase(a5),a6
	jsr	_LVOOpenWindow(a6)      ; Open the window.
	move.l	d0,WindowPtr(a5)        ; Store the window ptr into global var.
	beq	KillLib 		; If not close lib.

	move.l	WindowPtr(a5),a0        ; Put window ptr into a0
	move.l	wd_UserPort(a0),a1      ; Put UserPort into a1
	move.b	MP_SIGBIT(a1),WindowBit(a5) ; Store window's sigbit in global var.

	movea.l 4.w,a6			; Load ExecBase back in a6

	move.l	#IOSTD_SIZE,-(sp)       ; Push the size of the requests we need
	pea.l	ConWritePort(a5)        ; Push the beginning of the data structure
	jsr	AllocPorts(pc)          ; Call the handy AllocPorts command
	addq.l	#8,sp			; Pop everything we pushed
	tst.l	d0			; Test and see if everything opened
	beq	KillWindow		; If not close the window

	movea.l ConWriteReq(a5),a1      ; Move the write request into a1
	move.l	WindowPtr(a5),IO_DATA(a1) ; Shove the window ptr into io_data
	move.l	#wd_Size,IO_LENGTH(a1)  ; Put the window size into length
	moveq	#0,d0			; This is the con unit number
	clr.l	d1			; clear out d1
	lea.l	ConName(pc),a0          ; Load the console name into a0
	jsr	_LVOOpenDevice(a6)      ; Open it
	tst.l	d0			; See if it was opened
	bne	KillConPorts		; If not kill the ports

	move.l	ConWriteReq(a5),a0      ; Move write request into a0
	move.l	ConReadReq(a5),a1       ; Move read request into a1
	move.l	IO_DEVICE(a0),IO_DEVICE(a1) ; Copy the IO_DEVICE and the IO_UNIT
	move.l	IO_UNIT(a0),IO_UNIT(a1) ; From the write request to the read request


	move.l	#IOEXTSER_SIZE,-(sp)    ; Push the serial request size
	pea.l	SerWritePort(a5)        ; Push the beginning of the data structure
	jsr	AllocPorts(pc)          ; Get the ports
	addq.l	#8,sp			; Pop everything
	tst.l	d0			; See if it all opened
	beq	CloseConsole		; If not close the console

	move.l	SerWriteReq(a5),a1      ; Move the write request into a1
	move.b	#SERF_SHARED,IO_SERFLAGS(a1) ; Set the flags
	clr.l	d0			; Unit 0
	clr.l	d1			; No flags
	lea.l	SerName(pc),a0          ; Put the ser name into a0
	jsr	_LVOOpenDevice(a6)      ; Open the device
	tst.l	d0			; See if it opened
	bne	KillSerPorts		; If not kill the ports

	move.l	SerWriteReq(a5),a0      ; Move the write request into a0
	move.l	SerReadReq(a5),a1       ; Move the read  request into a1
	move.l	IO_DEVICE(a0),IO_DEVICE(a1) ; Copy IO_DEVICE and IO_UNIT
	move.l	IO_UNIT(a0),IO_UNIT(a1) ; from the write request into the read request

	jsr	ConGet(pc)              ; Send out a read request for the console.

	jsr	SerGet(pc)              ; Send out a read request for the serial port.

;***********************************************
;****		   MAIN LOOP		    ****
;***********************************************

Mainloop
	clr.l	d0			; Clear out d0.
	move.b	WindowBit(a5),d1        ; Put window's sigbit into d1.
	bset.l	d1,d0			; Set it in d0.
	move.b	ConsoleBit(a5),d1       ; Put console's sigbit into d1.
	bset.l	d1,d0			; Set it in d0.
	move.b	SerialBit(a5),d1        ; Put serial's sigbit into d1.
	bset.l	d1,d0			; Set it in d0.
	jsr	_LVOWait(a6)            ; Wait for any of the sigbits.
	move.l	d0,WaitState(a5)        ; Save the sigbits that woke us up in a global var.

	move.b	ConsoleBit(a5),d1       ; Put console's sigbit in d1.
	btst.l	d1,d0			; See if it is one of the bits that woke us.
	beq	.1			; If not go to next test.
	move.l	SerWriteReq(a5),a1      ; Move the serial write request into a1
	move.w	#CMD_WRITE,IO_COMMAND(a1) ; Prepare to write
	lea.l	conbuffer(a5),a0        ; Load the conbuffer into the data
	move.l	a0,IO_DATA(a1)          ; Load its length into the length
	move.l	#1,IO_LENGTH(a1)
	jsr	_LVODoIO(a6)            ; Write it
	jsr	ConGet(pc)              ; Send out the console read request again

.1	move.l	WaitState(a5),d0        ; Load the wakeup bits again
	move.b	SerialBit(a5),d1        ; Put the serial sigbit into d1
	btst.l	d1,d0			; See if we have the ser bit
	beq	.2			; If not go to .2
	move.l	ConWriteReq(a5),a1      ; Else put the console write request into a1
	move.w	#CMD_WRITE,IO_COMMAND(a1) ; Prepare to write
	lea.l	serbuffer(a5),a0        ; Load the serbuffer as the data
	move.l	a0,IO_DATA(a1)
	move.l	#-1,IO_LENGTH(a1)       ; Set length for a null terminated string
	jsr	_LVODoIO(a6)            ; Write it
	jsr	SerGet(pc)              ; Send out the serial read request again

.2	move.l	WaitState(a5),d0        ; Load the wakeup bits yet again
	move.b	WindowBit(a5),d1        ; Load the window's sigbit into d1
	btst.l	d1,d0			; See if we got a close gadget hit
	beq	Mainloop		; If not loop again

;***********************************************
;****		   CLOSING		    ****
;***********************************************

_Close
	movea.l SerReadReq(a5),a1       ; Load the serial read request into a1
	jsr	_LVOCheckIO(a6)         ; See if it's still out
	tst.l	d0
	bne	.1			; If not jump to .1
	movea.l SerReadReq(a5),a1       ; Load the read request again
	jsr	_LVOAbortIO(a6)         ; Abort it
	movea.l SerReadReq(a5),a1       ; Load the read request again
	jsr	_LVOWaitIO(a6)          ; Wait for it to be aborted

.1	movea.l SerWriteReq(a5),a1      ; Load the write request into a1
	jsr	_LVOCloseDevice(a6)     ; Close the serial port

KillSerPorts
	pea.l	SerWritePort(a5)        ; Push the begining of the data structure
	jsr	FreePorts(pc)           ; Free the ports
	addq.l	#4,sp			; Pop everything

CloseConsole
	movea.l ConReadReq(a5),a1       ; Load the console write port into a1
	jsr	_LVOCheckIO(a6)         ; See if it's still out
	tst.l	d0
	bne	.1			; If not jump to .1
	movea.l ConReadReq(a5),a1       ; Load the read request again
	jsr	_LVOAbortIO(a6)         ; Abort the request
	movea.l ConReadReq(a5),a1       ; Load the read request again
	jsr	_LVOWaitIO(a6)          ; Wait for it to be aborted

.1	movea.l ConWriteReq(a5),a1      ; Load the write request into a1
	jsr	_LVOCloseDevice(a6)     ; Close the console

KillConPorts
	pea.l	ConWritePort(a5)        ; Push the begining of the data structure
	jsr	FreePorts(pc)           ; Free the ports
	addq.l	#4,sp			; Pop everything

KillWindow
	move.l	WindowPtr(a5),a0        ; Move the window ptr into a0
	movea.l _IntuitionBase(a5),a6   ; Load IntuitionBase into a6
	jsr	_LVOCloseWindow(a6)     ; Close the window

KillLib
.4	move.l	_IntuitionBase(a5),a1   ; Move IntuitionBase into a1
	movea.l 4.w,a6			; Load ExecBase into a6
	jsr	_LVOCloseLibrary(a6)    ; Close Intuition

KillMem
	adda.l	#GT_VarLength,a5	; Add our global variable length to a5
	unlk	a5			; Free our global variables
	clr.l	d0			; This returns "no errors" to AmigaDOS
ending	rts				; Return


; AllocPorts ( baseaddr, size )

AllocPorts
	move.l	a5,-(sp)                ; Save the contents of a5
	movea.l 8(sp),a5                ; Load the base addr into a5

	jsr	_LVOCreateMsgPort(a6)   ; Create a msg port
	move.l	d0,WritePort(a5)        ; Save it in the write port
	beq	PortsEnding		; If we didn't get it then end

	movea.l d0,a0			; Move the port into a0
	move.l	12(sp),d0               ; Move the size into d0
	jsr	_LVOCreateIORequest(a6) ; Create an I/O Request
	move.l	d0,WriteReq(a5)         ; Save it's addr
	beq	FreeWritePort		; If we didn't get it then free the write port

	jsr	_LVOCreateMsgPort(a6)   ; Create a read port
	move.l	d0,ReadPort(a5)         ; Save it's addr
	beq	FreeWriteReq		; If we didn't get it then free the write req
	movea.l d0,a0			; Move the port into a0
	move.b	MP_SIGBIT(a0),SignalBit(a5) ; Save it's sigbit

	move.l	12(sp),d0               ; Move the size into d0
	jsr	_LVOCreateIORequest(a6) ; Create a read request
	move.l	d0,ReadReq(a5)          ; Save it's addr
	beq	FreeReadPort		; If we didn't get it free the read port
	moveq	#1,d0			; Put a 1 in d0 to show we opened everything
	movea.l (sp)+,a5                ; Pop the previous value of a5
	rts

FreePorts
	move.l	a5,-(sp)                ; Save the contents of a5
	movea.l 8(sp),a5                ; Load the base addr into a5

FreeReadReq
	move.l	ReadReq(a5),a0          ; Move the read request into a0
	jsr	_LVODeleteIORequest(a6) ; Delete it

FreeReadPort
	move.l	ReadPort(a5),a0         ; Move the read port into a0
	jsr	_LVODeleteMsgPort(a6)   ; Delete it

FreeWriteReq
	move.l	WriteReq(a5),a0         ; Move the write request into a0
	jsr	_LVODeleteIORequest(a6) ; Delete it

FreeWritePort
	move.l	WritePort(a5),a0        ; Move the write port into a0
	jsr	_LVODeleteMsgPort(a6)   ; Delete it
PortsEnding
	clr.l	d0			; Clear d0 to indicate Alloc ports failed
	movea.l (sp)+,a5                ; Pop the previous value of a5
	rts


ConGet
	movea.l ConReadPort(a5),a0      ; Move the read port into a0
	jsr	_LVOGetMsg(a6)          ; Get the message in it
	movea.l ConReadReq(a5),a1       ; Move the read request into a1
	move.w	#CMD_READ,IO_COMMAND(a1) ; Prepare to read
	lea.l	conbuffer(a5),a2        ; Load the conbuffer into the data
	move.l	a2,IO_DATA(a1)
	move.l	#1,IO_LENGTH(a1)        ; Load 1 as the length
	jsr	_LVOSendIO(a6)          ; Send it out
	rts


SerGet
	lea.l	serbuffer(a5),a0        ; Load address of serbuffer into a0
	jsr	ClearBuffer(pc)         ; So we can clear it out
	move.l	SerReadPort(a5),a0      ; Move the address of the ReadPort into a0
	jsr	_LVOGetMsg(a6)          ; Get the message in the port.
	move.l	SerWriteReq(a5),a1      ; Move our write request into a1
	move.w	#SDCMD_QUERY,IO_COMMAND(a1) ; Get it ready for a querry
	jsr	_LVODoIO(a6)            ; Do it.
	move.l	SerWriteReq(a5),a0      ; Load the write request into a0
	move.l	IO_ACTUAL(a0),d0        ; Put the number of chars got into d0
	tst.l	d0			; Test it
	bne	.1			; Jump to .1 if it's not zero
	moveq	#1,d1			; Ask for 1 char
	jmp	.3(pc)                  ; Jump to .3
.1	cmp.l	#79,d0			; Compare the number to the size of the buffer
	ble	.2			; If chars waiting is greater than the buffer then jump to .2
	moveq	#79,d1			; Else move the buffer size into d1
	jmp	.3(pc)                  ; Jump to .3
.2	move.l	d0,d1			; Move the number of chars waiting into d1
.3	move.l	SerReadReq(a5),a1       ; Put the read request into a1
	move.w	#CMD_READ,IO_COMMAND(a1) ; Prepare to read
	lea.l	serbuffer(a5),a0        ; Load the address of the buffer into a0
	move.l	a0,IO_DATA(a1)          ; Then put it in the data part of the request
	move.l	d1,IO_LENGTH(a1)        ; Load the length
	jsr	_LVOSendIO(a6)          ; Do it
	rts

ClearBuffer
	moveq	#79,d0			; Just a quick routine to clear
.1	move.b	#0,(a0)+                ; the serial buffer
	dbf	d0,.1
	rts


windowdef
	dc.w	0,0			; The (x,y) position of the window.
	dc.w	640,200 		; Width and Height.
	dc.b	-1,-1			; Pens.
	dc.l	CLOSEWINDOW		; IDCMP Flags.
	dc.l	WINDOWDEPTH!WINDOWCLOSE!ACTIVATE!WINDOWDRAG!WINDOWSIZING!NOCAREREFRESH
	dc.l	0			; NO gads.
	dc.l	0			; NO checkmark.
	dc.l	WindTitle		; The window title.
	dc.l	0			; No personal Screen.
	dc.l	0			; No Bitmap.
	dc.w	100,50,640,400		; Min and Max window sizes.
	dc.w	WBENCHSCREEN		; Where to put it.

IntuiName   dc.b    'intuition.library',0
SerName     dc.b    'serial.device',0
ConName     dc.b    'console.device',0
WindTitle   dc.b    'nanoTerm 3.0 by Jason Fonseca',0
VerStr	    dc.b    '$VER: nanoTerm 3.0',0 ; This is so the "version" command will work.

	    END
