[comp.sys.cbm] 6510 assembly punter source anyone?

profesor@wpi.WPI.EDU (Matthew E Cross) (11/12/90)

At one time, I had in my hands the 6510 assembly source code for the
punter protocol.  Does anyone know where I can obtain a copy of this?
I believe it was on the mail archive server, something like
'acliu@skat.usc.edu' (is that still up?).  If you have a copy of this,
could you mail it to me?  I would really appreciate it!

I do have description of the protocol.  Now that I know a decent
amount of C/Unix programming, I'm trying to implement the protocol on
UNIX.  BUT, the protocol differs subtlely from the description I have.
(or so it seems.)  If I could find that source, I could see what the
64 end is doing, and maybe get the UNIX end to work...
-- 
+-------------------------------------+--------------+------------------------+
| "The letter U has a lot of uses ... | Looking for  |  profesor@wpi.wpi.edu  |
|  I like to play it like a guitar!"  | suggestions  +------------------------+
|          -Sesame Street             | for new gweepco programs...           |

root@zswamp.fidonet.org (Geoffrey Welsh) (11/16/90)

Matthew E Cross (profesor@wpi.WPI.EDU ) wrote:

 >At one time, I had in my hands the 6510 assembly source code 
 >for the
 >punter protocol.  Does anyone know where I can obtain a copy 
 >of this?

   At the risk of incurring much wrath, here it is:

10 rem  *** new protocol ***
20 rem  release 2: oct 27 1984
30 rem  by steve punter
40 ifpeek(49152)<>169thenload"term.c1 v2",8,1
50 open5,2,0,chr$(8):get#5,a$:bs=255:open1,8,15:dimt$(3):sys49176
60 ty$="psp":t$(1)="Program":t$(2)="SEQ":t$(3)="WordPro":cs=1.02273e6
70 br$="300":gosub6000
80 print"Option: ("+br$+" Baud) Block Size ="bs
90 print"   1 - Terminal Mode"
100 print"   2 - Transmit a File"
110 print"   3 - Receive a File"
120 print"   4 - Change Block Size"
130 print"   5 - Toggle Baud Rate (300-1200)"
140 geta$:ifa$=""then140
150 ifa$="2"then1000
160 ifa$="3"then2000
170 ifa$="4"then3000
180 ifa$="1"thenprint"Terminal Mode:":goto4000
190 ifa$="5"then6700
200 goto140
1000 print"Program Name? ";:gosub5000:ifi$=""orfl=1then80
1010 print"File Type (P, S, or W)?";
1020 geta$:ifa$=""then1020
1030 ifa$=" "then80
1040 ifa$="p"thensa=0:t=1:goto1080
1050 ifa$="s"thensa=2:t=2:goto1080
1060 ifa$="w"thensa=0:t=3:goto1080
1070 goto1020
1080 printt$(t):open2,8,sa,i$:input#1,e$,em$,t$,s$
1090 ifval(e$)>0thenprinte$","em$","t$","s$:close2:goto1010
1120 sys49173:poke51227,t:sys49164:ifpeek(512)=1thenclose2:goto4000
1130 sys49173:poke51224,bs:sys49158:close2:goto4000
2000 print"Save As? ";:gosub5000:ifi$=""orfl=1then80
2010 sys49173:sys49161:ifpeek(512)=1then4000
2020 t$=","+mid$(ty$,peek(51227),1)+",w"
2025 print"File Type: "t$(peek(51227))
2030 open2,8,2,"@0:"+i$+t$:forx=1to1300:nextx:print:sys61310
2040 sys49173:sys49155:close2:forx=1to1500:nextx:poke668,peek(667):goto4000
3000 print"Block Size? ";:gosub5000:ifi$=""then80
3010 bs=val(i$):ifbs<40thenbs=40
3020 ifbs>255thenbs=255
3030 goto80
4000 print
4010 sys49173:sys49167:goto80
5000 i$="":fl=0
5010 print" ";
5020 geta$:ifa$=""then5020
5030 ifa$=chr$(13)then5100
5040 ifa$=chr$(20)then5080
5045 ifa$="?"thenfl=1:goto5100
5050 iflen(a$)>20then5020
5060 if(asc(a$)and127)<32then5020
5070 printa$;:i$=i$+a$:goto5010
5080 iflen(i$)=0then5020
5090 print" ??";:i$=left$(i$,len(i$)-1):goto5010
5100 print" ":return
6000 s=val(br$):poke659,6:ifs=1200thenpoke659,8
6010 rc=cs/s:gosub6500:poke51968,lo:poke51969,hi
6020 ifs=1200thenrc=cs/s*.966:gosub6500
6030 poke665,lo:poke666,hi
6040 rc=cs/s/2-100:gosub6500:poke661,lo:poke662,hi:return
6500 hi=int(rc/256):lo=int(rc-hi*256):return
6700 ifbr$="300"thenbr$="1200":goto6720
6710 br$="300"
6720 gosub6000:goto80
          
