How to use DS1302 RTC on RC2014 Pro?

322 views
Skip to first unread message

Nick Bolton

unread,
Jul 31, 2022, 8:02:01 PM7/31/22
to RC2014-Z80
So, I bought the DS1302 Real Time Clock Module, and after building it and spending several hours trying to use it with my RC2014 Pro, I now realise that it only works with the RC2014 Zed, and requires RomWBW.

Is this module no use to me, or can I use it with the RC2014 Pro? 

I am of course assuming that RC2014 Pro doesn't have an RTC since `clock()` and `time(t)` don't seem to work (`clock()` returns 0, and `time(t)` returns random values).

That said, now I think about it, I could have sworn that `clock()` was returning something other than 0 at one point, unless it was all a dream... And if it was returning something, maybe I have broken something since?

Nick Bolton

unread,
Jul 31, 2022, 8:07:14 PM7/31/22
to RC2014-Z80
p.s. Forgot to mention I'm using C, compiled with zcc (z88dk).

Wayne Warthen

unread,
Jul 31, 2022, 8:09:43 PM7/31/22
to RC2014-Z80
On Sunday, July 31, 2022 at 5:02:01 PM UTC-7 nick-p...@nbolton.com wrote:
So, I bought the DS1302 Real Time Clock Module, and after building it and spending several hours trying to use it with my RC2014 Pro, I now realise that it only works with the RC2014 Zed, and requires RomWBW.

Well, this is a software issue, not a hardware issue.  CP/M 2.2 does not know what a clock is, so there is no way to integrate it with CP/M 2.2 per se.  However, you could certainly write CP/M 2.2 programs that interact with it (set or display time).  Such programs would run fine under RC2014 Pro.

Is this module no use to me, or can I use it with the RC2014 Pro? 

Definitely useful if you write some software to leverage it.
 
I am of course assuming that RC2014 Pro doesn't have an RTC since `clock()` and `time(t)` don't seem to work (`clock()` returns 0, and `time(t)` returns random values).

I assume clock() and time(t) are referring to Z88DK.  I'm not sure why Z88DK (when building for RC2014 Pro) would not support the DS1302.

That said, now I think about it, I could have sworn that `clock()` was returning something other than 0 at one point, unless it was all a dream... And if it was returning something, maybe I have broken something since?

Not sure.

Thanks,

Wayne 

Alan Cox

unread,
Jul 31, 2022, 8:23:22 PM7/31/22
to rc201...@googlegroups.com
On Mon, 1 Aug 2022 at 01:02, Nick Bolton <nick-p...@nbolton.com> wrote:
>
> So, I bought the DS1302 Real Time Clock Module, and after building it and spending several hours trying to use it with my RC2014 Pro, I now realise that it only works with the RC2014 Zed, and requires RomWBW.
>
> Is this module no use to me, or can I use it with the RC2014 Pro?

You would need to move to ZSDOS or CP/M 3 for proper time support.
It's possible to run both on a Pro system but I don't think anyone
ever has ever put together and shared a completed port.

Alan

Phillip Stevens

unread,
Jul 31, 2022, 10:03:26 PM7/31/22
to RC2014-Z80
Nick wrote:
So, I bought the DS1302 Real Time Clock Module, and after building it and spending several hours trying to use it with my RC2014 Pro, I now realise that it only works with the RC2014 Zed, and requires RomWBW.

Wayne wote: 
Well, this is a software issue, not a hardware issue.  CP/M 2.2 does not know what a clock is, so there is no way to integrate it with CP/M 2.2 per se.  However, you could certainly write CP/M 2.2 programs that interact with it (set or display time).  Such programs would run fine under RC2014 Pro.

Is this module no use to me, or can I use it with the RC2014 Pro? 

Definitely useful if you write some software to leverage it.
 
I am of course assuming that RC2014 Pro doesn't have an RTC since `clock()` and `time(t)` don't seem to work (`clock()` returns 0, and `time(t)` returns random values).

I assume clock() and time(t) are referring to Z88DK.  I'm not sure why Z88DK (when building for RC2014 Pro) would not support the DS1302.

That said, now I think about it, I could have sworn that `clock()` was returning something other than 0 at one point, unless it was all a dream... And if it was returning something, maybe I have broken something since?

Nick, the reason that z88dk doesn't have a driver for the DS1302 RTC Module for the RC2014 is that no one has ever written the required drivers.
That said, it is pretty easy to do so.

z88dk understands both clock() and time() because I did write a library for that, and to support those on the SCZ180 (with Z180 timers) we've got the time specification header in the new library for example for the SCZ180 (SC126, SC130, SC131, etc)  and other Z180 machines, and for other classic platforms here.

