        IF      IODRIVERBODY
        PAGE    *** CLOCK: DRIVER ***
CR              EQU     $0D
*
SSBCLKCNT100US  EQU     $F700          100 US COUNTER
SSBCLKCNT10MS   EQU     $F701          10 MS COUNTER
SSBCLKCNTSEC    EQU     $F702          SECONDS COUNTER
SSBCLKCNTMIN    EQU     $F703          MINUTES COUNTER
SSBCLKCNTHRS    EQU     $F704          HOURS COUNTER
SSBCLKCNTDAYS   EQU     $F706          DAYS COUNTER
SSBCLKCNTMNTHS  EQU     $F707          MONTHS COUNTER
SSBCLKRAM100US  EQU     $F708          RAM 100 US COUNTER
SSBCLKRAM10MS   EQU     $F709          RAM 10MS COUNTER
SSBCLKRAMSEC    EQU     $F70A          RAM SECONDS COUNTER
SSBCLKRAMMIN    EQU     $F70B          RAM MINUTES COUNTER
SSBCLKRAMHRS    EQU     $F70C          RAM HOURS COUNTER
SSBCLKRAMDAYS   EQU     $F70E          RAM DAYS COUNTER
SSBCLKRAMMNTHS  EQU     $F70F          RAM MONTHS COUNTER
SSBCLKISR       EQU     $F710          INTERRUPT STATUS REGISTER
SSBCLKICR       EQU     $F711          INTERRUPT CONTROL REGISTER
SSBSTATUSBIT    EQU     $F714          ROLLOVER OCCURED DURING READ
*
CLOCKDRIVER     FDB     CLOCKOPEN
        FDB     CLOCKCLOSE
        FDB     CLOCKREADA
        FDB     ILLDEVICEOP     WRITEA IS A NO-NO
        FDB     CLOCKREADB
        FDB     CLOCKWRITEB
        FDB     ILLDEVICEOP     YOU UPDATE THE CLOCK, NOT REBUILD IT (CREATE)
        FDB     ILLDEVICEOP     RENAME IT TO WHAT? CLOCK-RADIO: ???
        FDB     ILLDEVICEOP     YOU CAN'T GET RID OF THE CLOCK, NEITHER
        FDB     ILLDEVICEOP     NO CONTROL FUNCTIONS
        FDB     CLOCKSTATUS     SAY "I'M A CLOCK, TICK-TOCK"
        FDB     CLOCKRESET      
        FDB     CLOCKPFRESTART  WHO UNPLUGGED ME???
*
CLOCKCLOSE      EQU     *       WHAT AM I SUPPOSED TO DO, PUT THE CLOCK AWAY??
CLOCKOPEN       EQU     *       HOW ABOUT LOOKING AT YOUR $9 TI CHEAPIE, MAC!
CLOCKPFRESTART  EQU     *       AM I SUPPOSED TO KEEP TIME WITH NO POWER???
        OKRTS           TOUGH!

*
CLOCKSPRUNG     JMP     ILLDEVICEOP

CLOCKSTATUS     CMPA    #SC:GETTYPE
        BNE     CLOCKSPRUNG
        LDX     SDOS+SDOS:IOBLOCKPTR
        JSR     SDOS+SDOS:CHECKRDLEN    HAS HE GOT A BYTE SPACE
        FDB     1
        LDX     SCBLK:RDBUF,X   GET THE BUFFER POINTER
        LDAA    #DVTYP.CLOCK    I'M ALIVE AND TICKING (HOPEFULLY!)
        STAA    DVTYP:TYPE,X
        OKRTS

CLOCKWRITEB     JSR     SDOS+SDOS:CHECKWRLEN    HAS HE GOT 6 BYTES?
        FDB     6
        LDX     SCBLK:WRBUF,X   WRITE BUFFER POINTER
        LDAB    #6
CLOCKWB1
        LDAA    0,X
        INX
        PSHA
        DECB
        BNE     CLOCKWB1
        NOP                     DON'T WANT TO UPDATE THE CLOCK WHILE SETTING IT
        SEI
        LDX     #SDOS
        LDAB    #6
CLOCKWB2
        PULA
        STAA    SDOS:CLOCK+5,X
        DEX
        DECB
        BNE     CLOCKWB2
        CLI
