********************************************************************
* mc09rtc - read/write DS1802 RTC attached to multicomp09 GPIO
*
* Rather than bloat the timer module (which is memory-resident)
* this is a stand-alone utility that can either read the RTC and
* update the system time, or read the system time and update the
* RTC. It can be run from the startup file to set the time at boot.
*
* usage:
*
* mc09rtc -r
* read RTC and update system time
* mc09rtc -w
* write RTC with current system time
* mc09rtc -d
* dump RTC clock and RAM contents
*
* This is for the 6809: it contains no 6309-specific code.
*
*   +-----------------+  <--  Y          (highest address)
*   !                 !
*   !   Parameter     !
*   !     Area        !
*   !                 !
*   +-----------------+  <-- X, SP
*   !                 !
*   !   Data Area     !
*   !                 !
*   +- - - - - - - - -+
*   !   Direct Page   !
*   +-----------------+  <-- U, DP       (lowest address)
*
*   D = parameter area size
*  PC = module entry point abs. address
*  CC = F=0, I=0, others undefined
*
*
* Edt/Rev  YYYY/MM/DD  Modified by
* Comment
* ------------------------------------------------------------------
*   1      2015/11/07  Neal Crook
* Created.
*
         nam   mc09rtc
         ttl   Read/write DS1802 RTC, copy to/from system time

         use   defsfile

tylg     set   Prgrm+Objct
atrv     set   ReEnt+rev
rev      set   $00
edition  set   1

         mod   eom,name,tylg,atrv,start,size

* Bit-masks for GPIO bits connected to RTC
RTCCE     EQU 4      bit 2
RTCCLK    EQU 2      bit 1
RTCDAT    EQU 1      bit 0

         org   0
* buffer for NITROS9-format 6-byte "time packet"
sysbuf   rmb    0       same as sysyear. [NAC HACK 2015Nov17] without "rmb 0" this becomes an offset in *code* space..
sysyear  rmb    1
sysmonth rmb    1
sysdate  rmb    1       documentation refers to this as "day"
syshour  rmb    1
sysmin   rmb    1
syssec   rmb    1

* buffer for RTC time registers (and, in -D, for RTC RAM)
rtcbuf  rmb     0       same as rtcsec. [NAC HACK 2015Nov17] without "rmb 0" this becomes an offset in *code* space..
rtcsec  rmb     1
rtcmin  rmb     1
rtchour rmb     1
rtcdate rmb     1
rtcmonth rmb    1
rtcday  rmb     1
rtcyear rmb     1
rtcprot rmb     1       have to read this as part of the burst
rtcxxx  rmb     23      31 bytes total, to hold RTC RAM

txtbuf  rmb   80        text output buffer
        rmb   200       stack
size    equ   .

name    fcs   /mc09rtc/
        fcb   edition

WrMsg   fcc     'RTC updated from system time'
WrMsgE
RdMsg   fcc     'System time updated from RTC'
RdMsgE

DMsg1   fcc     'Yr Mo Dt Hr Mi Se'
        fcb     C$CR
DMsg2   fcc     '        NitrOS9 binary "time packet"'
        fcb     C$CR
DMsg3   fcc     'Se Mi Hr Dt Mo Dy Yr Pr'
        fcb     C$CR
DMsg4   fcc     '  DS1302 RTC BCD Date/protection'
        fcb     C$CR
DMsg5   fcc     '                       trickle'
        fcb     C$CR
DMsg6   fcc     '  RAM'
DMsg7   fcb     C$CR


********************************************************************
* Start of program/Entry point
start   pshs    x               preserve pointer to cmd line args

* Load time from RTC - for use by -R and -D options.
        lbsr    initio
        lda     #$BF            cmd for clock read burst
        ldx     #8              read 8 bytes
        leay    rtcbuf,u        where to put it: the RTC buffer
        lbsr    rd_n

* Load system time - for use by -W and -D options.
        leax    sysbuf,u        buffer
        os9     F$Time          read system time into buffer
        bcs     badexit         something bad happened

* Now, what are we expected to do?
        puls    x
        lda     ,x+             first char of cmd-line arguments
*                               leading spaces stripped by shell
        cmpa    #$2D            require "-" for -R -W -D
        bne     badexit         not found so set carry and exit
        lda     ,x+             character after "-"
        anda    #$df            force to upper case
        cmpa    #'R
        beq     read
        cmpa    #'W
        lbeq     write
        cmpa    #'D
        lbeq    dump
