view level1/modules/clock2.asm @ 1692:96c2b4a93483

Robert's reorganized clock2.asm, with each clock supported in alphabetical order
author boisy
date Mon, 02 Aug 2004 13:19:03 +0000
parents a4d7f06d1797
children
line wrap: on
line source

********************************************************************
* Clock2 - Real-Time Clock Subroutines
*
* $Id$
*
*
* RTC Specific Notes:
*
* Dallas Semiconductor DS1216 SmartWatch
*
* Wakeup sequence $C5 3A A3 5C C5 3A A3 5C
*
* Time byte sequence in Binary Coded Decimal
*
* byte	bit 7     6     5     4     3     2     1      0
* 0        |       0.1 sec MSB       |        0.1 sec LSB     |
* 1        |  0  |   10 sec          |        seconds         |
* 2        |  0  |   10 min          |        minutes         |	
* 3        |12/24|  0   | AM/PM | HR |        Hour            |
*                       |  10 HR     |        Hour            |
*        0=12, 1=24       0=AM, 1=PM
* 4        | 0   |  0   |  OSC |RESET|  0  |  Weekday         |
* 5        | 0   |  0   |  10  Date  |        Date            |
* 6        | 0   |  0   |   0  |10 Month|     Month           |
* 7        |        10 Year          |        Year            |
*
* OSC = 1;  turns off clock to save battery. RESET not used in Coco circuit.
*
* When inserted in external ROM socket, the clock is addressed at:
* $C000     bit = 0
* $C001     bit = 1
* $C004     read byte to wakeup then send wakeup sequence to bit toggles.
*           Then either read or send time.
*
*
* Edt/Rev  YYYY/MM/DD  Modified by
* Comment
* ------------------------------------------------------------------
*   1      2003/08/18  Boisy G. Pitre
* Stripped from clock.asm in order to modularize clocks.
*
*          2004/07/13  Robert Gault
* Added Vavasour/Collyer emulator & MESS (Disto) versions and relocated
* 'GetTime equ'   statement so it is not within a chip heading.
*
*          2004/07/28  Robert Gault
* Complete rewrite of SmartWatch segment which would never have worked.
* See previous versions for old code if desired. Routine now will search
* through all MPI slots to find clock and accept either AM/PM or military
* time. User notified if clock not found or data memory not available.
*
* Initialization routine contains code that bypasses OS-9 system calls to
* acquire needed low RAM that can't become ROM. This type of code is not
* recommended in most cases but nothing else was usable.
*          2004/07/31  Robert Gault
* Added a settime routine and changed "no clock found" routine. If the
* clock is not found, the D.Time entries are cleared but no message is sent.
* Date -t will never get passed one minute.
*          2004/07/31  Rodney Hamilton
* Improved RTCJVEmu code, conditionalized RTC type comments.
*          2004/08/2   Robert Gault
* Alphabetized all clock listings so things can be found much more easily.
* Placed list of clock types at beginning of source for record keeping.

         nam   Clock2    
         ttl   Real-Time Clock Subroutines

         ifp1            
         use   defsfile  
         endc            

*---------------------------------------------------------------------------
* Setup for specific RTC chip
*
* CHIP TYPES INCLUDED:
*
* RTCBB+RTCCloud9
* RTCDriveWire
* RTCDsto2+RTCDsto4
* RTCElim
* RTCHarrs
* RTCJVEmu
* RTCMESSEmu
* RTCSmart
* RTCSoft

         IFNE  RTCBB+RTCCloud9
         IFNE  RTCBB     
RTC.Base equ   $FF5C      In SCS* Decode
         ELSE            
RTC.Base equ   $FF7C      Fully decoded RTC
         ENDC            
RTC.Zero equ   -4         Send zero bit by writing this offset
RTC.One  equ   -3         Send one bit by writing this offset
RTC.Read equ   0          Read data from this offset
         ENDC            

         IFNE  RTCDriveWire
RTC.Base equ   $0000     
         ENDC            

         IFNE  RTCDsto2+RTCDsto4
RTC.Base equ   $FF50      Base address of clock
         ENDC            

         IFNE  RTCElim   
RTC.Sped equ   $20        32.768 KHz, rate=0
RTC.Strt equ   $06        binary, 24 Hour, DST disabled
RTC.Stop equ   $86        bit 7 set stops clock to allow setting time
RTC.Base equ   $FF72      I don't know base for this chip.
         ENDC            

         IFNE  RTCHarrs  
RTC.Base equ   $FF60      Base address for clock
         ENDC            

         IFNE  RTCJVEmu
RTC.Base equ   $FFC0
         ENDC

         IFNE  RTCMESSEmu
RTC.Base equ   $FF50
         ENDC

         IFNE  RTCSmart  
