;EXECPRT.ASM 12/09/88
;COPYRIGHT 1988 BY DANIEL WOLF PH.D.
;EXEC I/O EXAMPLE FOR PRINTER OUTPUT
;USES DEVICE-BASED I/O WITH IOBLOCK AND REPLY MSGPORT

SIZE.STDIO     EQU $30
SIZE.MP        EQU $22
CMD_WRITE      EQU $3

LVO.ALLOCMEM   EQU $FFFFFF3A
LVO.ADDPORT    EQU $FFFFFE9E
LVO.FINDTASK   EQU $FFFFFEDA
LVO.FREEMEM    EQU $FFFFFF2E
LVO.GETMSG     EQU $FFFFFE8C
LVO.WAITPORT   EQU $FFFFFE80
LVO.DOIO       EQU $FFFFFE38
LVO.CHECKIO    EQU $FFFFFE2C
LVO.ABORTIO    EQU $FFFFFE20
LVO.SENDIO     EQU $FFFFFE32
LVO.WAITIO     EQU $FFFFFE26
LVO.OPENDEVICE EQU $FFFFFE44
LVO.FREESIGNAL EQU $FFFFFEB0
LVO.CLOSEDEVICE EQU $FFFFFE3E
LVO.ALLOCSIGNAL EQU $FFFFFEB6

IO.COMMAND     EQU $1C
IO.DEVICE      EQU $14
IO.DATA        EQU $28
IO.LENGTH      EQU $24
LIST.HEAD      EQU $0
LIST.TAIL      EQU $4
MEMF_PUBLIC    EQU $0
MEMF_CLEAR     EQU $10000
MP.MSGLIST     EQU $14
MP.SIGTASK     EQU $10
MP.SIGBIT      EQU $F
MP.FLAGS       EQU $E
MSG.REPLYPORT  EQU $E
NODE.PRED      EQU $4
NODE.NAME      EQU $A
NODE.TYPE      EQU $8
NODE.PRI       EQU $9
NT_MSGPORT     EQU $4
NT_MESSAGE     EQU $5
PA_SIGNAL      EQU $0

MAIN
 BSR OPENPRINTER        ;SET UP REPLY MSGPORT, SIGBIT, IOBLOCK THEN OPEN IT
 TST.L D0               ;IF PRINTER OPENING FAILED THEN
 BNE ERROR              ; BRANCH OUT OF HERE

 MOVE.L IOREQ,A1        ;ELSE SET UP POINTERS TO IOBLOCK AND ASCII TEXT
 LEA IOTEXT,A0
 BSR PRINTERWRITE       ;CALL PRINTER OUTPUT ROUTINE

; MOVE.L #18000,D0      ;A VERY DUMB DELAY LOOP TO WASTE SOME TIME
;LOOP                   ;IF YOU CHANGE PRINTERWRITE TO USE SENDIO.
; MOVE.L A0,-(SP)       ;DOIO DOESN'T NEED THIS BECAUSE IT WAITS TIL I/O DONE.
; MOVE.L (SP)+,A0       ;WITHOUT THIS DELAY, ABORTIO (IN CLOSEPRINTER)
; DBRA D0,LOOP          ;HAPPENS IMMEDIATELY  - TOO FAST FOR SENDIO TO FINISH

 BSR CLOSEPRINTER       ;FREE ALL RESOURCES ASSOCIATED WITH PRINTER DEVICE

 MOVEQ #0,D0
ERROR                   ;AND WAVE GOODBYE
 RTS

;**************** APPROPRIATE EXEC AND I/O MACROS

NEWLIST MACRO     ;An
 MOVE.L \1,(\1)                         ;MOVE An TO (An)
 MOVE.L \1,LIST.HEAD(\1)                ;SHUFFLE HEAD AND TAIL POINTERS
 ADD.L #LIST.TAIL,LIST.HEAD(\1)
 CLR.L LIST.TAIL(\1)
 MOVE.L \1,LIST.TAIL+NODE.PRED(\1)      ;SO THIS PORT IS FIRST IN LIST
 ENDM

OPENDEVICE MACRO  ;*NAME,UNIT,*IOREQUEST,FLAGS
 LEA \1,A0                      ;PUT NAME POINTER  INTO A0
 MOVE.L #\2,D0                  ;PUT UNIT NUMBER   INTO D0
 MOVE.L \3,A1                   ;PUT IOREQ POINTER INTO A1
 MOVE.L #\4,D1                  ;PUT IOREQ FLAGS   INTO D1
 SYSCALL OPENDEVICE             ;NOW MAKE THE CALL
 ENDM

CLOSEDEVICE MACRO ;*IOREQUEST   ;SET UP TO CLOSE A DEVICE
 MOVE.L \1,A1                   ;PUT IOREQ POINTER INTO A1
 SYSCALL CLOSEDEVICE            ;NOW MAKE THE CALL
 ENDM

