;;-*-Lisp-*-
(in-package goal)

;; name: rpc-h.gc
;; name in dgo: rpc-h
;; dgos: GAME

(defun-extern sound-buffer-dump int)

;; DECOMP BEGINS

(deftype rpc-buffer (basic)
  "Buffer for storing input/output data for a remote procedure call to the overlord driver on the IOP."
  ((elt-size   uint32)
   (elt-count  uint32)
   (elt-used   uint32)
   (busy       symbol)
   (base       pointer)
   (data       uint8  :dynamic :offset  32)
   )
  (:methods
    (new (symbol type uint uint) _type_)
    )
  )


(defmethod new rpc-buffer ((allocation symbol) (type-to-make type) (elt-size uint) (elt-count uint))
  (let* ((a2-2 (+ (-> type-to-make size) 63 (* (the-as int elt-size) (the-as int elt-count))))
         (v0-0 (object-new allocation type-to-make (the-as int a2-2)))
         )
    (set! (-> v0-0 elt-size) elt-size)
    (set! (-> v0-0 elt-count) elt-count)
    (set! (-> v0-0 elt-used) (the-as uint 0))
    (set! (-> v0-0 busy) #f)
    (set! (-> v0-0 base) (logand -64 (&-> (-> v0-0 data) 63)))
    v0-0
    )
  )

(deftype rpc-buffer-pair (basic)
  "A double buffer of RPC buffers. This is used to let the game queue up data in one buffer while
the other is being read/written by overlord."
  ((buffer            rpc-buffer  2)
   (current           rpc-buffer)
   (last-recv-buffer  pointer)
   (rpc-port          int32)
   )
  (:methods
    (new (symbol type uint uint int) rpc-buffer-pair)
    (call (rpc-buffer-pair uint pointer uint) int)
    (add-element (rpc-buffer-pair) pointer)
    (decrement-elt-used (rpc-buffer-pair) int)
    (sync (rpc-buffer-pair symbol) int)
    (check-busy (rpc-buffer-pair) symbol)
    (pop-last-received (rpc-buffer-pair) pointer)
    )
  )


(defmethod new rpc-buffer-pair ((allocation symbol) (type-to-make type) (elt-size uint) (elt-count uint) (rpc-port int))
  (let ((s3-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
    (set! (-> s3-0 buffer 0) (new 'global 'rpc-buffer elt-size elt-count))
    (set! (-> s3-0 buffer 1) (new 'global 'rpc-buffer elt-size elt-count))
    (set! (-> s3-0 current) (-> s3-0 buffer 0))
    (set! (-> s3-0 last-recv-buffer) (the-as pointer #f))
    (set! (-> s3-0 rpc-port) rpc-port)
    s3-0
    )
  )

(defmethod sync ((this rpc-buffer-pair) (print-stall-warning symbol))
  "Wait for an in-progress rpc to finish."
  (let ((s5-0 (if (= (-> this current) (-> this buffer 0))
                  (-> this buffer 1)
                  (-> this buffer 0)
                  )
              )
        )
    (when (-> s5-0 busy)
      (when (nonzero? (rpc-busy? (-> this rpc-port)))
        (if print-stall-warning
            (format 0 "STALL: waiting for IOP on RPC port #~D~%" (-> this rpc-port))
            )
        (while (nonzero? (rpc-busy? (-> this rpc-port)))
          (nop!)
          (nop!)
          (nop!)
          (nop!)
          (nop!)
          (nop!)
          (nop!)
          (nop!)
          )
        )
      (set! (-> s5-0 busy) #f)
      (set! (-> s5-0 elt-used) (the-as uint 0))
      0
      )
    )
  0
  )

(defmethod check-busy ((this rpc-buffer-pair))
  "Check to see if an rpc is in progress."
  (let ((gp-0 (if (= (-> this current) (-> this buffer 0))
                  (-> this buffer 1)
                  (-> this buffer 0)
                  )
              )
        )
    (when (-> gp-0 busy)
      (if (nonzero? (rpc-busy? (-> this rpc-port)))
          (return #t)
          )
      (set! (-> gp-0 busy) #f)
      (set! (-> gp-0 elt-used) (the-as uint 0))
      0
      )
    )
  #f
  )

(defmethod call ((this rpc-buffer-pair) (fno uint) (recv-buffer pointer) (recv-buffer-size uint))
  "Start an async RPC call. If there is already one in progress, stall and wait for it to finish."
  (when (nonzero? (-> this current elt-used))
    (let ((s2-0 (if (= (-> this current) (-> this buffer 0))
                    (-> this buffer 1)
                    (-> this buffer 0)
                    )
                )
          )
      (when (-> s2-0 busy)
        (when (nonzero? (rpc-busy? (-> this rpc-port)))
          (format 0 "STALL: waiting for IOP on RPC port #~D~%" (-> this rpc-port))
          (while (nonzero? (rpc-busy? (-> this rpc-port)))
            (nop!)
            (nop!)
            (nop!)
            (nop!)
            (nop!)
            (nop!)
            (nop!)
            (nop!)
            )
          )
        (set! (-> s2-0 busy) #f)
        (set! (-> s2-0 elt-used) (the-as uint 0))
        0
        )
      (let ((s1-0 (-> this current)))
        (rpc-call
          (-> this rpc-port)
          fno
          (the-as uint 1)
          (the-as uint (-> s1-0 base))
          (the-as int (* (-> s1-0 elt-size) (-> s1-0 elt-used)))
          (the-as uint recv-buffer)
          (the-as int recv-buffer-size)
          )
        (set! (-> s1-0 busy) #t)
        )
      (set! (-> this last-recv-buffer) recv-buffer)
      (set! (-> this current) s2-0)
      )
    )
  0
  )

(defmethod pop-last-received ((this rpc-buffer-pair))
  "Pop the response from the most recently completed rpc call."
  (let ((v0-0 (-> this last-recv-buffer)))
    (set! (-> this last-recv-buffer) (the-as pointer #f))
    v0-0
    )
  )

(defmethod add-element ((this rpc-buffer-pair))
  "Add an element. If the buffer is full, flush it!"
  (let ((v1-0 (-> this current)))
    (when (= (-> v1-0 elt-used) (-> v1-0 elt-count))
      (when (zero? (-> this rpc-port))
          ;; if we're RPC 0, this is evidently a situation to warn about.
          (format 0 "WARNING: too many sound commands queued~%")
          (sound-buffer-dump) ;; og:preserve-this added for debugging
          )
      (format 0 "WARNING: flushing RPC port #~D~%" (-> this rpc-port)) ;; og:preserve-this
      (call this (the-as uint 0) (the-as pointer 0) (the-as uint 0))
      (set! v1-0 (-> this current))
      )
    (let ((v0-2 (&+ (-> v1-0 base) (* (-> v1-0 elt-used) (-> v1-0 elt-size)))))
      (+! (-> v1-0 elt-used) 1)
      v0-2
      )
    )
  )

(defmethod decrement-elt-used ((this rpc-buffer-pair))
  "Remove the most recently queued element."
  (if (> (-> this current elt-used) 0)
      (+! (-> this current elt-used) -1)
      )
  0
  )