RTC.Base equ   $C000      clock mapped to $C000-$DFFF; $FFA6 MMU slot
RTC.Zero equ   0          Send zero bit
RTC.One  equ   1          Send ones bit
RTC.Read equ   4
*D.SWPage                 on system DP; Refer to os9defs.
D.RTCSlt equ   0          on SmartWatch ?data? page
D.RTCFlg equ   1          on SW page
D.RTCMod equ   2
D.Temp   equ   3          on SW page, holds "clock" data
D.Start  equ   4          on SW page, code starts here
         ENDC            

         IFNE  RTCSoft   
RTC.Base equ   0          Have to have one defined.
         ENDC            

*------------------------------------------------------------
*
* Start of module
*
         mod   len,name,Sbrtn+Objct,ReEnt+0,JmpTable,RTC.Base

name     fcs   "Clock2"  
         fcb   1         

         IFNE  MPIFlag   
SlotSlct fcb   MPI.Slot-1 Slot constant for MPI select code
         ENDC            

* Jump table for RTC
*
* Entry points:
*  - Init
*  - SetTime
*  - GetTime
JmpTable                 
         lbra  Init      
         bra   GetTime   
         nop             
         lbra  SetTime   

*--------------------------------------------------------------------------
* GetTime Subroutine
*
* This subroutine is called by the main clock module.
*

GetTime  equ   *

         IFNE  RTCBB+RTCCloud9
*
* Update time from B&B RTC
*
         pshs  u,y,cc    
         leay  ReadBCD,pcr Read bytes of clock

TfrTime  orcc  #IntMasks  turn off interrupts
         ldu   M$Mem,pcr  Get base address

         IFNE  MPIFlag   
         ldb   >MPI.Slct  Select slot
         pshs  b         
         andb  #$F0      
         orb   SlotSlct,pcr
         stb   >MPI.Slct 
         ENDC            

         lbsr  SendMsg    Initialize clock
         ldx   #D.Sec    
         ldb   #8         Tfr 8 bytes

tfrloop  jsr   ,y         Tfr 1 byte

         bitb  #$03      
         beq   skipstuf   Skip over day-of-week, etc.
         leax  -1,x      
skipstuf decb            
         bne   tfrloop   

         IFNE  MPIFlag   
         puls  b         
         stb   >MPI.Slct  restore MPAK slot
         ENDC            

         puls  u,y,cc,pc 

ClkMsg   fcb   $C5,$3A,$A3,$5C,$C5,$3A,$A3,$5C
* Enable clock with message $C53AA35CC53AA35C
SendMsg  lda   RTC.Read,u Send Initialization message to clock
         leax  <ClkMsg,pcr
         ldb   #8        
msgloop  lda   ,x+       
         bsr   SendByte  
         decb            
         bne   msgloop   
         rts             

SendBCD  pshs  b          Send byte to clock, first converting to BCD
         bitb  #$03      
         bne   BCDskip    Send zero for day-of-week, etc.
         lda   #0        
         bra   SndBCDGo  
BCDskip  lda   ,x        
SndBCDGo tfr   a,b       
         bra   binenter  
binloop  adda  #6        
binenter subb  #10       
         bhs   binloop   
         puls  b         
SendByte coma             Send one byte to clock
         rora            
         bcc   sendone   
sendzero tst   RTC.Zero,u
         lsra            
         bcc   sendone   
         bne   sendzero  
         rts             
sendone  tst   RTC.One,u 
         lsra            
         bcc   sendone   
         bne   sendzero  
         rts             

ReadBCD  pshs  b         
         ldb   #$80       High bit will rotate out after we read 8 bits
readbit  lda   RTC.Read,u Read a bit
         lsra            
         rorb             Shift it into B
         bcc   readbit    Stop when marker bit appears
         tfr   b,a       
         bra   BCDEnter   Convert BCD number to Binary
BCDLoop  subb  #6         by subtracting 6 for each $10
BCDEnter suba  #$10      
         bhs   BCDLoop   
         stb   ,x        
         puls  b,pc      
         ENDC            

         IFNE  RTCDriveWire
*
* Update time from DriveWire
*
         lbra  DoDW      

         use   bbwrite.asm

DoDW     pshs  y,x,cc    
         lda   #'#        Time packet
         orcc  #IntMasks  Disable interrupts
         lbsr  SerWrite  
         bsr   SerRead    Read year byte
         bcs   UpdLeave  
         sta   <D.Year   
         bsr   SerRead    Read month byte
         bcs   UpdLeave  
         sta   <D.Month  
         bsr   SerRead    Read day byte
         bcs   UpdLeave  
         sta   <D.Day    
         bsr   SerRead    Read hour byte
         bcs   UpdLeave  
         sta   <D.Hour   
         bsr   SerRead    Read minute byte
         bcs   UpdLeave  
         sta   <D.Min    
         bsr   SerRead    Read second byte
         bcs   UpdLeave  
         sta   <D.Sec    
         bsr   SerRead    Read day of week (0-6) byte
UpdLeave puls  cc,x,y,pc 

         use   bbread.asm
         ENDC            

         IFNE  RTCDsto2  
