rlines	=50
;	*-------+---------------------------------------*
;	|Name	| SLOWPAROUT				|
;	|Version| 0.14	Comment:Timerwait bug, try, lvl6|
;	+-------+---------------------------------------+
;	|	Amiga->Amiga par sender (see ParIn)	|
;	+-----------------------------------------------+
;	|	  Copyright (C) 1993 MACROCOSM		|
;	*-----------------------------------------------*
;970501 now adds CR after LF for "printout".
;0.14 Fuck lvl6, try raswait just to test
;0.13 Levle6req is polled for timing
;0.12 Timerwait, filelen bugs with large files tpy 49149
;0.11 made HPOStimerloop instead of VPOS timer
;@below=comments for parin
;0.13:fixed rpmode bug
;0.12:total remake, chksum etc. testing.. OBS! exit via double 00,ff!
;0.11:optimizing parrec(), sends chksum
;if OpenLib/CloseLib is used, set LibBits!
;Bits 0-7=Dos,Int,Gfx,Con,Icon,MathF,MathT,MathI
;Presume that all macros destroy D0-D1/A0-A1, and preserves all other regs.
;Macros that use supplied work-registers preserve ALL registers.
;When calling a macro, A5 MUST BE INIT-ADR+32768 AND A6 MUST BE $DFF002!
;Most Macros CONTAIN GLOBAL LABELS, so use local only in-between Macro calls.
;Labels to define if relevant macros are used:
;Params (#Max longs)
;CurrHdl (current filehandle storage long)
;Buf (blk.b 81,0) for conversion & input routines


Spd	=3
BSize	=49152
LibBits=%00000101
INCLUDE "CO:Symbols.S"
INCLUDE "CO:Makaron.S"

START:
	move.l a0,ParamP
	move.w d0,ParamLen
	clr.b -1(a0,d0.w)		;make paramstr asciiz
	INIT
	OPENLIBS
	ALLOC #BSize+128,_Any
	move.l d0,BAddr-R(a5)
	beq.w BADERR

	OPENF d7,ParamP(PC),#_Old		;ram:q.dms
	move.l d7,Fhdl
	beq ERR
	OPENF d7,#ParName,#_Old		;OPEN PAR to own it
	move.l d7,ParHdl
	beq.s PARERR
	;-----------------
ML:	move.l #BSize,d6
	move.l fhdl(PC),d7
	READF d7,BAddr(PC),d6		;read buf
	move.l d0,d6			;save actual #bytes read for cmp later
	beq.s DONE			;eof? then done
	move.l BAddr(PC),a0
	bsr ParSend
	cmp.l #Bsize,d6
	blo.s DONE			;if last chunk of file read, done
	moveq #25,d7			;wait min 500msec (25frames)
.ll:	bsr WtSync
	dbra d7,.ll
	btst #6,$bfe001
	bne.s ML			;@break send with LMB

DONE:	move.l ParHdl(PC),d7		;close par
	CLOSEF d7	
	move.l fhdl(PC),d7
	CLOSEF d7

PARERR:
ERR:	FREE BAddr(PC),#BSize+128
BADERR:	CLOSELIBS
	EXIT
	RTS
Fhdl:	dc.l 0
;;**********************************************************************
ParSend:	;receive par data.a0=bufaddr,d0.l=bufsize.Ret #read bytes in d0.l
	MOVEM.L D1-A6,-(SP)
	move.l BAddr(PC),a0
	move.l a0,a1			;buf lower bound
	add.l d0,a1			;buf upper bound

	MOVEM.L D0-A6,-(SP)
	move.l 4.w,a6
	jsr EDisable(a6)		;no ints
	MOVEM.L (SP)+,D0-A6

;	bsr Start6			;start level 6 "int" (for reqpoll)

	LEA $BFE101,A5
	LEA $DFF000,A6
	move.w 2(a6),-(SP)
	move.w #$0200,$96(a6)		;low dma off $017f=bpl,blt,spr,dsk,aud dma off

	;a2-a3 free from here on

	;---init--------------
.i:	LEA $180(A6),A6			;OBS!OBS! only in this loop!
	moveq #0,d3			;const (esc code & clear-const)
	move.b #$80,d4			;const (block ctrl code)
	moveq #0,d0			;so upper 24 bits always 0!(move.w)
	moveq #0,d2			;so upper 24 bits always 0!(move.w)
	clr.l RLen			;@opti.report length 0 if EOF
	clr.l RChk
	clr.l RChk+4
;---------------------------------------
	;---start writin'--------------
	move.b #-1,$200(a5)		;DDRB, PRB (Parallel D0..7)=OUTputs!
	;-----------------
;	move.b #$00,d0			;HEAD:send $00,$82 ***
;	bsr ParOut
;	move.b #$82,d0
;	bsr ParOut
	;---loop--------------
.wl:	cmp.l a0,a1
	ble.s .done
	move.b (a0)+,d0
	cmp.b #10,d0
	bne.s .cont
	bsr ParOut			;@lf=cr+lf
	move.b #13,d0
.cont:	bsr ParOut			;@PLAIN FEED IT!
	bra.s .wl
	;---loopend--------------
.done:
;---------------------------------------
.e:
	LEA $DFF000,A6
	move.w (SP)+,d1
	bset #15,d1
	move.w #$7fff,$96(a6)
	move.w d1,$96(a6)

;	bsr Stop6			;stop level 6 "int" (for reqpoll)

;	move.b #0,$200(a5)		;DDRB, PRB (Parallel D0..7)=INputs!

	MOVEM.L D0-A6,-(SP)
	move.l 4.w,a6
	jsr EEnable(a6)		;ints on
	MOVEM.L (SP)+,D0-A6

	MOVEM.L (SP)+,D1-A6
	RTS

;;**************************************
RevBytes:		;rev. bytes until ptrs meet.a0=start,a1=stop. uses d1
.l:	move.b (a0),d1
	move.b -(a1),(a0)+
	move.b d1,(a1)
	cmp.l a1,a0
	blt.s .l
	RTS
;;**************************************
ParOut:				;d0=byte to send
	MOVEM.L D0-A6,-(SP)
	move.b d0,(a5)			;feed to PRB
	move.w d0,(a6)			;feed to bkgr color
	lea $dff006,a4
	moveq #rlines,d7			;min. #rasterlines to wait
.ll:	move.b (a4),d2
.l:	cmp.b (a4),d2			;wait til next rline
	beq.s .l
	dbra d7,.ll

;.l:	move.w $1e-$180(a6),d2
;	btst #13,d2
;	bne.s .l
;	move.w #$2000,$9c-$180(a6)		;clrreq

;	moveq #1,d0			;half a scanline
;	bsr TimerWt
;	lea $dff006,a4			;(V)HPOSR
;	move.w (a4),d7
;	add.w #127,d7			;addvalue,#horiz.cycles to wait
;.l:	cmp.w (a4),d7
;	bgt.s .l
	MOVEM.L (SP)+,D0-A6
	move.b d0,d1			;D1=SENT BYTE! (for last-cmp)
	RTS
****************************************
;;**************************************
ReadPar:				;read par to [a0..a1] until modechange
	clr.b RPmode			;@opti
	moveq #0,d5			;CHKSUM:0
	;---dynamic readloop--------------
.l:	move.b d0,d2			;save last databyte if 0
	bsr WaitNew			;read byte
	beq.s .ctrl			;0=ctrl escapecode!

.data:	move.b d0,(a0)+			;1-$ff=data!
	add.l d0,d5			;add to chksum!
.cont:	cmp.l a1,a0
	bmi.s .l			;buffull?
	bra.s .e			;then stop!

	;---ctrl codes--------------
.ctrl:					;DON't save d0=0 as last databyte
	bsr WaitNew			;read ctrl code (d0)
	bmi.s .spec			;$80-$ff=special

	move.w d0,(a6)			;@temp. change color
	move.w d0,d7
	subq.w #1,d7			;compens. for dbra
.rpt:	move.b d2,(a0)+			;last DATA byte
	add.l d2,d5			;add to chksum!;@opti'd
	dbra d7,.rpt
.cont2:	cmp.l a1,a0
	bmi.s .l			;buffull?
	bra.s .e			;then stop!

.spec:	cmp.b d4,d0			;d4=$80=data $00(temp)
	bne.s .MODE
	move.b d3,(a0)+			;d3=const $00
;	add.l d0,d5			;add to chksum!;@opti'd
.cont3:	cmp.l a1,a0
	bmi.s .l			;buffull?
	bra.w .e			;then stop!
	;---switch datadest!--------------
.e:
	bra.s .x			;buffer filled to the rim,exit,no mode
	;@beq above.otherwise too small buffer!!

.wm2:	move.w #$fff,$dff180		;indicate TOO SMALL BUFFER!
	move.w #$000,$dff180
	btst #10,$dff016		;RMB
	bne.s .wm2
.h2:	bra.s .h2			;halt

.MODE:	move.b #1,RPmode		;@opti
.x:	move.b d0,RPd0
	RTS				;d0=mode ($fa-$ff),d5=chk,a0=last+1
;---------------------------------------
RPmode:	dc.b 0
RPd0:	dc.b 0
;;**************************************
WaitNew:	;Wait for new parbyte. d0=old parbyte.ret:d0=new,tested!
	move.b d0,d1
.wdiff:	move.b (a5),d0			;read par D
	cmp.b d0,d1
	beq.s .wdiff			;wait until different

;.diff:	move.b (a5),d2			;read par D
;	cmp.b d0,d1
;	beq.s .wdiff			;wait until different

					;REPEAT UNTIL BEEN SAME FOR A WHILE
	REPT Spd			;@rplc with dynamic wait?or param?
	cmp.b (a5),d0
	bne.s .wdiff			;nope-changed too fast, =NOISE,re-wait
	ENDR
	;---real byte read!--------------
;	and.w #$00ff,d0
;	move.w d0,$dff180		;@just test!
	tst.b d0
	RTS
;;**************************************
WtSync:				;waits 1 frame to start of frame
	MOVEM.L D0-A6,-(SP)
	lea $dff000,a6
.wlp:	move.b $5(A6),d0
	btst #0,d0
	BEQ.S .wlp
.loop:	move.b $5(A6),d0
	btst #0,d0
	BNE.S .loop
	MOVEM.L (SP)+,D0-A6
	RTS
;;**************************************
Start6:
	MOVEM.L D0-A6,-(SP)
	lea $bfd400,a5			;reg. base
	lea LoHi(PC),a0
	move.b (a5),(a0)+		;save old timerA values
	move.b $100(a5),(a0)+

	move.b #$2d,(a5)		;set new timerA value lo
	move.b #$00,$100(a5)		;to approx 1 scanline,hi
	move.b #$81,$900(a5)		;bit#7=set/clr,#0=TimerA

	move.b #$11,$a00(a5)		;Bit#0=TimerA,#4=strobe,#3=one-shot

	MOVEM.L (SP)+,D0-A6
	RTS

Stop6:
	MOVEM.L D0-A6,-(SP)
	lea $bfd400,a5			;reg. base
	lea LoHi(PC),a0
	move.b (a0)+,(a5)		;rstr old timerA values
	move.b (a0)+,$100(a5)
	MOVEM.L (SP)+,D0-A6
	RTS
	

LoHi:
dc.w 0
;;**********************************************************************
ParHdl:	dc.l 0
ParamP:	dc.l 0
ParamLen:	dc.w 0
BAddr:	dc.l 0
Len:	dc.l 0
	blk.b 128,0		;extra so no check-per-byte
Chk:	dc.l 0,0		;upto 8 bytes chksum!
	blk.b 128,0		;extra so no check-per-byte
;---------------------------------------
RLen:	dc.l 0			;don't move vv!
RChk:	dc.l 0,0		;upto 8 bytes chksum!
;---------------------------------------
ParName:dc.b "PAR:",0
END


;parallel port hardware addresses and bits
CIAF_PRTRBUSY   equ   0
ciab_pra         equ   $BFD000
ciab_ddra      equ   $BFD200

ciaa_ddrb      equ   $BFE301
ciaa_prb         equ   $BFE101

;for ciaa.resource
_LVOSetICR         equ   -24
_LVORemICRVector   equ   -12
_LVOAddICRVector   equ   -6
_LVOAbleICR         equ   -18
CIAICRB_FLG         equ   4
CIAICRF_FLG         equ   16   ;bit #4 set for handshaking

;for misc.resource, see "Libraries and Devices" pg C-8
_LVOGetMiscResource   equ   -6
_LVOFreeMiscResource   equ   -12
MR_PARALLELPORT   equ   2
MR_PARALLELBITS   equ   3

   XDEF   _main
_main:
CIAAName:	dc.b "ciaa.resource",0
Name:	dc.b "ParOut",0		;name of us, the resource grabbers
;================ OPEN RESOURCES (used to grab hardware responsibly)========
; In order to obtain exclusive access to hardware in the Amiga with the
; knowledge and consent of Exec, we must "allocate" the hardware via the
; CIAA and Misc resources. These software modules help make it easier to
; set up the hardware, as well as informing Exec that no other task should
; be allowed to use the hardware while we "own" it. In this way, we can
; write directly to parallel port hardware addresses, without worrying about
; some task using the printer or parallel devices. Exec will prevent those
; devices from gaining access to the hardware, as those devices "allocate"
; the hardware via the resources, just as we are about to do. The resources
; only allow one task to "own" the hardware at a time. See appendix C of the
; "Libraries and Devices" manual.
   ;---Open the ciaa.resource
      lea      CIAAName,a1
      moveq      #0,d0
      movea.l   4.w,a6
      jsr      _LVOOpenResource(a6)
      move.l   d0,_CIAAResource
      bne.s      ociaa
      moveq      #10,d0
      rts
   ;---Open the misc.resource
ociaa:   lea      MiscName,a1
      moveq      #0,d0
      jsr      _LVOOpenResource(a6)
      move.l   d0,_MiscResource
      bne.s      omisc
      moveq      #20,d0
      rts
;=======================GRAB PARALLEL HARDWARE===========================   
;This is where we get our 8bits for parallel transfer. Remember, if some
;other task "allocated" MR_PARALLELPORT before us, then this routine will
;return a non-zero value (i.e. the Name of the dirty sod who stole the
;parallel port). This means "forget it chump. You can't use the hardware
;until this other bozo frees it". If we get a 0, then the hardware is ours.
omisc:   lea      Name,a1               ;Our Name
      moveq      #MR_PARALLELPORT,d0   ;unit
      movea.l   d0,a6                  ;MiscResource Base
      jsr      _LVOGetMiscResource(a6)
      move.l   d0,d1
      beq.s      port
   ;---Oops. Someone else must have grabbed this hardware before us.
      moveq      #30,d0
      rts
;This is where we get busy(bit 0), pout(bit 1), sel(bit 2) lines
port:   lea      Name,a1               ;name
      moveq      #MR_PARALLELBITS,d0   ;unit
      jsr      _LVOGetMiscResource(a6)
      move.l   d0,d1
      beq.s      bits
   ;---Oops. Someone else must have grabbed this hardware before us.
      moveq      #40,d2
      bra      Free1
;================== ALLOCATE A SIGNAL TO WAKE UP _main =====================
   ;---Allocate a Signal for waking us up
bits:   moveq      #-1,d0
      movea.l   _SysBase,a6
      jsr      _LVOAllocSignal(a6)
      move.b   d0,d7
      bpl.s      mask
      ;---If error, Free MR_PARALLELPORT and MR_PARALLELBITS
      moveq      #50,d2
      bra      Free2
   ;----Make a mask of the signal and store it where our handshake interrupt
   ;    code can get it; in the IS_DATA field of our interrupt structure.
mask:   moveq      #0,d1
      Bset.l   d0,d1
      move.l   d1,SigMask



;**** Now we own the parallel port, let's get a handshake interrupt setup ****
; We now add an interrupt vector for the CIAICRB_FLG bit of the parallel
; port. This is the "handshaking" bit that an external device connected to
; the parallel port would toggle to inform us when it is ready for something.
      lea      CIAAInterrupt,a1
      moveq      #CIAICRB_FLG,d0
      movea.l   _CIAAResource,a6
      jsr      _LVOAddICRVector(a6)
      move.l   d0,d1
      beq.s      hand
   ;---If an error, free hardware so others can use it
      moveq      #50,d2
      bra.s      Free3
;-----Disable HandShake interrupt (before we set up data direction regs)----
hand   moveq      #CIAICRF_FLG,d0
      jsr      _LVOAbleICR(a6)
;=========== Now we write the hardware addresses directly ===============
;---make CIAA port B all 8 "outputs" (a 1 bit means that pin is an out)-----
      move.b   #$FF,ciaa_ddrb
;----make BUSY, SEL, POUT lines "inputs" on CIAA port A---
      andi.b   #$FF,ciab_ddra
;----Clear any pending HandShake interrupts (before we enable handshaking)
      moveq      #CIAICRF_FLG,d0
      jsr      _LVOSetICR(a6)
;----Finally, enable HandShake interrupt----
      moveq      #0,d0
      move.b   #144,d0         ;CIAICRF_FLG with enable (bit #7) set
      jsr      _LVOAbleICR(a6)
;========================== OUTPUT DATA LOOP ===============================
; We are now going to output our NULL-terminated string one byte at a time,
; waiting for the external device to ACK (handshake) after each received byte.
      lea      Message,a2      ;the string to output
      movea.l   _SysBase,a6
aByte   move.b   (a2)+,d0
      beq.s      Free5            ;end of our string yet?
   ;---Make sure that the device is not busy. We do this by busy polling
   ;   the PRTRBUSY line of ciab.ciapra. Actually, if the device were taking
   ;    a long time, this busy wait would eat up processor time, but we
   ;    assume that the device accepts data quickly.   
busy   Btst.b   #CIAF_PRTRBUSY,ciab_pra
      bne.s      busy
   ;---write this ascii byte to output port (viola! Out the parallel port)
      move.b   d0,ciaa_prb
   ;----Wait for the external device to ACK (on the HandShake line)
   ;    Gee, I hope that you have some intelligent device hooked up now!!!
      move.l   SigMask,d0
      jsr      _LVOWait(a6)
      bra.s      aByte
;=================Free resources (parallel port) and exit=================
; Now free the hardware for someone else to use
   ;---error code = OK
Free5   moveq      #0,d2
   ;---Disable the HandShake interrupt (before we remove it)
      moveq      #16,d0
      movea.l   _CIAAResource,a6
      jsr      _LVOAbleICR(a6)
   ;---Remove the HandShake interrupt
Free4   lea      CIAAInterrupt,a1
      moveq      #4,d0
      movea.l   _CIAAResource,a6
      jsr      _LVORemICRVector(a6)
   ;---Free Signal
Free3   move.b   d7,d0
      movea.l   _SysBase,a6
      jsr      _LVOFreeSignal(a6)
   ;---Free MR_PARALLELBITS
Free2   moveq      #MR_PARALLELBITS,d0
      movea.l   _MiscResource,a6
      jsr      _LVOFreeMiscResource(a6)
   ;---Free MR_PARALLELPORT
Free1   moveq      #MR_PARALLELPORT,d0
      movea.l   _MiscResource,a6
      jsr      _LVOFreeMiscResource(a6)
   ;---return error code to DOS
      move.l   d2,d0
      rts

;=========================================================================
; This routine is called (via the operating system) whenever an external
; device sets the "handshaking" line of the parallel port. We installed
; this interrupt handler in _main. This routine simply wakes _main up.
; _main waits for an ACK after each byte of our string is pumped out the port.
; Exec puts our IS_DATA in a1. Our IS_DATA should be our wakeup mask. Now
; we only need to get the address of our main task which our StartUp.o
; startup code placed at the label _ThisTask.

   XDEF   CIAARoutine
CIAARoutine:
   move.l   a1,d0
   movea.l   _ThisTask,a1
   movea.l   _SysBase,a6
   jsr      _LVOSignal(a6)
   moveq      #0,d0
   rts

   XDEF   _CIAAResource,_MiscResource
_CIAAResource   dc.l   0
_MiscResource   dc.l   0

NT_INTERRUPT   equ   2

   XDEF   CIAAInterrupt,SigMask
   ;The interrupt structure for the "handshaking" line of the parallel port.
CIAAInterrupt:
   dc.l   0,0
   dc.b   NT_INTERRUPT,0
   dc.l   Name
SigMask:
   dc.l   0            ;we'll store our wakeup mask in the IS_DATA field
   dc.l   CIAARoutine   ;IS_CODE (routine executed when "handshake" occurs)

   XDEF   IntuitionName,CIAAName,MiscName
IntuitionName   dc.b   'intuition.library',0
CIAAName         dc.b   'ciaa.resource',0
MiscName         dc.b   'misc.resource',0

   XDEF   Name
Name   dc.b   'Parallel Test',0

   XDEF   Message
   ;This is the NULL-terminated string we'll send out of the parallel port.
Message   dc.b   'This is a test of the parallel port',0

   END
