view level1/modules/clock.asm @ 2242:2a889a511f0a

Adding Aaron Wolfe's DriveWire work
author boisy
date Thu, 24 Dec 2009 01:16:31 +0000
parents d88e7ec829a5
children f506d1905781
line wrap: on
line source

********************************************************************
* Clock - NitrOS-9 System Clock
*
* CoCo 3 notes:
* Includes support for several different RTC chips, GIME Toggle
* IRQ fix, numerous minor changes.
*
* $Id$
*
* Edt/Rev  YYYY/MM/DD  Modified by
* Comment
* ------------------------------------------------------------------
*          ????/??/??
* NitrOS-9 2.00 distribution.
*
*   9r4    2003/01/01  Boisy G. Pitre
* Back-ported to OS-9 Level Two.
*
*   9r5    2003/08/18  Boisy G. Pitre
* Separated clock into Clock and Clock2 for modularity.
*
*   9r6    2003/09/04  Boisy G. Pitre
* Combined Level One and Level Two sources
*
*   9r7    2004/11/27  Phill Harvey-Smith
* Fixed bug in init routine that was causing DP and CC to
* be pulled off the stack and stored in D.Proc under Level 1
*		
*   9r7    2005/01/17  Boisy G. Pitre
* Fixed incorrect value for PIA initialization.  Robert indicated
* that it should be $3434, not $3435.
*		
*   9r7    2005/04/08  Phill Harvey-Smith
* Made the above level dependent as having PIAs inited with $3434
* will disable the IRQ from them, this is ok for Level 2/CoCo 3 as the
* IRQ is later enabled from the GIME, however the CoCo 1,2 and Dragon
* do not posses a GIME so anything dependent on the clock tick will 
* hang. So changed to conditionaly compile based on level :-
*
*   9r8    2005/12/04  Boisy G. Pitre
* Minor code optimizations, fixed issue in Level 1 where clock ran slow
* due to improper initialization of certain system globals.
 
         nam   Clock     
         ttl   NitrOS-9 System Clock
                         
         ifp1            
         use   defsfile  
         ifgt  Level-1   
         use   vtiodefs_cc3
         endc            
         endc            
                         
tylg     set   Systm+Objct
atrv     set   ReEnt+rev 
rev      set   8
edition  set   9         
                         
                         
*------------------------------------------------------------
*
* Start of module
*
         mod   len,name,tylg,atrv,init,0
                         
name     fcs   "Clock"   
         fcb   edition   
                         
                         
         ifeq  Level-1   
TkPerTS  equ   TkPerSec/10 ticks per time slice
         else            
TkPerTS  equ   2          ticks per time slice
         endc            
                         
*
* Table to set up Service Calls
*
NewSvc   fcb   F$Time    
         fdb   FTime-*-2 
         fcb   F$VIRQ    
         fdb   FVIRQ-*-2 
         ifgt  Level-1   
         fcb   F$Alarm   
         fdb   FALARM-*-2
         endc            
         fcb   F$STime   
         fdb   FSTime-*-2
         fcb   $80        end of service call installation table
                         
                         
*------------------------------------------------------------
*
* Handle F$STime system call
*
* First, copy time packet from user address space to system time
* variables, then fall through to code to update RTC.
*
FSTime   equ   *         
         ifgt  Level-1   
         ldx   <D.Proc    caller's process descriptor
         lda   P$Task,x   source is in user map
         ldx   R$X,u      address of caller's time packet
         ldu   #D.Time    destination address
         ldb   <D.SysTsk  destination is in system map
         lbsr  STime.Mv   get time packet (ignore errors)
         else            
         ldx   R$X,u     
         ldd   ,x        
         std   <D.Year   
         ldd   2,x       
         std   <D.Day    
         ldd   4,x       
         std   <D.Min    
         endc            
         lda   #TkPerSec  reset to start of second
         sta   <D.Tick   
         ldx   <D.Clock2  get entry point to Clock2
         clra             clear carry
         jmp   $06,x      and call SetTime entry point
                         
                         