Putting "integration" into z88dk aside, all you need to use a library is to compile your library C code with -x on the command line (which produces a library file), and then later link this library in with -lmylib when you're building your application code, you can also include the header with -Imylib.h.

In the essence of "teach a man to fish, and he shall never hunger", check out some of the libraries I've prepared previously for various modules for the RC2014 here at z88dk-libraries.
A you can get a good overview looking at the simple examples to support the TH02 Temperature and Humidity sensor.

One complexity (or specific thing to get right) is going to be linking the DS1302 interrupt into the RC2014 if you want to use a 32kHz timer. I haven't looked at the module to see how this would be done. Perhaps that wouldn't even be necessary for you to do, if you just want to calculate day time and day date by querying the DS1302 Module?

Don't hesitate to ask further if you strike issues.
Cheers, Phillip

Phillip Stevens

unread,
Jul 31, 2022, 10:05:33 PM7/31/22
to RC2014-Z80
Phillip Stevens wrote:

z88dk understands both clock() and time() because I did write a library for that.

Actually, to give credit where it is due, I borrowed/stole the library from Michael Duane Rice. Sorry Michael if you're ever reading this.

Phillip Stevens

unread,
Jul 31, 2022, 10:19:15 PM7/31/22
to RC2014-Z80
Phillip Stevens wrote:
Nick wrote:
So, I bought the DS1302 Real Time Clock Module, and after building it and spending several hours trying to use it with my RC2014 Pro, I now realise that it only works with the RC2014 Zed, and requires RomWBW.
In the essence of "teach a man to fish, and he shall never hunger", check out some of the libraries I've prepared previously for various modules for the RC2014 here at z88dk-libraries.
A you can get a good overview looking at the simple examples to support the TH02 Temperature and Humidity sensor.

Also see the Arduino DS1302 library for hints.

One complexity (or specific thing to get right) is going to be linking the DS1302 interrupt into the RC2014 if you want to use a 32kHz timer. I haven't looked at the module to see how this would be done. Perhaps that wouldn't even be necessary for you to do, if you just want to calculate day time and day date by querying the DS1302 Module?

And, to write and read to RC2014 I/O ports you can read this guide.

Cheers, Phillip

Nick Bolton

unread,
Aug 1, 2022, 4:52:01 AM8/1/22
to RC2014-Z80
> Arduino DS1302 library

Ok, well Ds1302.cpp looks pretty straightforward. I can use inp() and outp() for now since they're easy (albeit slow), but certainly writing an ASM lib would be a neat little project if I ever get round to that.

I'm actually not looking for accurate date/time, though that might be handy. What I really want is to count elapsed milliseconds, so I can run a function every n millis. I tried doing this with counting `while` loop iterations, but since some functions take several cycles, so results vary wildly. Arduino has a handy function called millis() which I use often.

> RC2014 I/O ports

Ah...

> z88dk has the standard inp() and outp() functions [..] This takes 18 cycles to output a byte, typically.

Yeah, no kidding. inp() and outp() are very slow. Phillip, many thanks for the Hardware IO link, I couldn't find that even with my google-fu on max! I guess because it was buried in a long page (which I probably arrived at, and promptly bookmarked due to the page length). In light of this, I bet there is a ton of info on that page I'd benefit from knowing.

So, in short. If I port Ds1302.cpp with z88dk, using inp() and outp(), then I wonder if I can access elapsed millis? Can I really be that simple?

Nick

Nick Bolton

unread,
Aug 1, 2022, 4:53:50 AM8/1/22
to RC2014-Z80
p.s. Hilarious typo: "Can I really be that simple?"

The answer is yes! 

What I meant is... Can it really be that simple?

Nick Bolton

unread,
Aug 1, 2022, 5:04:57 AM8/1/22
to RC2014-Z80
Hmm, ok, reading further...

> You can also do this via the standard C inp() and outp() functions, but it is much much slower. It typically takes more than 145 cycles to achieve the same outcome.

Yeah ok, 145 cycles actually sounds more familiar. I thought 18 cycles sounded faster than what I was experiencing. The docs are actually referring to the faster way!

Phillip Stevens

unread,
Aug 1, 2022, 5:31:26 AM8/1/22
to RC2014-Z80
Nick wrote:
I'm actually not looking for accurate date/time, though that might be handy. What I really want is to count elapsed milliseconds, so I can run a function every n millis. I tried doing this with counting `while` loop iterations, but since some functions take several cycles, so results vary wildly. Arduino has a handy function called millis() which I use often.

Ok, then the RTC Module is the wrong tool for the job. AFAIK, the DS1302 can only count seconds granularity.

