You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1135 lines
29 KiB

;
;==================================================================================================
; ZILOG SCC DRIVER (SERIAL PORT)
;==================================================================================================
;
; SETUP PARAMETER WORD:
; +-------+---+-------------------+ +---+---+-----------+---+-------+
; | |RTS| ENCODED BAUD RATE | |DTR|XON| PARITY |STP| 8/7/6 |
; +-------+---+---+---------------+ ----+---+-----------+---+-------+
; F E D C B A 9 8 7 6 5 4 3 2 1 0
; -- MSB (D REGISTER) -- -- LSB (E REGISTER) --
;
; TODO:
; - INTERRUPT MODE 2 IS UNTESTED
;
SCC_BUFSZ .EQU 32 ; RECEIVE RING BUFFER SIZE
;
SCC_NONE .EQU 0
SCC_SCC .EQU 1
SCC_ESCC .EQU 2
;
SCC_DEFBAUD .EQU 38400 ; FAILSAFE BAUD RATE
SCC_DEFCLK .EQU 4915200 ; FAILSAVE BAUD CLOCK
SCC_DEFDIV .EQU (SCC_DEFCLK / (2 * 16 * SCC_DEFBAUD)) - 2
;
SCC_RTSON .EQU $EA
SCC_RTSOFF .EQU $E8
;
#IF ((SCCINTS) & (INTMODE > 0))
SCC_WR1VAL .EQU $10 ; WR1 VALUE FOR INT ON RECEIVED CHARS
SCC_WR9VAL .EQU $08 ; WR9 VALUE FOR MASTER INTERRUPTS ENABLED
#ELSE
SCC_WR1VAL .EQU $00 ; WR1 VALUE FOR NO INTS
SCC_WR9VAL .EQU $00 ; WR9 VALUE FOR MASTER INTERRUPTS DISABLED
#ENDIF
;
#IF ((SCCINTS) & (INTMODE >= 2))
;
SCC0_IVT .EQU IVT(INT_SCC0)
SCC1_IVT .EQU IVT(INT_SCC1)
SCC0_VEC .EQU VEC(INT_SCC0)
SCC1_VEC .EQU VEC(INT_SCC1)
;
#ENDIF
;
#IF (SCC0MODE == SCCMODE_STD)
SCC0A_CMD .EQU SCC0BASE + $01
SCC0A_DAT .EQU SCC0BASE + $00
SCC0B_CMD .EQU SCC0BASE + $03
SCC0B_DAT .EQU SCC0BASE + $02
#ENDIF
;
#IF (SCC0MODE == SCCMODE_SZ80)
SCC0A_CMD .EQU SCC0BASE + $01
SCC0A_DAT .EQU SCC0BASE + $03
SCC0B_CMD .EQU SCC0BASE + $00
SCC0B_DAT .EQU SCC0BASE + $02
#ENDIF
;
#IF (SCCCNT >= 2)
;
#IF (SCC1MODE == SCCMODE_STD)
SCC1A_CMD .EQU SCC1BASE + $01
SCC1A_DAT .EQU SCC1BASE + $00
SCC1B_CMD .EQU SCC1BASE + $03
SCC1B_DAT .EQU SCC1BASE + $02
#ENDIF
;
#IF (SCC0MODE == SCCMODE_SZ80)
SCC1A_CMD .EQU SCC1BASE + $01
SCC1A_DAT .EQU SCC1BASE + $03
SCC1B_CMD .EQU SCC1BASE + $00
SCC1B_DAT .EQU SCC1BASE + $02
#ENDIF
;
#ENDIF
;
;--------------------------------------------------------------------------------------------------
; HBIOS MODULE HEADER
;--------------------------------------------------------------------------------------------------
;
ORG_SCC .EQU $
;
.DW SIZ_SCC ; MODULE SIZE
.DW SCC_INITPHASE ; ADR OF INIT PHASE HANDLER
;
SCC_INITPHASE:
; INIT PHASE HANDLER, A=PHASE
CP HB_PHASE_PREINIT ; PREINIT PHASE?
JP Z,SCC_PREINIT ; DO PREINIT
CP HB_PHASE_INIT ; INIT PHASE?
JP Z,SCC_INIT ; DO INIT
RET ; DONE
;
SCC_PREINIT:
;
; SETUP THE DISPATCH TABLE ENTRIES
; NOTE: INTS WILL BE DISABLED WHEN PREINIT IS CALLED AND THEY MUST REMIAIN
; DISABLED.
;
CALL SCC_PROBE ; PROBE FOR CHIPS
;
LD B,SCC_CFGCNT ; LOOP CONTROL
XOR A ; ZERO TO ACCUM
LD (SCC_DEV),A ; CURRENT DEVICE NUMBER
LD IY,SCC_CFG ; POINT TO START OF CFG TABLE
SCC_PREINIT0:
PUSH BC ; SAVE LOOP CONTROL
CALL SCC_INITUNIT ; HAND OFF TO GENERIC INIT CODE
POP BC ; RESTORE LOOP CONTROL
;
LD A,(IY+1) ; GET THE SCC TYPE DETECTED
OR A ; SET FLAGS
JR Z,SCC_PREINIT2 ; SKIP IT IF NOTHING FOUND
;
PUSH BC ; SAVE LOOP CONTROL
PUSH IY ; CFG ENTRY ADDRESS
POP DE ; ... TO DE
LD BC,SCC_FNTBL ; BC := FUNCTION TABLE ADDRESS
CALL NZ,CIO_ADDENT ; ADD ENTRY IF SCC FOUND, BC:DE
POP BC ; RESTORE LOOP CONTROL
;
SCC_PREINIT2:
LD DE,SCC_CFGSIZ ; SIZE OF CFG ENTRY
ADD IY,DE ; BUMP IY TO NEXT ENTRY
DJNZ SCC_PREINIT0 ; LOOP UNTIL DONE
;
#IF ((SCCINTS) & (INTMODE > 0))
; SETUP INT VECTORS AS APPROPRIATE
LD A,(SCC_DEV) ; GET DEVICE COUNT
OR A ; SET FLAGS
JR Z,SCC_PREINIT3 ; IF ZERO, NO SCC DEVICES, ABORT
;
#IF (INTMODE == 1)
; ADD IM1 INT CALL LIST ENTRY
LD HL,SCC_INT ; GET INT VECTOR
CALL HB_ADDIM1 ; ADD TO IM1 CALL LIST
#ENDIF
;
#IF ((INTMODE == 2) | (INTMODE == 3))
; SETUP IM2/3 VECTORS
LD HL,SCC_INT0
LD (SCC0_IVT),HL ; IVT INDEX
;
#IF (SCCCNT >= 2)
LD HL,SCC_INT1
LD (SCC1_IVT),HL ; IVT INDEX
#ENDIF
;
#ENDIF
;
#ENDIF
;
SCC_PREINIT3:
XOR A ; SIGNAL SUCCESS
RET ; AND RETURN
;
; SCC INITIALIZATION ROUTINE
;
SCC_INITUNIT:
CALL SCC_DETECT ; DETERMINE SCC TYPE
LD (IY+1),A ; SAVE IN CONFIG TABLE
OR A ; SET FLAGS
RET Z ; ABORT IF NOTHING THERE
; UPDATE WORKING SCC DEVICE NUM
LD HL,SCC_DEV ; POINT TO CURRENT UART DEVICE NUM
LD A,(HL) ; PUT IN ACCUM
INC (HL) ; INCREMENT IT (FOR NEXT LOOP)
LD (IY),A ; UPDATE UNIT NUM
; IT IS EASY TO SPECIFY A SERIAL CONFIG THAT CANNOT BE IMPLEMENTED
; DUE TO THE CONSTRAINTS OF THE SCC. HERE WE FORCE A GENERIC
; FAILSAFE CONFIG ONTO THE CHANNEL. IF THE SUBSEQUENT "REAL"
; CONFIG FAILS, AT LEAST THE CHIP WILL BE ABLE TO SPIT DATA OUT
; AT A RATIONAL BAUD/DATA/PARITY/STOP CONFIG.
CALL SCC_INITSAFE
;
; SET DEFAULT CONFIG
LD DE,-1 ; LEAVE CONFIG ALONE
JP SCC_INITDEV ; IMPLEMENT IT AND RETURN
;
;
;
SCC_INIT:
LD B,SCC_CFGCNT ; COUNT OF POSSIBLE SCC UNITS
LD IY,SCC_CFG ; POINT TO START OF CFG TABLE
SCC_INIT1:
PUSH BC ; SAVE LOOP CONTROL
LD A,(IY+1) ; GET SCC TYPE
OR A ; SET FLAGS
CALL NZ,SCC_PRTCFG ; PRINT IF NOT ZERO
POP BC ; RESTORE LOOP CONTROL
LD DE,SCC_CFGSIZ ; SIZE OF CFG ENTRY
ADD IY,DE ; BUMP IY TO NEXT ENTRY
DJNZ SCC_INIT1 ; LOOP TILL DONE
;
XOR A ; SIGNAL SUCCESS
RET ; DONE
;
; RECEIVE INTERRUPT HANDLER
;
#IF ((SCCINTS) & (INTMODE > 0))
;
; IM1 ENTRY POINT
;
SCC_INT:
; CHECK/HANDLE FIRST CARD (SCC0) IF IT EXISTS
LD A,(SCC0A_CFG + 1) ; GET SCC TYPE FOR FIRST CHANNEL OF FIRST SCC
OR A ; SET FLAGS
CALL NZ,SCC_INT0 ; CALL IF CARD EXISTS
RET NZ ; DONE IF INT HANDLED
;
#IF (SCCCNT >= 2)
; CHECK/HANDLE SECOND CARD (SCC1) IF IT EXISTS
LD A,(SCC1A_CFG + 1) ; GET SCC TYPE FOR FIRST CHANNEL OF SECOND SCC
OR A ; SET FLAGS
CALL NZ,SCC_INT1 ; CALL IF CARD EXISTS
#ENDIF
;
RET ; DONE
;
; IM2 ENTRY POINTS
;
SCC_INT0:
; INTERRUPT HANDLER FOR FIRST SCC (SCC0)
LD IY,SCC0A_CFG ; POINT TO SCC0A CFG
CALL SCC_INTRCV ; TRY TO RECEIVE FROM IT
RET NZ ; DONE IF INT HANDLED
LD IY,SCC0B_CFG ; POINT TO SCC0B CFG
JR SCC_INTRCV ; TRY TO RECEIVE FROM IT AND RETURN
;
#IF (SCCCNT >= 2)
;
SCC_INT1:
; INTERRUPT HANDLER FOR SECOND SCC (SCC1)
LD IY,SCC1A_CFG ; POINT TO SCC1A CFG
CALL SCC_INTRCV ; TRY TO RECEIVE FROM IT
RET NZ ; DONE IF INT HANDLED
LD IY,SCC1B_CFG ; POINT TO SCC1B CFG
JR SCC_INTRCV ; TRY TO RECEIVE FROM IT AND RETURN
;
#ENDIF
;
; HANDLE INT FOR A SPECIFIC CHANNEL
; BASED ON UNIT CFG POINTED TO BY IY
;
SCC_INTRCV:
; CHECK TO SEE IF SOMETHING IS ACTUALLY THERE
LD C,(IY+3) ; CMD/STAT PORT TO C
XOR A ; A := 0
OUT (C),A ; ADDRESS RR0
IN A,(C) ; GET RR0
AND $01 ; ISOLATE RECEIVE READY BIT
RET Z ; NOTHING AVAILABLE ON CURRENT CHANNEL
;
SCC_INTRCV1:
; RECEIVE CHARACTER INTO BUFFER
LD C,(IY+4) ; DATA PORT TO C
IN A,(C) ; READ PORT
#IF (SCCBOOT != 0)
CP SCCBOOT ; REBOOT REQUEST?
JP Z,SYS_RESCOLD ; IF SO, DO IT, NO RETURN
#ENDIF
LD B,A ; SAVE BYTE READ
LD L,(IY+7) ; SET HL TO
LD H,(IY+8) ; ... START OF BUFFER STRUCT
LD A,(HL) ; GET COUNT
CP SCC_BUFSZ ; COMPARE TO BUFFER SIZE
JR Z,SCC_INTRCV4 ; BAIL OUT IF BUFFER FULL, RCV BYTE DISCARDED
INC A ; INCREMENT THE COUNT
LD (HL),A ; AND SAVE IT
CP SCC_BUFSZ / 2 ; BUFFER GETTING FULL?
JR NZ,SCC_INTRCV2 ; IF NOT, BYPASS CLEARING RTS
LD C,(IY+3) ; CMD/STAT PORT TO C
LD A,5 ; RTS IS IN WR5
OUT (C),A ; ADDRESS WR5
LD A,SCC_RTSOFF ; VALUE TO CLEAR RTS
OUT (C),A ; DO IT
SCC_INTRCV2:
INC HL ; HL NOW HAS ADR OF HEAD PTR
PUSH HL ; SAVE ADR OF HEAD PTR
LD A,(HL) ; DEREFERENCE HL
INC HL
LD H,(HL)
LD L,A ; HL IS NOW ACTUAL HEAD PTR
LD (HL),B ; SAVE CHARACTER RECEIVED IN BUFFER AT HEAD
INC HL ; BUMP HEAD POINTER
POP DE ; RECOVER ADR OF HEAD PTR
LD A,L ; GET LOW BYTE OF HEAD PTR
SUB SCC_BUFSZ+4 ; SUBTRACT SIZE OF BUFFER AND POINTER
CP E ; IF EQUAL TO START, HEAD PTR IS PAST BUF END
JR NZ,SCC_INTRCV3 ; IF NOT, BYPASS
LD H,D ; SET HL TO
LD L,E ; ... HEAD PTR ADR
INC HL ; BUMP PAST HEAD PTR
INC HL
INC HL
INC HL ; ... SO HL NOW HAS ADR OF ACTUAL BUFFER START
SCC_INTRCV3:
EX DE,HL ; DE := HEAD PTR VAL, HL := ADR OF HEAD PTR
LD (HL),E ; SAVE UPDATED HEAD PTR
INC HL
LD (HL),D
; CHECK FOR MORE PENDING...
LD C,(IY+3) ; CMD/STAT PORT TO C
XOR A ; A := 0
OUT (C),A ; ADDRESS RD0
IN A,(C) ; GET RD0
RRA ; READY BIT TO CF
JR C,SCC_INTRCV1 ; IF SET, DO SOME MORE
SCC_INTRCV4:
; CHECK FOR ANY PENDING SPECIAL CONDITIONS
; IF AN ERROR OCCURS, THE RECEIVER WILL BE LOCKED UNTIL YOU
; CLEAR IT BY READ RR1
LD C,(IY+3) ; CMD/STAT PORT TO C
LD A,1 ; RR1
OUT (C),A ; SELECT IT
IN A,(C) ; READ IT
AND %0111000 ; ISOLATE ERROR BITS
JR Z,SCC_INTRCV5 ; DONE IF NO BITS SET
;CALL PRTHEXBYTE ; *DEBUG*
;
; ERROR RESET
LD A,%00110000 ; ERROR RESET
OUT (C),A ; SEND IT
;
SCC_INTRCV5:
; RESET INTERRUPT UNDER SERVICE
LD A,%00111000 ; RESET HIGHEST IUS
OUT (C),A ; DO IT
;
OR $FF ; NZ SET TO INDICATE INT HANDLED
RET ; AND RETURN
;
#ENDIF
;
; DRIVER FUNCTION TABLE
;
SCC_FNTBL:
.DW SCC_IN
.DW SCC_OUT
.DW SCC_IST
.DW SCC_OST
.DW SCC_INITDEV
.DW SCC_QUERY
.DW SCC_DEVICE
#IF (($ - SCC_FNTBL) != (CIO_FNCNT * 2))
.ECHO "*** INVALID SCC FUNCTION TABLE ***\n"
#ENDIF
;
;
;
#IF ((SCCINTS) & (INTMODE > 0))
;
SCC_IN:
CALL SCC_IST ; SEE IF CHAR AVAILABLE
JR Z,SCC_IN ; LOOP UNTIL SO
HB_DI ; AVOID COLLISCCN WITH INT HANDLER
LD L,(IY+7) ; SET HL TO
LD H,(IY+8) ; ... START OF BUFFER STRUCT
LD A,(HL) ; GET COUNT
DEC A ; DECREMENT COUNT
LD (HL),A ; SAVE UPDATED COUNT
CP SCC_BUFSZ / 4 ; BUFFER LOW THRESHOLD
JR NZ,SCC_IN1 ; IF NOT, BYPASS SETTING RTS
LD C,(IY+3) ; C IS CMD/STATUS PORT ADR
LD A,5 ; RTS IS IN WR5
OUT (C),A ; ADDRESS WR5
LD A,SCC_RTSON ; VALUE TO SET RTS
OUT (C),A ; DO IT
SCC_IN1:
INC HL
INC HL
INC HL ; HL NOW HAS ADR OF TAIL PTR
PUSH HL ; SAVE ADR OF TAIL PTR
LD A,(HL) ; DEREFERENCE HL
INC HL
LD H,(HL)
LD L,A ; HL IS NOW ACTUAL TAIL PTR
LD C,(HL) ; C := CHAR TO BE RETURNED
INC HL ; BUMP TAIL PTR
POP DE ; RECOVER ADR OF TAIL PTR
LD A,L ; GET LOW BYTE OF TAIL PTR
SUB SCC_BUFSZ+2 ; SUBTRACT SIZE OF BUFFER AND POINTER
CP E ; IF EQUAL TO START, TAIL PTR IS PAST BUF END
JR NZ,SCC_IN2 ; IF NOT, BYPASS
LD H,D ; SET HL TO
LD L,E ; ... TAIL PTR ADR
INC HL ; BUMP PAST TAIL PTR
INC HL ; ... SO HL NOW HAS ADR OF ACTUAL BUFFER START
SCC_IN2:
EX DE,HL ; DE := TAIL PTR VAL, HL := ADR OF TAIL PTR
LD (HL),E ; SAVE UPDATED TAIL PTR
INC HL
LD (HL),D
LD E,C ; MOVE CHAR TO RETURN TO E
HB_EI ; INTERRUPTS OK AGAIN
XOR A ; SIGNAL SUCCESS
RET ; AND DONE
;
#ELSE
;
SCC_IN:
CALL SCC_IST ; CHAR WAITING?
JR Z,SCC_IN ; LOOP IF NOT
LD C,(IY+4) ; DATA PORT
IN E,(C) ; GET CHAR
XOR A ; SIGNAL SUCCESS
RET
;
#ENDIF
;
;
;
SCC_OUT:
CALL SCC_OST ; READY FOR CHAR?
JR Z,SCC_OUT ; LOOP IF NOT
LD C,(IY+4) ; DATA PORT
OUT (C),E ; SEND CHAR FROM E
XOR A ; SIGNAL SUCCESS
RET
;
;
;
#IF ((SCCINTS) & (INTMODE > 0))
;
SCC_IST:
LD L,(IY+7) ; GET ADDRESS
LD H,(IY+8) ; ... OF RECEIVE BUFFER
LD A,(HL) ; BUFFER UTILIZATION COUNT
OR A ; SET FLAGS
JP Z,CIO_IDLE ; NOT READY, RETURN VIA IDLE PROCESSING
RET
;
#ELSE
;
SCC_IST:
LD C,(IY+3) ; CMD PORT
XOR A ; WR0
OUT (C),A ; DO IT
IN A,(C) ; GET STATUS
AND $01 ; ISOLATE BIT 0 (RX READY)
JP Z,CIO_IDLE ; NOT READY, RETURN VIA IDLE PROCESSING
XOR A ; ZERO ACCUM
INC A ; ASCCUM := 1 TO SIGNAL 1 CHAR WAITING
RET ; DONE
;
#ENDIF
;
;
;
SCC_OST:
LD C,(IY+3) ; CMD PORT
XOR A ; WR0
OUT (C),A ; DO IT
IN A,(C) ; GET STATUS
AND $04 ; ISOLATE BIT 2 (TX EMPTY)
JP Z,CIO_IDLE ; NOT READY, RETURN VIA IDLE PROCESSING
XOR A ; ZERO ACCUM
INC A ; ACCUM := 1 TO SIGNAL 1 BUFFER POSITION
RET ; DONE
;
; AT INITIALIZATION THE SETUP PARAMETER WORD IS TRANSLATED TO THE FORMAT
; REQUIRED BY THE SCC AND STORED IN A PORT/REGISTER INITIALIZATION TABLE,
; WHICH IS THEN LOADED INTO THE SCC.
;
; RTS, DTR AND XON SETTING IS NOT CURRENTLY SUPPORTED.
; MARK & SPACE PARITY AND 1.5 STOP BITS IS NOT SUPPORTED BY THE SCC.
; INITIALIZATION WILL NOT BE COMPLETED IF AN INVALID SETTING IS DETECTED.
;
SCC_INITDEV:
; INITDEV CAN BE CALLED PRIOR TO INTERRUPTS BEING ENABLED. WE
; NEED TO LEAVE INTERRUPTS ALONE IN THIS SCENARIO
LD A,(INTSENAB) ; INTS ENABLED?
OR A ; TEST VALUE
JR Z,SCC_INITDEV0 ; BYPASS DI/EI IF NOT ENABLED
;
; INTERRUPTS DISABLED DURING INIT
HB_DI ; DISABLE INTS
CALL SCC_INITDEV0 ; DO THE WORK
HB_EI ; INTS BACK ON
RET ; DONE
;
SCC_INITDEV0:
;
; THIS ENTRY POINT BYPASSES DISABLING/ENABLING INTS WHICH IS REQUIRED BY
; PREINIT ABOVE. PREINIT IS NOT ALLOWED TO ENABLE INTS!
;
#IF (SCCDEBUG)
CALL NEWLINE
PRTS("SCC$")
LD A,(IY+2)
SRL A
CALL PRTDECB
LD A,(IY+2)
AND $01
ADD A,'A'
CALL COUT
CALL PC_COLON
#ENDIF
;
; TEST FOR -1 WHICH MEANS USE CURRENT CONFIG (JUST REINIT)
LD A,D ; TEST DE FOR
AND E ; ... VALUE OF -1
INC A ; ... SO Z SET IF -1
JR NZ,SCC_INITDEV1 ; IF DE == -1, REINIT CURRENT CONFIG
;
; LOAD EXISTING CONFIG TO REINIT
LD E,(IY+5) ; LOW BYTE
LD D,(IY+6) ; HIGH BYTE
;
SCC_INITDEV1:
;
#IF (SCCDEBUG)
PUSH DE
POP BC
PRTS(" CFG=$")
CALL PRTHEXWORD
#ENDIF
;
; SETUP DEFAULT VALUES FOR SCC REGISTERS
PUSH DE
LD HL,SCC_INITDEFS
LD DE,SCC_INITVALS
LD BC,SCC_INITLEN
LDIR
POP DE
;
; BUILD WR4
LD A,(SCC_WR4)
AND %11110000 ; CLEAR BITS WE ARE GOING TO CHG
SET 2,A ; ASSUME 1 STOP BIT
BIT 2,E ; WANT 2 STOP BITS?
JR Z,SCC_INITDEV_WR4A ; SKIP IF NOT
SET 3,A ; SET CONFIG BIT FOR 2 STOP BITS
SCC_INITDEV_WR4A:
BIT 3,E ; PARITY ENABLE?
JR Z,SCC_INITDEV_WR4B ; SKIP IF NOT
SET 0,A ; SET CONFIG BIT FOR PARITY ENABLE
SCC_INITDEV_WR4B:
BIT 4,E ; PARITY EVEN?
JR Z,SCC_INITDEV_WR4C ; SKIP IF NOT
SET 1,A ; SET CONFIG BIT FOR EVEN PARITY
SCC_INITDEV_WR4C:
LD (SCC_WR4),A ; SAVE WR4 VALUE
;
; CREATE BITS PER CHAR VALUE
; WARNING: SCC USES NON-SEQUENTIAL VALUES:
; %00 = 5 BITS
; %01 = 7 BITS
; %10 = 6 BITS
; %11 = 8 BITS
LD B,0 ; INIT B FOR WORKING VALUE
BIT 0,E ; BIT 0 OF E SET?
JR Z,SCC_INITDEV_BPCA ; SKIP IF NOT
SET 1,B ; SET BIT 1 OF B
SCC_INITDEV_BPCA:
BIT 1,E ; BIT 1 OF E SET?
JR Z,SCC_INITDEV_BPCB ; SKIP IF NOT
SET 0,B ; SET BIT 0 OF B
SCC_INITDEV_BPCB:
;
; BUILD WR3
LD A,(SCC_WR3) ; GET CUR WR3 VAL
AND %00111111 ; CLEAR BPC BITS
RRC B ; MOVE BPC BITS
RRC B ; ... INTO POSITION
OR B ; APPLY NEW BPC BITS
LD (SCC_WR3),A ; SAVE IT IN WR3
SET 0,A ; ADD RX ENABLE
LD (SCC_WR3A),A ; AND SAVE IT IN WR3A
;
; BUILD WR5
LD A,(SCC_WR5) ; GET CUR WR5 VAL
AND %10011111 ; CLEAR BPC BITS
RRC B ; MOVE BPC BITS INTO POSITION
OR B ; APPLY NEW BPC BITS
LD (SCC_WR5),A ; SAVE IT IN WR5
SET 3,A ; ADD TX ENABLE
LD (SCC_WR5A),A ; AND SAVE IT IN WR3A
;
; DETERMINE BAUD DIVISOR
PUSH DE ; SAVE CONFIG
CALL SCC_COMPDIV ; COMPUTE DIVISOR TO BC
POP DE ; RESTORE CONFIG
RET NZ ; ABORT IF COMPDIV FAILS!
;
; SCC BRG MUST BE PROGRAMMED WITH THE DIVISOR - 2
DEC BC ; SUBTRACT 2
DEC BC ; ... FROM DIVISOR
BIT 7,B ; DID WE UNDERFLOW?
JR NZ,SCC_INITERR ; ERROR IF SO
;
; BUILD WR12/13
LD A,C ; LSB OF BRG DIVISOR
LD (SCC_WR12),A ; SAVE IT
LD A,B ; MSB OF BRG DIVISOR
LD (SCC_WR13),A ; SAVE IT
;
; SAVE CONFIG PERMANENTLY NOW
;
LD (IY+5),E ; SAVE LOW WORD
LD (IY+6),D ; SAVE HI WORD
;
JR SCC_INITGO ; GO TO SEND INIT
;
SCC_INITERR:
OR $FF ; SIGNAL ERROR
RET
;
; ENTER HERE TO PERFORM A "SAFE" INITIALIZTION. I.E., INIT THE
; CHANNEL USING THE DEFAULT, GENERIC REGISTER VALUES. THIS CAN BE
; USED TO ENSURE INITIALIZATION IF THE FULL CONFIGURATION ABOVE
; FAILS.
;
SCC_INITSAFE:
; SETUP DEFAULT VALUES FOR SCC REGISTERS
LD HL,SCC_INITDEFS
LD DE,SCC_INITVALS
LD BC,SCC_INITLEN
LDIR
;
SCC_INITGO:
;
; SET INTERRUPT VECTOR OFFSET WR2
;
#IF ((SCCINTS) & (INTMODE >= 2))
LD A,(IY+2) ; CHIP / CHANNEL
SRL A ; SHIFT AWAY CHANNEL BIT
LD L,SCC0_VEC ; ASSUME CHIP 0
JR Z,SCC_INITIVT ; IF SO, DO IT
LD L,SCC1_VEC ; ASSUME CHIP 1
DEC A ; CHIP 1?
JR Z,SCC_INITIVT ; IF SO, DO IT
SYSCHKERR(ERR_NOUNIT) ; IMPOSSIBLE SITUATION
RET
SCC_INITIVT:
LD A,L ; VALUE TO A
LD (SCC_WR2),A ; SAVE IT
;
#ENDIF
;
#IF (SCCDEBUG)
LD HL,SCC_INITVALS
LD B,SCC_INITLEN/2
SCC_INITPRT:
PRTS(" WR$")
LD A,(HL)
CALL PRTHEXBYTE
INC HL
LD A,'='
CALL COUT
LD A,(HL)
CALL PRTHEXBYTE
INC HL
DJNZ SCC_INITPRT
LD DE,65
CALL VDELAY ; WAIT FOR FINAL CHAR TO SEND
#ENDIF
;
; RESET THE CHANNEL
LD C,(IY+3) ; CONTROL PORT
LD A,9 ; REGISTER WR9
OUT (C),A ; SELECT IT
LD A,%10000000 ; CHANNEL A RESET CMD
BIT 0,(IY+2) ; TEST CHANNEL NUM
JR Z,SCC_INITGO1 ; SKIP AHEAD IF CHANNEL 0
RRCA ; ADJUST FOR CHANNEL 1
SCC_INITGO1:
OUT (C),A ; SEND IT
NOP ; SMALL DELAY REQUIRED
;
; PROGRAM THE SCC CHIP CHANNEL
LD C,(IY+3) ; COMMAND PORT
LD HL,SCC_INITVALS ; POINT TO INIT VALUES
LD B,SCC_INITLEN ; COUNT OF BYTES TO WRITE
OTIR ; WRITE ALL VALUES
;
#IF ((SCCINTS) & (INTMODE > 0))
;
; RESET THE RECEIVE BUFFER
LD E,(IY+7)
LD D,(IY+8) ; DE := _CNT
XOR A ; A := 0
LD (DE),A ; _CNT = 0
INC DE ; DE := ADR OF _HD
PUSH DE ; SAVE IT
INC DE
INC DE
INC DE
INC DE ; DE := ADR OF _BUF
POP HL ; HL := ADR OF _HD
LD (HL),E
INC HL
LD (HL),D ; _HD := _BUF
INC HL
LD (HL),E
INC HL
LD (HL),D ; _TL := _BUF
;
#ENDIF
;
XOR A ; SIGNAL SUCCESS
RET ; RETURN
;
; THE FOLLOWING TABLE IS A GENERIC, STATIC SET OF CONFIG VALUES THAT CAN
; BE USED TO INITIALIZE THE CHIP. THESE ARE THE STARTING VALUES USED FOR
; CHIP CONFIGURATION.
;
; THE SCC IS A LITTLE PRICKLY ABOUT THE ORDER IN WHICH REGISTERS ARE
; WRITTEN DURING CONFIGURATION. THE FOLLOWING TABLE ESTABLISHES THE
; ORDER THE REGISTERS WILL BE PROGRAMMED AND SETS UP THE DEFAULT
; VALUES COPIED TO SCC_INITVALS. INITVALS IS ADJUSTED AS NEEDED,
; THEN INITVALS IS WRITTEN TO THE CHIP.
;
; *** UPDATE SCC_WR... EQUATES BELOW IF TABLE CHANGES!!!
;
SCC_INITDEFS:
.DB 4, $44 ; ASYNC MODE, X16, 1 STOP, NO PARITY ; 0100 0100
.DB 1, SCC_WR1VAL ; CONFIGURE INTERRUPTS
.DB 3, $C0 ; RX 8 BITS PER CHAR ; 1100 0000
.DB 5, $E2 ; TX 8 BITS PER CHAR ; 1110 0010
.DB 11, $56 ; RTxC VIA BRG ; 0101 0110
.DB 12, SCC_DEFDIV & $FF ; BAUD RATE DIVISOR LO BYTE
.DB 13, SCC_DEFDIV >> 8 ; BAUD RATE DIVISOR HI BYTE
#IF (SCCPCLK)
.DB 14, $02 ; BRG SOURCE PCLK ; 0000 0000
.DB 14, $03 ; ENABLE BRG ; 0000 0001
#ELSE
.DB 14, $00 ; BRG SOURCE RTxC ; 0000 0000
.DB 14, $01 ; ENABLE BRG ; 0000 0001
#ENDIF
.DB 3, $C1 ; ENABLE RECEIVER ; 1100 0001
.DB 5, $EA ; ENABLE TRANSMITTER ; 1110 1010
.DB 9, SCC_WR9VAL ; MASTER INTERRUPT CONTROL
;
SCC_INITLEN .EQU $ - SCC_INITDEFS
;
SCC_INITVALS .FILL SCC_INITLEN,0
;
; BELOW ADDRESSES USED TO ADDRESS CONFIGURATION VALUE REGISTERS
; *** MUST SYNC WITH SCC_INITDEFS !!!
;
SCC_WR4 .EQU SCC_INITVALS + 1
SCC_WR1 .EQU SCC_WR4 + 2
SCC_WR3 .EQU SCC_WR1 + 2
SCC_WR5 .EQU SCC_WR3 + 2
SCC_WR11 .EQU SCC_WR5 + 2
SCC_WR12 .EQU SCC_WR11 + 2
SCC_WR13 .EQU SCC_WR12 + 2
SCC_WR14 .EQU SCC_WR13 + 2
SCC_WR14A .EQU SCC_WR14 + 2
SCC_WR3A .EQU SCC_WR14A + 2
SCC_WR5A .EQU SCC_WR3A + 2
SCC_WR9 .EQU SCC_WR5A + 2
;
;
;
SCC_QUERY:
LD E,(IY+5) ; FIRST CONFIG BYTE TO E
LD D,(IY+6) ; SECOND CONFIG BYTE TO D
XOR A ; SIGNAL SUCCESS
RET ; DONE
;
;
;
SCC_DEVICE:
LD D,CIODEV_SCC ; D := DEVICE TYPE
LD E,(IY) ; E := PHYSICAL UNIT
LD C,$00 ; C := DEVICE TYPE, 0x00 IS RS-232
LD H,(IY+14) ; H := MODE
LD L,(IY+3) ; L := BASE I/O ADDRESS
XOR A ; SIGNAL SUCCESS
RET
;
; SCC CHIP PROBE
; CHECK FOR PRESENCE OF SCC CHIPS AND POPULATE THE
; SCC_MAP BITMAP (ONE BIT PER CHIP). THIS DETECTS
; CHIPS, NOT CHANNELS. EACH CHIP HAS 2 CHANNELS.
; MAX OF TWO CHIPS CURRENTLY. INT VEC VALUE IS TRASHED!
;
SCC_PROBE:
; CLEAR THE PRESENCE BITMAP
LD HL,SCC_MAP ; HL POINTS TO BITMAP
XOR A ; ZERO
LD (SCC_MAP),A ; CLEAR CHIP PRESENT BITMAP
LD C,SCC0B_CMD ; FIRST CHIP CMD/STAT PORT
CALL SCC_PROBECHIP ; PROBE IT
JR NZ,SCC_PROBE1 ; IF NOT ZERO, NOT FOUND
SET 0,(HL) ; SET BIT FOR FIRST CARD
SCC_PROBE1:
;
#IF (SCCCNT >= 2)
LD C,SCC1B_CMD ; SECOND CHIP CMD/STAT PORT
CALL SCC_PROBECHIP ; PROBE IT
JR NZ,SCC_PROBE2 ; IF NOT ZERO, NOT FOUND
SET 1,(HL) ; SET BIT FOR SECOND CARD
SCC_PROBE2:
#ENDIF
;
RET
;
SCC_PROBECHIP:
LD B,12 ; R12 (LOW BYTE OF BRG)
LD A,$A5 ; TEST VAL
CALL SCC_WR ; WRITE IT
CALL SCC_RD ; READ IT
CP $A5 ; CORRECT VALUE RETURNED?
RET NZ ; ABORT IF NOT
LD A,$5A ; NEXT TEST VAL
CALL SCC_WR ; WRITE IT
CALL SCC_RD ; READ IT
CP $5A ; CORRECT VALUE RETURNED?
RET ; RET W ZF SET AS NEEDED
;
; READ/WRITE CHIP REGISTER. ENTER CHIP CMD/STAT PORT ADR IN C
; AND CHIP REGISTER NUMBER IN B. VALUE TO WRITE IN A OR VALUE
; RETURNED IN A.
;
SCC_WR:
OUT (C),B ; SELECT CHIP REGISTER
OUT (C),A ; WRITE VALUE
RET
;
SCC_RD:
OUT (C),B ; SELECT CHIP REGISTER
IN A,(C) ; GET VALUE
RET
;
; SCC DETECTION ROUTINE
; THERE IS ONLY ONE VARIATION OF SCC CHIP, SO HERE WE JUST CHECK THE
; CHIP PRESENCE BITMAP TO SET THE CHIP TYPE OF EITHER NONE OR SCC.
;
SCC_DETECT:
LD B,(IY+2) ; GET CHIP/CHANNEL
SRL B ; SHIFT AWAY THE CHANNEL BIT
INC B ; NUMBER OF TIMES TO ROTATE BITS
LD A,(SCC_MAP) ; BIT MAP IN A
SCC_DETECT1:
; ROTATE DESIRED CHIP BIT INTO CF
RRA ; ROTATE NEXT BIT INTO CF
DJNZ SCC_DETECT1 ; DO THIS UNTIL WE HAVE DESIRED BIT
; RETURN CHIP TYPE
LD A,SCC_NONE ; ASSUME NOTHING HERE
RET NC ; IF CF NOT SET, RETURN
; CHECK FOR ESCC
LD C,(IY+3) ; CMD/STATUS PORT
LD B,9 ; R9
LD A,%11000000 ; RESET CHIP
CALL SCC_WR ; DO IT
LD B,15 ; R15
CALL SCC_RD ; READ IT
CALL PRTHEXBYTE
CP $01 ; ESCC?
JR Z,SCC_DETECT2 ; IF SO, JUMP AHEAD
LD A,SCC_SCC ; CHIP TYPE IS SCC
RET ; DONE
SCC_DETECT2:
LD A,SCC_ESCC ; CHIP TYPE IS ESCC
RET
;
; COMPUTE DIVISOR TO BC
;
SCC_COMPDIV:
; WE WANT TO DETERMINE A DIVISOR FOR THE BAUD CLOCK
; THAT RESULTS IN THE DESIRED BAUD RATE.
; BAUD RATE = BAUD CLK / DIVISOR, OR TO SOLVE FOR DIVISOR
; DIVISOR = BAUD CLK / BAUDRATE.
; THE BAUD CLOCK IS THE SCC OSC PRESCALED BY 32. WE CAN
; TAKE ADVANTAGE OF ENCODED BAUD RATES ALWAYS BEING A FACTOR OF 75.
; SO, WE CAN USE (BAUD CLK / 32 / 75) / (BAUDRATE / 75)
;
; FIRST WE DECODE THE BAUDRATE, BUT WE USE A CONSTANT OF 1 INSTEAD
; OF THE NORMAL 75. THIS PRODUCES (BAUDRATE / 75).
LD A,D ; GET CONFIG MSB
AND $1F ; ISOLATE ENCODED BAUD RATE
LD L,A ; PUT IN L
LD H,0 ; H IS ALWAYS ZERO
LD DE,1 ; USE 1 FOR ENCODING CONSTANT
CALL DECODE ; DE:HL := BAUD RATE, ERRORS IGNORED
EX DE,HL ; DE := (BAUDRATE / 75), DISCARD HL
PUSH DE ; SAVE FOR NOW
;
; GET THE BAUD CLOCK FROM CONFIG
LD A,9
CALL LDHLIYA ; HL = ADR OF BAUD CLOCK DWORD
CALL LD32 ; GET THE BAUD CLOCK SPEED
;
; DIVIDE BY 32, THEN 75 TO PRODUCE (BAUD CLK / 32 / 75)
LD C,32 ; DIVIDE BY 32
CALL DIV32X8 ; DO IT
LD C,75 ; DIVIDE BY 75
CALL DIV32X8 ; DO IT
;
; RECOVER THE BAUDRATE AND RETURN VIA FINAL DIVISION
POP DE ; RECOVER DE (DECODED BAUD RATE)
JP DIV16 ; BC := HL/DE == DIVISOR AND RETURN
;
;
;
SCC_PRTCFG:
; ANNOUNCE PORT
CALL NEWLINE ; FORMATTING
PRTS("SCC$") ; FORMATTING
LD A,(IY) ; DEVICE NUM
CALL PRTDECB ; PRINT DEVICE NUM
PRTS(": IO=0x$") ; FORMATTING
LD A,(IY+3) ; GET BASE PORT
CALL PRTHEXBYTE ; PRINT BASE PORT
; PRINT THE SCC TYPE
CALL PC_SPACE ; FORMATTING
LD A,(IY+1) ; GET SCC TYPE BYTE
RLCA ; MAKE IT A WORD OFFSET
LD HL,SCC_TYPE_MAP ; POINT HL TO TYPE MAP TABLE
CALL ADDHLA ; HL := ENTRY
LD E,(HL) ; DEREFERENCE
INC HL ; ...
LD D,(HL) ; ... TO GET STRING POINTER
CALL WRITESTR ; PRINT IT
;
; ALL DONE IF NO SCC WAS DETECTED
LD A,(IY+1) ; GET SCC TYPE BYTE
OR A ; SET FLAGS
RET Z ; IF ZERO, NOT PRESENT
;
PRTS(" MODE=$") ; FORMATTING
LD E,(IY+5) ; LOAD CONFIG
LD D,(IY+6) ; ... WORD TO DE
CALL PS_PRTSC0 ; PRINT CONFIG
;
XOR A
RET
;
;
;
SCC_TYPE_MAP:
.DW SCC_STR_NONE
.DW SCC_STR_SCC
.DW SCC_STR_ESCC
SCC_STR_NONE .DB "<NOT PRESENT>$"
SCC_STR_SCC .DB "8530$"
SCC_STR_ESCC .DB "85230$"
;
; WORKING VARIABLES
;
SCC_DEV .DB 0 ; DEVICE NUM USED DURING INIT
SCC_MAP .DB 0 ; CHIP PRESENCE BITMAP
;
#IF ((SCCINTS) & (INTMODE > 0))
;
; SCC0 CHANNEL A RECEIVE BUFFER
SCC0A_RCVBUF:
SCC0A_CNT .DB 0 ; CHARACTERS IN RING BUFFER
SCC0A_HD .DW SCC0A_BUF ; BUFFER HEAD POINTER
SCC0A_TL .DW SCC0A_BUF ; BUFFER TAIL POINTER
SCC0A_BUF .FILL SCC_BUFSZ,0 ; RECEIVE RING BUFFER
;
; SCC0 CHANNEL B RECEIVE BUFFER
SCC0B_RCVBUF:
SCC0B_CNT .DB 0 ; CHARACTERS IN RING BUFFER
SCC0B_HD .DW SCC0B_BUF ; BUFFER HEAD POINTER
SCC0B_TL .DW SCC0B_BUF ; BUFFER TAIL POINTER
SCC0B_BUF .FILL SCC_BUFSZ,0 ; RECEIVE RING BUFFER
;
#IF (SCCCNT >= 2)
;
; SCC1 CHANNEL A RECEIVE BUFFER
SCC1A_RCVBUF:
SCC1A_CNT .DB 0 ; CHARACTERS IN RING BUFFER
SCC1A_HD .DW SCC1A_BUF ; BUFFER HEAD POINTER
SCC1A_TL .DW SCC1A_BUF ; BUFFER TAIL POINTER
SCC1A_BUF .FILL SCC_BUFSZ,0 ; RECEIVE RING BUFFER
;
; SCC1 CHANNEL B RECEIVE BUFFER
SCC1B_RCVBUF:
SCC1B_CNT .DB 0 ; CHARACTERS IN RING BUFFER
SCC1B_HD .DW SCC1B_BUF ; BUFFER HEAD POINTER
SCC1B_TL .DW SCC1B_BUF ; BUFFER TAIL POINTER
SCC1B_BUF .FILL SCC_BUFSZ,0 ; RECEIVE RING BUFFER
;
#ENDIF
;
#ELSE
;
SCC0A_RCVBUF .EQU 0
SCC0B_RCVBUF .EQU 0
;
#IF (SCCCNT >= 2)
SCC1A_RCVBUF .EQU 0
SCC1B_RCVBUF .EQU 0
#ENDIF
;
#ENDIF
;
; SCC PORT TABLE
;
SCC_CFG:
; SCC0 CHANNEL A
SCC0A_CFG:
.DB 0 ; DEVICE NUMBER (SET DURING INIT)
.DB 0 ; SCC TYPE (SET DURING INIT)
.DB $00 ; CHIP 0 / CHANNEL A (LOW BIT IS CHANNEL)
.DB SCC0A_CMD ; CMD/STATUS PORT
.DB SCC0A_DAT ; DATA PORT
.DW SCC0ACFG ; LINE CONFIGURATION
.DW SCC0A_RCVBUF ; POINTER TO RCV BUFFER STRUCT
.DW SCC0ACLK & $FFFF ; CLOCK FREQ AS
.DW SCC0ACLK >> 16 ; ... DWORD VALUE
.DB SCC0ACTCC ; CTC CHANNEL
.DB SCC0MODE ; MODE
;
DEVECHO "SCC MODE="
#IF (SCC0MODE == SCCMODE_STD)
DEVECHO "STD"
#ENDIF
#IF (SCC0MODE == SCCMODE_SZ80)
DEVECHO "SZ80"
#ENDIF
DEVECHO ", IO="
DEVECHO SCC0BASE
DEVECHO ", CHANNEL A"
#IF ((SCCINTS) & (INTMODE > 0))
DEVECHO ", INTERRUPTS ENABLED"
#ENDIF
DEVECHO "\n"
;
SCC_CFGSIZ .EQU $ - SCC_CFG ; SIZE OF ONE CFG TABLE ENTRY
;
; SCC0 CHANNEL B
SCC0B_CFG:
.DB 0 ; DEVICE NUMBER (SET DURING INIT)
.DB 0 ; SCC TYPE (SET DURING INIT)
.DB $01 ; CHIP 0 / CHANNEL B (LOW BIT IS CHANNEL)
.DB SCC0B_CMD ; CMD/STATUS PORT
.DB SCC0B_DAT ; DATA PORT
.DW SCC0BCFG ; LINE CONFIGURATION
.DW SCC0B_RCVBUF ; POINTER TO RCV BUFFER STRUCT
.DW SCC0BCLK & $FFFF ; CLOCK FREQ AS
.DW SCC0BCLK >> 16 ; ... DWORD VALUE
.DB SCC0BCTCC ; CTC CHANNEL
.DB SCC0MODE ; MODE
;
DEVECHO "SCC MODE="
#IF (SCC0MODE == SCCMODE_STD)
DEVECHO "STD"
#ENDIF
#IF (SCC0MODE == SCCMODE_SZ80)
DEVECHO "SZ80"
#ENDIF
DEVECHO ", IO="
DEVECHO SCC0BASE
DEVECHO ", CHANNEL B"
#IF ((SCCINTS) & (INTMODE > 0))
DEVECHO ", INTERRUPTS ENABLED"
#ENDIF
DEVECHO "\n"
;
#IF (SCCCNT >= 2)
;
; SCC1 CHANNEL A
SCC1A_CFG:
.DB 0 ; DEVICE NUMBER (SET DURING INIT)
.DB 0 ; SCC TYPE (SET DURING INIT)
.DB $02 ; CHIP 1 / CHANNEL A (LOW BIT IS CHANNEL)
.DB SCC1A_CMD ; CMD/STATUS PORT
.DB SCC1A_DAT ; DATA PORT
.DW SCC1ACFG ; LINE CONFIGURATION
.DW SCC1A_RCVBUF ; POINTER TO RCV BUFFER STRUCT
.DW SCC1ACLK & $FFFF ; CLOCK FREQ AS
.DW SCC1ACLK >> 16 ; ... DWORD VALUE
.DB SCC1ACTCC ; CTC CHANNEL
.DB SCC1MODE ; MODE
;
DEVECHO "SCC MODE="
#IF (SCC1MODE == SCCMODE_STD)
DEVECHO "STD"
#ENDIF
#IF (SCC1MODE == SCCMODE_SZ80)
DEVECHO "SZ80"
#ENDIF
DEVECHO ", IO="
DEVECHO SCC1BASE
DEVECHO ", CHANNEL A"
#IF ((SCCINTS) & (INTMODE > 0))
DEVECHO ", INTERRUPTS ENABLED"
#ENDIF
DEVECHO "\n"
;
; SCC1 CHANNEL B
SCC1B_CFG:
.DB 0 ; DEVICE NUMBER (SET DURING INIT)
.DB 0 ; SCC TYPE (SET DURING INIT)
.DB $03 ; CHIP 1 / CHANNEL B (LOW BIT IS CHANNEL)
.DB SCC1B_CMD ; CMD/STATUS PORT
.DB SCC1B_DAT ; DATA PORT
.DW SCC1BCFG ; LINE CONFIGURATION
.DW SCC1B_RCVBUF ; POINTER TO RCV BUFFER STRUCT
.DW SCC1BCLK & $FFFF ; CLOCK FREQ AS
.DW SCC1BCLK >> 16 ; ... DWORD VALUE
.DB SCC1BCTCC ; CTC CHANNEL
.DB SCC1MODE ; MODE
;
DEVECHO "SCC MODE="
#IF (SCC1MODE == SCCMODE_STD)
DEVECHO "STD"
#ENDIF
#IF (SCC1MODE == SCCMODE_SZ80)
DEVECHO "SZ80"
#ENDIF
DEVECHO ", IO="
DEVECHO SCC1BASE
DEVECHO ", CHANNEL B"
#IF ((SCCINTS) & (INTMODE > 0))
DEVECHO ", INTERRUPTS ENABLED"
#ENDIF
DEVECHO "\n"
;
#ENDIF
;
SCC_CFGCNT .EQU ($ - SCC_CFG) / SCC_CFGSIZ
;
;--------------------------------------------------------------------------------------------------
; HBIOS MODULE TRAILER
;--------------------------------------------------------------------------------------------------
;
END_SCC .EQU $
SIZ_SCC .EQU END_SCC - ORG_SCC
;
MEMECHO "SCC occupies "
MEMECHO SIZ_SCC
MEMECHO " bytes.\n"