1000 REM  SAVE "@0:NEWPROT.SRC.1200",8
1010 :
1020 OPEN2,8,2,"@0:NEWPROT1200,P,W"
1030 :
1040 SYS700
1050 ;
1060 .OPT O2
1070 ;
1080 DIFFER = $0000
1090 STARTLOC = $C000
1100 C64 = 1
1110 PNTA = $62
1120 PNTB = $64
1130 STAT = $96
1140 DEFTO = $009A ;DEFAULT OUTPUT DEVICE (KERNAL)
1150 PTR1 = $009E ;TAPE PASS1 ERROR LOG (KERNAL)
1160 BUFPNTR = $A6 ;POINTER TO TAPE I/O BUFFER (KERNAL) [2]
1170 TAPE1 = $B2 ;POINTER TO START OF TAPE BUFFER (KERNAL) [2]
1180 ROBUF = $00F9 ;POINTER TO RS232 OUTPUT BUFFER (KERNAL)
1190 LASTCH = $0200 ;LAST USED CHARACTER
1200 RIDBE = $029B
1210 RIDBS = $029C
1220 RODBS = $029D ;START POSITION OF RS232 OUTPUT BUFFER (KERNAL)
1230 RODBE = $029E ;END POSITION OF RS232 OUTPUT BUFFER (KERNAL)
1240 RS232ENB = $02A1 ;RS232 ENABLE=128, DISABLE=255
1250 IBSOUT = $0326 ;CHROUT ROUTINE VECTOR (KERNAL) [2]
1260 CODEBUF  = $C800-DIFFER ;BUFFER FOR INCOMING 3 CHR CODES
1270 BITPNT   = $C803-DIFFER ;BIT POINTER FOR ALLOWABLE MATCHES
1280 BITCNT   = $C804-DIFFER ;BIT COUNTER (0 TO 4)
1290 BITPAT   = $C805-DIFFER ;BIT PATTERN FOR SEARCHES
1300 TIMER1   = $C806-DIFFER ;TIMER FOR NON-RECEIVED CHARACTERS (2)
1310 GBSAVE   = $C808-DIFFER ;LOCATION TO SAVE GOOD BAD SIGNAL NEEDED
1320 BUFCOUNT = $C809-DIFFER ;NUMBER OF CHRS TO BUFFER INTO BLOCK
1330 DELAY    = $C80B-DIFFER ;DELAY FOR WAIT PERIOD
1340 SKPDELAY = $C80C-DIFFER ;DELAY SKIP COUNTER
1350 ENDFLAG  = $C80D-DIFFER ;FLAG TO INDICATE LAST BLOCK
1360 CHECK    = $C80E-DIFFER ;SAVE PLACE FOR CHECKSUM (4)
1370 CHECK1   = $C812-DIFFER ;SECONDARY CHECKSUM HOLDING PLACE (4)
1380 BUFPNT   = $C816-DIFFER ;POINTER TO CURRENT BUFFER
1390 RECSIZE  = $C817-DIFFER ;SIZE OF RECEIVED BUFFER
1400 MAXSIZE  = $C818-DIFFER ;MAXIMUM BLOCK SIZE
1410 BLOCKNUM = $C819-DIFFER ;BLOCK NUMBER (2)
1420 FILETYPE = $C81B-DIFFER ;FILE TYPE (FROM BASIC)
1430 STACK    = $C81C-DIFFER ;STACK POINTER AT ENTRY
1440 DONTDASH = $C81D-DIFFER ;FLAG TO SUPPRESS DASHES AND COLONS
1450 SPECMODE = $C81E-DIFFER ;FLAG TO SEND SPECIAL START CODE
1460 BUFFER   = $C900-DIFFER ;BUFFER FOR BLOCK
1470 ;
1480 ;BUFFER POSITIONS
1490 ;
1500 SIZEPOS = 4
1510 NUMPOS = 5
1520 DATAPOS = 7
1530 ;
1540 XMIT = $CB00
1550 OLDOUT = $CB02
1560 BASIC4 = $EF06 ;BASIC CALL FROM CHROUT
1570 BASIC3 = $EF3B ;BASIC CALL FROM CHROUT
1580 SETUP = $EF7E  ;SET UP RS232 TO RECEIVE AGAIN
1590 ;
1600 ;KERNAL LOCATIONS
1610 ;
1620 BASIC1 = $F80D ;BASIC CALL FROM CHROUT
1630 BASIC2 = $F864 ;BASIC CALL FROM CHROUT
1640 READST = $FFB7
1650 CHKIN  = $FFC6 ;OPEN CHANNEL FOR INPUT
1660 CHKOUT = $FFC9 ;OPEN CHANNEL FOR OUTPUT
1670 CLRCHN = $FFCC ;CLOSE INPUT AND OUTPUT CHANNELS
1680 CHRIN  = $FFCF ;INPUT CHARACTER FROM CHANNEL
1690 CHROUT = $FFD2 ;OUTPUT CHARACTER TO CHANNEL
1700 GETIN  = $FFE4 ;GET A CHARACTER FROM KEYBOARD QUEUE
1710 ZFFFE = $FFFE
1720 ;
1730 *=STARTLOC
1740 ;
1750 LDA #00   ;SYS 49152
1760 .BYT $2C
1770 LDA #03   ;SYS 49155
1780 .BYT $2C
1790 LDA #06   ;SYS 49158
1800 .BYT $2C
1810 LDA #09   ;SYS 49161
1820 .BYT $2C
1830 LDA #12   ;SYS 49164
1840 .BYT $2C
1850 LDA #15   ;SYS 49167
1860 NOP
1870 JMP OVER
1880 JMP RESET
1890 JMP INIT
1900 ;
1910 OVER STA PNTA
1920 TSX 
1930 STX STACK
1940 LDA #<TABLE
1950 CLC 
1960 ADC PNTA
1970 STA JMPPOINT+1
1980 LDA #>TABLE
1990 ADC #$00
2000 STA JMPPOINT+2
2010 JMPPOINT JMP TABLE
2020 ;
2030 TABLE JMP ACCEPT
2040 JMP RECEIVE
2050 JMP TRANSMIT
2060 JMP RECTYPE
2070 JMP TRANTYPE
2080 JMP TERMINAL
2090 JMP INIT
2100 ;
2110 CODES .ASC "GOO"
2120 .ASC "BAD"
2130 .ASC "ACK"
2140 .ASC "S/B"
2150 .ASC "SYN"
2160 ;
2170 ;ACCEPT CHARACTERS AND CHECK FOR CODES
2180 ;
2190 ACCEPT STA BITPAT ;SAVE REQUIRED BIT PATTERN
2200 LDA #$00
2210 STA CODEBUF
2220 STA CODEBUF+1
2230 STA CODEBUF+2
2240 CD1 LDA #$00
2250 STA TIMER1 ;CLEAR TIMER
2260 STA TIMER1+1
2270 CD2 JSR EXIT
2280 JSR GETNUM ;GET#5,A$
2290 LDA STAT
2300 BNE CD3 ;IF NO CHR, DO TIMER CHECK
2310 LDA CODEBUF+1
2320 STA CODEBUF
2330 LDA CODEBUF+2
2340 STA CODEBUF+1
2350 LDA LASTCH
2360 STA CODEBUF+2
2370 LDA #$00
2380 STA BITCNT ;CLEAR BIT COUNTER
2390 LDA #$01
2400 STA BITPNT ;INITIALIZE BIT POINTER
2410 CD4 LDA BITPAT ;LOOK AT BIT PATTERN
2420 BIT BITPNT ;IS BIT SET
2430 BEQ CD5 ;NO, DON'T CHECK THIS CODE WORD
2440 LDY BITCNT
2450 LDX #$00
2460 CD6 LDA CODEBUF,X
2470 CMP CODES,Y
2480 BNE CD5
2490 INY 
2500 INX 
2510 CPX #$03
2520 BNE CD6
2530 JMP CD7
2540 ;
2550 CD5 ASL BITPNT ;SHIFT BIT POINTER
2560 LDA BITCNT
2570 CLC 
2580 ADC #$03
2590 STA BITCNT
2600 CMP #15
2610 BNE CD4
2620 JMP CD1
2630 ;
2640 CD7 LDA #255
2650 STA TIMER1
2660 STA TIMER1+1
2670 JMP CD2
2680 ;
2690 CD3 INC TIMER1
2700 BNE CD9
2710 INC TIMER1+1
2720 CD9 LDA TIMER1+1
2730 ORA TIMER1
2740 BEQ CD8
2750 LDA TIMER1
2760 CMP #$07
2770 .IF C64:LDA TIMER1+1
2780 .IF C64:CMP #20
2790 BCC CD2
2800 LDA #$01
2810 STA STAT
2820 JMP DODELAY
2830 ;
2840 CD8 LDA #$00
2850 STA STAT
2860 RTS 
2870 ;
2880 ;
2890 .IF C64:.GOTO 3210
2900 ;
2910 ;DO A GET# FOR PET
2920 ;
2930 GETNUM LDX #5
2940 JSR CHKIN
2950 JSR CHRIN
2960 STA LASTCH
2970 JSR CLRCHN
2980 RTS
2990 ;
3000 ;DO A GET# FOR PET TERMINAL MODE
3010 ;
3020 GETNUM1 LDA $E823
3030 BPL GT1
3040 LDX #5
3050 JSR CHKIN
3060 JSR CHRIN
3070 STA LASTCH
3080 JSR CLRCHN
3090 LDA $E822
3100 LDA #0
3110 STA STAT
3120 RTS
3130 ;
3140 GT1 LDA #0
3150 STA LASTCH
3160 LDA #2
3170 STA STAT
3180 RTS
3190 ;
3200 .GOTO 3540
3210 ;
3220 ;GET# FOR C64
3230 ;
3240 GETNUM1 NOP 
3250 GETNUM TYA 
3260 PHA 
3270 LDA RIDBE
3280 CMP RIDBS
3290 BEQ GET1
3300 LDY RIDBS
3310 LDA ($F7),Y
3320 PHA 
3330 INC RIDBS
3340 LDA #$00
3350 STA STAT
3360 PLA 
3370 STA LASTCH
3380 PLA 
3390 TAY 
3400 JMP DORTS
3410 ;
3420 GET1 LDA #$02
3430 STA STAT
3440 LDA #$00
3450 STA LASTCH
3460 PLA 
3470 TAY 
3480 ;
3490 DORTS PHA 
3500 LDA #$03
3510 STA $BA
3520 PLA 
3530 RTS 
3540 ;
3550 ;SEND A CODE
3560 ;
3570 SENDCODE LDX #$05
3580 JSR CHKOUT
3590 LDX #$00
3600 SN1 LDA CODES,Y
3610 JSR CHROUT
3620 INY 
3630 INX 
3640 CPX #$03
3650 BNE SN1
3660 JMP CLRCHN
3670 ;
3680 ;DO HANDSHAKING FOR RECEPTION END
3690 ;
3700 RECHAND STA GBSAVE ;SAVE GOOD OR BAD SIGNAL AS NEEDED
3710 LDA #$00
3720 STA DELAY ;NO DELAY
3730 RC1 LDA #$02
3740 STA PNTA
3750 LDY GBSAVE
3760 JSR SENDCODE ;SEND G/B SIGNAL
3770 RC9 LDA #%00100 ;ALLOW "ACK" SIGNALS
3780 JSR ACCEPT ;WAIT FOR CODE
3790 LDA STAT
3800 BEQ RC2 ;IF OK, SEND G/B SIGNAL AGAIN
3810 DEC PNTA
3820 BNE RC9
3830 JMP RC1
3840 ;
3850 RC2 LDY #$09
3860 JSR SENDCODE ;SEND "S/B" CODE
3870 LDA ENDFLAG
3880 BEQ RC5
3890 LDA GBSAVE
3900 BEQ RC6
3910 RC5 LDA BUFFER+SIZEPOS
3920 STA BUFCOUNT
3930 STA RECSIZE
3940 JSR RECMODEM ;WAIT FOR BLOCK
3950 LDA STAT
3960 CMP #%0001 ;CHECK FOR GOOD BLOCK
3970 BEQ RC4
3980 CMP #%0010 ;CHECK FOR BLANK INPUT
3990 BEQ RC2
4000 CMP #%0100 ;CHECK FOR LOSS OF SIGNAL
4010 BEQ RC4
4020 CMP #%1000 ;CHECK FOR "ACK" SIGNAL
4030 BEQ RC2
4040 RC4 RTS 
4050 ;
4060 RC6 LDA #%10000 ;WAIT FOR "SYN" SIGNAL
4070 JSR ACCEPT
4080 LDA STAT
4090 BNE RC2 ;IF NOT, SEND "S/B" AGAIN
4100 LDA #10
4110 STA BUFCOUNT
4120 RC8 LDY #12 ;SEND "SYN" SIGNAL
4130 JSR SENDCODE
4140 LDA #%01000 ;WAIT FOR "S/B" SIGNAL
4150 JSR ACCEPT
4160 LDA STAT
4170 BEQ RC7
4180 DEC BUFCOUNT
4190 BNE RC8
4200 RC7 RTS 
4210 ;
4220 ;DO HANDSHAKING FOR TRANSMISSION END
4230 ;
4240 TRANHAND LDA #$01
4250 STA DELAY ;USE DELAY
4260 TX2 LDA SPECMODE
4270 BEQ TX20
4280 LDY #$00
4290 JSR SENDCODE ;SEND A "GOO" SIGNAL
4300 TX20 LDA #%01011 ;ALLOW "GOO", "BAD", AND "S/B"
4310 JSR ACCEPT ;WAIT FOR CODES
4320 LDA STAT
4330 BNE TX2 ;IF NO SIGNAL, WAIT AGAIN
4340 LDA #$00
4350 STA SPECMODE
4360 LDA BITCNT
4370 CMP #$00 ;"GOOD" SIGNAL
4380 BNE TX10 ;NO, RESEND OLD BLOCK
4390 LDA ENDFLAG
4400 BNE TX4
4410 INC BLOCKNUM
4420 BNE TX7
4430 INC BLOCKNUM+1
4440 TX7 JSR THISBUF
4450 LDY #NUMPOS ;BLOCK NUMBER HIGH ORDER PART
4460 INY 
4470 LDA (PNTB),Y
4480 CMP #255
4490 BNE TX3
4500 LDA #$01
4510 STA ENDFLAG
4520 LDA BUFPNT
4530 EOR #$01
4540 STA BUFPNT
4550 JSR THISBUF
4560 JSR DUMMYBL1
4570 JMP TX1
4580 ;
4590 TX3 JSR DUMMYBLK ;YES, GET NEW BLOCK
4600 TX1 LDA #"-"
4610 .BYT $2C
4620 TX10 LDA #":"
4630 JSR PRTDASH
4640 LDY #$06
4650 JSR SENDCODE ;SEND "ACK" CODE
4660 LDA #%01000 ;ALLOW ONLY "S/B" CODE
4670 JSR ACCEPT ;WAIT FOR CODE
4680 LDA STAT
4690 BNE TX1
4700 JSR THISBUF
4710 LDY #SIZEPOS ;BLOCK SIZE
4720 LDA (PNTB),Y
4730 STA BUFCOUNT
4740 JSR ALTBUF
4750 LDX #$05
4760 JSR CHKOUT
4770 LDY #$00
4780 TX6 LDA (PNTB),Y ;TRANSMIT ALTERNATE BUFFER
4790 JSR CHROUT
4800 INY 
4810 CPY BUFCOUNT
4820 BNE TX6
4830 JSR CLRCHN
4840 LDA #$00
4850 RTS 
4860 ;
4870 TX4 LDA #"*"
4880 JSR PRTDASH
4890 LDY #$06
4900 JSR SENDCODE ;SEND "ACK" SIGNAL
4910 LDA #%01000
4920 JSR ACCEPT ;WAIT FOR "S/B" SIGNAL
4930 LDA STAT
4940 BNE TX4 ;IF NOT, RESEND "ACK" SIGNAL
4950 LDA #10
4960 STA BUFCOUNT
4970 TX5 LDY #12
4980 JSR SENDCODE ;SEND "SYN" SIGNAL
4990 LDA #%10000
5000 JSR ACCEPT ;WAIT FOR "SYN" SIGNAL BACK
5010 LDA STAT
5020 BEQ TX8
5030 DEC BUFCOUNT
5040 BNE TX5
5050 TX8 LDA #$03
5060 STA BUFCOUNT
5070 TX9 LDY #$09
5080 JSR SENDCODE ;SEND "S/B" SIGNAL
5090 LDA #$00000
5100 JSR ACCEPT ;JUST WAIT
5110 DEC BUFCOUNT
5120 BNE TX9
5130 LDA #$01
5140 RTS 
5150 ;
5160 ;RECEIVE A BLOCK FROM THE MODEM
5170 ;
5180 ; STAT RETURNS WITH:
5190 ;
5200 ;  BIT 0 - BUFFERED ALL CHARACTERS SUCCESSFULLY
5210 ;  BIT 1 - NO CHARACTERS RECEIVED AT ALL
5220 ;  BIT 2 - INSUFFICIENT CHARACTERS RECEIVED
5230 ;  BIT 3 - "ACK" SIGNAL RECEIVED
5240 ;
5250 RECMODEM LDY #$00 ;START INDEX
5260 RCM5 LDA #$00 ;CLEAR TIMERS
5270 STA TIMER1
5280 STA TIMER1+1
5290 RCM1 JSR EXIT
5300 JSR GETNUM ;GET A CHR FROM THE MODEM
5310 LDA STAT
5320 BNE RCM2 ;NO CHARACTER RECEIVED
5330 LDA LASTCH
5340 STA BUFFER,Y ;SAVE CHR IN BUFFER
5350 CPY #$03 ;CHR ONE OF THE FIRST 3
5360 BCS RCM3 ;NO, SKIP CODE CHECK
5370 STA CODEBUF,Y ;SAVE CHR IN CODE BUFFER
5380 CPY #$02 ;ON THE 3RD CHR
5390 BNE RCM3 ;NO, DON'T LOOK AT CHRS YET
5400 LDA CODEBUF ;CHECK FOR A "ACK" SIGNAL
5410 CMP #"A"
5420 BNE RCM3
5430 LDA CODEBUF+1
5440 CMP #"C"
5450 BNE RCM3
5460 LDA CODEBUF+2
5470 CMP #"K"
5480 BEQ RCM4 ;"ACK" FOUND
5490 RCM3 INY ;INC INDEX
5500 CPY BUFCOUNT ;BUFFERED ALL CHRS
5510 BNE RCM5 ;NO, BUFFER NEXT
5520 LDA #%0001 ;YES, RETURN BIT 0 SET
5530 STA STAT
5540 RTS 
5550 ;
5560 RCM4 LDA #$FF ;"SYN" FOUND, SET TIMER TO -1
5570 STA TIMER1
5580 STA TIMER1+1
5590 JMP RCM1 ;SEE IF THERE IS ANOTHER CHR
5600 ;
5610 RCM2 INC TIMER1 ;INC TIMER
5620 BNE RCM6
5630 INC TIMER1+1
5640 RCM6 LDA TIMER1
5650 ORA TIMER1+1 ;TIMER NOW AT ZERO
5660 BEQ RCM7 ;"SYN" FOUND WITH NO FOLLOWING CHRS
5670 LDA TIMER1
5680 CMP #$06
5690 .IF C64:LDA TIMER1+1
5700 .IF C64:CMP #16 ;TIME OUT YET
5710 BNE RCM1 ;NO, GET NEXT CHR
5720 LDA #%0010 ;YES, SET BIT 1
5730 STA STAT
5740 CPY #$00
5750 BEQ RCM9
5760 LDA #%0100 ;BUT IF CHRS RECEIVED, SET BIT 2
5770 STA STAT
5780 RCM9 JMP DODELAY
5790 ;
5800 RCM7 LDA #%1000 ;"ACK" FOUND, SET BIT 2
5810 STA STAT
5820 RTS 
5830 ;
5840 ;CREATE DUMMY BLOCK FOR TRANSMISSION
5850 ;
5860 DUMMYBLK LDA BUFPNT
5870 EOR #$01
5880 STA BUFPNT
5890 JSR THISBUF ;READ BLOCK INTO "THIS" BUFFER
5900 LDY #NUMPOS ;BLOCK NUMBER
5910 LDA BLOCKNUM
5920 CLC 
5930 ADC #$01
5940 STA (PNTB),Y ;SET BLOCK NUMBER LOW PART
5950 INY 
5960 LDA BLOCKNUM+1
5970 ADC #$00
5980 STA (PNTB),Y ;SET BLOCK NUMBER HIGH PART
5990 LDX #$02
6000 JSR CHKIN
6010 LDY #DATAPOS ;ACTUAL BLOCK
6020 DB1 JSR CHRIN
6030 STA (PNTB),Y
6040 INY 
6050 JSR READST
6060 BNE DB4
6070 CPY MAXSIZE
6080 BNE DB1
6090 TYA 
6100 PHA 
6110 JMP DB5
6120 ;
6130 DB4 TYA 
6140 PHA 
6150 LDY #NUMPOS ;BLOCK NUMBER
6160 INY ;HIGH PART
6170 LDA #255
6180 STA (PNTB),Y
6190 JMP DB5
6200 ;
6210 DUMMYBL1 PHA ;SAVE SIZE OF JUST READ BLOCK
6220 DB5 JSR CLRCHN
6230 .IF C64:JSR RESET
6240 .IF C64:JSR DOD2
6250 .IF C64:JSR RESET
6260 LDY #SIZEPOS ;BLOCK SIZE
6270 LDA (PNTB),Y
6280 STA BUFCOUNT ;SET BUFCOUNT FOR CHECKSUM
6290 JSR ALTBUF
6300 PLA 
6310 LDY #SIZEPOS ;BLOCK SIZE
6320 STA (PNTB),Y
6330 JSR CHECKSUM
6340 RTS 
6350 ;
6360 ;SET POINTERS FOR CURRENT BUFFER
6370 ;
6380 THISBUF LDA #<BUFFER
6390 STA PNTB
6400 LDA BUFPNT
6410 CLC 
6420 ADC #>BUFFER
6430 STA PNTB+1
6440 RTS 
6450 ;
6460 ;SET POINTER B FOR ALTERNATE BUFFER
6470 ;
6480 ALTBUF LDA #<BUFFER
6490 STA PNTB
6500 LDA BUFPNT
6510 EOR #$01
6520 CLC 
6530 ADC #>BUFFER
6540 STA PNTB+1
6550 RTS 
6560 ;
6570 ;CALCULATE CHECKSUM
6580 ;
6590 CHECKSUM LDA #$00
6600 STA CHECK1
6610 STA CHECK1+1
6620 STA CHECK1+2
6630 STA CHECK1+3
6640 LDY #SIZEPOS
6650 CKS1 LDA CHECK1
6660 CLC 
6670 ADC (PNTB),Y
6680 STA CHECK1
6690 BCC CKS2
6700 INC CHECK1+1
6710 CKS2 LDA CHECK1+2
6720 EOR (PNTB),Y
6730 STA CHECK1+2
6740 LDA CHECK1+3
6750 ROL A ;SET OR CLEAR CARRY FLAG
6760 ROL CHECK1+2
6770 ROL CHECK1+3
6780 INY 
6790 CPY BUFCOUNT
6800 BNE CKS1
6810 LDY #$00
6820 LDA CHECK1
6830 STA (PNTB),Y
6840 INY 
6850 LDA CHECK1+1
6860 STA (PNTB),Y
6870 INY 
6880 LDA CHECK1+2
6890 STA (PNTB),Y
6900 INY 
6910 LDA CHECK1+3
6920 STA (PNTB),Y
6930 RTS 
6940 ;
6950 ;TRANSMIT A PROGRAM
6960 ;
6970 TRANSMIT LDA #$00
6980 STA ENDFLAG
6990 STA SKPDELAY
7000 STA DONTDASH
7010 LDA #$01
7020 STA BUFPNT
7030 LDA #$FF
7040 STA BLOCKNUM
7050 STA BLOCKNUM+1
7060 JSR ALTBUF
7070 LDY #SIZEPOS ;BLOCK SIZE
7080 LDA #DATAPOS
7090 STA (PNTB),Y
7100 JSR THISBUF
7110 LDY #NUMPOS ;BLOCK NUMBER
7120 LDA #$00
7130 STA (PNTB),Y
7140 INY 
7150 STA (PNTB),Y
7160 TRM1 JSR TRANHAND
7170 BEQ TRM1
7180 REC3 LDA #$00
7190 STA LASTCH
7200 RTS 
7210 ;
7220 ;RECEIVE A FILE
7230 ;
7240 RECEIVE LDA #$01
7250 STA BLOCKNUM
7260 LDA #$00
7270 STA BLOCKNUM+1
7280 STA ENDFLAG
7290 STA BUFPNT
7300 STA BUFFER+NUMPOS ;BLOCK NUMBER
7310 STA BUFFER+NUMPOS+1
7320 STA SKPDELAY
7330 LDA #DATAPOS
7340 STA BUFFER+SIZEPOS ;BLOCK SIZE
7350 LDA #$00
7360 REC1 JSR RECHAND
7370 LDA ENDFLAG
7380 BNE REC3
7390 JSR MATCH ;DO CHECKSUMS MATCH
7400 BNE REC2 ;NO
7410 JSR CLRCHN
7420 LDA BUFCOUNT
7430 CMP #DATAPOS
7440 BEQ REC7
7450 LDX #$02
7460 JSR CHKOUT
7470 LDY #DATAPOS
7480 REC6 LDA BUFFER,Y
7490 JSR CHROUT
7500 INY 
7510 CPY BUFCOUNT
7520 BNE REC6
7530 JSR CLRCHN
7540 REC7 LDA BUFFER+NUMPOS+1 ;BLOCK NUMBER HIGH ORDER PART
7550 CMP #$FF
7560 BNE REC4
7570 LDA #$01
7580 STA ENDFLAG
7590 LDA #"*"
7600 .BYT $2C
7610 REC4 LDA #"-"
7620 JSR CHROUT
7630 .IF C64:JSR RESET
7640 LDA #$00
7650 JMP REC1
7660 ;
7670 REC2 JSR CLRCHN
7680 LDA #":"
7690 JSR CHROUT
7700 LDA RECSIZE
7710 STA BUFFER+SIZEPOS
7720 LDA #$03
7730 JMP REC1
7740 ;
7750 ;SEE IF CHECKSUMS MATCH
7760 ;
7770 MATCH LDA BUFFER
7780 STA CHECK
7790 LDA BUFFER+1
7800 STA CHECK+1
7810 LDA BUFFER+2
7820 STA CHECK+2
7830 LDA BUFFER+3
7840 STA CHECK+3
7850 JSR THISBUF
7860 LDA RECSIZE
7870 STA BUFCOUNT
7880 JSR CHECKSUM
7890 LDA BUFFER
7900 CMP CHECK
7910 BNE MTC1
7920 LDA BUFFER+1
7930 CMP CHECK+1
7940 BNE MTC1
7950 LDA BUFFER+2
7960 CMP CHECK+2
7970 BNE MTC1
7980 LDA BUFFER+3
7990 CMP CHECK+3
8000 BNE MTC1
8010 LDA #$00
8020 RTS 
8030 ;
8040 MTC1 LDA #$01
8050 RTS 
8060 ;
8070 ;RECEIVE FILE TYPE BLOCK
8080 ;
8090 RECTYPE LDA #$00
8100 STA BLOCKNUM
8110 STA BLOCKNUM+1
8120 STA ENDFLAG
8130 STA BUFPNT
8140 STA SKPDELAY
8150 LDA #DATAPOS
8160 CLC 
8170 ADC #$01
8180 STA BUFFER+SIZEPOS
8190 LDA #$00
8200 RCT3 JSR RECHAND
8210 LDA ENDFLAG
8220 BNE RCT1
8230 JSR MATCH
8240 BNE RCT2
8250 LDA BUFFER+DATAPOS
8260 STA FILETYPE
8270 LDA #$01
8280 STA ENDFLAG
8290 LDA #$00
8300 JMP RCT3
8310 ;
8320 RCT2 LDA RECSIZE
8330 STA BUFFER+SIZEPOS
8340 LDA #$03
8350 JMP RCT3
8360 ;
8370 RCT1 LDA #$00
8380 STA LASTCH
8390 RTS 
8400 ;
8410 ;TRANSMIT FILE TYPE
8420 ;
8430 TRANTYPE LDA #$00
8440 STA ENDFLAG
8450 STA SKPDELAY
8460 LDA #$01
8470 STA BUFPNT
8480 STA DONTDASH
8490 LDA #255
8500 STA BLOCKNUM
8510 STA BLOCKNUM+1
8520 JSR ALTBUF
8530 LDY #SIZEPOS ;BLOCK SIZE
8540 LDA #DATAPOS
8550 CLC 
8560 ADC #$01
8570 STA (PNTB),Y
8580 JSR THISBUF
8590 LDY #NUMPOS ;BLOCK NUMBER
8600 LDA #255
8610 STA (PNTB),Y
8620 INY 
8630 STA (PNTB),Y
8640 LDY #DATAPOS
8650 LDA FILETYPE
8660 STA (PNTB),Y
8670 LDA #$01
8680 STA SPECMODE
8690 TRF1 JSR TRANHAND
8700 BEQ TRF1
8710 LDA #$00
8720 STA LASTCH
8730 RTS 
8740 ;
8750 ;DO DELAY FOR TIMING
8760 ;
8770 DODELAY INC SKPDELAY
8780 LDA SKPDELAY
8790 CMP #$03
8800 BCC DOD1
8810 LDA #$00
8820 STA SKPDELAY
8830 LDA DELAY
8840 BEQ DOD2
8850 BNE DOD3
8860 ;
8870 DOD1 LDA DELAY
8880 BEQ DOD3
8890 ;
8900 DOD2 LDX #$00
8910 LP1 LDY #$00
8920 LP2 INY 
8930 BNE LP2
8940 INX 
8950 CPX #120
8960 BNE LP1
8970 DOD3 RTS 
8980 ;
8990 ;PRINT DASH, COLON, OR STAR
9000 ;
9010 PRTDASH PHA 
9020 LDA BLOCKNUM
9030 ORA BLOCKNUM+1
9040 BEQ PRTD1
9050 LDA DONTDASH
9060 BNE PRTD1
9070 PLA 
9080 JSR CHROUT
9090 PHA 
9100 PRTD1 PLA 
9110 RTS 
9120 ;
9130 ;RESET RS232 PORT
9140 ;
9150 RESET JSR SETUP
9160 LDA RS232ENB
9170 CMP #$80
9180 BEQ RESET
9190 CMP #$92
9200 BEQ RESET
9210 RTS 
9220 ;
9230 ;TERMINAL EMULATION ROUTINE
9240 ;
9250 TERMINAL JSR CURSOR
9260 TERM JSR GETNUM1
9270 LDA STAT
9280 BNE KEYBJ
9290 LDA LASTCH
9300 AND #$7F
9310 STA LASTCH
9320 CMP #$08
9330 BEQ OK1
9340 CMP #$0D
9350 BEQ OK1
9360 CMP #$20
9370 BPL OK1
9380 KEYBJ JMP KEYBOARD
9390 ;
9400 OK1 CMP #"A"+$20
9410 BCC OK2
9420 CMP #"Z"+$21
9430 BCS OK2
9440 SEC 
9450 SBC #$20
9460 STA LASTCH
9470 JMP OK3
9480 ;
9490 OK2 CMP #$41
9500 BCC OK3
9510 CMP #"Z"+1
9520 BCS OK3
9530 CLC 
9540 ADC #$80
9550 STA LASTCH
9560 ;
9570 OK3 CMP #$08
9580 BNE OK4
9590 LDA #$14
9600 STA LASTCH
9610 OK4 CMP #34 ;QUOTE
9620 BNE OK5
9630 JSR CHROUT
9640 LDA #20
9650 JSR CHROUT
9660 LDA #34
9670 OK5 LDA LASTCH
9680 CMP #$0D
9690 BNE OK6
9700 LDA #$20
9710 JSR CHROUT
9720 LDA #$0D
9730 OK6 JSR CHROUT
9740 JSR CURSOR
9750 ;
9760 KEYBOARD JSR GETIN
9770 BEQ TERM
9780 STA LASTCH
9790 CMP #$13      ;CLR/HOME KEY
9800 BEQ TERMOUT
9810 CMP #"A"
9820 BCC OK7       ;<"A"
9830 CMP #"Z"+1
9840 BCS OK7       ;>"Z"
9850 CLC
9860 ADC #$20      ;TO LOWERCASE ASCII
9870 STA LASTCH
9880 JMP OK8
9890 ;
9900 OK7 LDA LASTCH
9910 CMP #"A"+$80
9920 BCC OK8       ;<"A"
9930 CMP #"Z"+$81
9940 BCS OK8       ;>"Z"
9950 SEC
9960 SBC #$80      ;TO UPPERCASE ASCII
9970 STA LASTCH
9980 ;
9990 OK8 CMP #20   ;BACKSPACE
10000 BNE OK9
10010 LDA #$08
10020 STA LASTCH
10030 OK9 CMP #$83  ;SHIFT R/S
10040 BNE OKA
10050 LDA #$10      ;CTRL P
10060 STA LASTCH
10070 OKA LDX #$05
10080 JSR CHKOUT
10090 LDA LASTCH
10100 JSR CHROUT
10110 JSR CLRCHN
10120 JMP TERMINAL
10130 ;
10140 TERMOUT RTS   ;WITH CLR/HOME
10150 ;
10160 CURSOR LDA #$12
10170 JSR CHROUT
10180 LDA #$20
10190 JSR CHROUT
10200 LDA #$9D
10210 JSR CHROUT
10220 LDA #$92
10230 JSR CHROUT
10240 ;
10250 ;CHECK FOR COMMODORE KEY
10260 ;
10270 EXIT LDA $028D   ;IS COMMODORE
10280 CMP #$02         ;KEY DOWN
10290 BNE EXIT1
10300 EXIT2 PLA 
10310 TSX 
10320 CPX STACK
10330 BNE EXIT2
10340 EXIT1 LDA #$01
10350 STA LASTCH
10360 RTS 
10370 ;
10380 ;MOVE CHROUT VECTOR IF NECESSARY
10390 ;
10400 INIT LDA IBSOUT  ;BEEN MOVED YET
10410 CMP #<NEWOUT
10420 BNE INIT1        ;NO, CHANGE IT
10430 LDA IBSOUT+1
10440 CMP #>NEWOUT
10450 BEQ INIT2        ;YES, LEAVE IT
10460 INIT1 LDA IBSOUT ;STORE OLD CHROUT VECTOR
10470 STA OLDOUT
10480 LDA IBSOUT+1
10490 STA OLDOUT+1
10500 LDA #<NEWOUT     ;SET NEW CHROUT VECTOR
10510 STA IBSOUT
10520 LDA #>NEWOUT
10530 STA IBSOUT+1
10540 INIT2 RTS 
10550 ;
10560 ;NEW CHROUT ROUTINE TO CORRECT FOR 1200 BAUD SPEED PROBLEMS
10570 ;
10580 NEWOUT PHA ;DUPLICIATON OF ORIGINAL KERNAL ROUTINES
10590 LDA DEFTO  ;TEST DFAULT OUTPUT DEVICE FOR
10600 CMP #$03   ;SCREEN, AND...
10610 BNE NEWOUT1
10620 PLA        ;IF SO, GO BACK TO ORIGINAL ROM ROUTINES
10630 JMP (OLDOUT)
10640 ;
10650 NEWOUT1 BCC NEWOUT2 ;IF DEVICE NUMBER LESS THAN 3,
10660 PLA ;ALSO GO BACK TO ORIGINAL KERNAL ROUTINES
10670 JMP (OLDOUT)
10680 ;
10690 NEWOUT2 LSR A
10700 PLA 
10710 STA PTR1
10720 TXA 
10730 PHA 
10740 TYA 
10750 PHA 
10760 BCC NEWOUT9
10770 JSR BASIC1
10780 BNE NEWOUT5
10790 JSR BASIC2
10800 BCS NEWOUT7
10810 LDA #$02
10820 LDY #$00
10830 STA (TAPE1),Y
10840 INY 
10850 STY BUFPNTR
10860 NEWOUT5 LDA PTR1
10870 STA (TAPE1),Y
10880 NEWOUT6 CLC 
10890 NEWOUT7 PLA 
10900 TAY 
10910 PLA 
10920 TAX 
10930 LDA PTR1
10940 BCC NEWOUT8
10950 LDA #$00
10960 NEWOUT8 RTS 
10970 ;
10980 NEWOUT9 JSR NEWOUT10
10990 JMP NEWOUT6
11000 ;
11010 NEWOUT11 JSR NEWOUT12
11020 NEWOUT10 LDY RODBE
11030 INY 
11040 CPY RODBS
11050 BEQ NEWOUT11
11060 STY RODBE
11070 DEY 
11080 LDA PTR1
11090 STA (ROBUF),Y
11100 ;
11110 NEWOUT12 LDA RS232ENB
11120 LSR A
11130 BCS NEWOUT13
11140 LDA #$10
11150 STA $DD0E
11160 LDA XMIT
11170 STA $DD04
11180 LDA XMIT+1
11190 STA $DD05
11200 LDA #$81
11210 JSR BASIC3
11220 JSR BASIC4
11230 LDA #$11
11240 STA $DD0E
11250 NEWOUT13 RTS
11260 ;
11270 .END