I can suggest three options to measure millisecond level granularity.
  1. Stephen's CTC Module, or
  2. Spencer's Dual Clock Module set to a slow speed, and connected to the NMI interrupt, or
  3. z88dk timing function cpu_delay_ms() from cpu.h (which is T-State accurate across all the CPU types).
The cpu_delay_ms() function will be the cheapest option, but note that it is a busy wait (like Arduino delay()).

Cheers, Phillip

Nick Bolton

unread,
Aug 1, 2022, 7:04:27 AM8/1/22
to RC2014-Z80
Ah, I actually have this module. I thought it was purely a clock for the CPU. Perhaps I misunderstood the use of this module?

>  set to a slow speed, and connected to the NMI interrupt, or

Are there docs on how to do this with C?

Nick Bolton

unread,
Aug 1, 2022, 7:09:51 AM8/1/22
to RC2014-Z80
> AFAIK, the DS1302 can only count seconds granularity.

By the way, I still do want to keep time for something else, so all the info you gave is still very useful :-)

Phillip Stevens

unread,
Aug 1, 2022, 7:22:32 AM8/1/22
to RC2014-Z80
Nick wrote:
Spencer's Dual Clock Module 

Ah, I actually have this module. I thought it was purely a clock for the CPU. Perhaps I misunderstood the use of this module?

No, you’re correct it is normally for CPU and serial module timing. But just as you can connect one output to a serial module block, you could connect it to the NMI bus pin to trigger timing increment code. I haven’t seen this done before though.

You’d have to set up the interrupt in your C application within CP/M, as CP/M itself uses that 0x66 location for its command line processing so it will overwrite your 0x66 JP code.

>  set to a slow speed, and connected to the NMI interrupt, or

Are there docs on how to do this with C?

There is info on attaching interrupts or filling RST jump locations in the same RC2014 guide for z88dk I linked before. 

As the cp/m subtypes generally have 0x100 as their origin, you will have to use a small assembly or C function to fill the 0x66 jump location when your app initialises.

Cheers, P

Nick Bolton

unread,
Aug 1, 2022, 8:15:24 AM8/1/22
to RC2014-Z80
> But just as you can connect one output to a serial module block, you could connect it to the NMI bus pin to trigger timing increment code. I haven’t seen this done before though.

Thanks Phillip, very helpful. It's quite interesting that nobody has attempted (or published work) to count millis in C with the RC2014; I believe millis() is a fairly well used function among Arduino developers. I was expecting clock() to do this, but it just returns 0.

I wonder if using CP/M holding me back here of if it's unrelated.

Phillip Stevens

unread,
Aug 1, 2022, 8:30:42 AM8/1/22
to rc201...@googlegroups.com
Nick wrote:
> But just as you can connect one output to a serial module block, you could connect it to the NMI bus pin to trigger timing increment code. I haven’t seen this done before though.

Thanks Phillip, very helpful. It's quite interesting that nobody has attempted (or published work) to count millis in C with the RC2014; I believe millis() is a fairly well used function among Arduino developers. I was expecting clock() to do this, but it just returns 0.

I wonder if using CP/M holding me back here of if it's unrelated.

It is related to the Z80 not having inherent timing capability, and relying on the CTC device for this and this CTC not being part of the original simple CP/M on a Breadboard RC2014 design.

Also related to the original 8080 CPU not having any timing capability, leading to CP/M originally not having need for timing BDOS calls. Timing capability wasn’t added until too late for it to be widely adopted by application developers.

Arduino (AVR MCUs) have always had timers, because of their intended commercial / industrial control application.

Z180 CPU based hardware with RomWBW HBOS has good timer capability, integrated with z88dk. That’s another option to start with.

P. 
--
Sent from a Mobile Device. Replies may appear terse.

Nick Bolton

unread,
Aug 1, 2022, 9:16:43 AM8/1/22
to RC2014-Z80
Got it. So it sounds like although the clock() function is included in z88dk, that's just for compliance. It sounds like z88dk's clock() supports the Z180 CPU (1997) via RomWBW HBOS, but before that, the Z80 was never designed to have inherent timing capability.

So, it might be possible to create a Z80 clock() function, a la "connect it to the NMI bus pin [on Spencer's Dual Clock Module] to trigger timing increment code".

Did I understand correctly?

Alan Cox

unread,
Aug 1, 2022, 11:11:55 AM8/1/22
to rc201...@googlegroups.com
On Mon, 1 Aug 2022 at 14:16, Nick Bolton <nick-p...@nbolton.com> wrote:
>
> Got it. So it sounds like although the clock() function is included in z88dk, that's just for compliance. It sounds like z88dk's clock() supports the Z180 CPU (1997) via RomWBW HBOS, but before that, the Z80 was never designed to have inherent timing capability.
>
> So, it might be possible to create a Z80 clock() function, a la "connect it to the NMI bus pin [on Spencer's Dual Clock Module] to trigger timing increment code".
>
> Did I understand correctly?

