; ;Port driver for Apple (R) Super Serial Card and compatibles ; ;Version 1.0b1 ; ;by Scott Alfter ; ;Copyright (C) 1990 by Skunk Works Software Co. ; 43 Megan Dr. ; Henderson, NV 89014 ; ;All Rights Reserved ; ;Source code for MindCraft (TM) Assembler ; ;------------------------------------------------------------------------- ; ;This driver is designed to work with the Term One (TM) telecommunications ;system by Skunk Works Software, but could easily be used in other ;telecommunications applications. Term One uses only a handful of ;addresses to talk to any port driver: ; ;$0800 Initialize hardware (INIT) ;$0803 Set baud rate and data format (SETPARMS) ;$0806 Control buffer handshaking (HNDSHK) ;$0809 Get a character through the port (GETCHAR) ;$080C Send a character out the port (SENDCHAR) ;$080F Send a 233 ms BREAK out the port (SENDBRK) ;$0812 Shut down the driver (SHUTDOWN) ; ;Do a JSR to these addresses. Parameters are as follows: ; ;INIT: Baud rate/data format code in accumulator, slot number in ; X-register. (Code documented before routine.) If card in slot X ; is incompatible with this driver, the routine quits with the carry ; set and the high bit of the Y-register set. ;SETPARMS: Baud rate/data format code in accumulator. ;HNDSHK: High bit of accumulator set to enable buffer handshaking. ;GETCHAR: Character returned in accumulator. ;SENDCHAR: Character to send in accumulator. ;SENDBRK: No parameters. ;SHUTDOWN: No parameters. ; ;For all routines, the carry flag indicates if an error occurred; it will ;be set if something went wrong. ; ;------------------------------------------------------------------------- ; ;My legal stuff: ; "Term One" is a trademark of the Skunk Works Software Co. ; ;Other people's legal stuff: ; "Apple" is a registered trademark of Apple Computer, Inc. ; "MindCraft" is a trademark of MindCraft Publishing Corp. ; ;------------------------------------------------------------------------- ; ;Some EQUates needed for proper operation ; ALLOC_INTERRUPT EQU $40 ;MLI command to make an interrupt vector DEALLOC_INTERRUPT EQU $41 ;MLI command to remove an interrupt vector MLICALL EQU $BF00 ;Enter the MLI here TDREG EQU $C088 ;ACIA transmit register RDREG EQU $C088 ;ACIA receive register STATUS EQU $C089 ;Tells all about the ACIA COMMAND EQU $C08A ;Sets parity and turns interrupts on and off CONTROL EQU $C08B ;Sets baud rate and number of stop bits ; ;Jump table and other interfacing stuff ; ORG $800 JMP INIT JMP SETPARMS JMP HNDSHK JMP GETCHAR JMP SENDCHAR JMP SENDBRK JMP SHUTDOWN DFC $10 ;Driver protocol version number ASC "Apple SSC" ;Hardware description DFC 0 ; ;System initialization ; NOTSSC LDY #$80 ;Initialization error also returns with high bit ;of Y-reg set if card wasn't an SSC INITQUIT PLA ;Clear the baud rate out of the stack SEC ;Flag an error RTS INIT STX SLOT ;Save slot number PHA ;Save baud rate for later TXA ;Make $Cx so we can identify card in slot ORA #$C0 STA CHECK1 ;Alter addresses for card check STA CHECK2 STA CHECK3 STA CHECK4 CHECK1 EQU .+2 LDA $C005 ;$Cx05 should be $38 CMP #$38 BNE NOTSSC CHECK2 EQU .+2 LDA $C007 ;$Cx07 should be $18 CMP #$18 BNE NOTSSC CHECK3 EQU .+2 LDA $C00B ;$Cx0B should be $01 CMP #$01 BNE NOTSSC CHECK4 EQU .+2 LDA $C00C ;$Cx0C should be $31 CMP #$31 BNE NOTSSC LDA SLOT ;Multiply slot number by 16 ASL ASL ASL ASL STA SLOT CLC ;To prevent false read during SSC transmit ADC #TDREG+1 STA SPECSLOT JSR MLICALL ;Set up an interrupt vector for us DFC ALLOC_INTERRUPT ADR ALLOC_BLK LDY #0 ;In case an error occurs at this stage... BCS INITQUIT ;Error brings it to a stop LDA ALLOC_BLK+1 ;Move vector number for shutdown STA DEALLOC_BLK+1 LDA #9 ;Enable interrupts from card and set no parity LDX SLOT STA COMMAND,X LDA #0 ;Default for handshaking is off STA HSFLAG CLI ;Tell the processor to start taking interrupts PLA ;Get the baud rate back JSR SETPARMS ;Set baud rate and data format BCC INITOK ;All's cool if we can set the parameters JSR SHUTDOWN ;If not, undo initialization SEC LDY #0 INITOK RTS ; ;Set baud rate and data format ; ;Accumulator contains 0000fbbb on entry, where ;bbb: 0=300 baud ; 1=1200 baud ; 2=2400 baud ; 3=4800 baud ; 4=9600 baud ; 5=14400 baud ; 6=19200 baud ; w: 0=8 data bits, 1 stop bit, no parity ; 1=7 data bits, 1 stop bit, even parity ; ;Note that the SSC does not support 14400 baud, so the carry returns set ;if the calling routine tries to select this baud rate. ; SETPARMS AND #$F ;Get rid of unused bits TAY ;Look up the actual parameter code to set LDA ]TABLE,Y BEQ ]ERROR ;Some choices are invalid LDX SLOT STA CONTROL,X ;Set the baud rate CLC ;No errors RTS ;All done here! ]ERROR SEC ;Something went wrong RTS ]TABLE DFC $16 ;300/8/N/1 DFC $18 ;1200/8/N/1 DFC $1A ;2400/8/N/1 DFC $1C ;4800/8/N/1 DFC $1E ;9600/8/N/1 DFC 0 ;14.4K/8/N/1 (not supported by SSC) DFC $1F ;19.2K/8/N/1 DFC 0 ;future expansion? DFC $36 ;300/7/E/1 DFC $38 ;1200/7/E/1 DFC $3A ;2400/7/E/1 DFC $3C ;4800/7/E/1 DFC $3E ;9600/7/E/1 DFC 0 ;14.4K/7/E/1 (not supported by SSC) DFC $3F ;19.2K/7/E/1 DFC 0 ;future expansion? ; ;Turn XON/XOFF buffer handshaking on or off ; HNDSHK STA HSFLAG RTS ; ;Get a character from the port ; GETCHAR LDA BUFOUT ;First, see if anything's come in CMP BUFIN BEQ ]ERROR ;Exit if no character TAY ;Get the character LDA BUFFER,Y INC BUFOUT ;Bump the pointer up a notch PHA BIT PAUSE ;Did the handshaking kick in? BPL ]DONE SEC ;See if the buffer's emptying LDA BUFIN SBC BUFOUT CMP #64 ;When it's only 1/4 full, we can receive again BCS ]DONE LDA #0 ;Clear the flag STA PAUSE LDA #$11 ;Send XON JSR SENDCHAR ]DONE PLA CLC ;No errors (we got a character) RTS ]ERROR SEC ;Carry set if no character was available RTS ; ;Send a character out the port ; SENDCHAR PHA ;Save the outgoing character for the time being BIT PAUSE ;Refuse to send if handshaking is in effect BMI ]ERROR LDX SLOT LDA STATUS,X ;See if the card's ready AND #$60 ;If bits 5 or 6 are set, the card ain't up to it BNE ]ERROR ]1 LDA STATUS,X AND #$10 ;If bit 4 is clear, the card's still chewing on BEQ [1 ;the last character PLA ;Get the character back LDX SPECSLOT ;Takes care of "false read" problem STA $BFFF,X ;Send the character on its way CLC ;No errors RTS ]ERROR PLA ;Return the stack to normal SEC ;Quit with an error flagged RTS ; ;Shut down the driver ; SHUTDOWN SEI ;The processor will no longer get interrupts LDA #$B ;The SSC will no longer make interrupts LDX SLOT STA COMMAND,X JSR MLICALL ;Remove the interrupt handler DFC DEALLOC_INTERRUPT ADR DEALLOC_BLK RTS ;If something went wrong, it was with the MLI ; ;Send a 233 ms BREAK out the SSC ;(Timing is presently valid only at 1 MHz--may act up on a IIGS, IIc Plus, ;or any other accelerated system.) ; SENDBRK LDA #$D ;Tell SSC to start streaming zero bits LDX SLOT STA COMMAND,X LDX #233 ;Kill 233 ms of time ]1 LDY #$CA ]2 DEY BNE [2 DEX BNE [1 LDA #9 ;Restore the SSC to normal operation LDX SLOT STA COMMAND,X CLC ;How could something go wrong? RTS ;All done! ; ;This is the daemon that grabs incoming characters from the port. When a ;character arrives, the SSC generates an interrupt. ProDOS catches the ;interrupt and sends it on to us. Assuming that the interrupt is ours ;(other daemons may very well be installed for other purposes), we get the ;incoming character and stuff it into a 256-byte FIFO queue for the ;GETCHAR routine to retrieve whenever Term One is up to it. ; DAEMON CLD ;Interrupt daemons always start this way LDX SLOT LDA STATUS,X ;Check to see if the interrupt is ours BMI ]YES SEC ;Tell ProDOS to try another daemon (better not be RTS ;dealing with a spurious interrupt here :-| ) ]YES LDY BUFIN ;OK, so stuff the character into the queue LDA RDREG,X STA BUFFER,Y INC BUFIN SEC LDA BUFIN SBC BUFOUT CMP #$80 ;Tell remote device to stop sending stuff... BCC ]DONE BIT PAUSE ;...but only if we haven't already... BMI ]DONE BIT HSFLAG ;...and only if handshaking has been enabled BPL ]DONE LDA #$13 ;Send XOFF JSR SENDCHAR LDA #$80 ;Tell GETCHAR that pause is in effect STA PAUSE ]DONE CLC ;ProDOS need not look further RTS ; ;Room for data and such ; SLOT DFS 1 ;Slot number (times 16) goes here SPECSLOT DFC 1 ;This, when added to $BFFF, yields the right ;TDREG ALLOC_BLK DFC 2 ;2 parameters for ALLOC_INTERRUPT call DFS 1 ;Interrupt priority is returned here ADR DAEMON ;Address of the interrupt daemon goes here DEALLOC_BLK DFC 1 ;Only 1 parameter for DEALLOC_INTERRUPT DFS 1 ;Interrupt priority returned by ALLOC_INTERRUPT ;goes here BUFIN DFC 0 ;The interrupt daemon bumps this when a character ;arrives BUFOUT DFC 0 ;GETCHAR bumps this when Term One gets a ;character PAUSE DFC 0 ;When XOFF has automatically been sent, the high ;bit is set HSFLAG DFC 0 ;High bit set to enable XON/XOFF buffer ;handshaking BUFFER DFS 128 ;The FIFO queue (too bad this assembler can't DFS 128 ;handle DFS 256 instead :-( )