Source code consists of assembler API implementation mkhbcos_ds1685.s and 'C' header file mkhbcos_ds1685.h to be used with 'C' programs using the API. I am yet to create simplified versions of these functions to be used/called directly from assembler code without all the unnecessary in such case overhead in coding (e.g: passing/retrieving arguments via stack).
Here is the DS1685 API implementation. Functions are compliant to cc65 calling convention:
;------------------------------------------------------------------
;
; File: mkhbcos_ds1685.s
; Author: Marek Karcz
; Purpose: Implement's initialization routines and API for
; Real Time Clock chip DS1685 with multiplexed
; address bus connected to buffered I/O bus as an I/O
; device.
;
; Revision history:
; 2012-01-31:
; Initial revision.
; (NOTE: These routines will eventually make their way
; to EPROM as a part of firmware. At that time,
; this file will be revised to call up the API
; functions in the kernal table, instead of
; being full implementation.)
;
; 2012-02-06:
; Implementation.
;
;------------------------------------------------------------------
; M.O.S. API defines (kernal)
.define mos_StrPtr $E0
.define tmp_zpgPt $F6
.define IOBase $c000
.define IO4 IOBase+4*256
.define DSCALADDR IO4+1
.define DSCALDATA IO4
.setcpu "6502"
.import ldaxysp,pushax,popax,pusha,popa,staspidx
.import incsp2,ldauidx
.define sp $20
RegB = tmp_zpgPt
RegA = tmp_zpgPt+1
RegXB = tmp_zpgPt+2
RegXA = tmp_zpgPt+3
RegC = tmp_zpgPt+4
Temp = tmp_zpgPt+5
; DS RTC registers mask bits
; reg. A
DSC_REGA_UIP = %10000000
DSC_REGA_DV2 = %01000000
DSC_REGA_DV1 = 100000
DSC_REGA_DV0 = 010000
DSC_REGA_RS3 = 001000
DSC_REGA_RS2 = 000100
DSC_REGA_RS1 = 000010
DSC_REGA_RS0 = 000001
; aliases
DSC_REGA_CTDWN = DSC_REGA_DV2
DSC_REGA_OSCEN = DSC_REGA_DV1
DSC_REGA_BSEL = DSC_REGA_DV0
DSC_REGA_BANK0 = $EF
DSC_REGA_BANK1 = $10
; reg. B
DSC_REGB_SET = %10000000
DSC_REGB_PIE = %01000000
DSC_REGB_AIE = 100000
DSC_REGB_UIE = 010000
DSC_REGB_SQWE = 001000
DSC_REGB_DM = 000100
DSC_REGB_24o12 = 000010
DSC_REGB_DSE = 000001
; aliases
DSC_REGB_UNSET = %01111111
; code
.export _ds1685_init,_ds1685_rdclock,_ds1685_setclock,_ds1685_settime
;,_read
.segment "CODE"
; Initialize DS1685 RTC chip.
; unsigned char __fastcall__ ds1685_init (unsigned char regb,
; unsigned char rega,
; unsigned char regextb,
; unsigned char regexta)
.proc _ds1685_init: near
; get parameters, put them in temp. buffer
jsr pusha
ldy #$03
ldx #$00
lda (sp),y
sta RegB
ldy #$02
ldx #$00
lda (sp),y
sta RegA
ldy #$01
ldx #$00
lda (sp),y
sta RegXB
ldy #$00
ldx #$00
lda (sp),y
sta RegXA
; initialize control register B
ldx RegB
lda #$0b
jsr WrRTC
; read status register C
lda #$0c
jsr RdRTC
sta RegC
; initialize control register A, switch to bank 1
lda RegA
ora #DSC_REGA_BANK1 ; switch to bank 1
tax
lda #$0a
jsr WrRTC
; initialize extended control register B
ldx RegXB
lda #$4b
jsr WrRTC
; initialize extended control register A
ldx RegXA
lda #$4a
jsr WrRTC
; switch to original bank
lda #$0a
jsr RdRTC
and #DSC_REGA_BANK0
tax
lda #$0a
jsr WrRTC
ldx #$00
lda RegC
rts
.endproc
.segment "CODE"
; read clock data
; struct ds1685_clkdata *ds1685_rdclock(struct ds1685_clkdata *buf);
; struct ds1685_clkdata
; {
; unsigned char seconds;
; unsigned char minutes;
; unsigned char hours;
; unsigned char dayofweek;
; unsigned char date; // day
; unsigned char month;
; unsigned char year;
; unsigned char century;
; };
.proc _ds1685_rdclock:near
; disable update transfers
lda #$0b
jsr RdRTC
sta RegB ; save register B for later
ora #DSC_REGB_SET
tax
lda #$0b
jsr WrRTC
; determine mode (BCD or BIN)
lda #DSC_REGB_DM
sta Temp
lda RegB
bit Temp
bne binmoderead
ldy #$01
jsr ldaxysp
jsr incsp2
rts
binmoderead:
; binary mode read
lda #$00 ; load register address of seconds
jsr RdRTC ; read register value to Acc
and #111111 ; mask 2 upper bits
ldy #$00
jsr Tfer2RetBuf ; transfer to return buffer at index 0 (seconds)
lda #$02 ; load register address of minutes
jsr RdRTC ; read register value to Acc
and #111111 ; mask 2 upper bits
ldy #$01
jsr Tfer2RetBuf ; transfer to return buffer at index 1 (minutes)
lda #$04 ; load register address of hours
jsr RdRTC ; read register value to Acc
pha
lda #DSC_REGB_24o12
sta Temp
lda RegB ; determine which hours mode (12/24 hours)
bit Temp
beq mode12hbin
pla
and #011111 ; mask 3 upper bits for 24H mode read
clc
bcc storehours
mode12hbin:
pla
and #001111 ; mask 4 upper bits for 12H mode read
storehours:
ldy #$02
jsr Tfer2RetBuf ; transfer to return buffer at index 2 (hours)
lda #$06 ; load register address of day (of week)
jsr RdRTC ; read register value to Acc
and #000111 ; mask 5 upper bits
ldy #$03
jsr Tfer2RetBuf ; transfer to return buffer at index 3 (dayofweek)
lda #$07 ; load register address of date (day of month)
jsr RdRTC ; read register value to Acc
and #011111 ; mask 3 upper bits
ldy #$04
jsr Tfer2RetBuf ; transfer to return buffer at index 4 (date)
lda #$08 ; load register address of month
jsr RdRTC ; read register value to Acc
and #001111 ; mask 4 upper bits
ldy #$05
jsr Tfer2RetBuf ; transfer to return buffer at index 5 (month)
lda #$09 ; load register address of year
jsr RdRTC ; read register value to Acc
and #%011111111 ; mask the highest bit
ldy #$06
jsr Tfer2RetBuf ; transfer to return buffer at index 6 (year)
lda #$0a
jsr RdRTC
ora #DSC_REGA_BANK1 ; switch to bank 1
tax
lda #$0a
jsr WrRTC
lda #$48 ; load register address of century
jsr RdRTC ; read register value to Acc
ldy #$07
jsr Tfer2RetBuf ; transfer to return buffer at index 7 (century)
lda #$0a
jsr RdRTC
and #DSC_REGA_BANK0 ; switch to original bank
tax
lda #$0a
jsr WrRTC
; enable update transfers
lda #$0b
jsr RdRTC
and #DSC_REGB_UNSET
tax
lda #$0b
jsr WrRTC
ldy #$01
jsr ldaxysp
jsr incsp2
rts
.endproc
.segment "CODE"
; set clock data
; void ds1685_setclock (struct ds1685_clkdata *buf);
.proc _ds1685_setclock:near
; disable update transfers
; set binary mode
lda #$0b
jsr RdRTC
ora #DSC_REGB_SET
ora #DSC_REGB_DM
tax
lda #$0b
jsr WrRTC
ldy #$00 ; get argument 0 (seconds)
jsr GetParFromSpIdx
tax
lda #$00 ; write to DS1685 seconds register
jsr WrRTC
ldy #$01 ; get argument 1 (minutes)
jsr GetParFromSpIdx
tax
lda #$02 ; write to DS1685 minutes register
jsr WrRTC
ldy #$02 ; hours
jsr GetParFromSpIdx
tax
lda #$04
jsr WrRTC
ldy #$03 ; day of week
jsr GetParFromSpIdx
tax
lda #$06
jsr WrRTC
ldy #$04 ; date (day of month)
jsr GetParFromSpIdx
tax
lda #$07
jsr WrRTC
ldy #$05 ; month
jsr GetParFromSpIdx
tax
lda #$08
jsr WrRTC
ldy #$06 ; year
jsr GetParFromSpIdx
tax
lda #$09
jsr WrRTC
; enable update transfers
lda #$0b
jsr RdRTC
and #DSC_REGB_UNSET
tax
lda #$0b
jsr WrRTC
; disable update transfers
lda #$0b
jsr RdRTC
ora #DSC_REGB_SET
tax
lda #$0b
jsr WrRTC
lda #$0a ; get reg. A
jsr RdRTC
ora #DSC_REGA_BANK1 ; switch to bank 1
tax
lda #$0a
jsr WrRTC
ldy #$07 ; century
jsr GetParFromSpIdx
tax
lda #$48
jsr WrRTC
lda #$0a
jsr RdRTC
and #DSC_REGA_BANK0 ; switch to original bank
tax
lda #$0a
jsr WrRTC
; enable update transfers
lda #$0b
jsr RdRTC
and #DSC_REGB_UNSET
tax
lda #$0b
jsr WrRTC
jsr incsp2
rts
.endproc
.segment "CODE"
; set clock data
; void ds1685_settime (struct ds1685_clkdata *buf);
.proc _ds1685_settime:near
; switch to original bank
lda #$0a
jsr RdRTC
and #DSC_REGA_BANK0
tax
lda #$0a
jsr WrRTC
; disable update transfers
; set binary mode
lda #$0b
jsr RdRTC
ora #DSC_REGB_SET
ora #DSC_REGB_DM
tax
lda #$0b
jsr WrRTC
ldy #$00 ; get argument 0 (seconds)
jsr GetParFromSpIdx
tax
lda #$00 ; write to DS1685 seconds register
jsr WrRTC
ldy #$01 ; get argument 1 (minutes)
jsr GetParFromSpIdx
tax
lda #$02 ; write to DS1685 minutes register
jsr WrRTC
ldy #$02 ; hours
jsr GetParFromSpIdx
tax
lda #$04
jsr WrRTC
; enable update transfers
lda #$0b
jsr RdRTC
and #DSC_REGB_UNSET
tax
lda #$0b
jsr WrRTC
jsr incsp2
rts
.endproc
.segment "CODE"
; helper procedures
;-------------------------------------------------------------------------------
; Write DS1685 address (Acc).
;-------------------------------------------------------------------------------
WrRTCAddr:
sta DSCALADDR
rts
;-------------------------------------------------------------------------------
; Write DS1685 data (Acc).
;-------------------------------------------------------------------------------
WrRTCData:
sta DSCALDATA
rts
;-------------------------------------------------------------------------------
; Read DS1685 data (-> Acc).
;-------------------------------------------------------------------------------
RdRTCData:
lda DSCALDATA
rts
;-------------------------------------------------------------------------------
; Write DS1685 Acc = Addr, X = Data
;-------------------------------------------------------------------------------
WrRTC:
jsr WrRTCAddr
txa
jsr WrRTCData
rts
;-------------------------------------------------------------------------------
; Read DS1685 Acc = Addr -> A = Data
;-------------------------------------------------------------------------------
RdRTC:
jsr WrRTCAddr
jsr RdRTCData
rts
;-------------------------------------------------------------------------------
; Transfer A to return buffer at index Y.
;-------------------------------------------------------------------------------
Tfer2RetBuf:
pha
tya
pha
ldy #$01 ; transfer to return buffer
jsr ldaxysp
jsr pushax
ldx #$00
pla
tay
pla
jsr staspidx
rts
;-------------------------------------------------------------------------------
; Load to A from arguments buffer/stack at index Y.
;-------------------------------------------------------------------------------
GetParFromSpIdx:
tya
pha
ldy #$01 ; get buffer
jsr ldaxysp
sta Temp
pla
tay
lda Temp
jsr ldauidx
rts
;------------------------------ END OF FILE -------------------------------------
Here is the 'C' header file:
/*
* File: mkhbcos_ds1685.h
* Purpose: Declarations and definitions for
* DS1685 RTC (Real Time Clock) chip API.
* Author: Marek Karcz
* Created: 02/05/2012
*
* Revision history:
*
*
*/
#ifndef MKHBCOS_DS1685
#define MKHBCOS_DS1685
// DS RTC registers mask bits
// reg. A
#define DSC_REGA_UIP 0x80 // %10000000
#define DSC_REGA_DV2 0x40 // %01000000
#define DSC_REGA_DV1 0x20 // 100000
#define DSC_REGA_DV0 0x10 // 010000
#define DSC_REGA_RS3 0x08 // 001000
#define DSC_REGA_RS2 0x04 // 000100
#define DSC_REGA_RS1 0x02 // 000010
#define DSC_REGA_RS0 0x01 // 000001
// aliases
#define DSC_REGA_CTDWN DSC_REGA_DV2
#define DSC_REGA_OSCEN DSC_REGA_DV1
#define DSC_REGA_BSEL DSC_REGA_DV0
#define DSC_REGA_BANK0 0xEF
#define DSC_REGA_BANK1 0x10
// reg. B
#define DSC_REGB_SET 0x80 // %10000000
#define DSC_REGB_PIE 0x40 // %01000000
#define DSC_REGB_AIE 0x20 // 100000
#define DSC_REGB_UIE 0x10 // 010000
#define DSC_REGB_SQWE 0x08 // 001000
#define DSC_REGB_DM 0x04 // 000100
#define DSC_REGB_24o12 0x02 // 000010
#define DSC_REGB_DSE 0x01 // 000001
// aliases
#define DSC_REGB_UNSET 0x7F // %01111111
struct ds1685_clkdata
{
unsigned char seconds;
unsigned char minutes;
unsigned char hours;
unsigned char dayofweek;
unsigned char date; // day
unsigned char month;
unsigned char year;
unsigned char century;
};
unsigned char __fastcall__ ds1685_init (unsigned char regb,
unsigned char rega,
unsigned char regextb,
unsigned char regexta);
struct ds1685_clkdata *ds1685_rdclock (struct ds1685_clkdata *buf);
void ds1685_setclock (struct ds1685_clkdata *buf);
void ds1685_settime (struct ds1685_clkdata *buf);
#endif
My 'C' program code for enhanced shell is too long to be quoted here. I promise to publish it later. For now it is still work in progress.
Code and DS1685 in action:
Figure 1: Screenshot of my enhanced shell with transcript of programs for setting and then reading calendar/time information.
Cheers!
Marek