--  
UUCP:     watmath!xenitec!zswamp!root | 602-66 Mooregate Crescent
Internet: root@zswamp.fidonet.org     | Kitchener, Ontario
FidoNet:  SYSOP, 1:221/171            | N2M 5E6 CANADA
Data:     (519) 742-8939              | (519) 741-9553
MC Hammer, n. Device used to ensure firm seating of MicroChannel boards
Try our new Bud 'C' compiler... it specializes in 'case' statements!

root@zswamp.fidonet.org (Geoffrey Welsh) (11/16/90)

Matthew E Cross (profesor@wpi.WPI.EDU ) wrote:

 >I do have description of the protocol.

   I'll include that, while I'm at it. Please read the next message for 
comments about the errors in the description and for the batch hack.

                                The C1 Protocol

                                by Steve Punter

Inception
---------
During the summer of 1981, when I first got the idea of putting up  a  BBS,  I
started  work  on  a  simple protocol for transfering programs to and from the
BBS. This protocol was similar in structure to XMODEM, and had about the  same
reliability.  Under  good  line conditions, it would give error free transfers
(this was to be expected). Under moderate noise conditions, the protocol would
hold up, and would still give error free transmissions. It was under poor line
conditions that it, and XMODEM, would fall apart.
 
In the summer of 1984, I started work on a very ambitious project; to  produce
a protocol that was both fast, and extremely reliable, even under the worst of
line conditions.  From  this  work  came  the  "C1"  protocol;  not  a  simple
block/checksum affair, but a complete communication system for the computer.
 
