Clock Failure

discuss both original and modern hardware for the bbc micro/electron
Post Reply
User avatar
BeebMaster
Posts: 3628
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Clock Failure

Post by BeebMaster » Sat Jul 25, 2020 11:20 pm

A few weeks back, when I was making a picture set showing the version strings from different versions of the Level 3 file server, I encountered an unexpected Clock Failure error. It didn't take long to figure out why.
IMG_7348.JPG
Sad, because it had been working very nicely for 15 years or so.

Can some kind soul repair it or is it beyond redemption?
Image

WrightStuff
Posts: 27
Joined: Fri Mar 16, 2012 1:54 pm
Contact:

Re: Clock Failure

Post by WrightStuff » Sat Jul 25, 2020 11:36 pm

I'm trying to figure out why your clock needs a ribon cable ? :lol:

User avatar
8271
Posts: 94
Joined: Sun May 24, 2020 1:20 pm
Contact:

Re: Clock Failure

Post by 8271 » Sat Jul 25, 2020 11:49 pm

Is it potted? How much core is showing before it delves under the potting compound?
Serial computer hoarder & econetophile
2 x BBC-B, 4 x Master128, Master Viglen, Master Domesday, A310, A4, A5000, RiscPC700 Level 4 Server, RPC_SA233 & RPC_SA200, RiscOS_Pi, Econet & Ethernet

User avatar
1024MAK
Posts: 10294
Joined: Mon Apr 18, 2011 5:46 pm
Location: Looking forward to summer in Somerset, UK...
Contact:

Re: Clock Failure

Post by 1024MAK » Sun Jul 26, 2020 4:51 am

Looking at the photo, it looks like there is hardly any length of copper core sticking out the epoxy potting compound. This makes a repair attempt a lot of difficult work :(

Mark

User avatar
flaxcottage
Posts: 4256
Joined: Thu Dec 13, 2012 8:46 pm
Location: Derbyshire
Contact:

Re: Clock Failure

Post by flaxcottage » Sun Jul 26, 2020 8:57 am

You will probably find that there will be at least 1/4" of wire accessible, maybe more, if the epoxy is carefully dug/cut/ground out of the way.

With that length available one could be able to solder a 20-way DIL header onto the wires and, when tested as electrically working, the header could then be held in place with more epoxy resin to make a permanent repair.

An interesting challenge, this will be a very difficult repair. :?
- John

Image

User avatar
BeebMaster
Posts: 3628
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Clock Failure

Post by BeebMaster » Sun Jul 26, 2020 12:38 pm

Yes, definitely a challenge, and not one for me I think! I expect a similar fate has come to lots of these over the years.

Somebody did open one up once, there are pictures of the insides on the forum somewhere.

Adding a header emerging from the box is a good idea, if it could be done.

I do have a spare which is still working (in fact once upon a time I had three altogether, which means I still have three altogether, but the third one has become "Lost in library"). I'm thinking along similar lines with the spare one, and sticking it down inside a box and just having the IDC header sticking out, so there's no possibility of the cable becoming broken at the point it emerges, and then just connecting it up with a 20-way extension.
Image

User avatar
flaxcottage
Posts: 4256
Joined: Thu Dec 13, 2012 8:46 pm
Location: Derbyshire
Contact:

Re: Clock Failure

Post by flaxcottage » Sun Jul 26, 2020 1:14 pm

I'll have a go. :shock: :wink: #-o

PM sent.
- John

Image

User avatar
IanS
Posts: 1413
Joined: Mon Aug 31, 2009 7:02 pm
Contact:

Re: Clock Failure

Post by IanS » Sun Jul 26, 2020 1:46 pm

BeebMaster wrote:
Sun Jul 26, 2020 12:38 pm
Yes, definitely a challenge, and not one for me I think! I expect a similar fate has come to lots of these over the years.

Somebody did open one up once, there are pictures of the insides on the forum somewhere.
Has it been reverse-engineered enough to produce a replacement?

I remember seeing the pictures at the time, but can't find them now. Anyone got a link?

User avatar
BeebMaster
Posts: 3628
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Clock Failure

Post by BeebMaster » Sun Jul 26, 2020 1:50 pm

Thanks John!

I just remembered I have a Watford user port splitter, which I use with my Morley EPROM Programmer really only as an extension cable, so I could redeploy that for the other Clock dongle, and tape it to the user port splitter so the cable can't move and start to crack.
Image

User avatar
BeebMaster
Posts: 3628
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Clock Failure

Post by BeebMaster » Sun Jul 26, 2020 2:00 pm

Here's the link to the previous destruction:

viewtopic.php?t=5164

I don't think the part number of the actual RTC chip could be made out, but there's speculation.

Of course, the dongle isn't essential as L3 FS has been patched not to need it, but it's nice for authenticity (and for testing things with an unpatched version).

Looking at the source code released by Alan Williams, it may be as simple as changing the dongle=1 flag to dongle=0 at build-time.
Image

User avatar
BeebMaster
Posts: 3628
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Clock Failure

Post by BeebMaster » Sat Aug 22, 2020 11:56 pm

I've cracked it! (And I don't mean the ribbon cable!) Brace yourselves...

The first thing I did was to extract all the relevant time-reading parts from the Level 3 source code supplied by Alan Williams, and converted the mnemonics to work in the BBC BASIC assembler so I could see if it would detect and read the clock. The bit I couldn't get to work was where it prints the "Clock failure" message, it's quite ingenious and took me a while to figure out how it works. It JSRs to the printing routine, and the string to be printed is in the code after the jump, and I eventually worked out from the Advanced User Guide that the program counter is pushed on the stack when it does a JSR, so it pulls the PC back from the stack and uses this as the address of the string to print, until it finds a null byte, then pushes the PC back on the stack and returns. But it doesn't work for some reason in isolation, so I had to put in my own Clock failure error message. Other than that, it's pretty much the Acorn code from the source, which is all over the place. There's actually a lot more clock code in the source, to do with writing the clock, printing the time on the screen, dealing with midnight, leap years, new years etc. but this is the minimum to a) check the dongle (which it does by writing then reading an unused register) and then b) reading the time and date. All the variables are in upper case but I had to change some to lower case as they confused the interpreter as they contained BASIC keywords (eg. ORB). Also Acorn used numbers for labels!!!!!! So I had to prefix them all with "L":

