gno/kern/gno/kern.asm

2310 lines
44 KiB
NASM

* $Id: kern.asm,v 1.2 1998/02/22 05:05:45 taubert Exp $
* GNO Multitasking environment
* Low-level Kernel code
* Context switching, tool patch vector, CDA patches,
* process ID allocation, etc.
* Copyright 1991-1998, Procyon Software
* added select() timeout support - DT - 2/13/96
mcopy m/kern.mac
copy global.equates
case on
TIMESLICE gequ 3
NUM_DEFER gequ 16
;IncBusy gequ $E10064
;DecBusy gequ $E10068
BusyFlag gequ $E100FF
****************************************************************************
* Equates - Various equates required by these routines.
*
versionNumber gequ $0100 ; The version number of this library.
dispatch1 gequ $e10000 ; The first toolbox dispatch vector.
dispatch2 gequ $e10004 ; The second toolbox dispatch vector.
ToolPointerTable gequ $e103c0 ; Pointer to the active System TPT.
UserToolPointerTable gequ $e103c8 ; Pointer to the active User TPT.
; Error return values from the routines InstallE10000 and RemoveE10000
noError gequ $0000 ; Value returned if no error occurs.
badHeaderError gequ $8001 ; Patch header wasn't valid.
headerNotFoundError gequ $8002 ; Header to remove wasn't in the
; linked list.
****************************************************************************
* CheckPatch - Checks the passed toolbox dispatch vector to see if it
* points to a valid patch.
*
* Input: Passed via the stack following C conventions.
* newPatchAddr (long) - Address of the patch routine.
*
* Output:
* If newPatchAddr is a valid patch -
* Carry clear
* If newPatchAddr is not a valid patch -
* Carry set
*
CheckPatch START
zprtl equ $01 ; The address for the rtl on our direct
; page.
newPatchAddr equ zprtl+3 ; Address of patch (parameter to this
; routine).
tsc ; Make the stack the direct page after
; saving
phd ; the current direct page.
tcd
lda newPatchAddr+2 ; Simple check to check for a valid
; pointer.
and #$ff00
bne BadPatch ; Wasn't zero, can't be a valid pointer.
lda [newPatchAddr] ; Check for the first JML instruction.
and #$00ff
cmp #$005c
bne BadPatch
ldy #$04 ; Check for the second JML instruction.
lda [newPatchAddr],y
and #$00ff
cmp #$005c
bne BadPatch
ldy #$08 ; Check for the third JML instruction.
lda [newPatchAddr],y
and #$00ff
cmp #$005c
bne BadPatch
ldy #$0c ; Check for the fourth JML instruction.
lda [newPatchAddr],y
and #$00ff
cmp #$005c
bne BadPatch
ldy #$10 ; Check for the rtl and phk instructions.
lda [newPatchAddr],y
cmp #$4b6b
bne BadPatch
iny ; Check for the phk and pea instructions.
lda [newPatchAddr],y
cmp #$f44b
bne BadPatch
clc ; Calculate the address of the rtl
; instruction.
lda newPatchAddr
adc #$000f
ldy #$13 ; Check for address of the rtl
; instruction.
cmp [newPatchAddr],y
bne BadPatch
GoodPatch anop
pld ; Restore the direct page and report
clc ; that it was a good patch.
rtl
BadPatch anop
pld ; Restore the direct page and report
sec ; that something was wrong.
rtl
END
****************************************************************************
* InstallE10000 - Sets the jump vector at $e10000 and $e10004 to point to
* the passed new toolbox dispatch vector patch. This routine
* also updates the linked lists so that more than one routine
* can be patched into the dispatch vectors.
*
* Input: Passed via the stack following C conventions.
* newPatchAddr (long) - Address of the patch routine.
*
* Output:
* If an error occurred -
* Carry set, Accumulator contains one of the following error codes:
* badHeaderError
* If no error occurred and patch was installed successfully -
* Carry clear, Accumulator contains zero.
*
InstallE10000 START
oldPatchAddr equ $01 ; Address of existing patch.
zprtl equ oldPatchAddr+4 ; The address for the rtl.
zpsize equ zprtl-oldPatchAddr ; Size of direct page we'll have on
; the stack.
newPatchAddr equ zprtl+3 ; Address of patch (parameter to
; this routine).
tsc ; Move the stack pointer to point beyond
sec ; the direct page variables that we'll
sbc #zpsize ; place on the stack.
tcs
phd ; Save the direct page register.
tcd ; Set the direct page.
php ; Disable interrupts
sei
pei newPatchAddr+2 ; Check if patch header is valid.
pei newPatchAddr
jsl CheckPatch
plx ; Remove the parameters from the stack.
plx
bcc x1 ; Report the badHeaderError if detected.
ldy #badHeaderError
jmp Exit
x1 lda >dispatch1 ; Set up the next1Vector in the new patch.
sta [newPatchAddr] ; The JML instruction and low byte.
lda >dispatch1+2
ldy #$02
sta [newPatchAddr],y ; The middle and upper bytes.
lda >dispatch2 ; Set up the next2Vector in the new patch.
ldy #$04
sta [newPatchAddr],y ; The JML instruction and low byte.
lda >dispatch2+2
ldy #$06
sta [newPatchAddr],y ; The middle and upper bytes.
lda >dispatch1+3 ; See if there's already a patch in
; dispatch1.
and #$00ff
sta oldPatchAddr+2
pha ; High byte of possible header address.
lda >dispatch1+1
sec
sbc #$0011
sta oldPatchAddr
pha ; Low byte of possible header address.
jsl CheckPatch
plx
plx
bcs First ; JIF this will be the first patch
; installed.
ldy #$08 ; Set up the dispatch1Vector in the new
; patch.
lda [oldPatchAddr],y
sta [newPatchAddr],y ; The JML instruction and low byte.
ldy #$0a
lda [oldPatchAddr],y
sta [newPatchAddr],y ; The middle and upper bytes.
ldy #$0c ; Set up the dispatch2Vector in the new
; patch.
lda [oldPatchAddr],y
sta [newPatchAddr],y ; The JML instruction and low byte.
ldy #$0e
lda [oldPatchAddr],y
sta [newPatchAddr],y ; The middle and upper bytes.
bra PatchIt ; Now patch dispatch1 and dispatch2.
First ldy #$08 ; Set up the dispatch1Vector in the new
; patch.
lda >dispatch1
sta [newPatchAddr],y ; The JML instruction and low byte.
ldy #$0a
lda >dispatch1+2
sta [newPatchAddr],y ; The middle and upper bytes.
ldy #$0c ; Set up the dispatch2Vector in the new
; patch.
lda >dispatch2
sta [newPatchAddr],y ; The JML instruction and low byte.
ldy #$0e
lda >dispatch2+2
sta [newPatchAddr],y ; The middle and upper bytes.
PatchIt anop
clc ; Calculate the address of the new
; dispatch2.
lda newPatchAddr
adc #$0015
sta newPatchAddr
xba
and #$ff00 ; Mask in the JML instruction.
ora #$005c
sta >dispatch2 ; The JML instruction and low byte.
lda newPatchAddr+1
sta >dispatch2+2 ; The middle and upper bytes.
sec ; Calculate the address of the new
; dispatch1.
lda newPatchAddr
sbc #$0004
sta newPatchAddr
xba
and #$ff00 ; Mask in the JML instruction.
ora #$005c
sta >dispatch1 ; The JML instruction and low byte.
lda newPatchAddr+1
sta >dispatch1+2 ; The middle and upper bytes.
ldy #noError ; Report that all went well.
Exit plp ; Restore the interrupt state.
pld ; Restore the previous direct page
; register.
tsc ; Restore the stack pointer.
clc
adc #zpsize
tcs
tya ; Value to return.
beq noerr
sec ; Report that there was an error.
rtl
noerr clc ; Report that there was no error.
rtl
END
****************************************************************************
* RemoveE10000 - Removes the specified patch from the dispatch1 and dispatch2
* vectors and updates the linked lists for the remaining
* toolbox patches.
*
* Input: Passed via the stack following C conventions.
* patchToRemove (long) - Address of the patch to remove.
*
* Output:
* If an error occurred -
* Carry set, Accumulator contains one of the following error codes:
* badHeaderError
* headerNotFoundError
* If no error occurred and patch was removed successfully -
* Carry clear, Accumulator contains zero.
*
RemoveE10000 START
patchDispAddr equ $01 ; Address of existing patch (and 1
; extra byte).
prevHeader equ patchDispAddr+5 ; Used to search through the
; linked list.
zprtl equ prevHeader+4 ; The address for the rtl.
zpsize equ zprtl-patchDispAddr ; Size of direct page we'll have
; on the stack.
patchToRemove equ zprtl+3 ; Address of patch (parameter to
; this routine).
tsc ; Move the stack pointer to point beyond
sec ; the direct page variables that we'll
sbc #zpsize ; place on the stack.
tcs
phd ; Save the direct page register.
tcd ; Set the direct page.
php ; Disable interrupts
sei
pei patchToRemove+2 ; Check if patch header we were asked to
pei patchToRemove ; remove is a valid header.
jsl CheckPatch
plx ; Remove the parameters from the stack.
plx
bcc x1 ; Report the badHeaderError if detected.
ldy #badHeaderError
jmp Exit
x1 clc ; Create the JML instruction that would
; exist
lda patchToRemove ; if the patchToRemove was installed.
adc #$0011
sta patchDispAddr+1
lda patchToRemove+2
sta patchDispAddr+3
lda patchDispAddr ; Mask in the JML instruction.
and #$ff00
ora #$005c
sta patchDispAddr
cmp >dispatch1 ; Check if the patch to remove is the
; first
bne NotFirstOne ; patch installed.
lda >dispatch1+2
cmp patchDispAddr+2
bne NotFirstOne
lda [patchToRemove] ; Restore the Dispatch1 vector.
sta >dispatch1
ldy #$02
lda [patchToRemove],y
sta >dispatch1+2
ldy #$04 ; Restore the Dispatch2 vector.
lda [patchToRemove],y
sta >dispatch2
ldy #$06
lda [patchToRemove],y
sta >dispatch2+2
bra NoErr ; Everything went well.
NotFirstOne anop
sec ; Assume that whatever is in dispatch1 is
lda >dispatch1+1 ; patch and get the address of its header.
sbc #$0011
sta prevHeader ; Low and middle bytes.
lda >dispatch1+3
and #$00ff
sta prevHeader+2 ; Upper byte of header address.
loop pei prevHeader+2 ; Check if it really is a valid header.
pei prevHeader
jsl CheckPatch
plx ; Remove the parameters from the stack.
plx
bcc x2 ; Report that the patch that we asked to
ldy #headerNotFoundError ; remove wasn't found.
bra Exit
x2 lda [prevHeader] ; See if this patch points to patch we
cmp patchDispAddr ; want to remove.
bne nope
ldy #$02
lda [prevHeader],y
cmp patchDispAddr+2
bne nope
lda [patchToRemove] ; Restore the next1Vector.
sta [prevHeader]
ldy #$02
lda [patchToRemove],y
sta [prevHeader],y
ldy #$04 ; Restore the next2Vector.
lda [patchToRemove],y
sta [prevHeader],y
ldy #$06
lda [patchToRemove],y
sta [prevHeader],y
bra NoErr ; Everything went well.
nope ldy #$02 ; Get the address of the next patch
; header.
lda [prevHeader],y
tax
lda [prevHeader]
sta prevHeader
stx prevHeader+2
sec
lda prevHeader+1
sbc #$11
sta prevHeader
lda prevHeader+3
and #$00ff
sta prevHeader+2
bra loop ; Now check this header.
NoErr ldy #noError ; Report that all went well.
Exit plp ; Restore the interrupt state.
pld ; Restore the previous direct page
; register.
tsc ; Restore the stack pointer.
clc
adc #zpsize
tcs
tya ; Value to return.
beq xnoerr
sec ; Report that there was an error.
rtl
xnoerr clc ; Report that there was no error.
rtl
END
toolV2Dat DATA KERN2
t2Stack ds 128*32
END
****************************************************************************
* PatchHeader - Header required of all routines that will be patched
* into the toolbox dispatch vectors.
*
* Note: The code between next1Vector and NewDispatch2 must be included
* for all calls. The code below NewDispatch2 only needs to be
* included for patches that want to post patch the calls.
*
PatchHeader START
using toolV2Dat
using KernelStruct
next1Vector ENTRY ; Where dispatch1 should go when
; finished.
jml next1Vector ; (Filled in by InstallE10000).
next2Vector ENTRY ; Where dispatch2 should go when
; finished.
jml next2Vector ; (Filled in by InstallE10000).
dispatch1Vector ENTRY ; Holds the JML instruction from
; $e10000.
jml dispatch1Vector ; (Filled in by InstallE10000).
dispatch2Vector ENTRY ; Holds the JML instruction from
; $e10004.
jml dispatch2Vector ; (Filled in by InstallE10000).
anRtl rtl ; An RTL instruction. Its address
; will be
* ; pushed on the stack for dispatch1
; calls.
NewDispatch1 ENTRY ; Entry point for dispatch1 toolbox
; vector.
phk ; Push program bank.
pea anRtl-1 ; Push the address of a RTL.
NewDispatch2 ENTRY ; Entry point for dispatch2 toolbox
; vector.
; The following code should be included in the PatchHeader if the patch wants
; to perform post patching. This code will determine if the call that was
; made actually exists and if it does, post patching can occur. If the call
; doesn't exist, any pre-call routines can be executed, but the post patching
; shouldn't be attempted because the dispatcher will remove the second return
; address from the stack, thus not returning to your post patching routines.
; Stack equates for this routine.
aLong equ $0001 ; A temporary long value.
oldDP equ aLong+4 ; Where the direct page is saved to.
oldTM equ oldDP+2 ; Where the tool call number is saved.
phx ; Save the call that's being made.
phd ; Save the current direct page.
lda >ToolPointerTable+2 ; Get the TPT to determine the number
pha ; of tool sets loaded.
lda >ToolPointerTable
pha
tsc ; Set the direct page to the stack.
tcd
txa ; See if this tool set exits.
and #$00ff
cmp [aLong] ; Is it larger than the number of tool
; sets?
bcs noCall ; JIF this tool set doesn't exist.
asl a
asl a
tay ; Now get the pointer to the FPT.
lda [aLong],y
tax
iny
iny
lda [aLong],y
sta aLong+2
stx aLong
lda oldTM ; Get the function number.
and #$ff00
xba
cmp [aLong] ; Compare it to the number of entries in
; table.
noCall anop
pla ; Remove aLong from the stack.
pla
pld ; Restore the original direct page.
plx ; Recover the tool number.
bcs noCall2 ; don't pre patch, because we
* ; cannot post-patch
; At this point the carry flag is set if the tool call doesn't exist and clear
; if the tool call exits. No post patching must occur if the carry flag is
; set.
php
sei
phb
phk
plb ; 5,s
pha ; 3,s
phx ; 1,s
jsl incBusy
ldx curProcInd
lda t2StackPtr,x
asl a
asl a
clc
adc curProcInd ; add current proc offset
tax
lda 7,s
sta >t2Stack,x
short m
lda 9,s
sta >t2Stack+2,x
long m
ldx curProcInd
inc t2StackPtr,x
* Now, put our return address on the stack instead
short m
lda #^t2Return-1
sta 9,s
long m
lda #t2Return-1
sta 7,s
plx
pla
plb
plp
noCall2 anop
jmp next2Vector ; Go to the original $e10004 jump
; instruction.
t2Return anop
phb
pha ; space for return address
php ; 6,s
sei
phb ; 5,s
phk
plb
pha ; 3,s
phx ; 1,s
ldx curProcInd
lda t2StackPtr,x
dec a
sta t2StackPtr,x
asl a
asl a
clc
adc curProcInd
tax
short m
lda >t2Stack+2,x
sta 9,s
long m
lda >t2Stack,x
sta 7,s
plx
pla
plb
plp
jmp >decBusy
; jsl decBusy
; rtl
END
SaveAllPatch START
using toolV2Dat
using KernelStruct
using IgnoreInfo
php
sei
long ai
phb
phk
plb
pha
phx
phy
; jsl incBusy
lda curProcInd
sta tcurProcInd
lda truepid
sta ttruepid
lda #0
sta curProcInd
sta truepid
lda gsosDebug
sta tgsosDebug
stz gsosDebug
* we need to move some tool stack info to make it look like the kernel
* process called "SaveAll".
ldx tcurProcInd
lda t2StackPtr,x
dec a
sta t2StackPtr,x
asl a
asl a
clc
adc tcurProcInd
tax
lda >t2Stack,x
sta tstack
lda >t2Stack+2,x
sta tstack+2
lda t2StackPtr
asl a
asl a
tax
lda tstack
sta >t2Stack,x
lda tstack+2
sta >t2Stack+2,x
lda t2StackPtr
inc a
sta t2StackPtr
pushword #$0000 ; system tool set
pushword #$0C ; texttools
pushlong >oldTextTT ; thar she blows
_SetTSPtr
ply
plx
pla
plb
plp
OLDSAVEALL ENTRY
jmp >$000000
RestAllPatch ENTRY
php
sei
long ai
phb
phk
plb
pha
phx
phy
phd
lda tcurProcInd
sta curProcInd
lda ttruepid
sta truepid
lda tgsosDebug
sta gsosDebug
* we need to move some tool stack info to make it look like the interrupted
* process called "RestoreAll".
lda t2StackPtr
dec a
sta t2StackPtr
asl a
asl a
tax
lda >t2Stack,x
sta tstack
lda >t2Stack+2,x
sta tstack+2
ldx tcurProcInd
lda t2StackPtr,x
asl a
asl a
clc
adc tcurProcInd
tax
lda tstack
sta >t2Stack,x
lda tstack+2
sta >t2Stack+2,x
ldx tcurProcInd
lda t2StackPtr,x
inc a
sta t2StackPtr,x
; set the flag to ignore the next occurrences of those
; particular tools that are grieving us right now.
; for ROM 01 this works, as they only try to restore IN OUT, not ERR.
lda #1
sta ignSetInDev
sta ignSetInGlo
sta ignSetOutDev
sta ignSetOutGlo
pushword #$0000 ; system tool set
pushword #$0C ; texttools
pushlong #TTtable ; thar she blows
_SetTSPtr
; jsl decBusy
pld
ply
plx
pla
plb
plp
; ldx #0
; ldy #0
; jmp tool_exit
OLDRESTALL ENTRY
jmp >$000000
savedCDAInfo ENTRY
ttruepid dc i2'0'
tcurProcInd dc i2'0'
tgsosDebug dc i2'0'
tstack dc i4'0'
END
cdaPatch START
using KernelStruct
php
lda >reschedFlag
inc a
sta >reschedFlag
lda 4,s
sta >rtlSave+2
lda 1,s
sta 4,s
long m
lda 2,s
sta >rtlSave
pla
short m
pla
plp
; jsl oldCDAVect
php
php
php
php
short m
lda >rtlSave+2
sta 4,s
long m
lda >rtlSave
sta 2,s
lda >reschedFlag
dec a
sta >reschedFlag
plp
rtl
rtlSave dc i4'0'
END
; BrkVect
; kills the offending process
; gets info on the BRK by doing a GetInterruptState
BrkVect START
using KernelStruct
sei
clc ; (might not be necessary, but fuck it)
xce
phk
plb
long ai
jsl incBusy
lda >curProcInd
tax
lda >lastTool,x
sta >brkState+20
pushlong #brkState
pushword #$14
_GetInterruptState
lda 4,s ; copy PC into record
sta brkState+13
lda 2,s
sta brkState+14
tsc ; copy S into record
sta brkState+6
pushlong #brkState
tsc
pha
jsl PRINTBRK
bra brkproc
BrkVect1 ENTRY
Using KernelStruct
sei
clc ; (might not be necessary, but fuck it)
xce
phk
plb
long ai
jsl incBusy
lda >curProcInd
tax
lda >lastTool,x
sta >brkState+20
pushlong #brkState
pushword #$14
_GetInterruptState
lda 2,s
sta brkState+$0E
short a
lda 4,s
sta brkState+$0D
long a
pushlong #brkState
tsc
pha
jsl PRINTBRK
; jsr FlexBeep
; kill() now resets mutex to 0 if prog crashes in mutex
brkproc anop
tsc
sta >$604
pha
pha
jsl KERNgetpid ; this is #pragma toolparms 1
pea 9
ph4 #killerr
jsl KERNkill ; this is #pragma toolparms 1
pla
oops bra oops
killerr dc i2'0'
brkState ds 32
END
; since the kernel null process doesn't _resched, we won't get in
; an 'exploding time slice' situation. Make sure null process never calls
; _resched
CopVect START
using IntState
using KernelStruct
clc
xce
long mx
php
phb
sta >int_A ; save accumulator
lda #0 ; 0 indicates COP
sta >intType
* start saving off the registers
short m
pla
sta >int_B ; save data bank
pla
sta >int_P ; save status of 'e' bit
lda >$E0C02D
sta >int_SLTROM
lda >$E0C068
sta >int_STATEREG
long a
* the value of s that's saved shouldn't differ from the value upon
* entry
tsc
sta >int_S ; save stack pointer
tdc
sta >int_D ; save direct page
phk
plb
stx int_X
sty int_Y ; save X & Y
lda >$E0C035
and #%1111111111110111 ; don't ever change SHR shadowing
sta int_state
jsl contextSwitch
; restore stuff
newViaCOP ENTRY
long mx ; just for kicks
lda >$E0C035 ; don't ever change SHR shadowing
and #%0000000000001000 ; get old SHR shadow bit
ora >int_state
sta >$E0C035
lda >int_Y
tay
lda >int_X
tax
lda >int_D
tcd
short m
lda >int_B
pha
plb
lda >int_SLTROM
sta >$E0C02D
lda >int_STATEREG
sta >$E0C068
lda >int_P
pha
long m
lda >int_A
plp
xce
rti
END
longa on
longi on
InitKernel START
using KernelStruct
; TO CONSOLE
; using InOutData
phb
phk
plb
move3 $E10071,oldBrk+1
; move3 #BrkVect,$E10071
pha
pha
pea $5
_GetVector
pl4 oldCop
; move3 $E10015,oldCop
pea $5
ph4 #CopVect
_SetVector
; move3 #CopVect,$E10015
; pha
; _MMStartUp
; pla
; sta userID
; kp->userID set in main.c
; TO CONSOLE
; jsl InOutStart ; good enough here....
jsl patchGSOS
jsr PatchText
jsl initvar
jsl patchRM
ldx #0
loop lda #0
sta ParentProc,x
sta ProcessState,x
txa
clc
adc #procEntSize
tax
cpx #(procEntSize*NPROC)
bcc loop
lda #pRunning
sta ProcessState
; let's do it, dude!
pushlong #alarmHB
_SetHeartBeat
php ; jb 6/23/91
sei ; jb 6/23/91
move3 $E10069,oldDecBusy+1
move3 #newDecBusy,$E10069
move3 $E10011,oldInt+1
move3 #IntHndl,$E10011
; mv4 >$E10048,oldCDAvect
; move3 #CDAPatch,>$E10049
ph4 #PatchHeader
jsl InstallE10000
ply
ply
plp ; jb 6/23/91
plb
rtl
DeInitKern ENTRY
pushlong #alarmHB
_DelHeartBeat
pea $4 ; Interrupt Manager
ph4 oldInt+1
_SetVector
pea $5 ; COP Manager
ph4 oldCop
_SetVector
ph4 #PatchHeader
jsl RemoveE10000
ply
ply
move3 oldDecBusy+1,$E10069
move3 oldBrk+1,$E10071
; mv4 oldCDAvect,>$E10048
jsl unPatchRM
jsl unpatchGSOS
jsr UnPatchText
; TO CONSOLE
; jsl InOutEnd
rtl
oldInt ENTRY
jmp >$000000
dc i2'0' ; buffer space!
oldBrk ENTRY
jmp >$000000
oldCop dc i4'0'
oldTool1 dc i4'0'
;oldCDAvect ENTRY
; dc i4'0'
oldDecBusy ENTRY
jmp >$000000
END
mapPID START
using KernelStruct
retval equ 0
subroutine (2:pid),2
ldx #0
lp anop
lda ProcessState,x
beq trynext ; no process here, don't check
lda flpid,x
cmp pid
beq gotit
trynext anop
txa
clc
adc #procEntSize
cmp #(procEntSize*NPROC)
bcs notfound
tax
bra lp
gotit txa
lsr a
lsr a
lsr a
lsr a
lsr a
lsr a
lsr a
goaway anop
sta retval
return 2:retval
notfound lda #-1
bra goaway ; my stupidity amazes me. NOT gotit
END
allocPID START
using KernelStruct
retval equ 0
subroutine (0:foo),2
jsl incBusy
lp0 anop
ldx #0
lp anop
lda ProcessState,x
beq nextent
lda floatingPID
cmp flpid,x
beq trynext
nextent anop ; inc x to next entry
txa
clc
adc #procEntSize
cmp #(procEntSize*NPROC)
bcs gotone
tax
bra lp
trynext lda floatingPID
inc a
bpl inrange
lda #1
inrange sta floatingPID
bra lp0
gotone lda floatingPID
sta retval
inc a
bpl inrange2
lda #1
inrange2 sta floatingPID
jsl decBusy
return 2:retval
END
IntState DATA
int_A dc i2'0'
int_X dc i2'0'
int_Y dc i2'0'
int_S dc i2'0'
int_D dc i2'0'
int_B dc i2'0'
int_P dc i2'0' ; needed to go back to emulation mode
intType dc i2'0'
int_state dc i2'0'
int_SLTROM dc i1'0'
int_STATEREG dc i1'0'
END
IntHndl START
using IntState
using KernelStruct
; bvc nobrk
; jsr FlexBeep
; jmp BrkVect1
nobrk anop
clc
xce
bcs ohmygod
php
short a
pha
lda $E1C046
and #$08
bne gotVBL
tofw pla
plp
ohmygod xce
jmp oldInt
gotVBL anop
; lda >reschedFlag
; sta >$E00626
lda >timeleft
beq noMoreTime
dec a
sta >timeleft
bne tofw
noMoreTime anop
lda #1 ;1 indicates VBL
sta >intType
pla
; sei
long ai
sta >int_A ; save accumulator
short a
lda >$E0C02D
sta >int_SLTROM
lda >$E0C068
sta >int_STATEREG
pla
sta >int_P
phb
pla
sta >int_B ; save data bank
long a
tsc
sta >int_S ; save stack pointer
tdc
sta >int_D ; save direct page
phk ; set up kernel's databank
plb
txa
sta >int_X
tya
sta >int_Y ; save X & Y
ldx curProcInd ; update the process'
lda ticks,x
adc #TIMESLICE
sta ticks,x
bcc nocarry
inc ticks+2,x
nocarry anop
lda >$E0C035
and #%1111111111110111
sta >int_state
jsl contextSwitch
; restore stuff
newViaVBL ENTRY
long mx
lda >$E0C035
and #%0000000000001000
ora >int_state
; lda >int_state
sta >$E0C035
lda >int_Y
tay
lda >int_X
tax
lda >int_D
tcd
short a
lda >int_B
pha
plb
lda >int_SLTROM
sta >$E0C02D
lda >int_STATEREG
sta >$E0C068
lda >int_P
pha
long a
lda >int_A
plp
xce
jmp oldInt
END
* Databank MUST be set up before entry
contextSwitch Start
Using KernelStruct
Using IntState
; there used to be a LONG here, but it's unnecessary
lda >BusyFlag
beq humph
brl goaway
humph anop
lda #TIMESLICE
sta timeleft ; run me again please
ldx curProcInd
jsr GetInterruptState
ldx curProcInd
tsc
; sta irq_S1,x
; if the process state isn't Running, the kernel wants to change it's status,
; so oblige it by not changing it to 'Ready'
lda ProcessState,x
cmp #pRunning ; change of state?
bne findit
spcl anop
lda #pReady
sta ProcessState,x
findit jsr FindNextEntry
stx curProcInd
txa
clc
adc #KernelStruct
sta >procPtr
lda #^KernelStruct
adc #0
sta >procPtr+2
txa
lsr a
lsr a
lsr a
lsr a
lsr a
lsr a ; divide by 128 to get true pid
lsr a
sta truepid
* Repair SANE's DP space
phd
lda >$E103CA
pha
lda >$E103C8
pha
tsc
tcd
ldy #$A*4
ldx curProcInd
lda SANEwap,x
sta [1],y
pla
pla
pld
lda ProcessState,x
cmp #pNew
bne okay
; the following code is hopelessly obtuse, but it basically creates information
; on the stack to simulate an interrupt, so that the new process starts it's
; time slice like every other one does
lda #pRunning
sta ProcessState,x
dec irq_S,x ; make room on new stack
dec irq_S,x ;
dec irq_S,x ;
dec irq_S,x ;
jsr SetInterruptState ; set up A,X,Y, etc
lda irq_D,x
tcd
lda irq_S,x ; new proc's stack
tcs ;
lda irq_P,x ;
and #%11111011 ; make sure interrupts are on when
sta 1,S ; we start the new process
lda irq_PC,x ; fake an interrupt
sta 2,S ; on the new process' stack
short a
lda irq_K,x
sta 4,s
pha
plb
long a
lda >intType
bne vbl
jmp newViaCOP ; this is required in order to
vbl jmp newViaVBL ; turn off VBL if one occurred
longa on ; grr stupid stupid m/x 816
longi on ; fricken bits
okay anop
* ; we'll return to COP or int hndl
jsr SetInterruptState
ldx curProcInd
lda #pRunning
sta ProcessState,x
; lda irq_S1,x
; clc
; adc #3
lda irq_S,x
tcs
lda >intType
bne vbl1
jmp newViaCOP ; this is required in order to
vbl1 jmp newViaVBL ; turn off VBL if one occurred
goaway anop ; well, so much for that!
rtl
temp dc i4'0'
timState dc i2'0'
stat dc i2'0'
END
longa on
longi on
GetInterruptState START
using KernelStruct
using IntState
lda int_A
sta irq_A,x
lda int_X
sta irq_X,x
lda int_Y
sta irq_Y,x
lda int_B
sta irq_B,x
lda int_P
sta irq_P,x
lda int_D
sta irq_D,x
lda int_S
sta irq_S,x
lda int_state
sta irq_state,x
lda int_SLTROM
sta irq_SLTROM,x
rts
END
SetInterruptState START
using KernelStruct
using IntState
lda irq_A,x
sta int_A
lda irq_X,x
sta int_X
lda irq_Y,x
sta int_Y
lda irq_B,x
sta int_B
lda irq_P,x
sta int_P
lda irq_D,x
sta int_D
lda irq_S,x
sta int_S
lda irq_state,x
sta int_state
lda irq_SLTROM,x
sta int_SLTROM
rts
END
; we need FindNextEntry to return x -> ProcessState,x
; When we're of a mind to, rewrite this to support priority scheduling
; (it will involve keeping a queue of 'Ready' processes).
FindNextEntry Start
Using KernelStruct
ldx curProcInd
txa
clc
adc #procEntSize
tax
cpx #(procEntSize*NPROC)
bmi loop
ldx #0
loop lda ProcessState,x
cmp #pReady
beq found
cmp #pNew
beq found
txa
clc
adc #procEntSize
tax
cpx #(procEntSize*NPROC)
bcc loop
ldx #0
bra loop
found rts
End
* if X is -1 then the FindEmptyEntry call failed
* this needs to return offset in ProcessState otherwise
FindEmptyEntry Start
Using KernelStruct
ldx #procEntSize
loop lda ProcessState,x
cmp #pUnused
beq found
txa
clc
adc #procEntSize
tax
cpx #(procEntSize*NPROC)
bcc loop
notfound ldx #$FFFF
found rts
End
; the NASTY assembly interface to the process tables
; currently, eight process entries are defined, and this must be set
; manually if NUMPROC in the system header files are changed. Kinda yucky,
; but then there's no other way.
KernelStruct DATA
CKernData ENTRY
ParentProc dc i2'-1' ;0
ProcessState dc i2'0' ;2
procUserID dc i2'0' ;4
ttyID dc i2'0' ;6
irq_A dc i2'0' ;8
irq_X dc i2'0' ;10
irq_Y dc i2'0' ;12
irq_S dc i2'0' ;14
irq_D dc i2'0' ;16
irq_B dc i2'0' ;18
irq_P dc i2'0' ;20
irq_state dc i2'0' ;22
irq_PC dc i2'0' ;24
irq_K dc i2'0' ;26
psem dc i2'0' ;28
prefixh dc i4'0' ;30
argsp dc i4'0' ;34
env dc i4'0' ;38
siginfo dc i4'0' ;42
irq_SLTROM dc i1'0' ;46
irq_STATEREG dc i1'0' ;47
resapp anop
lastTool dc i2'0' ;48
ticks dc i4'0' ;50
flags dc i2'0' ;54
openFiles dc i4'0' ;56
pgrp dc i2'0' ;60
exitCode dc i2'0' ;62
LInfo dc i4'0' ;64
stoppedState dc i2'0' ;68
alarmCount dc i4'0' ;70
executeHook dc i4'0' ;74
queueLink dc i2'0' ;78
waitq dc i4'0' ;80
waitdone dc i2'0' ;84
flpid dc i2'1' ;86
retStack dc i4'0' ;88
t2StackPtr dc i2'0' ;92
p_uid dc i2'0' ;94
p_gid dc i2'0' ;96
p_euid dc i2'0' ;98
p_egid dc i2'0' ;100
SANEwap dc i2'0' ;102
msg dc i4'0' ;104
childTicks dc i4'0' ;108
p_waitvec dc i4'0' ;112
p_slink dc i2'0' ;116
p_link dc i4'0' ;118
p_rlink dc i4'0' ;122
p_prio dc i2'0' ;126
procEntSize gequ *-CKernData
ProcessQueue ds 128*31 ; more o' the above
curProcInd dc i2'0' ; basically current process ID
userID dc i2'0' ; kernel userID
mutexNOMO dc i2'0' ; system-wide mutex flag.
timeleft dc i2'TIMESLICE' ; number of ticks left in timeslice
numProcs dc i2'1' ; number of processes active (inc.Null)
truepid dc i2'0' ; curProcInd/128
shutdown dc i2'0' ; should the null process exit?
gsosDebug dc i2'0' ; GS/OS Debug level
floatingPID dc i2'2' ; next pid to allocate
reschedFlag dc i2'0'
END
_asmresched START
using KernelStruct
* give process our remaining time plus theirs
lda >timeleft
inc a
sta >timeleft
cop $7f ; force a context switch
rts
END
vogue DATA
dc c'TIDDLYWINKS000000',h'00'
END
NullProcess START
using KernelStruct
using sigQueue
using deferdata
itself anop
; check for deferred actions (probably from an interrupt handler to wake
; up a process) and execute them until no more are left.
defer_loop lda defer_num
beq no_defer
jsl exec_defer
bra defer_loop
no_defer anop
* if a CDA event is pending across two consecutive runs of the nullprocess,
* GetNextEvent to initiate the CDA.
* Only do the check every four invocations of NullProcess to avoid
* unnecessary overhead
dec loopcnt
bne noCDA
lda #4
sta loopcnt
pha
pea $0400
ph4 #evtRecord
_EventAvail
pla
cmp #0
beq noCDA
lda cdaFlag
cmp #1
bcc justInc
pha
pea $0400
ph4 #evtRecord
_GetNextEvent
pla
stz cdaFlag
bra noCDA
justInc inc a
sta cdaFlag
noCDA lda sighead
cmp sigtail
beq nosigs
jsl incBusy
lda sighead
asl a
tax
pha
lda procs,x
pha
lda sigs,x
pha
ph4 #sigerr
jsl KERNkill ; this is #pragma toolparms 1
pla
lda sighead
inc a
sta sighead
cmp #32
bcc inrange
stz sighead
inrange jsl decBusy
nosigs anop
lda shutdown
bne endGNO ; kill() will shut down processes before this
jsl readyEntry
cpx #0 ; if it only found us, loop to avoid
beq jumpfuck ; interrupt overhead
cop $7f ; otherwise, let that process run
jumpfuck jmp itself
endGNO anop
rtl
sigerr dc i2'0'
loopcnt dc i2'4'
cdaFlag dc i2'0'
evtRecord ds 16
END
sigQueue DATA
sighead dc i2'0'
sigtail dc i2'0'
sigs ds 64
procs ds 64
END
queueSignal START
using sigQueue
pha
phx
lda sigtail
asl a
tax
lda 1,s
sta sigs,x
lda 3,s
sta procs,x
lda sigtail
inc a
sta sigtail
cmp #32
bcc inrange
stz sigtail
inrange plx
pla
rts
END
longa off
alarmHB DATA
dc i4'0'
ALtimer dc i2'6'
dc h'5AA5'
END
alarmRoutine START
using alarmHB
using KernelStruct
using SelTimStruct
php
long ai
phb
phk
plb
; re-activate our heart beat entry
lda #6
sta ALtimer
; check for SIGALRM timeouts
ldx #0
loop anop
lda ProcessState,x
cmp #pUnused
beq nextProc
lda alarmCount,x
ora alarmCount+2,x
beq nextProc
lda alarmCount,x
sec
sbc #1
sta alarmCount,x
lda alarmCount+2,x
sbc #0
sta alarmCount+2,x
ora alarmCount,x
bne nextProc
phx
lda flpid,x
ldx #14
jsr queueSignal
plx
nextProc anop
txa
clc
adc #procEntSize
tax
cpx #(procEntSize*NPROC)
bcc loop
; check for select() timeouts
ldx #0
selloop anop
lda SelTimTimeout,x
ora SelTimTimeout+2,x
beq nextSel
lda SelTimTimeout,x
sec
sbc #1
sta SelTimTimeout,x
lda SelTimTimeout+2,x
sbc #0
sta SelTimTimeout+2,x
ora SelTimTimeout,x
bne nextSel
lda #1
sta SelTimExpired,x
phx
lda SelTimPID,x ; wake up this PID
pha
pea 0 ; no collision
jsl selwakeup
plx
nextSel anop
txa
clc
adc #8
tax
cpx #(8*NPROC)
bcc selloop
plb
plp
rtl
END
* AHHA!
* The remarkably simple new Kernel mutex method
* is totally based off of BusyFlag. Now things seem to work much
* smoother, and I can accomplish all sorts of stuff now.
* Yea, right, you dumbshit (jb 7/5/93)
incBusy START
disableps ENTRY
jmp >$E10064
decBusy ENTRY
enableps ENTRY
jmp >$E10068
END
newDecBusy START
using KernelStruct
php
pha
jsl oldDecBusy
lda >timeleft
bne stillmore
lda >BusyFlag
bne stillmore
lda >reschedFlag
bne stillmore ; don't reschedule
* We now allow a context switch when interrupts are off - this
* allows flawless execution of semaphore sleep code, etc.
lda 3,s ; check the P reg- is the
bit #4 ; i bit set? if so, do not task
bne stillmore ; switch
lda >$E100CB
and #$FF
bne stillmore ; we're in an interrupt handler!
tsc
and #$FF00
cmp #$0100
beq stillmore
pla
plp
jsl fakeACop
; cop $7f ; we're out o' time
rtl
stillmore pla
plp
rtl
END
ttyStruct START
pgrp dc 38i2'0'
END
* BGCheck MUST MUST MUST be called only if the busy flag is zero
* otherwise, the process will not be suspended before it does the
* I/O procedure.
* Note that the use of this routine in TextTools to generate an error code
* is okay, because the only way for a program to have blocked or ignored
* the signal is to be aware of GNO, and if they do that they should check for
* errors from these texttools calls (which they should check for anyway).
* Input: A = signal number
* X = TTY device number
* needs to be in the kernel segment
BGCheck START
using KernelStruct
retval equ 1
signum equ 1
signum2 equ 5
sigptr equ 1
phb
phk
plb
phd
pha ; push input parameter
tsc
tcd
txa
asl a
tax
lda ttyStruct,x
beq nopgrp
ldx curProcInd
cmp pgrp,x
beq nopgrp ; it's the foreground process
lda siginfo+2,x
pha
lda siginfo,x
pha
tsc
tcd
lda #$0010
ldy signum2
cpy #21
beq noasl
asl a
noasl ldy #2
and [sigptr],y
bne sigblocked
lda signum2
dec a
asl a
asl a
clc
adc #8
tay
lda [sigptr],y
cmp #1
bne notign
iny2
lda [sigptr],y
cmp #0
bne notign
* the signal is ignored or blocked, so do not send the signal, and
* tell the caller that we did not.
sigblocked pla
pla
pla
lda #-1
pld
plb
rtl
* the signal was not ignored or blocked, so send the signal,
* tell the caller that we did, and return
notign lda pgrp,x
pha
pei (signum2)
jsl asmkillpg
cop $7F
pla
pla
pla
lda #1
pld
plb
rtl
* the caller was indeed in the TTY's process group, and so should be left
* quite alone.
nopgrp pla
lda #0
pld
plb
rtl
END
asmkillpg START
using KernelStruct
subroutine (2:pg,2:signum),0
ldx #128 ; don't EVER signal the kernel
sigloop lda ProcessState,x
cmp #pUnused
beq nextproc
lda pgrp,x
cmp pg
bne nextproc
phx
lda flpid,x
ldx signum
jsr queueSignal
plx
nextproc anop
txa
clc
adc #128
tax
cpx #4096 ; oops!
bcc sigloop
return
END
extQSignal START
using KernelStruct
space equ 0
subroutine (2:s,2:p),space
lda p
ldx s
jsr queueSignal
return
END
ttyQSignal START
using KernelStruct
space equ 0
subroutine (2:s,2:tty),space
lda tty
asl a
tax
lda ttyStruct,x
beq noSignal ; no process group
pha
ldx #128 ; don't EVER signal the kernel
sigloop lda ProcessState,x
cmp #pUnused
beq nextproc
lda pgrp,x
cmp 1,s
bne nextproc
phx
lda >flpid,x
ldx s
jsr queueSignal
plx
nextproc anop
txa
clc
adc #128
tax
cpx #4096 ; oops!
bcc sigloop
pla
noSignal return
END
snooperInfo DATA KERN2
blockLen dc i2'len-blockLen'
nameString str '~PROCYON~GNO~SNOOPER'
s_procTable dc i4'CKernData'
s_pipeTable dc i4'pipeRecord'
refTrack dc i4'pipeData' ; lots of dereffing, but hey!
queueSig dc i4'extQSignal'
info1 dc i4'savedCDAInfo'
reftty dc i4'ttyStruct'
refpgrp dc i4'pgrpInfo'
refRefTab dc i4'pipeData'
len equ *
END
* At this point, the process RTL address is on the stack. Push the P
* register to complete simulating the interrupt environment.
fakeACop START
php
sei
long m
pha
lda 4,s
inc a ; 'fix' rti address
sta 4,s
pla
jmp >CopVect
END
readyEntry Start KERN2
Using KernelStruct
ldx #(1*procEntSize)
loop lda >ProcessState,x
cmp #pReady
beq found
cmp #pNew
beq found
txa
clc
adc #procEntSize
tax
cpx #(procEntSize*NPROC)
bcc loop
ldx #0
rtl
found rtl
End
sleepbusy START KERN2
php
sei
short m
lda >$E100FF
pha
lda #0
sta >$E100FF
cop $7F
pla
sta >$E100FF
plp
rtl
END
longa on
longi on
exec_defer START
using deferdata
proc equ 0
val1 equ 4
val2 equ 6
subroutine (0:foo),8
phb
phk
plb
php
sei
ldy defer_tail
tya
iny
cpy #NUM_DEFER
bne nowrap
ldy #0
nowrap sty defer_tail
dec defer_num
asl a
asl a
tax
lda defertab,x
sta proc
lda defertab+2,x
sta proc+2
lda defertab+4,x
sta val1
lda defertab+6,x
sta val2
plp
; pass params in 'C' format
pei (val2)
pei (val1)
; push return address
phk
pea |comeback-1
; push routine address
short m
lda proc+2
pha
long m
lda proc
dec a
pha
rtl
comeback anop
plb
return
END
defer START
using deferdata
subroutine (4:procPtr,2:val1,2:val2),
phb
phk
plb
php
sei
lda defer_num
cmp #NUM_DEFER
beq fail
ldy defer_head
tya
iny
cpy #NUM_DEFER
bne nowrap
ldy #0
nowrap sty defer_head
inc defer_num
asl a
asl a
tax
lda procPtr
sta defertab,x
lda procPtr+2
sta defertab+2,x
lda val1
sta defertab+4,x
lda val2
sta defertab+6,x
fail1 plp
plb
return
fail anop
short m
lda >$E0C034
and #$F0
pha
lda >$E0C034
inc a
and #$F
ora 1,s
sta >$E0C034
pla
long m
bra fail1
END
deferdata DATA
defer_head dc i2'0'
defer_tail dc i2'0'
defer_num dc i2'0'
defertab ds 8*NUM_DEFER
END
k_sleep START
using KernelStruct
hv equ 0
last equ 2
pid128 equ 4
subroutine (2:pid,2:pri,4:vec),6
jsl disableps
lda vec hv = hash_vector(vec);
and #$3F
sta hv
lda pid
and #$00FF
xba
lsr a
sta pid128
tax
lda vec
sta >p_waitvec,x
lda vec+2
sta >p_waitvec+2,x
lda hv
asl a
tax
lda >hv_tab,x
sta last
cmp #0
bne addtoend
lda pid
sta >hv_tab,x
bra skip1
addtoend anop
and #$FF
xba
lsr a
tax
lda >p_slink,x
bne addtoend
; last proc index is in X
lda pid
sta >p_slink,x
skip1 lda #0
ldx pid128
sta >p_slink,x
pei (pid)
jsl dosleep
jsl sleepbusy
jsl enableps
return
END
longa on
longi on
* used to be in signal.c, orig. C source still is
Kreceive START KERN2
using KernelStruct
tmp equ 0
subroutine (4:errnoptr),4
lda >curProcInd
tax
php
sei
lda #2
sta >waitdone,x
lda >flags,x
bit #$0080 ; FL_MSGRECVD
bne gotmsg
lda #3
sta >ProcessState,x
jsl sleepbusy
gotmsg lda >waitdone,x
cmp #2
bne huhwhat
lda >msg,x
sta tmp
lda >msg+2,x
sta tmp+2
lda >flags,x
; and #$0080.EOR.$FFFF
and #$FF7F
sta >flags,x
bra what2
huhwhat lda #$FFFF
sta tmp
sta tmp+2
what2 plp
return 4:tmp
END