*
* Disto 2-in-1 RTC time update
*
         pshs  a,cc       Save old interrupt status and mask IRQs
         bsr   RTCPre    

         bsr   GetVal     Get Year
         bsr   GetVal     Get Month
         bsr   GetVal     Get Day
         decb             ldb #5
         stb   2,x       
         decb            
         lda   ,x        
         anda  #3        
         bsr   GetVal1    Get Hour
         bsr   GetVal     Get Minute
         bsr   GetVal     Get Second

RTCPost  clr   >$FFD9     2 MHz  (Really should check $A0 first)
         puls  cc,b      

         IFNE  MPIFlag   
         stb   >MPI.Slct  Restore saved "currently" selected MPak slot
         ENDC            

         clrb            
         rts             

RTCPre   orcc  #IntMasks 

         IFNE  MPIFlag   
         ldb   >MPI.Slct  Save currently selected MPak slot on stack
         stb   3,s       
         andb  #$F0      
         orb   >SlotSlct,pcr Get slot to select
         stb   >MPI.Slct  Select MPak slot for clock
         ENDC            

         ldy   #D.Time   
         ldx   M$Mem,pcr 
         clr   1,x       
         ldb   #12       
         clr   >$FFD8     1 MHz
         rts             

GetVal   stb   2,x       
         decb            
         lda   ,x         read tens digit from clock
         anda  #$0f      
GetVal1  pshs  b          save b
         ldb   #10       
         mul              multiply by 10 to get value
         stb   ,y         save 10s value
         puls  b          set up clock for ones digit
         stb   2,x       
         decb            
         lda   ,x         read ones digit from clock
         anda  #$0f      
         adda  ,y         add ones + tens
         sta   ,y+        store clock value into time packet
         rts             

         ENDC            

         IFNE  RTCDsto4  
*
* Disto 4-in-1 RTC time update
*
         IFNE  MPIFlag   
         pshs  cc         Save old interrupt status and mask IRQs
         orcc  #IntMasks 
         ldb   >MPI.Slct  Save currently selected MPak slot on stack
         pshs  b         
         andb  #$F0      
         orb   >SlotSlct,pcr Select MPak slot for clock
         stb   >MPI.Slct 
         ENDC            

         ldx   M$Mem,pcr 
         ldy   #D.Time    Start with seconds

         ldb   #11       
         bsr   GetVal     Get Year
         bsr   GetVal     Get Month
         bsr   GetVal     Get Day
         lda   #3         Mask tens digit of hour to remove AM/PM bit
         bsr   GetVal1    Get Hour
         bsr   GetVal     Get Minute
         bsr   GetVal     Get Second

         IFNE  MPIFlag   
         puls  b          Restore saved "currently" selected MPak slot
         stb   >MPI.Slct 
         puls  cc,pc      Restore previous IRQ status
         ELSE            
         rts              No MPI, don't need to mess with slot, CC
         ENDC            

GetVal   lda   #$0f       Mask to apply to tens digit
GetVal1  stb   1,x       
         decb            
         anda  ,x         read ones digit from clock
         pshs  b          save b
         ldb   #10       
         mul              multiply by 10 to get value
         stb   ,y         Add to ones digit
         puls  b         
         stb   1,x       
         decb            
         lda   ,x         read tens digit from clock and mask it
         anda  #$0f      
         adda  ,y        
         sta   ,y+       
         rts             
         ENDC            

         IFNE  RTCElim   
*
* Eliminator time update  (lacks MPI slot select ability)
*
         ldx   M$Mem,pcr  get RTC base address from fake memory requirement
         ldb   #$0A       UIP status register address
         stb   ,x         generate address strobe
         lda   1,x        get UIP status
         bpl   NoUIP      Update In Progress, go shift next RTC read
         lda   #TkPerSec/2 set up next RTC read attempt in 1/2 second
         sta   <D.Tick    save tick
         bra   UpdTExit   and return

NoUIP    decb             year register address
         stb   ,x         generate address strobe
         lda   1,x        get year
         sta   <D.Year   
         decb             month register address
         stb   ,x        
         lda   1,x       
         sta   <D.Month  
         decb             day of month register address
         stb   ,x        
         lda   1,x       
         sta   <D.Day    
         ldb   #4         hour register address
         stb   ,x        
         lda   1,x       
         sta   <D.Hour   
         ldb   #2         minute register address
         stb   ,x        
         lda   1,x       
         sta   <D.Min    
         clrb             second register address
         stb   ,x        
         lda   1,x       
SaveSec  sta   <D.Sec    
UpdTExit rts             
         ENDC            

         IFNE  RTCHarrs  