You also can't use NMI in CP/M properly because CP/M was designed for
the 8080 and the NMI vector is actually part of the CP/M FCB space. As
you can't mask an NMI this is a somewhat fatal flaw in that proposal.

There are three ways to address it for timing events in my experience
unless you want to wire up a CTC and the extra bus wires plus small
BIOS change.

1. Use delay loops instead. In fact for short delays it's better
because you can find the Z80 interrupt takes longer to process
2. Some CP/M games used to assume or get passed a baud rate. They
could then use the number of characters printed (plus padding with
"no-op" sequences) to keep the game speed as desired. That broke
spectacularly once onboard video happened but it will work on most
RC2014.
3. Wire a slow signal to one of the Z80 SIO modem pins and use the SIO
as an interrupt handler for the other sources. This involves modifying
the CP/M BIOS a bit but does work. It's how (without CP/M BIOS though)
I run Fuzix on that platform.

I've got a tiny board I designed that produces a square wave at all
sorts of useful rates for serial and timing, and with that hooked to
the SIO I can do most stuff.

If you just want a polled time of day then hardcoding something non
portable is about your only choice short of the CP/M upgrade path. If
you are going to play with any ROM changes you also want to replace
the old OTP EPROM setup the board uses with a 28C256 so you can
reflash it thousands of times at a few seconds a time, or you will
need EPROM erasers and a whole world of prehistoric development pain.

Alan

Phillip Stevens

unread,
Aug 1, 2022, 9:02:11 PM8/1/22
to RC2014-Z80


Alan wrote:
Nick wrote:
> Got it. So it sounds like although the clock() function is included in z88dk, that's just for compliance. It sounds like z88dk's clock() supports the Z180 CPU (1997) via RomWBW HBOS, but before that, the Z80 was never designed to have inherent timing capability.

Yes. Mostly right. Although with z88dk it is hard to be absolutely definitive. If you say "A" then there's sure to be a target somewhere in the over 100 supported that doesn't do "A".

There is integrated support for the clock_getres(), clock_gettime(), clock_settime() functions needed to support a millisecond counter within the Z180 targets. Eg YAZ180.
For the SCZ180 it is assumed that we're running RomWBW, so these functions are implemented within HBIOS target, using proper HBIOS API calls.

For interest it is very easy to develop to the HBIOS target. Reboot, hit M for monitor, and L for load.
Cat your HEX program (output from z88dk create-app) up to the SCZ180, and watch your debugging output.
Rinse, repeat.

> So, it might be possible to create a Z80 clock() function, a la "connect it to the NMI bus pin [on Spencer's Dual Clock Module] to trigger timing increment code".
> Did I understand correctly?

You also can't use NMI in CP/M properly because CP/M was designed for
the 8080 and the NMI vector is actually part of the CP/M FCB space. As
you can't mask an NMI this is a somewhat fatal flaw in that proposal.

Yes, Alan is correct here. As I noted the NMI jump address 0x66 is smack in the middle of the command line processing scratch (FCB) for CP/M. There couldn't be a worse place for it.
So if you wanted to use it you would be limited to within a C program launched by CP/M. After launching the program, you'd have to write your interrupt vector location into 0x66 before enabling the interrupt source, and then disable the interrupt source before returning to CP/M. Otherwise things would be bad. So the fatal flaw is that Spencer's Dual Clock Module doesn't have switchable outputs.

/rant-on
I must write a blog on "The arrogance of Federico Faggin". I mean he went out of his way to screw with CP/M wherever he could.
The INT1 address overloads RST7 at 0x38, thus screwing up DDT, unless it is rewritten as a special Z80 version to use RST6.
And putting the NMI address right in the middle of the FCB is a masterstroke of F*$kery.
He knew his customers all were using CP/M, so why do that?
He was forcing them to use IM2, and the rest of his Z80 specific devices.
The Intel engineers building the 8085 were able to work around CP/M, and even extend into unused BIOS space for RST V at 0x40.
Why couldn't Faggin?
/rant-off
 
There are three ways to address it for timing events in my experience
unless you want to wire up a CTC and the extra bus wires plus small
BIOS change.

This is Stephen's CTC Module option.
 
1. Use delay loops instead. In fact for short delays it's better
because you can find the Z80 interrupt takes longer to process

Within z88dk this is the timing function cpu_delay_ms() from cpu.h (which is T-State accurate across all the CPU types).
The cpu_delay_ms() function will be the cheapest option, but as noted it is a busy wait like Arduino delay(), and not like millis(). So you might have to rework your code to suit.
 
P.
Reply all
Reply to author
Forward
0 new messages