home *** CD-ROM | disk | FTP | other *** search
-
- ; PARALLEL PORT NETWORK LOW LEVEL ROUTINES
- ;
- ; CABLE: Connect D7-D0,SEL,POUT, and BUSY across,
- ; Connect ACK to SEL locally:
- ; Connect GND lines indicated
- ;
- ; (2-9) D7-D0 ------------ D7-D0
- ; (12) POUT ------------ POUT
- ; (11) BUSY ------------ BUSY PARALLEL PORT
- ; (13) SEL --+------+-- SEL
- ; (10) ACK -/ \- ACK
- ; (18-22) GND ------------ GND
- ;
- ; WARNING: you cannot connect RI on the serial port to your
- ; modem because it interferes with the parallel
- ; port's SEL line in this configuration.
- ;
- ; * 28K/sec bandwidth
- ; * # machines depends on extra hardware for buffering,
- ; but 3 ought to work fine without any extra hardware.
- ;
- ; (network protocol can now handle 254)
- ;
- ; Data Line Definitions:
- ; CIAA PORTB : D7-D0 used for byte data transfer
- ; CIAB PORTA : D2-D0 used for line aquisition and
- ; handshaking (SEL,POUT,BUSY)
- ;
- ; All lines pulled up. Thus, asserted state is a 0. Idle
- ; state is an undriven (1). Protocol transfers a byte at
- ; a time. Protocol is ethernet style with a small window
- ; of error in the line aquisition routine.
- ;
- ; Note: Timeouts should be set around a second. Ideally
- ; defaults should be fixed on faster machines.
- ;
- ; CIAB PORTA D0 ~ACK hand shake
- ; D1 ~REQ hand shake
- ; D2 CTL 1 = special byte, 0 = data byte.
- ; (for address mark, valid when ~REQ
- ; goes low. For EOP mark, sample
- ; when ~REQ goes high)
- ;
- ; PROTOCOL
- ;
- ; HandShake (Reader <- Writer transfer). A Handshake
- ; sequence transfers TWO bytes of information.
- ;
- ; WRITER READER
- ; |-> place data, ~REQ->0
- ; | wait for ~REQ->0
- ; | read data & store
- ; | set ~ACK->0
- ; | wait ~ACK->0
- ; | place data, ~REQ->1
- ; | wait for ~REQ->1
- ; | read data & store
- ; | set ~ACK->1
- ; | wait ~ACK->1
- ; |<- LOOP (2 bytes written) LOOP (2 bytes read)
- ;
- ;
- ; Read: (1) Determine if your machine is being addressed
- ; (~REQ=0, data=myaddress, CTL=1)
- ; (1) set DDR for ~ACK to output and
- ; (2) Handshake sequence for the address mark, only
- ; first byte valid.
- ; (3) Handshake sequence for data util rcv byte
- ; with CTL = 1 (EOP), byte must == 0.
- ; (4) Set ~ACK (bit 0) to input
- ;
- ; Write: (1) AQUIRE THE NETWORK (see below)
- ; involves gaining control and then setting the
- ; DDR for CTL and ~REQ to outputs.
- ;
- ; Also checks if somebody is writing to us,
- ; in which case -2 is returned instantaniously
- ; indicating we should do a ParRead().
- ;
- ; (2) Handshake sequence for address mark, send dest
- ; address (second byte garbage). Note that CTL->1
- ; *BEFORE* we set ~REQ->0
- ; (3) Handshake sequence for data bytes
- ; (4) Handshake sequence for EOP mark (Note that CTL->1
- ; *AFTER* we get the ~ACK->1 and before release
- ; ~REQ (->1). Only firstbyte valid and set to 0.
- ;
- ; (5) Set ~ACK and ~CTL to inputs
- ;
- ; AQUIRE: Line aquisition prevents two people from writing to
- ; the net at the same time.
- ;
- ; * A line is considered aquired if ANY of the 3
- ; control lines is 0.
- ; * If network is not aquired:
- ; - set ~ACK to output and 1
- ; - bclr ~ACK to 0 and bne success
- ; (else set ~ACK to input and try again)
- ; - set data lines to output and place my address
- ; (Must ]be done before CTL is glitched)
- ; - set ~CTL to output and 1
- ; - set CTL to 0 and then 1 (glitch it) to cause
- ; FLAG interrupt on all other machines
- ; - set ~REQ to output and 0 (beginning of handshake
- ; sequence)
- ; - lastly, release ~ACK by setting it to an input
- ;
- ; Note that at all times at least one line is a 0
- ; so no other machine will attempt to aquire the net.
- ;
- ; Note that the destination address is placed on the data
- ; lines after we have aquired the line but before we glitch
- ; the CTL line to cause an interrupt. This allows the
- ; other machines to instantaniously determine who is being
- ; addressed.
- ;
- ; Note that the CTL line gets glitched at the end of a packet
- ; too for the EOP mark. In this case there is a 0 on the
- ; data lines so while an interrupt is generated, nobody
- ; thinks they are being addressed.
-
- INCLUDE "exec/types.i"
- INCLUDE "exec/execbase.i"
-
- XREF _intena
-
- DISABLE MACRO
- MOVE.W #$04000,_intena *(NOT IF_SETCLR)+IF_INTEN
- ADDQ.B #1,IDNestCnt(A6)
- ENDM
-
- ENABLE MACRO
- SUBQ.B #1,IDNestCnt(A6)
- BGE.S ENABLE\@
- MOVE.W #$0C000,_intena *IF_SETCLR+IF_INTEN
- ENABLE\@
- ENDM
-
-
- section __MERGED,DATA
-
- xdef _ParLLTimeout
- xdef _ParDAT
- xdef _ParDDR
- xdef _ParCollision1
- xdef _ParCollision2
- xdef _ParDebug
-
- ; Note, the timeout is set manually on init by using the
- ; timer.device to time one second.
-
- _ParLLTimeout dc.l 1000000 ; default timeout value (count). 1 second
- _ParDAT dc.l $BFE101 ; data port
- _ParDDR dc.l $BFE301 ; data ddr
- _ParCAT dc.l $BFD000 ; ctl port (D0-D2)
- _ParCDR dc.l $BFD200 ; ctl ddr (D0-D2)
- _ParNetAddr dc.b 0 ; default network address (msb 4 bits)
- _DummyBuf dc.b 0 ; dummy buffer
- dc.b 0 ; dummy buffer
- dc.b 0 ; pad
- _ParCollision1 dc.l 0 ; statistics
- _ParCollision2 dc.l 0 ;
- Null dc.l 0 ; always 0
- dc.l 0 ; always 0
-
- _ParDebug ds.l 16 ; 16 longword debug entries
-
- section text,code
-
- ; (void) ParAddress(myaddr)
- ; Set my address to (1-254)
- ;
- ; 0 and 255 are reserved!
- ;
- ; int = ParDataReady()
- ; returns 1 if packet pending
- ; returns 0 if line is currently idle
- ; returns -1 if packet isn't for you
- ;
- ; if line has been aquired but no control address has been
- ; put on it yet, ParDataReady() will wait for a control
- ; address. Thus, after a signal, a single call to
- ; ParDataReady() should suffice.
- ;
- ; n = ParReadV (buf1, bytes1, buf2, bytes2, ..., NULL, NULL);
- ; (buffer sizes must be even)
- ; n = ParRead (buf, bytes)
- ; read a pending packet. Returns n = -1 (1 second timeout
- ; no packet pending), n = 0 to bytes -1 (1 second timeout
- ; after transmission interrupted), n = bytes (success),
- ; or n > bytes (transmitting machine's packet was larger
- ; than we can handle, extra bytes thrown out)
- ;
- ; NOTE: requesting an odd number of bytes is O.K. but
- ; if you request N where N is odd and the writer
- ; sends N + 1 you will never know (N will be
- ; returned). See ParWrite() below
- ;
- ; n = ParWriteV (destadr, buf1, bytes1, buf2, bytes2, ..., NULL, NULL);
- ; (buffer sizes must be even)
- ; n = ParWrite(destadr, buf, bytes)
- ; write a packet. Returns:
- ; n = -2 Cannot write anything, a packet is pending
- ; (instantanious)
- ; n = -1 Destination machine does not respond (1 sec to)
- ; n = N N bytes written ok (success if n == bytes)
- ;
- ; NOTE: sending an odd number of bytes is O.K. but if
- ; you write N where N is odd and the reader requests
- ; N + 1 he will get N + 1 the last byte being garbage.
- ;
- ;
- ; CIAA PORTB : DATA ONLY
- ; CIAB PORTA : D0 ~DAck
- ; D1 ~DRdy
- ; D2 Ctl
-
- xdef _ParRead
- xdef _ParReadV
- xdef _ParWrite
- xdef _ParWriteV
- xdef _ParAddress
- xdef _ParDataReady
- xdef _LongCheckSum
- xdef _Time10000
-
- _ParAddress: move.l 4(sp),D0 ; address 1-254
- move.b D0,_ParNetAddr ; store
- rts
-
- ; ParDataReady()
- ;
- ; -1 packet isn't for you
- ; 0 line is idle
- ; 1 packet probably pending for you
-
- _ParDataReady: move.l _ParDAT,A0 ; data register
- move.l _ParCAT,A1 ; data control register
-
- .pdstable move.b (A1),D0
- move.b (A0),D1
- cmp.b (A1),D0 ; control lines stable?
- bne .pdstable
-
- ; Now, ParDataReady might be called after the sending machine
- ; has aquired but before it can assert REQ. However, the
- ; sending machine has already (guarenteed) placed its address
- ; on the data port. So while the address matches, loop while
- ; REQ not asserted.
-
- btst.l #1,D0 ; ~Req asserted?
- beq .pd10 ; beq yes
- cmp.b _ParNetAddr,D1 ; no, does data match anyway?
- beq .pdstable ; YES, loop until get ~REQ or
- bra .pdfail ; data bad.
-
- .pd10 btst.l #2,D0 ; yes, Ctl ?
- beq .pdrn ; no, middle of some packet
- cmp.b _ParNetAddr,D1 ; yes, my address?
- bne .pdrn
- moveq.l #1,D0 ; yes, packet (probably) for us
- rts
-
- .pdfail btst.l #2,D0 ; fail due to ~Req not asserted
- beq .pdrn ; Ctl = 0, line busy
- .pdr0 moveq.l #0,D0 ; line idle
- rts
- .pdrn moveq.l #-1,D0 ; line busy, packet not for me
- rts
-
- _ParReadV: ; Read Into Vector
- movem.l D2-D7/A2-A5,-(sp)
- lea 12+40(sp),A3
- bra .rm000
-
- _ParRead:
- movem.l D2-D7/A2-A5,-(sp)
- lea Null,A3 ; Pointer to next vector
- .rm000 move.l 4+40(sp),A0 ; A0 = buffer to read into
- move.l 8+40(sp),D7 ; D7 = # bytes to read (maximum)
- move.l _ParDAT,A1 ; A1 = data reg
- move.l _ParDDR,A2 ; A2 = ddr
- move.l _ParCAT,A4 ; A4 = data reg
- move.l _ParCDR,A5 ; A5 = ddr
-
- move.b #0,(A2) ; ensure all are inputs
- bclr.b #0,(A5)
- bclr.b #1,(A5)
- bclr.b #2,(A5)
-
- move.l _ParLLTimeout,D5 ; D5 = timeout load
- moveq.l #-1,D6 ; D6 = # bytes read so far
-
- ; WAIT LOOK FOR ADDRESS MARK
- ;
- ; Ctl = 1, ~DReq = 0
-
- move.l D5,D4 ; D4 = timeout countdown
- .rmstab move.b (A4),D0 ; control data
- move.b (A1),D1 ; data data (network addr)
- cmp.b (A4),D0
- bne .rmstab
- btst.l #2,D0 ; expect CTL = 1
- beq .rms1 ; nope
- btst.l #1,D0 ; expect ~REQ = 0
- beq .rms2 ; yes
- .rms1
- add.l #1,_ParDebug+0
- subq.l #1,D4 ; timeout
- bne .rmstab
- bra .rmend ; no address mark!
-
- .rms2 cmp.b _ParNetAddr,D1 ; my address?
- bne .rms1 ; no, timeout loop
-
- ; My address, ~Ack byte.
-
- bclr.b #0,(A4) ; set ~ACK to 0
- bset.b #0,(A5) ; set to output
-
- move.l D5,D4 ; reset timeout
- .rms4 btst.b #1,(A4) ; wait for ~REQ to go away
- bne .rms5
- add.l #1,_ParDebug+4
- subq.l #1,D4
- bne .rms4
- moveq.l #-2,D6 ; ~REQ not released ?????
- bra .rmend
-
- .rms5 bset.b #0,(A4) ; release ~ACK
- moveq.l #0,D6 ; set # bytes read to 0
- bra .rms10 ; skip past move
-
- ; MAIN READ LOOP
- ;
- ; D6 holds cnt, A0 buffer ptr, D0-D4 free to allocate
-
- .rms10loop
- move.b D0,(A0) ; store data
- addq.l #1,A0 ; next addr.
-
- .rms10 btst.b #1,(A4) ; wait for ~REQ asserted
- beq .rms20
- btst.b #1,(A4)
- beq .rms20
- move.l D5,D4 ; load timeout
- .rms11 btst.b #1,(A4) ; wait for ~REQ asserted w/to
- beq .rms20
- add.l #1,_ParDebug+8
- subq.l #1,D4
- bne .rms11
- bra .rmend
-
- .rms20 move.b (A1),D0 ; get data and
- bclr.b #0,(A4) ; assert ~ACK
-
- ; note, on CTL = 1 end sequence this data item is a dummy
-
- move.b D0,(A0) ; store data
- addq.l #1,A0 ; next addr.
- addq.l #2,D6 ; optimized but not quite
- ; true, we've only written 1 sf.
- btst.b #1,(A4) ; wait for ~REQ released
- bne .rms30
- btst.b #1,(A4)
- bne .rms30
- move.l D5,D4
- .rms21 btst.b #1,(A4) ; wait for ~REQ rel w/ to
- bne .rms30
- add.l #1,_ParDebug+12
- subq.l #1,D4
- bne .rms21
- bra .rmendsub ; sub because D6 is 2 ahead
-
- .rms30 move.b (A1),D0 ; get data
- move.b (A4),D1 ; get CTL status
- bset.b #0,(A4) ; release ~ACK
- btst.l #2,D1 ; EOP if CTL = 1
- bne .rmeop
-
- ; CANNOT STORE DATA HERE! In case odd # bytes requested,
- ; second byte would overflow buffer
-
- subq.l #2,D7 ; # bytes remaining
- bgt .rms10loop
- bne .rmnlb
- move.b D0,(A0) ; if D7 = 0 its even and we
- ; should store the last byte
- .rmnlb cmp.l #-1,D7 ; -1 = was odd #
- bne .rmeven ; fixup count
- subq.l #1,D6
-
- .rmeven
- .rmsev0 tst.l (A3) ; if next buffer NULL
- beq .rmovflow
- move.l (A3)+,A0 ; next buffer
- move.l (A3)+,D7
- beq .rmsev0 ; 0 bytes, goto next buffer
- bra .rms10 ; loop, continue reading
-
- .rmovflow
- lea _DummyBuf,A0 ; overflow, dummy buffer
- bra .rms10
-
- .rmeop tst.b D0 ; EOP data better be 0!
- beq .rmendsub
- moveq.l #-3,D6
- bra .rmend
-
- .rmendsub subq.l #2,D6 ; because we were two ahead
-
- .rmend bset.b #0,(A4) ; active pull up before (?)
- bclr.b #0,(A5) ; setting ~ACK to input
- move.l D6,D0 ; return value
- movem.l (sp)+,D2-D7/A2-A5 ; restore registers
- rts
-
- _ParWriteV: movem.l D2-D7/A2-A6,-(sp) ; write vector
- lea 16+44(sp),A3
- bra .wm000
-
- _ParWrite:
- movem.l D2-D7/A2-A6,-(sp)
- lea Null,A3
- .wm000 move.l 4+44(sp),D3 ; D3 = destination address
- move.l 8+44(sp),A0 ; A0 = buffer to write
- move.l 12+44(sp),D7 ; D7 = # bytes to write
- move.l _ParDAT,A1 ; A1 = data reg
- move.l _ParDDR,A2 ; A2 = ddr
- move.l _ParCAT,A4 ; A4 = data reg
- move.l _ParCDR,A5 ; A5 = ddr
- move.l 4,A6 ; SYSBase
-
- move.b #0,(A2)
- and.b #%11111000,(A5)
- move.l _ParLLTimeout,D5 ; D5 = timeout load
-
- move.l D5,D4 ; D4 = timeout countdown
- moveq.l #-2,D6 ; D6 = # bytes written
-
- ; AQUIRE THE LINE USING ~ACK
-
- .wmstab
- bset.b #0,(A4) ; so is a 1 when we set it to w
-
- DISABLE
-
- move.b (A4),D0 ; get stable data
- move.b (A1),D1
- cmp.b (A4),D0
- beq .wmstab1
-
- ENABLE
-
- bra .wmstab
-
- ; Ints still disabled
- ; D0 holds ~ACK ~REQ CTL status
-
- .wmstab1 and.b #%111,D0 ; ~ACK=1, ~REQ=1, CTL=1
- cmp.b #%111,D0
- beq .wm02
-
- ; no, if CTL = 1, ~REQ = 0, and D1 = my address then
- ; return w/ -2
-
- btst.l #1,D0
- bne .wm01
- btst.l #2,D0
- beq .wm01
- cmp.b _ParNetAddr,D1 ; somebody calling me?
- bne .wm01
-
- ENABLE
- bra .wmend
-
- .wm01
- ENABLE
-
- add.l #1,_ParDebug+16
- subq.l #1,D4
- bne .wmstab
- bra .wmend
-
- ; interrupts still disabled
- ; we almost own the line
-
- .wm02 bset.b #0,(A5) ; set ~ACK to an output
- nop
- bclr.b #0,(A4) ; assert ~ACK
- bne .wm05 ; was released before, have line!
-
- ; don't have line,
- bclr.b #0,(A5) ; set back to input
- bra .wm01
-
- ; Line now aquired.
-
- .wm05
- ENABLE
-
- move.b #$FF,(A2) ; set data ddr to outputs
- move.b D3,(A1) ; set data lines to our addr
-
- ; Before asserting ~REQ pulse CTL to cause interrupt on remote
- ; machines. Note that our address is already on the data
- ; lines.
-
- bset.b #2,(A5) ; set CTL to output
- bclr.b #2,(A4) ; pulse CTL to cause FLAG int
- or.b #%00000111,(A4) ; set CTL = 1 and make sure
- ; REQ will be one when we
- bset.b #1,(A5) ; set ~REQ to output
-
- bclr.b #1,(A4) ; assert ~REQ
-
- bclr.b #0,(A5) ; make ~ACK an input
- ; (note that REQ->0 before ACK->release)
-
- moveq.l #-1,D6 ; D6 = # bytes written
-
- ; INTERRUPTS ENABLED FOR TXFER (fully handshaked)
- ;
- ; Address mark ~ACK, wait for ~ACK asserted
-
- .wm10 btst.b #0,(A4)
- beq .wm15
- move.l D5,D4 ; D4 = timeout countdown
- .wm11 btst.b #0,(A4)
- beq .wm15
- add.l #1,_ParDebug+20
- subq.l #1,D4
- bne .wm11
- bra .wmend
-
- ; got ack, now set CTL = 0 (leaves at least one line 0 so
- ; nobody else thinks the bus is idle!)
- ;
- ; note: Since this is the address mark, and is sampled by
- ; the reader before it asserts ~ACK, I can set CTL
- ; = 0 now instead of waiting till after ~ACK is
- ; released.
-
- .wm15 bclr.b #2,(A4) ; set CTL = 0 for duration of pkt
- nop ; ???
- bset.b #1,(A4) ; release ~REQ
-
-
- moveq.l #0,D6 ; # bytes written
-
- ; DATA XFER LOOP
- ;
- ; wait for ~ACK to be released (->1). If no more bytes
- ; then skip to .wm50
-
- .wm20
- tst.l D7 ; more data in this buffer?
- ble .wm50 ; nope.
-
- btst.b #0,(A4) ; wait ~ACK released
- bne .wm30
- move.l D5,D4 ; D4 = timeout countdown
- .wm21 btst.b #0,(A4)
- bne .wm30 ; need the timeout here?
- btst.b #0,(A4)
- bne .wm30
- add.l #1,_ParDebug+24
- subq.l #1,D4
- bne .wm21
- bra .wmend
-
- ; Assert ~REQ for this data byte and wait for ~ACK
-
- .wm30
- move.b (A0)+,D0 ; get next data byte
- move.b D0,(A1) ; store data and
- bclr.b #1,(A4) ; assert ~REQ
-
- move.b (A0)+,D0 ; get next data byte
- subq.l #2,D7 ; one less byte (this and next)
- addq.l #1,D6 ; # bytes written (this only)
- ; (not valid until we get ACK
- ; which is why the wmendsub
-
- btst.b #0,(A4) ; wait for ACK
- beq .wm40
- btst.b #0,(A4)
- beq .wm40
- move.l D5,D4 ; D4 = timeout countdown
- .wm31 btst.b #0,(A4)
- beq .wm40
- add.l #1,_ParDebug+28
- subq.l #1,D4
- bne .wm31
- bra .wmendsub
-
- ; Have ~ACK, byte transmitted. ++bytes written, --bytes left
- ; and loop
-
- .wm40 move.b D0,(A1) ; store second byte
- bset.b #1,(A4) ; release ~REQ
-
- addq.l #1,D6 ; # bytes written
-
- bra .wm20
-
- ; Last byte in buffer has been transmitted.
- ;
- ; Get next buffer in vector
-
- .wm50 tst.l (A3)
- beq .wm50a
- move.l (A3)+,A0 ; buffer ptr
- move.l (A3)+,D7 ; # bytes
- bra .wm20 ; loop to top
-
- .wm50a
- ; Last byte has been transmitted,
- ;
- ; Wait for ~ACK to be released and then assert ~REQ with
- ; EOP & CTL = 1
- ;
- ; (timing on read is that CTL is sampled when ~REQ is
- ; RELEASED so no timing window here)
-
- btst.b #0,(A4) ; Wait ~ACK released
- beq .wm50
-
- move.b #0,(A1) ; EOP mark (0)
- bclr.b #1,(A4) ; assert ~REQ
-
- ; Wait for ~ACK asserted
-
- btst.b #0,(A4)
- beq .wm60
- move.l D5,D4
- .wm51 btst.b #0,(A4)
- beq .wm60
- add.l #1,_ParDebug+32
- subq.l #1,D4
- bne .wm51
- moveq.l #-3,D6 ; EOP failed
- bra .wmend
-
- ; Set CTL = 1 then release ~REQ, then wait for ~ACK released
-
- .wm60 or.b #%00000100,(A4) ; set CTL = 1
- or.b #%00000110,(A4) ; release ~REQ
-
- ; Wait ~ACK released ?
-
- .wm61 btst.b #0,(A4)
- beq .wm61
-
- ; Add D7 to D6. This handles fixup if an odd number of bytes
- ; were requested written, D7 will be -1 (odd) or 0 (even) and
- ; D6 will be one too large (odd) or perfect (even)
-
- add.l D7,D6
-
- bra .wmend
-
- .wmendsub subq.l #1,D6 ; was ahead in count
-
- .wmend move.b #0,(A2) ; set data port to input
- and.b #%11111000,(A5) ; set data port for ctl lines to input
-
- move.l D6,D0 ; return value
- movem.l (sp)+,D2-D7/A2-A6 ; restore registers
- rts
-
- ; sum = LongCheckSum(buf, bytes)
- ; (buffer must be lw aligned and bytes must be multiples of 4)
-
- _LongCheckSum:
- moveq.l #0,D0 ; D0 = accumulated checksum
- move.l 4(sp),A0 ; A0 = ptr
- move.l 8(sp),D1 ; D1 = bytes
- beq .pcrts
- .pc10 add.l (A0)+,D0
- subq.l #4,D1
- bgt .pc10
- tst.l D1
- bne .pc20 ; not multiple of 4 bytes!
- .pcrts rts ; return checksum
- .pc20 illegal ; cause task-held msg
- rts
-
- ; Delays 10000 rough timeout loops, used to determine
- ; timeout on init
-
- _Time10000:
- move.l #10000,D4
- t10 move.l D4,D4
- move.l D4,D4
- move.l D4,D4
- move.l D4,D4
- subq.l #1,D4
- bne t10
- rts
-
- END
-
-