C C BBC Microcomputer emulation package C BBCMOS_TIME - System time functions C C Copyright Peter Coghlan 1996 C C These routines are incredibly convoluted because : C - Practically nothing including EDIV can process a quadword C into a value larger than a longword. C - We want the time to the nearest 10ms - C Lib$Cvt_From/To_Interal_Time will not do that but C does give one value which is good for up to a year. C - We want TIME to reflect the VMS uptime which might be years - C Sys$NumTim / Lib$Cvt_VecTim makes that very difficult C but they do let us work to the nearest 10ms. C - A delta time of zero is not allowed for some reason. We must C treat zero as a special case and substitute the C smallest delta time (100ns) instead. C Osword 1 returns the number of 10ms units since the last system boot, C unless the time has been reset using Osword 2. Subroutine BBC_Time(A, X, Y) Implicit None Include '($SyiDef)' Include '($LibDtDef)' Integer Status, Sys$GetTim, Sys$NumTim, Lib$Cvt_VecTim, Lib$GetSyi Integer Lib$Sub_Times, Lib$Cvt_From_Internal_Time, Lib$Emul Integer Lib$Add_Times, Lib$Cvt_To_Internal_Time, Lib$Ediv Integer A, X, Y, Address, Seconds, Hundredths C If Y6502_Read_Memory is declared Integer *4, we get errors assigning C values larger than 127 to bytes. Integer *1 Y6502_Read_Memory C The BbcTime structure is extended to 8 bytes in order to use Lib$Emul. Structure /BbcTime/ Union Map Integer *4 Long0, Long1 End Map Map Integer *1 Byte0, Byte1, Byte2, Byte3, Byte4, %Fill(3) End Map End Union End Structure Structure /Timbuf/ Integer *2 Years, Months, Days, Hours, Minutes, Seconds, Csecs End Structure Integer *4 Realtime(2), BootTime(2), OffsetTime(2), Uptime(2) Integer *4 AdjustedTime(2), TimeA(2), TimeB(2), SmallestDelta(2) Record /BbcTime/ BbcTime Record /Timbuf/ TimeArray Logical Add_Time /.False./ C Default OffsetTime to a delta time of 0 (or 100ns depending on pov) Data OffsetTime /ZFFFFFFFF, ZFFFFFFFF/ Data SmallestDelta /ZFFFFFFFF, ZFFFFFFFF/ External Lib$_NegTim C First, find the address of the control block Address = X + Y * 256 C Get the system uptime in internal form (Time now minus boottime) Status = Sys$GetTim(RealTime) If (.Not. Status) Call Lib$Signal(%Val(Status)) Status = Lib$GetSyi(Syi$_BootTime, BootTime, , , , ) If (.Not. Status) Call Lib$Signal(%Val(Status)) Status = Lib$Sub_Times(RealTime, BootTime, UpTime) If (.Not. Status) Call Lib$Signal(%Val(Status)) C Osword 1 - Read system clock If (A .Eq. 1) Then C Add or subtract any offset set earlier with Osword 2. If (Add_Time) Then Status = Lib$Add_Times(Uptime, OffsetTime, AdjustedTime) Else Status = Lib$Sub_Times(UpTime, OffsetTime, AdjustedTime) Endif If (.Not. Status) Call Lib$Signal(%Val(Status)) C Find out how many seconds in the adjusted time Status = Lib$Cvt_From_Internal_Time(Lib$K_Delta_Seconds, + Seconds, AdjustedTime) If (.Not. Status) Call Lib$Signal(%Val(Status)) C Also find out how many hundredths of a second in the adjusted time Status = Sys$NumTim(TimeArray, AdjustedTime) If (.Not. Status) Call Lib$Signal(%Val(Status)) C Extend TimeArray.Csecs to a longword Hundredths = TimeArray.Csecs C Add the hundredths to 100 times the seconds to get a BBC style time. C Because a delta time can be up to 10000 days (8.64E10 hundredths of C a second) it might not fit in a signed longword (2.15E9). Therefore, C use Lib$Emul to generate the result. Status = Lib$Emul(Seconds, 100, Hundredths, BbcTime) If (.Not. Status) Call Lib$Signal(%Val(Status)) C Finally, we have a 5 byte BBC style time - store it for the caller. Call Y6502_Write_Memory(Address + 4, BbcTime.Byte4) Call Y6502_Write_Memory(Address + 3, BbcTime.Byte3) Call Y6502_Write_Memory(Address + 2, BbcTime.Byte2) Call Y6502_Write_Memory(Address + 1, BbcTime.Byte1) Call Y6502_Write_Memory(Address + 0, BbcTime.Byte0) C Osword 2 - Set system clock Else If (A .Eq. 2) Then C Initialise the top three bytes of the BbcTime quadword to 0 BbcTime.Long1 = 0 C Put the time we wish to set the clock to in the rest of BbcTime BbcTime.Byte0 = Y6502_Read_Memory(Address + 0) BbcTime.Byte1 = Y6502_Read_Memory(Address + 1) BbcTime.Byte2 = Y6502_Read_Memory(Address + 2) BbcTime.Byte3 = Y6502_Read_Memory(Address + 3) BbcTime.Byte4 = Y6502_Read_Memory(Address + 4) C Separate it out into seconds and hundredths of a second as there C is no library routine which can handle both and also cope with a C full spec delta time. (We can still have problems - the BbcTime C might be larger than a longword after dividing it by 100 but these C problems are not as important as wierd things happening when you try C to read the time after the system has been up for x days.) Status = Lib$Ediv(100, BbcTime, Seconds, Hundredths) If (.Not. Status) Call Lib$Signal(%Val(Status)) C Genetate an internal time from the seconds and one from the hundredths C Treat zero as a special case each time as it is not allowed! If (Seconds .Ne. 0) Then Status = Lib$Cvt_To_Internal_Time(Lib$K_Delta_Seconds, + Seconds, TimeA) If (.Not. Status) Call Lib$Signal(%Val(Status)) Else TimeA(1) = SmallestDelta(1) TimeA(2) = SmallestDelta(2) Endif If (Hundredths .Ne. 0) Then TimeArray.Csecs = Hundredths TimeArray.Seconds = 0 TimeArray.Minutes = 0 TimeArray.Hours = 0 TimeArray.Days = 0 TimeArray.Months = 0 TimeArray.Years = 0 Status = Lib$Cvt_Vectim(TimeArray, TimeB) If (.Not. Status) Call Lib$Signal(%Val(Status)) Else TimeB(1) = SmallestDelta(1) TimeB(2) = SmallestDelta(2) Endif C Add the two times together Status = Lib$Add_Times(TimeA, TimeB, AdjustedTime) If (.Not. Status) Call Lib$Signal(%Val(Status)) C Subtract from the system uptime to arrive at the offset time. C The offset time is subtracted from the system uptime to give C the value returned to the caller when they read the time with C Osword 1. Status = Lib$Sub_Times(Uptime, AdjustedTime, OffsetTime) C If we got a negative result then time was set to a value greater C than uptime. In this case, find out how much greater and set a C flag to note that the offset should be added to the uptime rather C than subtracted as usual. (Why can't delta times be negative?) If (Status .Eq. %Loc(Lib$_NegTim)) Then Status = Lib$Sub_Times(AdjustedTime, Uptime, OffsetTime) Add_Time = .True. Else Add_Time = .False. Endif If (.Not. Status) Call Lib$Signal(%Val(Status)) Endif Return End