mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
559 lines
18 KiB
Common Lisp
559 lines
18 KiB
Common Lisp
;;-*-Lisp-*-
|
|
(in-package goal)
|
|
|
|
;; name: gkernel-h.gc
|
|
;; name in dgo: gkernel-h
|
|
;; dgos: KERNEL
|
|
|
|
;; Type definitions for the GOAL Kernel.
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; CONSTANTS
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; -hardware-
|
|
|
|
;; the end of the 16 kB fast "scratchpad" memory of the PS2.
|
|
;; this memory is mapped to 0x70000000 in the PS2.
|
|
(defconstant *scratch-memory-top* (the pointer #x70004000))
|
|
|
|
;; -versions-
|
|
|
|
;; the version of the kernel. This is checked in the C Kernel.
|
|
;; This must match the version in common/versions.h when building gk
|
|
(defconstant *kernel-major-version* 2)
|
|
(defconstant *kernel-minor-version* 0)
|
|
|
|
;; the version of the OVERLORD I/O driver.
|
|
;; this may be unused.
|
|
(defconstant *irx-major-version* 1)
|
|
(defconstant *irx-minor-version* 2)
|
|
|
|
;; -memory-
|
|
|
|
;; the size of the execution stack (~14 kB) shared by all threads
|
|
(defconstant DPROCESS_STACK_SIZE #x3800)
|
|
|
|
;; another stack size used as a maximum for temporary threads
|
|
(defconstant PROCESS_STACK_SIZE #x1c00)
|
|
|
|
;; default size of stack to backup for a process
|
|
(defconstant PROCESS_STACK_SAVE_SIZE 256)
|
|
|
|
;; the size of the shared heap used by dynamically sized processes
|
|
(defconstant PROCESS_HEAP_SIZE (* 984 1024))
|
|
|
|
;; -system-
|
|
|
|
;; tab size for printing.
|
|
(defconstant *tab-size* (the binteger 8))
|
|
|
|
(defconstant *gtype-basic-offset* 4)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ENUMS
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; bitfield enum to indicate proprties about a process-tree
|
|
(defenum process-mask
|
|
:bitfield #t :type uint32
|
|
(execute 0) ;; 1
|
|
(draw 1) ;; 2
|
|
(pause 2) ;; 4
|
|
(menu 3) ;; 8
|
|
(progress 4) ;; 16
|
|
(actor-pause 5) ;; 32
|
|
(sleep 6) ;; 64
|
|
(sleep-code 7) ;; 128
|
|
(process-tree 8) ;; 256 not an actual process, just a "tree node" for organization
|
|
(heap-shrunk 9) ;; 512
|
|
(going 10) ;; 1024
|
|
(movie 11) ;; 2048
|
|
(movie-subject 12) ;; 4096
|
|
(target 13) ;; 8192
|
|
(sidekick 14) ;; 16384
|
|
(crate 15) ;; 32768
|
|
(collectable 16) ;; 65536
|
|
(enemy 17) ;; 131072
|
|
(camera 18) ;; 262144
|
|
(platform 19) ;; 524288
|
|
(ambient 20) ;; 1048576
|
|
(entity 21) ;; 2097152
|
|
(projectile 22) ;; 4194304
|
|
(attackable 23) ;; 8388608
|
|
(death 24) ;; 16777216
|
|
)
|
|
|
|
;; -961
|
|
(defconstant PROCESS_CLEAR_MASK
|
|
(lognot (process-mask sleep sleep-code process-tree heap-shrunk)))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; MACROS
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; trigger an exception. (GOAL used lw r0, 2(r0))
|
|
(defmacro break ()
|
|
`(/ 0 0)
|
|
)
|
|
|
|
(defmacro msg-err (&rest args)
|
|
;; "Print a message to stdout immediately. This won't appear in the compiler."
|
|
`(format 0 ,@args)
|
|
)
|
|
|
|
(defmacro msg-warn (&rest args)
|
|
`(format 0 ,@args)
|
|
)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; TYPES
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; this stores the current state of the kernel.
|
|
(deftype kernel-context (basic)
|
|
((prevent-from-run process-mask :offset-assert 4)
|
|
(require-for-run process-mask :offset-assert 8)
|
|
(allow-to-run process-mask :offset-assert 12)
|
|
(next-pid int32 :offset-assert 16)
|
|
(fast-stack-top pointer :offset-assert 20)
|
|
(current-process basic :offset-assert 24)
|
|
(relocating-process basic :offset-assert 28)
|
|
(relocating-min int32 :offset-assert 32)
|
|
(relocating-max int32 :offset-assert 36)
|
|
(relocating-offset int32 :offset-assert 40)
|
|
(low-memory-message basic :offset-assert 44)
|
|
)
|
|
|
|
:size-assert #x30
|
|
:method-count-assert 9
|
|
:flag-assert #x900000030
|
|
)
|
|
|
|
; A thread belongs to a process and has a reference to a stack.
|
|
; they have an "execution stack", which is where the stack goes when the thread runs.
|
|
; and also a "backup stack", which stores the stack when the thread doesn't run.
|
|
; this means threads can't leak pointers to stack variables to other threads...
|
|
; optionally, threads may know how to suspend/resume themselves.
|
|
|
|
(declare-type process basic)
|
|
(declare-type stack-frame basic)
|
|
(declare-type state basic)
|
|
(declare-type cpu-thread basic)
|
|
(declare-type dead-pool basic)
|
|
(declare-type event-message-block structure)
|
|
|
|
; DANGER - this type is created in kscheme.cpp. It has room for 12 methods and size 0x28 bytes.
|
|
(deftype thread (basic)
|
|
((name basic :offset-assert 4) ;; name of the thread (usually a symbol?)
|
|
(process process :offset-assert 8) ;; process that the thread belongs to
|
|
(previous thread :offset-assert 12) ;; previous thread that was running in the process
|
|
(suspend-hook (function cpu-thread none) :offset-assert 16) ;; function to suspend this thread
|
|
(resume-hook (function cpu-thread none) :offset-assert 20) ;; function to resume this thread
|
|
(pc pointer :offset-assert 24) ;; program counter of the thread
|
|
(sp pointer :offset-assert 28) ;; stack pointer of the thread (actual stack)
|
|
(stack-top pointer :offset-assert 32) ;; top of the thread's stack (actual stack)
|
|
(stack-size int32 :offset-assert 36) ;; size of the thread's stack (backup stack)
|
|
)
|
|
|
|
(:methods
|
|
(stack-size-set! (_type_ int) none 9)
|
|
(thread-suspend (_type_) none 10)
|
|
(thread-resume (_type_) none 11)
|
|
)
|
|
|
|
:size-assert #x28
|
|
:method-count-assert 12
|
|
:flag-assert #xc00000028
|
|
;; is already defined in kscheme but we define it again.
|
|
)
|
|
|
|
;; A CPU thread is a thread which has some memory to save registers and a stack
|
|
(deftype cpu-thread (thread)
|
|
(
|
|
;; This is what GOAL did:
|
|
;; (rreg uint64 8 :offset-assert 40) ;; general purpose saved registers
|
|
;; (freg float 6 :offset-assert 104) ;; floating point registers
|
|
|
|
;; OpenGOAL has only 5 saved registers but 8 fregs, so we swap a rreg for 2 fregs.
|
|
(rreg uint64 7 :offset-assert 40)
|
|
(freg float 8)
|
|
|
|
;; This is the same between GOAL and OpenGOAL
|
|
(stack uint8 :dynamic :offset-assert 128) ;; stack memory (dynamic array)
|
|
)
|
|
|
|
(:methods
|
|
(new (symbol type process symbol int pointer) _type_ 0)
|
|
(thread-suspend (_type_) none 10)
|
|
(thread-resume (_type_) none 11)
|
|
)
|
|
|
|
:size-assert #x80
|
|
:method-count-assert 12
|
|
:flag-assert #xc00000080
|
|
)
|
|
|
|
;; Parent type of all process tree nodes.
|
|
;; A process-tree is a left-child right-sibling binary tree
|
|
;; (except GOAL is old and it looks like they called them left-child right-brother trees back then)
|
|
(deftype process-tree (basic)
|
|
((name basic :offset-assert 4)
|
|
(mask process-mask :offset-assert 8)
|
|
(parent (pointer process-tree) :offset-assert 12)
|
|
(brother (pointer process-tree) :offset-assert 16)
|
|
(child (pointer process-tree) :offset-assert 20)
|
|
(ppointer (pointer process-tree) :offset-assert 24)
|
|
(self process-tree :offset-assert 28)
|
|
)
|
|
|
|
(:methods
|
|
(new (symbol type basic) _type_ 0)
|
|
(activate (_type_ process-tree basic pointer) process-tree 9)
|
|
(deactivate (_type_) none 10)
|
|
(dummy-method-11 () none 11)
|
|
(run-logic? (_type_) symbol 12)
|
|
(dummy-method () none 13)
|
|
)
|
|
|
|
:size-assert #x20
|
|
:method-count-assert 14
|
|
:no-runtime-type ;; already defined by kscheme. Don't do it again.
|
|
)
|
|
|
|
|
|
;; A GOAL process. A GOAL process contains memory and a suspendable main-thread.
|
|
(deftype process (process-tree)
|
|
((pool dead-pool :offset-assert #x20)
|
|
(status basic :offset-assert #x24)
|
|
(pid int32 :offset-assert #x28)
|
|
(main-thread cpu-thread :offset-assert #x2c)
|
|
(top-thread thread :offset-assert #x30)
|
|
(entity basic :offset-assert #x34)
|
|
(state state :offset-assert #x38)
|
|
(trans-hook function :offset-assert #x3c)
|
|
(post-hook function :offset-assert #x40)
|
|
(event-hook (function basic int basic event-message-block object) :offset-assert #x44)
|
|
(allocated-length int32 :offset-assert #x48)
|
|
(next-state state :offset-assert #x4c)
|
|
(heap-base pointer :offset-assert #x50)
|
|
(heap-top pointer :offset-assert #x54)
|
|
(heap-cur pointer :offset-assert #x58)
|
|
(stack-frame-top stack-frame :offset-assert #x5c)
|
|
(connection-list connectable :inline :offset-assert #x60)
|
|
(stack uint8 :dynamic :offset-assert #x70)
|
|
)
|
|
|
|
(:methods
|
|
(new (symbol type basic int) _type_ 0)
|
|
(activate (_type_ process-tree basic pointer) process-tree 9)
|
|
(deactivate (process) none 10)
|
|
(dummy-method-11 () none 11)
|
|
(run-logic? (process) symbol 12)
|
|
(dummy-method () none 13)
|
|
)
|
|
|
|
:size-assert #x70
|
|
:method-count-assert 14
|
|
:no-runtime-type ;; already defined by kscheme. Don't do it again.
|
|
)
|
|
|
|
;; A dead pool is simply a process-tree node which contains all dead processes.
|
|
;; It supports getting and returning processes.
|
|
(deftype dead-pool (process-tree)
|
|
(
|
|
;; nothing new!
|
|
)
|
|
(:methods
|
|
(new (symbol type int int basic) _type_ 0)
|
|
(get-process (_type_ type int) process 14)
|
|
(return-process ( _type_ process) none 15)
|
|
)
|
|
:size-assert #x20
|
|
:method-count-assert 16
|
|
:flag-assert #x1000000020
|
|
)
|
|
|
|
;; A dead-pool-heap-rec is a record for a process which lives on a dead-pool-heap.
|
|
;; these processes can move around in memory, but the records can't.
|
|
;; Therefore a pointer to these can be used as a handle for the process, so you can
|
|
;; find it after it moves
|
|
(deftype dead-pool-heap-rec (structure)
|
|
((process process :offset-assert 0) ;; the process of this record
|
|
(prev dead-pool-heap-rec :offset-assert 4) ;; next rec in the linked list
|
|
(next dead-pool-heap-rec :offset-assert 8) ;; prev. rec in the linked list
|
|
)
|
|
|
|
:pack-me ; don't worry about aligning me to 16-bytes in arrays and types.
|
|
:size-assert #xc
|
|
:method-count-assert 9
|
|
:flag-assert #x90000000c
|
|
)
|
|
|
|
;; This is a pool of dead processes which can be dynamically sized and allocated from a common heap.
|
|
;; Alive processess in a dead-pool-heap can be relocated and compacted to reduce heap fragmentation.
|
|
(deftype dead-pool-heap (dead-pool)
|
|
((allocated-length int32 :offset-assert #x20) ;; size of heap
|
|
(compact-time uint32 :offset-assert #x24) ;; ??
|
|
(compact-count-targ uint32 :offset-assert #x28) ;; ??
|
|
(compact-count uint32 :offset-assert #x2c) ;; ??
|
|
(fill-percent float :offset-assert #x30) ;; ??
|
|
(first-gap dead-pool-heap-rec :offset-assert #x34) ;; ??
|
|
(first-shrink dead-pool-heap-rec :offset-assert #x38) ;; ??
|
|
(heap kheap :inline :offset-assert 64) ;; ??
|
|
(alive-list dead-pool-heap-rec :inline :offset-assert 80) ;; ??
|
|
(last dead-pool-heap-rec :offset #x54 :offset-assert #x54) ;; overlay of (-> alive-list prev)
|
|
;; note - the placement of dead-list at 92 here is used to determine the packing behavior.
|
|
;; see TypeSystem::get_size_in_type().
|
|
(dead-list dead-pool-heap-rec :inline :offset-assert 92) ;; ??
|
|
(process-list dead-pool-heap-rec :inline :dynamic :offset-assert 104)
|
|
)
|
|
(:methods
|
|
(new (symbol type basic int int) _type_ 0)
|
|
(compact (dead-pool-heap int) none 16)
|
|
(shrink-heap (dead-pool-heap process) dead-pool-heap 17)
|
|
(churn (dead-pool-heap int) none 18)
|
|
(memory-used (dead-pool-heap) int 19)
|
|
(memory-total (dead-pool-heap) int 20)
|
|
(gap-size (dead-pool-heap dead-pool-heap-rec) int 21)
|
|
(gap-location (dead-pool-heap dead-pool-heap-rec) pointer 22)
|
|
(find-gap (dead-pool-heap dead-pool-heap-rec) dead-pool-heap-rec 23)
|
|
(find-gap-by-size (dead-pool-heap int) dead-pool-heap-rec 24)
|
|
(memory-free (dead-pool-heap) int 25)
|
|
(compact-time (dead-pool-heap) uint 26)
|
|
)
|
|
|
|
:size-assert #x68
|
|
:method-count-assert #x1b
|
|
:flag-assert #x1b00000068
|
|
)
|
|
|
|
|
|
;; GOAL can create a series of stack frames for unwinding/cleaning up.
|
|
;; This is the parent type for any stack frame.
|
|
(deftype stack-frame (basic)
|
|
((name basic :offset 4)
|
|
(next stack-frame :offset 8) ;; which way does this point?
|
|
)
|
|
|
|
:size-assert #xc
|
|
:method-count-assert 9
|
|
:flag-assert #x90000000c
|
|
:no-runtime-type ;; already constructed, don't do it again.
|
|
)
|
|
|
|
|
|
;; A catch frame is a frame you can "throw" to, by name.
|
|
;; You can "throw" out of a function and into another function.
|
|
(deftype catch-frame (stack-frame)
|
|
((sp int32 :offset 12) ;; where to reset the stack when throwing.
|
|
(ra int32 :offset 16) ;; where to jump when throwing
|
|
|
|
;; In GOAL
|
|
;; (freg float 6 :offset-assert 20) ;; saved floating point registers from "catch" statement
|
|
;; (rreg uint128 8 :offset-assert 48) ;; saved GPRs from "catch" statement (ugh they are 128s)
|
|
|
|
;; In OpenGOAL, we swap a rreg for 4 more fregs.
|
|
(freg float 10 :offset-assert 20) ;; only use 8
|
|
(rreg uint128 7) ;; only use 5
|
|
)
|
|
|
|
(:methods
|
|
(new (symbol type symbol function (pointer uint64)) object 0)
|
|
)
|
|
:size-assert #xb0
|
|
:method-count-assert 9
|
|
:flag-assert #x9000000b0
|
|
)
|
|
|
|
;; A protect frame is a frame which has a cleanup function called on exit.
|
|
(deftype protect-frame (stack-frame)
|
|
((exit (function object) :offset-assert 12)) ;; function to call to clean up
|
|
|
|
(:methods
|
|
(new (symbol type (function object)) protect-frame)
|
|
)
|
|
:size-assert 16
|
|
:method-count-assert 9
|
|
:flag-assert #x900000010
|
|
)
|
|
|
|
;; A handle is a reference to a _specific_ process.
|
|
;; There are two tricks here:
|
|
;; 1). A process can be relocated in memory, so we can't just store a process.
|
|
;; Instead, we use a (pointer process) that points to a non-moving record.
|
|
;; dead-pool-heap takes care of maintaining these.
|
|
;; 2). Process memory can be reused. We don't want to get confused by this.
|
|
;; So we also store a unique PID to the specific activation of a process.
|
|
;; This way we can check the handle's PID against the PID in the process.
|
|
(deftype handle (uint64)
|
|
((process (pointer process) :offset 0) ;; set to #f for null.
|
|
(pid int32 :offset 32)
|
|
(u64 uint64 :offset 0)
|
|
)
|
|
:flag-assert #x900000008
|
|
)
|
|
|
|
(defmethod inspect handle ((obj handle))
|
|
(format #t "[~8x] ~A~%" 'handle)
|
|
(format #t "~Tprocess: #x~x~%" (-> obj process))
|
|
(format #t "~Tpid: ~D~%" (-> obj pid))
|
|
obj
|
|
)
|
|
|
|
|
|
(defmacro get-process-from-handle (handle)
|
|
;; the actual implementation is more clever than this.
|
|
;; Checks PID.
|
|
`(if (-> ,handle process)
|
|
(let ((proc (-> (-> ,handle process))))
|
|
(if (= (-> ,handle pid) (-> proc pid))
|
|
proc
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
(defmacro process->ppointer (proc)
|
|
`(if ,proc (the (pointer process) (-> ,proc ppointer)))
|
|
)
|
|
|
|
(defmacro ppointer->handle (pproc)
|
|
`(new 'static 'handle :process ,pproc :pid (-> ,pproc 0 pid))
|
|
)
|
|
|
|
(defmacro process->handle (proc)
|
|
`(ppointer->handle (process->ppointer ,proc))
|
|
)
|
|
|
|
(defmethod print handle ((obj handle))
|
|
;; the get-process-from-handle macro can't deal with
|
|
;; a 0 in the process field, so we check it manually here.
|
|
(if (nonzero? (-> obj u64))
|
|
(format #t "#<handle :process ~A :pid ~D>"
|
|
(get-process-from-handle obj)
|
|
(-> obj pid)
|
|
)
|
|
(format #t "#<handle :process 0 :pid 0>")
|
|
)
|
|
obj
|
|
)
|
|
|
|
(deftype state (protect-frame)
|
|
((code function :offset-assert 16)
|
|
(trans (function object) :offset-assert 20)
|
|
(post function :offset-assert 24)
|
|
(enter (function object object object object object object object) :offset-assert 28)
|
|
(event (function basic int basic event-message-block object) :offset-assert 32)
|
|
)
|
|
(:methods
|
|
(new (symbol type basic function
|
|
(function object)
|
|
(function object object object object object object object)
|
|
(function object)
|
|
(function basic int basic event-message-block object)) _type_ 0)
|
|
)
|
|
:method-count-assert 9
|
|
:size-assert #x24
|
|
:flag-assert #x900000024
|
|
)
|
|
|
|
|
|
;; Not sure what this is, but it's probably used in the event system.
|
|
(deftype event-message-block (structure)
|
|
((to basic :offset-assert 0)
|
|
(from basic :offset-assert 4)
|
|
(num-params int32 :offset-assert 8)
|
|
(message basic :offset-assert 12)
|
|
(param uint64 7 :offset-assert 16)
|
|
)
|
|
|
|
:size-assert #x48
|
|
:method-count-assert 9
|
|
:flag-assert #x900000048
|
|
)
|
|
|
|
(defmacro as-process (ppointer)
|
|
;; convert a (pointer process) to a process.
|
|
;; this uses the self field, which seems to always just get set to the object.
|
|
;; perhaps when deleting a process you could have it set self to #f?
|
|
;; I don't see this happen anywhere though, so it's not clear.
|
|
`(if ,ppointer
|
|
(-> (-> ,ppointer) self)
|
|
)
|
|
)
|
|
|
|
(defmacro as-ppointer (proc)
|
|
;"safely get a (pointer process) from a process, returning #f if invalid."
|
|
`(if ,proc
|
|
(-> ,proc ppointer)
|
|
)
|
|
)
|
|
|
|
(defmacro process-stack-used (proc)
|
|
;; get how much stack the top thread of a process has used.
|
|
`(- (the int (-> ,proc top-thread stack-top))
|
|
(the int (-> ,proc top-thread sp))
|
|
)
|
|
)
|
|
|
|
(defmacro process-stack-size (proc)
|
|
;; get how much stack the top thread of a process has
|
|
`(-> ,proc top-thread stack-size)
|
|
)
|
|
|
|
(defmacro process-heap-used (proc)
|
|
;; get how much heap a process has used.
|
|
`(- (-> ,proc allocated-length)
|
|
(- (the int (-> ,proc heap-top))
|
|
(the int (-> ,proc heap-cur))
|
|
)
|
|
)
|
|
)
|
|
|
|
(defmacro process-heap-size (proc)
|
|
;; get how much heap a process has
|
|
`(the int (-> ,proc allocated-length))
|
|
)
|
|
|
|
(defmacro process-mask? (mask enum-value)
|
|
`(!= 0 (logand ,mask (process-mask ,enum-value)))
|
|
)
|
|
|
|
(defmacro process-mask-set! (mask &rest enum-value)
|
|
;; sets the given bits in the process mask (with or)
|
|
`(set! ,mask (logior ,mask (process-mask ,@enum-value)))
|
|
)
|
|
|
|
(defmacro process-mask-clear! (mask &rest enum-value)
|
|
;; sets the given bits in the process mask (with or)
|
|
`(set! ,mask (logand ,mask (lognot (process-mask ,@enum-value))))
|
|
)
|
|
|
|
(defmacro suspend ()
|
|
;; suspend the current process.
|
|
`(rlet ((pp :reg r13 :reset-here #t))
|
|
;; we pass the current thread to the kernel with the pp register.
|
|
;; so it should be backed up on the stack here.
|
|
(.push pp)
|
|
;; set to the current thread
|
|
(set! pp (-> (the process pp) top-thread))
|
|
;; call the suspend hook (put nothing as the argument)
|
|
((-> (the cpu-thread pp) suspend-hook) (the cpu-thread 0))
|
|
;; now we've been resumed, restore pp.
|
|
(.pop pp)
|
|
)
|
|
)
|
|
|
|
(defmacro process-deactivate ()
|
|
;; deactivate the current process
|
|
`(rlet ((pp :reg r13 :reset-here #t :type process))
|
|
(deactivate pp)
|
|
)
|
|
)
|
|
|
|
(defmacro with-pp (&rest body)
|
|
`(rlet ((pp :reg r13 :reset-here #t :type process))
|
|
,@body)
|
|
)
|