Be  warned, therefore, that understanding the ins and outs of "C1" will not be
easy, but with enough patience, there's no reason why even the  least  skilled
programmer cannot be comfortable with it.
 
Concepts
--------
The  concept  behind  the  "C1" protocol was simple; to allow two computers to
"talk" with one another (while transferring data) in such a way  that  nothing
short  of  a  complete  distortion  of the transmission line could result in a
misunderstanding. If this concept could  be  realized,  then  files  could  be
transferred  between  computers without fear of line noise causing a breakdown
in the protocol, or that the received data would differ, in any way, from that
which  was  sent.  Nothing is perfect though, and I don't, for a minute, claim
that "C1" is completely infallible, but I can say,  with  reasonable  comfort,
that  "C1"  can deliver bad line accuracy not found in any other microcomputer
transfer protocols. For this accuracy though, there is a price to pay, and  it
is  complexity;  the  protocol  is  extremely difficult to duplicate without a
complete and utter understanding of  the  intricate  workings  of  "C1".  This
document will attempt to give you that required understanding.

A Simple Conversation
---------------------
In first deciding how the protocol would function, I thought of how two people
could  carry  on  a  conversation   under   high   noise   conditions,   where
misunderstanding would be the norm. The senario I'm going to give differs from
the protocol in that the people talking have no way of verifying the  accuracy
of  what  they believe they have heard. What it is meant to demonstrate is how
the the two computers "talk" with one  another,  and  discuss  the  neccessary
repetition,  or  non-repetition,  of  each block of data (the cornerstone of a
checksum based transfer protocol).
 
Ken and John are attempting to assemble a machine in  the  middle  of  a  very
noisy  machine shop. Ken reads the instructions to John, who carries them out.
Even at close proximity, the two have difficulty hearing one another, so  they
adopt  of  form  banter  which  allows  each  instuction  to  be  verified and
acknowledged. Here is how the conversation might go:
 
John: Put part "A" in hole "D".
 
Ken: Understood, putting part "A" in hole "D".
 
John: Acknowledged, let me know when you are ready for the next instruction.
 
Ken: Go ahead, what do I do next?
     
John: Put screw "E" through slot "T".
 
Ken: I didn't understand that, could you please repeat.
 
John: Oh, ok, tell me when you're ready for that instruction again.
 
Ken: Ready now.
 
The conversation continues on in this fashion, guaranteeing that both John and
Ken  are fully aware of what the other is doing. In real life, people wouldn't
have the patience to keep up that sort of banter, but  that's  why  they  make
more mistakes than a computer. It is just this sort of "conversation" that the
two computers have between each other, only the  language  is  different;  the
instruction  is  replaced  by  the  block of data, and all other statements by
special codes.

Communication Codes
-------------------
One of the areas where simple protocols fall apart is in the  transmission  of
"handshaking  codes".  It's called handshaking because is implies that the two
computers are  having  a  dialogue,  rather  than  a  monologue.  These  other
protocols rely on single byte (8 bit) words for their communication codes, and
that could spell trouble, since the likelihood of any one  8  bit  code  being
transposed  into  another  is  greater  than for multiple byte codes. For this
reason, "C1" uses 3 byte (24 bit) codes which are sufficiently different  that
the  likelihood of a transposition is extremely low. Not only that, but as you
will soon learn, the method of receiving 3 byte codes is designed such that if
there  is  sufficient  line noise to make the neccessary transpositions, there
would most likely be extra characters sent; "C1" can avoid this situation.
 
Five distinct codes are used in the protocol; "GOO", "BAD", "ACK", "S/B",  and
"SYN". Each has it's own meaning, just like any English word, and all are used
in a  specific  sequence  such  that  synchronization  difficulties  would  be
automatically identified and corrected.
 
Checksums
---------
When  a  block  of  data  is  sent, we must have a way of determining if it is
correctly received or not. This is accomplished by using what is  known  as  a
checksum. Quite simply, a checksum is a number which is mathematically derived
from all the bytes within the block. The receiving computer  recalculates  the
sum  and  compares  it  with  the  sum  it  received  along  with  the  block.
Theoretically, any fault in the  transmitted  data  will  result  in  the  two
checksums  not  matching;  but  that's theory. In reality, the accuracy of the
checksum is based on the type of mathematical operation used to calculate  it,
and what kind of noise it encounters.
 
The simplest way to create a checksum is to add up all the ASCII values of the
bytes contained in the block. This is fine for many types of errors,  but  not
the type which inverts a particular bit. Should two identical inversions occur
on two opposite bits, the sum will remain the  same.  For  example,  take  the
following two bytes:
 
     11010011 = 211
Plus 01101101 = 109
     --------   ---
                320
 
Now  assume  that  the forth bit from the right of both of these bytes becomes
inverted by line noise:
 
     11011011 = 219
Plus 01100101 = 101
     --------   ---
                320
 
As you can see, the sum remains 320, even though line noise has  made  obvious
changes to the bytes. A better system is one called "Cyclic Redundancy", which
works on a somewhat different principle. The checksum is 16 bits long, and  is
created  in the following fashion; each byte from the block is Exclusive OR'ed
with the low order part of the checksum. The checksum is then ROTATED one  bit
to  the left, and the procedure repeated with the next byte.  Even this highly
superior method can be tripped  up,  so  I  have  combined  BOTH  an  additive
checksum and Cyclic Redundancy checksum to create one very hard to beat 32 bit
"super" checksum.
 
Listening For Code Words
------------------------
Although 3 byte code words are more reliable than 1 byte code  words,  nothing
is  perfect.  It  was  once said that if you let an infinite number of monkeys
bash away at typewriters for an infinite amount of time,  one  of  them  would
eventually  type  "To  be  or  not to be, that is the question". Although this
stretches statistical probability to it's limit, this kind of thing can easily
happen  on  a  smaller  scale;  the  letters  "GOO" could quite conceivably be
produced by purely random line noise.
 
To try and eliminate ALL possible errors isn't feasible,  but  "C1"  makes  an
attempt  at  trying  to eliminate as many as possible. One reasonably probable
fact is that any noise capable of randomly producing  "GOO",  would  not  stop
there;  more  likely,  it would produce a string of characters, something like
"HGOOEK". Were we to allow the protocol to listen exclusively for three letter
combinations, it would most assuredly pick out the "GOO" in that string.
 
My specifications for "C1" call for a code recognition routine which will ONLY
make code word comparisons on the LAST 3 RECEIVED bytes. This is  accomplished
in  my  coding  by  going back and testing for further characters after I have
identified a three byte  code  word.  Should  another  byte  be  present,  the
identified code word is thrown away, and the search will continue.
 
Statement and Listen Loops
--------------------------
One immediate drawback to the system described above is that a REAL code word,
masked within some random noise, would be rejected by the receiving  computer.
This  would  also be true of a code word simply damaged by noise (like "GOE").
For a protocol to be impervious to this sort of corruption, it must be capable
of  restating  code  words  over  and  over  until  the receiving computer can
understand, yet it must also have a  way  of  knowing  whether  the  receiving
computer got the code word or not. This was a fact that eluded me when I wrote
the original protocol. When we  talk  to  other  people,  the  cornerstone  of
understanding  is  recognition.  If  we  ask  "What do you think?", yet get no
reply, we ask again. Only when we receive a reply from the person to  whom  we
are  talking  do we continue on with our next statement. It would be pointless
wasting our breath on someone who isn't listening.
 
Within "C1", communication between computers  is  handled  through  a  similar
system which I call the "Statement and Listen Loop". It's quite simple really;
when one computer has to "say" something to the other, it does so, then  waits
for  a  predetermined  time  for a known response. Should it fail to receive a
response within that period of time, the code word  is  said  again,  and  the
computer  listens for the reply. This continues until the required response is
heard. The system is further enhanced by the  fact  that  both  computers  are
ALWAYS engaged in a "Statement and Listen Loop".
 
Synchronization Lock
--------------------
That  rather  ominous sounding title is actually rather simple; it refers to a
condition whereby the "Statement and Listen Loops"  of  each  computer  become
locked  together.  This  is analogous to two people speaking at the same time,
over and over, such that no effective communication takes place. In  order  to
guarantee  that the two computers never get into this state, the wait times of
the loops are altered slightly.
 