badexit clrb                    exit code=0
        orcc    #1              set C to indicate error
        os9     F$Exit

********************************************************************
* "-R" - read from RTC and update system time.
read
* Time from RTC has already been loaded in.
* Convert it and move it from RTC buffer to system time buffer.
        lda     <rtcsec         0-59
        anda    #$7f            omit CH flag
        bsr     bcd2bin
        sta     <syssec

        lda     <rtcmin         0-59
        bsr     bcd2bin
        sta     <sysmin

        lda     <rtchour        0-23
        anda    #$3f            omit 12/24 flag
        bsr     bcd2bin
        sta     <syshour

        lda     <rtcdate        1-31
        bsr     bcd2bin
        sta     <sysdate

        lda     <rtcmonth       1-12
        bsr     bcd2bin
        sta     <sysmonth

        lda     <rtcyear        0-99
        bsr     bcd2bin
        adda    #100            nitros stores year as offset from 1900
        sta     <sysyear

        leax    sysbuf,u        buffer
        os9     F$STime         set system time from buffer

* Report our action and exit
        leax    >RdMsg,pcr      point to message
        ldy     #RdMsgE-RdMsg   length
        lbra    prexit


********************************************************************
bcd2bin
*
* in  A= value in BCD
* out A= value in binary
*
* eg: $10 in, $0A out
* CC modified, all other registers preserved.
*
* algorithm: take tens digit, multiply by 8 and then by 2 and add
* those two values to the units.
        pshs    b
        tfr     a,b
        anda    #$0f            units in A
        andb    #$f0            tens in B
        asrb                    tens move from *16 position to *8 position
        pshs    b               }
        adda    ,s+             } add b (8/10ths of tens) to a
        asrb
        asrb                    tens * 2
        pshs    b               }
        adda    ,s+             } units + 8/10ths + 2/10ths of tens
        puls    b,pc


********************************************************************
bin2bcd
*
* in  A= value in binary
* out A= value in BCD
*
* eg: $16 in, $24 out
* CC modified, all other registers preserved.
*
* algorithm: do successive subtract of $0a and increment counter by
* $10 each time. When result is less than $0a, add in count value.
*
        pshs    b
        clrb
bcdnxt  cmpa    #$0a
        blo     bcddone         branch if lower ie 0-9 remaining
        addb    #$10            bump up by bcd 10
        suba    #$0a            as we subtract by binary 10
        bra     bcdnxt          and go round again

bcddone pshs    b               } add b to a
        adda    ,s+             }
        puls    b,pc


********************************************************************
* "-W" - write system time to RTC.
write
* System time has already been loaded in.
* Convert it and move it from system time buffer to RTC buffer.
        lda     <syssec         0-59 (I hope)
        bsr     bin2bcd
        sta     <rtcsec         CH bit is 0 so clock runs

        lda     <sysmin         0-59 (I hope)
        bsr     bin2bcd
        sta     <rtcmin

        lda     <syshour        0-23 (I hope)
        anda    #$3f            select 24-hour clock
        bsr     bin2bcd
        sta     <rtchour

        lda     <sysdate        1-31 (I hope)
        bsr     bin2bcd
        sta     <rtcdate

        lda     <sysmonth       1-12 (I hope)
        bsr     bin2bcd
        sta     <rtcmonth

        lda     #1
        sta     <rtcday         1-7. Not used but want it legal

        lda     <sysyear        100-199 (I hope)
        suba    #100            [NAC HACK 2015Nov18] check first!! Error if last century.
        bsr     bin2bcd
        sta     <rtcyear

* Write buffer to RTC
        lbsr    initio
        lda     #$BE            cmd for clock write burst
        ldx     #8              read 8 bytes
        leay    rtcbuf,u        get it from the RTC buffer
        lbsr    wr_n

* Report our action and exit
        leax    >WrMsg,pcr      point to message
        ldy     #WrMsgE-WrMsg   length
        lbra    prexit