*--------------------------------------------------
*
* Clock Initialization
*
* This vector is called by the kernel to service the first F$STime
* call.  F$STime is usually called by SysGo (with a dummy argument)
* in order to initialize the clock.  F$STime is re-vectored to the
* service code above to handle future F$STime calls.
*
*
                         
Clock2   fcs   "Clock2"  
                         
init                     
         ifeq  Level-1   
         pshs  dp,cc      save DP and CC
         clra            
         tfr   a,dp       set DP to zero
         else            
         ldx   <D.Proc    save user proc
         pshs  x         
         ldx   <D.SysPrc  make sys for link
         stx   <D.Proc   
         endc            
                         
         leax  <Clock2,pcr
         lda   #Sbrtn+Objct
         os9   F$Link    
                         
         bcc   LinkOk    
                         
         ifeq  Level-1   
         jmp   >$FFFE     level 1: jump to reset vector
         else            
         lda   #E$MNF    
         jmp   <D.Crash   level 2: jump to CRASH vector
         endc            
                         
LinkOk                   
         ifeq  Level-1   
         puls  cc,dp      ; Restore saved dp and cc
         else            
         puls  x         
         stx   <D.Proc    restore user proc
         endc            
                         
         sty   <D.Clock2  save entry point
InitCont                 
         ldx   #PIA0Base  point to PIA0
         clra             no error for return...
         pshs  cc         save IRQ enable status (and Carry clear)
         orcc  #IntMasks  stop interrupts
                         
         ifgt  Level-1   
* Note: this code can go away once we have a rel_50hz
         ifeq  TkPerSec-50
         ldb   <D.VIDMD   get video mode register copy
         orb   #$08       set 50 Hz VSYNC bit
         stb   <D.VIDMD   save video mode register copy
         stb   >$FF98     set 50 Hz VSYNC
         endc            
         endc            
                         
         sta   1,x        enable DDRA
         sta   ,x         set port A all inputs
         sta   3,x        enable DDRB
         coma            
         sta   2,x        set port B all outputs
                         
;	ldd	#$343C		[A]=PIA0 CRA contents, [B]=PIA0 CRB contents
                         
         ifgt  Level-1   
         ldd   #$3434     as per Robert Gault's suggestion
         else            
         ldd   #$3435     IRQ needs to be left enabled for Level1, as no GIME generated IRQ
         endif            
                         
         sta   1,x        CA2 (MUX0) out low, port A, disable HBORD high-to-low IRQs
         stb   3,x        CB2 (MUX1) out low, port B, disable VBORD low-to-high IRQs
                         
         ifgt  Level-1   
         lda   ,x         clear possible pending PIA0 HBORD IRQ
         endc            
         lda   2,x        clear possible pending PIA0 VBORD IRQ
                         
* Don't need to explicitly read RTC during initialization
         ldd   #59*256+$01 last second and last tick
         std   <D.Sec     will prompt RTC read at next time slice
         ifeq  Level-1
         ldb   #TkPerSec
         stb   <D.TSec    set ticks per second
         endc
         ldb   #TkPerTS   get ticks per time slice
         stb   <D.TSlice  set ticks per time slice
         stb   <D.Slice   set first time slice
         leax  SvcIRQ,pcr set IRQ handler
         stx   <D.IRQ    
                         
         ifgt  Level-1   
         leax  SvcVIRQ,pcr set VIRQ handler
         stx   <D.VIRQ   
         endc            
                         
         leay  NewSvc,pcr insert syscalls
         os9   F$SSvc    
                         
         ifgt  Level-1   
         ifne  H6309     
         oim              #$08,<D.IRQER
         else            
         lda   <D.IRQER   get shadow GIME IRQ enable register
         ora   #$08       set VBORD bit
         sta   <D.IRQER   save shadow register
         endc            
         sta   >IRQEnR    enable GIME VBORD IRQs
         endc            
                         
* Call Clock2 init routine
         ldy   <D.Clock2  get entry point to Clock2
         jsr   ,y         call init entry point of Clock2
InitRts  puls  cc,pc      recover IRQ enable status and return
                         
         ifeq  Level-1   