*
*       SET THE HARDWARE CLOCK FROM THE USER'S WRITE BUFFER
*
SSBSETCLOCK
        LDX     SDOS+SDOS:IOBLOCKPTR   GET ADDRESS OF I/O BLOCK
        LDX     SCBLK:WRBUF,X          GET ADDRESS OF WRITE BUFFER
        LDD     ,X++                   MOVE THE TIME INTO CLOCKBUFFER
        STD     CLOCKBUFFER
        LDD     ,X++                   GET LSB OF TIME AND MONTH
        STA     CLOCKBUFFER+2
        STB     SSBCLKCNTDAYS          REMEMBER THE MONTH
        LDD     ,X                     GET DAY AND YEAR
        STA     SSBCLKCNTMNTHS         REMEMBER THE DAY
        BSR     SETYEAR
        JSR     DIVIDEBY60             NOW DIVIDEND HAS SECONDS
        LDX     #SSBCLKCNTSEC
        BSR     MAKEBCD                SAVE SECONDS
        BSR     MAKEBCD                SAVE MINUTES
*       BSR     MAKEBCD                SAVE HOURS
*
MAKEBCD JSR     DIVIDEBY60
        ASLB
        ASLB
        ASLB
        ASLB
        PSHS    A
        ADDB    ,S+
        STB     ,X+
        OKRTS                          AND WE'S DONE!
*
SETYEAR BSR     SETYEAR1               STORE 4 BITS OF THE YEAR IN EACH BYTE
        STA     SSBCLKRAMDAYS
SETYEAR1
        ROLD
        ROLD
        ASLA
        ASLA
        ROLD
        ROLD
        ORA     #$CC
        STA     SSBCLKRAMMNTHS
        RTS
*
*
*
CLOCKREADB      JSR     SDOS+SDOS:CHECKRDLEN
        FDB     6       HE BETTER HAVE 6 BYTES AT LEAST
        NOP                     DON'T WANT CLOCK UPDATED WHILE READING IT
        SEI
        LDX     #SDOS
        LDAB    #6
CLOCKRB1
        LDAA    SDOS:CLOCK,X
        INX
        PSHA
        DECB
        BNE     CLOCKRB1
        CLI
        LDX     SDOS+SDOS:IOBLOCKPTR
        LDX     SCBLK:RDBUF,X
        LDAB    #6
CLOCKRB2        PULA
        STAA    5,X
        DEX
        DECB
        BNE     CLOCKRB2
        OKRTS
*
*
*
CLOCKREADA
        JSR     SDOS+SDOS:CHECKRDLEN
        FDB     17                     ENOUGH FOR HH:MM:SS MM/DD/YY
        BSR     CLOCKGETTD             GET TIME, DATE FROM SDOS
        BSR     CLOCKDATE              FORMAT DATE
        JSR     CLOCKTIME              FORMAT TIME
        LDX     SDOS+SDOS:IOBLOCKPTR
        LDD     SCBLK:RDLEN,X          SEE IF ENOUGH SPACE FOR CR
        CMPD    #18
        BLO     CLOCKREADA1            B/ NO
        INC     SCBLK:RPLEN+1,X        YES, INCLUDE THE CR IN THE COUNT
CLOCKREADA1
        LDX     SCBLK:RDBUF,X
        BLO     CLOCKREADA2            B/ CR NOT INCLUDED
        LDA     #CR
        STA     17,X
CLOCKREADA2
        LDAA    #17
        STX     TEMPX
        LDX     #TIME$
CLOCKREADA3
        LDAB    0,X
        INX
        STX     TEMPX+2
        LDX     TEMPX
        STAB    0,X
        INX
        STX     TEMPX
        LDX     TEMPX+2
        DECA
        BNE     CLOCKREADA3
        OKRTS
*
*
*
CLOCKGETTD
        NOP
        SEI
        LDX     #SDOS
        LDAB    #6
CLOCKGETTD1
        LDAA    SDOS:CLOCK,X
        INX
        PSHA
        DECB
        BNE     CLOCKGETTD1
        CLI
        LDX     #CLOCKBUFFER
        LDAB    #6
CLOCKGETTD2
        PULA
        STAA    5,X
        DEX
        DECB
        BNE     CLOCKGETTD2
        RTS
*
*
*
DATE    BSR     CLOCKGETTD
CLOCKDATE       LDAA    MONTH
        BSR     BCDTOASC
        STAA    DATE$:MONTH
        STAB    DATE$:MONTH+1
        LDAA    DAY
        BSR     BCDTOASC
        STAA    DATE$:DAY
        STAB    DATE$:DAY+1
        LDAA    YEAR
        BSR     BCDTOASC
        STAA    DATE$:YEAR
        STAB    DATE$:YEAR+1
        LDX     #DATE$
        LDAA    #8
        RTS
*
BCDTOASC        TAB
        ANDB    #$F
        ADDB    #'0
        LSRA
        LSRA
        LSRA
        LSRA
        ADDA    #'0
        RTS