********************************************************************
* "-D" - dump RTC contents.
dump
* system and RTC buffer are already loaded. Print them out then
* load and report in addition
* 1 byte from trickle-charge register, 31 bytes from RAM
*
* Yr Mo Dt Hr Mi Se
* xx xx xx xx xx xx         NitrOS9 binary "time packet"
*
* Se Mi Hr Dt Mo Dy Yr Pr
* xx xx xx xx xx xx xx xx   DS1302 RTC BCD Date/protection
* xx                        trickle
* xx xx xx xx xx xx xx xx   RAM
* xx xx xx xx xx xx xx xx
* xx xx xx xx xx xx xx xx
* xx xx xx xx xx xx xx
*

        leax    >DMsg1,pcr      point to message
        ldy     #80             max length
        lbsr    prbufb          print from x or die in the attempt

        * display content of system time packet buffer
        leay    sysbuf,u        data to display
        leax    >txtbuf,pcr     buffer to store ASCII version
        lda     #6              bytes to convert
        lbsr    hex2buf
        ldy     #18             2 digits + 1 space per
        lbsr    prbufa          print txtbuf or die in the attempt

        leax    >DMsg2,pcr      point to message
        ldy     #80             max length
        lbsr    prbufb          print from x or die in the attempt

        leax    >DMsg7,pcr      point to message - extra CR
        ldy     #80             max length
        lbsr    prbufb          print from x or die in the attempt

        leax    >DMsg3,pcr      point to message
        ldy     #80             max length
        lbsr    prbufb          print from x or die in the attempt

        * display content of RTC time buffer
        leay    rtcbuf,u        data to display
        leax    >txtbuf,pcr     buffer to store ASCII version
        lda     #8              bytes to convert
        lbsr    hex2buf
        ldy     #24             2 digits + 1 space per
        lbsr    prbufa          print txtbuf or die in the attempt

        leax    >DMsg4,pcr      point to message
        ldy     #80             max length
        lbsr    prbufb          print from x or die in the attempt

        * read the 1-byte trickle register
        leay    rtcbuf,u        where to store it
        lda     #$91
        lbsr    putcmd
        lbsr    getbyte
        sta     ,y+
        lbsr    endtrans

        * display 1 byte from RTC time buffer
        leay    rtcbuf,u        data to display
        leax    >txtbuf,pcr     buffer to store ASCII version
        lda     #1              bytes to convert
        lbsr    hex2buf
        ldy     #3              2 digits + 1 space per
        lbsr    prbufa          print txtbuf or die in the attempt

        leax    >DMsg5,pcr      point to message
        ldy     #80             max length
        bsr     prbufb          print from x or die in the attempt

        * read RAM
        leay    rtcbuf,u        where to store it
        lda     #$FF            RAM read burst
        ldx     #31             read 31 bytes
        leay    rtcbuf,u        where to put it: the RTC buffer
        lbsr    rd_n

        * display 1st 8 bytes of RTC RAM from RTC time buffer
        leay    rtcbuf,u        data to display
        leax    >txtbuf,pcr     buffer to store ASCII version
        lda     #8              bytes to convert
        bsr     hex2buf
        ldy     #24             2 digits + 1 space per
        bsr     prbufa          print txtbuf or die in the attempt

        leax    >DMsg6,pcr      point to message
        ldy     #80             max length
        bsr     prbufb          print from x or die in the attempt

        * display 2nd 8 bytes of RTC RAM from RTC time buffer
        leay    8+rtcbuf,u      data to display
        leax    >txtbuf,pcr     buffer to store ASCII version
        lda     #8              bytes to convert
        bsr     hex2buf
        lda     #C$CR           add CR
        sta     ,x+
        ldy     #25             2 digits + 1 space per + CR
        bsr     prbufa          print txtbuf or die in the attempt

        * display 3rd 8 bytes of RTC RAM from RTC time buffer
        leay    16+rtcbuf,u     data to display
        leax    >txtbuf,pcr     buffer to store ASCII version
        lda     #8              bytes to convert
        bsr     hex2buf
        lda     #C$CR           add CR
        sta     ,x+
        ldy     #25             2 digits + 1 space per + CR
        bsr     prbufa          print txtbuf or die in the attempt

        * display 4th 7 bytes of RTC RAM from RTC time buffer
        leay    24+rtcbuf,u     data to display
        leax    >txtbuf,pcr     buffer to store ASCII version
        lda     #7              bytes to convert
        bsr     hex2buf
        lda     #C$CR           add CR
        sta     ,x+
        ldy     #22             2 digits + 1 space per + CR
        leax    >txtbuf,pcr     where to display from
prexit  bsr     prbufb          go display it
        clrb                    success
        os9     F$Exit          and exit


********************************************************************
prbufa  leax    >txtbuf,pcr     where to display from
prbufb  lda     #1              standard out
        os9     I$Writln        print it up to CR
        bcs     prbad
        rts