*
* Clock IRQ Entry Point
*
* For CoCo 1/2, called once every 16.667 milliseconds
SvcIRQ                   
         clra            
         tfr   a,dp       set direct page to zero
         tst   PIA0Base+3 get hw byte
         bmi   L0032      branch if sync flag on
         jmp   [>D.SvcIRQ] else service other possible IRQ
L0032    tst   PIA0Base+2 clear interrupt
         dec   <D.Tick    decrement tick counter
         bne   L007F      go around if not zero
         ldb   <D.Sec     get minutes/seconds
* Seconds increment
         incb             increment seconds
         cmpb  #60        full minute?
         bcs   L0079      nope...
*
* Call GetTime entry point in Clock2
*
         ldx   <D.Clock2  get entry point to Clock2
         jsr   $03,x      call GetTime entry point
         fcb   $8C        skip next 2 bytes
L0079    stb   <D.Sec     update sec
L007B    lda   <D.TSec    get ticks per second value
         sta   <D.Tick    and repopulate tick decrement counter
L007F    clra             clear A
         pshs  a          and save it on the stack
         ldy   <D.CLTb    get pointer to VIRQ Polling Entries
         bra   L009E      go to the processing portion of the loop
L0087    ldd   Vi.Cnt,x   get count down counter
         subd  #$0001     subtract tick count
         bne   L009C      branch if not at terminal count ($0000)
         lda   #$01      
         sta   ,s         set flag on stack to 1
         lda   Vi.Stat,x  get status byte
         beq   DelEntry   branch if zero (one shot, so delete)
L0096    ora   #Vi.IFlag  set interrupted flag
         sta   Vi.Stat,x  save in packet
         ldd   Vi.Rst,x   get reset count
L009C    std   Vi.Cnt,x   save tick count back
L009E    ldx   ,y++       get two bytes at Y
         bne   L0087      if not zero, branch
         lda   ,s+        else get byte off stack
         beq   GoAltIRQ   branch if zero
         ldx   <D.Proc    else get pointer to current process descriptor
         beq   L00AE      branch if none
         tst   P$State,x  test process' state
         bpl   UsrPoll    branch if system state not set
L00AE    jsr   [>D.Poll]  poll ISRs
         bcc   L00AE      keep polling until carry set
GoAltIRQ                 
         jmp   [>D.AltIRQ] jump into an alternate IRQ if available
DelEntry                 
         bsr   DelVIRQ    delete the VIRQ entry
         bra   L0096     
                         
UsrPoll  leay  >up@,pcr   point to routine to execute
         jmp   [>D.URtoSs] User to System
up@      jsr   [>D.Poll]  call polling routine
         bcc   up@        keep polling until carry set
         ldx   <D.Proc    get current process descriptor
         ldb   P$State,x  and its state
         andb  #^SysState turn off sysstate bit
         stb   P$State,x  save new state
         ldd   <P$SWI2,x 
         std   <D.SWI2   
         ldd   <D.UsrIRQ 
         std   <D.SvcIRQ 
         bra   GoAltIRQ  
                         
DelVIRQ  pshs  y,x        save off Y,X
dl@      ldx   ,y++       get next entry
         stx   -$04,y     move up
         bne   dl@        continue until all are moved
         puls  y,x        restore
         leay  -2,y       move back 2 from Y (points to last entry)
         rts              return
                         
* Install or Remove VIRQ Entry
FVIRQ    pshs  cc        
         orcc  #IntMasks  mask all interrupts
         ldy   <D.CLTb    get pointer to VIRQ polling table
         ldx   <D.Init    get pointer to init module
         ldb   PollCnt,x  get poll count
         ldx   R$X,u      get pointer to caller's X
         beq   L0118      branch if removing
         tst   ,y         entry available?
         beq   L010C     
         subb  #$02      
         lslb            
         leay  b,y       
         tst   ,y        
         bne   PTblFul    polling table full
L0106    tst   ,--y      
         beq   L0106     
         leay  $02,y     
L010C    ldx   R$Y,u     
         stx   ,y        
         ldy   R$D,u     
         sty   ,x        
         bra   L0124     