Assume that the fixed wait loop time was 0.5 seconds; this is called a "Short"
wait.  We  also  have  a  "Long" wait, which would be slightly longer, say 0.6
seconds (actually, the delay within a  "Statement  and  Listen  Loop"  is  not
particually critical, but should be somewhere in the neighbourhood of one half
second). Each time the computer goes through an SLL, a counter would determine
which  type  of wait to use; Long or Short. The sequence is broken into three;
the transmitting computer will use  a  Long-Long-Short,  while  the  receiving
computer will use a Short-Short-Long.
 
Block Structure
---------------
Each block of data contains somewhat more than just a collection of characters
taken from disk, it also contains a "header". The header is 7 bytes long,  and
contains the following information:
 
Byte 1: Low part of ADDITIVE checksum
Byte 2: High part of ADDITIVE checksum
Byte 3: Low part of CLC checksum
Byte 4: High part of CLC checksum
Byte 5: Size of NEXT block
Byte 6: Low part of Block Number
Byte 7: High part of Block Number
 
As  you  remember  from  the  section on "checksums", there are two distinctly
different, 16 bit (2 byte) checksums. One is an additive checksum, composed of
the  mathematical  sum  of the ASCII values of all the DATA bytes (and bytes 5
through 7 of the header). The other checksum is calculated using Cyclic  (CLC)
Redundancy (on the same bytes). These 32 checksum bits are placed in the first
4 bytes of the header.
 
The 5th byte is the length of the NEXT block. This may seem odd to  some,  but
consider  the  difficulties  in  sending the size of the current block in that
self same block. You need to know the block size to  calculate  the  checksum,
but  you  can't  know  for sure that the block size is correct unless you have
verified the checksum. We call this a Catch-22. By sending  the  size  of  any
given  block  in  the  PREVIOUS block, the size is known for a fact BEFORE the
checksum is calculated.
 
In the 6th and 7th byte are the block number. This was added quite early on in
the development of "C1" under the assumption that it would be necessary (as it
is in XMODEM). As it turned out, "C1" uses a method of handshaking which makes
this unnecessary. None the less, my specifications call for it's inclusion, as
certain uses of the block number could be made. Also, the high order  part  of
the block number (byte 7 of the header) is used to flag the last block.
 
Varying Block Size
------------------
The  reason that block size was included in the header was originally to allow
the last block only to vary in size (one can never guarantee that  the  amount
of  data  to  be sent will divide nicely into a preset block size). It quickly
dawned on me that "C1" was set up in such a way that ANY block size  could  be
used  for  ANY  block  in  the  transmission.  Varying  block  size  has  it's
advantages; under reasonably clean line conditions, large blocks transmit  the
most data with the least handshaking (which is mildly time consuming). Smaller
blocks are superior under bad noise conditions, since  smaller  blocks  run  a
higher  chance  of  making it through the noise unscathed; and should it still
fail to make it, less time is required to repeat a smaller block.
 
My current implementation of "C1" allows the user to pick a fixed  block  size
between 40 and 255 bytes, but in other implementations, there is no reason why
block size couldn't be varied DURING transmission to adapt  to  CHANGING  line
conditions.
 
One  final  thing  concerning block structure is how would one presume to know
the size of the FIRST BLOCK if that is revealed only in the  block  that  came
before it (quite a paradox). "C1" requires that the first block contain ONLY a
header, which would make that block 7 bytes long. This header would do  little
more  than  supply  the  receiving computer with the size of first REAL block.
Accuracy of this first "dummy" block is guaranteed since it  must  still  pass
the checksum tests. You must make the block number for this dummy block "0".
 
Communication Syntax
--------------------
Now  that  you  understand block structure, handshaking methods, and code word
vocabulary, it comes time to find  out  how  this  all  comes  together.  Most
procotols  have  very  simple handshaking between blocks which is easy to trip
up, given sufficiently noisy conditions. Usually,  the  transmitting  computer
sends the block, then waits for a response from the receiving computer; either
"good" or "bad". The transmitting computer then  proceeds  to  send  the  next
block (if "good") or resend the last block (if "bad"). This system falls apart
the moment the transmitting computer receives a false indication of "good"  or
"bad"  and  goes  on  to  transmit  the wrong block (and whether the receiving
computer likes it or not, it has to tackle with another block). Should  things
get  out  of  sync, and the transmitting computer sends the next block when it
should have sent the last one again, XMODEM attempts to  make  corrections  by
use of the block number encoded within each block.
 
"C1"  does  nothing  so  crude; it's very communication syntax guarantees that
neither computer will get out of phase with the other. Whereas XMODEM  uses  a
single  statement  monologue  between  each  block,  "C1" uses a multiple part
dialogue. This makes  "C1"  about  3%  slower  than  XMODEM,  but  this  small
trade-off  in  speed for accuracy will be well worth it the first time you run
into trouble with XMODEM.
 
XMODEM commincations would look something like this:
     
Xmit: Transmits Block
Rec : "Good"
Xmit: Transmits Next Block
Rec : "Bad"
Xmit: Transmits Same Block Again
 
In "C1", the transmission would look something like this:
 
Xmit: Transmits Block
Rec : "Good"
Xmit: Good block acknowledged
Rec : Send next block for me
Xmit: Transmits Next Block
Rec : "Bad"
Xmit: Bad block acknowledged
Rec : Send that block again
Xmit: Transmits Same Block Again
 
In this type of transmission dialogue, neither computer can get out  of  sync,
since should it receive the opposite response than it expects, it goes back to
give the correct code word for the response it  DID  RECEIVE,  thus  regaining
proper synchronization. Couple this with the "Statement and Listen Loops", and
you can readily see than communication would be hard to break down.
 
Syntax Description
------------------
The following diagram  should  give  you  an  understanding  of  the  flow  of
information between blocks:
 
For a Good Block:
 
Xmit: [Block]   "ACK"   [Next Block]
Rec :       "GOO"   "S/B"
 
For a Bad Block:
 
Xmit: [Block]   "ACK"   [Same Block]
Rec :       "BAD"   "S/B"
 
Actually,  the  two  are identical; the only difference is the substitution of
either "GOO" or "BAD" as the response to the received block. Immediately after
receiving  the  block,  the  receiving  computer  recalculats  the checksum to
determine validity of the data. In the  meantime,  the  transmitting  computer
starts  to  wait for a "GOO" or "BAD" signal. Since it can "say" nothing until
it receives one of these codes, it merely waits. That may  sound  suspiciously
like  a  good  place  to  "hang  up"  the  protocol,  but the receiving end is
eventually going to finish receiving the block, either because  it  timed  out
waiting,  or  it  finished  collecting  the  correct  number of bytes from the
transmitting computer.
 
At that time, the receiving computer sends the appropriate code word ("GOO" or
"BAD")  and  begins  to  wait  for  an  acknowledgement ("ACK"). If it doesn't
receive the "ACK" in about one half second, it sends the "GOO" or  "BAD"  code
word  once  again.  Meanwhile,  the  transmitting  computer has been patiently
awaiting the reception of the "GOO" or "BAD" code. Once  it  receives  it,  it
transmits  an  "ACK" and starts to wait for an "send block" signal ("S/B"). If
it doesn't get the "S/B" within about one half second, it sends "ACK" again.
 
Back at the receiving computer, which is waiting for  this  "ACK"  signal,  it
receives  it  and  sends  the  "S/B"  signal and begins to wait for the block.
Should it receive an "ACK" while waiting for the block, or receives nothing at
all  for  approximately  .5 seconds, it assumes that the transmitting computer
hasn't  heard  the  "S/B"  and  transmits  it  again.  In  the  meantime,  the
transmitting  computer  is  waiting  for the "S/B", and upon reception, starts
sending the block. The process has now started all over again.
 
A quick analysis of this system will reveal that it's damned  near  impossible
to  get  any  type  of  noise  which  could possibly mimick the code sequences
required. Also, no noise could stop  the  eventual  completion  of  the  above
sequence,  since  each  computer is aways "sending and waiting". If two people
keep repeating their sentences over and over, and continue to  listen  to  the
other  person,  even  a noisy room couldn't stop them from hearing one another
EVENTUALLY. Of course, some line noise is just so horrendous, that  even  this
method  of communication could fail. Then again, this type of noise would make
it damned near impossible for the user to be online in the first place, so  it
can  be  considered  an  unlikely  event.  But, should one of the computers go
offline for any reason, we wouldn't want the other computer  to  keep  looping
and looping until it died of old age.
 
Although  I  haven't  built  in  such  protection  into the terminal program I
distribute in the public domain, my  BBS  program  does  have  abortion  code.
Should  the  protocol  on the BBS have to go through the "Statement and Listen
Loop" more than 24 times in row  (which  is  hightly  unlikely  if  the  other
computer  is  still online), it will abort the transfer. Similar code could be
used in your implementation.
 
The End-Off Situation
---------------------
When the final block is transmitted, the high order part of the  block  number
should be made HEX "FF" (255 decimal). This will inform the receiving computer
that this is the last block of data, and to expect no more. The  question  now
arises;  how  can both computers be 100% sure that the other is fully aware of
the file completion? A fair question, but not one with a simple answer.
 
When the transmitting computer receives the "GOO" for the last block,  it  can
be  fairly  certain  that the receiving computer has received the final block,
but it must inform the receiving computer that it knows this. It  does  so  by
sending  an  "ACK", but cannot be sure the receiving computer has received the
"ACK" unless it gets the "S/B" signal back.  Now,  the  transmitting  computer
must   acknowledge   the   reception  of  the  "S/B",  but  under  the  normal
communications syntax, it would now have send  a  block.  This  is  where  the
"End-Off"  syntax comes into play; after receiving the "S/B", the transmitting
computer sends back a "SYN" signal. In response  to  that  receiving  computer
sends  it's  own  "S/B"  signal,  then  waits  for  the  final  "S/B" from the
transmitting computer. Since it will not be responding to this code, it simply
goes into a wait cycle for approximately 5 seconds.
 
If  it  does  get  the  "S/B"  within that 5 seconds, it ends immediately, but
otherwise doesn't really care if it receives the code or  not  since  at  this
stage,  there is a 100% assurance of both computers knowing things are Ok. The
transmitting computer need only send three copies of the "S/B"  code  at  this
point, since, as stated above, there is full assurance that both computers are
finished. NOTE that the code words chosen for the End-Off  situation  are  not
necessarily related to their appearant function.
 
Transfering File Type
---------------------
When  transfering  files from one computer to another it is often necessary to
also transfer the file type, but this must be known BEFORE the file is opened,
and,  therefore,  before  the protocol begins. "C1" does not impose any strict
rules on what sort of information you transfer about the files,  if  any,  but
when writing a terminal program to communicate with one of my bulletin boards,
the following should be done:
 
Using a full implementation of the "C1"  procotol  (first  dummy  block,  data
block,  and  End-Off),  transmit  a  single  byte of data corresponding to the
following file types:
 
1 = Program File
2 = SEQ File
3 = WordPro File
 
Transmitting this single piece of data would require that TWO blocks be  sent;
the  initial  dummy block to set up the size of the first data block (of which
there will be only one, size 8), and the data block itself,  consisting  of  7
header  bytes and the single file type byte. For other applications, one could
conceivable transfer much more information, including file  name,  file  type,
computer  type,  etc.  It  could  even be possible to transfer multiple files,
specifying the number and name  of  each  file  in  this  first  transmission.
Alternately,  no one said you HAVE to use this first separate transmission; if
no information other the file needs to be transmitted, you just send the  file
and nothing more.


--  
UUCP:     watmath!xenitec!zswamp!root | 602-66 Mooregate Crescent
Internet: root@zswamp.fidonet.org     | Kitchener, Ontario
FidoNet:  SYSOP, 1:221/171            | N2M 5E6 CANADA
Data:     (519) 742-8939              | (519) 741-9553
MC Hammer, n. Device used to ensure firm seating of MicroChannel boards
Try our new Bud 'C' compiler... it specializes in 'case' statements!

root@zswamp.fidonet.org (Geoffrey Welsh) (11/16/90)

Matthew E Cross (profesor@wpi.WPI.EDU ) wrote:

 >I do have description of the protocol.

 ... and, if you implement according to that standard, it won't work.

                     Notes on Implementing the C1 Protocol

                               by Geoffrey Welsh

                             with Matthew Desmond

                              November 13th, 1988

   When Steve Punter realized that his original PET Transfer Protocol (also 
known as "old Punter protocol") accepted too many compromises (its checksum 
mode was unreliable; its 7-bit approach meant that it ran at half speed, 
etc.), he decided to design a new protocol that circumvented the problems 
of performing file transfers on a Commodore 64 and some of the problems he had 
seen with his old protocol. At the time, many superior protocols existed that 
could have been adapted to use on the C64, but Steve was not aware of them or 
their strengths. He could only compare his new design to XMODEM.

   Steve's document describing the C1 protocol does not accurately reflect 
much of the real information needed to implement C1 and, in some cases, is 
outright misleading. Hence this file to help you understand what's going on.