*
* Update time from Harris RTC 
*
         pshs  cc        
         orcc  #IntMasks  Disable interrupts

         ldu   M$Mem,pcr  Get base address
         ldy   #D.Time    Pointer to time in system map

         lda   #%00001100 Init command register (Normal,Int. Disabled,
         sta   $11,u      Run,24-hour mode, 32kHz)

         lda   ,u         Read base address to set-up clock regs for read
         lda   6,u        Get year
         sta   ,y+       
         lda   4,u        Get month
         sta   ,y+       
         lda   5,u        Get day
         sta   ,y+       
         lda   1,u        Get hour
         sta   ,y+       
         lda   2,u        Get minute
         sta   ,y+       
         lda   3,u        Get second
         sta   ,y+       

         puls  cc,pc      Re-enable interrupts
         ENDC            

         IFNE  RTCJVEmu
*
* Vavasour / Collyer Emulator (ignores MPI slot)
*
         ldx   #RTC.Base
         ldd   ,x	get year (CCYY)
         suba  #20
         bmi   yr1	19xx, OK as is
yr0      addb  #100	20xx adjustment
         deca		also check for
         bpl   yr0	21xx (optional)
yr1      stb   <D.Year	set year (~YY)
         ldd   2,x	get date
         std   <D.Month	set date (MMDD)
         IFNE  Level-1
         ldd   4,x	get time (wwhh)
         sta   <D.Daywk	set day of week
         ELSE
         ldb   5,x	get hour (hh)
         ENDC
         stb   <D.Hour	set hour (hh)
         ldd   6,x	get time (mmss)
         std   <D.Min	set time (mmss)
*        rts		fall thru to Setime/Init rts
         ENDC

         IFNE  RTCMESSEmu
*
* MESS time update in Disto mode (ignores MPI)
*   Assumes that PC clock is in AM/PM mode!!!
*
         ldx   #RTC.Base
         ldy   #D.Time
         ldb   #12           counter for data
         stb   1,x
         lda   ,x
         anda  #7
         IFNE  Level-1
         sta   <D.Daywk
         ENDC
         decb
         bsr   getval
         lda   -1,y
         cmpa  #70          if >xx70 then its 19xx
         bhi   not20
         adda  #100
         sta   -1,y
not20    bsr   getval       month
         bsr   getval       day
         lda   #7           AM/PM mask
         stb   1,x
         anda  ,x
         bitb  #4
         pshs  cc
         anda  #3
         bsr   getval1
         puls  cc
         beq   AM
         lda   #12         convert to 24hr time as it is PM
         adda  -1,y
         sta   -1,y
AM       bsr   getval      minute
* and now fall through into get second
getval   lda   #$0f
         stb   1,x
         anda  ,x
getval1  decb
         pshs  b
         ldb   #10
         mul
         stb   ,y
         puls  b
         stb   1,x
         decb
         lda   ,x
         anda  #$0f
         adda  ,y
         sta   ,y+
*        rts		fall thru to Setime/Init rts
         ENDC

         IFNE  RTCSmart
*
* Update time from Smartwatch RTC
*

         pshs  cc,d,x,y,u
         orcc  #$50
         lda   D.SWPage
         clrb
         tfr   d,u         point to working space
         lda   $FF7F
         pshs  a
         lda   D.RTCSlt,u  info for MPI slot
         sta   $FF7F
         clr   D.RTCMod,u  set for read time
         jsr   D.Start,u   jsr to it
         lbra  exit
         ENDC 

         IFNE  RTCSoft   
*
*
* Software time update
*
*
         lda   <D.Min     grab current minute
         inca             minute+1
         cmpa  #60        End of hour?
         blo   UpdMin     no, Set start of minute
         ldd   <D.Day     get day, hour
         incb             hour+1
         cmpb  #24        End of Day?
         blo   UpdHour    ..no
         inca             day+1
         leax  <months-1,pcr point to months table with offset-1: Jan = +1
         ldb   <D.Month   this month
         cmpa  b,x        end of month?
         bls   UpdDay     ..no, update the day
         cmpb  #2         yes, is it Feb?
         bne   NoLeap     ..no, ok
         ldb   <D.Year    else get year
         andb  #$03       check for leap year: good until 2099
         cmpd  #$1D00     29th on leap year?
         beq   UpdDay     ..yes, skip it
NoLeap   ldd   <D.Year    else month+1
         incb             month+1
         cmpb  #13        end of year?
         blo   UpdMonth   ..no
         inca             year+1
         ldb   #$01       set month to jan
UpdMonth std   <D.Year    save year, month
         lda   #$01       day=1st
UpdDay   clrb             hour=midnite
UpdHour  std   <D.Day     save day,hour
         clra             minute=00
UpdMin   clrb             seconds=00
         std   <D.Min     save min,secs
UpdTExit rts             

months   fcb   31,28,31,30,31,30,31,31,30,31,30,31 Days in each month
         ENDC            


*--------------------------------------------------------------------------
* SetTime Subroutine
*
* This subroutine is called by the main clock module.
*

SetTime  equ   *

         IFNE  RTCBB+RTCCloud9
*
* Set B&B RTC from Time variables
*
         pshs  u,y,cc    
         leay  SendBCD,pcr Send bytes of clock
         lbra  TfrTime   
         ENDC            

         IFNE  RTCDsto2  