L0118    leax  R$Y,u      X = caller's Y
L011A    tst   ,y         end of VIRQ table
         beq   L0124      branch if so
         cmpx  ,y++       else compare to current VIRQ entry and inc Y
         bne   L011A      continue searching if not matched
         bsr   DelVIRQ    else delete entry
L0124    puls  cc        
         clrb            
         rts             
PTblFul  puls  cc        
         comb            
         ldb   #E$Poll   
         rts             
                         
                         
                         
* F$Time system call code
FTime    ldx   R$X,u     
         ldd   <D.Year   
         std   ,x        
         ldd   <D.Day    
         std   2,x       
         ldd   <D.Min    
         std   4,x       
         clrb            
         rts             
                         
                         
                         
                         
         else            
                         
                         
                         
                         
* NitrOS-9 Level 2 Clock
                         
GI.Toggl equ   %00000001  GIME CART* IRQ enable bit, for CC3
                         
* TC9 needs to reset more interrupt sources
*GI.Toggl equ %00000111 GIME SERINT*, KEYINT*, CART* IRQ enable bits
                         
                         
*---------------------------------------------------------
* IRQ Handling starts here.
*
* Caveat: There may not be a stack at this point, so avoid using one.
*         Stack is set up by the kernel between here and SvcVIRQ.
*
SvcIRQ   lda   >IRQEnR    get GIME IRQ Status and save it.
         ora   <D.IRQS   
         sta   <D.IRQS   
         bita  #$08       check for clock interrupt
         beq   NoClock   
         anda  #^$08      drop clock interrupt
         sta   <D.IRQS   
         ldx   <D.VIRQ    set VIRQ routine to be executed
         clr   <D.QIRQ    ---x IS clock IRQ
         bra   ContIRQ   
                         
NoClock  leax  DoPoll,pcr if not clock IRQ, just poll IRQ source
         ifne  H6309     
         oim              #$FF,<D.QIRQ	---x set flag to NOT clock IRQ
         else            
         lda   #$FF      
         sta   <D.QIRQ   
         endc            
ContIRQ  stx   <D.SvcIRQ 
         jmp   [D.XIRQ]   chain through Kernel to continue IRQ handling
                         
*------------------------------------------------------------
*
* IRQ handling re-enters here on VSYNC IRQ.
*
* - Count down VIRQ timers, mark ones that are done
* - Call DoPoll/DoToggle to service VIRQs and IRQs and reset GIME
* - Call Keyboard scan
* - Update time variables
* - At end of minute, check alarm
*
SvcVIRQ  clra             flag if we find any VIRQs to service
         pshs  a         
         ldy   <D.CLTb    get address of VIRQ table
         bra   virqent   
                         
virqloop                 
         ifgt  Level-2   
         ldd   2,y        get Level 3 extended map type
         orcc  #IntMasks 
         sta   >$0643    
         stb   >$0645    
         std   >$FFA1    
         andcc  #^IntMasks
         endc            
                         
         ldd   Vi.Cnt,x   decrement tick count
         ifne  H6309     
         decd             --- subd #1
         else            
         subd  #$0001    
         endc            
         bne   notzero    is this one done?
         lda   Vi.Stat,x  should we reset?
         bmi   doreset   
         lbsr  DelVIRQ    no, delete this entry
doreset  ora   #$01       mark this VIRQ as triggered.
         sta   Vi.Stat,x 
         lda   #$80       add VIRQ as interrupt source
         sta   ,s        
         ldd   Vi.Rst,x   reset from Reset count.
notzero  std   Vi.Cnt,x  
virqent  ldx   ,y++      
         bne   virqloop  
                         
         ifgt  Level-2   
         puls  d         
         orcc  #Carry    
         stb   >$0643    
         stb   >$FFA1    
         incb            
         stb   >$0645    
         stb   >$FFA1    
         andcc  #^IntMasks
         else            
         puls  a          get VIRQ status flag: high bit set if VIRQ
         endc            
                         
         ora   <D.IRQS    Check to see if other hardware IRQ pending.
         bita  #%10110111 any V/IRQ interrupts pending?
         beq   toggle    
         ifgt  Level-2   
         lbsr  DoPoll     yes, go service them.
         else            
         bsr   DoPoll     yes, go service them.
         endc            
         bra   KbdCheck  