Code: Select all

   10REM L3 RTC Code
   20REM Extracted from v1.06 source
   30REM Converted to Mnemonics for BBC BASIC Assembler
   40REM ISW 18/viii/2020
   50REM Acorn comments "\", ISW "\\"
   60DIM clockcode 1024
   70PROCassemble
   80CLS
   90CALLreadclock
  100PRINT"L3 RTC time is:"
  110PRINT;?HRS;":";STRING$(2-LENSTR$?MINS,"0");?MINS;" on ";?DATE;"/";~DATE?1 AND15;"/";~DATE?1 AND 240
  120END
  130DEFPROCassemble
  140FOR B%=0 TO 3 STEP3
  150P%=clockcode
  160[
  170OPT B%
  180\\ Variables
  190.MINS EQUB0
  200.HRS EQUB0
  210.DATE EQUW0:EQUB0 \3 BYTE DATE VALUE Last byte = day
  220.orb EQUB0
  230.IRB EQUB0
  240.DDRB EQUB0
  250.time EQUD0:EQUD0 \TIME OF DAY  \\ISW not sure how many bytes required
  260.OTIME EQUD0:EQUD0 \COMPARISON  \\ISW not sure how many bytes required
  270\\.TSTART EQUW &F800
  280.VSTPTR EQUW 0 \area used by VSTRNG only
  290.readclock \\ISW label
  300 JSR RTC
  310 BEQ ASKDA
  320 JSR clockfail \\ ISW
  330\\JSR VSTRIN
  340\\EQUB13:EQUS"Clock failure":EQUB13
  350\\NOP
  360 JMP TSTART \ .. that's all folks ..   \\ TSTART = &F800
  370.TSTART RTS \\ISW
  380.ASKDA JSR RDTIME
  390 RTS \\ added ISW
  400.RDTIME
  410.PRDTE
  420 JSR RDDONG
  430 RTS \\ added ISW
  440.VSTRIN PLA
  450 STA VSTPTR
  460 PLA
  470 STA VSTPTR + 1
  480 LDY#  0
  490.VSTRLP INC VSTPTR
  500 BNE INCPX
  510 INC VSTPTR + 1
  520.INCPX LDA VSTPTR,Y
  530 BMI VSTRNX
  540 JSR &FFE7
  550 JMP VSTRLP
  560.VSTRNX JMP(VSTPTR) \continue execution   \\ ISW WAS JMI
  570.RDDONG
  580 LDX# 6 \read minutes
  590 JSR L00
  600 JSR L70
  610 STA MINS
  620 LDX# 4 \hours
  630 JSR L00
  640 JSR L70
  650 STA HRS
  660 LDX# 2 \days
  670 JSR L00
  680 JSR L70
  690 STA DATE
  700 LDX# 0 \months
  710 JSR L00
  720 JSR L70
  730 STA DATE+1
  740 
  750 LDX# 3 \oldmonth
  760 JSR L00
  770 JSR L70
  780 STA OTIME+2 \  somewhere but where
  790 LDX# 1 \year (in alarm resister)
  800 JSR L00
  810 JSR L70
  820 JSR SETYR
  830 \ JSR REDFIX  \ Fix year and month if neccessary \\ LH \\
  840             \ \\ 15/1/86 \\
  850 \ RTS
  860.RTC \check that the read time clock is present
  870 LDX# 7
  880 LDA# &71
  890 JSR L95 \write the minutes alarm register
  900 LDX# 7 \read the same register
  910 JSR L00
  920 JSR L70
  930 EOR# &0D \devious eh
  940 BNE RTCX \fail
  950 LDX# 7
  960 JSR L95 \write zero as well
  970 LDX# 7
  980 JSR L00 \read result
  990 CMP# 0
 1000.RTCX RTS \returns EQ if dongle present
 1010.L00 PHP \same as POKE
 1020 TXA
 1030 JSR L05
 1040 LDA# &06 \bits 7,5 are POWERFAIL and ALARM
 1050 STA DDRB
 1060 JSR L55 \write DDRB
 1070 JSR L10
 1080 LDX# 2
 1090 CLC
 1100 JSR L20
 1110 NOP
 1120 NOP
 1130 JSR L50
 1140 LDA IRB \read IRB
 1150 AND# 1
 1160 CLC
 1170 RORA
 1180 RORA
 1190 LDY# 6
 1200.L25 PHA
 1210 TXA
 1220 ORA# 1
 1230 TAX
 1240 CLC
 1250 JSR L40
 1260 NOP
 1270 NOP
 1280 JSR L50
 1290 LDA IRB
 1300 LSRA
 1310 PHP
 1320 TXA
 1330 AND# &FE
 1340 TAX
 1350 CLC
 1360 JSR L40
 1370 PLP
 1380 PLA
 1390 RORA
 1400 DEY
 1410 BNE L25
 1420 CLC
 1430 RORA
 1440 PHA
 1450 JSR L15
 1460 JSR L50
 1470 LDX IRB
 1480 PLA
 1490 PLP
 1500 RTS \all done
 1510.L05 LDX# &A7
 1520 STX DDRB
 1530 JSR L55 \write DDRB
 1540 LDY# 4
 1550 ASLA
 1560 LDX# 2 \fall thru'
 1570.L30 RORA
 1580 JSR L35
 1590 DEY
 1600 BNE L30
 1610 RTS \write bits one at a time
 1620.L10 JSR L15
 1630 NOP
 1640 NOP
 1650 CLC
 1660 JSR L35
 1670 NOP
 1680 NOP
 1690 RTS
 1700.L35 PHA
 1710 PHP
 1720 TXA
 1730 ORA# 1
 1740 TAX
 1750 JSR L40
 1760 TXA
 1770 AND# &FE
 1780 TAX
 1790 PLP
 1800 JSR L40
 1810 PLA
 1820 RTS
 1830.L15 LDX# 0
 1840 STX orb
 1850 BEQ L45
 1860.L40 NOP
 1870 NOP
 1880 NOP
 1890 NOP
 1900.L20 TXA
 1910 ROLA
 1920 STA orb
 1930.L45 PHP
 1940 PHA
 1950 TXA
 1960 PHA
 1970 TYA
 1980 PHA
 1990 LDA# 151
 2000 LDX# &60
 2010 LDY orb
 2020 JSR &FFF4
 2030 JMP L60
 2040.L50 PHP
 2050 PHA
 2060 TXA
 2070 PHA
 2080 TYA
 2090 PHA
 2100 LDA# 150
 2110 LDX# &60
 2120 JSR &FFF4
 2130 STY IRB
 2140 JMP L60
 2150.L55 PHP
 2160 PHA
 2170 TXA
 2180 PHA
 2190 TYA
 2200 PHA
 2210 LDA# 151
 2220 LDX# &62
 2230 LDY DDRB
 2240 JSR &FFF4
 2250.L60 PLA
 2260 TAY
 2270 PLA
 2280 TAX
 2290 PLA
 2300 PLP
 2310 RTS
 2320.L95 PHP \Set routine
 2330 JSR L85 \convert to BCD (without corrupting X)
 2340 CPX# 0 \detect writing munths
 2350 BNE L99
 2360 ORA# &40
 2370.L99 PHA
 2380 TXA
 2390 JSR L05
 2400 PLA
 2410 LDY# 7
 2420 JSR L30
 2430 JSR L10
 2440 JSR L15
 2450 PLP
 2460 RTS
 2470.L70 LDY# 0
 2480 TAX
 2490 BEQ L75
 2500.L80 CLD
 2510 INY
 2520 SED
 2530 SEC
 2540 SBC# 1
 2550 BNE L80
 2560 CLD
 2570.L75 TYA
 2580 RTS
 2590.L85 TAY \make BIN to BCD
 2600 BEQ L94
 2610 LDA# 0
 2620 SED
 2630.L90 CLC
 2640 ADC# 1
 2650 DEY
 2660 BNE L90
 2670.L94 CLD
 2680 RTS
 2690 
 2700.SETYR ASLA
 2710 ASLA
 2720 ASLA
 2730 ASLA
 2740 ORA DATE + 1
 2750 STA DATE + 1 \Year is top nibble of DATE +01
 2760 RTS
 2770.clockfail \\ ISW
 2780BRK:EQUB0:EQUS"Clock failure":EQUB0
 2790]
 2800NEXT
 2810ENDPROC
It works:
capture44.png
It seems to communicate with the Clock using legal methods via OSBYTE 150 and 151, so I thought it would be easy enough to figure out what data is sent and received, but this code is beyond my understanding! I spent about 2 days trying to follow it all through, working out what jumps and loops where, and keeping track of the registers during everything. I wrote it all out twice, but I still couldn't fathom it.

Next I thought I would save the register contents every time there was an OSBYTE call so I could look at exactly what it really was doing. It took me another 3 days to get the little bit of code just to store A,X and Y in incrementing memory locations working properly!! I also put in a dummy set of FF bytes after each separate read or write to the clock, so I could tell the progress it was making.

I took a break from it when it wasn't working to see if I could find any custom routines for communicating with the clock chip inside the dongle, which is suspected to be a PCF8573. Somebody had posted online some code for a project which uses this chip with a modern microcontroller, but the code was in assembler I didn't understand, so it didn't help. Realising the PCF8573 is an I2C device, searching around this topic led me back to this very forum and Martin B's I2C ROM! He very kindly sent me the discontinued user port version, but sadly it wouldn't communicate with the dongle. I was spurred on to get my register read-out code working, which eventually I did. I still don't know what I did wrong in the first place:

Code: Select all

  100REM L3 RTC Code
  200REM Extracted from v1.06 source
  300REM Converted to Mnemonics for BBC BASIC Assembler
  400REM ISW 18/viii/2020
  500REM V2 21/viii/2020 Saves regs after
  600REM every OSBYTE call
  700REM Acorn comments "\", ISW "\\"
  800DIM clockcode 1024
  850DIM acopy 1,xcopy 1,ycopy 1
  900DIM regs 4096
  950?&80=regs MOD256
  960?&81=regs DIV256:REM regs pointer
 1000FORB%=0TO4092STEP4
 1100regs!B%=0:NEXT
 1300PROCassemble
 1400CLS
 1450ONERRORGOTO1550
 1475*SPOOL RTCregs
 1500CALLreadclock
 1550ONERROROFF
 1600PRINT"L3 RTC time is:"
 1700PRINT;?HRS;":";STRING$(2-LENSTR$?MINS,"0");?MINS;" on ";?DATE;"/";~DATE?1 AND15;"/";~DATE?1 AND 240
 1800PROCshowregs
 1850*SPOOL
 1900END
 2000DEFPROCassemble
 2100FOR B%=0 TO 3 STEP3
 2200P%=clockcode
 2300[
 2400OPT B%
 2500\\ Variables
 2600.MINS EQUB0
 2700.HRS EQUB0
 2800.DATE EQUW0:EQUB0 \3 BYTE DATE VALUE Last byte = day
 2900.orb EQUB0
 3000.IRB EQUB0
 3100.DDRB EQUB0
 3200.time EQUD0:EQUD0 \TIME OF DAY  \\ISW not sure how many bytes required
 3300.OTIME EQUD0:EQUD0 \COMPARISON  \\ISW not sure how many bytes required
 3400\\.TSTART EQUW &F800
 3500.VSTPTR EQUW 0 \area used by VSTRNG only
 3900.readclock \\ISW label
 4000 JSR RTC
 4050JSRseparator \ ISW
 4100 BEQ ASKDA
 4200 JSR clockfail \\ ISW
 4300\\JSR VSTRIN
 4400\\EQUB13:EQUS"Clock failure":EQUB13
 4500\\NOP
 4600 JMP TSTART \ .. that's all folks ..   \\ TSTART = &F800
 4700.TSTART RTS \\ISW
 4800.ASKDA JSR RDTIME
 4900 RTS \\ added ISW
 5000.RDTIME
 5100.PRDTE
 5200 JSR RDDONG
 5300 RTS \\ added ISW
 5400.VSTRIN PLA
 5500 STA VSTPTR
 5600 PLA
 5700 STA VSTPTR + 1
 5800 LDY#  0
 5900.VSTRLP INC VSTPTR
 6000 BNE INCPX
 6100 INC VSTPTR + 1
 6200.INCPX LDA VSTPTR,Y
 6300 BMI VSTRNX
 6400 JSR &FFE7
 6500 JMP VSTRLP
 6600.VSTRNX JMP(VSTPTR) \continue execution   \\ ISW WAS JMI
 6700.RDDONG
 6800 LDX# 6 \read minutes
 6900 JSR L00
 7000 JSR L70
 7100 STA MINS
 7120JSRseparator \ISW
 7200 LDX# 4 \hours
 7300 JSR L00
 7400 JSR L70
 7500 STA HRS
 7550JSRseparator \ISW
 7600 LDX# 2 \days
 7700 JSR L00
 7800 JSR L70
 7900 STA DATE
 7950JSRseparator \ISW
 8000 LDX# 0 \months
 8100 JSR L00
 8200 JSR L70
 8300 STA DATE+1
 8310JSRseparator \ISW
 8400 
 8500 LDX# 3 \oldmonth
 8600 JSR L00
 8700 JSR L70
 8800 STA OTIME+2 \  somewhere but where
 8850JSRseparator \ISW
 8900 LDX# 1 \year (in alarm resister)
 9000 JSR L00
 9100 JSR L70
 9150JSRseparator \ISW
 9200 JSR SETYR
 9300 \ JSR REDFIX  \ Fix year and month if neccessary \\ LH \\
 9400             \ \\ 15/1/86 \\
 9500 RTS
 9600.RTC \check that the read time clock is present
 9700 LDX# 7
 9800 LDA# &71
 9900 JSR L95 \write the minutes alarm register
10000 LDX# 7 \read the same register
10100 JSR L00
10200 JSR L70
10300 EOR# &0D \devious eh
10400 BNE RTCX \fail
10500 LDX# 7
10600 JSR L95 \write zero as well
10700 LDX# 7
10800 JSR L00 \read result
10900 CMP# 0
11000.RTCX RTS \returns EQ if dongle present
11100.L00 PHP \same as POKE
11200 TXA
11300 JSR L05
11400 LDA# &06 \bits 7,5 are POWERFAIL and ALARM
11500 STA DDRB
11600 JSR L55 \write DDRB
11700 JSR L10
11800 LDX# 2
11900 CLC
12000 JSR L20
12100 NOP
12200 NOP
12300 JSR L50
12400 LDA IRB \read IRB
12500 AND# 1
12600 CLC
12700 RORA
12800 RORA
12900 LDY# 6
13000.L25 PHA
13100 TXA
13200 ORA# 1
13300 TAX
13400 CLC
13500 JSR L40
13600 NOP
13700 NOP
13800 JSR L50
13900 LDA IRB
14000 LSRA
14100 PHP
14200 TXA
14300 AND# &FE
14400 TAX
14500 CLC
14600 JSR L40
14700 PLP
14800 PLA
14900 RORA
15000 DEY
15100 BNE L25
15200 CLC
15300 RORA
15400 PHA
15500 JSR L15
15600 JSR L50
15700 LDX IRB
15800 PLA
15900 PLP
16000 RTS \all done
16100.L05 LDX# &A7
16200 STX DDRB
16300 JSR L55 \write DDRB
16400 LDY# 4
16500 ASLA
16600 LDX# 2 \fall thru'
16700.L30 RORA
16800 JSR L35
16900 DEY
17000 BNE L30
17100 RTS \write bits one at a time
17200.L10 JSR L15
17300 NOP
17400 NOP
17500 CLC
17600 JSR L35
17700 NOP
17800 NOP
17900 RTS
18000.L35 PHA
18100 PHP
18200 TXA
18300 ORA# 1
18400 TAX
18500 JSR L40
18600 TXA
18700 AND# &FE
18800 TAX
18900 PLP
19000 JSR L40
19100 PLA
19200 RTS
19300.L15 LDX# 0
19400 STX orb
19500 BEQ L45
19600.L40 NOP
19700 NOP
19800 NOP
19900 NOP
20000.L20 TXA
20100 ROLA
20200 STA orb
20300.L45 PHP
20400 PHA
20500 TXA
20600 PHA
20700 TYA
20800 PHA
20900 LDA# 151
21000 LDX# &60
21100 LDY orb
21200JSR saveregs
21300 JSR &FFF4
21500 JMP L60
21600.L50 PHP
21700 PHA
21800 TXA
21900 PHA
22000 TYA
22100 PHA
22200 LDA# 150
22300 LDX# &60
22500 JSR &FFF4
22550JSRsaveregs
22600 STY IRB
22800 JMP L60
22900.L55 PHP
23000 PHA
23100 TXA
23200 PHA
23300 TYA
23400 PHA
23500 LDA# 151
23600 LDX# &62
23700 LDY DDRB
23800JSRsaveregs
23900 JSR &FFF4
24100.L60 PLA
24200 TAY
24300 PLA
24400 TAX
24500 PLA
24600 PLP
24700 RTS
24800.L95 PHP \Set routine
24900 JSR L85 \convert to BCD (without corrupting X)
25000 CPX# 0 \detect writing munths
25100 BNE L99
25200 ORA# &40
25300.L99 PHA
25400 TXA
25500 JSR L05
25600 PLA
25700 LDY# 7
25800 JSR L30
25900 JSR L10
26000 JSR L15
26100 PLP
26200 RTS
26300.L70 LDY# 0
26400 TAX
26500 BEQ L75
26600.L80 CLD
26700 INY
26800 SED
26900 SEC
27000 SBC# 1
27100 BNE L80
27200 CLD
27300.L75 TYA
27400 RTS
27500.L85 TAY \make BIN to BCD
27600 BEQ L94
27700 LDA# 0
27800 SED
27900.L90 CLC
28000 ADC# 1
28100 DEY
28200 BNE L90
28300.L94 CLD
28400 RTS
28500 
28600.SETYR ASLA
28700 ASLA
28800 ASLA
28900 ASLA
29000 ORA DATE + 1
29100 STA DATE + 1 \Year is top nibble of DATE +01
29200 RTS
29300.clockfail \\ ISW
29400BRK:EQUB0:EQUS"Clock failure":EQUB0
29500.saveregs
29600STAacopy
29700STXxcopy
29800STYycopy
29900LDY&80
30100STA(&80)
30200INC&80
30220BNEsrjmp1
30230INC&81
30240.srjmp1
30300LDAxcopy
30400STA(&80)
30500INC&80
30520BNEsrjmp2
30530INC&81
30540.srjmp2
30600LDAycopy
30700STA(&80)
30750INC&80
30850BNEsrjmp3
30860INC&81
30870.srjmp3
31000LDAacopy
31100LDXxcopy
31200LDYycopy
31300RTS
31350.separator
31370PHA:PHX:PHY
31375LDA#255:TAX:TAY:JSRsaveregs
31380PLY:PLX:PLA
31390RTS
31400]
31500NEXT
31600ENDPROC
31700DEFPROCshowregs
31750regend=?&80+(?&81*256)
31800FORB%=regs TO regend STEP 3
31850PRINT~?B%,~B%?1,~B%?2
31880NEXT
31900ENDPROC
capture45.png
Now I had all the registers for every time the L3 code communicates with the dongle:

Code: Select all

RTCRegs
*******

Contents of A, X, Y at each OSBYTE call during
L3 file server RTC (detect dongle) and RDDTIME
(read time & date).

OSBYTE &97 - write to Sheila - regs are BEFORE call
OSBYTE &96 - read from Sheila - regs are AFTER call (ie. with byte read in Y)


L3 RTC time is:
15:21 on 22/8/90


RTC
        97        62        A7
        97        60         6
        97        60         4
        97        60         7
        97        60         5
        97        60         7
        97        60         5
        97        60         7
        97        60         5
        97        60         7
        97        60         5
        97        60         7
        97        60         5
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         7
        97        60         5
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         0
        97        60         2
        97        60         0
        97        60         0
        97        62        A7
        97        60         6
        97        60         4
        97        60         7
        97        60         5
        97        60         7
        97        60         5
        97        60         7
        97        60         5
        97        62         6
        97        60         0
        97        60         2
        97        60         0
        97        60         4
        96        60        5D
        97        60         6
        96        60        5F
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5F
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         0
        96        60        59
        97        62        A7
        97        60         6
        97        60         4
        97        60         7
        97        60         5
        97        60         7
        97        60         5
        97        60         7
        97        60         5
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         0
        97        60         2
        97        60         0
        97        60         0
        97        62        A7
        97        60         6
        97        60         4
        97        60         7
        97        60         5
        97        60         7
        97        60         5
        97        60         7
        97        60         5
        97        62         6
        97        60         0
        97        60         2
        97        60         0
        97        60         4
        96        60        5C
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         0
        96        60        59
        FF        FF        FF

RDDONG

Minutes (Register 6)
        97        62        A7
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         7
        97        60         5
        97        60         7
        97        60         5
        97        62         6
        97        60         0
        97        60         2
        97        60         0
        97        60         4
        96        60        5D
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5F
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         0
        96        60        59
        FF        FF        FF

Hours (Register 4)
        97        62        A7
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         7
        97        60         5
        97        62         6
        97        60         0
        97        60         2
        97        60         0
        97        60         4
        96        60        5D
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5F
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5F
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         0
        96        60        59
        FF        FF        FF

Days (Register 2)
        97        62        A7
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         7
        97        60         5
        97        60         6
        97        60         4
        97        62         6
        97        60         0
        97        60         2
        97        60         0
        97        60         4
        96        60        5C
        97        60         6
        96        60        5F
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5F
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         0
        96        60        59
        FF        FF        FF

Months (Register 0)
        97        62        A7
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        62         6
        97        60         0
        97        60         2
        97        60         0
        97        60         4
        96        60        5C
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5F
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         0
        96        60        59
        FF        FF        FF

Oldmonth (Register 3)
        97        62        A7
        97        60         6
        97        60         4
        97        60         7
        97        60         5
        97        60         7
        97        60         5
        97        60         6
        97        60         4
        97        62         6
        97        60         0
        97        60         2
        97        60         0
        97        60         4
        96        60        5D
        97        60         6
        96        60        5F
        97        60         4
        97        60         6
        96        60        5F
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         0
        96        60        59
        FF        FF        FF

Years (Alarm Register 1)
        97        62        A7
        97        60         6
        97        60         4
        97        60         7
        97        60         5
        97        60         6
        97        60         4
        97        60         6
        97        60         4
        97        62         6
        97        60         0
        97        60         2
        97        60         0
        97        60         4
        96        60        5D
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5F
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         6
        96        60        5E
        97        60         4
        97        60         0
        96        60        59
        FF        FF        FF
But what on earth does it all mean? I worked out that to read one of the registers, it's necessary to write to User Port Data Direction Register the value &A7, which seems to be the device number, then send 8 bits down the User Port Output Register sequentially representing the register number, then send some more DDRB and ORB data, then read 8 bits from the Input Register representing the value held in the register.

The difficult bit was understanding how the register number (0-7) was mangled into 8 bytes for sending, and how the register value was mangled from 8 bytes received to give the correct number. I fathomed it eventually:

Code: Select all

RTC Regs Interpretation
***********************

Hex to Binary reference table
-----------------------------
04 0000 0100
05 0000 0101
06 0000 0110
07 0000 0111

59 0101 1001
5C 0101 1100
5D 0101 1101
5E 0101 1110
5F 0101 1111

Protocol for Reading from RTC

1.      Initiate with a write to Sheila DDRB, value &A7
        (FX 151, &62, &A7)

2.      8 writes to Output Register B
        (FX 151, &60, yy)
        
        Bytes always appear to be in the range 04-07

        Appears to send the register to read by the following:
        a. Take low bit of each byte sent
        b. Group each 2 bits (always identical) together to give a 4 bit number
        c. Discard top bit
        d. Reverse remaining 3 bits

        e.g. read Minutes reg (6)
        06 04 06 04 07 05 07 05

        Low bits:

        0000 1111

        0 0 1 1 (grouped in 2s)

        Discard top bit & reverse = 110 = 6

        Hours register (4):

        06 04 06 04 06 04 07 05

        0000 0011

        0 0 0 1 (grouped in 2s)

        Discard top bit & reverse = 100 = 4

        Days register (2):

        06 04 06 04 07 05 06 04

        Low bits:

        0000 1100

        0 0 1 0 (grouped in 2s)

        Discard top bit & reverse = 010 = 2

        Months register (0):
        06 04 06 04 06 04 06 04

        0000 0000

        0 0 0 0 (grouped in 2s)

        Discard top bit & reverse = 000 = 0

        Old Months register (3):

        06 04 07 05 07 05 06 04

        0011 1100

        0 1 1 0 (grouped in 2s)

        Discard top bit & reverse = 011 = 3

        Year register (1):

        06 04 07 05 06 04 06 04

        0011 0000

        0 1 0 0 (grouped in 2s)

        Discard top bit & reverse = 001 = 1

