diff level2/modules/mc09clock.asm @ 3131:e1aadba01e81

Add new Level 2 port for Multicomp09 "mc09l2" Add infrastructure (directories, makefiles etc). Target name is mc09l2 to distinguish it from the Level 1 port (target name mc09). In the code, the name mc09 is used for platform-dependent code, whether for Level 1 or Level 2.
author Neal Crook <foofoobedoo@gmail.com>
date Mon, 05 Dec 2016 17:39:03 +0000
parents
children b79920779783
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/level2/modules/mc09clock.asm	Mon Dec 05 17:39:03 2016 +0000
@@ -0,0 +1,497 @@
+********************************************************************
+* Clock - Clock for OS-9 Level Two/NitrOS-9
+*
+* Clock module for CoCo 3 and TC9 OS9 Level 2 and NitrOS-9
+*
+* Includes support for several different RTC chips, GIME Toggle
+* IRQ fix, numerous minor changes.
+*
+* Based on Microware/Tandy Clock Module for CC3/L2
+*
+* $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.
+
+         nam   Clock
+         ttl   Clock for OS-9 Level Two/NitrOS-9
+
+TkPerTS  equ   2          ticks per time slice
+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
+
+         IFP1
+         use   defsfile
+         use   cocovtio.d
+         ENDC
+
+Edtn     equ   9
+Vrsn     equ   5
+
+*------------------------------------------------------------
+*
+* Start of module
+*
+         mod   len,name,Systm+Objct,ReEnt+Vrsn,Init,0
+
+name     fcs   "Clock"
+         fcb   Edtn
+
+*
+* Table to set up Service Calls:
+*
+NewSvc   fcb   F$Time
+         fdb   F.Time-*-2
+         fcb   F$VIRQ
+         fdb   F.VIRQ-*-2
+         fcb   F$Alarm
+         fdb   F.ALARM-*-2
+         fcb   F$STime
+         fdb   F.STime-*-2
+         fcb   $80        end of service call installation table
+
+*---------------------------------------------------------
+* 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 equ   *
+         IFGT  Level-2
+         ldd   2,y        Get Level 3 extended map type
+         orcc  #IntMasks
+         sta   >$0643
+         stb   >$0645
+         std   >DAT.Regs+1
+         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   >DAT.Regs+1
+         incb
+         stb   >$0645
+         stb   >DAT.Regs+1
+         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 equ   *
+         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   >DAT.Regs+1 and into RAM ---x
+         inca
+         sta   >$0645
+         sta   >DAT.Regs+2 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   >DAT.Regs+1 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
+Dopoll.i
+         jsr   [>D.Poll]  Call poll routine
+         bcc   DoPoll.i   Until error (error -> no interrupt found)
+
+         IFGT  Level-2
+         puls  d
+         orcc  #IntMasks
+         sta   >$0643
+         stb   >$0645
+         std   >DAT.Regs+1
+         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
+*
+F.VIRQ   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
+
+*------------------------------------------------------------
+*
+* Handle F$Alarm call
+*
+F.Alarm  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
+
+*------------------------------------------------------------
+*
+* Handle F$Time System call
+*
+F.Time   equ   *
+         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
+         rts
+
+*------------------------------------------------------------
+*
+* 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.
+*
+F.STime  equ  *
+         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
+         bsr   STime.Mv   Get time packet (ignore errors)
+         lda   #TkPerSec  Reset to start of second
+         sta   <D.Tick
+
+*
+* Call SetTime entry point in Clock2
+         ldx   <D.Clock2  get entry point to Clock2
+         jsr   $06,x      else call GetTime entry point
+
+NoSet    rts
+
+Clock2   fcs   "Clock2"
+
+*--------------------------------------------------
+*
+* Clock Initialization
+*
+* This vector is called by the kernel to service the first F$STime
+* call.  F$STime is usually called by CC3Go (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.
+*
+*
+Init     ldx   <D.Proc    save user proc
+         pshs  x
+         ldx   <D.SysPrc  make sys for link
+         stx   <D.Proc
+
+         leax  <Clock2,pcr
+         lda   #Sbrtn+Objct
+         os9   F$Link
+
+* And here, we restore the original D.Proc value
+         puls  x
+         stx   <D.Proc    restore user proc
+
+         bcc   LinkOk
+         lda   #E$MNF
+         jmp   <D.Crash
+LinkOk   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
+
+* 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
+
+         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
+         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
+         sta   $23,x      disable CART +RG Mar 14, 2012
+         lda   ,x         clear possible pending PIA0 HBORD IRQ
+         lda   2,x        clear possible pending PIA0 VBORD IRQ
+
+* Don't need to explicitly read RTC during initialization
+         ldd   #59*256+TkPerTS last second and time slice in minute
+         std   <D.Sec     Will prompt RTC read at next 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
+         leax  SvcVIRQ,pcr set VIRQ handler
+         stx   <D.VIRQ
+         leay  NewSvc,pcr insert syscalls
+         os9   F$SSvc
+* H6309 optimization opportunity here using oim
+         lda   <D.IRQER   get shadow GIME IRQ enable register
+         ora   #$08       set VBORD bit
+         sta   <D.IRQER   save shadow register
+         sta   >IRQEnR    enable GIME VBORD IRQs
+
+* 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
+
+         emod
+len      equ   *
+         end