' ' WPS Model 2 Teletype Translator. ' Tom Jennings ' $Id: model02.bas,v 1.3 2002/12/01 04:39:07 tomj Exp $ ' NOTE: TD basket support doesn't work. Need to work out mappings. ' ' Drives the hacked Model 28 Teletype from WPS record type A. ' Handles TTY motor and selector-magnet power control (eg. ' idle and explicit power off). ' ' Control codes supported: CR, LF, BEL, SO. BELL rings the bell via direct ' output bit. SO turns the motor off immediately, rather than waiting for ' the timeout (currently 30 seconds). When the switch is ON (towards the ' right), then record format "A" is expected; see the specification for ' details. The encapsulated data is treated the same as in TTY mode. ' ' NOTE: the loop voltage is too low for proper loop current flow, so local ' keyboard characters are not formed well enough for the printer to print ' -- though the software picks them up OK. The loop should run with 100VDC ' and a 2K resistor for proper waveform. ' ' 28 Nov 2002 ' Dropped support for character I/O, now only ' record type A. The switch now selects type basket ' TD (63ASCII) vs. RS (ITA2) or equiv. ' ' mru 22 Apr 2002 ' Added motor-off timer call to file mode state 2+. ' Shortened motor-off timer to 20 sec. ' Keyboard code commented out. ' ' mru 10 Mar 1999 ' Using new serinp routine to fix many dropped chars ' (not the fault of PTR). Motor-off idle timer is only run ' in (1) teletype mode or (2) file mode waiting for next ' record start (eg. doesn't run in file mode STX/data/ETX section). ' ' mru 9 Mar 1999 ' Tried to run 9600; failed. Don't know why. Revert to 2400. ' ' mru 8 Mar 1999 ' Added SO/explicit motor off, and removed a debug serout! ' ' mru 25 Feb 1999 ' Built new hardware, used DS2003 as driver instead of ' discrete transistors. Added WPS file system (record ' type A) compatibility, and a mode switch. Added BELL driver. ' ' mru 11 oct 98 ' Driven from Win95 Generic Printer, every other ' character is lost; 1st, 3rd, etc prints, 2nd, 4th, etc ' lost. It works fine, no lost chars, perfect printing, ' with a FOSSIL driver. Some Win9x thing (serial? Win ' driver? Generic Printer?) is not handling CTS properly. ' Tried every thing I could think of; pulsing CTS, true ' for duration of serin, etc; checking for 2nd char ' following. No avail. ' ' mru 8 oct 98 ' - Shortened startup code (pins= instead of high ...) etc. ' Delay after setup of rs232 line so clean start bit on power-up. ' ' mru 6 Oct 98 ' - clear idle timer when TTY keyboard character received ' - Fixed: there is only one FIGS/LTRS case; in and out ' are not separate. The Model 28 is *half duplex*; FIGS/LTRS ' from either source affects the Model 28. Hence only ' one state machine is needed. ' - added Control-E idle/off control ' ' mru 27 Sep 98 ' added SSR and power control; meta commands. (meta speed ' commands do not work!) ' ' written 4 Aug 98 ' Copyright Tom Jennings, 1998, 1999, 2000, 2001, 2002. ' ' ' ASCII(RS-232) to/from ITA2(60mA current loop) interface and translator. ' Based upon a Model 01 CPU-I/O card. ' ' See p 30.34, 1998 ARRL Handbook for ITA2 and speed info. ' ' DATA STOP SPEED (WPM) ' 22mS 26 60 ' 17 21 75 (UNTESTED) ' 13 16 100 (UNTESTED) ' symbol RXSPEED= N2400 ' RS-232 serial line speed symbol TXSPEED= N2400 ' RS-232 serial line speed ' ASCII characters symbol NUL= 0 symbol SOH= 1 symbol STX= 2 symbol ETX= 3 symbol BEL= 7 symbol LF= 10 ' ASCII line feed symbol CR= 13 ' ASCII carriage return symbol SO= 14 ' ASCII SO: turn motor off immediately symbol SI= 15 ' ASCII SI: delay 2 seconds ' ITA2 symbol FIGS= 27 ' ITA2 FIGS char and state value symbol LTRS= 31 ' ITA2 LTRS char and state value symbol DATAP= 21 ' ITA2 output, data bit width symbol STOPP= 33 ' ITA2 output, stop bit width symbol BSKIPP= 33 ' 1.5 * DATAP (start bit + 1/2 data bit) ' Teletype AC motor timer. symbol TIMEOUT1= 2 ' idle motor-off timer fudge factor symbol TIMEOUT=44000 ' for approx. 10 seconds in geta() symbol TDBASKET= 1 ' switch off: type basket TD (63ASCII) symbol RSBASKET= 0 ' switch on: type basket RS (ITA2) symbol poweron= bit0 ' (b0.0) mimics PWR pin symbol indata= bit1 ' (b0.1) STX..ETX state machine symbol astate= b1 ' ASCII state machine symbol case= b2 ' (char) FIGS or LTRS character symbol n= b3 symbol i= b4 symbol c= b5 symbol j= b6 symbol serflag= b7 ' (unsigned) serinp() control/status 'symbol unused2= b8 symbol timer2= b9 ' (unsigned) # of timer1's for motor-off symbol timer1= w10 ' (b20, b21) timer for AC power control symbol BOUT= 0 ' ITA2 serial output (1=idle) symbol BOUTpin= pin0 ' ITA2 serial output symbol BIN= 1 ' ITA2 serial input symbol BINpin= pin1 ' ITA2 serial input symbol PWR= pin2 ' solid state relay, teletype AC power symbol BELL= 3 ' Teletype bell symbol BELLpin= pin3 ' Teletype bell symbol MODEpin= pin4 ' TTY/WPS A mode symbol CTS= 5 ' ASCII Clear To Send symbol CTSpin= pin5 ' ASCII Clear To Send symbol TXD= 6 ' ASCII serial output symbol TXDpin= pin6 ' ASCII serial output symbol RXD= 7 ' ASCII serial input symbol RXDpin= pin7 ' ASCII serial input symbol DBUG= 3 ' 'scope trigger ' 76543210 dirs= %01101101 ' ins and outs... pins= %00000101 ' BOUT high, TXD high, CTS low, poweron= 0 ' assume TTY power on, serout TXD, TXSPEED, (CR, LF) ' chars to the ASCII port, serflag= 1 ' force serial init case= FIGS ' powers up in FIGS state c= CR : gosub AtoB ' print on new line c= LF : gosub AtoB c= BEL: gosub AtoB ' ' State machine for record type A. ' fm1: gosub timer call serinp : if serflag = 0 then fm1 ' wait for SOH, if c <> SOH then fm1 ' fm1a: gosub timer call serinp : if serflag = 0 then fm1a ' wait for 'A', if c <> 65 then fm1 fm2: indata= 0 ' no STX yet, fm2a: call serinp : if serflag = 0 then fm2a ' process char stream, ' NUL:restart 1:eh 2:STX 3:ETX 4:EOT branch c, (fm1, fm2a, fm2c, fm2, fm1) fm2b: if indata = 0 then fm2a ' wait for STX, fm2c: indata= 1 ' got STX, (AtoB will ignore it) gosub AtoB ' output data character, goto fm2a ' repeat. ' ' Count down the motor timer. ' timer: timer1= timer1 - 1 ' count down motor timer, if timer1 <> 0 then ret ' exit if not zero yet. timer1= TIMEOUT timer2= timer2 - 1 ' decrement number of ten-sec if timer2 <> 0 then ret ' intervals ' ' Turn motor off. ' off: timer1= TIMEOUT ' reset timer, timer2= TIMEOUT1 if poweron = 1 then of1 ' if motor AC already off, BOUTpin= 0 ' turn off magnet current, of1: PWR= 0 : poweron= 0 ' turn off AC motor, ret: return ' ' Convert ASCII character C to ITA2 character N. If the new character is not ' in the current case, issue the right case character. The table does all ' the work; it is the entire US TTY ITA2 character set in ASCII order in the ' lower six bits; the upper two bits contain the case the character resides ' in: $80=LTRS, $40=FIGS, 0=both (eg. CR, etc). $ff=unprintable character. ' ' US TTY, type basket RS. Note that we make the following mappings: ' ' ASCII ITA ' ! . ' + WRU (modified basket) ' ' 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 '00 $ff,$ff,$ff,$ff,$ff,$ff,$ff,$05,$ff,$ff,$02,$ff,$ff,$08,$ff,$ff, '16 $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff, '32 $04,$5c,$51,$54,$49,$04,$5a,$4b,$4f,$52,$04,$09,$4c,$43,$5c,$5d, '48 $56,$57,$53,$41,$4a,$50,$55,$47,$46,$58,$4e,$5e,$04,$04,$04,$59, '64 $04,$83,$99,$8e,$89,$81,$8d,$9a,$94,$86,$8b,$8f,$92,$9c,$8c,$98, '80 $96,$97,$8a,$85,$90,$87,$9e,$93,$9d,$95,$91,$04,$04,$04,$04,$04, ' ' Keep the above table as master, edit into nasty lookup() command. ' ' Modifies N, C, J. ' AtoB: if c = SO then off ' control char SO: motor off if c <> BEL then ab0 ' control char BEL: ding-a-ling pulsout BELL, 2500 ' bell has own solenoid, pause 200 ' let it ringgggggggg... return ' ' Process the character; convert to upper case, determine FIGS or LTRS ' case from the table; if different than the current case, issue ' case character first. ' ab0: if c < 96 then ab1 ' if lower case, c= c - 32 ' force to upper case if MODEpin = TDBASKET then ab1a ' if TD, don't translate ab1: lookup c, ($ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $02, $ff, $ff, $08, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $04, $4d, $51, $54, $49, $04, $5a, $4b, $4f, $52, $04, $04, $4c, $43, $5c, $5d, $56, $57, $53, $41, $4a, $50, $55, $47, $46, $58, $4e, $5e, $04, $04, $04, $59, $04, $83, $99, $8e, $89, $81, $8d, $9a, $94, $86, $8b, $8f, $92, $9c, $8c, $98, $96, $97, $8a, $85, $90, $87, $9e, $93, $9d, $95, $91, $04, $04, $04, $04, $04), c ab1a: if c = $FF then ret ' if unprintable, done. if poweron = 1 then ab1b ' else if TTY power off... ' ' The Teletype is turned off; turn it on, and issue the correct FIGS or ' LTRS command. Issuing a NUL simply runs the print/keyboard mechanism for ' the case where a key was pressed while motor off or garbage was issued ' by a bouncing RS232 line (such as when a laptop sleeps, ahem). Reissuing ' the case character is probably worthless, except in the marginal case ' where the garbage looks like FIGS or LTRS. ' BOUTpin= 1 ' turn on magnet loop current, pause 100 ' let settle PWR= 1 : poweron= 1 ' turn on big AC motor, pause 750 ' let motor start up, n= NUL : gosub outITA2 ' send a NUL to clear, n= case : gosub outITA2 ' restore FIGS/LTRS case, ab1b: timer1= TIMEOUT ' continuously reset the timer, timer2= TIMEOUT1 n= c / 64 ' N= case indicator bits, c= c & $1F ' C= ITA2 character, if n = 0 then ab2 ' if in both cases, output now, lookup n, (0, FIGS, LTRS, 0), n ' else gen case char if n = 0 then ab2 ' if in both cases, just output; if n = case then ab2 ' or same case, just output; case= n : gosub outITA2 ' else save, output case char, ab2: n= c ' then output data character. ' ' Output the LS 5 bits of N to the teletype. ' Modifies N, J. ' outITA2: BOUTpin= 0 ' do start bit ' pulsout DBUG, 4 pause DATAP for j= 1 to 5 ' five data bits, BOUTpin= n : n= n / 2 ' assert data, LS bit first ' pulsout DBUG, 1 pause DATAP ' generate data bit, next j BOUTpin= 1 ' do stop bit ' pulsout DBUG, 1 pause STOPP return ' ' Convert ITA2 character N to ASCII and output to the ASCII port. ' This keeps track of LTRS/FIGS state. ' Modifies N. ' BtoA: if n = FIGS then ba2 ' handle FIGS & LTRS separately if n = LTRS then ba2 if case = LTRS then ba1 : n= n + 32 ' select LTRS or FIGS table ' FIGS LTRS ba1: lookup n, (0,"E",10,"A SIU",13,"DRJNFCKTZLWHYPQOBG",255,"MXV",255, 0,"3",10,"- ",7,"87",13,"$4',!:(5",34,")2#6019?&",255,"./;",255), n if n = 255 then ret ' if translatable, serout TXD, TXSPEED, (n) ' output to ASCII port, return ba2: case= n : n= 255 : return ' ' Read a ITA2 character into N. There are five data bits followed by one ' stop bit. The start bit is now under way. We sample the middle of each ' bit period; therefore we delay initially 1-1/2 data bit times (skip start ' bit, half of 1st data bit), sample the data, then to read the next four ' data bits, delay one bit time and sample, four times. After the last ' data bit, we delay until the end of the stop bit -- because the teletype ' is half-duplex, if we hurry up and read an ASCII character and output ' it before the key board character is complete, we'll generate garbage. ' ' Modifies J, N. ' inITA2: timer1= TIMEOUT ' reset the timer, timer2= TIMEOUT1 n= 0 ' clear built-up character, ' pulsout DBUG, 4 pause BSKIPP ' skip to middle of 1st data bit j= 1 ' loop counter and bit value ib1:' pulsout DBUG, 1 if BINpin = 0 then ib2 ' if data bit present, n= n + j ' add in weighted bit ib2: pause DATAP ' delay into next bit time, j= j + j ' increase bit weight if j < 32 then ib1 ' do 5 times (eg. 2^^5 == 32) pause BSKIPP ' skip 2nd half of last data bit ' pulsout DBUG, 1 return END ASM ; ; Poll for a serial character, returns serflag=1 and character in C ; and CTS 0 if one is present (eg. start bit visible on serial line). If ; serflag=1 on entry, CTS is set and the serin() call arguments are ; set up before the serin() call; if serflag=0 on entry then this is ; skipped, which allows for greater speed in tight loops IFF no other ; serial I/O is done between calls. Note that upon character receipt, ; if serflag is left set then serin() will be initialized properly on ; the first call. ; _serinp movf _serflag, f btfss Z ; if serflag set, goto __sp1 ; go init. btfss portB, _RXD ; else if start bit not present, goto done ; exit now. ; Start bit has been asserted onto the serial line; read the ; character then turn off CTS and prepare for return. __sp2 call serin@W ; read character, bcf portb, _CTS ; deassert CTS, movwf _c ; store W in C, movlw 1 movwf _serflag ; set serflag goto done ; Assert CTS, and ready the args for the later serin() call. __sp1 bsf portB, _CTS ; assert CTS, movlw _RXD ; Rx pin number, call BP@Pin movlw _RXSPEED ; port speed, movwf GOP ; (local to ass'y code?) clrf _serflag ; set serflag false, goto done ENDASM