*
*
CLOCKTIME       JSR     DIVIDEBY60      NOW DIVIDEND HAS SECONDS
        LDX     #TIME$:SECONDS
        BSR     CLOCKMAKEXX
        LDX     #TIME$:MINUTES
        BSR     CLOCKMAKEXX
        LDX     #TIME$:HOURS
        BSR     CLOCKMAKEXX
        LDX     #TIME$
        LDAA    #8
        RTS
*
CLOCKMAKEXX     JSR     DIVIDEBY60
        ADDA    #'0
        ADDB    #'0
        STAB    0,X
        STAA    1,X
        RTS
*
*       DIVIDE BY 60 -- DIVIDE 3 BYTE "DIV60DIVIDEND" BY 60
*               DIVIDEND:=DIVIDEND/60
*               (A):=REMAINDER MOD 10
*               (B):=INT(REMAINDER/10)
DIVIDEBY60      EQU     *
        LDAB    #3*8+1  NUMBER OF BITS
        CLRA
DIVIDE60L       EQU     *
        ROLA
        ADDA    #-60
        BCS     DIVIDE60L2
        SUBA    #-60
DIVIDE60L2
        ROL     DIV60DIVIDEND+2
        ROL     DIV60DIVIDEND+1
        ROL     DIV60DIVIDEND+0
        DECB
        BNE     DIVIDE60L
        LDAB    #-1
DIVIDE60L3      INCB
        SUBA    #10
        BCC     DIVIDE60L3
        ADDA    #10
        RTS
*
*       READ THE CLOCK AND SET THE SDOS CLOCK
*       ASSERT INTERRUPTS ARE ALREADY OFF HERE
*       BECAUSE THIS IS CALLED ONLY ONCE FROM THE RESET CODE (INTS OFF)
*       AND ON THE FIRST CLOCK INTERRUPT (INTS OFF)
*
SSBREADCLOCK1
        LEAS    2,S                    POP THE BAD BYTES
SSBREADCLOCK
        LDA     SSBSTATUSBIT           CLEAR THE ROLLOVER STATUS
        LDA     SSBCLKCNTDAYS          GET DAYS
        LDB     SSBCLKCNTMNTHS         GET MONTHS
        STD     SDOS+SDOS:CLOCK+3      SAVE DAYS AND MONTHS
        LDA     SSBCLKCNTMIN           GET MINUTES
        LDB     SSBCLKCNTSEC           GET SECONDS
        PSHS    D
        LDA     SSBCLKCNTHRS           GET HOURS
        LDB     SSBSTATUSBIT           DID IT ROLL OVER DURING READ?
        BNE     SSBREADCLOCK1          B/ YES, DO IT ALL AGAIN
        PSHS    A
        BSR     GETYEAR                GET THE YEAR (KEPT IN NON-VOLITLE RAM)
        STB     SDOS+SDOS:CLOCK+5      SAVE THE YEAR
        CLR     SDOS+SDOS:CLOCK
        CLR     SDOS+SDOS:CLOCK+1
        CLR     SDOS+SDOS:CLOCK+2
        PULS    A
        BSR     MUL60                  START WITH HOURS
        PULS    A
        BSR     MUL60                  THEN DO MINUTES
        PULS    A
        BSR     MUL60                  THEN DO SECONDS
        CLRA
*       BSR     MUL60                  THEN DO 1/60 SECONDS
*
*       A CONTAINS BCD DIGIT
*       MULTIPLY CLOCK BUFFER BY 60 AND ADD A
*
MUL60   PSHS    A
        LSRA
        LSRA
        LSRA
        LSRA
        LDB     #10
        MUL
        LDA     ,S
        STB     ,S
        ANDA    #$0F
        ADDA    ,S+
        LDX     #SDOS+SDOS:CLOCK+3
MUL60.1 LDB     ,-X
        STA     ,X
        LDA     #60
        MUL
        ADDB    ,X
        ADCA    #0
        STB     ,X
        CMPX    #SDOS+SDOS:CLOCK
        BNE     MUL60.1
        OKRTS   ; SEE CLOCK INIT ROUTINE BEFORE YOU THINK THIS IS UNNECESSARY
*
GETYEAR LDA     SSBCLKRAMMNTHS         4 BITS OF THE YEAR IN EACH BYTE
        BSR     GETYEAR1
        LDA     SSBCLKRAMDAYS
GETYEAR1
        RORD
        RORD
        ASRA
        ASRA
        RORD
        RORD
        RTS
        PAGE    CLOCK INTERRUPT ROUTINE
*
*       ONCE AROUND THIS LOOP GIVES US 3 CLOCK TICKS IN 50 MS
*       WHICH IS 60 CLOCK TICKS IN 1 SEC (1000MS)
*       WHICH IS 1 CLOCK TICK EVERY 1/60 SEC ON THE AVERAGE
*       WHICH IS WHAT SDOS LIKES TO SEE
*
CLOCKINT
        JSR     SSBREADCLOCK           ENSURE CLOCK IS ACCURATE ON FIRST INT
