;;; event.ss
;;; Copyright 1984-2017 Cisco Systems, Inc.
;;; 
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;; 
;;; http://www.apache.org/licenses/LICENSE-2.0
;;; 
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

(eval-when (compile)
   (generate-interrupt-trap #f))

(let ()
(define stop-event-timer
   (lambda ()
      ($set-timer (most-positive-fixnum))))

(define start-event-timer
  (lambda ()
   ; set timer by way of $event, so recurrent calls to "set-timer" or
   ; "{dis,en}able-interrupts" can't prevent interrupts
    ($event)))

(set! set-timer
  (lambda (ticks)
    (unless (and (fixnum? ticks) (fx>= ticks 0))
       ($oops 'set-timer "~s is not a nonnegative fixnum" ticks))
    (let ([ticks-left (stop-event-timer)])
      (let ([t ($tc-field 'timer-ticks ($tc))])
        (if (fx> ticks 0)
            (begin
              ($tc-field 'something-pending ($tc) #t)
              ($tc-field 'timer-ticks ($tc) ticks))
            ($tc-field 'timer-ticks ($tc) #f))
        (if (fx= ($tc-field 'disable-count ($tc)) 0)
            (let ([old (if t (fx+ t ticks-left) 0)])
              (start-event-timer)
              old)
            (or t 0))))))

(set! disable-interrupts
  (lambda ()
    (let ([ticks (stop-event-timer)])
      (let ([disable-count ($tc-field 'disable-count ($tc))])
        (when (and (fx= disable-count 0) ($tc-field 'timer-ticks ($tc)))
          ($tc-field 'timer-ticks ($tc) (fx+ ($tc-field 'timer-ticks ($tc)) ticks)))
        (when (fx= disable-count (most-positive-fixnum))
          ($oops 'disable-interrupts
                 "too many consecutive calls to disable-interrupts"))
        (let ([disable-count (fx+ disable-count 1)])
          ($tc-field 'disable-count ($tc) disable-count)
          disable-count)))))

(let ()
  (define (enable can-event?)
    (let ([ticks (stop-event-timer)])
      (let ([disable-count (fx- ($tc-field 'disable-count ($tc)) 1)])
        (case disable-count
          [(-1) ($set-timer ticks) 0]
          [(0) ($tc-field 'disable-count ($tc) 0)
               (if can-event?
                   (start-event-timer)
                   ($set-timer 1)) ; trigger event on next trap check
               0]
          [else ($tc-field 'disable-count ($tc) disable-count)
                disable-count]))))

  (set! enable-interrupts (lambda () (enable #t)))
  (set! $enable-interrupts/no-event (lambda () (enable #f))))
)