"CRC" Calculation
-----------------

   Steve's "CRC" is calculated by exclusive-ORing each successive byte in the 
block (with the exception of the bytes in the header where the checksums go, 
for which one should substitute zero bytes) and then ROTATING the CRC left 
through 16 bits. Note that the bit that "falls out" of bit 15 must be placed 
back into bit 0 immediately.

Synchronization Lock
--------------------

   Steve's "Long-Long-Short" and "Short-Short-Long" solution to 
synchronization lock is downright silly. All will be well (and completely 
compatible) if the delay in the statement-and-listen loop is always set to 
long when transmitting and short when receiving.

Varying Block Size
------------------

   I recommend that block size variation be implemented such that maximum 
block size - 255 bytes total - is used on clean lines, but size is 
automatically reduced on noisy lines. In no circumstance should the block size 
exceed 255 bytes total (7 header and 248 data bytes); this is implied by the 
one-byte next block size indicator. Although block size may fall as low as 8 
bytes, it would be rediculous to spend time on-line getting only one data byte 
accross per block; a more reasonable lower limit might be 40 (as suggested by 
Steve), or 31 (start with block size = 255; shift right every time two 
consecutive blocks are bad and shift left with 1 in the carry every time three 
consecutive blocks are good).

Transfering File Type
---------------------

   This protocol was developed for use with Commodore computers, whose DOS 
treats executable, sequential, and random-access file types differently. As a 
result, existing implementations were designed to transmit the file type 
before the file itself. Non-Commodore machines may or may not preserve the 
file type transmitted by the uploader to be sent out when sending the file to 
a downloader using C1, but it is essential that SOME file type be identified 
when sending ANY file via the C1 protocol. Any file other than Commodore BASIC 
or 6502 executable binaries (with one exception: see note on "WordPro Files", 
next paragraph) should be sent under the file type "Sequential", so the type 
"Sequential" should be assumed if the exact type is not known. If a file is 
received under the wrong file type, it is easy enough to change Commodore DOS' 
file type flag. Nonetheless, it is obviously preferrable to preserve the file 
type as uploaded. 

   By "Wordpro File", Steve means a file compatible with the text format he 
used in his commercial package WordPro for Professional Software, Inc.; this 
file type is seldom used and really means that the file is written as a 
"Program File" under Commodore DOS even though it contains WordPro format 
text. This file type may be translated to "Program".

   Steve's paragraph that begins "Transmitting this single piece of data would 
require that TWO blocks be sent; the initial dummy block to inform the 
receiver that the first (and only) data block is of size 8, and the data block 
itself, consisting of 7 header bytes and the file type byte." is blatantly 
inaccurate. Existing C1 implementations send the file type in a single, 8-byte 
(7 bytes header plus one data byte) block before the transfer of the file 
itself. First you must transmit (or expect to receive) the 8-byte file-type 
block and then proceed through the end-off sequence, then start the file 
transfer with a 7-byte block zero. 

  No matter what, a close examination of Steve's implementation is absolutely 
necessary to obtain the finer details of the protocol. An ASCII dump of his 
Commodore BASIC and 6502 implementation is available on my BBS under the name 
C1IMP.TXT.

   Also, here is all that I could find about Alan Peters' "Multi-Punter" hack; 
this is the third of three C1 batch standards, and not necessarily the most 
common. Apologies for the all-caps, it's a side-effect of a quick PETSCII to 
ASCII conversion.

FROM   : ALAN PETERS
TO     : DEREK JAGDEO
POSTED : 1251H ON 21-JAN-87 * TECH *
SUBJECT: MULTI=TRANSFER

CAT    : GENERAL

YOU CAN GO AHEAD AND DO IT,  BUT  I'LL
GIVE YOU SOME SPECIFICS:
  UPLOADING. FOR EACH TIME  THROUGH  A
MULTI-SEND,  THE  FIRST   BLOCK   MUST
CONTAIN 27 BYTES OF DATA, RATHER  THEN
THE FILE TYPE. THIS IS STORED AS:
BYTES 0-15 : FILE  NAME,  PADDED  WITH
SHIFTED SPACES TO LENGTH 16.
BYTE 16 :I COMMA (,)
BYTE 17 : FILE TYPE (LOWER  CASE  P,S,
OR U)
BYTES 18-20 : FILES LEFT TO  SEND,  IE
045 FOR 45 FILES LEFT.
BYTES 21-23 : BLOCKS  LEFT,  TOTAL  OF
ALL FILES LEFT
BYTES 24-26 : CURRENT FILE SIZE.
    YOU   THEN   MODIFY   THE   PUNTER
PROTOCOL FOR MULTI LIKE THIS:
    OPEN CURRENT FILE FOR READ.
    CALL READY RS232 REC'V ROUTINE.
    CALL ROUTINE TO SWITCH PROTOCOL TO
MULTI
    CALL TRANSMIT 1 ROUTINE
    CALL ROUTINE  TO  SWITCH  PROTOCOL
BACK TO NORMAL.
    CHECK FOR ABORT ($200)
    IF  NOT,  RESUME  LIKE  A   NORMAL
SINGLE  FILE  TRANSFER  :  CALL  RS232
REC'V ROUTINE AGAIN, THEN TRANSMIT  2.
CLOSE  THE  FILE,  CLEAR  THE   BUFFER
POINTERS @ $29B/$29C, WAIT  A  LITTLE,
THEN DO THE NEXT FILE.
    IF YOU ARE DONE  ALL  FILES,  THEN
YOU MUST  SEND  ON  END-OF-MULTI  NULL
STRING, WHICH IS  27  "-"  CHARACTERS.
PUT THIS IN THE 27 BYTE  BUFFER,  THEN
CALL THE  ABOVE  ROUTINES  FOR  NORMAL
FILE  MULTI  UPLOAD.   DO   NOT   CALL
TRANSMIT 2. DON'T OPEN A  FILE,  SINCE
THERE  IS  NONE.  THIS  LAST  PART  IS
IMPORTANT, SO THAT THE  RECEIVER  WILL
KNOW WHEN TO STOP RECEIVING FILES.
   THE CHANGES TO THE PUNTER  PROTOCOL
ARE TRIVIAL. THE ROUTINE THAT CONVERTS
IT TO MULTI LOOKS LIKE THIS:
   TOMULTI LDA #27
           STA L1+1:STA L2+1
           LDA #128:STA MMODE
   BRN     LDA HSHAKE:PHA
           LDA HSHAKE+3:PHA
           LDA   HSHAKE+2:STA   HSHAKE
        LDA HSHAKE+5:STA HSHAKE+3
           PLA:STA HSHAKE+5
           PLA:STA HSHAKE+2\
           RTS
FROMMULT   LDA #1
           STA L1+1:STA L2+1
           LDA #0:STA MMODE
           BEQ BRN
THE FIRST  LABEL,  L1  OCCURS  IN  THE
REC'V ROUTINE @  $C4CE.  YOU  WILL  BE
CHANGING THE ADC #1 INSTRUCTION TO ADC
#27 TO MAKE THE FIRST BLOCK LENGTH  27
INSTEAD OF 1. IT MUST BE CHANGED  BACK
TO 1 BEFORE YOU CALL THE TRANSMIT 2 OR
RECEIVE 2 ROUTINES.
   THE MMODE VALUE  IS  TO  ALLOW  THE
BUFFERING ROUTINE IN THE  PROTOCOL  TO
MOVE THE  27  BYTES  FROM  THE  PUNTER
BUFFERS TO THE 27 BYTE BUFFER.  INSERT
THE FOLLOWING IN THE CODE  JUST  AFTER
THE FILE TYPE IS STORED IN RECEIVE1:
    BIT MMODE:BPL RESUME
    LDY #0
J1  LDA PUNTERB+7,Y
    STA BUFFER,Y
    INY:CPY #27:BNE J1
RESUME.....
    YOU  MUST  CHANGE   THE   TRANSMIT
ROUTINE AT $C517 AS WELL. THE  ADC  #1
WILL CHANGE  TO  ADC  #27,  THEN  BACK
AFTER THE FIRST BLOCK.
    IN  THIS   TRANSMIT   1   ROUTINE,
LOCATIONS  $64/65  INDIRECTLY  ADDRESS
THE PUNTER XMIT BUFFER. SO, THE  SMALL
MULTI INSERTION CODE LOOKS LIKE:
    BIT MMODE:BPL RESUME
    LDY #7
J1  LDA BUFFER-7,Y
    STA ($64),Y
    INY:CPY #34:BNE J1
RESUME ...
   THIS GOES JUST AFTER THE CODE WHERE
THE FILE TYPE IS PUT INTO THE BUFFER.
   THE HANDSHAKE REVERSAL  DOES  THIS:
GOO TO OOG AND BAD  TO  DAB.  I  FOUND
THAT IT IS NOT NECESSARY TO CHANGE THE
OTHER 3 SIGNALS.
   FOR THE RECEIVER, HE  MUST  DO  THE
SAME  AS WITH TRANSMIT,  BUT  USE  THE
BUFFER DATA TO WRITE THE FILE TO DISK.
HOPE THIS HELPS.

ALAN PETERS * TECH *


--  
UUCP:     watmath!xenitec!zswamp!root | 602-66 Mooregate Crescent
Internet: root@zswamp.fidonet.org     | Kitchener, Ontario
FidoNet:  SYSOP, 1:221/171            | N2M 5E6 CANADA
Data:     (519) 742-8939              | (519) 741-9553
MC Hammer, n. Device used to ensure firm seating of MicroChannel boards
Try our new Bud 'C' compiler... it specializes in 'case' statements!

root@zswamp.fidonet.org (Geoffrey Welsh) (11/16/90)

Matthew E Cross (profesor@wpi.WPI.EDU ) wrote:

 >Now that I know a decent amount of C/Unix programming, I'm
 >trying to implement the protocol on UNIX.

   Here is the 'C' source, written for Steve Punter's MS-DOS BBS software by 
Ben Pedersen:

               Docs for the 'C' source code the the C1 protocol

                                By Steve Punter

The following source code for my "C1" protocol was written by BEN PEDERSEN, 
and donated to the public domain. Specifically, it is written to compile under 
MicroSoft's  "Quick-C",  and  be  called from a higher level language ("Quick-
BASIC"). However, it should compile with most PC compilers, and you don't HAVE 
to call it from a higher language. 
 
The two important routines are "C1Rec" and "C1Send". Both these routines are 
passed THREE parameters: 
 
  File Name POINTER
  File Type Flag
  Base Location of Screen
 
The file name pointer is merely a pointer to a NULL TERMINATED string which 
contains the file name to be written to, or read from, the disk. The File Type 
Flag is an INTEGER value of 0 or 1, which determines whether the routines go 
through the initial transfer of a file type byte. This is NECESSARY when 
uploading or downloading from a Punter type BBS, but is not required by the 
protocol per se. The Base Location of Screen is a LONG value telling the 
protocol at what SEGMENT the screen is located (usually $B000 for monochrome, 
or $B800 for CGA). 

As the transfer proceeds, the number of successful bytes, and number of bad 
blocks, are put on the top line of your screen. This is to accommodate the 
STATUS LINE of the BBS, but will most likely not suit your applications. It 
should be changed appropriately. 
 
RS-232 is handled via three routines: GETSER(), PUTSER(), and CHECK(). All of 
the above are EXTERNAL routines which sould be supplied at compile time. 
GETSER() should return an INT value of the byte received from the RS-232 input 
port, or input buffer. PUTSER() should accept an INT value to put out on the 
RS-232 port. CHECK() should return an INT value of how many characters are 
waiting in the RS-232 input buffer.

extern void setser(unsigned,unsigned,unsigned);
extern int getser();
extern int check();

#include <dos.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <io.h>
#include <bios.h>

static unsigned buffer;
static unsigned ds;
static unsigned tab2;
static int tfh;
static int badblocks;
static long xfertotal;
static int nocount;
static long scnloc;

unsigned _dos_findfirst(char *,unsigned,struct find_t *);
unsigned _dos_findnext(struct find_t *);
void dosc(int *,int *);

typedef struct C1STUFF {
  int fp;               /* pointer to open file for send & receive */
  char *BuffPtr;          /* pointer to data buffer */
  char BuffOne[256];      /* data buffer no. one */
  char BuffTwo[256];      /* data buffer no. two */
  char TempBuff[500];     /* general purpose buffer */
  char *ReplyStr[5];      /* pointers to reply code strings */
  char *CharPtr;          /* general purpose char pointer */
  int BlkSize;            /* size of block being sent or received */
  int NextBsize;          /* size of next block to be sent or received */
  int FullBsize;          /* selected size, 40-255 bytes, of a full block */
  int BlkNum;             /* block number count */
  int FileFlag;           /* file type being sent or received */
  int ReplyOut;           /* array index flags currently-transmitted reply code 
*/
  int ReplyWant;          /* array index flags currently-wanted reply code */
  int ErrCodes;           /* count of erroneous reply codes received */
  int ErrBlocks;          /* count of erroneous blocks received */
  int Chksum;             /* additive checksum for current block */
  int CLC;                /* cyclic checksum for current block */
  int EndFlag;            /* local last block flag */
  int EndOff;             /* signal end of transfer */
  int EndXfer;            /* signal to terminate transfer */
  int TransFlag;          /* flag text file for ASCII-PETSCII-ASCII translate */
 
  int Wait;               /* index to Wait Period array for reply codes */
  long Period[3];         /* array of reply code Wait Period values */
};