3.      1 write to DDRB, always &6
        (FX 151, &62, &6)

        Low bit 0 = read?

4.      4 writes to ORB, always 00 02 00 04

        Low bits:

        0000 = meaning end of transmission??

5.      Begin read sequence:
        Read from Sheila &60 (OSBYTE 151,&60)
        Write &6 to ORB = 0 = continue??

6.      Loop
        Read &60, write ORB 2 bytes 4 then 6 = continue reading?

7.      End read sequence:
        After penultimate read:
        Write ORB 2 bytes 4 then 0 = request final byte?
        Read &60

8.      Bytes read from Sheila interpreted as follows:


Minutes - 8 bytes read:

5D 5E 5E 5E 5E 5F 5E 59

Low bits:

1000 0101

Actual minutes

21 0010 0001

Reverse bits read, discard first 2 bits = 6 bit number = 10 0001 = &21
(n.b. 7 bit number is required because registers are in BCD so high number is &59)

Hours - 8 bytes read:

5D 5E 5F 5E 5F 5E 5E 59

Low bits:

1010 1001

Actual Hours:

15 0001 0101

Reverse bits read, discard first 2 bits = 6 bit number = 01 0101 = &15


Days - 8 bytes read:

5C 5F 5E 5E 5E 5F 5E 59

Low bits:

0100 0101

Actual Days

22 0010 0010

Reverse bits read, discard first 2 bits = 6 bit number = 10 0010 = &22


Months - 8 bytes read:

5C 5E 5E 5F 5E 5E 5E 59

Low bits:

0001 0001

Actual Months

08 0000 1000

Reverse bits read, discard first 2 bits = 6 bit number = 00 1000 = &08


Old Months - 8 bytes read:

5D 5F 5F 5E 5E 5E 5E 59

Low bits:

1110 0001

Actual Old Months

Unknown

Reverse bits read, discard first 2 bits = 6 bit number = 00 0111 = &07 = previous month to 8, seems sensible


Years - 8 bytes read:

5D 5E 5E 5F 5E 5E 5E 59

Low bits:

1001 0001

Actual Year

90 = (19)90-(19)81 = 9 0000 1001

Reverse bits read, discard first 2 bits = 6 bit number = 00 1001 = &09


The year stumped me a while till I realised it's an offset from 1981 using the "Short Acorn Era" methodology.

What I discovered is that there is apparently no significance to anything other than the low bit of each byte read from the dongle, and to write the register number requires each of its bits to be sent twice as a byte with the low bit set in a 7-5 pattern, or for a clear bit, a 6-4 pattern, with the top four bits of the byte having no significance.

I've no idea if anything of that has any meaning to people more familar with the user port workings or I2C devices.

Once I knew how to construct the bytes to send to select a register, and interpret the bytes read back from the clock into the actual value held in the register, I could write my own code to read the L3 RTC dongle:

Code: Select all

  100REM Read L3 RTC Dongle
  200REM (C) Ian Wolstenholme 2020
  300REM Version 1.0 22/viii/2020
  400PROCvars
  800CLS:PRINT"Reading Real Time Clock Dongle...";
  900FORE%=0TO7:REM RTC has 8 registers
 1000register(E%)=FNreadregister(E%)
 1100NEXT
 1200PRINT'
 1300FORE%=0TO7
 1400PRINT"Register ";E%;": ";register(E%)
 1500NEXT
 1600PRINT'"The time is:"
 1700IFregister(4)>11 ampm$="pm":hours=register(4)-12 ELSEampm$="am":hours=register(4)
 1800minutes=register(6)
 1900day=register(2)
 2000month=register(0)
 2100year=1981+register(1)
 2200PRINT;hours;":";STRING$(2-LENSTR$minutes,"0");minutes;" ";ampm$;" ";day;"/";month;"/";year
 2300END
 2400DEFPROCwriteddrb(D%)
 2500REM Write a byte to User port Data Direction Register
 2600A%=151
 2700X%=&62:
 2800Y%=D%
 2900CALL&FFF4
 3000ENDPROC
 3100DEFPROCwriteorb(O%)
 3200REM Write a byte to User port Output Register
 3300A%=151
 3400X%=&60
 3500Y%=O%
 3600CALL&FFF4
 3700ENDPROC
 3800DEFFNreadirb
 3900REM Read a byte from User Port Input Register
 4000A%=150
 4100X%=&60
 4200Y%=0
 4300REM Result in Y register, mask off and return as result
 4400=(USR&FFF4 AND &FFFFFF) DIV &10000
 4500DEFFNconvert(C%)
 4600REM Convert the register number (3bit number only) to binary bits
 4700REM Bit 4 is always 0
 4800bin$="0"
 4900FORB%=0TO2
 5000bit=(C% AND 2^B%):IFbit bit=1
 5100bin$=bin$+STR$bit
 5200NEXT
 5300REM Convert the binary register number into a sequence of bytes for RTC
 5400tx$=""
 5500REM Each bit is sent as 2 bytes with low half 7 then 5 for bit set and
 5600REM lower half 6 then 4 for bit clear
 5700REM Build a string of 8 bytes for the 4 bits to be sent
 5800FORB%=1TO4
 5900IFMID$(bin$,B%,1)="0" tx$=tx$+clear$  ELSE tx$=tx$+set$
 6000NEXT
 6100=tx$
 6200DEFPROCsendregister(R%)
 6300REM Transmit the required register number down the User Port
 6400REM Convert the register number to a sequence of bytes understood by RTC
 6500txstring$=FNconvert(R%)
 6600REM Now send the 8 bytes individually to RTC via User Port Output Register
 6700FORB%=1TO15STEP2
 6800byte$="&"+MID$(txstring$,B%,2):byte=EVALbyte$
 6900PROCwriteorb(byte)
 7000NEXT
 7100ENDPROC
 7200DEFFNreadclock
 7300REM Read the selected register from RTC
 7400rd$=""
 7500REM Each register returns 1 byte of data, arrives as 8 bytes where only
 7600REM LSB is required
 7700FORB%=1TO8
 7800REM build string of the sequence of 8 bytes read from RTC
 7900byte=FNreadirb
 8000rd$=rd$+STRING$(2-LEN(STR$~byte),"0")+STR$~byte
 8100REM After 1st byte read, requires a write to Output Register of value 6
 8200REM After bytes 2-7 read, requires 4,6 pattern writing to Output Register
 8300REM After 7th byte read, requires 4,0 pattern writing to obtain final byte
 8400IFB%=1PROCwriteorb(6)
 8500IFB%>1 AND B%<7 PROCwriteorb(4):PROCwriteorb(6)
 8600IFB%=7 PROCwriteorb(4):PROCwriteorb(0)
 8700NEXT
 8800REM Now retain low bit of the 8 bytes read, in reverse order
 8900clock=0:bin$=""
 9000FORB%=1TO15STEP2
 9100clock$="&"+MID$(rd$,B%,2)
 9200clock=EVALclock$
 9300bit=clock AND 1
 9400bin$=STR$bit+bin$
 9500NEXT
 9600REM Now discard bit 7 of our binary string (in fact, bit 6 is not
 9700REM required except for the Minutes register which is a 7 bit reg)
 9800bin$=RIGHT$(bin$,7)
 9900REM Now convert from binary to get the actual value read from RTC register