toggle   equ   *         
         ifgt  Level-2   
         lbsr  DoToggle   no, toggle GIME anyway
         else            
         bsr   DoToggle   no, toggle GIME anyway
         endc            
                         
KbdCheck                 
         ifgt  Level-2   
         lda   >$0643     grab current map type
         ldb   >$0645    
         pshs  d          save it
         orcc  #IntMasks  IRQs off
         lda   >$0660     SCF local memory ---x
         sta   >$0643     into DAT image ---x
         sta   >$FFA1     and into RAM ---x
         inca            
         sta   >$0645    
         sta   >$FFA2     map in SCF, CC3IO, WindInt, etc.
         endc            
                         
         jsr   [>D.AltIRQ] go update mouse, gfx cursor, keyboard, etc.
                         
         ifgt  Level-2   
         puls  d          restore original map type ---x
         orcc  #IntMasks 
         sta   >$0643     into system DAT image ---x
         stb   >$0645    
         std   >$FFA1     and into RAM ---x
         andcc  #$AF      
         endc            
                         
         dec   <D.Tick    end of second?
         bne   VIRQend    no, skip time update and alarm check
         lda   #TkPerSec  reset tick count
         sta   <D.Tick   
                         
* ATD: Modified to call real time clocks on every minute ONLY.
         inc   <D.Sec     go up one second
         lda   <D.Sec     grab current second
         cmpa  #60        end of minute?
         blo   VIRQend    no, skip time update and alarm check
         clr   <D.Sec     reset second count to zero
                         
*
* Call GetTime entry point in Clock2
*
         ldx   <D.Clock2  get entry point to Clock2
         jsr   $03,x      call GetTime entry point
                         
NoGet    ldd   >WGlobal+G.AlPID
         ble   VIRQend    Quit if no Alarm set
         ldd   >WGlobal+G.AlPckt+3 does Hour/Minute agree?
         cmpd  <D.Hour   
         bne   VIRQend   
         ldd   >WGlobal+G.AlPckt+1 does Month/Day agree?
         cmpd  <D.Month  
         bne   VIRQend   
         ldb   >WGlobal+G.AlPckt+0 does Year agree?
         cmpb  <D.Year   
         bne   VIRQend   
         ldd   >WGlobal+G.AlPID
         cmpd  #1        
         beq   checkbel  
         os9   F$Send    
         bra   endalarm  
checkbel ldb   <D.Sec     sound bell for 15 seconds
         andb  #$F0      
         beq   dobell    
endalarm ldd   #$FFFF    
         std   >WGlobal+G.AlPID
         bra   VIRQend   
dobell   ldx   >WGlobal+G.BelVec
         beq   VIRQend   
         jsr   ,x        
VIRQend  jmp   [>D.Clock] jump to kernel's timeslice routine
                         
*------------------------------------------------------------
* Interrupt polling and GIME reset code
*
                         
*
* Call [D.Poll] until all interrupts have been handled
*
DoPoll                   
         ifgt  Level-2   
         lda   >$0643     Level 3: get map type
         ldb   >$0645    
         pshs  d          save for later
         endc            
d@       jsr   [>D.Poll]  call poll routine
         bcc   d@         until error (error -> no interrupt found)
                         
         ifgt  Level-2   
         puls  d         
         orcc  #IntMasks 
         sta   >$0643    
         stb   >$0645    
         std   >$FFA1    
         andcc  #^IntMasks
         endc            
                         
*
* Reset GIME to avoid missed IRQs
*
DoToggle                 
         lda   #^GI.Toggl mask off CART* bit
         anda  <D.IRQS   
         sta   <D.IRQS   
         lda   <D.IRQER   get current enable register status
         tfr   a,b       
         anda  #^GI.Toggl mask off CART* bit
         orb   #GI.Toggl  --- ensure that 60Hz IRQ's are always enabled
         sta   >IRQEnR    disable CART
         stb   >IRQEnR    enable CART
         clrb            
         rts             
                         
                         
