TITLE 'SERIAL.ASM - AN EXAMPLE FID FOR A SERIAL PORT' ; ; SUPPORTS AN 8251 USART ON THE SPECTRUM +3 ; WRITTEN BY LOCOMOTIVE SOFTWARE (JUNE 1988) ; CORRECTIONS BY F.T.GOWEN, 1990 ; ; BUILD BY RMAC SERIAL ; LINK SERIAL [OP] ; REN SERIAL.FID=SERIAL.PRL ; BASIC FIDCSUM ; ; DEFINITIONS ; =========== ; VERSION EQU 0100H ;VERSION NUMBER IN BCD ; SERIALDATA EQU 0FE7FH ;USART DATA CHANNEL SERIALCONTROL EQU 0FF7FH ;USART CONTROL CHANNEL ; ; Z80 INSTRUCTIONS ; OUTA EQU 079EDH ;OUT (C), A OUTE EQU 059EDH ;OUT (C), E INTOA EQU 078EDH ;IN A, (C) ; ; STRICTLY, ONE SHOULD LINK IN ANOTHER .REL FILE WITH THE SVC DEFINITIONS. ; THIS TRICK RELIES ON $=0100 AND AVOIDS HAVING TO HAVE TWO SOURCE FILES. ; SVCSCB EQU $ + 0FE03H ;ADDRESS OF SCB SVCCHOOK EQU $ + 0FE04H ;HOOK IN CHARACTER DEVICE ; ;========= FIDHEADER: ;========= ; ; STANDARD 32 BYTE FID HEADER ; JMP FIDEMS DB 'SPECTRUM' ;NAME DB 'FID' ;TYPE DW VERSION ;VERSION NUMBER DW 0000H ;CHECKSUM DB 0 ;CAN START ANYWHERE DB 0 ;CAN END ANYWHERE DB 0,0,0,0,0,0,0,0,0,0,0,0 ;RESERVED ; ; "PRIVATE" JUMPBLOCK ; =================== ; ; ROUTINES WHICH CAN BE ACCESSED BY .COM PROGRAMS WHICH USE FIND_FID ; ; THESE CAN VARY FROM FID TO FID ... THE ONES HERE ARE FOR THE STSERIAL ; PROGRAM SIMILAR TO THE STANDARD SERIAL CHANNEL'S "SETSIO" ; JMP CDSAINIT ;+32: SET PARAMETERS JMP CDSAPARAMS ;+35: FETCH PARAMETERS JMP CDSABAUD ;+38: SET BAUD RATE ; ; VARIABLES ; ========= ; MODE: DB 1 ;HANDSHAKE MODE PARITY: DB 0 ;NO PARITY DB 0 ;1 STOP BIT TXBITS: DB 8 ;TX 8 BITS DB 8 ;RX 8 BITS ; RXMASK: DS 1 ;MASK FOR RECEIVED CHARACTERS DEVTABBAUD: DS 2 ;ADDRESS IN DEVICE TABLE FOR BAUDRATE BAUDRATE: DS 1 ;THE CURRENT (HENCE LEGAL) BAUD RATE ; ;====== FIDEMS: ;====== ; ; EARLY MORNING START ; ; ENTRY: ; DE = FID ENVIRONMENT ; C = COUNTRY CODE ; EXIT: ; IF OK ; CARRY TRUE ; HL = SIGN-ON MESSAGE ; IF ERROR ; CARRY FALSE ; HL = ERROR MESSAGE ; ALWAYS ; OTHER FLAGS A BC DE IX IY CORRUPT ; ALL OTHER REGISTERS PRESERVED ; MOV A, D ORA A JNZ @10FIDEMS ;JUMP IF MAJOR VERSION NOT RECOGNISED ; ; HOOK IN THE SERIAL PORT ; LXI D, @30FIDEMS ;JUMPBLOCK LXI H, @40FIDEMS ;CHARACTER DEVICE TABLE CALL SVCCHOOK ;HOOK CHARACTER DEVICE @10FIDEMS: LXI H, @60FIDEMS ;FAIL MESSAGE RNC ;EXIT IF ERROR ; ; HIJACK THE AUX: DEVICE ; LXI D, 8000H ;WE KNOW IT CANNOT BE NUMBERED 0 @20FIDEMS: ORA A MOV A, D RAR MOV D, A MOV A, E RAR MOV E, A ;DE = DEVICE MASK DCR B JNZ @20FIDEMS ;CONSTRUCTED FROM DEVICE NUMBER ; LXI B, SVCSCB ;DONT WRITE "SCB+26H" LXI H, 26H DAD B ;AUXOUT: ENTRY ; MOV M, E INX H MOV M, D ;REDIRECT AUXOUT: INX H ; MOV M, E INX H MOV M, D ;REDIRECT AUXIN: ; ; OK EXIT ; LXI H, @50FIDEMS ;MESSAGE STC RET ; ; JUMPBLOCK ; @30FIDEMS: JMP FIDCINIT ;+ 0 JMP FIDCISTATUS ;+ 3 JMP FIDCINPUT ;+ 6 JMP FIDCOSTATUS ;+ 9 JMP FIDCOUTPUT ;+ 12 JMP FIDCOSTATUS ;+ 15 : NO SPECIALS FOR MESSAGES JMP FIDCOUTPUT ;+ 18 : NO SPECIALS FOR MESSAGES ; ; CHARACTER DEVICE ENTRY ; @40FIDEMS: DB 'SERIAL' ;DEVICE NAME DB 0FH ;IN + OUT + BAUD + SERIAL DB 8 ;1200 BAUD (* ASSUMED LEGAL AT EMS *) ; ; MESSAGES ; @50FIDEMS: DB 'SERIAL v' DB (HIGH VERSION) + '0', '.', (LOW VERSION) + '0' DB ' installed', 0DH, 0AH, 0FFH ; @60FIDEMS: DB 'SERIAL not installed', 0DH, 0AH, 0FFH ; ;======== FIDCINIT: ;======== ; ; INITIALIZE CHARACTER DEVICE ACCORDING TO DEVICE TABLE ENTRY ; ; ENTRY: ; DE = ADDRESS OF DEVICE TABLE ENTRY ; EXIT: ; AF BC DE HL CORRUPT ; ALL OTHER REGISTERS PRESERVED ; LXI H, 7 DAD D ;POINT AT BAUD RATE IN THE DEVICE TABLE SHLD DEVTABBAUD ;RECORD THIS ADDRESS ; MOV H, M MOV L, H ;H = L = BAUDRATE FROM DEVICE TABLE ;;;; JMP CDSABAUD ;SET THE SPEED ; ;======== CDSABAUD: ;======== ; ; SET BOTH TX AND RX BAUDRATES ; ; ENTRY: ; H = ENCODED RX BAUDRATE ; L = ENCODED TX BAUDRATE ; EXIT: ; AF BC DE HL CORRUPT ; ALL OTHER REGISTERS PRESERVED ; MOV A, H ;IGNORE TX BAUDRATE ; CPI 6 JZ @10CDSABAUD ;JUMP IF 300 (WE ASSUME 1-7 LINKAGE) CPI 8 JZ @10CDSABAUD ;JUMP IF 1200 (OR INDEED 1200/75) CPI 12 JZ @10CDSABAUD ;JUMP IF 4800 ; LHLD DEVTABBAUD LDA BAUDRATE MOV M, A ;REJECT PROPOSED BAUDRATE & USE CURRENT ; @10CDSABAUD: STA BAUDRATE ;RECORD NEW BAUDRATE ; CALL CDSAPARAMS ;FETCH CURRENT STATE ;;;; JMP CDSAINIT ;RESET TO USE THE NEW SPEED ; ;======== CDSAINIT: ;======== ; ; CHANGE SERIAL PORT PARAMETERS ; ; THE SERIAL PORT CAN BE OPERATED IN TWO MODES ; ; NON-INTERRUPT, NON-HANDSHAKE MODE ; DTR ALWAYS TRUE ; FOR INPUT POLLS "DATA AVAILABLE" ; FOR OUTPUT POLLS "TX BUFFER EMPTY" ; NON-INTERRUPT, HANDSHAKE MODE ; FOR INPUT RAISES DTR ; POLLS "DATA AVAILABLE" ; DROPS DTR ; FOR OUTPUT POLLS FOR "TX ALL SENT" AND DSR ; ; ENTRY: ; A = - SELECTOR (YES.. MINUS) ; IF +SELECTOR #00..#7F => MODE CHANGE ; BIT 0 <=> HANDSHAKE ; BIT 1 <=> INTERRUPTS (IGNORED ON +3) ; BITS 2..7 = 0 ; D = STOP BITS 0 => 1, 1 => 1.5, 2 => 2 ; E = PARITY 0 => NONE, 1 => ODD, 2 => EVEN ; H = RX DATA BITS 5..8 ; L = TX DATA BITS 5..8 ; ; IF +SELECTOR #80..#FF => COMMAND ; #80 => DROP DTR ; #81 => RAISE DTR ; #82 => DROP RTS ; #83 => RAISE RTS ; ; EXIT: ; AF BC DE HL CORRUPT ; ALL OTHER REGISTERS PRESERVED ; CMA INR A ;A = -A = SELECTOR ; LXI B, SERIALCONTROL JM @50CDSAINIT ;JUMP IF COMMAND ; ; RECORD PARAMETERS ; ANI 1 ;PLUS3 CANNOT USE INTERRUPTS STA MODE ;SET MODE ; SHLD TXBITS ;STORE TX & RX BITS XCHG SHLD PARITY ;STORE PARITY & STOP BITS ; ; CALCULATE THE RX DATA BITS MASK ; XRA A @10CDSAINIT: STC RAL ;ADD ONE BIT PER LOOP DCR E JNZ @10CDSAINIT ;LOOP TILL MASK COMPLETE ; STA RXMASK ; ; RESET THE SERIAL DEVICE : WE DO NOT KNOW ITS STATE - ASSUME THE WORST ; XRA A DW OUTA ;PERHAPS MODE CONTROL DW OUTA ;PERHAPS SYNC1 DW OUTA ;PERHAPS SYNC2 MVI A, 60H ;NOT 40H - FTG DW OUTA ;RESET.. AND STATE IS CERTAIN ; ; CONSTRUCT MODE CONTROL : BITS 0&1 : CLOCK RATE ; LDA BAUDRATE ;FETCH BAUDRATE CPI 12 MVI E, 2 ;ASSUME X16 CLOCK JZ @20CDSAINIT ;JUMP IF 4800 BAUD INR E ;ELSE X64 CLOCK ; ; CONSTRUCT MODE CONTROL : BITS 2&3 : TX BITS (HOPEFULLY >= RX BITS) ; @20CDSAINIT: MOV A, D ;A = TX BITS SUI 5 ;5..8 -> 0..3 ADD A ADD A ;MOVE INTO BITS 2&3 ORA E MOV E, A ;E = CONTROL BYTE SO FAR ; ; BITS 4&5 : PARITY ; MOV A, L ;A = PARITY REQUEST ORA A JZ @30CDSAINIT ;JUMP IF PARITY = NONE ; MVI A, 10H ;ENABLE PARITY DCR L JZ @30CDSAINIT ;JUMP IF ODD PARITY ; ORI 20H ;EVEN PARITY ; @30CDSAINIT: ORA E MOV E, A ;E = CONTROL BYTE SO FAR ; ; BITS 7&8 : STOP BITS ; MOV A, H ;A = STOP BITS INR A RRC RRC ;MOVE INTO BITS 6&7 ORA E ;CONSTRUCT THE COMPLETE BYTE ; DW OUTA ;SEND THE CONTROL BYTE ; ; RAISE DTR UNLESS FOR NON-INTERRUPT HANDSHAKE MODE ; LDA MODE ORA A JZ @40CDSAINIT ;JUMP IF NON-INTERRUPT, HANDSHAKE (A=0) ; MVI A, 02H ;ALL OTHER MODES SET DTR @40CDSAINIT: ORI 35H ;ENABLE TX & RX, ERROR RESET (NOT 15H - FTG) DW OUTA ;SEND IT ; RET ; ; COMMAND #80 DROP DTR ; #81 RAISE DTR ; @50CDSAINIT: ANI 7FH JZ DROPDTR ;JUMP IF DROP DTR ; DCR A RNZ ;IGNORE ALL OTHER COMMANDS ;;;; JMP RAISEDTR ;ELSE RAISE DTR ; ;======== RAISEDTR: ;======== ; ; RAISE DTR SIGNAL ; ; ENTRY: ; BC = USART CONTROL PORT ; EXIT: ; AF CORRUPT ; ALL OTHER REGISTERS PRESERVED ; MVI A, 27H ;SET DTR + ENABLE TX & RX (NOT 07H - FTG) DW OUTA ;SEND IT RET ; ;========== CDSAPARAMS: ;========== ; ; GET DEVICE PARAMETERS ; ; ENTRY: ; NO CONDITIONS ; EXIT: ; A = MODE #00 => NON-INTERRUPTS, NON-HANDSHAKE ; #FF => NON-INTERRUPTS, HANDSHAKE ; B = RX ENCODED BAUD RATE ; C = TX ENCODED BAUD RATE ; D = STOP BITS 0 => 1, 1 => 1.5, 2 => 2 ; E = PARITY 0 => NONE, 1 => ODD, 2 => EVEN ; H = RX DATA BITS 5..8 ; L = TX DATA BITS 5..8 ; F CORRUPT ; ALL OTHER REGISTERS PRESERVED ; LDA BAUDRATE MOV C, A ;C = TX BAUDRATE MOV B, A ;B = RX BAUDRATE ; LHLD PARITY XCHG ;E = PARITY, D = STOP BITS ; LHLD TXBITS ;L = TX BITS, H = RX BITS ; LDA MODE CMA INR A ;A = MODE RET ; ;=========== FIDCISTATUS: ;=========== ; ; TEST IF CHARACTER AVAILABLE FOR INPUT ; ; ENTRY: ; B = DEVICE NUMBER ; DE = ADDRESS OF DEVICE TABLE ENTRY ; EXIT: ; IF CHARACTER AVAILABLE ; CARRY TRUE ; IF NO CHARACTER AVAILABLE ; CARRY FALSE ; ALWAYS ; OTHER FLAGS A BC DE HL CORRUPT ; ALL OTHER REGISTERS PRESERVED ; LXI B, SERIALCONTROL ; LDA MODE ORA A JZ @10FIDCISTATUS ;NON-INTERRUPT, NON-HANDSHAKE ; ; HANDSHAKE : IS THERE A CHARACTER ALREADY THERE? ; DW INTOA ANI 02H STC RNZ ;EXIT (+C) IF GOT ONE ; CALL RAISEDTR ;ELSE TRY COAXING ; ; LOOK FOR A CHARACTER? ; @10FIDCISTATUS: DW INTOA ANI 02H RZ ;EXIT IF NO CHARACTER ; STC ;+C => CHARACTER RET ; ;========= FIDCINPUT: ;========= ; ; INPUT NEXT CHARACTER ; ; ENTRY: ; NO CONDITIONS ; EXIT: ; A = CHARACTER ; F BC DE HL CORRUPT ; ALL OTHER REGISTERS PRESERVED ; LXI B, SERIALCONTROL ; LDA MODE ORA A JZ AWAITCHAR ;JUMP IF NON-HANDSHAKE ; ; HANDSHAKE MODE ; DW INTOA ANI 02H CZ RAISEDTR ;RAISE DTR IF NO DATA ; CALL AWAITCHAR ;NOW AWAIT CHARACTER ARRIVAL ;;;; JMP DROP DTR ;DROP DTR AGAIN ; ;======= DROPDTR: ;======= ; ; LOWER THE DTR SIGNAL ; ; ENTRY: ; BC = USART CONTROL PORT ; EXIT: ; F CORRUPT ; ALL OTHER REGISTERS PRESERVED ; PUSH PSW ; MVI A, 25H ;ENABLE TX & RX (NOT 05H - FTG) DW OUTA ;SEND IT ; POP PSW RET ; ;========= AWAITCHAR: ;========= ; ; AWAIT CHARACTER FROM SERIAL PORT ; FETCH IT AND MASK IT TO THE EXPECTED NUMBER OF RX BITS ; ; ENTRY: ; BC = USART CONTROL PORT ; EXIT: ; A = CHARACTER ; F HL CORRUPT ; ALL OTHER REGISTERS PRESERVED ; PUSH B ; @10AWAITCHAR: DW INTOA ANI 02H JZ @10AWAITCHAR ;LOOP IF NO DATA ; LXI B, SERIALDATA DW INTOA ;FETCH CHARACTER ; LXI H, RXMASK ANA M ;MASK TO RANGE ; POP B RET ; ;========== FIDCOUTPUT: ;========== ; ; OUTPUT THE NEXT CHARACTER ; ; ENTRY: ; C = CHARACTER ; EXIT: ; AF BC DE HL CORRUPT ; ALL OTHER REGISTERS PRESERVED ; MOV E, C ;E = CHARACTER ; @10FIDCOUTPUT: CALL FIDCOSTATUS JNC @10FIDCOUTPUT ;LOOP IF NOT READY ; LXI B, SERIALDATA DW OUTE ;SEND CHARACTER ; RET ; ;=========== FIDCOSTATUS: ;=========== ; ; ARE WE READY FOR OUTPUT? ; ; ENTRY: ; NO CONDITIONS ; EXIT: ; IF READY TO OUTPUT ; CARRY TRUE ; IF NOT READY ; CARRY FALSE ; ALWAYS ; OTHER FLAGS A BC DE HL CORRUPT ; ALL OTHER REGISTERS PRESERVED ; LXI B, SERIALCONTROL ; LDA MODE ORA A JNZ @10FIDCOSTATUS ;JUMP IF HANDSHAKE MODE ; ; NON-HANDSHAKE: TEST TX BUFFER EMPTY ; DW INTOA ;IN A, (C) RAR ;CARRY TRUE IF TRANSMIT BUFFER EMPTY RET ; ; HANDSHAKE: TEST "ALL SENT" AND DSR ; @10FIDCOSTATUS: DW INTOA ;IN A, (C) ANI 85H XRI 85H RNZ ;EXIT IF NOT READY ; STC ;ELSE OUTPUT WILL BE POSSIBLE RET ; END