struct C1STUFF C1Stuff, *StuffPtr;  /* C1 variables structure & pointer */

void prtstat (gd,bb)

long gd;
int bb;
{
char far *screen;
char buf[20];
int i;
int z;
FP_SEG(screen)=scnloc;
FP_OFF(screen)=160;

sprintf(buf,"%lu",gd);
for (i=0,z=94;*(buf+i)!=0;i++,z=z+2)
  *(screen+z)=*(buf+i);

sprintf(buf,"%u",bb);
for (i=0,z=120;*(buf+i)!=0;i++,z=z+2)
  *(screen+z)=*(buf+i);
}

/***********************************
*       c1.c                       *
*                                  *
*   C language implementation      *
*       of Steve Punter's          *
*       C1 file transfer protocol  *
*       written for MS-DOS 2.xx    *
*                                  *
*       Ben Pedersen  Jun 88       *
************************************

/* constants for external assembler functions */
#define END 			0x4f	/* exit program */
#define PGUP			0x49	/* upload a file */
#define PGDN			0x51	/* download a file */
#define F1                      0x3b    /* DOS shell */
#define BELL			0x07	/* tweak speaker */

/* response code string array index */
#define CODE_ACK		0x00
#define CODE_GOO		0x01
#define CODE_BAD		0x02
#define CODE_SYN		0x03
#define CODE_SBK		0x04

/* used to clear a reply code flag */
#define NONE			0x05

/* short and long periods to wait for a response */
#define SHORT                   0x400L
#define LONG                    0x460L

#define ERROR			0xffff	/* flag error condition on function return */
#define NOERR			0x00	/* flag no error on function return */
#define MAXCODERR               0x18    /* maximum number of reply code errors 
*/
#define MAXBLKERR		0x05	/* maximum number of data block errors */
#define EOT_FLAG		0xff	/* end of transfer flag in BLKNUM_H */
#define HEADER			0x07	/* no. of bytes in block header */
#define CHKSUM_L		0x00	/* byte no. of additive checksum low byte */
#define CHKSUM_H		0x01	/* byte no. of additive checksum high byte */
#define CLC_L			0x02	/* byte no. of cyclic checksum low byte */
#define CLC_H			0x03	/* byte no. of cyclic checksum high byte */
#define NEXT_BSIZE		0x04	/* byte no. of next blocksize byte */
#define BLKNUM_L		0x05	/* byte no. of block number low byte */
#define BLKNUM_H		0x06	/* byte no. of block number high byte */
#define FILE_TYPE               0x07    /* byte no. of file type byte */
#define FALSE                   0       /* false response */
#define TRUE                    1       /* true response */


/***************************************************************************/

/* C1 protocol functions for receiving data */
int HandleRec();			/* handle reply codes for receiving data blocks */
int BlockRec(); 			/* get a block of data from serial buffer */
int ChksmRec(); 			/* performs checksum tests on received block */
int EndReceive();			/* handle reply codes for ending data receive */

/* C1 protocol functions for sending data */
int FtypeSend(int);                     /* handle sending file type block */
int DataSend();                         /* controls block sending sequence */
int DataBuild();                        /* builds both the next and the current 
block */
void ChksmSend();			/* generates checksums for current block */
int EndSend();				/* handle reply codes for data send */
int SLBlocks(); 			/* statement & listen loop for block send */

/* C1 protocol functions common to sending and receiving data */
int SLCodes();				/* statement & listen loop for reply codes */
int GetBytes();                         /* loop for getting reply chars */
int CodeOut();				/* send out a reply code */

/**************************************************************************
*
*	functions to receive blocks
*
**************************************************************************/

int fortran C1Rec(pathname,fflg,loc)

char *pathname;
int fflg;
long loc;
{
int i;
int filetype;
scnloc=loc;

C1Stuff.ReplyStr[0] = "ACK";   /* signal a good or bad block */
C1Stuff.ReplyStr[1] = "GOO";   /* signal a good block */
C1Stuff.ReplyStr[2] = "BAD";   /* signal a bad block */
C1Stuff.ReplyStr[3] = "SYN";   /* signal end of transfer sequence */
C1Stuff.ReplyStr[4] = "S/B";   /* request block or signal transfer done */

/* sequence of reply code wait periods */
C1Stuff.Period[0] = SHORT;
C1Stuff.Period[1] = SHORT;
C1Stuff.Period[2] = LONG;

StuffPtr = &C1Stuff;    /* set pointer to struct of C1 variables */
C1Stuff.BuffPtr = C1Stuff.BuffOne;    /* set pointer to a data buffer */
C1Stuff.ErrCodes = 0;  /* count of reply code errors */
C1Stuff.ErrBlocks = 0; /* count of data block errors */
C1Stuff.Wait = 0;       /* index variable of reply code wait periods */
C1Stuff.TransFlag = FALSE;  /* PETSCII-ASCII translation flag */
xfertotal = 0;
badblocks = 0;

_dos_creat(pathname,_A_NORMAL,&tfh);

C1Stuff.FileFlag=1;

if(fflg != 0)
  {
  nocount = 1;
  C1Stuff.BlkSize = HEADER+1; /* size of file type block */
  if(HandleRec() == ERROR)     /* receive file type block */
    {
    _dos_close(tfh);
    return(1);
    }
  StuffPtr->CharPtr = StuffPtr->BuffPtr+7;
  filetype = *StuffPtr->CharPtr & 255;

  if(EndReceive() == ERROR)     /* end off file type receive */
    {
    _dos_close(tfh);
    return(2);
    }
  }
nocount = 0;
C1Stuff.BlkSize = HEADER;   /* size of dummy data block */
if(HandleRec() == ERROR)    /* receive data blocks */
  {
  _dos_close(tfh);
  return(1);
  }
if(EndReceive() == ERROR) /* end off data receive */
  {
  _dos_close(tfh);
  return(3);
  }
_dos_close(tfh);
if (fflg==0)
  return(0);
else
  return (-filetype);
}

int HandleRec()   /* handle reply codes for receiving data blocks */

{
int flag;
int bw;

StuffPtr->BlkNum = 0;       /* local block number count */
StuffPtr->ReplyOut = CODE_GOO; /* reply code to send to sender */
StuffPtr->ReplyWant = CODE_ACK;    /* reply code wanted from sender */
StuffPtr->EndOff = 0;
StuffPtr->EndFlag = 0;
StuffPtr->EndXfer = 0;
if(SLCodes() == ERROR) return(ERROR); /* send & get reply */

do
  {
  do
    {
    StuffPtr->ReplyOut = CODE_SBK; /* code to request a block */
    CodeOut();            /* request a block */
    } while ((flag = BlockRec()) == -2);
  if(flag == NOERR)   /* receive a block */
    {
    if (StuffPtr->BlkNum > 0)
      {
      if (nocount==0) xfertotal += ((StuffPtr->BlkSize & 0xff) - HEADER);
      prtstat(xfertotal,badblocks);
      }
    ++StuffPtr->BlkNum; /* increment local block count */
    StuffPtr->ErrBlocks = NOERR;   /* zero block error count */
    StuffPtr->ReplyOut = CODE_GOO; /* reply code signals good block */
    if(StuffPtr->BlkNum > 1)    /* block number > 1 written to disk */
      {
      _dos_write(tfh,StuffPtr->BuffPtr+HEADER,
      (StuffPtr->BlkSize & 0xff)-HEADER,&bw);
      }
    /* last block received? - goto end off */
    if((*(StuffPtr->BuffPtr + BLKNUM_H) & 0xff) == EOT_FLAG)
      return(NOERR);
    /* get size of the next block from fifth byte of received block */
    StuffPtr->BlkSize = (unsigned)*(StuffPtr->BuffPtr+NEXT_BSIZE);
    }
  if(flag == ERROR)
    {
    /* transmitter still sending ACK? */
    if(GetBytes() != ERROR
     && strncmp(StuffPtr->CharPtr-3, StuffPtr->ReplyStr[CODE_ACK], 3)
     == NULL) continue;
    else   /* otherwise reply BAD */
      {
      badblocks++;
      prtstat(xfertotal,badblocks);
      StuffPtr->ReplyOut = CODE_BAD; /* signal a bad block received */
      ++StuffPtr->ErrBlocks; /* increment local block error count */
      }
    }
  if(SLCodes() == ERROR) return(ERROR); /* send & get reply */
  } while(StuffPtr->ErrBlocks < MAXBLKERR);
return(ERROR);
}

int BlockRec()      /* get a block of data from serial buffer */

{
long t1, t2;

StuffPtr->CharPtr = StuffPtr->BuffPtr;
/* cycle through wait periods */
if(++StuffPtr->Wait > 2) StuffPtr->Wait = 0;
t2 = 0L;
/* set to current time plus the wait period */
t1 = clock() + StuffPtr->Period[StuffPtr->Wait];
while(check() == NULL)
  {
  if(t2 > t1) return(ERROR);
  t2 = clock();
  }
do
  {
  t2 = 0L;
  t1 = clock() + 150L; /* max of .15 sec without receiving a byte */
  while(check() == NULL)
    {
    if(t2 > t1) return(-2);
    t2 = clock();
    }
  *StuffPtr->CharPtr = getser();
  ++StuffPtr->CharPtr;
  } while(StuffPtr->CharPtr < StuffPtr->BuffPtr+(StuffPtr->BlkSize & 0xff));
return(ChksmRec());  /* return result of checksum verification */
}

int ChksmRec()    /* performs checksum tests on received block */

{

/* do both additive and cyclical checksum calculation on block */
for(StuffPtr->Chksum = StuffPtr->CLC = 0,StuffPtr->CharPtr = 
StuffPtr->BuffPtr+4;
 StuffPtr->CharPtr < StuffPtr->BuffPtr + (StuffPtr->BlkSize & 0xff);
 StuffPtr->CharPtr++)
  {
  StuffPtr->Chksum += (*StuffPtr->CharPtr & 0xff);
  StuffPtr->CLC ^= (*StuffPtr->CharPtr & 0xff);
  if(StuffPtr->CLC & 0x8000) StuffPtr->CLC = (StuffPtr->CLC << 1) + 1;
  else StuffPtr->CLC <<= 1;
  }
/* test against checksums received */
if(*(StuffPtr->BuffPtr+CHKSUM_L) == (char)StuffPtr->Chksum & 0xff
 && *(StuffPtr->BuffPtr+CHKSUM_H) == (char)(StuffPtr->Chksum >> 8 & 0xff)
 && *(StuffPtr->BuffPtr+CLC_L) == (char)StuffPtr->CLC & 0xff
 && *(StuffPtr->BuffPtr+CLC_H) == (char)(StuffPtr->CLC >> 8 & 0xff))
  return(NOERR);  /* signal good block */
else return(ERROR);    /* signal bad block */
}

int EndReceive()   /* handle reply codes for ending data receive */

{

if(SLCodes() == ERROR) return(ERROR); /* send & get reply */
StuffPtr->ReplyOut = CODE_SBK;  /* send out 'S/B' */
StuffPtr->ReplyWant = CODE_SYN; /* want 'SYN' */
if(SLCodes() == ERROR) return(ERROR); /* send & get reply */
StuffPtr->ReplyOut = CODE_SYN;  /* send 'SYN' */
StuffPtr->ReplyWant = CODE_SBK; /* want 'S/B' */
if(SLCodes() == ERROR) return(ERROR); /* send & get reply */
StuffPtr->ReplyOut = CODE_SBK;  /* receiver terminates */
CodeOut();
return(NOERR);
}

/**************************************************************************
*
*	functions to send blocks
*
**************************************************************************/

int fortran C1Send(pathname,bsize,fflg,loc)

char *pathname;
int bsize;
int fflg;
long loc;
{
struct find_t find, *f_ptr;
int i;
int x;
scnloc=loc;

C1Stuff.ReplyStr[0] = "ACK";   /* signal a good or bad block */
C1Stuff.ReplyStr[1] = "GOO";   /* signal a good block */
C1Stuff.ReplyStr[2] = "BAD";   /* signal a bad block */
C1Stuff.ReplyStr[3] = "SYN";   /* signal end of transfer sequence */
C1Stuff.ReplyStr[4] = "S/B";   /* request block or signal transfer done */

/* reply code Wait Period sequence */
C1Stuff.Period[0] = LONG;
C1Stuff.Period[1] = LONG;
C1Stuff.Period[2] = SHORT;

StuffPtr = &C1Stuff;    /* set pointer to C1 variables struct */
f_ptr = &find;
C1Stuff.BuffPtr = C1Stuff.BuffOne;    /* set pointer to a data buffer */
C1Stuff.FullBsize = 0; /* zero size of a full block */
C1Stuff.Wait = 0;   /* zero reply code Wait Period index */
C1Stuff.TransFlag = FALSE;  /* set ASCII-PETSCII TransFlag flag */
xfertotal = 0;
badblocks = 0;

_dos_open(pathname,O_RDONLY,&tfh);

C1Stuff.FileFlag=fflg;
C1Stuff.FullBsize=bsize;

x=0;
do
  {
  GetBytes();
  if (x++>12 || c1cancel()==ERROR) return;
  } while (strncmp(StuffPtr->CharPtr-3,StuffPtr->ReplyStr[CODE_GOO],3) != NULL 
&& x++<80);
if(fflg != 0)
  {
  nocount = 1;
  if(FtypeSend(fflg) == ERROR)      /* send the file type block */
    {
    _dos_close(tfh);
    return(4);
    }
  }
nocount = 0;
if(DataSend() == ERROR)   /* send data blocks */
  {
  _dos_close(tfh);
  return(1);
  }
_dos_close(tfh);
return(0);
}