10000data=0
10100FORB%=1TO7
10200bit=VAL(MID$(bin$,B%,1))
10300IFbit data=data+2^(7-B%)
10400NEXT
10500=EVAL(STR$~data)
10600DEFFNreadregister(R%)
10700PROCwriteddrb(device):REM open comms channel with RTC
10800PROCsendregister(R%):REM send required register number to RTC
10900PROCwriteddrb(6):REM this byte required next
11000PROCwriteorb(0):PROCwriteorb(2):PROCwriteorb(0):PROCwriteorb(4):REM this pattern required next, now we are ready to read data from the register
11100=FNreadclock:REM do the read
12000DEFPROCvars
12400DIM register(7):REM 8 registers in RTC dongle
12500device=&A7:REM RTC clock device address
12600txstring$=STRING$(16,"0")
12700set$="0705":clear$="0604":REM sending a 1 to RTC requires 2 bytes where the lower 4 bits are 7 then 5, higher bits insignificant; sending 0 requires 6 and 4 pattern; no idea why
12800ENDPROC
It works!
capture46.png
I haven't got onto trying to set the clock, I may have to pull out the setting-the-time code from the source and give it the same treatment, although there's a clue in the "clock detect" routine which does a write followed by a read, so I might be able to figure it out from that.
Image

Kazzie
Posts: 1793
Joined: Sun Oct 15, 2017 8:10 pm
Location: North Wales
Contact:

Re: Clock Failure

Post by Kazzie » Sun Aug 23, 2020 8:19 am

BeebMaster wrote:
Sat Aug 22, 2020 11:56 pm
The bit I couldn't get to work was where it prints the "Clock failure" message, it's quite ingenious and took me a while to figure out how it works. It JSRs to the printing routine, and the string to be printed is in the code after the jump, and I eventually worked out from the Advanced User Guide that the program counter is pushed on the stack when it does a JSR, so it pulls the PC back from the stack and uses this as the address of the string to print, until it finds a null byte, then pushes the PC back on the stack and returns.
I found that the Replay ROMs use a similar routine for printing; it's a rather clever method.
BBC Model B 32K issue 7, Sidewise ROM board with 16K RAM
Archimedes 420/1 upgraded to 4MB RAM, ZIDEFS with 512MB CF card
RiscPC 600 under repair
Acorn System 1 home-made replica

User avatar
MartinB
Posts: 5340
Joined: Mon Mar 31, 2008 10:04 pm
Location: Obscurity
Contact:

Re: Clock Failure

Post by MartinB » Sun Aug 23, 2020 8:31 am

Ian wrote:.....searching around this topic led me back to this very forum and Martin B's I2C ROM! He very kindly sent me the discontinued user port version, but sadly it wouldn't communicate with the dongle.
and wrote:I haven't got onto trying to set the clock, I may have to pull out the setting-the-time code from the source and give it the same treatment, although there's a clue in the "clock detect" routine which does a write followed by a read, so I might be able to figure it out from that.

If by any chance someone turns up the interface schematic (even just the User Port connections) before you manage to sort any time-setting code, you should be able to set the clock and do anything else with the PCF8573 using the I2C rom. However, if the module doesn’t have a battery, it might need the User Port data lines that the I2C rom uses changed to match the module I2C lines so you could carry a time-set into a server session without powering-down.

User avatar
flaxcottage
Posts: 4256
Joined: Thu Dec 13, 2012 8:46 pm
Location: Derbyshire
Contact:

Re: Clock Failure

Post by flaxcottage » Sun Aug 23, 2020 9:30 am

That is brilliant! Way beyond my meagre 6502 programming skills. :?

