10 REM > SerNet/s
   20 REM Add serial port network support to NFS
   30 REM v0.01 14-Mar-95 Initial bits
   40 REM v0.02 22-Mar-95 all done except Tx from tube and Broadcast Rx
   50 REM Broadcast immediate sends broken scout
   60 REM No check for idle before action
   70 :
   80 DIM mcode% &3000
   90 OSCLI"LOAD NFSROM "+STR$~mcode%
  100 rom2%=&9FB5:OSBYTE=&FFF4
  110 :
  120 EcoTx%=mcode%!&1631 AND &FFFF
  130 EcoInit%=mcode%!&1634 AND &FFFF
  140 EcoNMIRelease%=mcode%!&1637 AND &FFFF
  150 EcoNMIClaim%=mcode%!&163A AND &FFFF
  160 :
  170 TxStatus=&FC08:TxRDY=1:TxData=&FC09
  180 RxStatus=&FC08:RxRDY=2:RxData=&FC09
  190 :
  200 esc=&9B
  210 REM Workspace at &0D80-&0D91
  220 REM &0D80 dest stn
  230 REM &0D81 dest net
  240 REM &0D82 src stn
  250 REM &0D83 src net
  260 REM &0D84 ctrl
  270 REM &0D85 port
  280 REM &0D86-9 RemoteAddr, Transfer Address
  290 REM &0D8A-D             Transfer End Address
  300 REM &0D86-D Broadcast data
  310 REM &0D8E incoming scout index/data flag
  320 REM &0D8F esc/tube/ack-nack/netnum
  330 REM &0D90-1 Incoming receive block
  340 :
  350 DEFFNorg(A%):P%=A%:O%=P%-&8000+mcode%:=P*3+4
  360 FOR P=0 TO 1
  370   [OPT FNorg(&8009)
  380   EQUS "SER"         :\ Title="SER,NET"
  390   :
  400   OPT FNorg(&9630)   :\ Low level networking entry points
  410   JMP SerNetTx       :\ Transmit block
  420   JMP SerNetInit     :\ Initialise, put receive handlers in place
  430   JMP EcoNMIRelease% :\ I must release NMIs
  440   JMP EcoNMIClaim%   :\ I can take over NMIs
  450   :
  460   OPT FNorg(&9643)
  470   JMP SerNetIRQ      :\ IRQ occured
  480   :
  490   OPT FNorg(rom2%)
  500   \ Service routine
  510   \ ===============
  520   RTS
  530   :
  540   :
  550   \ Initialise network drivers
  560   \ ==========================
  570   .SerNetInit
  580   JSR EcoInit%             :\ Initialise Econet drivers
  590   LDA #7:LDX #7:JSR OSBYTE
  600   LDA #8:LDX #7:JSR OSBYTE :\ Set serial port speed to 9600
  610   LDA #0:STA &190+232      :\ Serial IRQs come to me
  620   RTS
  630   :
  640   :
  650   \ Transmit a block of data
  660   \ ========================
  670   .SerNetTx
  680   \ On entry,
  690   \ X is important
  700   \ A,Y  unimportant
  710   \ &A0/1=>workspace+&6F
  720   \ (&A0),0    = control byte
  730   \       1    = port
  740   \       2    = station
  750   \       3    = net
  760   \       4-7  =>buffer start
  770   \       8-11 =>buffer end
  780   \       12-15= Remote Address
  790   \
  800   \ On exit,
  810   \ (&A0),0   = result, read by OSBYTE &32
  820   \ A,Y       = unimportant
  830   \ X         = preserved
  840   \
  850   LDY #3:LDA (&A0),Y         :\ Get network number
  860   CMP #&FF:BEQ SerTxBroadcast
  870   AND #&F0:CMP #&70
  880   BEQ SerTx:JMP EcoTx%       :\ Not net &7x, pass to Econet driver
  890   :
  900   .SerTxBroadcast
  910   JSR EcoTx%                 :\ Send broadcast to Econet
  920   .SerTx
  930   :
  940   \ Transmit a block of data via serial port
  950   \ Sends <header> <wait-ack> <data> <wait-ack>
  960   \
  970   \ Should wait for 'Background reception not in progress'
  980   :
  990   TXA:PHA:LDA &A9:PHA:LDA &A8:PHA  :\ Save X and some workspace
 1000   LDX #0:JSR SerTxScoutStart       :\ Frame start
 1010   LDY #2:LDA (&A0),Y:JSR SerTxByte :\ dest stn
 1020   INY:LDA (&A0),Y:JSR SerTxByte    :\ dest net
 1030   LDA &D22:JSR SerTxByte           :\ src stn
 1040   LDA &D8F:AND #15
 1050   ORA #&70:JSR SerTxByte           :\ src net
 1060   LDY #0:LDA (&A0),Y:JSR SerTxByte :\ Ctrl
 1070   INY:LDA (&A0),Y:JSR SerTxByte    :\ Port
 1080   BNE SerTxNotImmediate:LDY #12
 1090   .SerTxRAddr
 1100   LDA (&A0),Y:JSR SerTxByte        :\ Send RemoteAddress
 1110   INY:CPY #16:BNE SerTxRAddr
 1120   .SerTxNotImmediate
 1130   LDY #2:LDA (&A0),Y:CMP #&FF:PHP  :\ Dest=???.255 ?
 1140   \\\
 1150   \\\ Should check for Tube address
 1160   \\\
 1170   INY:LDA (&A0),Y:STA &A8
 1180   INY:LDA (&A0),Y:STA &A9          :\ &A8/9=address
 1190   PLP:BNE SerTxNotBroadcast
 1200   .SerTxBroadcast
 1210   LDA (&A8),Y:JSR SerTxByte
 1220   INY:CPY #8:BNE SerTxBroadcast    :\ Send exactly 8 bytes
 1230   .SerTxNotBroadcast
 1240   PHP:JSR SerTxScoutEnd:PLP        :\ End of scout frame
 1250   BEQ SerTxDone                    :\ Broadcast has no ACK
 1260   JSR SerWaitAck:BNE SerTxDone     :\ Not ACK'd, don't send data
 1270   JSR SerTxDataStart               :\ Start of data frame
 1280   .SerTxDataLp
 1290   LDY #0:LDA (&A8),Y:JSR SerTxByte :\ Send a byte of data
 1300   INC &A8:BNE P%+4:INC &A9         :\ Increment address
 1310   LDY #8:LDA (&A0),Y               :\ Compare with end address
 1320   CMP &A8:BNE SerTxDataLp          :\ More to do
 1330   INY:LDA (&A0),Y
 1340   CMP &A9:BNE SerTxDataLp          :\ More to do
 1350   JSR SerTxDataEnd                 :\ End of data frame
 1360   JSR SerWaitAck                   :\ Get ack/nak
 1370   .SerTxDone
 1380   TXA:LDY #0:STA (&A0),Y           :\ Store result
 1390   PLA:STA &A8:PLA:STA &A9:PLA:TAX  :\ Restore workspace and X
 1400   RTS
 1410   :
 1420   \ Transmission subroutines
 1430   \ ------------------------
 1440   .SerTxScoutStart:LDA #&A0:BNE SerTxCommand
 1450   .SerTxScoutEnd  :LDA #&A2:BNE SerTxCommand
 1460   .SerTxDataStart :LDA #&A4:BNE SerTxCommand
 1470   .SerTxDataEnd   :LDA #&A6:BNE SerTxCommand
 1480   .SerTxScoutACK  :LDA #&A8:BNE SerTxCommand
 1490   .SerTxScoutNAK  :LDA #&AA:BNE SerTxCommand
 1500   .SerTxDataACK   :LDA #&AC:BNE SerTxCommand
 1510   .SerTxDataNAK   :LDA #&AE:BNE SerTxCommand
 1520   :
 1530   .SerTxCommand
 1540   PHA:LDA #esc:JSR SerTxRaw        :\ Send esc to prefix command
 1550   PLA:BNE SerTxRaw                 :\ Send command byte
 1560   :
 1570   .SerTxByte
 1580   CMP #esc:BNE SerTxRaw            :\ esc has to be esc'ed
 1590   JSR SerTxRaw
 1600   .SerTxRaw
 1610   PHA
 1620   .SerTxByteWait
 1630   LDA TxStatus:AND #TxRDY          :\ Wait until Tx empty
 1640   BEQ SerTxByteWait
 1650   PLA:STA TxData                   :\ Send byte
 1660   RTS
 1670   :
 1680   .SerWaitAck
 1690   LDA &D8F:AND #&30:BEQ SerWaitAck :\ Wait for ACK/NAK response
 1700   AND #&20:BEQ P%+4:LDA #&42       :\ Convert to NetStatus value
 1710   TAX:LDA &8DF:AND #&CF:STA &8DF
 1720   TXA:RTS                          :\ EQ=ACK, NE=NAK
 1730   :
 1740   :
 1750   :
 1760   :
 1770   \ IRQ occured
 1780   \ ===========
 1790   \ &A8/&A9 stacked, usable as workspace
 1800   \ &A8=Y=Service Y parameter
 1810   \ &A9=service number, returned A value
 1820   \ X=?&BB
 1830   \ On exit, ?&A9 passed to A
 1840   \          Y returned to caller
 1850   \          X ignored, restored from &F4
 1860   \
 1870   \ &D80-&D8B - Incoming scout
 1880   \   &D80 dest stn
 1890   \   &D81 dest net
 1900   \   &D82 src stn
 1910   \   &D83 src net
 1920   \   &D84 ctrl
 1930   \   &D85 port
 1940   \   &D86-9 RemoteAddress, TransferAddress
 1950   \   &D8A-D                TransferAddressEnd
 1960   \   &D86-D Broadcast data
 1970   \ &D8E b7=0  - index into incoming scout
 1980   \      b7=1  - waiting for incoming data
 1990   \ &D8F b7    - waiting for escaped byte
 2000   \      b6    - transfering to Tube
 2010   \      b5-b4 - received ACK/NAK flag
 2020   \      b3-b0 - my network number
 2030   \ &D90-&D91 - Incoming receive block address
 2040   \
 2050   .SerNetIRQ
 2060   LDA RxStatus:BMI SerNetIRQYes
 2070   LDA #5:RTS                 :\ Not from serial, exit
 2080   .SerNetIRQYes
 2090   TYA:PHA:LDA &A9:PHA        :\ Save Y and ?&A9
 2100   LDA RxData                 :\ Get byte from serial port
 2110   BIT &D8F:BMI SerRxEsc2nd   :\ Waiting for escaped byte
 2120   CMP #esc:BEQ SerRxEsc1st   :\ Start of escape sequence
 2130   LDY &D8E:BMI SerRxData     :\ Waiting for data frame
 2140   CPY #12:BCS SerRxExit      :\ Scout overrun, ignore extra bytes
 2150   STA &D80,Y:INY:STY &D8E    :\ Store byte of scout
 2160   BNE SerRxExit              :\ Exit
 2170   :
 2180   .SerRxEsc1st
 2190   LDA #&80:ORA &D8F:STA &D8F :\ Set b7, waiting for escaped byte
 2200   BNE SerRxExit
 2210   :
 2220   .SerRxEsc2nd
 2230   CMP #esc:BNE SerRxEscCmd   :\ esc,<>esc -> escaped command
 2240   \ esc,esc -> esc
 2250   :
 2260   .SerRxData
 2270   BIT &D8F:BVC SerRxIO       :\ b6=0, read to I/O memory
 2280   STA &FEE5:BVS SerRxUpdate  :\ Read to Tube memory
 2290   .SerRxIO
 2300   TAY
 2310   LDA &D86:STA &A8
 2320   LDA &D87:STA &A9
 2330   TYA:LDY #0:STA (&A8),Y     :\ Store received byte
 2340   .SerRxUpdate
 2350   INC &D86:BNE SerRxUpdEnd   :\ Update transfer address
 2360   INC &D87:BNE SerRxUpdEnd
 2370   INC &D88:BNE SerRxUpdEnd
 2380   INC &D89
 2390   .SerRxUpdEnd
 2400   \ Should check for data overrun
 2410   .SerRxExit
 2420   PLA:STA &A9:PLA:TAY        :\ Restore workspace and Y
 2430   LDA #0:RTS
 2440   :
 2450   \ Escaped command
 2460   \ ---------------
 2470   \ &esc,&Ax - %10100xxx - incoming
 2480   \ &esc,&Ax - %10101xxx - replies
 2490   \
 2500   \            %1010000x - ScoutStart
 2510   \            %1010001x - ScoutEnd
 2520   \            %1010010x - DataStart
 2530   \            %1010011x - DataEnd
 2540   \
 2550   \            %1010100x - ScoutACK
 2560   \            %1010101x - ScoutNAK
 2570   \            %1010110x - DataACK
 2580   \            %1010111x - DataNAK
 2590   \
 2600   .SerRxEscCmd
 2610   TAY:AND #&F0
 2620   CMP #&A0:BNE SerRxExit     :\ Not %1010xxxx, ignore
 2630   TYA:AND #7:LSR A
 2640   BNE SerRxScoutEnd
 2650   :
 2660   \ ScoutStart
 2670   STA &D8E:BEQ SerRxExit     :\ Set count index to zero
 2680   :
 2690   .SerRxScoutEnd
 2700   CMP #2:BCS SerRxDataStart
 2710   LDA &D85:BEQ SerRxImmediate:\ Port=0, immediate command
 2720   \
 2730   \ Look through open receive block
 2740   \ See if anything's listening for this incoming scout
 2750   \ This is where gateway action could be performed
 2760   \ If deststn<>mystn OR destnet<>0, pass out onto Econet
 2770   \ Would need complemntary EcoRx->SerTx code
 2780   \
 2790   JSR FindReceiveBlock       :\ Returns X=ScoutACK/ScoutNAK
 2800   JSR SerTxCommand           :\ Send ACK/NAK
 2810   JMP SerRxExit
 2820   :
 2830   .SerRxDataStart
 2840   BNE SerRxDataEnd
 2850   LDA #&80:STA &D8E          :\ Waiting for data
 2860   BNE SerRxExit
 2870   :
 2880   .SerRxDataEnd
 2890   CMP #4:BCS SerRxACK        :\ ACK/NAK received
 2900   JSR SerTxDataACK           :\ Send ACK
 2910   LDA &D90:STA &A8           :\ Get Rx block address
 2920   LDA &D91:STA &A9
 2930   LDY #11
 2940   .SerRxEndAddr
 2950   LDA &D7E,Y:STA (&A8),Y     :\ Update end address with final data address
 2960   DEY:CPY #8:BNE SerRxEndAddr
 2970   LDY #3:LDA &D83:STA (&A8),Y:\ Actual Net received from
 2980   DEY:LDA &D82:STA (&A8),Y   :\ Actual Stn received from
 2990   DEY:LDA &D85:STA (&A8),Y   :\ Actual port received
 3000   DEY:LDA &D84:STA (&A8),Y   :\ Received control byte, closes receive block
 3010   LDA #0:STA &D8E            :\ Reset back to waiting for scout
 3020   BIT &D8F:BVC SerRxExit     :\ I/O transfer
 3030   LDA #&C0+&3E:JSR &406      :\ Release Tube
 3040   STA &D8F:AND #15:STA &D8F  :\ Clear Tube flag
 3050   JMP SerRxExit
 3060   :
 3070   .SerRxACK
 3080   TYA:AND #3:ORA #1          :\ A=01 11 01 11 for ACK/NAK/ACK/NAK
 3090   ASL A:ASL A:ASL A:ASL A    :\ A=&1x=ACK, &3x=NAK
 3100   ORA &D8F:STA &D8F          :\ Put ACK/NAK into flag byte
 3110   JMP SerRxExit
 3120   :
 3130   \ Immediate Operations
 3140   \ --------------------
 3150   \ If &D80<>mystat, not for me
 3160   \ If &D81<>mynet, not for me
 3170   .SerRxImmediate
 3180   LDA &D84:CMP #&88          :\ Only MachType supported
 3190   BEQ P%+5:JMP SerRxExit     :\ Ignore all others
 3200   :
 3210   \\\ Need to send a long scout in reply
 3220   \\\ dstn,dnet,sstn,snet,b1,b2,b3,b4
 3230   \\\
 3240   \\\ Need to wait until foreground not transmitting
 3250   \\\
 3260   JMP SerRxExit
 3270   :
 3280   :
 3290   \ Look for a matching open receive block
 3300   \ ======================================
 3310   \ Receive blocks are in private Econet workspace at (&9E),Y
 3320   \ If &D64.b7=1, listening receive block at &00C0 for FSOp
 3330   \ 0    - 0=end blocks, &7F=open receive block, &80+x=receive control byte
 3340   \ 1    - port
 3350   \ 2    - stn
 3360   \ 3    - net
 3370   \ 4-7  - buffer start
 3380   \ 8-11 - buffer end
 3390   \
 3400   .FindReceiveBlock
 3410   LDA &9F:STA &A9:LDA #0:STA &A8 :\ &A8/9=>Econet workspace
 3420   BIT &D64:BPL FindRxLp
 3430   STA &A9:LDA #&C0:STA &A8       :\ &A8/9=>&00C0 FSOp receive block
 3440   .FindRxLp
 3450   LDY #0
 3460   LDA (&A8),Y:BEQ FindRxNone     :\ End of blocks, no match
 3470   CMP #&7F:BNE FindRxNext        :\ Not an open block, try next
 3480   INY:LDA (&A8),Y:BEQ FindRxPort :\ Listen on any port
 3490   CMP &D85:BNE FindRxNext        :\ Port doesn't match, try next
 3500   .FindRxPort
 3510   INY:LDA (&A8),Y:BEQ FindRxFound:\ Listen to any station
 3520   CMP &0D80:BNE FindRxNext       :\ Station doesn't match, try next
 3530   INY:LDA (&A8),Y:BEQ FindRxFound:\ Listen to any network
 3540   CMP &0D81:BEQ FindRxFound      :\ Nets match, jump to process
 3550   :
 3560   .FindRxNext                    :\ This block doesn't match, step to next
 3570   LDA &A9:BEQ FindRxNone         :\ FSOp receive block
 3580   LDA &A8:CLC:ADC #12            :\ Step to next receive block
 3590   STA &A8:BCC FindRxLp           :\ Loop back to check this one
 3600   .FindRxNone
 3610   SEC:LDX #&AA:RTS               :\ X=ScoutEndNAK - no receive block found
 3620   :
 3630   \ Receive block matches
 3640   \ ---------------------
 3650   .FindRxFound
 3660   LDA &A8:STA &D90
 3670   LDA &A9:STA &D91               :\ Point to this receive block
 3680   \\\
 3690   \\\ If broadcast, eat the data now
 3700   \\\
 3710   LDY #4
 3720   .FindRxAddrLp
 3730   LDA (&A8),Y:STA &D82,Y         :\ Copy AddrStart and AddrEnd to workspace
 3740   INY:CPY #12:BNE FindRxAddrLp
 3750   LDA &D89:CMP #&FF:BNE FindRxIO :\ I/O memory, return
 3760   .FindRxTube
 3770   LDA #&80+&3E:JSR &406          :\ Claim Tube
 3780   BCC FindRxTube
 3790   LDA #1:JSR &406                :\ Transfer bytes to CoPro
 3800   LDA &D8F:ORA #&40:STA &D8F     :\ Flag 'Tube transfer'
 3810   .FindRxIO
 3820   CLC:LDX #&A8:RTS               :\ X=ScoutEndACK
 3830   :
 3840   ]
 3850 NEXT
 3860 PRINT"*SAVE SerNet/rom ";~mcode%;" ";~O%;" 0 FFFBBC00"