10 REM Y2K fix for CMOS clock
     20 REM Copyright (C) Mark Bush 2002
     30 REM For BBC Micros with CMOS clock
     40 :
     50 REM Uses 2 bytes of zero page at
     60 REM &70, &71, preserving contents
     70 REM and a page of static RAM (only
     80 REM 4 bytes are used).
     90 :
    100 ZP=&70
    110 ZP1=&71
    120 OSBYTE=&FFF4
    130 DIM C% 1000
    140 FOR D% = 4 TO 7 STEP 3
    150 P%=&8000
    160 O%=C%
    170 [OPT D%
    180 \ setup ROM header
    190 EQUB &00
    200 EQUW &00
    210 JMP SERVICE
    220 EQUB &82           \ code for a normal service ROM
    230 EQUB COPY-&8000
    240 EQUB &02
    250 EQUS "MJB Y2K Fix"
    260 EQUB &00
    270 EQUS "2.00"
    280 .COPY
    290 EQUB &00
    300 EQUS "(C)2002 Mark Bush"
    310 EQUB &00
    320 .SERVICE
    330 CMP #&02   \ claim static RAM
    340 BEQ WKSP
    350 CMP #&27   \ a reset has occurred
    360 BEQ RESET
    370 RTS
    380 .WKSP
    390 TYA
    400 STA &0DF0,X  \ save our workspace page number
    410 LDA #&02
    420 INY       \ claim 1 page for our use
    430 RTS
    440 .RESET
    450 \ after a reset, the MOS vectors will have been reset
    460 \ so we need to redo the redirection of OSWORD through
    470 \ our routine
    480 TYA
    490 PHA
    500 \ save zero page locations
    510 LDA ZP
    520 PHA
    530 LDA ZP1
    540 PHA
    550 \ OSBYTE &A8 retrieves start of extended vector area
    560 LDX #&00
    570 LDY #&FF
    580 LDA #&A8
    590 JSR OSBYTE
    600 \ (X;Y) returned
    610 STX ZP
    620 STY ZP1
    630 \ each vector is 3 bytes long and OSWORD is the 7th so
    640 \ we need to offset 18 bytes (6 vectors) to reach it
    650 LDY #&12
    660 \ save new vector as:
    670 \ byte 1: target low byte
    680 \ byte 2: target high byte
    690 \ byte 3: ROM number to page in to access target
    700 LDA #TIM MOD 256
    710 STA (ZP),Y
    720 INY
    730 LDA #TIM DIV 256
    740 STA (ZP),Y
    750 INY
    760 LDX &F4   \ our ROM number
    770 STA (ZP),Y
    780 \ retrieve our saved workspace page number
    790 LDA &0DF0,X
    800 STA ZP1
    810 LDA #&00
    820 STA ZP
    830 \ get current address of the OSWORD routine (which may
    840 \ already be redirected) and save 1 less in our workspace
    850 \ (we use the trick of pushing the address on the stack
    860 \ and using RTS to go there which adds 1 to the address)
    870 LDA &020C
    880 SEC
    890 SBC #&01
    900 STA (ZP)
    910 INC ZP
    920 LDA &020D
    930 SBC #&00
    940 STA (ZP)
    950 \ do the same for the address we want OSWORD to return to
    960 \ for call &0E so we can process the output
    970 INC ZP
    980 LDA #RET MOD 256
    990 SEC
   1000 SBC #&01
   1010 STA (ZP)
   1020 INC ZP
   1030 LDA #RET DIV 256
   1040 SBC #&00
   1050 STA (ZP)
   1060 \ restore the zero page locations
   1070 PLA
   1080 STA ZP1
   1090 PLA
   1100 STA ZP
   1110 \ set WORDV to now redirect through extended vectors
   1120 LDA #&12
   1130 STA &020C
   1140 LDA #&FF
   1150 STA &020D
   1160 \ restore state and return
   1170 PLA
   1180 TAY
   1190 LDA #&27
   1200 RTS
   1210 .TIM
   1220 \ OSWORD now always redirects here first
   1230 \ check if it is call &0E for the CMOS
   1240 \ clock routine
   1250 CMP #&0E
   1260 BEQ CLOCK
   1270 \ save call number
   1280 PHA
   1290 \ keep 2 stack places for the OSWORD address
   1300 \ to return to
   1310 PHA
   1320 PHA
   1330 \ preserve X
   1340 TXA
   1350 PHA
   1360 \ preserve zero page locations
   1370 LDA ZP
   1380 PHA
   1390 LDA ZP1
   1400 PHA
   1410 \ get our ROM number, retrieve our workspace page and
   1420 \ extract the saved OSWORD address and put it in the
   1430 \ stack over the 2 places saved for it above
   1440 LDX &F4
   1450 LDA &0DF0,X
   1460 STA ZP1
   1470 LDA #&00
   1480 STA ZP
   1490 TSX
   1500 LDA (ZP)
   1510 STA &0105,X
   1520 INC ZP
   1530 LDA (ZP)
   1540 STA &0106,X
   1550 \ restore zero page
   1560 PLA
   1570 STA ZP1
   1580 PLA
   1590 STA ZP
   1600 \ restore X and A
   1610 PLA
   1620 TAX
   1630 PLA
   1640 \ stack now has the address (less 1) of OSWORD
   1650 \ so RTS will go there as if it had been called
   1660 \ normally
   1670 RTS
   1680 .CLOCK
   1690 \ we now know that we are in the CMOS clock OSWORD (&0E)
   1700 \ preserve Y and X
   1710 TYA
   1720 PHA
   1730 TXA
   1740 PHA
   1750 \ keep 1 space to hold the type of call this is (X;Y):
   1760 \ 0: return string of date and time
   1770 \ 1: return BCD format not including the century
   1780 \ 2: turn BCD format into string format
   1790 PHA
   1800 \ keep 4 stack places
   1810 \ these will be the OSWORD address and our address of
   1820 \ RET so that a return from the real OSWORD comes back to us
   1830 PHA
   1840 PHA
   1850 PHA
   1860 PHA
   1870 \ preserve zero page locations
   1880 LDA ZP
   1890 PHA
   1900 LDA ZP1
   1910 PHA
   1920 \ as before, get the OSWORD and RET addresses and
   1930 \ put them into their places in the stack
   1940 LDX &F4
   1950 LDA &0DF0,X
   1960 STA ZP1
   1970 LDA #&00
   1980 STA ZP
   1990 TSX
   2000 \ OSWORD address
   2010 LDA (ZP)
   2020 STA &0103,X
   2030 INC ZP
   2040 LDA (ZP)
   2050 STA &0104,X
   2060 \ RET address
   2070 INC ZP
   2080 LDA (ZP)
   2090 STA &0105,X
   2100 INC ZP
   2110 LDA (ZP)
   2120 STA &0106,X
   2130 \ save (X;Y)
   2140 STY ZP1
   2150 LDA &0108,X
   2160 STA ZP
   2170 LDA (ZP)
   2180 STA &0107,X
   2190 \ restore X
   2200 LDA &0108,X
   2210 TAX
   2220 \ restore zero page
   2230 PLA
   2240 STA ZP1
   2250 PLA
   2260 STA ZP
   2270 \ restore A
   2280 LDA #&0E
   2290 \ "return" to OSWORD
   2300 RTS
   2310 .RET
   2320 \ OSWORD will return to this point
   2330 \ retrieve the call type (X;Y)
   2340 PLA
   2350 \ if it is 1 then we are done
   2360 CMP #&01
   2370 BEQ DONE
   2380 \ otherwise preserve zero page locations
   2390 LDA ZP
   2400 PHA
   2410 LDA ZP1
   2420 PHA
   2430 \ get X and Y to access the return string
   2440 TSX
   2450 LDA &0103,X
   2460 STA ZP
   2470 LDA &0104,X
   2480 STA ZP1
   2490 \ set century to "20"
   2500 LDA #ASC("2")
   2510 LDY #&0B
   2520 STA (ZP),Y
   2530 LDA #ASC("0")
   2540 INY
   2550 STA (ZP),Y
   2560 \ restore zero page
   2570 PLA
   2580 STA ZP1
   2590 PLA
   2600 STA ZP
   2610 .DONE
   2620 \ we are done
   2630 \ restore X, Y and A and return
   2640 PLA
   2650 TAX
   2660 PLA
   2670 TAY
   2680 LDA #&0E
   2690 RTS
   2700 .DNE
   2710 ]
   2720 NEXT
   2730 S$=STR$ ~C%
   2740 E$=STR$ ~(DNE-&8000+C%)
   2750 OSCLI "SAVE Y2KROM "+S$+" "+E$