;************************************ ;ROUTINE: SSCDRIVR.A65 ;PURP: SUPER SERIAL DRIVER ; THIS PROGRAM PROVIDES INTERRUPT DRIVEN I/O USING THE SUPER SERIAL ; CARD. IT WRITES DIRECTLY TO THE 6551; THEREFORE, THE USUAL PASCAL ; RESTRICTION TO 7-BIT DATA TRANSFERS DOES NOT EXIST. 8-BIT DATA ; CAN BE SENT AND RECEIVED. ; ; THE DRIVER IS A DIRECT ADAPTATION OF THE PCPI SUPRSER.A65 DRIVER ; ; 02/5/87 (RAP) ; CREATED ;************************************ ;!!!!!!!!!!!!!!!!!! IMPORTANT NOTE !!!!!!!!!!!!!!!!!!!!! ; ; IF THIS DEVICE MUST USE PAGE 0 LOCATIONS OTHER THAN THOSE THAT ARE ; TO BE RELOCATED THEY MUST BE SAVED AND RESTORED BY THE DRIVER. AN ; EXAMPLE WOULD BE A DRIVER WHICH USES ROUTINES IN A ROM IN A PERIPHERAL ; SLOT AND THAT PERIPHERAL USES FIXED PAGE ZERO LOCATIONS. THESE SHOULD BE ; SAVED AND RESTORED. THERE ARE ONLY 128 BYTES OF PAGE ZERO FOR DRIVERS ; SO USE THEM SPARINGLY ; ;!!!!!!!!! ANOTHER IMPORTANT NOTE !!!!!!!!!!!!!!!!!!!!!! ; ; THE DEVICE NUMBER IS UP TO YOU TO SET. I'VE SET IT AT 18. BUT ; YOU MIGHT NOT LIKE THAT. YOU CAN CHANGE IT HERE, OR USING INSTALL.COM ; ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ; ;ASK THE OPERATOR AT ASSEMBLY TIME WHAT THE VALUE OF BASEP0 SHOULD BE ; WHEN THE DRIVER USES PAGE 0 THE CODE MUST BE ASSEMBLED TWICE ONCE WITH ; BASEP0 AT 0 AND ONCE AT 1. THESE TO FILES ARE THEN COMPARED TO GENERATE ; THE PAGE 0 RELOCATION BIT MAP. BASEP0: .QUERY "ENTER BASE OF PAGE 0: " PAGE0W0: .EQU BASEP0 ;WORD 0 OF PAGE 0 LENP0: .EQU 2 ;0 BYTES OF PAGE 0 SLOTS0: .QUERY "ENTER THE SUPER SERIAL CARD SLOT: " FALSE: .EQU 0 ;ASSEMBLER FALSE VALUE TRUE: .EQU NOT FALSE ; PLEASE NOTE THE USE OF "TURN ON THIS VIDEO" COMMAND AS A MEANS ; OF DISABLING INTERRUPTS IN THIS DRIVER QUITCMD: .EQU 2 ;SUPER SERIAL CARD EQUATES IBUFF .EQU 00200H ;CIRCULAR BUFFER SET TO PAGE 2 DATREG .EQU 0C088H ;6551 REGISTERS STAREG .EQU 0C089H ; MEMORY MAPPED INTO CMDREG .EQU 0C08AH ; C0 SPACE CTLREG .EQU 0C08BH TMASK .EQU 010H ;MASKS FOR SSC DATA READY FLAGS RMASK .EQU 008H ;HEADER FOR RELOCATION TOP: .WORD 0 ;IF THIS WORD IS 0 THEN ; RELOCATABLE DRIVER ; AND A RELOCATING BIT MAP IS EXPECTED ;ELSE ; THE DRIVER STORED WHERE INDICATED .WORD ((BOTTOM-TOP)+0FFH) AND 0FF00H ;LENGTH OF THIS CODE ; TO NEXT PAGE BOUNDRY .BYTE LENP0 ;THIS IS THE NUMBER OF PAGE 0 BYTES ; REQUIRED. IF ITS NO ZERO THEN A RELOCATING ; BIT MAP IS EXPECTED FOR PAGE 0 .BYTE 0 ;TAG FIELD DEVNUM: .WORD 18 ;DEVICE NUMBER !! CHANGE THIS FOR YOUR USE!! .WORD 1 ;NUMBER OF DEVICES THIS DRIVER WILL SERVICE .WORD INITENTRY ;INIT ENTRY POINT .WORD INENTRY ;INPUT ENTRY POINT .WORD OUTENTRY ;OUTPUT ENTRY POINT .WORD OTHERENTRY ;OTHER ENTRY POINT .WORD POLLENTRY ;POLL ENTRY POINT .WORD 1 ;VERSION NUMBER NAME: .BYTE 15,"SSC INTRPT DRVR" ;NAME OF DRIVER (MUST BE 15 BYTES PLUS ; THE LENGTH BYTE) SSCSLOT: .WORD SLOTS0*16 ;GET PCPI EQUATES .INCLUDE DRVREQUS.A65 ;************************************* ;ROUTINE: INITENTRY ;PURP: RESET THE DRIVER. ; THIS CODE TURNS THE DRIVER OFF, ; INSTEAD OF TURNING IT ON, AS YOU WOULD USUALLY ; EXPECT. A RESET OF THE COMPUTER WILL NOT ALWAYS DISABLE ; INTERRUPTS, WHICH COULD LEAVE YOU AT THE MERCY OF WHATEVER WAS ; GOING ON WHEN THE RESET OCCURRED. IF THE MODEM ; WERE STILL ON LINE, FOR INSTANCE, EACH INPUT CHARACTER ; WOULD BECOME A POTENTIAL CRASH. AS IT IS, THE RESET ; SHOULD INITIALIZE THE DRIVERS, WHICH WILL TURN OFF ; THE INTERRUPTS. VOILA! SAFETY (WE HOPE) ; THE DRIVER IS INITIALIZED BY THE FIRST ATTEMPT ; TO WRITE TO IT. ; ;ENTRY: NONE ;USED: ALL ;************************************* INITENTRY: SEI ;RESET INTERRUPTS LDY SSCSLOT ;INDEX TO SSC SLOT LDA #00BH ;RESET THE INTERRUPT ENABLE BIT STA CMDREG LDA #0A5H ;MARK IT NOT POWERED UP STA INITCHK ; IN A FLAG TO BE CHECKED BY RTS ; THE OUTPUT ROUTINE ;*********************************** ;ROUTINE: INENTRY: ; IT SHOULD NOT BE NECESSARY TO DO THE ; CHECK I'VE INCLUDED TO ENSURE THAT A ; CHARACTER IS READY. BUT IF THE CALLER DOESN'T ; CHECK THE INPUT STATUS BEFORE THE CALL ; FOR WHATEVER REASON, THE BUFFER WILL GET MESSED ; UP, AND YOU'LL GET 255 CHARS OF GARBAGE. A NULL ; MIGHT NOT BE THE BEST THING TO RETURN IN THIS CASE, ; BUT NOTHING ELSE IS, EITHER. FEEL FREE TO USE ; 0FFH, FOR INSTANCE, IF YOU PREFER. ; ;PURP: INPUT A CHARACTER ;EXIT: A = CHARACTER ;USED: A, Y ;*********************************** INENTRY: LDY TAIL ;CHECK TO SEE IF ANY DATA IS READY CPY HEAD ; BY COMPARING HEAD AND TAIL BEQ NOINT ;IF THEY'RE THE SAME, NO DATA LDA IBUFF,Y ;ELSE GET A BYTE INC TAIL ;INCREMENT THE ; TAIL TOWARD THE HEAD RTS ; AND RETURN NOINT LDA #000H ;NO ERROR, BUT RTS ; NO DATA, EITHER ;*********************************** ;ROUTINE: OUTENTRY ;PURP: OUTPUT A CHARACTER TO THE DEVICE ;ENTRY: A = CHARACTER ;EXIT: NONE ;USED: A, Y ;************************************ OUTENTRY: PHA ;SAVE THE CHARACTER LDA INITCHK ;HAS IT BEEN INITIALLIZED? BEQ SENDIT ; IF ZERO, DOESN'T NEED INITIALIZATION JSR SSCINIT ; ELSE IT DOES NEED IT. SENDIT: PLA ;RECOVER THE CHARACTER LDY SSCSLOT ;GET THE SLOT OFFSET STA DATREG,Y ;WRITE TO THE DATA REGISTER RTS ;AND GO BACK HOME. TOO EASY. ; THIS INITIALIZES THE SSC TO ENABLE INTERRUPTS, SET THE BAUD RATE ; AT 300, 8 BITS, 1 STOP, NO PARITY. IT ALSO INSTALLS THE IRQ ; VECTOR. SSCINIT: SEI ;DISABLE INTERRUPTS FOR A MOMENT LDY SSCSLOT LDA #009H ;ENABLES INTERRUPTS, NO PARITY STA CMDREG,Y LDA #016H ;$16 = 300 BAUD, $18 = 1200 BAUD STA CTLREG,Y LDA DATREG,Y ;AFTER WRITING TO THE CONTROL REG, ; READ DATA TO CLEAR LDA SERVER ;INSTALL INTERRUPT SERVICE ADDRESS ; IN THE 6502'S IRQ VECTOR STA 0FFFEH LDA SERVER+1 STA 0FFFFH LDA #000H ;SET THE BUFFER HEAD AND TAIL TO ZERO STA HEAD STA TAIL CLI RTS ;*********************************** ;ROUTINE: INTERRUPT SERVICE ROUTINE ; THE ROUTINE USES A CIRCULAR BUFFER THAT !!MUST!! BEGIN ; ON A PAGE BOUNDARY TO ALLOW THE WRAP-AROUND TO WORK. ; IF YOU DON'T USE PAGE 2 FOR THE BUFFER, BE CAREFUL. ; ;PURP: HANDLE INTERRUPTS FROM THE SSC ;ENTRY: DOESN'T MATTER ; ;EXIT: ALL REGISTERS ARE PRESERVED ;USED: N/A ;*********************************** ISERV PHA ;SAVE ALL REGISTERS USED TYA PHA TXA PHA LDY SSCSLOT ;GET SSC OFFSET INTO Y LDA STAREG,Y ;CHECK INPUT STATUS AND #RMASK BEQ SKIP ;IF NO INPUT, SOMETHING ELSE CAUSED ; THE INTERRUPT, SO IGNORE IT LDA DATREG,Y ;ELSE READ THE DATA LDY HEAD ;GET THE HEAD POINTER STA IBUFF,Y ;STORE THE DATA IN THE BUFFER INC HEAD ;INCREMENT THE HEAD POINTER SKIP PLA ;RESTORE THE REGISTERS AND LEAVE TAX PLA TAY PLA RTI ;THAT'S ALL THERE IS TO IT! ;*********************************** ;ROUTINE: OTHERENTRY ; THIS ROUTINE USES GENERAL COMMAND 2 TO SHUT ; DOWN THE INTERRUPTS. OTHER 'OTHER' COMMANDS ; COULD BE USED TO SET BAUD RATES, DATA, PARITY ; AND STOP BITS. AS NOTED, THIS ONE IS SET UP ; FOR 300 BAUD, 8 DATA, 1 STOP, NO PARITY. ; ;PURP: HANDLE OTHER COMMANDS ;ENTRY: A = OTHER COMMAND ;EXIT: A = ERROR CODE ;USED: ALL ;*********************************** OTHERENTRY: CMP #0 BEQ OUTSTAT ;BRANCH IF OUTPUT STATUS COMMAND CMP #1 BEQ INSTAT ;BRANCH IF INPUT STATUS COMMAND CMP #SNDNAMECMD BEQ SENDNAME ;BRANCH IF SEND NAME COMMAND CMP #QUITCMD BNE BADCMD JSR INITENTRY LDA #000H RTS ;ERROR BAD COMMAND BADCMD: LDA #0FFH ;ELSE ERROR RTS INSTAT: LDA TAIL ;CHECK TO SEE IF ANY DATA IS READY CMP HEAD ; BY COMPARING HEAD AND TAIL BEQ NOTRDY ;IF THEY'RE THE SAME, NO DATA BNE RDY ; ELSE THERE IS DATA OUTSTAT: LDY SSCSLOT LDA STAREG,Y ;READ THE STATUS REGISTER AND #TMASK ;MASK IT BNE RDY ;ZERO = NOT READY ;FALL THROUGH IF = ZERO NOTRDY: LDA #0 CLC RTS RDY: LDA #0FFH SEC RTS SENDNAME: ;SEND THE NAME COMMAND LDA NAME ;GET LENGTH STA CNT ;SAVE AS COUNT JSR WR1Z80BYTE ;SEND IT TO HOST LDA #1 STA IDX $LP: LDX IDX LDA NAME,X ;GET NEXT CHARACTER JSR WR1Z80BYTE INC IDX DEC CNT BNE $LP ;CONTINUE UNTIL ALL BYTES ARE SENT LDA #0 RTS ;*********************************** ;ROUTINE: POLLENTRY ;PURP: HANDLE POLLING, THIS ENTRY POINT ; IS CALLED PERIODICALY WHILE THE APPLE IS ; WAITING FOR A COMMAND FROM THE Z-80. NOTHING ; IS DONE BY THE SSC DURING POLLS. ;ENTRY: NONE ;EXIT: NONE ;USED: ALL ;*********************************** POLLENTRY: RTS ;TEMPORARY DATA HEAD: .BYTE 1 ;CIRCULAR BUFFER TAIL: .BYTE 1 ; POINTERS SERVER: .WORD ISERV INITCHK: .BYTE 1 ;INITIALIZATION DONE FLAG CNT: .BLOCK 1 ;TEMPORARY IDX: .BLOCK 1 ;TEMPORARY BOTTOM: .END