int FtypeSend(ftype)    /* handle sending file type block */

int ftype;
{

StuffPtr->ErrCodes = NOERR;    /* zero reply code error count */
StuffPtr->ErrBlocks = NOERR;   /* zero data block error count */
StuffPtr->EndOff = FALSE;      /* end off sequence flag */
StuffPtr->BlkSize = HEADER+1;  /* set size of this block */
StuffPtr->BlkNum = 0;          /* set number of this block */

/* build the file type block */
*(StuffPtr->BuffPtr+BLKNUM_L) = EOT_FLAG;  /* sixth byte = 255 */
*(StuffPtr->BuffPtr+BLKNUM_H) = EOT_FLAG;  /* seventh byte = 255 */
/* eighth byte contains file type: 1=SEQ(text),2=PROG(binary),3=WORDPRO */
*(StuffPtr->BuffPtr+FILE_TYPE) = ftype;
ChksmSend();  /* get checksum into first four bytes */

/* start of a file type transfer */
if(SLBlocks() == ERROR) return(ERROR); /* send file type block */
return(EndSend()); /* return result of file type xfer end off */
}

int DataSend()     /* controls block sending sequence */

{

StuffPtr->EndFlag = FALSE; /* local last block flag */
StuffPtr->EndOff = FALSE;  /* end off sequence flag */
StuffPtr->ErrCodes = NOERR;    /* zero reply code error count */
StuffPtr->ErrBlocks = NOERR;   /* zero data block error count */

StuffPtr->BlkSize = HEADER;    /* set up block zero */
*(StuffPtr->BuffPtr+NEXT_BSIZE) = HEADER;  /* size of block zero */
/* build block zero */
*(StuffPtr->BuffPtr+BLKNUM_L) = 0x00;
*(StuffPtr->BuffPtr+BLKNUM_H) = 0x00;
StuffPtr->BlkNum = -1;
ChksmSend();  /* get checksums into first four bytes */
if(SLBlocks() == ERROR) return(ERROR);    /* send block zero */

++StuffPtr->BlkNum; /* increment local block number */
/* set up block two so its size can be sent in block one */
if(DataBuild() == ERROR) return(ERROR);
StuffPtr->BlkSize = HEADER;    /* size of block one */
/* build block number one */
*(StuffPtr->BuffPtr+BLKNUM_L) = 0x01;
*(StuffPtr->BuffPtr+BLKNUM_H) = 0x00;
ChksmSend();  /* get checksums into first four bytes */
if(SLBlocks() == ERROR) return(ERROR);    /* send block one */

do
  {
  ++StuffPtr->BlkNum; /* increment local block number */
  /* current block obtained previously so its size is already known */
  StuffPtr->BlkSize = StuffPtr->NextBsize;
  /* build next block and do checksums for current block */
  if(DataBuild() == ERROR) return(ERROR);
  if(SLBlocks() == ERROR) return(ERROR);  /* send current block */
  /* if final block was sent do end off and return result */
  if(StuffPtr->EndOff == TRUE) return(EndSend());
  } while(TRUE);
}

int DataBuild()    /* builds both the next and the current block */

{
int BlkSize;   /* holds actual no. of bytes read from disk */

/* read a block from disk if last block not yet obtained */
/* NOTE: block read now will be the following one sent, not current one */
if(StuffPtr->EndFlag == FALSE)
  {
  _dos_read(tfh,StuffPtr->BuffPtr+HEADER,StuffPtr->FullBsize-HEADER,&BlkSize);
  if(eof(tfh) != NULL)     /* check for end-of-file */
    {
    /* signal last block to receiver */
    *(StuffPtr->BuffPtr+BLKNUM_L) = EOT_FLAG;
    *(StuffPtr->BuffPtr+BLKNUM_H) = EOT_FLAG;
    StuffPtr->EndFlag = TRUE; /* set local last block flag */
    }
  else   /* put block number in sixth & seventh bytes */
    {
    *(StuffPtr->BuffPtr+BLKNUM_L) = (StuffPtr->BlkNum+1) & 0xff;
    *(StuffPtr->BuffPtr+BLKNUM_H) = (StuffPtr->BlkNum+1) >> 8 & 0xff;
    }
  /* store size of this block for when it is sent after current one */
  StuffPtr->NextBsize = BlkSize+HEADER;
  }
/* swap buffer pointer to other buffer which contains current block */
StuffPtr->BuffPtr = (StuffPtr->BuffPtr == StuffPtr->BuffOne)
? StuffPtr->BuffTwo : StuffPtr->BuffOne;

/* set fifth byte of current block to size of next block */
*(StuffPtr->BuffPtr+NEXT_BSIZE) = StuffPtr->NextBsize & 0xff;
/* put additive & cyclical checksums into first four bytes */
ChksmSend();
return(NOERR);
}

int SLBlocks()     /* statement & listen loop for block send */

{
int resend;
char *charptr;

do
  {
  resend = FALSE;         /* set resend flag */
  StuffPtr->ReplyOut = CODE_ACK; /* send 'ACK' to receiver */
  StuffPtr->ReplyWant = CODE_SBK; /* want 'S/B' from receiver */
  if(SLCodes() == ERROR) /* send & get reply */
    {
    return(ERROR);
    }
  for(charptr = StuffPtr->BuffPtr;        /* send out block */
  charptr < StuffPtr->BuffPtr + (unsigned)StuffPtr->BlkSize;
  charptr++) putser(*charptr);
  do
    {
    GetBytes();
    /* search for string match for 'GOO' or 'BAD' */
    if(strncmp(StuffPtr->CharPtr-3,StuffPtr->ReplyStr[CODE_GOO],3) == NULL)
      {
      if (*(StuffPtr->BuffPtr + BLKNUM_H) !=0 || *(StuffPtr->BuffPtr + BLKNUM_L) 
!=0)
	{
	if (nocount==0) xfertotal += ((StuffPtr->BlkSize & 0xff) - HEADER);
	prtstat(xfertotal,badblocks);
	}
      StuffPtr->ErrBlocks = NOERR;   /* clear block error count */
      StuffPtr->ErrCodes = NOERR;    /* clear reply error count */
      if((*(StuffPtr->BuffPtr + BLKNUM_H) & 0xff) == EOT_FLAG)
      StuffPtr->EndOff = TRUE;   /* final block sent ok */
      return(NOERR);
      }
    else if(strncmp(StuffPtr->CharPtr-3,StuffPtr->ReplyStr[CODE_BAD],3) == NULL)
 
      {
      badblocks++;
      prtstat(xfertotal,badblocks);
      ++StuffPtr->ErrBlocks; /* increment block error count */
      StuffPtr->ErrCodes = NOERR; /* clear reply error count */
      resend = TRUE;  /* flag no end off */
      }
    else
      ++StuffPtr->ErrCodes;
    if(StuffPtr->ErrCodes > MAXCODERR) return(ERROR);
    } while(resend == FALSE);
  } while(StuffPtr->ErrBlocks < MAXBLKERR);
return(ERROR);
}

void ChksmSend()   /* generates checksums for current block */

{
int x;

/* calculate additive and cyclical checksums */
for(StuffPtr->Chksum=StuffPtr->CLC=0,StuffPtr->CharPtr=StuffPtr->BuffPtr+4;
 StuffPtr->CharPtr < StuffPtr->BuffPtr + (StuffPtr->BlkSize & 0xff);
 StuffPtr->CharPtr++)
  {
  StuffPtr->Chksum += (*StuffPtr->CharPtr & 0xff);
  StuffPtr->CLC ^= (*StuffPtr->CharPtr & 0xff);
  if(StuffPtr->CLC & 0x8000) StuffPtr->CLC = (StuffPtr->CLC << 1) + 1;
  else StuffPtr->CLC <<= 1;
  }
/* put checksums into first four bytes */
*(StuffPtr->BuffPtr+CHKSUM_L) = StuffPtr->Chksum & 0xff;
*(StuffPtr->BuffPtr+CHKSUM_H) = StuffPtr->Chksum >> 8 & 0xff;
*(StuffPtr->BuffPtr+CLC_L) = StuffPtr->CLC & 0xff;
*(StuffPtr->BuffPtr+CLC_H) = StuffPtr->CLC >> 8 & 0xff;
}

int EndSend()      /* handle reply codes for data send */

{

StuffPtr->EndXfer = FALSE;

StuffPtr->ReplyOut = CODE_ACK;  /* send 'ACK' */
StuffPtr->ReplyWant = CODE_SBK; /* want 'S/B' */
if(SLCodes() == ERROR) return(ERROR);  /* send & get reply */
StuffPtr->ReplyOut = CODE_SYN;  /* send 'SYN' */
StuffPtr->ReplyWant = CODE_SYN; /* want 'SYN' */
if(SLCodes() == ERROR) return(ERROR);  /* send & get reply */
StuffPtr->EndXfer = TRUE;
StuffPtr->ReplyOut = CODE_SBK;  /* send 'S/B' */
StuffPtr->ReplyWant = CODE_SBK; /* want 'S/B' */
if(SLCodes() == ERROR) return(ERROR);  /* send & get reply */
return(NOERR);
}

/**************************************************************************
*
*	functions common to both receiving and sending
*
**************************************************************************/

int SLCodes()      /* statement & listen loop for reply codes */

{

do
  {
  if (c1cancel()==ERROR) return (ERROR);
  CodeOut();    /* send reply code to receiver */
  /* on final end off only send 'S/B' three times */
  if(StuffPtr->EndXfer == TRUE && StuffPtr->ErrCodes > 1) return(NOERR);
  if(GetBytes() == ERROR) continue;
  /* search for string match */
  if(strncmp(StuffPtr->CharPtr-3, StuffPtr->ReplyStr[StuffPtr->ReplyWant],3) == 
NULL)
    {
    StuffPtr->ErrCodes = NOERR; /* clear reply code error count */
    return(NOERR);
    }
  else
    ++StuffPtr->ErrCodes;
  } while(StuffPtr->ErrCodes < MAXCODERR);
return(ERROR);
}

int GetBytes()

{

long t1, t2;

StuffPtr->CharPtr = StuffPtr->TempBuff;
*(StuffPtr->CharPtr+0)=0;
*(StuffPtr->CharPtr+1)=0;
*(StuffPtr->CharPtr+2)=0;

/* cycle through wait periods */
if(++StuffPtr->Wait > 2) StuffPtr->Wait = 0;
t2 = 0L;
/* set to current time plus the wait period */
t1 = clock() + StuffPtr->Period[StuffPtr->Wait];
while(check() == NULL)
  {
  if(t2 > t1)
    {
    ++StuffPtr->ErrCodes;
    return(ERROR);
    }
  t2 = clock();
  }
do
  {
  t2 = 0L;
  t1 = clock() + 50L; /* max of .05 sec without receiving a byte */
  while(check() == NULL)
    {
    if(t2 > t1)
      {
      StuffPtr->CharPtr += 3;
      return(NOERR);
      }
    t2 = clock();
    }
  *(StuffPtr->CharPtr+0)=*(StuffPtr->CharPtr+1);
  *(StuffPtr->CharPtr+1)=*(StuffPtr->CharPtr+2);
  *(StuffPtr->CharPtr+2)=getser();
  } while(1);
}

int CodeOut()     /* send out a reply code */

{
if (StuffPtr->ReplyOut < 5)
  {
  StuffPtr->CharPtr = StuffPtr->ReplyStr[StuffPtr->ReplyOut];
  while(*StuffPtr->CharPtr != NULL) putser(*StuffPtr->CharPtr++);
  }
}

int c1cancel ()

{
int x;

if ((inp(1022) & 128)==0) return (ERROR);
x=_bios_keybrd(_KEYBRD_SHIFTSTATUS) & 15;
if (x==14 || x==13) return (ERROR);
return (NOERR);
}


--  
UUCP:     watmath!xenitec!zswamp!root | 602-66 Mooregate Crescent
Internet: root@zswamp.fidonet.org     | Kitchener, Ontario
FidoNet:  SYSOP, 1:221/171            | N2M 5E6 CANADA
Data:     (519) 742-8939              | (519) 741-9553
MC Hammer, n. Device used to ensure firm seating of MicroChannel boards
Try our new Bud 'C' compiler... it specializes in 'case' statements!