*------------------------------------------------------------
*
* Handle F$VIRQ system call
*
FVIRQ    pshs  cc        
         orcc  #IntMasks  disable interrupts
         ldy   <D.CLTb    address of VIRQ table
         ldx   <D.Init    address of INIT
         ldb   PollCnt,x  number of polling table entries from INIT
         ldx   R$X,u      zero means delete entry
         beq   RemVIRQ   
         ifgt  Level-2   
         bra   FindVIRQ   ---x
                         
v.loop   leay  4,y        ---x
         endc            
FindVIRQ                 
         ldx   ,y++       is VIRQ entry null?
         beq   AddVIRQ    if yes, add entry here
         decb            
         bne   FindVIRQ  
         puls  cc        
         comb            
         ldb   #E$Poll   
         rts             
                         
AddVIRQ                  
         ifgt  Level-2   
         ldx   R$Y,u     
         stx   ,y        
         lda   >$0643    
         ldb   >$0645    
         std   2,y       
         else            
         leay  -2,y       point to first null VIRQ entry
         ldx   R$Y,u     
         stx   ,y        
         endc            
         ldy   R$D,u     
         sty   ,x        
         bra   virqexit  
                         
         ifgt  Level-2   
v.chk    leay  4,y       
RemVIRQ  ldx   ,y        
         else            
RemVIRQ  ldx   ,y++      
         endc            
         beq   virqexit  
         cmpx  R$Y,u     
         bne   RemVIRQ   
         bsr   DelVIRQ   
virqexit puls  cc        
         clrb            
         rts             
                         
DelVIRQ  pshs  x,y       
DelVLup                  
         ifgt  Level-2   
         ldq              ,y++		move entries up in table
         leay  2,y       
         stq              -8,y
         bne   DelVLup   
         puls  x,y,pc    
         else            
         ldx   ,y++       move entries up in table
         stx   -4,y      
         bne   DelVLup   
         puls  x,y       
         leay  -2,y      
         rts             
         endc            
                         
         ifgt  Level-1   
*------------------------------------------------------------
*
* Handle F$Alarm call
*
FAlarm   ldx   #WGlobal+G.AlPckt
         ldd   R$D,u     
         bne   DoAlarm   
         std   G.AlPID-G.AlPckt,x erase F$Alarm PID, Signal.
         rts             
                         
DoAlarm  tsta             if PID != 0, set alarm for this process
         bne   SetAlarm  
         cmpd  #1         1 -> Set system-wide alarm
         bne   GetAlarm  
SetAlarm                 
         std   G.AlPID-G.AlPckt,x
         ldy   <D.Proc   
         lda   P$Task,y   move from process task
         ldb   <D.SysTsk  to system task
         ldx   R$X,u      from address given in X
         ldu   #WGlobal+G.AlPckt
         ldy   #5         move 5 bytes
         bra   FMove     
                         
GetAlarm                 
         cmpd  #2        
         bne   AlarmErr  
         ldd   G.AlPID-G.AlPckt,x
         std   R$D,u     
         bra   RetTime   
AlarmErr                 
         comb            
         ldb   #E$IllArg 
         rts             
         endc            
                         
*------------------------------------------------------------
*
* Handle F$Time System call
*
FTime    equ   *         
         ifgt  Level-1   
         ldx   #D.Time    address of system time packet
RetTime  ldy   <D.Proc    get pointer to current proc descriptor
         ldb   P$Task,y   process Task number
         lda   <D.SysTsk  from System Task
         ldu   R$X,u     
STime.Mv                 
         ldy   #6         move 6 bytes
FMove    os9   F$Move    
         else            
         ldx   R$X,u      get pointer to caller's space
         ldd   <D.Year    get year and month
         std   ,x        
         ldd   <D.Day     get day and hour
         std   2,x       
         ldd   <D.Min     get minute and second
         std   4,x       
         clrb            
         endc            
         rts             
                         
         endc            
                         
         emod            
len      equ   *         
         end