*
* Set Disto 2-in-1 RTC from Time variables
*
         pshs  a,cc      
         lbsr  RTCPre     Initialize

         bsr   SetVal     Set Year
         bsr   SetVal     Set Month
         bsr   SetVal     Set Day
         ldd   #$0805     $08 in A, $05 in B
         bsr   SetVal1    Set Hour   (OR value in A ($08) with hour)
         bsr   SetVal     Set Minute
         bsr   SetVal     Set Second

         lbra  RTCPost    Clean up + return

SetVal   clra            
SetVal1  stb   2,x        Set Clock address
         decb            
         pshs  b         
         ldb   ,y+        Get current value
DvLoop   subb  #10        Get Tens digit in A, ones digit in B
         bcs   DvDone    
         inca            
         bra   DvLoop    
DvDone   addb  #10       
         sta   ,x         Store tens digit
         tfr   b,a       
         puls  b          Get back original clock address
         stb   2,x       
         decb            
         sta   ,x         Store ones digit
         rts             
         ENDC            

         IFNE  RTCDsto4  
*
* Set Disto 4-in-1 RTC from Time variables
*
         pshs  cc        
         orcc  #IntMasks 

         IFNE  MPIFlag   
         ldb   >MPI.Slct  Save currently selected MPak slot
         pshs  b         
         andb  #$F0      
         orb   >SlotSlct,pcr Get slot to select
         stb   >MPI.Slct  Select MPak slot for clock
         ENDC            

         ldy   #D.Time+6 
         ldx   M$Mem,pcr 
         clrb            
         bsr   SetVal     Set Second
         bsr   SetVal     Set Minute
         bsr   SetVal     Set Hour
         bsr   SetVal     Set Day
         bsr   SetVal     Set Month
         bsr   SetVal     Set Year

         IFNE  MPIFlag   
         puls  b          Restore old MPAK slot
         stb   >MPI.Slct 
         ENDC            

         puls  cc        
         clrb             No error
         rts             

SetVal   clr   ,-s        Create variable for tens digit
         lda   ,-y        Get current value
DvLoop   suba  #10        Get Tens digit on stack, ones digit in A
         bcs   DvDone    
         inc   ,s        
         bra   DvLoop    
DvDone   adda  #10       
         stb   1,x        Set Clock address
         incb            
         sta   ,x         Store ones digit
         stb   1,x       
         incb            
         puls  a         
         sta   ,x         Store tens digit
         rts             
         ENDC            

         IFNE  RTCElim   