prbad   os9     F$Exit


********************************************************************
hex2buf
*
* in:   Y binary data to convert
*       X buffer to build ASCII version
*       A number of bytes to convert
*
* out:  Y, X updated
*       A,B,CC modified
        ldb     ,y+             get byte
        bsr     Byte2Hex        store ASCII version in buffer
        ldb     #C$SPAC
        stb     ,x+             add trailing space
        deca
        bne     hex2buf
        rts

********************************************************************
* Convert byte in B to Hex string at X (from debug.asm)
* B,X,CC altered.
Byte2Hex pshs  b		save copy of B on stack
         andb  #$F0		mask upper nibble
         lsrb  			and bring to lower nibble
         lsrb
         lsrb
         lsrb
         bsr   Nibl2Hex		convert byte in B to ASCII
         puls  b		get saved B
         andb  #$0F		do lower nibble
* Convert lower nibble in B to Hex character at X
Nibl2Hex cmpb  #$09		9?
         bls   n@		branch if lower/same
         addb  #$07		else add 7
n@       addb  #'0		and ASCII 0
         stb   ,x+		save B
         rts


********************************************************************
rd_n
* Read from a sequence of RTC locations
*
* X = number of bytes to read
* Y = where to store the read data
* A = command byte
*
* Assumes IO has been initialised.
        pshs    x               modified value is held on stack

        lbsr    putcmd

rd_nxt  lbsr    getbyte         read byte into A
        sta     ,y+             store data byte read
        puls    x               recover count value
        leax    -1,x            update count
        pshs    x               stash count value
        bne     rd_nxt          go do next byte
        bsr     endtrans        tidy up at end

        puls    x,pc            tidy up the stack and return


********************************************************************
wr_n
* Write to a sequence of RTC locations
*
* X = number of bytes to write
* Y = where to get the write data from
* A = command byte
*
* Assumes IO has been initialised.
* Clears write protect first, sets it at end.
        pshs    x               modified value is held on stack
        pshs    a

        * clear the write-protect bit
        lda     #$8e
        lbsr    putcmd
        clra
        lbsr    putbyte
        bsr     endtrans

        puls    a
        lbsr    putcmd

wr_nxt  lda     ,y+             get byte to write
        lbsr    putbyte         write it
        puls    x               recover count value
        leax    -1,x            update count
        pshs    x               stash count value
        bne     wr_nxt          go do next byte
        bsr     endtrans        tidy up at end

        * set the write-protect bit
        lda     #$8e
        bsr     putcmd
        lda     #$80
        bsr     putbyte
        bsr     endtrans

        puls    x,pc            tidy up the stack and return


********************************************************************
* Low-level DS1802 read/write. Broken into the following parts:
*
* initio   - GPIO init
* putcmd   - write 1st (cmd) byte
* putbyte  - write 1 data byte
* getbyte  - read 1 data byte
* endtrans - end read or write transaction
*
* Leave CE and CLK permanently outputs.
* Leave DAT as input by default, make it an output when it needs
* to be but always end a routine with it set to an input. It is OK
* to float DAT because the DS1302 provides an internal pulldown.
*
* designed to be used thus:
* initio (one-time)
* putcmd putbyte [putbyte..] endtrans
* putcmd getbyte [getbyte..] endtrans
*
* Reads from and writes to clock registers *must* be done as
* bursts to take advantage of the internal buffering. Without this,
* it would be necessary to check for carries that occurred between
* accessing one location and the next.


********************************************************************
initio
*
* only need to call this once.
* in:       none
* out:      none
* modifies: A B CC, gpio state.
*
        clrb
        lda     #GPDAT0
        sta     GPIOADR     select dat0
        stb     GPIODAT     set values to 0
        lda     #GPDDR1
        sta     GPIOADR     select ddr1
        incb
        stb     GPIODAT     CE out, CLK out, DAT in.
        rts


********************************************************************
endtrans
*
* take CE back low and wait recovery time.
* in:       none
* out:      none
* modifies: none
*
*        v-------------- send pattern ---------------------V
* CE   H LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL L
* CLK  L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL L
* DAT  z zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz z
*      ^-assumed on entry            xsition(if any) on exit-^
*
*        -------------------------------------------------->
*        wait 4us in this state to meet recovery time
*
        pshs    a,cc
        lda     #GPDAT0
        sta     GPIOADR     select dat0
        clr     GPIODAT     falling edge on CE, keep CLK=0.

        bsr     wait1us
        bsr     wait1us
        bsr     wait1us
        bsr     wait1us
        puls    a,cc,pc