Does that mean that the prototype RTC I sent you works? [-o<
- John

Image

User avatar
BeebMaster
Posts: 3628
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Clock Failure

Post by BeebMaster » Sun Aug 23, 2020 11:58 am

Not yet - but there are several reasons and possibilities.

One is that it might not be a PCF8573 inside the clock dongle at all. It does look a likely candidate, seems to follow the communication protocol given in the data sheet, when factoring in the bit-banging required down the user port, and it has the same registers. Against that is that the register numbers in the data sheet don't appear to match those in the L3 code.

It wouldn't surprise me in the least if Acorn obtained a job lot of non-standard wacky clock chips on the cheap in the 1980s and bodged the entire L3 file server to work around it - seems to have been a bit of habit of theirs!

Another is that maybe the surface mount part is just a bit quicker than the user port can keep up with, so comms just aren't getting through. I'm wondering actually if those 7-5 and 6-4 bit-patterns to send 1s and 0s to the dongle are a bit of a delaying measure, (presumably it takes slightly longer to send 00000111 or 00000101 than just 00000001) so I might try just sending bytes of 1 and 0 instead.

Something else is that it may need setting correctly before it starts functioning. The datasheet isn't clear on what happens at power on, whether it starts counting immediately or if it waits to be programmed before the time increments begin.

When I've got the time-setting code worked out that may help.

Also I'm not exactly sure what is the significance of "A7" as in *FX 151, &62, &A7 - if that's the device number then again it doesn't correspond to the "slave address" given in the datasheet.
Image

User avatar
BeebMaster
Posts: 3628
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Clock Failure

Post by BeebMaster » Sun Aug 23, 2020 6:15 pm

I've added the write function now. It wasn't too difficult with the register read-out from the part of the code which checks the presence of the dongle. It's a similar pattern: open communications on device(?) &A7, write the the register number to write to by sending the number as 8 bytes representing each of the bits in the register number twice using a 7/5 or 4/6 pattern as established earlier on, then write 7 bits of data in 8 7/5 or 4/6 bytes, and then send an "end of transmission" message:

Code: Select all

  100REM Read L3 RTC Dongle
  200REM (C) Ian Wolstenholme 2020
  300REM Version 1.2 23/viii/2020
  400REM User input new time & date
  500REM *******************************************************************
  600REM *                                                                 *
  700REM * Registers in Clock Chip (possible PFC8573) are:                 *
  800REM * 0 Months (0-&12)                                                *
  900REM * 1 Alarm Months (0-&12), used as Years by Acorn                  *
 1000REM * 2 Days (0-&31)                                                  *
 1100REM * 3 Alarm Days (0-&31), used as Old Months by Acorn               *
 1200REM * 4 Hours (0-&23), requires bits 6 & 7 set when transmitting      *
 1300REM * 5 Alarm Hours (0-&23), unused - poss. overflow year in 2K fix?  *
 1400REM * 6 Minutes (0-&59)                                               *
 1500REM * 7 Alarm Minutes (0-&59), used by Acorn to detect dongle         *
 1600REM *                                                                 *
 1700REM * Chip has no Years register (as above) or Seconds register       *
 1800REM * n.b. all register values are written & read in BCD              *
 1900REM * This program does a conversion, so 16:32 sent/read as "&16:&32" *
 2000REM *                                                                 *
 2100REM *******************************************************************
 2200PROCvars
 2300CLS:PRINT"Reading Real Time Clock Dongle...";
 2400PROCtestclock
 2500PROCreadregisters
 2600PRINT'
 2700PROCshowregisters
 2800PROCprinttime
 2900PROCchangetime
 3000PROCchangedate
 3100IFnewtime$<>"" OR newdate$<>""PROCreadregisters:PROCprinttime
 3200END
 3300DEFPROCwriteddrb(D%)
 3400REM Write a byte to User port Data Direction Register
 3500A%=151
 3600X%=&62:
 3700Y%=D%
 3800CALL&FFF4
 3900ENDPROC
 4000DEFPROCwriteorb(O%)
 4100REM Write a byte to User port Output Register
 4200A%=151
 4300X%=&60
 4400Y%=O%
 4500CALL&FFF4
 4600ENDPROC
 4700DEFFNreadirb
 4800REM Read a byte from User Port Input Register
 4900A%=150
 5000X%=&60
 5100Y%=0
 5200REM Result in Y register, mask off and return as result
 5300=(USR&FFF4 AND &FFFFFF) DIV &10000
 5400DEFFNconvert(C%)
 5500REM Convert the register number (3bit number only) to binary bits
 5600REM Bit 4 is always 0
 5700bin$="0"
 5800FORB%=0TO2
 5900bit=(C% AND 2^B%):IFbit bit=1
 6000bin$=bin$+STR$bit
 6100NEXT
 6200REM Convert the binary register number into a sequence of bytes for RTC
 6300tx$=""
 6400REM Each bit is sent as 2 bytes with low half 7 then 5 for bit set and
 6500REM lower half 6 then 4 for bit clear
 6600REM Build a string of 8 bytes for the 4 bits to be sent
 6700FORB%=1TO4
 6800IFMID$(bin$,B%,1)="0" tx$=tx$+clear$  ELSE tx$=tx$+set$
 6900NEXT
 7000=tx$
 7100DEFPROCsendregister(R%)
 7200REM Transmit the required register number down the User Port
 7300REM Convert the register number to a sequence of bytes understood by RTC
 7400txstring$=FNconvert(R%)
 7500REM Now send the 8 bytes individually to RTC via User Port Output Register
 7600FORB%=1TO15STEP2
 7700byte$="&"+MID$(txstring$,B%,2):byte=EVALbyte$
 7800PROCwriteorb(byte)
 7900NEXT
 8000ENDPROC
 8100DEFFNreadchip
 8200REM Read the selected register from RTC
 8300rd$=""
 8400REM Each register returns 1 byte of data, arrives as 8 bytes where only
 8500REM LSB is required
 8600FORB%=1TO8
 8700REM build string of the sequence of 8 bytes read from RTC
 8800byte=FNreadirb
 8900rd$=rd$+STRING$(2-LEN(STR$~byte),"0")+STR$~byte
 9000REM After 1st byte read, requires a write to Output Register of value 6
 9100REM After bytes 2-7 read, requires 4,6 pattern writing to Output Register
 9200REM After 7th byte read, requires 4,0 pattern writing to obtain final byte
 9300IFB%=1PROCwriteorb(6)
 9400IFB%>1 AND B%<7 PROCwriteorb(4):PROCwriteorb(6)
 9500IFB%=7 PROCwriteorb(4):PROCwriteorb(0)
 9600NEXT
 9700REM Now retain low bit of the 8 bytes read, in reverse order
 9800chip=0:bin$=""
 9900FORB%=1TO15STEP2
10000chip$="&"+MID$(rd$,B%,2)
10100chip=EVALchip$
10200bit=chip AND 1
10300bin$=STR$bit+bin$
10400NEXT
10500REM Now discard bit 7 of our binary string (in fact, bit 6 is not
10600REM required except for the Minutes register which is a 7 bit reg)
10700bin$=RIGHT$(bin$,7)
10800REM Now convert from binary to get the actual value read from RTC register
10900data=0
11000FORB%=1TO7
11100bit=VAL(MID$(bin$,B%,1))
11200IFbit data=data+2^(7-B%)
11300NEXT
11400=EVAL(STR$~data)
11500DEFFNreadregister(R%)
11600PROCwriteddrb(device):REM open comms channel with RTC
11700PROCsendregister(R%):REM send required register number to RTC
11800PROCstartrx
11900=FNreadchip:REM do the read
12000DEFPROCvars
12100DIM register(7):REM 8 registers in RTC dongle
12200device=&A7:REM RTC clock device address
12300txstring$=STRING$(16,"0")
12400set$="0705":clear$="0604"
12500ENDPROC
12600DEFPROCwrite(R%,T%)
12700T%=EVAL("&"+STR$T%)
12800PROCwriteddrb(device)
12900PROCsendregister(R%)
13000IFR%=0 T%=T%+32+64:REM Reg 0 requires top 2 bits of data to be 1
13100FOR B%=0TO6
13200IF T% AND 2^B% PROCwriteorb(7):PROCwriteorb(5) ELSE PROCwriteorb(6):PROCwriteorb(4)
13300NEXT
13400REM send "end of transmission" message
13500PROCendtx
13600ENDPROC
13700DEFPROCstartrx
13800REM send sequence to begin reading from chip
13900PROCwriteddrb(6)
14000PROCwriteorb(0):PROCwriteorb(2)
14100PROCwriteorb(0):PROCwriteorb(4):REM this pattern required next, now we are ready to read data from the register
14200ENDPROC
14300DEFPROCendtx
14400REM send to sequence to end chip writes
14500PROCwriteorb(0):PROCwriteorb(2)
14600PROCwriteorb(0):PROCwriteorb(0)
14700ENDPROC
14800DEFPROCtestclock
14900REM Uses same method as Acorn (write &13 to reg 7 and read back, then 0)
15000PROCwrite(7,13):IFFNreadregister(7)<>13 PRINT"Clock failure":END
15100PROCwrite(7,0):IFFNreadregister(7)<>0 PRINT"Clock failure":END
15200ENDPROC
15300DEFPROCshowregisters
15400FORE%=0TO7
15500PRINT"Register ";E%;": ";register(E%)
15600NEXT
15700ENDPROC
15800DEFPROCprinttime
15900PRINT'"The time is ";
16000IFregister(4)>11 ampm$="pm" ELSEampm$="am"
16100hours=register(4):IFhours>12 hours=hours-12
16200minutes=register(6)
16300day=register(2)
16400month=register(0)
16500year=1981+register(1)
16600PRINT;hours;":";STRING$(2-LENSTR$minutes,"0");minutes;" ";ampm$;" on ";day;"/";month;"/";year
16700ENDPROC
16800DEFPROCchangetime
16900REPEAT
17000REPEAT
17100PRINT"Input new time (hh:mm) ";:INPUT""newtime$
17200IFnewtime$="" PRINT"Unchanged":UNTIL-1:UNTIL-1:ENDPROC
17300colon=INSTR(newtime$,":")
17400FORB%=1TOLENnewtime$
17500IFINSTR("0123456789:",MID$(newtime$,B%,1)) ELSE colon=0
17600NEXT
17700UNTILcolon ANDLENnewtime$>3
17800newhours=VAL(LEFT$(newtime$,colon-1)):newminutes=VAL(MID$(newtime$,colon+1))
17900UNTIL newhours<24 AND newhours>=0 AND newminutes<60 AND newminutes>=0
18000PRINT"Setting new time...";
18100PROCwrite(6,newminutes):PROCwrite(4,newhours)
18200PRINT
18300ENDPROC
18400DEFPROCchangedate
18500REPEATREPEAT
18600PRINT"Input new date (dd/mm/yy) ";:INPUT""newdate$
18700IFnewdate$="" PRINT"Unchanged":UNTIL-1:UNTIL-1:ENDPROC
18800slash1=INSTR(newdate$,"/"):slash2=INSTR(MID$(newdate$,slash1+1),"/")+slash1
18900FORB%=1TOLENnewdate$
19000IFINSTR("0123456789/",MID$(newdate$,B%,1)) ELSE slash1=0
19100NEXT
19200UNTILslash1>0 AND slash2>0 AND LENnewdate$>4
19300newdays=VAL(LEFT$(newdate$,slash1-1)):newmonths=VAL(MID$(newdate$,slash1+1,slash2-1)):newyear=VAL(MID$(newdate$,slash2+1))
19400valid=-1
19500IFnewdays>0 AND newdays<32 ELSE valid=0
19600IFnewmonths>0 AND newmonths<13 ELSE valid=0
19700IFnewmonths=2 AND newdays>29 valid=0
19800IFnewmonths=4 OR newmonths=6 OR newmonths=9 OR newmonths=11 THEN IF newdays>30 valid=0
19900IFnewyear>99 AND newyear<1900 valid=0
20000UNTILvalid
20100IFnewyear>1900 newyear=newyear-1900
20200IFnewyear>81 newyear=newyear-81
20300PRINT"Setting new date...";
20400PROCwrite(2,newdays):PROCwrite(0,newmonths):PROCwrite(1,newyear)
20500PRINT
20600ENDPROC
20700ENDPROC
20800DEFPROCreadregisters
20900FORE%=0TO7:REM read all registers
21000register(E%)=FNreadregister(E%)
21100NEXT
21200ENDPROC
It works with the real dongle:
capture47.png
But unfortunately John's prototype is still incommunicado:
capture48.png
Image

awilliams
Posts: 42
Joined: Sun Feb 22, 2015 10:51 am
Location: Adelaide, Australia
Contact:

Re: Clock Failure

Post by awilliams » Thu Sep 24, 2020 1:02 am

Brilliant!
I got some PCF8573 in the post on Monday and breadboarded up what I thought the circuit was from the pictures of the dissembled one.
I once wrote 6502 code in ROM to bit bang IIC on the user port to drive a PCF8574 io expander so I was about to start co-opting that to the RTC when I found your code. I have abandoned my code for now and have started testing my breadboard version with your code. This failed initially. If i get that working I will test with FS106 and table the circuit.

I also turned my shed upside-down, not a page was left unturned because I have seen the circuit for the original, but do not have it now.
Alan

User avatar
BeebMaster
Posts: 3628
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Clock Failure

Post by BeebMaster » Thu Sep 24, 2020 1:48 pm

Good luck - do we think it definitely is a PCF8573 inside there then? Because we're running out of possible candidates if it isn't.

And it still wouldn't surprise me (said it before many times) if Del-Boy went round there in 1984, "Hermann my son...deal of a lifetime! I've got these totally unique absolute guaranteed incompatible Rajah Computers clock chips, made in Silicon Valley, East Korea...5,000 of 'em...yours for half a dollar each!"
Image

awilliams
Posts: 42
Joined: Sun Feb 22, 2015 10:51 am
Location: Adelaide, Australia
Contact:

Re: Clock Failure

Post by awilliams » Thu Sep 24, 2020 2:45 pm

The circuit certainly suggests a PCF8573 but I am starting to suspect they have not used a standard part. While it seems a lot of trouble to go to something's are not adding up.

I am going to wire one to a pi or arduino and see what works there over the next few days.

All I have to add for my evenings works is that when I connect my little usb logic analyser, which can decode IIC, it says 'Address Write 55' and does not get an ACK from the chip so stops there.

I can't see in your code where the device address byte comes from. You have a variable device=&A7 but as far as I can see that is just the DDRB value, its not the IIC device address which according to the data sheet ought to be &D0 or maybe &68 depending on how the W/R bit is included.

I am also a bit concerned with what its doing with the extra three signals COMP, EXTPF, PFIN. Potentially the power fail signals could be used to detect a dead flat dongle but I don't know if it does that. It would be nice if it was actually not using them at all.

I have IIC slave code for atTiny85 running for another project so I was thinking plan B might be to program something else to exist at the right IIC address and work as expected. With a little extra fiddling it would be possible to expose the nvram too and a sidways rom in the beeb may be able to implement some of the *CONFIGURE /UNPLUG stuff from the M128/Compact.

If Acorn used a chip nobody else could by why then go to the bother of potting it in epoxy resin.
There is quite a family of them but none come in the same package any more:
https://www.nxp.com/docs/en/user-guide/UM10301.pdf

What might help us is to connect a real dongle with a few dupont leads to an Arduino running the IIC scan sketch. That would tell us categorically what the IIC address of the chip in the dongle is and thus if its a standard PCF8573 or not. This might need a couple of 4k7 resisters to 5v on the SDA and SCL lines, the arduino pullups might not be good enough on their own. I was surprised these are not present on the dongle board but they never read the clock lines and the 6522 pull must be good enough on the data line.

I am not sure if I still have an original dongle. I suspect not, but I will have a look over the weekend.

I was thinking that if a new one was to be made I would do it in the current fashion of fitting directly into the user port like piTubeDirect and friends do on the tube and 1MHzE bus.

Alan

User avatar
BeebMaster
Posts: 3628
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Clock Failure

Post by BeebMaster » Thu Sep 24, 2020 5:39 pm

One of things I found when doing all that was that there didn't seem to be a device address in use for the clock, it was a case of selecting a register and reading or writing it. For the same reason, I couldn't communicate with the dongle using Martin B's I2C ROM.

So maybe what we are searching for is a pre- or non-I2C device which just happens to have the same pinout and registers as a PCF8573??

It would be good to solve this mystery, but of course in one way it's a bit academic - there are hacks to the L3 code to skip the check for the presence of the dongle, and with the v1.06 source we can build the code without a dongle check by altering the DONGLE variable.
Image

User avatar
jgharston
Posts: 4121
Joined: Thu Sep 24, 2009 12:22 pm
Location: Whitby/Sheffield
Contact:

Re: Clock Failure

Post by jgharston » Thu Sep 24, 2020 7:16 pm

BeebMaster wrote:
Sat Aug 22, 2020 11:56 pm
The bit I couldn't get to work was where it prints the "Clock failure" message, it's quite ingenious and took me a while to figure out how it works. It JSRs to the printing routine, and the string to be printed is in the code after the jump, and I eventually worked out from the Advanced User Guide that the program counter is pushed on the stack when it does a JSR, so it pulls the PC back from the stack and uses this as the address of the string to print, until it finds a null byte, then pushes the PC back on the stack and returns. But it doesn't work for some reason in isolation, so I had to put in my own Clock failure error message.
It's standard 'print inline string' code, but you've transcribed it incorrectly:
440.VSTRIN PLA
450 STA VSTPTR
460 PLA
470 STA VSTPTR + 1
480 LDY# 0
490.VSTRLP INC VSTPTR
500 BNE INCPX
510 INC VSTPTR + 1
520.INCPX LDA (VSTPTR),Y ; fetch byte from inline string pointed to by (VSTPTR)
530 BMI VSTRNX
540 JSR &FFE7
550 JMP VSTRLP
560.VSTRNX JMP(VSTPTR) \continue execution \\ ISW WAS JMI

Code: Select all

$ bbcbasic
PDP11 BBC BASIC IV Version 0.32
(C) Copyright J.G.Harston 1989,2005-2020
>_

User avatar
BeebMaster
Posts: 3628
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Clock Failure

Post by BeebMaster » Thu Sep 24, 2020 10:44 pm

I had a few problems with the assembler mnemonics in the source code, I don't know why Acorn didn't use the same mnemonics in their own BBC BASIC assembler! JMI had me stumped for ages...I kept thinking Jump if Minus, that doesn't exist - till I realised it means Jump with Indirect addressing. Same with the one at line 520, the original is LDAIY which I sort of resolved in my mind as Load A immediate (as in LDAIM which = LDA#) with Y indexing!

I've also had to change VSTPTR to point to a zero page location or it gives a Byte error.

For some reason it still doesn't work though - I just get a set of carriage returns printed instead of the "Clock failure" message.
Image

User avatar
BeebMaster
Posts: 3628
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Clock Failure

Post by BeebMaster » Thu Sep 24, 2020 10:58 pm

I'd mistakenly translated OSASCI into FFE7, so I've changed it to FFE3 and now I do get characters printed but it looks like it's skipping a byte - the message comes out as "lc alr" followed by gibberish.
Image

awilliams
Posts: 42
Joined: Sun Feb 22, 2015 10:51 am
Location: Adelaide, Australia
Contact:

Re: Clock Failure

Post by awilliams » Fri Sep 25, 2020 1:56 am

I think that its accidental that my logic analyser picked up IIC address 55 in the bitsteam from your code. I set your code to just repeatedly read.

When I started FS106 with the logic analyser it did not detect the bit stream as IIC. I captured from BREAK as the initial conditions of the 6522 look important.

I am inclined to agree this is getting a bit academic.

User avatar
BeebMaster
Posts: 3628
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Clock Failure

Post by BeebMaster » Fri Sep 25, 2020 7:12 pm

True, we don't need to have working Clock dongles to keep the L3 file server in use, but it would be still good to know what the blue blazes Acorn actually put in it!
Image

Post Reply

Return to “8-bit acorn hardware”