*
* Set Eliminator RTC from D.Time
*
         pshs  cc         save interrupt status
         orcc  #IntMasks  disable IRQs
         ldx   M$Mem,pcr  get RTC base address from fake memory requirement
         ldy   #D.Time    point [Y] to time variables in DP
         ldd   #$0B*256+RTC.Stop
         bsr   UpdatCk0   stop clock before setting it
         ldb   #RTC.Sped 
         bsr   UpdatCk0   set crystal speed, output rate
         bsr   UpdatClk   go set year
         bsr   UpdatClk   go set month
         bsr   UpdatClk   go set day of month
         bsr   UpdatCk0   go set day of week (value doesn't matter)
         bsr   UpdatCk0   go set hours alarm (value doesn't matter)
         bsr   UpdatClk   go set hour
         bsr   UpdatCk0   go set minutes alarm (value doesn't matter)
         bsr   UpdatClk   go set minute
         bsr   UpdatCk0   go set seconds alarm (value doesn't matter)
         bsr   UpdatClk   go set second
         ldd   #$0B*256+RTC.Strt
         bsr   UpdatCk0   go start clock
         puls  cc         Recover IRQ status
         clrb            
         rts             

UpdatClk ldb   ,y+        get data from D.Time variables in DP
UpdatCk0 std   ,x         generate address strobe, save data
         deca             set [A] to next register down
         rts             

         IFGT  Level-1
* OS-9 Level Two code only (for now)
NewSvc   fcb   F$NVRAM    Eliminator adds one new service call
         fdb   F.NVRAM-*-2
         fcb   $80        end of service call installation table

*------------------------------------------------------------
* read/write RTC Non Volatile RAM (NVRAM)
*
* INPUT:  [U] = pointer to caller's register stack
*         R$A = access mode (1 = read, 2 = write, other = error)
*         R$B = byte count (1 through 50 here, but in other implementations
*               may be 1 through 256 where 0 implies 256)
*         R$X = address of buffer in user map
*         R$Y = start address in NVRAM
*
* OUTPUT:  RTC NVRAM read/written
*
* ERROR OUTPUT:  [CC] = Carry set
*                [B] = error code
F.NVRAM  tfr   u,y        copy caller's register stack pointer
         ldd   #$0100     ask for one page
         os9   F$SRqMem  
         bcs   NVR.Exit   go report error...
         pshs  y,u        save caller's stack and data buffer pointers
         ldx   R$Y,y      get NVRAM start address
         cmpx  #50        too high?
         bhs   Arg.Err    yes, go return error...
         ldb   R$B,y      get NVRAM byte count
         beq   Arg.Err   
         abx              check end address
         cmpx  #50        too high?
         bhi   Arg.Err    yes, go return error...
         lda   R$A,y      get direction flag
         cmpa  #WRITE.    put caller's data into NVRAM?
         bne   ChkRead    no, go check if read...
         clra             [D]=byte count
         pshs  d          save it...
         ldx   <D.Proc    get caller's process descriptor address
         lda   P$Task,x   caller is source task
         ldb   <D.SysTsk  system is destination task
         ldx   R$X,y      get caller's source pointer
         puls  y          recover byte count
         os9   F$Move     go MOVE data
         bcs   NVR.Err   
         ldy   ,s         get caller's register stack pointer from stack
         lda   R$Y+1,y    get NVRAM start address
         adda  #$0E       add offset to first RTC NVRAM address
         ldb   R$B,y      get byte count
         ldx   M$Mem,pcr  get clock base address from fake memory requirement
         pshs  cc,b       save IRQ enable status and byte counter
         orcc  #IntMasks  disable IRQs
WrNVR.Lp ldb   ,u+        get caller's data
         std   ,x         generate RTC address strobe and save data to NVRAM
         inca             next NVRAM address
         dec   1,s        done yet?
         bne   WrNVR.Lp   no, go save another byte
         puls  cc,b       recover IRQ enable status and clean up stack
NVR.RtM  puls  y,u        recover register stack & data buffer pointers
         ldd   #$0100     return one page
         os9   F$SRtMem  
NVR.Exit rts             

Arg.Err  ldb   #E$IllArg  Illegal Argument error
         bra   NVR.Err   

ChkRead  cmpa  #READ.     return NVRAM data to caller?
         bne   Arg.Err    illegal access mode, go return error...
         lda   R$Y+1,y    get NVRAM start address
         adda  #$0E       add offset to first RTC NVRAM address
         ldx   M$Mem,pcr  get clock base address from fake memory requirement
         pshs  cc,b       save IRQ enable status and byte counter
         orcc  #IntMasks  disable IRQs
RdNVR.Lp sta   ,x         generate RTC address strobe
         ldb   1,x        get NVRAM data
         stb   ,u+        save it to buffer
         inca             next NVRAM address
         dec   1,s        done yet?
         bne   RdNVR.Lp   no, go get another byte
         puls  cc,a       recover IRQ enable status, clean up stack ([A]=0)
         ldb   R$B,y      [D]=byte count
         pshs  d          save it...
         ldx   <D.Proc    get caller's process descriptor address
         ldb   P$Task,x   caller is source task
         lda   <D.SysTsk  system is destination task
         ldu   R$X,y      get caller's source pointer
         puls  y          recover byte count
         ldx   2,s        get data buffer (source) pointer
         os9   F$Move     go MOVE data
         bcc   NVR.RtM   
NVR.Err  puls  y,u        recover caller's stack and data pointers       
         pshs  b          save error code
         ldd   #$0100     return one page
         os9   F$SRtMem  
         comb             set       Carry for error
         puls  b,pc       recover error code, return...
         ENDC
         ENDC  

         IFNE  RTCHarrs  
*
* Set Harris 1770 RTC from Time variables
*
         pshs  cc        
         orcc  #IntMasks  Disable interrupts

         ldu   M$Mem,pcr  Get base address
         ldy   #D.Time    Pointer to time in system map

         lda   #%00000100 Init command register (Normal,Int. Disabled,
         sta   $11,u      STOP clock,24-hour mode, 32kHz)

         lda   ,y+        Get year
         sta   6,u       
         lda   ,y+        Get month
         sta   4,u       
         lda   ,y+        Get day
         sta   5,u       
         lda   ,y+        Get hour
         sta   1,u       
         lda   ,y+        Get minute
         sta   2,u       
         lda   ,y         Get second
         sta   3,u       

         lda   #%00001100 Init command register (Normal,Int. Disabled,
         sta   $11,u      START clock,24-hour mode, 32kHz)

         puls  cc,pc      Re-enable interrupts
         ENDC            

         IFNE  RTCSmart
*
* Update time from Smartwatch RTC
*
* This set time routine forces military time. It can't turn off clock but can
* be used as a timer if time set to 0:0:0  hr:min:sec
         pshs  cc,d,x,y,u
         orcc  #$50
         lda   D.SWPage
         clrb
         tfr   d,u         point to working space
         lda   $FF7F
         pshs  a
         lda   D.RTCSlt,u  info for MPI slot
         sta   $FF7F
         lda   #-1
         sta   D.RTCMod,u  indicate set time rather than read
         IFGT  Level-1
         ldx   #D.Slice
         ELSE
         ldx   #D.TSec
         ENDC
         tfr   u,y                          get location of safe region
         leay  >D.Start+alrtend-reloc,y     point to end of wakeup code
         lda   ,-x                          get D.Time data and store it
         ldb   #4                           convert tenths sec, sec, min, hours
         bsr   binbcd                       to binary coded decimal
         IFGT  Level-1
         lda   D.Daywk
         ELSE
         clra
         ENDC
         sta  ,y+                           set day of week if present
         lda  ,x
         ldb  #3                            convert day of month, month, year
         bsr  binbcd                        to BCD
         jsr   >D.Start,u                   send data to clock
         lbra  exit
         
binbcd   pshs b
bcd3     clrb
bcd1     cmpa #10
         bcs  bcd2
         addd #$f610      decrease bin regA by 10 add bcd $10 to regB
         bra  bcd1
bcd2     pshs a
         addb ,s+         add in remainder; BCD = binary when less than 10
         stb  ,y+         place in message to clock
         lda  ,-x         get next byte of D.Time
         dec  ,s          decrease counter
         bne  bcd3
         puls b,pc

* This becomes D.Start
reloc    equ   *
         IFGT  Level-1
         lda   D.HINIT
         anda  #$CC
         sta   $FF90
         ENDC
         ldb   $FFA6       choose to use normal location
         pshs  b
         ldb   #$3E
         stb   $FFA6       reset MMU for clock
         sta   $FFDE
         ldd   #RTC.Base
         tfr   a,dp        DP now points to clock
         tst   D.RTCMod,u  are we reading the clock or setting it?
         beq   findclk     go if reading
         lbsr  wakeup      we are setting a found clock
         lbra  found
findclk  lda   #-1
         sta   alrtend,pcr
         lbsr  wakeup      wakeup the clock
         ldx   #D.Sec      one incoming byte dropped
         lda   #8          bytes to get
         pshs  a
L0050    ldb   #8          bits to get
L0052    lsr   <RTC.Read   get a bit
         rora
         decb  
         bne   L0052
         tst   D.RTCFlg,u
         bne   maybe
         cmpa  D.Temp,u
         beq   maybe       clock might look like ROM
         inc   D.RTCFlg,u  found the clock
maybe    sta   ,x          transfer it to time
         lda   ,s          check loop counter
         cmpa  #8
         beq   L006F       skip if 0.1 sec
         cmpa  #4
         bne   L006B       skip if not day of week
         IFGT  Level-1
         lda   ,x          move to correct location
         anda  #7
         sta   D.Daywk
         ENDC
         bra   L006F
L006B    cmpa  #5          hour byte
         bne   wd
         lda   ,x
         bita  #%10000000  12/24hr
         beq   wd
         bita  #%00100000  AM/PM
         pshs  cc
         anda  #%00011111  keep only time
         puls  cc
         bne   pm
         cmpa  #$12        these are BCD tests
         bne   am
         clr   ,x          12AM=0hr military
         bra   wd
pm       cmpa  #$12        12PM=12hr military
         beq   wd
         adda  #$12        1-11PM add 12 for military
am       sta   ,x
wd       leax  -1,x        update time slot
         bsr   L0087       convert from BCD to binary
L006F    dec   ,s
         bne   L0050       get the next byte from clock
         lda   1,x         get year
         cmpa  #50         half assed test for century
         bhs   c19
         adda  #100        make it 20th
c19      sta   1,x
         leas  1,s
         tst   D.RTCFlg,u
         bne   found
         ldb   D.RTCSlt,u
         bitb  #$30
         beq   found
         subb  #$10        not found so move to next slot
         stb   D.RTCSlt,u
         stb   $FF7F
         lbra  findclk
found    clra              system DP is always 0
         tfr   a,dp
         IFGT  Level-1
         lda   D.HINIT     reset system before rts
         sta   $FF90
         ENDC
         sta   $FFDF
         puls  a
         sta   $FFA6
         rts               go back to normal code location
         
* Convert BCD to binary
L0087    lda   1,x
         clrb  
L008A    cmpa  #$10        BCD 10
         bcs   L0094
         addd  #$F00A      decrease BCD by $10 and add binary 10
         bra   L008A
L0094    pshs  a
         addb  ,s+
         stb   1,x
term     rts   

wakeup   lda   <RTC.Read   clear the clock for input
* When getting time data, bit0 is rotated into a full byte. This
* means the result is $00 or $FF for ROM. Any other value used for a test
* will give a false positive for the clock.
         clrb
         bita  #1
         beq   w1
         comb
w1       stb   D.Temp,u
         leax  alert,pcr   point to message
nxtbyte  ldb   #8          8 bytes to send
         lda   ,x+
         cmpa  #-1         changed from 0 to -1 to accommodate settime
         beq   term
nxtbit   lsra              bits sent to clock by toggling
         bcs   high        Zero and One
         cmpa  <RTC.Zero   faster than tst
         bra   nxtbit2
high     cmpa  <RTC.One
nxtbit2  decb  
         bne   nxtbit
         bra   nxtbyte

* SmartWatch wakeup sequence
alert    fcb   $c5,$3a,$a3,$5c,$c5,$3a,$a3,$5c
* The next 8 bytes become time data when setting the clock. Terminator is
* now $FF instead of $00 to permit $00 as data.
alrtend  fcb   $FF
alrtime  rmb   7
         fcb   $FF

exit     equ   *
         puls  a
         sta   $FF7F       restore MPI
         tst   D.RTCFlg,u  was clock found?
         beq   noclock
         puls  cc,d,x,y,u,pc
         ENDC

*-----------------------------------------------------------------------
* RTC-specific initializations here
*

Init     equ   *   
      
         IFNE  RTCDsto4  
* Disto 4-N-1 RTC specific initialization
         ldx   M$Mem,pcr 
         ldd   #$010F     Set mode for RTC chip
         stb   1,x       
         sta   ,x        
         ldd   #$0504    
         sta   ,x        
         stb   ,x        
         ENDC            

         IFNE  RTCElim   
         IFGT  Level-1
* Eliminator will install specific system calls
         leay  NewSvc,pcr insert syscalls
         os9   F$SSvc    
         ENDC
         ENDC            

         IFNE  RTCSmart
         clr   <D.SWPage        safe location for Read
         pshs  d,x,y,u
         IFGT  Level-1
         ldx   <D.SysMem        get memory map
         ldy   <D.SysDAT        get MMU map
         ldb   #$20             first 20 pages always in use
         abx                    point to page
A1       tst  ,x+
         beq   A2               found free page so go
         incb                   update page counter
         bpl   A1               still in RAM only, then go
A4       lda   #2               can't find RAM only memory
         leax  mem_mes,pcr
         ldy   #40
         os9   I$WritLn
         puls  d,x,y,u,pc
A2       pshs  b                save page #
         andb  #%11100000       modulo MMU blocks
         lsrb                   convert to DAT byte value
         lsrb                   page# * $100 / $2000 = MMU #
         lsrb                   $2000/$100=$20 at 2 bytes per MMU
         lsrb                   then page#/$10 gives answer
         ldd   b,y              get the MMU value
         cmpd  #$333E           is DAT unused
         pshs  cc               save answer
         inc   1,s              update page # for a used page
         puls  cc,b             get back answer and page #
         beq   A1               if unused keep going
         lda   #RAMinUse
         sta   ,-x              flag the memory in use
         ELSE
         ldx   <$20        D.FMBM     free memory bit map
         ldy   <$22        top of memory bit map
         ldb   #-1         preset counter
A1       lda   ,x+         get bits
         incb              update $800 counter
         pshs  y           test for end of map
         cmpx  ,s+
         bhi   A4          send error message
         clr   -1,s        preset bit counter
A2       inc   ,s
         lsra              read left to right
         bcs   A2
         lda   ,s+
         deca              convert to number of shifts
         cmpa  #8          overflow value
         beq   A1          get more map on ov
         pshs  a           save the number of shifts
         lda   #8          bytes*8
         mul
         addb  ,s          add modulo $800
         cmpb  #$7E        need RAM not ROM for clock data
         bhs   A4
         lda   #%10000000  need to create mask to map the
A5       lsra              unused bit, so reverse the process
         dec   ,s          decrease shift counter
         bne   A5
         leas  1,s         yank counter
         ora   -1,x        mark bit used and
         sta   -1,x        tell the system
         bra   A3          
A4       leas  1,s            yank bit info
         lda   #2             error path #
         leax  mem_mes,pcr    good memory not found
         ldy   #200
         os9   I$WritLn
         puls  d,x,y,u,pc
A3       equ   *
         ENDC
         stb   <D.SWPage      keep the info for Read
         tfr   b,a
         clrb
         tfr   d,x            regX now points to SW data page
         ldb   $FF7F          get MPI values
         andb  #3             keep IRQ info
         orb   #$30           force slot #4 to start search
         stb   D.RTCSlt,x     save the info
         clr   D.RTCFlg,x     set to no clock found
         clr   D.RTCMod,x     set to read time
         leax  D.Start,x      safe location for moved code
         IFNE  H6309
         leay  reloc,pcr
         ldw   #exit-reloc
         tfm   y+,x+
         ELSE
         leau  reloc,pcr       relocation routine to move code
         ldy   #exit-reloc     to a RAM only location
B3       lda   ,u+
         sta   ,x+
         leay -1,y
         bne   B3
         ENDC
         puls  d,x,y,u,pc

noclock  equ   *
         ldd   #7          seven time bytes to clear
         ldx   #D.Time
         IFGT  Level-1
         sta   D.Daywk
         ENDC
nc       sta   ,x+
         decb
         bne   nc
         puls  cc,d,x,y,u,pc

mem_mes  fcc   /There is no system memory for/
         fcb   $0a
         fcc   /the SmartWatch. Please reduce/
         fcb   $0a
         fcc   /os9boot size or use soft clock./
         fcb   $0d

         ELSE

         rts                    used for clocks without Init routines
         ENDC

         emod            
len      equ   *         
         end