;*************** AND A FEW SIMPLE PROGRAMMING MACROS

SYSCALL MACRO                   ;CALL A EXEC LIBRARY ROUTINE
 MOVE.L 4,A6
 JSR LVO.\1(A6)
 ENDM

CALL MACRO                      ;CALL A KERNEL ROUTINE
 JSR LVO.\1(A6)
 ENDM

PUSH MACRO                      ;PUSH A REGISTER ONTO STACK
 MOVEM.L \1,-(SP)
 ENDM

PULL MACRO                      ;PULL A REGISTER FROM STACK
 MOVEM.L (SP)+,\1
 ENDM

PUBLICMEM MACRO    ;SIZE        ;ALLOCATE SOME PUBLIC MEMORY
 MOVE.L #MEMF_PUBLIC!MEMF_CLEAR,D1
 MOVE.L \1,D0
 SYSCALL ALLOCMEM
 ENDM

;*************** GENERAL PURPOSE CREATE AND DELETE MSGPORT ROUTINES

MAKEAPORT        ;ENTER W/ PTR TO NAME IN A0, PRIORITY IN D0
 PUSH D0/A0                        ;STASH THE REGS
 MOVE #-1,D0                       ;DON'T CARE WHICH SIGBIT ALLOCATED FOR PORT
 SYSCALL ALLOCSIGNAL               ;GET A SIGBIT
 MOVE.B D0,D7                      ;STASH SIGBIT IN D7
 CMPI.B #-1,D0                     ;IF ERROR -
 BEQ ERR_PORT                      ; BRANCH OUT FROM HERE
 PUBLICMEM #SIZE.MP                ;ELSE ALLOCATE MEM FOR A MESSAGE PORT
 TST.L D0                          ;IF ERROR -
 BEQ.S ERR_PORT                    ; BRANCH OUT FROM HERE
 MOVE.L D0,A4                      ;ELSE PUT POINTER TO ALLOCATED MEM IN A4
 MOVEQ #0,D0
 MOVE.L D0,A1                      ;FOR THIS TASK -
 CALL FINDTASK                     ;FIND THE TASK CONTROL BLOCK
 MOVE.L D0,MP.SIGTASK(A4)          ;TELL PORT WHERE THE TCB IS
 PULL D0/A0                        ;RESTORE THE REGS
 MOVE.L A0,NODE.NAME(A4)           ;TELL LISTNODE OF PORT ITS NAME
 MOVE.B D0,NODE.PRI(A4)            ;TELL LISTNODE OF PORT ITS PRIORITY
 MOVE.B #NT_MSGPORT,NODE.TYPE(A4)  ;TELL LISTNODE OF PORT ITS TYPE
 MOVE.B #PA_SIGNAL,MP.FLAGS(A4)    ;TELL PORT ITS FLAGS
 MOVE.B D7,MP.SIGBIT(A4)           ;TELL PORT WHICH SIGBIT WAS ASSIGNED TO IT
 CMPA.L #0,A0                      ;IF NO NAME
 BEQ.S PRIVATEPORT                 ; THEN ITS A PRIVATE PORT
PUBLICPORT                         ;ELSE IT HAS A NAME AND ITS PUBLIC
 MOVE.L A4,A1                      ;PUT PTR TO MEM OF PORT INTO A1
 CALL ADDPORT                      ;AND ADD THIS PORT TO EXISTING ONES
 BRA NOPORTERR                     ;AND FINISH UP
PRIVATEPORT
 LEA MP.MSGLIST(A4),A0             ;SET UP PORT AS FIRST IN LIST
 NEWLIST A0                        ;SEE MACRO ABOVE
NOPORTERR
 MOVE.L A4,D0                      ;PUT ADDRESS OF MSGPORT IN D0
 RTS
ERR_PORT                           ;IF ERROR, SET D0 = 0
 MOVEQ #0,D0
 RTS

KILLAPORT       ;ENTER W/ POINTER TO MSGPORT IN A0
 PUSH A0                        ;STASH THE POINTER ON THE STACK
 MOVEQ #0,D0
 MOVE.B MP.SIGBIT(A0),D0        ;AND FREE UP THE SIGNAL BIT
 CALL FREESIGNAL                ;PREVIOUSLY ALLOCATED FOR THIS PORT
 PULL A1                        ;AND THE PORT'S MEMORY
 MOVE.L #SIZE.MP,D0
 SYSCALL FREEMEM
 RTS

;**************** PRINTER DEVICE SUPPORT CODE

