title Qdart (Resident in the IOP) entry qdinit entry qdsetup, qdshut entry qdhxstat entry qd_dtr, qd_xdtr, qdsmodm, qdgmodm entry qd_cts? entry qdunmtbe, qdunmrda, qdmskrda, qdmsktbe entry qdenbrx, qddisrx entry qdressio, qdrestbe entry qdsetspeed entry qdtbreak, qdbreak, qdjoin ext timerint ext chget, chdiscrd, chnumber, MXSTRUC ext iotime ext ioaddint, iosubint ext zero, addhla, chgstate list nogen, nocond *maclib macros.z80 *include jsysequ.z80 *include headdef.z80 *include queuedef.z80 *include modeequ.z80 qdbase equ 40H ; base port address for the 16 Quadart channels ; ----------------------------------------------------------------------------- subttl Qdart Devices ; /* array of 16 pointers to the quadart device structures, ; indexed by the minor device number. ; used in open routines to tell whether a particular quadart is being used. ; (there are 16 possible quadart addresses.) ; */ NSIOPRTS equ 16 ; Number of Quadart serial ports per IOP ; (must be a power of 2) qdarts: ; quadart structure pointers (indexed by minor dev no.) rept NSIOPRTS db UNUSED ; space for a pointer to a channel db UNUSED mend rept UNUSED equ 0FFH qdfind: ; get pointer to a quadart's structure ; (indexed by minor device number). ; call with: a = minor device number ; returns with: hl = &qdarts[dn&00FH] ; and with cy-flag set if the quadart serial ; port is in use ; alters: af, hl and a,NSIOPRTS-1 ; strip flags from minor device number add a,a ; minor device number times 2 ld hl,qdarts + 1 call addhla ; index into the array ld a,(hl) ; does the quadart point dec hl ; to a channel? and a,(hl) cp a,UNUSED ; (compare to 0ffh) ret qddiscrd: ; discard quadart ; (in the quadart array indexed by minor device number) ; call with: a = minor device number ; alters: af and a,NSIOPRTS-1 ; strip flags from minor device number add a,a ; minor device number times 2 push hl ld hl,qdarts + 1 call addhla ; index into the array ld (hl),UNUSED ; mark the quadart's channel dec hl ; pointer unused ld (hl),UNUSED pop hl ret ; ----------------------------------------------------------------------------- subttl Serial Quadart Device Set Up qdsetup: ; set up quadart serial device ; call with: interrupts disabled ; de = device number ; bc = addr of input interrupt routine ; hl = addr of output interrupt routine ; ix = addr of EXT/STAT (modem) interrupt routine ; iy -> the device command table ; returns: cy set and a = error number if illegal device, ; illegal number, quadart unavailable, etc. ; otherwise: ; de and iy -> device structure ; if first open, z-flag is set, and ; b = iop channel number ; alters: af, bc, de, hl ,iy push de push hl ld a,e ; get minor device number push af ; and also save it call qdfind ; find quadart. hl -> qdarts[dn&00FH] jr c,su10 ; does it point to a channel already? push bc push iy ; no pop bc ; bc -> the command table call chget ; get a channel pop bc jr c,suerrx1 ; channel available? push de ; yes. hl -> iop channel pop iy ; de and iy -> device structure pop af ; a = minor device number ex (sp),hl ; hl = addr of output interrupt routine ; (sp) = addr of iop channel call qdasints ; setup quadart async serial interrupts jr c,suerrx4 ; was there room for the interrupts? ld bc,MXSTRUC ; get the maximum structure length ld b,c call zero ; zero the entire structure pop bc ; bc -> iop channel pop hl push hl ld a,l ; yes. minor dev number again call qdfind ; find the quadart again ld (hl),c ; hl -> this quadart (qdarts[dn&00FH]) inc hl ld (hl),b ; qdarts[dn&00FH] -> the iop channel pop hl ; device number ld (iy+devno),l ld (iy+devno+1),h ld a,l ; minor dev number call qddport ; a = data port number ld (iy+dataport),a ;; push bc ;; ld c,a ; data port number ;; ld a,l ; minor device number ;; call qdunmrda ; unmask RX (RDA) ;; ld a,l ; ;; call qdunmtbe ; & TX (TBE) interrupts ;; pop hl ; hl -> iop channel ld l,c ld h,b ; hl -> IOP channel call chnumber ld b,a ; b = IOP channel number xor a,a ; set z-flag ret su10: ; the quadart device has already been loaded ld e,(hl) inc hl ld d,(hl) ex de,hl ; hl -> iop channel ld e,(hl) inc hl ld d,(hl) push de pop iy ; de and iy -> device structure pop bc ; clean stack pop bc pop bc ; bc = device number ld l,(iy+devno) ld h,(iy+devno+1) xor a,a sbc hl,bc ; same device number? jr nz,suerrx3 dec a ; yes. (cy is reset.) reset z-flag. ret suerrx1: pop bc ; clean stack pop bc sux2: pop bc suerrx3:ld a,?devopen scf ret suerrx4: pop hl ; hl -> iop channel call chnumber ; a = iop channel number call chdiscrd ; discard the channel jr sux2 ; ----------------------------------------------------------------------------- subttl Shup Down Quadart Device qdshut: ; shut down quadart ; call with: interrupts disabled ; de and iy -> device structure ; a = iop channel number ; alters: af call chdiscrd ; discard the iop channel push bc ld b,(iy+devno) ; minor device number ld a,b ; minor device number call qddiscrd ; discard the quadart ld c,(iy+dataport) ; data port number ld a,b ; minor device number call qdmskrda ; mask out the RDA interrupt ld a,b call qdmsktbe ; & the TBE interrupt ld a,b call qdmskext ; & the EXT/STAT interrupts ld a,b call qdasxints ; remove the interrupts from the Ipage xor a,a ; clear cy and ld (iy+devno+1),a ; zero the stored major device number pop bc ret ; ----------------------------------------------------------------------------- subttl Data Port Address qddport: ; call with: a = minor device number ; returns: a = SIO data port address and a,NSIOPRTS-1 ; strip flags from minor device number push hl ld hl,sioports call addhla ld a,(hl) ; data port address pop hl ret ; ----------------------------------------------------------------------------- subttl Interrupt Vectors tbevec: ; call with: a = minor device number ; returns with: a = TBE interrupt vector and a,NSIOPRTS-1 ; strip flags from minor device number push hl ld hl,intvecs call addhla ld a,(hl) pop hl ret xstatvec: ; call with: a = minor device number ; returns: a = EXT/STAT interrupt vector call tbevec add a,2 ret rdavec: ; call with: a = minor device number ; returns with: a = RDA interrupt vector call tbevec add a,4 ret ovrrnvec: ; call with: a = minor device number ; returns with: a = overrun interrupt vector call tbevec add a,6 ret ; ----------------------------------------------------------------------------- subttl Interrupt Set Up qdasints: ; add asynchronous serial interrupts for a quadart channel ; to the Ipage ; call with: interrupts disabled ; bc -> input interrupt routine ; hl -> output interrupt routine ; ix -> EXT/STAT interrupt routine ; de -> device's data structure ; a = minor device number di ; (being cautious) push bc push de push af call rdavec ; a = RX (RDA) interrupt vector call ioaddint ; add the interrupt to the Ipage jr c,inerrx ; was there room? pop af ; yes push af call tbevec ; a = TX (TBE) interrupt vector ld c,l ld b,h ; bc -> output interrupt routine call ioaddint jr c,inerrx2 ; room? pop af ; yes. minor device number again push af call ovrrnvec ; a = overrun interrupt vector ld bc,overrun call ioaddint jr c,inerrx3 ; room? pop af ; yes push af call xstatvec ; a = EXT/STAT interrupt vector push ix pop bc ; bc -> EXT/STAT interrupt routine call ioaddint jr c,inerrx4 ; room? pop af and a,a inx: pop de pop bc ret inerrx4: pop af ; remove the RDA and TBE interrupts call xrdatbeovr scf jr inx inerrx3: pop af ; remove the RDA and TBE interrupts call xrdatbe scf jr inx inerrx2: pop af ; remove the RDA interrupt call xrda scf jr inx inerrx: pop af scf jr inx overrun: ; reset the overrun error flag (SIO special receive condition) ; called by an interrupt with: ; de -> a device data structure which uses the standard heading push de pop iy ld c,(iy+dataport) ; SIO data port number inc c ; SIO control port number ld a,cERRRESET|WR0 ; reset the error flag out (c),a ret qdhxstat: ; Handles EXT/STAT interrupts. ; a change in the CTS or the DCD lines and also a change ; from break to non-break or vice versa will cause ; an interrupt which goes to the driver. (however, ; this routine ignores breaks.) the driver calls ; this subroutine to handle the interrupt. ; ; call with: de and iy -> a device data structure ; which uses the standard heading ; l = the previous value of EXT/STAT ; ; returns: h = the new value of EXT/STAT ; cy & z-flag set if CTS & DCD are both on ; cy set but z-flag reset CTS & DCD are both off ; cy reset, otherwise ; ; alters: af, bc, hl ld c,(iy+dataport) ; SIO data port number call getxstat ; get EXT/STAT ld h,a ; and save it in h-reg. ;; and a,a ; clear cy ;; bit BREAK_RR0,h ; break or abort? ;; jr nz,resxstat ; if so, ignore it bit DCD_RR0,h ; no. DCD off? call z,qddisrx ; if so, disable receiver ld a,h xor a,l ; compare to previous EXT/STAT and a,^DCD_RR0|^CTS_RR0 ; if neither DCD nor CTS has changed, jr z,resxstat ; done ld a,h ; either DCD or CTS changed, or both and a,^DCD_RR0|^CTS_RR0 jr z,xh6 ; if both DCD & CTS are off, hangup jp po,resxstat ; if one is on & one off, done call qdenbrx ; both are on. enable receiver call resxstat ; reset xstat xor a,a ; report that CTS & DCD are both on scf ret xh6: ; both DCD and CTS are off call resxstat ; reset xstat xor a,a ; report that modem has hung up inc a scf ret resxstat: ; reset the EXT/STAT interrupt bits inc c ; SIO control port number ld a,cRESXSTAT|WR0 ; reset the EXT/STAT bits out (c),a dec c ret ; ----------------------------------------------------------------------------- subttl Interrupt Removal qdasxints: ; remove the asynchronous serial i/o interrupts from the Ipage ; for a quadart channel ; call with: interrupts disabled ; a = minor device number di ; (being cautious) push af ; remove the EXT/STAT interrupt call xstatvec call iosubint pop af xrdatbeovr: ; a = minor device number push af ; remove the overrun interrupt call ovrrnvec call iosubint pop af xrdatbe: ; a = minor device number push af ; remove the TBE interrupt call tbevec call iosubint pop af xrda: ; a = minor device number push af ; remove the RDA interrupt call rdavec call iosubint pop af and a,a ret ; ----------------------------------------------------------------------------- subttl Interrupt Masking siomsktbl: ; storage for the SIO interrupt masks ; indexed by the minor device number (ANDed with 00FH). ds NSIOPRTS qdunmask: ; unmask interrupts using a given mask ; call with: interrupts disabled ; a = minor device number ; c = SIO data port address ; b = the mask push bc jr unm2 qdunmext: ; unmask external interrupts (DCD, CTS, break) ; call with: interrupts disabled ; a = minor device number ; c = SIO data port address push bc ld b,EXTINTS ; external interrupts mask jr unm2 qdunmtbe: ; unmask TBE interrupts ; call with: interrupts disabled ; a = minor device number ; c = SIO data port address push bc ld b,TXINT ; TBE mask jr unm2 qdunmrda: ; unmask RDA interrupts ; call with: interrupts disabled ; a = minor device number ; c = SIO data port address push bc ld b,RXINT ; RDA mask unm2: di ; (being cautious) push hl and a,NSIOPRTS-1 ; strip flags from minor device number ld hl,siomsktbl call addhla ; hl -> device's interrupt mask store inc c ; c = SIO control port address ld a,b ; get the interrupt mask to be added or a,(hl) ; add the bits from the store ld (hl),a ; update the mask ld a,cNULL|WR1 ; point to WR1 out (c),a outi ; output the updated mask pop hl pop bc ret qdmskext: ; mask external interrupts (DCD, CTS, break) ; call with: interrupts disabled ; a = minor device number ; c = SIO data port address push bc ld b,EXTINTS ; external interrupts mask jr msk2 qdmsktbe: ; mask TBE interrupts ; call with: interrupts disabled ; a = minor device number ; c = SIO data port address push bc ld b,TXINT ; TBE mask jr msk2 qdmskrda: ; mask RDA interrupts ; call with: interrupts disabled ; a = minor device number ; c = SIO data port address push bc ld b,RXINT ; RDA mask msk2: di push hl and a,NSIOPRTS-1 ; strip flags from minor dev. no. ld hl,siomsktbl call addhla ; hl -> device's interrupt mask store inc c ; c = SIO control port address ld a,b ; get the mask to be removed cpl and a,(hl) ; subtract from the bits in the store ld (hl),a ; update the mask ld a,cNULL|WR1 ; point to WR1 out (c),a outi ; output the updated mask pop hl pop bc ret qdrestbe: ; reset the TBE-interrupt-pending ; until the next character is output to the transmitter ; call with: interrupts disabled ; c = SIO data port address inc c ; SIO control port address ld a,cRESTBE|WR0 out (c),a dec c ret ; ----------------------------------------------------------------------------- subttl Modem Control modemmsks: ; Storage for the SIO WR5 masks ; Indexed by the minor device number (ANDed with 00FH). rept NSIOPRTS db 0 mend qdsmodm: ; Change modem lines ; Call with: Interrupts disabled ; IY -> data structure using standard header ; C = mask for which bits are to be changed ; B = mask for the direction these bits are to be changed ; Returns: A = previous state of these bits ; CY-flag reset di ; (being cautious) save bc,hl ld a,(iy+devno) ; Minor device number and a,NSIOPRTS-1 ; Strip flags from it ld hl,modemmsks call addhla ; HL -> modem mask store call chgstate ; Change the mask state according to BC ld a,b ; Resultant state and a,not .TXNONO ; Disallow the setting of certain bits ld b,(hl) ; Get the previous mask state ld (hl),a ; Update the mask state ld c,(iy+dataport) inc c ; Control port address ld h,cNULL|WR5 out (c),h out (c),a ; Output mask ld a,b ; Return the previous mask restor bc,hl ret qdbreak: ; Break the data line to a serial device. ; Call with: interrupts disabled push bc ; Start a break ld bc,[.BREAK]*100H+[.BREAK] jr qmd2 qdjoin: ; Re-join the line to a serial device ; call with: interrupts disabled ; c = SIO data port address push bc ; Stop the break ld bc,[0]*100H+[.BREAK] jr qmd2 qd_dtr: ; Set DTR so the modem will work. ; (called when the baudrate is set, among other times) ; Call with: interrupts disabled push bc ; Turn on DTR and TXENABLE ld bc,[.DTR|.TXENABLE]*100H+[.DTR|.TXENABLE] qmd2: call qdsmodm pop bc ret qd_xdtr: ; after device is finally closed, cause the modem ; to hang up by resetting DTR for at least 50 MS. ; (called during .CLOSE & .SETMODE system calls. ; cannot be called by an interrupt.) ; call with: c = SIO data port address ; a = minor device number ; returns: interrupts enabled di call qdmskext ; mask external interrupts push bc ld b,15 ; BREAK for 1.5 seconds call qdtbreak ;; ei ;; ld a,2 ; wait for 0.2 seconds ;; call iotime ;; di ld bc,[0]*100h+[.DTR] ; Turn DTR off call qdsmodm pop bc ei ld a,40 ; wait for 4.0 seconds jp iotime qd_cts? ; clear to send? ; call with: interrupts disabled ; c = SIO data port number ; a = minor device number ; returns: cy set if not clear to send call qdunmext ; unmask external interrupts call getxstat and a,^CTS_RR0 ret nz scf ret getxstat: ; get EXT/STAT bits ; call with: c = SIO data port number ; returns: a = EXT/STAT bits (RR0) inc c ; SIO control port number in a,(c) ; read RR0 dec c ret qdgmodm:; Get CTS and DCD (EXT/STAT) and DSR and RI modem lines ; Call with: C = SIO data port number ; Returns: HL = state of CTS, DCD, DSR, and RI call getxstat ; A = state of CTS and DCD push af save bc,de call find_pio ; C = PIO control port number dec c ; C = PIO data port number in a,(c) jr nc,qgm2 ; Odd PIO channel (PIO1 or PIO3)? rlca ; Yes. Shift the bits to the rlca ; upper nibble where they belong rlca rlca qgm2: ld l,a ; L = state of DSR and RI restor bc,de pop af ld h,a ; H = state of CTS and DCD ret ; ----------------------------------------------------------------------------- subttl Timed break qdtbreak:; Send a timed break ; Call with: interrupts disabled ; c = SIO data port number ; b = number of tenths of seconds to break ; Returns: CY flag set if IOTIME was aborted by a signal ; call qddisrx ; disable receiver call qdbreak ; send a break to the 3102 ld a,b ; suspend the iop process call iotime ; for (B) tenths of a second push af ; call qdjoin ; & then turn BREAK off call qdenbrx ; enable receiver pop af ; return CY flag from IOTIME ret ; ----------------------------------------------------------------------------- subttl Set Timer qdtimer: ; Set the timer system on quadart 0 ; to interrupt every 16*250*100/4000,000 or 0.100 seconds ; ; Call with: interrupts disabled ; Returns with: a = the timer interrupt vector ; Alters: af, c di ; (being cautious) ld a,TMINTVEC push af ; send the timer interrupt vector out (QD0CTC1),a ; to CTC1, the 2nd CTC on quadart 0 ld c,QD0TMB ; timer B on quadart 0 ld a,^TMCONST|^RESET|^CONTROL out (c),a ; set timer mode, prescale 16 ld a,250 out (c),a ; send the time constant ld c,QD0TMC ; timer C on quadart 0 ld a,^INTENAB|^COUNTER|^TMCONST|^RESET|^CONTROL out (c),a ; set counter mode, interrupt ld a,100 out (c),a ; send the time constant pop af ; a = timer interrupt vector ld bc,timerint ; addr of timer interrupt routine jp ioaddint ; add the timer interrupt to the ipage ; ----------------------------------------------------------------------------- subttl Set Speed qdsetspeed: ; set speed (baudrate) ; call with: a = minor device number ; b = Cromix speed code ; returns with: cy set if illegal speed code ; interrupts enabled push bc push de push hl and a,NSIOPRTS-1 ; strip flags from minor device number ld e,a ; store the result in E ld a,b ; Cromix speed code cp a,NSPEEDS ; is it out of range? jr nc,sserrx ld hl,speedtbl ; no call addhla ld a,(hl) ; get the CTC time constant and a,a ; zero? jr z,sserrx ld d,a ; no. save it ld a,e ; index from the minor device number ld hl,sioports call addhla ld c,(hl) ; c = data port address di ld a,b cp a,S_HANGUP ; hang up? jr z,sshangup call sioinit ; no. initialize the SIO channel call qd_dtr ; turn DTR on call hibaud? ; turn on the aux EIA line for high ld a,e ; baud, turn it off for low baud. call qdunmrda ; unmask RX (RDA) ld a,e ; call qdunmtbe ; & TX (TBE) interrupts ld a,e ; index from the minor device number ld hl,ctcports call addhla ; hl -> CTC port number call setctc ; set CTC time constant (in d-reg) ss8: ei and a,a ; to get the desired baudrate. ssx: pop hl pop de pop bc ret sserrx: ld a,?badvalue scf jr ssx sshangup: ld b,0 ; turn off the high speed EIA line call hibaud? jr ss8 setctc: ; set the CTC time constant ; call with: hl -> CTC port number ; d = CTC time constant ; alters: af, c ld c,(hl) ; c = CTC port number ld a,SPEEDCMD out (c),a ; command to set time constant out (c),d ; value of CTC time constant ret ; ----------------------------------------------------------------------------- subttl SIO Channel Initialization qdressio: ; reset a given quadart sio channel ; call with: interrupts disabled ; c = SIO data port address ; After calling this subroutine, all of the SIO control ; registers must be reloaded before the channel can ; be used again. This is done by calling SIOINIT. di push hl push bc inc c ; control port address ld a,cCHRESET|WR0 ; reset the channel out (c),a ; calculate the control port address for channel B in the same SIO. ld a,c ; control port address for this channel or a,BPORT_OR ; control port address for channel B ld c,a ; in the same SIO ; send channel B its interrupt vector. ld a,cNULL|WR2 ; select Write Register 2 out (c),a ; in channel B ld a,c dec a ; channel B's data port address call minordn ; a = minor device number of channel B ld hl,intvecs call addhla ; hl -> the corresponding int vector outi ; send it to channel B pop bc pop hl ret sioinit: ; initialize an SIO channel ; call with: interrupts disabled ; c = SIO data port address ; b = Cromix speed (baudrate) code ; returns: with transmitter & receiver enabled save bc inc c ; SIO control port number ld a,cNULL|WR4 ; point to Write Register 4 out (c),a ld a,b ; speed code cp a,S_110 ; 110 baud? ld a,X16|NOPAR|STOPS1 ; if not, use 1 stop bit, jr nz,si2 ; x16 clock, and no parity. ld a,X16|NOPAR|STOPS2 ; yes. use 2 stop bits, etc. si2: out (c),a dec c push bc ld bc,[.TXENABLE]*100h+.TXENABLE call qdsmodm ; Enable transmitter pop bc call qdenbrx ; Enable receiver ld a,c call minordn ; a = minor device number ld b,0 ; output the stored interrupt mask, call qdunmask ; without adding anything to it restor bc ret qdenbrx: ; enable receiver ; call with: c = SIO data port number ld a,RXENABLE rx2: inc c push af ld a,cNULL|WR3 ; point to Write Register 3 out (c),a pop af out (c),a dec c ; SIO data port number ret qddisrx: ; disable receiver ; call with: c = SIO data port number ld a,RXDISABLE jr rx2 minordn: ; calculate the minor device number ; call with: a = data port address ; returns: a = minor device number (modulo 10H) push bc push hl ld hl,sioports ; find the data port ld bc,NSIOPRTS ; in the Quadart SIO cpir ; port table ld a,NSIOPRTS - 1 sub a,c ; a = the index into this table pop hl pop bc ret ; ----------------------------------------------------------------------------- subttl PIO Initialization pioinit: ; call with: c = PIO control port number ; e = a byte which specifies the auxilliary EIA ; CY lines (see notCY02 and notCY13) ; d = a byte which specifies the PIO interrupt ; status (see no_ints) ld a,defbits ; define the i/o bit directions out (c),a ld a,iobits out (c),a dec c out (c),e ; CY word sent to lower port # inc c out (c),d ret hibaud?: ; set the PIO auxilliary EIA line (which is called CY) for high ; baud, reset it for low baud ; call with: interrupts disabled ; c = SIO data port number ; b = Cromix speed code save bc,de,hl call find_pio ; hl -> old CY mask, e = new mask, ; c = PIO port number, etc. ld a,b ; speed code cp a,S_1200 ; 1200 baud or higher? ld a,e ; a = new CY mask jr c,au2 ; yes. turn on the PIO auxilliary EIA line cpl ; invert because 0 means turn CY on and a,(hl) ; turn on this channel's CY jr au4 au2: ; no. turn off the PIO auxilliary EIA line or a,(hl) ; turn off this channel's CY au4: ld (hl),a ld e,a ; send it to PIOINIT call pioinit restor bc,de,hl ret find_pio: ; call with: interrupts disabled ; c = SIO data port number ; returns: hl -> the current CY mask for the channel pair ; c = PIO control port number ; d = NO_INTS (for use of PIOINIT) ; e = this channel's new CY mask (for PIOINIT) ; CY-flag set if odd PIO channel (PIO1 or PIO3) ld a,c call minordn ; a = minor device number (modulo 10H) and a,a ; clear carry-flag rra ; index = device number / 2 ld c,a ; save the index ld de,no_ints<<8 + ^notCY02; no PIO interrupts jr nc,cy2 ; even device number (channel no.)? ld e,^notCY13 ; no, odd cy2: push af ; Save CY-flag ld hl,pioports call addhla ld a,c ; get index into PIOLIST ld c,(hl) ; this channel's PIO control port no. ld hl,piolist call addhla ; hl -> store for the notCY flags for pop af ; a pair of channels ret ; ----------------------------------------------------------------------------- subttl Quadart Initialization qdiniflg: db 0 ; the quadart interface has not been ; initialized yet. qdinit: ; initialize the timer & all 16 quadart serial channels ; call with: interrupts disabled ; alters: af, bc, de, hl, ix ld hl,qdiniflg ; have the four quadarts attached ld a,(hl) ; to this IOP ever been initialized? and a,a ret nz ld (hl),-1 ; not yet call qdtimer ; initialize the timer xor a,a ; start with minor device number 0 ld b,NSIOPRTS ; number of quadart ports (per iop) in1: push af call qddiscrd ; mark the quadart port free pop af inc a djnz in1 ; initialize the SIO interrupt mask storage area ld hl,siomsktbl ld b,NSIOPRTS in2: ld (hl),INITIMSK ; initialize the interrupt mask inc hl ; storage area djnz in2 ; initialize the PIOs ld ix,pioports ; list of PIO control ports ld b,NPIOPRTS ; number of ports ld de,no_ints<<8 + no_cys ; no CYs & no PIO interrupts in4: ld c,(ix+00) ; get port address call pioinit inc ix ; point to next port djnz in4 ; initialize the storage for the PIO CY masks ld hl,piolist ld b,NPIOPRTS in5: ld (hl),e inc hl djnz in5 ; initialize all SIO channels on all quadarts ld hl,sioports ld b,NSIOPRTS in6: ld c,(hl) ; get a channel's data port number inc hl call qdressio ; reset the sio channel call resxstat ; reset EXT/STAT interrupts push bc ld b,S_9600 ; 1 stop bit (rather than 2) call sioinit ; initialize SIO pop bc djnz in6 ; initialize all CTCs for 9600 baud ld hl,ctcports ld b,NCTCPRTS ld a,(speedtbl + S_9600) ; CTC time constant for 9600 baud ld d,a in7: call setctc ; set the CTC inc hl djnz in7 ; initialize all loopback ports on all quadarts ld hl,loopprts ; list of loopback ports ld b,NLOOPPRTS ; number of ports xor a,a in8: ld c,(hl) out (c),a inc hl djnz in8 ret ; ----------------------------------------------------------------------------- subttl Quadart Port Addresses and Data Constants pioports: ; PIO control port addresses db qdbase+09H,qdbase+0BH ; quadart 0 db qdbase+29H,qdbase+2BH ; quadart 1 db qdbase+49H,qdbase+4BH ; quadart 2 db qdbase+69H,qdbase+6BH ; quadart 3 NPIOPRTS equ $-pioports piolist: ; storage for PIO CY masks for pairs of channels ds NPIOPRTS ; control bits output to PIO data ports (one output handles channels 0 and 1 ; another output handles channels 2 and 3) notCY02 equ 5 ; not auxilliary EIA output, quadart channel 0 or 2 notCY13 equ 1 ; not " , " " 1 or 3 EXTCK02 equ 4 ; external clock, quadart channel 0 or 2 EXTCK13 equ 0 ; " " , " " 1 or 3 ; control bits output to PIO command ports ENABINTS equ 7 ; enable PIO interrupts ; PIO initialization words defbits equ 11001111B ; next word will define i/o bit directions iobits equ 11001100B ; i/o bit directions (1=input, 0=output) no_cys equ ^notCY02|^notCY13; output to PIO data port no_ints equ (0&^ENABINTS)|0111B; this word defines the interrupt status sioports: ; SIO data port addresses db qdbase+00H,qdbase+02H,qdbase+04H,qdbase+06H ; quadart 0 db qdbase+20H,qdbase+22H,qdbase+24H,qdbase+26H ; quadart 1 db qdbase+40H,qdbase+42H,qdbase+44H,qdbase+46H ; quadart 2 db qdbase+60H,qdbase+62H,qdbase+64H,qdbase+66H ; quadart 3 if $-sioports <> NSIOPRTS mnote NSIOPRTS should be the number of SIO ports. It isn't. endif ; mask to logical-OR to any SIO port address in order to obtain ; the corresponding port address for channel B in the same SIO BPORT_OR: equ 02H intvecs: ; List of interrupt base (TBE) vectors ; in the order of the minor device numbers, ; viz., SIO channel A before SIO channel B. ; Each quadart has 2 SIO chips, and each SIO ; chip has a channel A and a channel B. ; (Channel B of each SIO is loaded with its vector by ; the routine QDRESSIO, but channel A is not, nor need it be.) db 08H,00H,18H,10H ; quadart 0 db 28H,20H,38H,30H ; quadart 1 db 48H,40H,58H,50H ; quadart 2 db 68H,60H,78H,70H ; quadart 3 loopprts: ; Loopback port addresses db qdbase+14H ; quadart 0 db qdbase+34H ; quadart 1 db qdbase+54H ; quadart 2 db qdbase+74H ; quadart 3 NLOOPPRTS equ $-loopprts ; ----------------------------------------------------------------------------- ; sio definitions ; WR0: the command register cNULL equ ^3*000B ; Null command cRESXSTAT equ ^3*010B ; Reset external/status interrupts cCHRESET equ ^3*011B ; Channel reset cRESTBE equ ^3*101B ; Reset TBE interrupt pending cERRRESET equ ^3*110B ; Reset parity & overrun error conditions ; pointers sent to WR0 to in order to address write registers & read registers WR0 equ ^0*000B WR1 equ ^0*001B WR2 equ ^0*010B WR3 equ ^0*011B WR4 equ ^0*100B WR5 equ ^0*101B ; (read RR0 without first sending an address byte) RR1 equ ^0*001B ; WR1: contains the control bits for the various interrupt & WAIT/READY modes RXnoINT equ ^3*00B ; Disable interrupts on rec'd char RXINTP equ ^3*10B ; Interrupt on all received characters ; (parity affects vector) RXINT equ ^3*11B ; Interrupt on all received characters ; (parity does not affect vector) INTVECS4 equ ^2 ; 4 different (vs. 1) interrupt vectors TXINT equ ^1 ; Transmitter interrupt enable EXTINTS equ ^0 ; External interrupts (DCD, CTS, break) INITIMSK equ INTVECS4 ; Initial interrupt mask ; WR2: interrupt vector (sent only to channel B of each SIO) ; WR4: receiver & transmitter parameters (must be sent before writing ; to WR1, WR3, WR5, WR6, WR7) X1 equ ^6*00B ; clock rate = data rate x 1 X16 equ ^6*01B X32 equ ^6*10B X64 equ ^6*11B STOPS1 equ ^2*01B ; 1 stop bit STOPS2 equ ^2*11B ; 2 stop bits NOPAR equ ^0*00B ; no parity ODDPAR equ ^0*01B ; odd parity EVENPAR equ ^0*11B ; even parity ; WR5: transmitter parameters (bit-2 is for receiver & transmitter) .RTS equ ^1 ; enable Request To Send pin .TX8BITS equ ^5*11B ; 8 bits per transmitted character .TXENBL equ ^3 ; enable transmitter .BREAK equ ^4 ; send break .DTR equ ^7 ; enable Data Terminal Ready pin .TXENABLE equ .TX8BITS|.TXENBL .TXNONO equ ^0|^2 ; bits that are not used ; for asynchronous transmission ; WR3: receiver parameters (must be sent after writing to WR4 ; and to WR5 if bit-2, CRC-16/SDLC, is changed) RX8BITS equ ^6*11B ; 8 bits per received character .RXENBL equ ^0 ; enable receiver RXENABLE equ RX8BITS|.RXENBL RXDISABLE equ RX8BITS ; disable receiver ; RR0: EXT/STAT flags DCD_RR0 equ 3 ; Data-Carrier-Detect bit CTS_RR0 equ 5 ; Clear-to-Send bit BREAK_RR0 equ 7 ; Break or abort ; ----------------------------------------------------------------------------- ; quadart port addresses and data constants ctcports: ; CTC channel control register ports db qdbase+0CH,qdbase+0DH,qdbase+0EH,qdbase+10H ; quadart 0 db qdbase+2CH,qdbase+2DH,qdbase+2EH,qdbase+30H ; quadart 1 db qdbase+4CH,qdbase+4DH,qdbase+4EH,qdbase+50H ; quadart 2 db qdbase+6CH,qdbase+6DH,qdbase+6EH,qdbase+70H ; quadart 3 NCTCPRTS equ $ - ctcports ; CTC addresses and timer ports for quadart 0 QD0CTC0 equ qdbase+0CH ; first CTC on quadart 0 QD0CTC1 equ qdbase+10H ; second CTC on quadart 0 QD0TMB equ qdbase+11H ; timer B (on CTC1) QD0TMC equ qdbase+12H ; timer C (on CTC1) QD0TMD equ qdbase+13H ; timer D (on CTC1) ; timer interrup vectors TMBIVEC equ 02H ; timer B TMCIVEC equ 04H ; timer C TMDIVEC equ 06H ; timer D TMINTVEC equ 80H+TMCIVEC ; interrupt vector for timer C ; CTC control register bits INTENAB equ 7 ; Interrupt every time Down Counter reaches 0 COUNTER equ 6 ; Counter Mode: Down Ctr decr by Ext Clock ; (vs. Timer Mode: Sys Clock thru Prescaler) PRESC256 equ 5 ; In timer Mode prescaler factor is 256 ; (versus 16) POSEDGE equ 4 ; Positive edge trigger (vs. negative edge) EXTTIMER equ 3 ; In Timer Mode external trigger starts timer ; after time constant loaded (vs. auto start) TMCONST equ 2 ; Time constant follows RESET equ 1 ; Reset channel CONTROL equ 0 ; This is a control word (vs. an interrupt ; vector) ; output the following byte to the CTC channel control register ; before outputting the time constant for the baudrate SPEEDCMD equ ^COUNTER|^TMCONST|^RESET|^CONTROL speedtbl: ; quadart time constant baudrate tt_speed byte db 0 ; - 0 db 0 ; - 1 db 0 ; - 2 db 175 ; 110 3 db 0 ; - 4 db 128 ; 150 5 db 0 ; - 6 db 64 ; 300 7 db 32 ; 600 8 db 16 ; 1200 9 db 0 ; - 10 db 8 ; 2400 11 db 4 ; 4800 12 db 2 ; 9600 13 db 0 ; - 14 db 0 ; - 15 db 1 ; 19200 16 NSPEEDS equ $-speedtbl ; Note: There is no baudrate corresponding to a time constant of zero. ; =============================================================================