********************************************************************
putcmd
*
* in:       A=command byte to send
* out:      none
* modifies: A B CC X
* enter with CE=0 CLK=0 DAT=0.
* exit with CE=1 and TODO timing met
*
*        v-------------- send pattern ---------------------V
* CE   L HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH H
* CLK  L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL L
* DAT  z zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz z
*      ^-assumed on entry            xsition(if any) on exit-^
*
*        -------------------------------------------------->
*        3us
*
*        v-------------- send pattern ---------------------V
* CE   H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH H
* CLK  L LLLHHHLLLHHHLLLHHHLLLHHHLLLHHHLLLHHHLLLHHHLLLHHHHHH L
* DAT  z 000000111111222222333333444444555555666666777777zzz z
*      ^-assumed on entry            xsition(if any) on exit-^
*
*        <-> 1us high and low time on CLK
*
        pshs    a
        lda     #GPDAT0
        sta     GPIOADR     select dat0
        lda     #RTCCE      assert CE
        sta     GPIODAT

        bsr     wait1us
        bsr     wait1us
        bsr     wait1us

        puls    a
        bra     putbyte


********************************************************************
putbyte
*
* in:       A=data byte to send
* out:      none
* modifies: A B CC X
*
*        v-------------- send pattern ---------------------V
* CE   H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH H
* CLK  L LLLHHHLLLHHHLLLHHHLLLHHHLLLHHHLLLHHHLLLHHHLLLHHHHHH L
* DAT  z 000000111111222222333333444444555555666666777777zzz z
*      ^-assumed on entry            xsition(if any) on exit-^
*
*        <-> 1us high and low time on CLK
*
        ldb     #GPDDR1
        stb     GPIOADR     select ddr1
        clr     GPIODAT     CE out, CLK out, DAT out.

        ldb     #GPDAT0
        stb     GPIOADR     select dat0

        ldx     #8          number of bits to send
pblop
        tfr     a,b
        andb    #$1        keep LSB
        orb     #RTCCE     LSB with CLK=0, CE=1
        stb     GPIODAT    send out data bit with CLK=0
        bsr     wait1us
        eorb    #RTCCLK    set CLK=1
        stb     GPIODAT    send out data bit with CLK=1
        bsr     wait1us
        rora            put next bit in LSB position
        leax    -1,x
        bne     pblop      more bits?

        ldb     #GPDDR1
        stb     GPIOADR     select ddr1
        ldb     #1
        stb     GPIODAT     DAT to input ie z.
        bsr     wait1us

        lda     #GPDAT0
        sta     GPIOADR     select dat0
        lda     #RTCCE
        sta     GPIODAT     falling edge on CLK, keep CE=1.

        rts


********************************************************************
getbyte
*
* in:       none
* out:      A=data byte read
* modifies: A B CC X
*
*        v-------------- send pattern ------------------V
* CE   H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH H
* CLK  L LLLHHHLLLHHHLLLHHHLLLHHHLLLHHHLLLHHHLLLHHHLLLHHH L
* DAT  z 000000111111222222333333444444555555666666777777 0
*      ^-assumed on entry         xsition(if any) on exit-^
*
* at entry falling edge has already occurred that will yield
* the first data bit.
* at exit falling edge has already occurred that will yield
* the first data bit of the next byte. It's no problem if
* no "next byte" is needed; taking CE high will stop everything
* politely.
*
        ldb     #GPDAT0
        stb     GPIOADR         select dat0

        ldx     #8              number of bits to receive
gblop
        bsr     wait1us         wait with clock low
        ldb     #RTCCE|RTCCLK
        stb     GPIODAT         set CLK=1
        ldb     GPIODAT         get DAT in LSB
        * first incoming bit is bit 0 and will rotate
        * all the way down to LSB of A
        rorb                    rotate bit from B into C
        rora                    rotate bit from C into A
        bsr     wait1us         wait with clock high
        ldb     #RTCCE
        stb     GPIODAT         set CLK=0

        leax    -1,x
        bne     gblop           more bits?
        rts


********************************************************************
wait1us
*
* wait for 1us (includes call/return time)
* in:       none
* out:      none
* modifies: none
        nop             [NAC HACK 2015Nov13] tune..
        nop
        nop
        nop
        nop
        rts


********************************************************************
* all done here folks
         emod
eom      equ   *
         end