OPENPRINTER
 MOVEQ #0,D0               ;SET NAME AND PRIORITY OF MSGPORT = 0
 MOVE.L D0,A0
 BSR MAKEAPORT             ;AND CREATE REPLY MSGPORT FOR PRINTER I/O BLOCK
 TST.L D0
 BEQ ERR_OPENPRT
 MOVE.L D0,PMPORT          ;ELSE THIS IS ADDR OF PRINTER MSGPORT
 BSR CREATEPRTIO           ;USE MSGPORT TO HELP SET UP AN IOBLOCK
 TST.L D0                  ;IF IOBLOCK BAD THEN
 BEQ ERR_OPENPRT           ; BRANCH OUT FROM HERE
 MOVE.L PRTBUF,IO.DATA(A0) ;ELSE ATTACH BUFFER TO IOBLOCK
 MOVE.L #-1,IO.LENGTH(A0)  ;AND SET ARBITRARY LENGTH I/O
OPENPRT
 OPENDEVICE DEVNAME,0,IOREQ,0     ;NOW ATTACH PRINTER DEVICE TO IOBLOCK
 TST.L D0                         ;IF NO PROBLEM THEN
 BEQ.S END_OPENPRT                ; BRANCH TO END WITH D0 = 0
ERR_OPENPRT
 MOVE.L #1,D0                     ;ELSE FAIL WITH       D0 = 1
END_OPENPRT
 RTS

CREATEPRTIO
 PUBLICMEM #SIZE.STDIO            ;GET SOME MEM FOR AN IOBLOCK
 TST.L D0                         ;IF UNSUCCESSFUL THEN
 BEQ ERR_PRTIO                    ; BRANCH OUT FROM HERE W/ D0 = 0
 MOVE.L D0,IOREQ                  ;ELSE D0 = PTR TO IOBLOCK
 MOVE.L D0,A0
 MOVE.B #NT_MESSAGE,NODE.TYPE(A0) ;SET ITS LISTNODE TYPE = 'MESSAGE'
 MOVE.B #0,NODE.PRI(A0)           ;SET ITS LISTNODE PRIORITY = 0
 MOVE.L PMPORT,MSG.REPLYPORT(A0)  ;SET ITS REPLYPORT = MSGPORT
ERR_PRTIO
 RTS                              ;EXIT W/ D0 = ADDR OF IOBLOCK (0 IF ERROR)

CLOSEPRINTER
 MOVE.L IOREQ,A1                ;GET ADDRESS OF IOBLOCK
 CALL CHECKIO                   ;SEE IF I/O DONE
 TST.L D0                       ;IF DONE THEN
 BNE CLOSEPRT                   ; BRANCH OUT OF HERE
 MOVE.L IOREQ,A1                ;ELSE GET ADDRESS OF IOBLOCK
 CALL ABORTIO                   ;AND FORCE I/O TO END
 MOVE.L PMPORT,A0               ;NOW WAIT FOR A MESSAGE AT THE REPLY PORT
 CALL WAITPORT
 MOVE.L PMPORT,A0
 CALL GETMSG                    ;GET MESSAGE FROM PORT SAYING I/O ABORTED
CLOSEPRT
 CLOSEDEVICE IOREQ              ;NOW CLOSE DEVICE ATTACHED TO THE IOBLOCK
 MOVE.L PMPORT,A0
 BSR KILLAPORT                  ;AND FREE MSGPORT'S SIG BIT AND MEMORY
 MOVE.L IOREQ,A1                ;AND THE IOBLOCK'S MEMORY, TOO!
 MOVE.L #SIZE.STDIO,D0
 SYSCALL FREEMEM
 RTS

PRINTERWRITE             ;ENTER W/ PTR TO IOBLOCK IN A1
                         ;      W/ PTR TO NULL-TERMINATED ASCII IN A0

 MOVE #CMD_WRITE,IO.COMMAND(A1) ;PUT 'WRITE' INTO IOBLOCK COMMAND FIELD
 MOVE.L A0,IO.DATA(A1)          ;PTR TO NULL-TERMINATED DATA
 MOVE.L #-1,IO.LENGTH(A1)       ;#BYTES TO WRITE (-1 MEANS UNTIL A NULL)
 CALL DOIO                      ;AND CALL THE I/O ROUTINE (TRY SENDIO HERE)
 RTS

 DATA

PMPORT DC.L 0                   ;PTR TO IOBLOCK REPLY MSGPORT
IOREQ DC.L 0                    ;PTR TO PRINTER IOBLOCK
PRTBUF DS.B 16                  ;ABSOLUTELY NECESSARY LITTLE BUFFER

DEVNAME
 DC.B 'printer.device',0        ;NAME OF PRINTER DEVICE
 EVEN
IOTEXT
 DC.B 10,'Hello from the heart of Amiga Exec',13,10,0
 EVEN
 END