CLOCKINT1
        LDA     #$60                   DO 16 MS
        BSR     CLOCKDONE
        LDA     #$70                   DO 17 MS
        BSR     CLOCKDONE
        LDA     #$70                   DO 17 MS
        BSR     CLOCKDONE
        BRA     CLOCKINT1
*
CLOCKDONE
        ADDA    SSBCLKRAM100US         ADD IN 16 OR 17 MS TO THE RAM COUNTER
        DAA
        STA     SSBCLKRAM100US
        LDA     #$01
        ADCA    SSBCLKRAM10MS
        DAA                            IT'S BCD
        ORA     #$C0                   DON'T CARE FOR 1/10 SEC
        STA     SSBCLKRAM10MS
        PULS    D                      SETUP INTERRUPT CONTINUE ADDRESS
        STD     CLOCKSTATE+1
        LDA     #1                     SIGNAL 1 CLOCK TICK
        JMP     MT:CLOCKTICKED         DONE
        FIN     IODRIVERBODY
        IF      IODRIVERPOLL
        PAGE    CLOCK INTERRUPT POLL CODE
*
*       CLOCK INTERRUPT POLL CODE
*
        LDA     SSBCLKISR
        BEQ     NOTCLOCKINT            B/ IT'S NOT THE CLOCK
        BITA    #$FE                   NONE OF THESE BITS SHOULD BE SET
        BNE     CLOCKPOLL1             B/ SOMETHING IS WRONG (SOME OF THEM ARE)
        LDX     #CLOCKDCB
        JMP     CLOCKSTATE
CLOCKPOLL1
        SWI
NOTCLOCKINT
        FIN     IODRIVERPOLL
        IF      IODRIVERRAM
CLOCKDCB
        FCB     1                      CLOCK'S ALWAYS DONE
        FDB     0                      LASTER
        FDB     CLOCKSTR
        FDB     NEXTDEVICEDCB
        FDB     CLOCKDRIVER
CLOCKSTATE
        JMP     CLOCKINT
DIV60DIVIDEND   EQU     *
CLOCKBUFFER     FCB     0,0,0
DAY     FCB     0
MONTH   FCB     0
YEAR    FCB     0
CLOCKFRACTION   FCB     -1
CLOCKSTR        FCC     'CLOCK:'
        FCB     0
*
NEXTDEVICEDCB   SET     CLOCKDCB
*
TIME$   EQU     *
TIME$:HOURS     FCC     '00:'
TIME$:MINUTES   FCC     '00:'
TIME$:SECONDS   FCC     '00 '
DATE$   EQU     *
DATE$:MONTH     FCC     '00/'
DATE$:DAY       FCC     '00/'
DATE$:YEAR      FCC     '00'
*
        FIN     IODRIVERRAM
        IF      IODRIVERINIT
CLOCKRESET
        LDX     #SSBCLKRAM100US
        CLR     ,X+                    CLEAR THE 100US RAM
        LDA     #$C0
        STA     ,X+                    SET DON'T CARE FOR HALF OF 10MS RAM
        LDA     #$CC                   
        STA     ,X+                    SET DON'T CARE FOR SECONDS
        STA     ,X+                    SET DON'T CARE FOR MINUTES
        STA     ,X+                    SET DON'T CARE FOR HOURS
        STA     ,X+                    SET DON'T CARE FOR DAY OF WEEK
        ORA     ,X                     SET DON'T CARE FOR DAYS
        STA     ,X+
        LDA     #$CC                   SET DON'T CARE FOR MONTHS
        ORA     ,X
        STA     ,X+
        LDA     ,X+                    READ THE CLOCK ISR FOR GOOD LUCK
        LDA     #1                     ENABLE 'COMPARE' INTERRUPT
        STA     ,X+                    IN THE CLOCK ICR
        JSR     GETYEAR
        CMPB    #EDITYEAR&$FF
        BHS     CLOCKRESETDONE         B/ VALID YEAR SET
*
*       INVALID YEAR, FORCE USER TO SET THE TIME
*
        LDB     #EDITYEAR&$FF          SET THE YEAR = EDIT YEAR
        JSR     SETYEAR
        CLR     SSBCLKCNTDAYS          CLEAR OUT THE DAY
        CLR     SSBCLKCNTMNTHS         CLEAR OUT THE MONTH
        OKRTS
CLOCKRESETDONE
        JMP     SSBREADCLOCK           SET THE INITIAL TIME
        FIN     IODRIVERINIT
