DataMuseum.dk

Presents historical artifacts from the history of:

Bogika Butler

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Bogika Butler

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download

⟦93851a888⟧ TextFile

    Length: 152832 (0x25500)
    Types: TextFile
    Names: »MMPROCS.SA«

Derivation

└─⟦1ed910a99⟧ Bits:30009789/_.ft.Ibm2.50006596.imd Mogens Pelles Zilog 80,000 / EOS projekt
    └─⟦this⟧ »MMPROCS.SA« 
└─⟦798d317aa⟧ Bits:30009789/_.ft.Ibm2.50006626.imd Mogens Pelles Zilog 80,000 / EOS projekt
└─⟦798d317aa⟧ Bits:30009789/_.ft.Ibm2.50006628.imd Mogens Pelles Zilog 80,000 / EOS projekt
    └─⟦this⟧ »MMPROCS.SA« 

TextFile

 
***********************************************************************
*                       Copyright 1984 by                             *
*                       NCR Corporation                               *
*                       Dayton, Ohio  U.S.A.                          *
*                       All Rights Reserved                           *
***********************************************************************
*                       EOS Software produced by:                     *
*                       NCR Systems Engineering - Copenhagen          *
*                       Copenhagen                                    *
*                       DENMARK                                       *
***********************************************************************
 
MM_PROCS   IDNT 1,0               MEMORY MANAGEMENT PROCEDURES
           OPT FRS                FORWARD REFERENCE SHORT,
           NOLIST
           INCLUDE COMDEF.SA
           LIST
 
* WRITTEN BY VILHELM ROSENQVIST
*
* A SYSTEM DEFINED PART OF THE ADDRESS SPACE IS MANAGED BY
* THE MM_PROCS. THE FIRST AND LAST PART OF THIS ADDRESS SPACE MUST BE RAM.
* NO_RAM AREAS MAY BE PRESENT IN BETWEEN. THA WHOLE ADDRESS SPACE IS
* STRUCTURED AS A TWO_WAY CHAINED LIST OF MEMORY RECORDS (MEM_RECS).
* THE CHAINS MAKE IT POSSIBLE TO SCAN THE MEMORY RECORDS IN INCREASING OR
* DECREASING ADDRESS ORDER. EACH MEMORY REC CONSISTS OF THREE PARTS; NAMELY
* A NO_USE PART, A FREE PART AND A USED PART
* THE NO_USE PART CANNOT BE USED TO ALLOCATE NEW OBJECTS. THE FREE PART MAY
* BE USED TO ALLOCATE KERNEL PARTS OR USER PARTS
* THE USED PART CONTAINS EITHER KERNEL PARTS OR USER PARTS, NOT BOTH.
* A MEM_REC CONTAINING KERNEL PARTS IN THE USED PART IS CALLED A KERNEL
* MEM_REC, WHILE A MEM_REC CONTAINING USER PARTS IS CALLED A USER MEM_REC.
* IF NO USED PART IS PRESENT THE RECORD IS CALLED A FREE MEM_REC.
* THE MEM_REC CHAIN STARTS AT 'F_ALLOC' AND ENDS AT 'L_ALLOC'. ALL KERNEL
* MEM_RECS PRECEDE ALL THE USER MEM_RECS. FREE MEM_RECS IS ONLY PRESENT
* WHEN THE LAST PART OF A RAM AREA IS FREE. OTHER FREE AREAS APPEARS
* ONLY AS THE FREE PART OF A KERNEL OR USER MEM_REC.
* THE TYPE OF THE MEM_REC, THE CHAIN FIELDS AND A DESCRIPTION OF THE
* THREE PARTS ARE PLACED AT THE BEGINNING OF THE MEM_REC, AND
* IS CALLED THE HEAD OF THE MEM_REC.
*
* HEAD OF MEM_REC
           OFFSET 0
MR_NXT     DS.L   1  NEXT: ADDR OF NEXT MEM_REC  (OFFSET 0 USED)
MR_PRV     DS.L   1  PREV: ADDR OF PRIOR MEM_REC (OFFSET 4 USED)
MR_FST     DS.L   1  FIRST: ADDR OF THE FIRST FREE BYTE (THE FREE PART)
MR_FRE     DS.L   1  FREE_BYTES: NUMBER OF BYTES IN THE FREE PART (MAY BE ZERO)
* ONE WORD WITH TWO BYTES FOLLOWS:
MR_FIX     DS.B   1  FIXED: 0=DYNAMICLY CREATED MEM_REC HEAD, 1=INITIAL HEAD
MR_TYP     DS.B   1  TYPE: 0=FREE MEM_REC, 1=KERNEL MEM_REC,-1=USER MEM_REC
MR_SIZ     DS.W   0  HEAD_SIZE: SIZE OF HEAD
* LET HEAD BE THE ADDRESS OF A MEM_RECORD. WE DEFINE THE FOLLOWING ADDRESSES:
* FIRST_NO_USE = HEAD
* FIRST_FREE   = HEAD.FST
* FIRST_USED   = HEAD.FST+HEAD.FRE
* TOP_USED     = HEAD.NXT
* THE THREE PARTS OF A MEM_RECORD ARE:
* NO_USE PART  = FIRST_NO_USE..FIRST_FREE-1 (MAY BE EMPTY)
* FREE PART    = FIRST_FREE..FIRST_USED-1   (MAY BE EMPTY)
* USED PART    = FIRST_USED..TOP_USED-1     (MAY BE EMPTY)
*
    XDEF  MR_FRE,MR_SIZ   USED BY ENTER TO COMPUTE SIZE OF FREE MEMORY.
*
* THE HEAD OF THE MEM_REC MAY BE PART OF THE NO_USE PART (FIXED=1, THE
* HEAD IS NEVER DESTROYED) OR THE FREE PART (THE HEAD MAY BE DESTROYED
* WHEN FREE MEMORY IS USED TO ALLOCATE OBJECTS,FIXED=0).
* IN THE LAST CASE FIRST_FREE POINT TO THE HEAD ITSELF.
* THE ADDRESS SPACE IS INITIALLY DEFINED TO MM AS A (SORTED) LIST OF
* ADDRESS PAIRS. EACH PAIR DEFINES A RAM AREA THROUGH
* ITS FIRST AND TOP ADDRESS. INITIAL MEM_RECS ARE CREATED AS FOLLOWS:
* ON THE LAST PAGE OF EACH RAM AREA IS PLACED A HEAD WHICH DESCRIBES THAT
* PAGE PLUS THE FOLLOWING NO_RAM AREA AS A NO_USE AREA AND DESCRIBE THE
* NEXT RAM AREA (EXCEPT ITS LAST PAGE) AS A FREE AREA. NO USED AREA IS PRESENT
* IN THESE INITIAL FREE MEM_RECS. AN INITIAL MEM_RECS HAS FIXED=1, AND THE
* FREE PART OF THE MEM_REC ENDS AT A PAGE BAUNDARY AND BEGINS AT A WORD
* BAUNDARY.
* THE FIRST AND LAST RAM AREA IS TREATED A LITTLE DIFFERENTLY:
* THE FIRST RAM AREA (EXCEPT THE LAST PAGE) IS DESCRIBED IN THE FIRST
* MR_SIZ BYTES OF THE AREA.
* THE HEAD IS EQUVALENT WITH THE NO_USE PART OF THIS FIRST MEM_REC
* THE ADDRESS OF THE FIRST HEAD IS CALLED F_ALLOC.
* THE LAST PART OF THE LAST RAM AREA IS USED FOR THE PAGE TABLE
* (SEE LATER), AND A DUMMY HEAD IN FRONT OF THE TABLE. THE ADDRESS
* OF THIS HEAD IS CALLED L_ALLOC. THE MEM_REC BEFORE L_ALLOC
* HAS A USED PART CONTAINING ONE "USER PART" (THE LAST REAL
* MEM_REC IS ALWAYS A USER MEM_REC). THIS USER PART IS
* USED TO HOLD THE SUPERVISOR STACK. IT IS NEVER MOVED,
* BUT MAY BE EXTENDED.
* IF ONLY ONE RAM AREA IS PRESENT, THE RULES FOR THE FIRST AND THE
* LAST RAM AREA APPLIES TO THIS AREA.
* HEADS OF INITIAL MEM_REC ARE NEVER DESTROYED, BECAUSE THEY ARE
* PLACED IN NO_USE PARTS. WHEN KERNEL, AND USER PARTS ARE CREATED AND REMOVED,
* NEW MEM_REC HEADS MAY BE CREATED. ALL THESE HEADS ARE PLACED IN THE FREE
* PART OF THE MEM_REC THEY DESCRIBE, I.E. HEAD.MR_FIRST=HEAD AND
* HEAD.MR_FIX=0.
* AN OBJECT IS REPRESENTED BY ONE KERNEL PART AND OPTIONALLY ONE USER PART.
* THE KERNEL PART IS ONLY ACCESSED BY THE KERNEL AND CONTAINS THE ADDRESS
* OF THE USER PART. THE USER PART REFERENCES THE KERNEL PART THROUGH A PAGE
* TABLE. THE TABLE CONSISTS OF ONE LONG WORD FOR EACH PAGE (256 BYTES) OF
* "ADDRESS SPACE", BETWEEN F_ALLOC AND L_ALLOC. LET TUA BE THE ADDRESS
* OF THE TOP BYTE OF A USER MEM_REC (THE HIGHEST ADDRESS +1), THEN
* PAGETABLE(TUA//256) CONTAINS THE ADDRESS OF THE KERNEL PART. PAGE TABLE
* ENTRIES THAT DO NOT CORRESPOND TO THE TOP PAGE OF A USER PART ARE
* UNDEFINED FOR THE MOMENT. ONLY ONE PAGETABLE IS PRESENT TO SIMPLIFY
* THE ALGORITHM. IF THE NO_USE AREAS ARE LARGE, A PAGETABLE COULD BE
* PLACED AT THE END OF EACH RAM AREA DESCRIBING THE PAGES OF
* THAT AREA. NO ENTRIES WOULD CORRESPOND TO NO_USE PAGES AS IS THE
* CASE IN THE PRESENT IMPLEMENTATION. THE METHOD ALLOWS ADDITION OF RAM AREAS
* "ON THE FLY".
* THE PAGE TABLE MAY ALSO BE REPLACED BY A CHAIN THROUGH ALL KERNEL PARTS.
* THE KERNEL PARTS SHOULD BE SORTED IN CHAIN, ACCORDING TO THE ADDRESS
* OF THEIR USER PART.
* THE PRESENT STRATEGY KEEPS ALL KERNEL PARTS IN THE LOW END OF THE ADDRESS
* SPACE, AND THE USER PARTS IN THE HIGH END. ANOTHER STRATEGY COULD USE
* EACH OF THE RAM AREAS IN THIS WAY I.E. ALLOCATE KERNEL PARTS IN
* THE LOW ADDRESS PART OF EACH RAM AREA WHILE USER PARTS ARE PLACED
* IN THE HIGH ADDRESS PARTS OF THESE AREAS. THIS APPROCH MAY HAVE A BETTER
* PERFORMANCE (LESS MOVE REQUIRED).
* THE PROCESS OF ALLOCATION AND DEALLOCATION SPLITS THE FREE MEMORY INTO
* SMALL FRAGMENTS. A FRAGMENT THAT IS SMALLER THAN THE SIZE OF A MEM_REC HEAD
* CANNOT HOLD THE MEMREC HEAD NEEDED TO DESCRIBE THE FRAGMENT AS BEING FREE.
* CONSEQUENTLY, SMALL FRAGMENTS CAN ONLY BE ALLOWED IF THEY BELONG TO AN
* FIXED MEM_REC (THE HEAD IS IN FRONT OF THE FREE AREA).
* TOO SMALL FRAGMENTS ARE AVOIDED AS FOLLOWS:
* KERNEL PARTS AND USER PARTS ARE ALWAYS BIGGER THAN A MEM_REC HEAD.
* USER PARTS ARE ALWAYS LOCATED ON A PAGE BAUNDARY AND OCCUPIES AN INTEGRAL
* NUMBER OF PAGES.CONSEQUENTLY, A FRAGMENT HAVING A USER PART AS NEIGHBOUR
* MUST BE AT LEAST ONE PAGE LONG I.E. IT CAN HOLD A MEM_REC HEAD, BE THE FIRST
* BYTES OF THE FREE AREA OF A FIXED MEM_REC I.E A MEM_REC HEAD IS NOT NEEDED,
* OR THE FRAGMENT MUST BE THE FREE AREA BETWEEN KERNEL PARTS AND USER PARTS.
* IN THE LAST CASE AN EXPLICIT CHECK IS MADE TO INSURE THAT ROOM FOR A HEAD IS
* SET ASIDE. THE KERNEL PARTS ARE NOT LOCATED ON PAGE BAUNDARIES. WHEN PARTS
* ARE ALLOCATED IT IS CHEKED THAT THE NEW PART FITS EXACTLY INTO THE FREE PART
* ,LEAVING NO FRAGMENT, OR LEAVES A FRAGMENT THAT CAN HOLD A MEM_REC HEAD.
 
    PAGE
           SECTION.S 7              = KNEL_VAR
* VARIABLES OF MEMORY MANAGEMENT:
*
* LOCAL VARIABLES = VARIABLES HAVING A MEANING BETWEEN CALLS TO THE MM_PROCS:
F_ALLOC    DS.L 1  CONTAINS THE ADDRESS OF THE FIRST MEM_REC
L_ALLOC    DS.L 1  CONTAINS THE ADDRESS OF THE LAST MEM_REC
F_SUPVS    DS.L 1  CONTAINS THE ADDRESS OF THE SUPERVISOR STACK (FIRST BYTE)
PAGE_TAB   DS.L 1  CONTAINS THE ADDRESS OF THE PAGE TABLE (ADDR OF ENTRY ZERO)
SUPV_STK   DS.L 1  CONTAINS THE "LAST + 1" ADDRESS OF THE SUPERVISOR STACK
MM_QUEUE   DS.W CHAIN CHAIN OF PROCESSES WAITING TO CREATE/REMOVE AN OBJECT.
MM_LOCK    DS.B 1  BIT_7=1 <=> ONE PROC CREATES/REMOVES AN OBJECT
           DS.W 0  BIT_7=0 <=> NO  PROC CREATES/REMOVES AN OBJECT
*
* CURKMOVE, CURUMOVE AND USERDEST:  THESE VARIABLES
* ARE DESCRIBED LATER AS GLOBAL TEMP VARIABLES BUT ARE IN FACT LOCALS.
*
 
* KERNEL VARIABLES DEFINED WITHIN OTHER MODULES:
* PREFACE MODULE:
 
  XREF.S 7:KV_FRMEM CONTAINS INITIALLY THE ADDRESS OF THE RAM AREA LIST; LATER
*                   THE NUMBER OF FREE BYTES FOR ALLOCATION OF OBJECTS
  XREF.S 7:KV_BOTLD CONTAINS ADDR. OF THE FIRST BOOT LOADED MODULE (RAM-BOOT)
*                   OR THE VALUE -1 (ROM-BOOT)
  XREF.S 7:KV_STUB  CONTAINS THE ADDRESS OF A ROM-STUB OR -1 WHEN THE STUB
*                   IS THE FIRST MODULE OF THE RAM-BOOT.
  XREF.S 7:TSTSTATU TEST OUTPUT FROM ALLOC/DEALLOC IS GENERATED WHEN BIT ZERO
*                   OF THIS VARIABLE EQUALS ONE
  XREF     PRINTA02 TEST PRINT PROCEDURE
*
* XREF.VAL TRP7STAK SUPERVISOR STACK SPACE (IN BYTES) NEEDED BY
*                   THE KERNEL OPERATIONS.
* XREF.VAL KV_STKSIZ INITIAL SIZE OF THE SUPERVISOR STACK
 
* SCHEDULER MODULE:
 
  XREF.S 7:KV_INVEC INTERRUPT VECTOR (NORMALLY = 0), CONTAINS ADDRESSES OF THE
*                   INTERRUPT PROCEDURES. THE ADDRESSES ARE ADJUSTET WHEN THE
*                   SEGMENTS HOLDING THE PROCEDURES ARE MOVED.
  XREF.S 7:DRIV_TAB DRIVER TABLE, CONTAINS ADDRESSES OF DRIVER ENVELOPES.
*                   THE ADDRESSES ARE ADJUSTET WHEN THE ENVELOPES ARE MOVED.
  XREF.S 7:KV_CTX   CURRENT CONTEXT,
*
  XREF.S 7:KV_PROC  CURRENT PROCESS
 
* KERNEL OPERATIONS MODULE:
 
* NONE SO FAR
 
* ENTER MODULE:
 
* NONE SO FAR
 
    PAGE
* INITIAL POINTERS
KV_IOWNP   DS.B PT_SIZ            OWNS OBJECT THAT ARE NEVER DELETED
*          XDEF KV_IOWNP ??       STUB OBJECT IS FIRST IN SET
 
KV_IMANP   DS.B PT_SIZ            MANAGES ENVELOPES THAT ARE NEVER DEMANAGED
*          XDEF KV_IMANP ??       STUB ENVELOPE IS FIRST IN SET
 
DUMMYOWN   DS.B PT_SIZ            OWNS A KERNEL PART WHILE THE USER PART
*                                 IS BEING ALLOCATED. (IS NOT INITIALIZED)
 
* INITIAL OBJECTS
ALLOCSIZ   EQU  SE_SIZ+SP_SIZ
ALLOCOBJ   DS.B ALLOCSIZ
* ALLOCOBJ HOLDS THE KERNEL PART OF THE KERNEL DEFINED ALLOC OBJECT.
* DURING MM_INITIALIZATION THE KERNEL PART IS USED AS THE KERNEL PART
* OF A NON_EMBEDDED SEGMENT DESCRIBING THE BOOT LOADED MODULES.
 
NEMB_SIZ   EQU  SE_SIZ+SP_SIZ     SIZE OF NON.EMB.SEGM.OBJ.
FULSPACE   DS.B NEMB_SIZ
* FULSPACE IS A SEGMENT OBJECT DESCRIBING THE WHOLE ADDRESS SPACE OF
* THE MC68000. SUBSEGMENTS OF FULSPACE IS USED BY DRIVER OBJECTS.
* THE STUB OBJECT GETS A REFERENCE TO FULSPACE.
 
    XDEF  ALLOCOBJ,F_ALLOC,L_ALLOC,F_SUPVS,SUPV_STK,GETSTUBE,GETSTUBO
    XDEF  INITGEIN,INITKNEL,INIT_ENV,MAKESEGM,SIMPLEPT,MM_CRE
    XDEF  MM_LOCK,MM_QUEUE,I_MMPROC,CHAINOWN
 
        PAGE
* "ALLOCATION PROCEDURES"
*
* NON EMBEDDED OBJECTS ARE ALLOCATED/DEALLOCATED BY CALLS TO MM_PROCS.
* THE FUNCTIONS ARE INVOKED FROM KERNEL OPERATIONS.
 
    XDEF   ALLOC_OB  ENTRY POINT FOR OBJCALL TO THE ALLO(CATE) OBJECT.
    XDEF   MM_TERM   LAST TERMINATION PROCEDURE FOR ALL NON EMB. OBJECTS.
 
*
* "SUB_ALLOCATION PROCEDURES"
*
* THE KERNEL PART AND USER PART OF AN OBJECT ARE USED BY
* THE KERNEL (OPERATIONS) TO HOLD THE FOLLOWING ENTITIES:
* KERNEL PART:
* 1) OBJECT-TYPE RELATED INFORMATION (GA, CO, SE, SU, GE, ...)
* 2) ENVELOPE DESCRIPTIONS
* 3) CONTEXT DESCRIPTIONS
* 4) DESCRIPTORS FOR EMBEDDED SEGM/SUBSEGM.
* USER PART:
* 5) SEGMENT DATA OF EMBEDDED AND NON EMBEDDED SEGMENT OBJECTS
*
* ALLOCATION/DEALLOCATION IS DONE BY CALLS TO THE FOLLOWING MM_PROCS
 
    XDEF   MM_PUSHK  ALLOCATES AN ENVELOPE OR CONTEXT DESCRIPTION
    XDEF   MM_POPK   DEALLOCATES
    XDEF   MM_PUSHU  ALLOCATES A DATA SEGMENT
    XDEF   MM_POPU   DEALLOCATES
    XDEF   MM_PUSHD  ALLOCATES AN EMBEDDED DESCRIPTOR
    XDEF   MM_POPD   DEALLOCATES
    XDEF   MM_PUSHO  ALLOCATES SPACE FOR OBJECT-TYPE RELATED INFORMATION
    XDEF   MM_POPO   DEALLOCATES
 
*
* ALLOCATION/DEALLOCATION
* MUST BE SYNCHRONIZED WITH THE MEMORY COMPACTION PROCEDURES
* (MOVEKNEL, MOVEUSER).
*
* THE GLOBAL VARIABLES SHARED BETWEEN THE COMPACTION PROCEDURES AND
* THE "SUB ALLOCATION" ARE CURKMOVE, CURUMOVE AND USERDEST (SEE BELOW).
 
    PAGE
* GLOBAL TEMP VARIABLES = VARIABLES USED DURING CALLS TO MM_PROCS.
CURKMOVE   DS.L 1         CURRENT KERNEL MOVE CANDIDATE
CURUMOVE   EQU  CURKMOVE  CURRENT USER MOVE CANDIDATE
* HOLDS THE ADDRESS OF THE NEXT ITEM (ENVELOPE, CONTEXT, SEGMENT) TO BE
* MOVED BY MM_PROCS. THE ITEM WILL BE MOVED WHEN MM_PROC REENTERS
* THE "SUB_MM" MONITOR
*
* THE ITEM MAY BE DESTROYED BY A CALL TO AN "MM_POP" -PROCEDURE.
* IN THIS CASE, THE "MM_POP" -PROCEDURE WILL UPDATE THE VARIABLE
 
CURKHEAD   DS.L 1
CUR_HEAD   EQU  CURKHEAD
* HOLDS THE ADDRESS OF THE HEAD OF A CONTEXT CHAIN OR OF AN ENVELOPE CHAIN.
 
CUROBOB    DS.L 1
* HOLDS THE ADDRESS OF AN OBJECT
 
CURDIST    DS.L 1
* HOLDS THE DISTANCE THAT ITEMS SHOULD BE MOVED.
 
CUR_STAK   DS.L 1
* HOLDS THE ADDRESS OF A CONTEXT OR ENVELOPE
 
CUR_EMBS   DS.L 1
* HOLDS THE ADDRESS OF AN EMBEDDED SEGMENT OBJECT
 
USERDEST   DS.L 1
* HOLDS THE FUTURE ADDRESS OF A USER PART BEING MOVED
* IF THE CURRENT USER MOVE CANDIDATE IS DESTROYED, THE MOVE
* IS ACTUALLY FINISHED, AND FUTURE ALLOCATION OF USER SEGMENTS
* SHOULD BE ALLOCATED ACCORDING TO THE NEW POSITION OF THE
* USER PART. THIS IS ENSURED BY MM_POPU, BY MOVING
* USERDEST TO SP_FIRU OF THE SPACE DESCRIPTION, IN CASE
* CURUMOVE IS POPPED.
 
SAVENEED   DS.L 1  HOLDS THE >>BYTES NEEDED<< DURING ALLOC.NEW_OBJ
SAVE_RPA   DS.L 1  HOLDS THE POINTER ARGUMENT DURING ALLOC.NEW_OBJ
*
* SAVEAREAS FOR REGISTERS  ( PROCSAVE MACRO ==>>  7-CHAR NAMES )
*
KSAVPRT   DS.L 15  USED BY C_UPARTS AND C_KPARTS
USAVPRT   EQU  KSAVPRT
KSAVCRS   DS.L 15  USED BY M_UACROS AND M_KACROS
USAVCRS   EQU  KSAVCRS
KSAVMOV   DS.L 15  USED BY MOVEUSER AND MOVEKNEL
USAVMOV   EQU  KSAVMOV
SAV_CHK   DS.L 2   USED BY CHECK_TIR
 
    PAGE
* INSTALLATION MODULE FORMAT.
* THE STUB IS NORMALLY THE FIRST INSTALATION MODULE.
           OFFSET 0               HEADER FIELDS
MOD_HSIZ   DS.W   1               THE SIZE OF THE MODULE HEADER SEGMENT
MOD_SIZE   DS.L   1               THE SIZE OF THE WHOLE MODULE. 1)
MOD_KIND   DS.W   1               MODULE KIND, SHOULD BE ZERO: PROGRAM MODULE
MOD_PROG   DS.W   1               THE ADDRESS OF THE PROGRAM DESCRIPTION. 2)
 
           OFFSET 0               PROGRAM OBJECT DESCRIPTION FIELDS
MOD_FDSD   DS.W   1               ADDRESS OF FIRST DATA SEGMENT DESCRIPTION. 3)
MOD_LOCS   DS.W   1               NO OF LOCAL POINTERS IN OBJECT
MOD_TEMP   DS.W   1               NO OF TEMP POINTERS IN CONTEXTS
MOD_TEMD   DS.L   1               NO OF TEMP BYTES IN CONTEXTS
MOD_STK    DS.W   SZ_TYP          CALL STACK REQUIREMENT = SIZE OF BOOT PROC
MOD_ENT    DS.L   1               ENTRY ADDRESS TO PROGRAM
MOD_NUL1   DS.W   1               UNUSED FIELD
MOD_LDSS   DS.W   1               NUMBER OF LOCAL DATA SEGMENT DESCRIPTIONS
 
           OFFSET 0               LOCAL DATA SEGMENT DESCRIPTION FIELDS
MOD_DSIZ   DS.W   1               SIZE OF DESCRIPTION
MOD_NUL2   DS.B  24               THESE FIELDS ARE NOT USED
MOD_LSDS   DS.W   1               NO OF LOAD SECTION DESCRIPTIONS
MOD_LDSZ   DS.W   0               SIZE OF FIXED PART OF DATA SEGMENT DESCRIPTION
 
           OFFSET 0               LOAD SECTION DESCRIPTION FIELDS
MOD_NUL3   DS.B   4               THESE FIELDS ARE NOT USED
MOD_SLEN   DS.L   1               THE LENGTH OF THE LOAD SECTION SEGMENT
MOD_NUL4   DS.B   4               THESE FIELDS ARE NOT USED
MOD_LSDZ   DS.W   0               SIZE OF ONE LOAD SECTION DESCRIPTION
 
* 1) 0 MEANS DUMMY TOP MODULE.
* 2) REL TO MOD_HSIZ
* 3) REL TO MOD_FDSD
 
    PAGE
* NEWHEAD  MACRO
 
* THE NEWHEAD MACRO CREATES A HEAD FOR A NEW MEM_REC AND
* AJUSTS THE CHAINS TO THE NEXT AND PRIOR MEM_RECS.
 
*   NEWHEAD NEW_HEAD,FIX+TYPE,PRIOR,NEXT
NEWHEAD MACRO
    MOVE.L Ø1,MR_FST(Ø1)          NEW_HEAD.FIRST_FREE:=NEW_HEAD;
    MOVE.W Ø2,MR_FIX(Ø1)          NEW_HEAD.RECORD_TYPE:=TYPE;"FIXED+TYPE"
    MOVE.L Ø3,MR_PRV(Ø1)          NEW_HEAD.PRIOR_REC:=PRIOR;
    MOVE.L Ø4,MR_NXT(Ø1)          NEW_HEAD.NEXT_REC:=NEXT;
    MOVE.L Ø1,MR_NXT(Ø3)          PRIOR.NEXT_REC:=NEW_HEAD;
    MOVE.L Ø1,MR_PRV(Ø4)          NEXT.PRIOR_REC:=NEW_HEAD;
  ENDM
* OBS  FREE_BYTES ARE NOT ASSIGNED. THE FIELDS ARE OFTEN REASSIGNED BY CALLER.
*
*-----------------
* PROCSAVE   MACRO
* SAVES REGISTERS IN A GLOBAL AREA
*
*   PROCSAVE <REGLIST>,<SAVEAREA>
*
PROCSAVE MACRO
.Ø2 REG Ø1
    MOVEM.L .Ø2,Ø2
    ENDM
*
*------------------
* PROCRTRN    MACRO
* RESTORE REGISTERS FROM A GLOBAL AREA
*
PROCRTRN MACRO
    MOVEM.L Ø1,.Ø1
    RTS
    ENDM
*
 
    PAGE
* MOVLM MACRO
*
* AJUST PRIOR AND NEXT, AND MOVE CHAIN ELEMENT
*
* CALL:  MOVLM AS, AD             AS=SOURCE ADDRESS REGISTER
*                                 AD=DESTINATION ADDRESS REGISTER (=NEW
*                                    POSITION)
* EXAMPLE: MOVLM A1,A0
*
* FUNCTION: REFERENCES TO THE ELEMENT TO BE MOVED ARE CHANGED TO REFLECT
*           THE NEW POSITION OF THE ELEMENT
*           THE ELEMENT IS MOVED
*           AS AND AD IS INCREASED BY SIZE OF ELEMENT (=8)
* NOTE: A5 AND A6 ARE USED AS WORKING ADDRESS REGISTERS
*
MOVLM MACRO
    MOVEM.L (Ø1),A5/A6            (A5,A6):=(NEXT,PRIOR) ELEMENT;
    MOVE.L Ø2,4(A5)               NEXT.PRIOR:=NEW POSITION;
    MOVE.L Ø2,(A6)                PRIOR.NEXT:=NEW POSITION;
    MOVE.L (Ø1)+,(Ø2)+            MOVE CHAIN ELEMENT
    MOVE.L (Ø1)+,(Ø2)+            AND UPDATE ADDRESS REGISTERS;
  ENDM
*
* 104 CYKLES ARE USED TO EXECUTE THE INSTRUCTIONS ABOVE
    PAGE
* MOVHD MACRO
* AJUST PRIOR AND NEXT AND ALL REFS IN CHAIN, AND MOVE CHAIN HEAD
*
* CALL: MOVHD AS,AD,NR            AS=SOURCE ADDRESS REGISTER
*                                 AD=DESTINATION ADDRESS REGISTER
*                                    (=NEW POSITION)
*                                 NR=NEW REFERENCE REGISTER
* EXAMPLE: MOVHD A1,A0,A2
*
* FUNCTION: ALL REFS IN ELEMENTS IN CHAIN ARE SET TO NR
*           REFERENCES TO THE CHAIN HEAD ARE CHANGED TO REFLECT
*           THE NEW POSITION OF THE CHAIN HEAD
*           THE ELEMENT IS MOVED
*           AS AND AD IS INCREASED BY SIZE OF HEAD (=8)
* NOTE: A5 AND A6 ARE USED AS WORKING ADDRESS REGISTERS
*
MOVHD MACRO
    MOVEM.L (Ø1),A5/A6                (A5,A6):=(NEXT,PRIOR) ELEMENT,
    MOVE.L Ø2,4(A5)                   NEXT.PRIOR:=NEW POSITION;
    MOVE.L Ø2,(A6)                    PRIOR.NEXT:=NEW POSITION;
    MOVE.L (Ø1),A5                    A5:=CUR:=NEXT;"MAY HAVE CHANGED"
  WHILE.L A5 <NE> Ø2 DO.S           WHILE CUR <> NEW POSITION DO
    MOVE.L Ø3,CH_HOLD(A5)             CUR.REF:=NEW REFERENCE;
    PRT_MEM MOVHD_NEWREF,(A5),PT_SIZ  ********
    MOVE.L (A5),A5                    CUR:=CUR.NEXT
  ENDW                              ENDW
 
    MOVE.L (Ø1)+,(Ø2)+                MOVE CHAIN HEAD
    MOVE.L (Ø1)+,(Ø2)+                AND UPDATE ADDRESS REGISTERS
  ENDM                              ENDW
*
*
*
* TEST THAT A VALUE IS A MULTIPLUM OF 256
* GENERATE AN ADDRESS EXCEPTION IF IT IS NOT THE CASE
* THE TEST IS USED IN THE INITIALIZATION.
*---------------------------------------------------------------------
TSTPAGE MACRO
        TST.B Ø1                   Ø1 MUST BE A DREG
  IF <NE> THEN.S                 IF LAST BYTE IS NOT ZERO THEN
        ERROR Ø2                   ERROR(Ø2);
  ENDI                           ENDI;
  ENDM
        PAGE
        SECTION 11           = INITIALIZE
 
* THE MM_PROC INITIALIZATION GETS CONTROL RIGHT AFTER THE PREFACE
*
* THE FOLLOWING STEPS ARE CARRIED OUT:
*
* 1) ALLOCATE PAGETABLE
*
* 2) SET UP INITIAL MEM_REC STRUCTURE
*
* 3) BOOT LOADED MODULES (IN RAM) ARE DESCRIBED AS ONE SEGMENT OBJECT
*
* 4) CREATE THE FULSPACE SEGMENT OBJECT AND INITIAL POINTERS.
*
* 5) ALLOCATE AND INITIALIZE THE STUB OBJECT, EXCEPT LOCAL POINTERS.
*
* 6) CREATE THE BOOT OWNER SET, DESCRIBING THE BOOT LOADED MODULES,
*      IN LOCAL-2 OF THE STUB OBJECT.
*
* 7) INITIALIZE LOCAL-1: REF TO THE CODE OF THE STUB
*               LOCAL-6: REF TO THE FULSPACE SEGMENT OBJECT
*
* 8) CREATE THE ALLOC OBJECT.
*
*
*       XREF.VAL   SCHTEMP,SCHTEMD   CONSTANTS OF SCHED
 
        XREF  11:I_SCHED           INIT OF SCHEDULER
 
 
* BELOW FOLLOWS PROCEDURES USED BY THE INITIALIZATION.
* SOME PROCEDURES IN SECTION 9 OF MMPROCS SHOULD BE MOVED TO SECTION 11
 
MMBUSERR EQU *                     BUS_ERROR_EXCEPTION:
*
* BUS_ERROR_EXCEPTION MAY BE GENERATED BY ILLEGAL ADDRESS
* SPECIFICATIONS IN THE RAM AREA LIST.
*
*
        ERROR  0                       ERROR(0);
*
        PAGE
GET_STMD EQU *   RETURN THE STUB MODULE ADDRESS IN A5
        MOVE.L   KV_STUB,A5            A5:=STUB ADDRESS;
  IF.L A5 <EQ> #-1 THEN.S            IF STUB_ADDRESS=-1 THEN "STUB IN RAM"
GET_LDMD EQU *   RETURN ADDRESS OF BOOT LOADED MODULES IN A5
 
        LEA      ALLOCOBJ,A5           A5:=FIRST_OF_BOOT :=
        MOVE.L   SE_FIR(A5),A5         ALLOC.FIRST_BYTE_OF_SEGMDATA;
  ENDI                               ENDI;
        RTS                            RETURN;
*
*
GETSTUBE EQU *   GET ADDRESS OF THE STUB ENVELOPE INTO A3
        MOVE.L   KV_IMANP,A3           A3:=STUB_ENVELOPE:=KV_IMANP.FIRST;
        RTS                            RETURN;
*
*
GETSTUBO EQU *   GET ADDRESS OF THE STUB OBJECT INTO A0
        MOVE.L   KV_IOWNP,A0           A0:=STUB_OBJECT:=KV_IOWNP.FIRST;
        RTS
 
 
MAKESEGM EQU *   MAKE A NON EMBEDDED SEGMENT OBJECT
*       CALL                           RETURN
* D1    LENGTH OF USER PART            SAME
* A0    ADDRESS OF KERNEL PART         SAME
* A1    ADDRESS OF USER PART           SAME
*
        PRT_REG  MAKESEGM_D1/A0/A1     ********
        ENTRYSAV D0/A0-A1,.MAKESEG     SAVE REGS
        MOVE.L   D1,D0                 D0:=TOP ADDRESS OF USER PART :=
        ADD.L    A1,D0                     LENGTH+FIRST OF USER PART;
        EXG.L    A0,A1                 A0:=USER PART, A1:=OBJECT:=KNELPART;
  IF.L D0 <LT> L_ALLOC THEN.S        IF TOP_ADDRESS INSIDE PAGETABLE THEN
  IF.L D0 <GT> F_ALLOC THEN.S
        BSR.L    PGTABSET              PAGETABLE (D0=TOP_ADDRESS):=KNEL_PART=A1;
  ENDI                               ENDI;
  ENDI                               ENDI;
        MOVE.B   #OB_SEOB,OB_KIN(A1)   OBJECT.KIND:=SEGMENT OBJECT;
        MOVE.B   #OB_SEGM,OB_STA(A1)   OBJECT.STATE:=SEGMENT;
        CLR.W    SE_IO(A1)             OBJECT.IO_COUNT:=0;
        MOVE.L   A0,SE_FIR(A1)         OBJECT.FIRST SEGMENT:=USER PART;
        MOVE.L   D1,SE_LEN(A1)         OBJECT.LENGTH SEGMENT:=USER BYTES;
        LEA      SE_WAIT(A1),A1        A1:= SEGM.SE_WAIT; "CHAIN HEAD"
        MOVE.L   A1,(A1)               SE_WAIT:=
        MOVE.L   A1,4(A1)              EMPTY;
        MOVE.L   OB_SPA-SE_WAIT(A1),A1 A1:=SP_DESC:=OBJECT.SPACE_DESCR;
        MOVE.L   A0,SP_FIRU(A1)        SP_DESCR.FIRST_FREE_USER:=USER PART;
        CLR.L    SP_FREU(A1)           SP_DESC.FREE_USER_BYTES:=0;
        MOVE.L   D1,SP_SIZU(A1)        SP_DESC.SIZE_OF_USER_PART:=USER BYTES;
        ADDI.L   #(SE_SIZ-OB_SIZ),SP_FIRK(A1)  SP_DESC.FIRST_FREE_KNEL:=...
        SUBI.W   #(SE_SIZ-OB_SIZ),SP_FREK(A1)  SP_DESC.FREE_KNEL_BYTES:=...
        MOVEM.L  (A7)+,.MAKESEG        RESTORE REGS BEFORE PRT_MEM
        PRT_MEM  MAKESEGM,(A0),ALLOCSIZ ********
        RTS                            RETURN;
 
    PAGE
I_MMPROC   EQU *  START OF INITIALIZATION:
    XDEF   I_MMPROC     GETS CONTROL AFTER INIT OF THE PREFACE
    LEA    MMBUSERR,A0                A0:=BUSERROR EXECPTION HANDLER
    MOVE.L A0,12                      ADDRESS EXCEPTION :=
    MOVE.L A0,8                       BUSEXCEPTION:=BUSERROR EXCEPTION HANDLER
* THE BUSERROR EXCEPTION HANDLER GETS CONTROL IF A NON EXISTING ADDRESS
* OR UNEVEN ADDRESS IS REFERENCED BY THE MM_PROC INITIALIZATION PHASE.
*
* FORMAT THE LAST PART OF THE LAST RAM AREA.
* COMPUTE THE ADDRESS OF: PAGE_TAB, L_ALLOC, F_ALLOC, F_SUPVS, SUPVSTK
*
    MOVE.L KV_FRMEM,A0                A0:=RAM AREA LIST;
    MOVE.W (A0)+,D0                   D0:=NO_OF_RAM_AREAS;
    SUBQ.W #1,D0                      D0:=INDEX TO LAST ADDRESS PAIR
    ASL.W  #3,D0                        :=(NO_OF_RAM_AREAS-1) *8;
    MOVE.L 4(A0,D0.W),A1              A1:=TOP_LAST_RAM;
    MOVE.L A1,D1                      D1:=ALLOC_SPACE:=TOP_LAST_RAM
    SUB.L  (A0),D1                       -FIRST_FIRST_RAM;
 
  IF <LT> THEN.S                    IF ALLOC_SPACE <=0 THEN
    ERROR  3                          ERROR(3);
  ENDI                              ENDI
 
* THE PAGETABLE NEED NOT DESCRIBE THE MEMORY HOLDING THE PAGETABLE.
* THE TABLE CONTAINS 4 BYTES FOR EACH PAGE OF 256 BYTES, CONSEQUENTLY:
* TABSIZE *64+TABSIZE=ALLOC_SPACE<=>TABSIZE=ALLOC_SPACE DIV 65;
* DIVIDE ALLOC_SPACE (D1) WITH 65;
* TABSIZE MAY BE LARGER THEN 1<<16, HENCE SIMPLE DIVISION IS INSUFFICIENT.
*
    CLR.L  D2                         D2:=TABSIZE:=0;
    MOVEQ.L #65,D3                    D3:=65
    MOVE.L #65000,D4                  D4:=65000
    MOVE.L #4225000,D5                D5:=65x65000
  WHILE.L D1 <GT> D5 DO.S           WHILE ALLOC_SPACE > 65x65000 DO
    SUB.L  D5,D1                      ALLOC_SPACE:=...-65x65000;
    ADD.L  D4,D2                      TABSIZE:=TABSIZE+65000;
  ENDW                              ENDW
    PAGE
    DIVU   D3,D1                      D1:=DIVRES:=ALLOC_SPACE DIV 65
    CLR.L  D3                         D3:=
    MOVE.W D1,D3                      EXTEND DIVRES TO LONG
    ADD.L  D3,D2                      TABSIZE:=...+DIVRES;
    ADDQ.L #7,D2                      ROUND UP TABSIZE
    BCLR.L #0,D2                      TABSIZE MUST BE EVEN.
    SUBA.L D2,A1                      A1:=FIRST_ENTRY:=TOP_LAST_RAM-TABSIZE;
    MOVE.L A1,D1                      D1:=LAST_MEM_REC
    SUBI.L #MR_SIZ,D1                   :=FIRST_ENTRY-SIZE OF MEM_REC HEAD;
    CLR.B  D1                           ROUNDED TO A PAGE BOUNDARY;
    MOVE.L D1,L_ALLOC                 L_ALLOC:=LAST_MEM_REC;
    MOVE.L D1,SUPV_STK                TOP BYTE OF SUPV.STACK:=L_ALLOC
 
* SET UP THE SUPERVISOR STACK, SUBROUTINES MAY NOW BE CALLED.
    MOVE.L D1,A7                      A7:=STACK:=SUPV_STK;
    SUB.L  #KV_STKSIZ,D1              D1:=F_SUPVS:=SUPV_STK-STK_SIZE
    CLR.B  D1                           ROUNDED TO APAGE BOUNDARY;
    MOVE.L D1,F_SUPVS                 F_SUPVS:=D1;
 
* THE VARIABLE CALLED PAGE_TAB SHOULD CONTAIN THE ADDRESS OF "ENTRY ZERO" OF
* THE PAGE TABLE AND NOT "ENTRY FIRST" (=A1).
    MOVE.L (A0),D1                    D1:=FIRST_FIRST_RAM;
    LSR.L  #8,D1                      D1:=FANTOM_TABSIZE:=
    LSL.L  #2,D1                        FIRST_FIRST_RAM DIV 256*4;
    SUB.L  D1,A1                      A1:=PAGE_TAB:=ENTRY ZERO
    MOVE.L A1,PAGE_TAB                  :=ENTRY FIRST-FANTOM_TABSIZE;
    MOVE.L (A0),F_ALLOC               F_ALLOC:=FIRST_FIRSTRAM;
    PRT_MEM F_ALLOC_ETC.,F_ALLOC,MM_QUEUE-F_ALLOC ********
 
    PAGE
* SET UP THE INITIAL MEM_RECS.
* D0=INDEX TO LAST PAIR OF ADDRESS; A0=ADDRESS OF FIRST PAIR
    CLR.W  D1                         D1:=NEXT_PAIR:=FIRST_PAIR:=0;
    MOVE.L (A0),A1                    A1:=CUR_MEM_REC:=FIRST_FIRST_RAM;
    MOVE.L A1,D2                      D2:=FIRST_FREE:=FIRST_FIRST_RAM
    ADDI.L #MR_SIZ,D2                   +SIZE OF MEM_REC HEAD;
    MOVE.L D2,A4                      A4:=PRIOR_MEM_REC:=FIRST_FREE; "BAD TRICK"
  REPEAT                            REPEAT "ONE MEM_REC FOR EACH PAIR"
 
  IF.W D1 <NE> D0 THEN.S            IF CUR_PAIR <> LAST_PAIR THEN
    MOVE.L 4(A0,D1.W),D3              D3:=TOP_FREE:=TOP_RAM(NEXT_PAIR)
    SUBI.L #MR_SIZ,D3                   -SIZE OF MEM_REC HEAD
    CLR.B  D3                           ROUNDED TO A PAGE BOUNDARY;
    MOVE.L D3,A2                      A2:=NEXT_MEM_REC:=TOP_FREE;
    ADDQ.L #8,D1                      D1:=NEXT PAIR;
  ELSE.S                            ELSE
    MOVEQ.L #-1,D0                    D0:=STOP;
    MOVE.L L_ALLOC,A2                 A2:=NEXT_MEM_REC:=LAST_MEM_REC;
    MOVE.L F_SUPVS,D3                 D3:=TOP_FREE:=F_SUPVS;
  ENDI                              ENDI
 
* A1=CUR_MEM_REC,A2=NEXT_MEM_REC,A4=PRIOR_MEM_REC
* D2=FIRST_FREE,D1=NEXT_PAIR (OR LAST PAIR),D3=TOP_FREE
    PRT_REG INITIAL_MEMREC_D0/D1/D2/D3/A1/A2/A4 ********
  IF.L A1 <GE> D3 THEN.S            IF CUR > = NEXT THEN
    ERROR  4                          ERROR (4)
  ENDI                              ENDI;
    NEWHEAD  A1,#256,A4,A2             CREATE INITIAL MEM_REC HEAD
    SUB.L  D2,D3                      D3:=FREE_BYTES:=TOP_FREE-FIRST_FREE
    MOVE.L D2,MR_FST(A1)              ASSIGN FIRST_FREE TO MEM_REC;
    MOVE.L D3,MR_FRE(A1)              ASSIGN FREE_BYTES TO MEM_REC;
    PRT_MEM NEW_HEAD,(A1),MR_SIZ      ********
    MOVE.L A1,A4                      A4:=PRIOR_MEM_REC:=CUR_MEM_REC
    MOVE.L A2,A1                      A2:=CUR_MEM_REC:=NEXT_MEM_REC
    MOVE.L (A0,D1.W),D2               D2:=FIRST_FREE:=FIRST_RAM (NEXT_PAIR);
    TST.W  D0                         TEST FOR STOP;
  UNTIL <LT>                        UNTIL STOP;
* A1=LAST_MEM_REC,A4:=MEM_REC HOLDING SUPERVISOR STACK
* ADJUST THESE MEM_REC.
    MOVE.L A4,MR_PRV(A1)              LAST_MEM_REC.PREV:=PRIOR MEM_REC;
*                                     ASSIGN USER_TYPE TO
    MOVE.B #-1,MR_TYP(A4)             THE MEM_REC HOLDING THE SUPERVISOR STACK;
* ASSIGN KNOWN BUT ILLEGAL ADDRESSES TO UNDEFINED CHAINFIELDS.
    MOVE.L #$4711,MR_NXT(A1)          NEXT TO LAST:=ODD ADDRESS;
    MOVE.W #256,MR_FIX(A1)            L_ALLOC.FIX:=INITIAL;
    CLR.L  MR_FRE(A1)                 L_ALLOC.FREE_BYTES:=0;
    PRT_MEM LAST_HEAD,(A1),MR_SIZ     ********
    MOVE.L F_ALLOC,A1
    MOVE.L #$7913,MR_PRV(A1)          PRIOR TO FIRST:=UNEVEN ADDRESS;
    PRT_MEM FIRST_HEAD,(A1),MR_SIZ    ********
    PAGE
* CREATE ONE SEGMENT OBJECT THAT HAS THE BOOT LOADED MODULES
* AS SEGMENT DATA. THIS WILL PROTECT THE MODULES DURING THE INITIALIZATION;
* THE ALLOCOBJ IS USED AS THE KERNEL PART OF THIS SEGMENT OBJECT.
 
  IF.L KV_BOTLD <NE> #-1 THEN.S     IF MODULES HAVE BEEN BOOT LOADED THEN
 
* FIND THE MEM_REC THAT HOLDS THE BOOT LOADED MODULES. THIS MEM_REC IS
* CALLED "HOLDER". THE MODULES MUST BE PRESENT IN ONE OF THE MEM_RECS.
* THE MODULES HAVE BEEN DAMAGED BY THE PREVIOUS INITIALIZATION IF
* THEY WERE LOCATED TOO NEAR TO THE END (OR BEGINNING) OF THE
* RAM AREA THAT HOLDS THE MODULES.
*
    MOVE.L F_ALLOC,A1                 A1:=CUR_MEM_REC:=FIRST_MEM_REC
    MOVE.L KV_BOTLD,A3                A3:=BOOT LOADED MODULES;
  REPEAT                            REPEAT
    MOVE.L A1,A0                      A0:=HOLDER:=CUR_MEM_REC;
    MOVE.L (A1),A1                    A1:=CUR_MEM_REC:=NEXT_MEM_REC
  UNTIL.L A1 <GT> A3                  UNTIL CUR_MEM_REC > BOOT LOADED MODULES;
* A0 = HOLDER; FIND LENGTH OF BOOT LOADED MODULES
* A3=FIRST_MODULE
    MOVE.L A3,A1                      A1:=FIRST MODULE;
  REPEAT                            REPEAT
    MOVE.L MOD_SIZE(A3),D1            D1:=LENGTH OF NEXT MODULE
    ADDA.L D1,A3                      A3:=NEXT_MODULE:=...+LENGTH;
    TST.L  D1
  UNTIL <EQ>                        UNTIL LENGTH=0;
    LEA    256(A3),A3                 A3:=A3+256;"INCLUDE THE "ZERO" MODULE"
 
* A0=HOLDER,A1=FIRST_FIRST_MODULE,A3=TOP_LAST_MODULE,
* RESTRUCTURE THE MEM_REC IN A0; CREATE MEM_REC AT A3;
*
    PRT_REG BOOT_MODULE_A0/A1/A3      ********
    MOVE.L A3,D1                      D1:=TOP_LAST_MODULES;
    TST.B  D1                         TEST TOP_LAST_ADDRESS;
  IF <NE> THEN.S                    IF ADDRESS <> PAGE START THEN
    ERROR  1                          STOP WITH ERROR=1
  ENDI                              ENDI;
    SUB.L  A1,D1                      D1:=SIZE OF BOOT:=TOP_LAST-FIRST_FIRST;
    TST.B  D1                         TEST FIRST_FIRST_ADDRESS;
  IF <NE> THEN.S                    IF ADDRESS <> PAGE START THEN
    ERROR  2                          STOP WITH ERROR=2
  ENDI                              ENDI
    MOVE.W #$FF,D7                    D7:=USER PART:=FF
  IF.L D1 <GT> #256 THEN.S          IF REAL BOOT MODULES PRESENT THEN
    BSR.L  RECHAINC                   RECHAINC(A0=MEM_REC,A1=NEW_PART,
*                                       D1=SIZE OF PART, D7=TYPE);
  ENDI                              ENDI;
  ELSE.S                            ELSE "NO BOOT LOADED MODULES"
    CLR.L  D1                         D1:=SIZE:=NO_MODULE:=0;
  ENDI                              ENDI;
    MOVE.L #ALLOCSIZ,D0               D0:=SIZE OF KNELPART OF ALLOC
    LEA    ALLOCOBJ,A0                A0:=ADDRESS OF KNELPART OF ALLOC
    BSR.L  INITKNEL                   INITKNEL (A0=ALLOC,D0=SIZE_OF_ALLOC);
  IF.L D1 <GT> #256 THEN.S          IF BOOT LOADED MODULES THEN
    BSR.L  MAKESEGM                   MAKESEGM(A0=ALLOC,A1=BOOTMODULES,D1=SIZE)
  ENDI                              ENDI;
 
    PAGE
* CREATE FULSPACE OBJECT
    LEA    FULSPACE,A0                A0:=FULSPACE OBJECT
    MOVE.W #NEMB_SIZ,D0               D0:=LENGTH OF KERNEL PART
    BSR.L  INITKNEL                   INITIALIZE_KERNEL_PART_OF_OBJECT(A0,D0);
    MOVE.W #0,A1                      A1:=SEGMENT ADDRESS:=0;
    MOVE.L #$1000000,D1               D1:=SEGMENT LENGTH:=TOP OF ADDRESS SPACE
    BSR.L  MAKESEGM                   MAKE_SEGMENT OBJECT(A0=FULSPACE,A1=SEGMENT
*                                         START D1:=LENGTH);
 
*  INITIAL POINTER INIT
    LEA    KV_IOWNP,A0                A0:=INITIAL OWNER POINTER;
    MOVE.L A0,(A0)                    OWNER_SET:=EMPTY;
    MOVE.L A0,4(A0)
    CLR.B  PT_INF(A0)                 SCOPE:= INITIAL;  ???
 
    LEA    KV_IMANP,A0                A0:=INITIAL MANAGER POINTER;
    MOVE.L A0,(A0)                    MANAGER:= EMPTY;
    MOVE.L A0,4(A0)
    CLR.B  PT_INF(A0)                 SCOPE:= INITIAL;  ???
 
* VARIOUS GLOBAL VARIABLES
    CLR.B  MM_LOCK                    MM_LOCK := OPEN;
    LEA    MM_QUEUE,A0                MM_QUEUE := EMPTY;
    MOVE.L A0,(A0)
    MOVE.L A0,4(A0)
 
    CLR.L  CURKMOVE                   CLEAR MOVE CANDIDATE
    PRT_MEM MM_QUEUE_ETC.,MM_QUEUE,ALLOCOBJ-MM_QUEUE ********
*
    PAGE
* ALLOCATE STUB OBJECT
* THE STUB MODULE CAN BE FOUND IN KV_STUB (STUB IN ROM),
* OR (WHEN KV_STUB=-1) IN ALLOCOBJ.SE_FIR (STUB IN RAM).
    BSR.L  GET_STMD                   GET_STUB_MODULE (A5:=STUB_MODULE);
 
* COMPUTE THE SIZE OF THE STUB OBJECT IN D0
    ADDA.W MOD_PROG(A5),A5            A5:=PROGRAM DESCRIPTION IN STUB MODULE
    MOVE.W MOD_LOCS(A5),D1            D1:=NO_OF_LOCALS
  IF.W D1 <LT> #10 THEN.S           IF NO_OF_LOCALS < 10 THEN
    ERROR  8                            ERROR(8);
  ENDI                              ENDI;
    MOVEQ.L #PT_SIZ,D0                D0:=SIZE OF KERNEL PART:=POINTER_SIZE
    MULU.W D1,D0                      *NO_OF_LOCALS +
    ADDI.L #(SP_SIZ+GE_SIZ+EN_SIZ),D0 SPACE FOR GENERAL OBJECT;
    CLR.L  D1                         D1:=SIZE OF USER PART:=0;
    BSR.L  MM_CRE                     CREATE_OBJECT(D0=KERNEL_BYTES,D1=USER_BYTE
*                                     A0:=KERNEL_PART,A1:=USER_PART);
 
  IF <EQ> THEN.S                    IF NO_ROOM THEN
    ERROR  5                            ERROR(5)
  ENDI                              ENDI;
 
* INITIAL OWNER POINTER HOLDS THE STUB AS THE FIRST MEMBER.
    LEA    KV_IOWNP,A3                (A3,D3.W):=INITIAL OWNER POINTER
    CLR.W  D3
    BSR.L  CHAINOWN                   CHAINOWN(A0=STUB OBJECT,(A3,D3.W)=INITIAL
*                                       OWNER, A2:=INITIAL OWNER POINTER);
 
* CREATE PRIMARY ENVELOPE, A0=STUB,D0=KERNEL PART SIZE;
    SUBI.W #SP_SIZ+GE_SIZ,D0          D0:=SIZE OF ENVELOPE ELEMENT;
    BSR.L  MM_PUSHK                   PUSH_ENV(A0=KNELPART,D0=SIZE,
*                                        A3:=ENV,A6:=SPACEDESC);
 
    BSR.L  INIT_ENV                   INITIALIZE_ENV(D0=SIZE,A3=ENVELOPE,
*                                        A6=SPACE DESCR);
 
* INITIAL MANAGER POINTER HOLDS THE STUB ENVELOPE AS FIRST MEMBER.
    MOVE.L A3,A0                      A0:=ENVELOPE
    LEA    KV_IMANP,A3                A3:=INITIAL MANAGER POINTER, D3=0;
    BSR.L  CHAINMAN                   CHAINMAN(A0=ENVELOPE,(A3,D3.W)=
*                                        INITIAL MANAGER,
*                                        A2:=INITIAL MANAGER POINTER);
    PAGE
* INITIALIZE GENERAL PART OF STUB OBJECT
*
    BSR.L  GETSTUBO                   A0:=KERNEL PART OF STUB OBJECT;
    BSR.L  GET_STMD                   A5:=STUB MODULE ADDRESS;
    ADDA.W MOD_PROG(A5),A5            A5:=PROGRAM DESCR.OF STUB MODULE;
    MOVE.L MOD_ENT(A5),A1             A1:=ENTRY POINT OF STUB PROGRAM;
    BSR.L  INITGEIN                   INITGEIN (A0=KNELPART,A1:=ENTRY POINT);
 
* MODIFY STUB OBJECT ACCORDING TO THE DESCRIPTION.
    MOVE.W MOD_TEMP(A5),GE_TEMP(A0)   STUB.TEMPPOINTERS:=MODULE.TEMPPOINTERS
    ADDQ.W #1,GE_TEMP(A0)             STUB.TEMPPOINTERS:=...+1; "INCLUDE T(0)"
    MOVE.L MOD_STK(A5),D5             STUB.CALLSTK:=MODULE.CALLSTK
    MOVE.L D5,GE_STK(A0)              .
    MOVE.W MOD_STK+SZ_K(A5),D4        .
    MOVE.W D4,GE_STK+SZ_K(A0)         .
    EXT.L  D4                         D4.L/D5.L HOLDS STUB.CALLSTK
    MOVEM.L D4/D5,-(A7)               CALL STACK IS LEFT ON THE STACK
* IT IS PICKED UP WHEN THE BOOT PROCESS IS CREATED BY THE ENTER MODULE
    MOVE.L MOD_TEMD(A5),GE_TEMD(A0)   STUB.TEMPDATA:= MODULE.TEMPDATA;
 
    PAGE
* CREATE A SET OF NON EMBEDDED SEGMENT OBJECTS.
* EACH SEGMENT OBJECT CONTAINS A HEADER SEGMENT OR A LOAD SECTION
* FROM THE BOOT LOADED MODULES CURRENTLY "PROTECTED" BY THE ALLOC OBJECT.
* THE SEGMENT DATA OF THE ALLOC OBJECT SHRINKS AS THE NEW SET
* GROWS. THE SET IS OWNED BY LOCAL-2 OF THE STUB OBJECT.
* THE HEADER SECTIONS AS WELL AS THE LOAD SECTIONS MUST BE
* AN INTEGRAL NUMBER OF PAGES.
*
  IF.L KV_BOTLD <NE> #-1 THEN.L     IF RAM-BOOT IS THE CASE THEN
    BSR.L  GET_LDMD                   A5:=FIRST BOOT LOADED MODULE
  REPEAT                            REPEAT "FOR EACH MODULE DO"
  PRT_MEM HEADER_SECTION,(A5),300
* MOVE THE SIZE OF THE HEADER SECTION, AND OF THE ASSOCIATED
* LOAD SECTIONS, TO THE STACK.
    MOVE.L A7,A6                      A6:=OLD STACK TOP;
    MOVE.L MOD_SIZE(A5),A0            A0:=REST:=LENGTH OF TOTAL MODULE;
    MOVE.W MOD_HSIZ(A5),D0            D0:=LENGTH OF HEADER SECTION;
    TSTPAGE D0,6                      LENGTH SHOULD BE ON INTEGRAL #PAGES;
    SUBA.W D0,A0                      A0:=REST:=...-LENGTH OF HEADER;
    MOVE.W D0,-(A7)                   PUSH LENGTH OF HEADER SECTION;
    ADDA.W MOD_PROG(A5),A5            A5:=ADDRESS OF PROGRAM OBJECT DESCRIPTION;
    MOVE.W MOD_LDSS(A5),D5            D5:=#LOAD DATA SEGMENT DESCRIPTIONS;
    ADDA.W MOD_FDSD(A5),A5            A5:=FIRST LOCAL DATA SEGMENT DESCRIPTION;
  WHILE.W D5 <NE> #0 DO.S           WHILE MORE LOCAL DATA SEGM DESCR DO
    MOVE.W MOD_LSDS(A5),D4            D4:=#LOAD SECTION DESCRIPTIONS;
    LEA    MOD_LDSZ(A5),A4            A4:=FIRST LOAD SECTION DESCRIPTION;
  WHILE.W D4 <NE> #0 DO.S           WHILE MORE LOAD SECTION DESCR. DO
    MOVE.L MOD_SLEN(A4),D0            D0:=LENGTH OF LOAD SECTION;
    TSTPAGE D0,6                      LENGTH MUST BE AN INTEGRAL #PAGES;
    SUBA.L D0,A0                      A0:=REST:=...-LENGTH OF SECTION;
    MOVE.L D0,-(A7)                   PUSH LENGTH OF LOAD SECTION;
    SUBQ.W #1,D4                      D4:=REST #LOAD SECTION DESC:=...-1;
    LEA    MOD_LSDZ(A4),A4            A4:=ADDRESS OF NEXT LOAD SECTION DESCR;
  ENDW                              ENDW;
    SUBQ.W #1,D5                      D5:=REST #LOAD DATA SEFM DESCR:=..-1
    ADDA.W MOD_DSIZ(A5),A5            A5:=ADDRESS OF NEXT LOCAL DATA SEGM DESC.
  ENDW                              ENDW;
  IF.L A0 <NE> #0 THEN.S            IF REST <> 0 THEN
    ERROR  7                          ERROR 7; "MODULE JAM"
  ENDI                              ENDI;
    MOVE.L A6,-(A7)                   SAVE OLD STACK TOP AN TOP OF STACK;
 
    PAGE
* THE STACK CONTAINS A WORD AND A NUMBER OF LONGS.
* CREATE SEGMENT OBJECTS ACCORDING TO THESE VALUES.
    MOVE.L #NEMB_SIZ,D0               D0:=KNEL_PART SIZE FOR NON EMB SEGM;
    MOVE.W -(A6),D6                   D6:=SIZE OF FIRST SEGMENT;
    EXT.L  D6                         SIGN EXTEND D6
  REPEAT                            REPEAT  "FOR ALL VALUES DO"
    CLR.W  D1                         D1:=NO USER PART:=0;
    BSR.L  MM_CRE                     CREATEOBJ (D0=KNELSIZE, D1=USER SIZE,
*                                                A0:=KNELPART,A1:=USER PART);
  IF <EQ> THEN.S                    IF NO_ROOM THEN
    ERROR  8                            ERROR(8);
  ENDI                              ENDI;
    BSR.L  GET_LDMD                   A1:=A5:=PROTECTED BOOT MODULES;
    MOVE.L A5,A1
    MOVE.L D6,D1                      D1:=LENGTH OF SEGMENT
* REMAP DATA INTO NEW OBJECT
    BSR.L  MAKESEGM                   MAKESEGM (D1=LENGTH,A0=SEGM,A1=DATA)
* REMOVE DATA FROM THE PROTECTING ALLOC SEGMENT
    LEA    ALLOCOBJ,A3                A3:=ALLOCOBJ;
    ADD.L  D1,SE_FIR(A3)              ALLOCOBJ.FIRSTSEGMDATA:=...+LENGTH;
    SUB.L  D1,SE_LEN(A3)              ALLOCOBJ;.SEGMLENGTH:=...-LENGTH;
    MOVE.L OB_SPA(A3),A3              A3:=SPACE_DESCR OF ALLOCOBJ;
    ADD.L  D1,SP_FIRU(A3)             ALLOCOBJ.FIRST USER DTAT:=...+LENGTH;
    SUB.L  D1,SP_SIZU(A3)             ALLOCOBJ;.SIZE USER DATA:=...-LENGTH;
    PRT_MEM UNPROTECT,ALLOCOBJ,ALLOCSIZ ********
* CHAIN NEW SEGMENT OBJECT (A0) TO LOCAL-2 OF STUB ENVELOPE
    BSR.L  GETSTUBE                   A3:=STUB ENVELOPE;
    MOVE.W #EN_SIZ+PT_SIZ,D3          D3:=OFFSET TO LOCAL-2;
    BSR.L  CHAINOWN                   CHAINOWN(A0=OBJECT,A3/D3=POINTER,
*                                              A2:=UNDEF);
    MOVE.L -(A6),D6                   D6:=SIZE OF NEXT SEGMENT;
  UNTIL.L A6 <EQ> A7                UNTIL ALL SIZES HAVE BEEN USED;
    MOVE.L D6,A7                      A7:=OLD STACK TOP; "POP SIZE LIST"
    BSR.L  GET_LDMD                   A5:=ADDR OF PROTECTED MODULES;
    TST.L  MOD_SIZE(A5)               TEST FOR TERMINATING ZERO MODULE;
  UNTIL <EQ>                        UNTIL ZERO MODULE IS REACHED;
*
    LEA    ALLOCOBJ,A3                A3=ALLOCOBJ
    MOVE.L SE_LEN(A3),D1              D1=ALLOCOBJ.SEGMLENGTH;
  IF.L D1 <NE> #256 THEN.S          IF ALLOCOBJ <> ZERO MODULE THEN
    ERROR  7                          ERROR 7 "MODULE JAM"
  ENDI                              ENDI;
    MOVE.L A5,A1                      A1:=ADDRESS OF ZERO MODULE;
    BSR.L  REMOVEUS                   REMOVEUS(D1=SIZE,A1=USERPART,
*                                              A0:=UNDEF);
  ENDI                              ENDI;  "RAM-BOOT APPEARS"
    PAGE
* SET UP PROGRAM REF IN LOCAL-1 OF STUB.
*
  IF.L KV_STUB <NE> #-1 THEN.S      IF STUB MODULE IN ROM THEN
* CREATE A SUBSEGMENT OF THE FULSPACE SEGMENT, DESCRIBING THE
* LOCAL SECTION(S) OF THE MODULE.
    MOVE.L #SU_SIZ+SP_SIZ,D0          D0:=SIZE OF NON EMB SUB SEGM OBJ;
    CLR.L  D1                         D1:=NO USER PART:=0;
    BSR.L  MM_CRE                     CREATEOBJ (D0=KNELSIZE,D1=0,
*   A0:= OBJECT                                  A0:=KNELPART,A1:=UNDEF);
    MOVE.L KV_STUB,A1                 A1:=STUB MODULE
    MOVE.L MOD_SIZE(A1),A2            A2:=SIZE OF SECTIONS:=SIZE OF MODULE
    SUBA.W MOD_HSIZ(A1),A2               -SIZE OF HEADER;
    ADDA.W MOD_HSIZ(A1),A1            A1:=ADDRESS OF SECTIONS
*                                       :=ADDRESS OF MODULE+SIZE OF HEADER;
    MOVE.B #OB_SEOB,OB_KIN(A0)        OBJECT.KIND:=SEGMENT;
    MOVE.B #OB_SUBS,OB_STA(A0)        OBJECT.STATE:=SUBSEGMENT;
    CLR.W  SE_IO(A0)                  OBJECT.IOCOUNT:=0;
    MOVE.L A1,SE_FIR(A0)              OBJECT.FIRST SEGM DATA:=ADDRESS OF SECTION
    MOVE.L A2,SE_LEN(A0)              OBJECT.SEGMLENGTH:=SIZE OF SECTIONS;
    INIT_HEAD SE_WAIT(A0),A3          OBJECT.WAITING := EMPTY;
    BSR.L  GETSTUBE                   A3:=ENVELOPE OF STUB;
    MOVE.W #EN_SIZ,D3                 D3:=OFFSET TO LOCAL-1;
    BSR.L  CHAINOWN                   CHAINOWN(A0=SUBSEGM,A3=STUBENV
*                                              D3=LOCAL-1,A2:=UNDEF);
 
* LET THE SEGMENT POINTER OF THE SUBSEGMENT POINT TO THE FULSPACE SEGM OBJECT
    LEA    FULSPACE,A4                A4:=FULSPACE SEGM OBJECT
    LEA    SU_P(A0),A0                A0:=OBJECT.SEGM POINTER;
    BSR.L  SIMPLEPT                   SEGM POINTER(=A0)POINTS TO FULSPACE(=A4)
    MOVE.B #PT_SEG,PT_KIN(A0)         CHANGE POINTER KIND TO "SEGM POINTER";
  ELSE.S                            ELSE  "THE STUB MODULE IS IN RAM"
* THE SECOND OBJECT IN THE OWNER SET OF LOCAL-2 OF THE STUB WILL
* BE THE CODE SEGMENT. CREATE A SIMPLE POINTER TO THAT OBJECT.
    BSR.L  GETSTUBE                   A3:=STUB ENVELOPE
    LEA    EN_SIZ+PT_SIZ(A3),A0       A0:=LOCAL-2
    MOVE.L (A0),A4                    A4:=FIRST_IN_SET:=LOCAL-2.NEXT;
    MOVE.L (A4),A4                    A4:=CODE_SEGM_OBJ:=FIRST_IN_SET.NEXT;
    LEA    -PT_SIZ(A0),A0             A0:=LOCAL-1;
    BSR.L  SIMPLEPT                   LOCAL-1(=A0)POINTS TO CODE_SEGM(=A4)
  ENDI                              ENDI;
* LOCAL-6 OF THE STUB ENVELOPE SHOULD POINT TO THE FULSPACE OBJECT
* A0 = LOCAL-1
    LEA    5*PT_SIZ(A0),A0            A0:=LOCAL-6
    LEA    FULSPACE,A4                A4:=FULSPACE OBJECT
    BSR.L  SIMPLEPT                   LOCAL-6(=A0)POINTS TO FULSPACE(=A4)
 
    PAGE
* INITIALIZE ALLOCOBJ AS THE INITIAL ALLOC OBJECT OF THE KERNEL.
*
    LEA    ALLOCOBJ,A0                A0:=KNELPART OF ALLOC;
    MOVE.L #ALLOCSIZE,D0              D0:=SIZE OF KNELPART;
    BSR.L  INITKNEL                   INITIALIZE ALLOC OBJ;
    MOVE.B #OB_ALLO,OB_KIN(A0)        OBJECT.KIND:=ALLOCATE;
    MOVE.B #1<<OB_REEN,OB_STA(A0)     OBJECT.STATE:=...+ ALLOC IS REENTRANT;
    PRT_MEM FINAL_ALLOC,(A0),ALLOCSIZ ********
 
* LOCAL-3 OF THE STUB SHOULD POINT TO THE ALLOC OBJECT
*
    MOVE.L A0,A4                      A4:=ALLOC OBJECT
    BSR.L  GETSTUBE                   A3:=STUB ENVELOPE
    LEA    2*PT_SIZ+EN_SIZ(A3),A0     A0:=LOCAL-3 OF STUB
    BSR.L  SIMPLEPT                   LOCAL-3(=A0) POINTS TO ALLOC(=A4);
* LOCAL 4,5,7,8 AND 9 ARE INITIALIZED BY THE SCHEDULER, LOCAL 10 BY ENTER.
 
    PRT_MEM END_OF_MMPROCS,(A7),8
    BRA.L  I_SCHED     CONTINUE IN KNELOP INITIALIZATION
* END OF SECTION INITIALIZE.
 
    PAGE
    SECTION   9                     = RUNTIME
 
* PROCEDURES DEFINED IN OTHER MODULES:
 
    XREF   9:KNELWAIT,KNELSIGN,TIMCHECK   PROCS OF SCHED
    XREF   9:C_PT,C_SUB,CLR_SMP           PROCS OF KNELOP
 
 
 
MM_POPU    EQU *   RELEASE THE SEGMENT DATA OF A SEGMENT OBJECT
* A0       KERNEL PART OF OBJECT      SAME
* A4       SEGMENT OBJECT             SAME
* D0.L     SIZE OF SEGMENT (INTEGRAL NO OF PAGES)  SAME
* A6       ANY                        SPACE DESCR OF OBJECT
* THE SEGMENT DATA MUST BE ON TOP OF THE DATA STACK OF THE OBJECT (A0)
*
* FUNCTION
* UPDATES THE SPACE DESCRIPTION OF THE OBJECT (SP_FREU IS INCREASED)
* IF THE SEGMENT OBJECT EQUALS THE CURRENT USER MOVE CANDIDATE, THE
* CURRENT USER MOVE CANDIDATE IS SET TO ZERO, AND
* SP_FIRU IN THE SPACE DESCR IS SET TO FUTURE USER DESTINATION.
* CURRENT USER MOVE CANDIDATE AND FUTURE USER DESTINATION ARE
* GLOBAL VARIABLES OF MM_PROCS (CURUMOVE AND USERDEST)
 
    MOVEA.L OB_SPA(A0),A6             A6:=SPACE DESCR OF OBJECT;
    ADD.L  D0,SP_FREU(A6)             FREE_USER_BYTES:=...+SIZE OF SEGMENT
  IF.L CURUMOVE <EQ> A4 THEN.S      IF CURRENT_USER_MOVE_CANDIDATE=SEGMENT THEN
    PRT_REG **********A4=CORUMOVE_RELEASE  ********
    CLR.L  CURUMOVE                   CURRENT_USER_MOVE_CANDIDATE:=NOMOVE:=0,
    MOVE.L USERDEST,SP_FIRU(A6)       FIRST_FREE_USER_BYTE:=FUTURE USER DESTINAT
  ENDI                              ENDI;
  RTS                               RETURN;
 
 
MM_PUSHU   EQU *  ALLOCATE SEGMENT DATA IN THE USER PART
*          CALL:                      RETURN:
* A0       KERNEL PART OF OBJECT      SAME
* D0.L     NO OF USER BYTES (INTEGRAL NO OF PAGES)  SAME
* A4       ANY                        SEGMENT DATA ADDRESS OR UNDEF
* A6       ANY                        SPACE DESCR OF OBJECT
* CCR                                 <EQ>: ERROR; <NE>: OK
*
* UPDATES THE SPACE DESCR OF THE OBJECT (SP_FREU IS REDUCED),
* AND RETURNS THE START ADDRESS OF THE SEGMENT.
* THE ADDRESS OF THE SEGMENT IS COMPUTED AS FOLLOWS:
*   SP_FREU:=SP_FREU-SIZE_OF_NEW_SEGMENT;
* A4 := SP_FIRU + SP_FREU;
*
    MOVE.L D0,-(A7)                   SAVE D0
    MOVE.L OB_SPA(A0),A6              A6:=SPACE DESCRIPTION;
    NEG.L  D0                         D0:=NEW_FREE_USER_BYTES:=
    ADD.L  SP_FREU(A6),D0             FREE_USER_BYTES-SIZE_OF_NEW_SEGMENT;
 IF <LT> THEN.S                    IF NEW_FREE_USER_BYTES <0 THEN "NO ROOM"
    CMP.W  D0,D0                      CCR:=NO ROOM:= <EQ>;
  ELSE.S                            ELSE  "ROOM IS AVAILABLE"
    MOVE.L D0,SP_FREU(A6)             FREE_USER_BYTES:=NEW_FREE_USER_BYTES=D0;
    ADD.L  SP_FIRU(A6),D0             A4:=SEGMENT ADDRESS;
    MOVE.L D0,A4                      CCR:=OK:=<NE>
  ENDI                              ENDI;
    MOVEM.L (A7)+,D0                  RESTORE D0;
    RTS                               RETURN;
 
    PAGE
MM_PUSHK   EQU *  CREATE A STACK ELEMENT IN OBJECT
*          CALL:                      RETURN:
* A0       KERNEL PART TO PROVIDE THE SPACE SAME
* A3       ANY                        STACK ELEMENT OR UNDEF
* D0.W     SIZE OF STACK ELEMENT INCL. POINTERS  SAME
* A6       ANY                        SPACE DESCR OF OBJECT
* CCR                                 <EQ>:ERROR,<NE>:OK
*
* CREATES A STACK ELEMENT AND RETURNS ITS ADDRESS (IF RESOURCES
* ARE AVAILABLE). ST_HOLD OF THE ELEMENT EQUALS A0.
* ACTUAL CHAIN OF THE STACK ELEMENT IS INITIALIZED TO EMPTY
* ST_TOP EQUALS D0.W
* FIRST_EMBEDDED EQUALS 0
* RESIDENT_COUNT IS ZERO
* IO_COUNT IS ZERO
* THE ADDRESS OF THE STACK ELEMENT WILL BE COMPUTED AS FOLLOWS.
*   SP_FREK:=SP_FREK-D0.W;
*   A3:=SP_FIRK+SP_FREEK;
*
 
    MOVE.L OB_SPA(A0),A6              A6:=SPACE DESCRIPTION;
    MOVEA.W SP_FREK(A6),A3            A3.L:=FREE BYTES IN THE KERNEL PART;
    SUBA.W D0,A3                      A3.L:=NEW_FREE_BYTES:=FREE_BYTES-SIZE;
  IF.W A3 <LT> #0 THEN.S            IF NEW_FREE_BYTES <0 THEN
    CMP.W  D0,D0                      CCR:=NO_ROOM:=<EQ>
  ELSE.S                            ELSE  "CREATE STACK ELEMENT"
    MOVE.W A3,SP_FREK(A6)             FREE_BYTES:=NEW_FREE_BYTES;
    ADDA.L SP_FIRK(A6),A3             A3:=ADDRESS_OF_STACK_ELEMENT:=FREE_BYTES+
*                                     FIRST_FREE_KERNEL_BYTE
* INITIALIZE FIELDS OF THE NEW STACK ELEMENT
    CLR.W  ST_FIR(A3)                 STACK.FIRST_EMB:=0;
    MOVE.W D0,ST_TOP(A3)              STACK.TOP_POINTERS:=SIZE;
    CLR.W  ST_RES(A3)                 STACK.RESIDENT_COUNT:=0;
    CLR.W  ST_IO(A3)                  STACK.IO_COUNT:=0;
    LEA    ST_ACT(A3),A3              A3:=ACTUAL CHAIN HEAD;
    MOVE.L A3,(A3)                      :=EMPTY;
    MOVE.L A3,4(A3)
    LEA    -ST_ACT(A3),A3             A3:=ADDRESS OF STACK ELEMENT;
    MOVE.L A0,ST_HOLD(A3)             STACK.HOLDING_OBJECT:=KERNEL PART;
*                                     CCR:=<NE>;
  ENDI                              ENDI;
    RTS                               RETURN;
    PAGE
MM_POPK    EQU *  POPS A STACK ELEMENT FROM AN OBJECT
*
*          CALL:                      RETURN:
* A3       ELEMENT TO BE POPPED       PRIOR ELEMENT
* A6       ANY                        SPACE DESCR OF OBJECT
 
* THE ELEMENT SIZE IS DESCRIBED IN THE ELEMENT (ST_TOP-ST_FIR)
* THE AMOUNT OF FREE MEMORY IS INCREASED (SP_FREK)
* THE STACK ELEMENT IS UNCHAINED FROM THE STACK CHAIN.
* IF THE STACK ELEMENT EQUALS THE CURRENT_KERNEL_MOVE_CANDIDATE,
* THE CURRENT KERNEL MOVE CANDIDATE IS CHANGED TO POINT TO PRIOR
* ELEMENT. CURRENT KERNEL MOVE CANDIDATE IS A GLOBAL
* VARIABLE OF MM_PROC (CURKMOVE).
 
    ENTRYSAV D0/A0/A1,.MM_POPK        SAVE REGISTERS;
    MOVEA.L ST_HOLD(A3),A6            A6:=KERNEL PART OF OBJECT HOLDING ELEMENT;
    MOVEA.L OB_SPA(A6),A6             A6:=SPACE DESCR OF OBJECT;
    MOVE.W  ST_TOP(A3),D0             D0:=SPACE_FOR_STACK_ELEMENT
    SUB.W   ST_FIR(A3),D0               + SPACE_FOR_SEGM_DESCRIPTIONS;ALWAYS 0?
    ADD.W   D0,SP_FREK(A6)            FREE_KERNEL_BYTES:=FREE_KERNEL_BYTES+
*                                     SPACE_FOR_STACK_ELEMENT+SPACE_FOR_SEGM_DE
* REMOVE ELEMENT FROM ITS STACK (ENV OR CTX).
    LEA     ST_STK(A3),A3             A3:=STACK CHAIN ELEMENT;
    MOVEM.L (A3),A0/A1                A0:=HEAD OF STACK:=ELEMENT.NXT;
*                                     A1:=PRIOR STACK ELEMENT;
    MOVE.L A0,(A1)                    PRIOR.NXT:=HEAD OF STACK;
    MOVE.L A1,4(A0)                   HEAD_OF_STACK.PRV:=PRIOR STACK ELEMENT;
   IF.L CURKMOVE <EQ> A3 THEN.S     IF CURRENT CANDIDATE=POPPED ELEMENT THEN
    PRT_REG **********A3=CURKMOVE_RELEASED ********
    MOVE.L A1,CURKMOVE                CURRENT CANDIDATE:=PRIOR ELEMENT;
  ENDI                              ENDI;
    LEA    -ST_STK(A1),A3             A3:=FIRST OF PRIOR ELEMENT;
    RETURN .MM_POPK                   RETURN;
 
    PAGE
MM_PUSHD   EQU *  PUSH DESCRIPTOR ON TOP OF STACK
*          CALL:                      RETURN:
* A0       KERNEL PART                SAME
* D0.W     SE_SIZ OR SU_SIZ           SAME
* A5       ANY                        SEGMENT/SUBSEGMENT DESCRIPTOR ADDR.
* A6       ANY                        SPACE DESCR
* CCR                                 <EQ>:ERROR,<NE>:OK
* THE DESCRIPTOR IS NOT INITIALIZED IN ANY WAY, SPACE DESCR IS UPDATED
* ST_FIR OF THE STACK ELEMENT IS UPDATED BY THE CALLER.
 
    MOVE.W D0,-(A7)                   SAVE DO.W;
    MOVEA.L OB_SPA(A0),A6             A6:=SPACE DESCRIPTION
    NEG.W  D0                         D0:=NEW_FREE_KNEL_BYTES:=
    ADD.W  SP_FREK(A6),D0             FREE_KERNEL_BYTES-SIZE_OF_SEGMENT_DESC;
  IF <LT> THEN.S                    IF NEW_FREE_KNEL_BYTES <0 THEN "NO ROOM"
    MOVE.W (A7)+,D0                   RESTORE D0.W
    CMP.W  D0,D0                      CCR:=NO_ROOM:=<EQ>;
  ELSE.S                            ELSE
    MOVE.W D0,SP_FREK(A6)             FREE_KNEL_BYTES:=NEW_FREE_KNEL_BYTES;
    MOVEA.L SP_FIRK(A6),A5            A5:=SEGM_DESC_ADDR:=
    ADDA.W D0,A5                      FIRST_FREE_ADDR+FREE_KNEL_BYTES;
    MOVE.W (A7)+,D0                   RESTORE D0.W; CCR:= OK := <NE>;
  ENDI                              ENDI;
    RTS                               RETURN;
 
MM_POPD    EQU *  POP DESCRIPTOR FROM TOP OF STACK
*          CALL:                      RETURN:
* D0.W     SE_SIZ OR SU_SIZ           SAME
* A0       KERNEL PART                SAME
* A5       DESCRIPTOR ADDRESS         PREVIOUS DESCRIPTOR ADDRESS
* A6       ANY                        SPACE DESCR
*
* RELEASE THE DESCRIPTOR, UPDATE SPACE DESCR.
* ST_FIR IS UPDATED BY THE CALLER.
 
    MOVE.L OB_SPA(A0),A6              A6:=SPACE DESCRIPTOR;
    ADD.W  D0,SP_FREK(A6)             FREE_KNEL_BYTES:=...+SIZE OF SEGM DESCR;
    ADD.W  D0,A5                      A5:=PREVIOUS_DESCR_ADDR:=
*                                       DESCR_ADDR+SIZE OF SEGM DESCR;
    RTS                               RETURN
    PAGE
MM_PUSHO   EQU *  ALLOCATE OBJECT-TYPE RELATED INFORMATION IN KNEL PART
*          CALL:                      RETURN:
* A0       KERNEL PART                SAME
* D0.W     SPACE NEEDED (E.G.:GE_SIZ-OB_SIZ) UNDEF
* A6       ANY                        SPACE DESCR
* CCR                                 <EQ>:ERROR,<NE>:OK
* SPACE IS ALLOCATED IN THE BEGINNING OF THE KERNEL PART.
    MOVEA.L OB_SPA(A0),A6             A6:=SPACE_PART
  IF.W D0 <GT> SP_FREK(A6) THEN.S  IF SPACE NEEDED > FREE KERNEL BYTES THEN
    CLR.W  D0                         D0:=NO ROOM:=0; CCR:=NO_ROOM:=<EQ>;
  ELSE.S                            ELSE
    SUB.W  D0,SP_FREK(A6)             FREE KERNEL BYTES:=...-SPACE NEEDED
    EXT.L  D0                         D0.L:=BYTES NEEDED
    ADD.L  D0,SP_FIRK(A6)             FIRST FREE KNEL BYTE:=...+SPACE NEEDED
*                                     CCR:= OK:= <NE>;
  ENDI                              ENDI;
    RTS                               RETURN;
 
MM_POPO    EQU *  RELEASE OBJECT_TYPE RELATED INFORMATION FROM KERNEL PART
*
*          CALL:                      RETURN:
* A0       KERNEL PART                SAME
* D0       SPACE RELEASED             SAME
* A6       ANY                        SPACE DESCR
* SPACE IS RELEASED IN THE BEGINNING OF THE KERNEL PART
*
 
    MOVE.L OB_SPA(A0),A6              A6:=SPACE DESCRIPTION;
    ADD.W  D0,SP_FREK(A6)             FREE KERNEL BYTES:=...+SPACE RELEASED;
    EXT.L  D0                         D0.L:=BYTES RELEASED;
    SUB.L  D0,SP_FIRK(A6)             FIRST FREE KNEL BYTES:=...-SPACE RELEASED;
    RTS                               RETURN;
 
******* SPECIAL TEST OUTPUT PROCEDURES
MEMPLUS EQU *   D0=USER,D1=KNEL
    MOVEM.L D0/D1/A0/A2,-(A7)     SAVE A0/A2, PUSH USER AND KNEL
    LEA     8(A7),A2              A2:= TOP OF SAVED D1
    ADD.L   D1,D0                 D0:=SUM
    MOVE.L  KV_FRMEM,D1           D1:=FREE MEMORY
    MOVEM.L D0/D1,-(A7)           PUSH SUM AND FREE
    MOVE.L  #$DDDDDDDD,D0         D0:= D DOR DEALLOC
    MOVE.L  D0,-(A7)
    MOVE.L  D0,-(A7)
    MOVE.L  A7,A0
    BSR.L   PRINTA02              TESTOUTPUT(A0=FIRST,A2=TOP,D0/D1/A0:=?);
    LEA     -8(A2),A7             A7:= TOP OF SAVED REGISTERS
    MOVEM.L (A7)+,D0/D1/A0/A2     RESTORE ALL REGS: D0/D1/A0/A2
    RTS
MEMMINUS EQU *  D0=KNEL,D1=USER,D2=SUM,D3=FREE
    EXG.L   D0,D1                 D0:=USER,D1:=KNEL
    MOVEM.L D0/D1/A0/A2,-(A7)     SAVE A0/A2, PUSH USER AND KNEL
    LEA     8(A7),A2              A2:= TOP OF SAVED D1
    MOVEM.L D2/D3,-(A7)           PUSH SUM AND FREE
    MOVE.L  #$AAAAAAAA,D0         D0:= A FOR ALLOC
    MOVE.L  D0,-(A7)
    MOVE.L  D0,-(A7)
    MOVE.L  A7,A0
    BSR.L   PRINTA02              TESTOUTPUT(A0=FIRST,A2=TOP,D0/D1/A0:=?);
    LEA     -8(A2),A7             A7:= TOP OF SAVED REGS
    MOVEM.L (A7)+,D0/D1/A0/A2     RESTORE ALL REGS: D0/D1/A0/A2
    EXG.L   D0,D1                 D0:=KNEL,D1:=USER
    RTS
 
    PAGE
ALLOC_OB   EQU *  ENTRY POINT FOR OBJ_CALL TO ALLOC OBJECT
*          CALL:                      RETURN:
* A0       EXTENSION (NOT USED)       SAME
* A2       CUR_CTX                    SAME
* A4       ARGUMENT                   UNDEF
* A5       ALLOC OBJECT               UNDEF
* D3       CALLER (=0 FOR NORMAL OBJECT) UNDEF
* D4       LOW_ARG                    UNDEF
* D5       FUNCTION                   UNDEF
* D6/D7    ARG#/UNDEF                 RESULT
* CCR                                 <EQ>: RETURN TO CALLING CONTEXT;
* OTHER REGISTERS ARE UNDEF AT RETURN
* ONLY ONE ALLOC OBJECT IS PRESENT IN THE SYSTEM
*
    LSL.W  #1,D5                      D5:=FUNC:=FUNCTION *2
    CMP.W  #4,D5                      TEST FUNCTION < 2<<1
    BGT.S  ER_FUILL                 WHEN FUNC >4 GOTO FUNCTION_ILLEGAL;
    TST.W  D3                         TEST THE CALLER
    BNE.S  ER_FUILL                 WHEN CALLER <> NORMAL_CTX GOTO FUNCTION_ILL
    CASEJMP D5                        GOTO CASE FUNC OF
    CASELAB ER_FUILL                  0<<1: NOT POSSIBLE
    CASELAB NEW_OBJ                   1<<1: NEW_OBJ
    CASELAB ER_FUILL                  2<<1: FUNCTION_ILLEGAL "GET AMOUNT?"
 
ER_ADDR    EQU *                  ADDRESS ILLEGAL:
    MOVE.L        #ECRADDR,D7         D7:=RESULT:= ADDRESS ILLEGAL;
    BRA.S  ALREJECT                   GOTO RETURN_REJECT;
 
ER_FUILL   EQU *                  FUNCTION_ILLEGAL:
    MOVE.L        #ECRFUNC,D7         D7:=RESULT:= FUNCTION ILLEGAL;
    BRA.S  ALREJECT                   GOTO RETURN_REJECT;
 
ER_ARGMIS  EQU *                  ARGUMENTS_MISSING:
    MOVE.L        #ECRARGMIS,D7       D7:=RESULT:= ARGUMENTS MISSING;
    BRA.S  ALREJECT                   GOTO RETURN_REJECT;
 
ER_VAL3MIS EQU *
    MOVEQ.L #3,D6                     D6:=ARG# OF VALUE SUB SEGM ARGUMENT;
ER_VALMIS  EQU *                  TOO_FEW_VALUE_PARAMS:
    MOVE.L        #ECRVALMIS,D7       D7:=RESULT:= VALUE PARAMETERS MISSING;
    BRA.S  ALREJECT                   GOTO RETURN_REJECT;
 
ER_DAILL   EQU *                  DATA_ILLEGAL:
    MOVE.L        #ECRDATA,D7         D7:=RESULT:= DATA VALUE ILLEGAL;
*   BRA.S  ALREJECT                   GOTO RETURN_REJECT;
ALREJECT   EQU *                  RETURN_REJECT:
    CMP.W  D0,D0                      CC := RETURN := <EQ>;
    RTS                               RETURN; "TO CALLING CONTEXT"
 
    PAGE
NEW_OBJ    EQU *  CREATE NEW OPEN OBJECT.
*
* GET AND CHECK VALUE DATA
    LEA    -AR_SIZ<<1(A4),A1          TEST #REMAINING ARGUMENTS >=2;
    CMPA.L D4,A1                      TEST (ARG-(2*ACT_SIZE)) - LOWARG;
    BLT.S  ER_ARGMIS                WHEN REMAINING <2 GOTO ARGUMENTS_MISSING;
    TST.W  -2(A4)                     TEST FOR VOID VALUE DATA
    BEQ.S  ER_VAL3MIS               WHEN VOID GOTO TOO_FEW_VALUE_PARAMS;
    BSR.L  C_SUB                      CHECK_SUB(A5:=BSEG,D0:=T_REL,D2:=LENGTH,.
    BEQ.S  ALREJECT                 WHEN NOT OK GOTO RETURN_REJECT;
    MOVE.L SE_FIR(A5),A6              A6:=FIRST OF BASE SEGMENT DATA
    ADD.L  A6,D0                      D0:=FIRST_VALUE:=T_REL+FIRST_DATA;
    BTST.L #0,D0                    IF FIRST_VALUE ADDRESS IS ODD
    BNE.S  ER_ADDR                  THEN REJECT;
    BTST.L #0,D2                    IF LENGTH OF VALUE SEGM IS ODD
    BNE.S  ER_ADDR                  THEN REJECT;
    MOVE.L D0,A6                      A6:=FIRST_VALUE;
    CMP.L  #14,D2                     TEST LENGTH-14; "14 BYTES REQUIRED"
    BLT.S  ER_VALMIS                WHEN LENGTH <14 GOTO TOO_FEW_VALUE_PARAMS
    MOVEQ.L #0,D6                     D6:=FIRST VALUE PARAMETER IDENT;
    SUBQ.B #1,D6                      .
    MOVE.W -(A6),D0                   D0:=LENGTH OF FIRST VALUE;
    CMP.W  #6,D0                      TEST LENGTH OF FIRST VALUE;
    BNE.S  ER_DAILL                 WHEN LENGTH <> 6 GOTO DATA_VALUE_ILLEGAL;
    MOVEQ.L #1,D0                     D0.L:=ROUNDUP BIT;"KNEL_BYTES MAY BE ODD"
    ADD.W  -(A6),D0                   D0.L:=KNEL_BYTES+1; "CALL VALUE"
    BLE.S  ER_DAILL                 WHEN KNEL_BYTES+1 <= 0 GOTO DATA_ILLEGAL;
    BCLR.L #0,D0                      D0:= EVEN KNEL_BYTES;
    MOVE.L -(A6),D1                   D1.L:=USER_BYTES; "CALL VALUE"
    BLT.S  ER_DAILL                 WHEN USER_BYTES <0 GOTO DATA_ILLEGAL;
    ADD.L  #255,D1                    ADJUST USER_BYTES, TO AN
    CLR.B  D1                         INTEGRAL NUMBER OF PAGES
    TST.L  D1
    BLT.S  ER_DAILL                 WHEN USER_BYTES <0 GOTO DATA_ILLEGAL;
    ADD.L  #(OB_SIZ+SP_SIZ),D0        KNEL_BYTES:=... + SIZE OF MINIMAL OBJECT;
* A6 POINTS NEXT TO THE RETURN VALUE
    SUBQ.B #1,D6                      D6:=SECOND VALUE PARAMETER ARG#
    MOVE.W -(A6),D2                   D2:=VALUE_LENGTH OF RETURN VALUE;
    CMP.W  #4,D2                      TEST VALUE_LENGTH;
    BNE.S  ER_DAILL                 WHEN VALUE_LENGTH <>4 GOTO DATA_ILLEGAL;
    MOVE.L D1,D2                      D2:=BYTES_NEEDED:=KNEL_BYTES
    ADD.L  D0,D2                        + USER_BYTES;
    MOVE.L D2,-(A6)                   RETURN_VALUE:=BYTES_NEEDED;
    MOVE.L -(A4),D3                   D3:="ADDRESS OF FUTURE OWNER POINTER"
*   MOVE.W -(A4),D4   SE NOTE BELOW   D4:= CALL/RETURN;
    TAS.B  MM_LOCK                    LOCK THE MM_LOCK;
  IF <NE> THEN.S                    IF MM_LOCK ALREADY LOCKED THEN
    MOVEM.L D0/D1/D2/D3,-(A7)         SAVE SIZE AND POINTER ADDRESS ON STAC
    LEA    MM_QUEUE,A1                A1:=MM_QUEUE
    BSR.L  KNELWAIT                   MM_QUEUE.WAIT;
    MOVEM.L (A7)+,D0/D1/D2/D3         RESTORE SIZE AND POINTER ADDRESS
  ENDI                              ENDI;
    MOVE.L D3,SAVE_RPA                SAVE OWNER POINTER ADDRESS;
    MOVE.L KV_FRMEM,D3                D3:=FREE_BYTES:=FREE_BYTES
    PRT_REG NEW_OBJ_D0/D1/D2/D3       ********
*
* SPECIAL TESTOUTPUT FROM MEMORY ALLOCATION
    BTST.B #0,TSTSTATU+1
  IF <NE> THEN.S
    BSR.L MEMMINUS                    MEMMINUS(D0=KNEL,D1=USER,D2=SUM,D3=FREE);
  ENDI
*
    SUB.L  D2,D3                        -BYTES_NEEDED;
  IF <LT> THEN.S                    IF BYTES_NEEDED NOT AVAILABLE THEN
AL_NORES   EQU *                  NO_RESOURCES:
    MOVE.L        #ECRNO_RES,D7       D7:=RESULT:=NO_RESOURCES;
    BRA.S  ALRETURN                   GOTO ALLOC_RETURN;
  ENDI                              ENDI;
* THE TEST BELOW IS NOT EFFECTIVE !!! D4 CANNOT BE USED TO HOLD THIS SINGLE
* BIT OF INFORMATION ACROSS THE KNELWAIT ABOVE. ONLY 4 REGISTERS CAN BE SAVED
* AT THIS POINT. THE BIT CAN BE ENCODED AS SIGNBIT OF SOM OF THE OTHER REGS.
*   BTST.L #1,D4                      TEST THAT RETURN BIT IS SET IN THE ACTUAL
* IF <EQ> THEN.S                    IF ACTUAL IS NOT RETURN ACTUAL THEN
*   MOVEQ.L #,D7                      RESULT:= OK;
*   BRA.S ALRETURN                    GOTO ALLOC_RETURN;
* ENDI                              ENDI;
 
    MOVE.L D2,SAVENEED                SAVE BYTES_NEEDED
* TRY TO ALLOCATE THE OBJECT
*
    BSR.L  MM_CRE                     MM_CRE(D0/D1=SIZE,A0/A1:=OBJECT)
    BEQ.S  AL_NORES                   WHEN NOT OK GOTO NO_RESOURCES;
    MOVE.L KV_CTX,A2                  A2:=CUR_CTX;
    TST.L  CT_OBJ(A2)                 TEST FOR DUMMYFIED.
    BEQ.S  REM_DUM                  WHEN CTX DUMMY GOTO DUMMYFIED
    MOVE.W #4+SAVE_RPA,A4             A4.L:=TOP ADDR OF SAVED ARG;
    MOVEQ  #3,D6                      D6:=#PREVIOUSLY CHECKED ARGS:=3;
    BSR.L  C_PT                       CHECK_POINTER(A6:=PT,D2:=KIND,...
    BEQ.S  REM_PTE                  WHEN NOT OK GOTO POINTER_ERROR;
    CMP.W  #PT_OWN,D2                 TEST POINTER KIND
  IF <EQ> THEN.S                    IF OWN_SET POINTER THEN "DO NOTHING"
  ELSE.S                            ELSE
  IF <LT> THEN.S                    IF SIMPLE POINTER THEN
    BSR.L  CLR_SMP                    CLEAR_POINTER(A1,D1=PT,...
    MOVE.B #PT_OWN,PT_KIN(A6)         POINTER.KIND:=OWN_SET;
  ELSE.S                            ELSE
    BRA.S  REM_PTV                    GOTO POINTER_VALUE_ILL;
  ENDI                              ENDI;
  ENDI                              ENDI;
    MOVE.L A6,A3                      A3:=POINTER CHAIN
    INS_ELEM A3,A0,A6,A3/A6           CHAIN OBJECT TO POINTER
    MOVE.L A1,OB_OWN(A0)              OBJECT.OWNER:=
    MOVE.W D1,OB_OFF(A0)              (POINTER STRUCTURE,POINTER OFFSET);
    MOVE.L SAVENEED,D7                D7:= BYTES_NEEDED;
    SUB.L  D7,KV_FRMEM                FREE_BYTES:=...-BYTES_NEEDED;
    CLR.L  D7                         D7:=RESULT:=OK;
ALRETURN   EQU *  RETURN FROM ALLOC  (MM_LOCK HAS BEEN LOCKED)
    LEA    MM_QUEUE,A1                A1:=MM_QUEUE;
    BSR.L  KNELSIGN                   MM_QUEUE.SIGNAL;
  IF <NE> THEN.S                    IF NO PROC SCHEDULED THEN
    BCLR.B #7,MM_LOCK                 OPEN THE MM_LOCK;
  ENDI                              ENDI;
    MOVE.L KV_PROC,A1                 A1:=CURRENT PROCESS
    MOVE.L EX_EXT+4(A4),A1            A1:=CURRENT EXTENSION MEMBER;
    LEA    -EX_EXT(A1),A0             A0:=ADDRESS OF CUR_EXTENSION;
    CMP.W  D0,D0                      CCR:=<EQ>; RETURN TO CALLER;
    RTS                               RETURN;
 
    PAGE
REM_DUM    EQU *                  DUMMYFIED: "D7 NEED NOT BE ASSIGNED"
REM_PTE    EQU *                  POINTER_ERROR:
    MOVE.L A0,A1                      A1:=KNEL PART
    BSR.L  REMOVOBJ                   REMOVE OBJ(A1=OBJ,D0:=BYTES,D1/A0:=...);
    BRA.S  ALRETURN                   GOTO ALLOC_RETURN;
*
REM_PTV    EQU *                  POINTER_VALUE_ILL;
    MOVE.L A0,A1                      A1:=KNEL PART
    BSR.L  REMOVOBJ                   REMOVE OBJ(A1=OBJ,D0:=BYTES,D1/A0:=...);
    MOVE.L        #ECRVALUE,D7        D7:=RESULT:=VALUE_ILLEGAL;
    BRA.S  ALRETURN                   GOTO ALLOC_RETURN;
*
 
 
MM_TERM    EQU *  TERMINATE OBJECT: TERMINATION PROCEDURE OF KERNEL ALLOC
*          CALL:                      RETURN
* A1       OBJECT                     UNDEF
* D0/D1/A0 ANY                        UNDEF
*
* THE MM_LOCK AND MM_QUEUE IS MANAGED OUTSIDE THIS PROCEDURE.
*
    BSR.S  REMOVOBJ                   REMOVE OBJ(A1=OBJ,D0:=BYTES,D1/A0:=...);
    ADD.L  D0,KV_FRMEM                ADJUST FREE_BYTES;
    RTS                               RETURN;
 
    PAGE
MM_CRE     EQU *  CREATE OBJECT
*          CALL:                      RETURN:
* D0       KNEL BYTE >0               SAME
* D1       USER BYTE >=0              SAME
* A0       ANY                        KNEL_PART
* A1       ANY                        USER_PART WHEN D1 >0
* CCR                                 <EQ> : NO_RESOURCES, <NE>: OK
* SR       27XX -> 2000 DURING CALL-> 27XX
*
* ALLOCATE THE KERNEL PART AND (WHEN D1>0) THE USER PART
* THE OBJECT WILL BE AN OPEN OBJECT WITH STATE = 0
* THE OWNER FIELDS OF THE OBJECT ARE UNDEFINED.
    MOVE.W #$2000,SR                  ALLOW INTERRUPTS
    EXG.L  D0,D1                      D1:=KNEL BYTES; D0:=USER BYTES
    LEA    KNELGET,A0
    BSR.L  GETROOM                    GETROOM(D1=SIZE,A0=KNELGET,A0:=MEM_REC);
  IF.W A0 <EQ> #0 THEN.S            IF NO ROOM THEN
    BRA.S  MM_CREEX                   GOTO NO_ROOM;
  ENDI                              ENDI;
    BSR.L  CREATEKN                   CREATEKN(D1=SIZE,A0=MEM_REC,A0:=KNELPART)
    EXG.L  D0,D1                      D1:=USER BYTES; D0:=KNEL BYTES;
    BSR.L  INITKNEL                   INIT KNELPART(D0=SIZE,A0=KNELPART);
    MOVE.W #OB_OPOB<<8,OB_KIN(A0)     OBJECT.KIND:=OPEN;
*                                     OBJECT.STATE:=0;
    TST.L  D1
  IF <EQ> THEN.S                    IF USER BYTES = 0 THEN
    EXG.L  A0,A1                      A0:=USER PART,A1:=KNELPART;
  ELSE.S                            ELSE
    LEA    DUMMYOWN,A1                A1:=DUMMY OWNER POINTER;
    MOVE.L A1,(A0)                    LET DUMMY OWNER POINTER
    MOVE.L A1,4(A0)                   OWN THE KNEL PART. THIS MAKES
    MOVE.L A0,(A1)                    IT POSSIBLE TO FIND IT AFTER
    MOVE.L A0,4(A1)                   THE CALL TO GETROOM;
    LEA    USERGET,A0
    BSR.L  GETROOM                    GETROOM(D1=SIZE,A0=USERGET,A0:=MEM_REC);
    MOVE.L DUMMYOWN,A1                A1:=KNEL PART;
  IF.W A0 <EQ> #0 THEN.S            IF NO ROOM FOR USER PART THEN
    EXG.L  D0,D1                      D0:=USER_BYTES,D1:=KNEL BYTES;
    BSR.L  REMOVEKN                   REMOVEKN(D1=SIZE,A1=KNELPART);
    EXG.L  D0,D1                      RESTORE D0,D1;
    BRA.S  MM_CREEX                   GOTO NO_ROOM;
  ENDI                              ENDI;
    BSR.L  CREATEUS                   CREATEUS(D1=SIZE,A0=MEM_REC,A0:=USER_PART
  ENDI                              ENDI;
* D1 = 0 OR SIZE OF USER PART
    BSR.L  ASSIGNUS                   ASSIGNUS(D1=SIZE,A0=USERPART,A1=KNELPART,
*                                              WHEN D1=0 , A1:=0 );
    EXG.L  A0,A1                      A0:=KNELP@RT; A1:=USER PART
    MOVE.W #$2700,SR                  PREVENT INTERRUPTS; CCR:= <NE>;
    RTS                               RETURN;
*
MM_CREEX   EQU *  EXIT, NO_ROOM:
    MOVE.W #$2704,SR                  PREVENT INTERRUPTS; CCR:= <EQ>;
    RTS                               RETURN;
 
    PAGE
REMOVOBJ   EQU *  REMOVE OBJECT
*          CALL                       RETURN
* A1       KERNEL PART OF OBJECT      UNDEF
* D0       ANY                        BYTES_RELEASED
* D1,A0 ARE SPOILED
* THE FUNCTION CORRESPONDS TO THE TERMINATION PROCEDURE OF THE ALLOC OBJECT.
* THE MM_GATE HAS BEEN LOCKED BY THE CALLER OF THIS PROCEDURE.
* THE FUNCTION REMOVES THE KERNEL PART AND THE USER PART AND UPDATES THE
* MEMORY STRUCTURES TO REFLECT THAT. CALLING PROC IS NOT SUSPENDED.
*
    MOVE.L A1,-(A7)                   SAVE THE CALL PARAM
    MOVE.L OB_SPA(A1),A1              A1:=SPACE DESCRIPTION;
    PRT_MEM REMOVOBJ,-SP_SIZ(A1),SP_SIZ ********
    MOVE.L SP_SIZU(A1),D1             D1:=SIZE OF USER PART;
    MOVE.L SP_FIRU(A1),A1             A1:=USER PART ADDRESS;
    MOVE.L D1,D0                      D0:=USER_BYTES:=SIZE OF USER PART;
  IF <NE> THEN.S                   IF USER PART PRESENT THEN
    BSR.L  REMOVEUS                   REMOVE USER PART (D1,A1,A0/A1:=?);
  ENDI                             ENDI;
    MOVE.L (A7)+,A1                   A1:=SAVED KERNEL PART ADDRESS;
    MOVE.W OB_SIZK(A1),D1             D1:=SIZE OF KERNEL PART;
    EXT.L  D1
*
* SPECIAL TESTOUTPUT FROM MEMORY DEALLOC
    BTST.B #0,TSTSTATU+1
  IF <NE> THEN.S
    BSR.L MEMPLUS                     MEMPLUS(D0=USER,D1=KNEL);
  ENDI
*
    ADD.L  D1,D0                      D0:=BYTES_RELEASED:=USER_BYTES+KNEL_BYTES;
    BSR.L  REMOVEKN                   REMOVE KERNEL PART (D1,A1,A0/A1:=?);
    RTS                               RETURN;
 
 
PGTABSET   EQU *  ASSIGN ENTRY OF PAGE TABLE
*          CALL:                      RETURN:
* D0       TOP ADDRESS OF USER PART   SAME
* A1       KERNEL PART ADDRESS        SAME
*
 
    ENTRYSAV D0/A0,.PGTABSE           SAVE REGISTERS;
    LSR.L  #6,D0                      D0:=OFFSET:=D0 SHIFTFT(-8)SHIFT 2;
    MOVE.L PAGE_TAB,A0                A0:=BASE ADDRESS OF PAGE_TABLE
    MOVE.L A1,(A0,D0.L)               PAGE_TABLE_OFFSET:=A1=KERNEL PART ADDRESS;
    PRT_MEM PGTABSET,<(A0,D0.L)>,4    ********
    RETURN .PGTABSE                   RETURN;
 
 
PGTABGET  EQU *  GET ENTRY FROM PAGE TABLE : KERNEL PART ADDRESS.
*          CALL:                      RETURN:
* D3       USER PART TOP ADDRESS      SAME
* A1       ANY                        KERNEL PART ADDRESS
*
    MOVE.L D3,-(A7)                   SAVE D3;
    LSR.L  #6,D3                      D3:=D3 SHIFT -6,"RELATIVE ENTRY ADDRESS"
    MOVE.L PAGE_TAB,A1                A1:=BASE OF PAGE TABLE;
    PRT_MEM PGTABGET,<(A1,D3.L)>,4    ********
    MOVE.L (A1,D3.L),A1               A1:=KERNEL PART ADDRESS:=CONTENT OF ENTRY;
    MOVE.L (A7)+,D3                   RESTORE D3;
    RTS                               RETURN;
 
    PAGE
CHAIN_IT   EQU *  CHAIN NEW ELEMENT NEXT TO OLD ELEMENT
*          CALL:                      RETURN:
* A4       OLD ELEMENT                SAME
* A0       NEW ELEMENT                SAME
* A2       ANY                        ELEMENT NEXT TO NEW ELEMENT
*
* OLD ELEMENT IS A CHAIN HEAD OR A CHAIN MEMBER
*
 
    MOVE.L (A4),A2                    A2:=OLDNEXT:=OLD.NXT;
    MOVEM.L A2/A4,(A0)                NEW.NXT:=A2:=OLDNEXT;NEW.PRV:=A4:=OLD;
    MOVE.L A0,4(A2)                   OLDNEXT.PRV:=A0:=NEW;
    MOVE.L A0,(A4)                    OLD.NXT:=A0:=NEW;
    RTS
 
CHAINOWN   EQU *  CHAIN OBJECT TO NIL POINTER OR OWN POINTER
*          CALL:                      RETURN:
* A0       KERNEL PART OF OBJECT      SAME
* A3       OWNER ENV OR CTX           SAME
* D3.W     POINTER OFFSET INTO ENV OR CTX  SAME
* A2       ANY                        OWNER POINTER ADDRESS
*
* (A3,D3.W) MUST BE THE ADDRESS OF A POINTER VARIABLE.
* THE CHAIN FIELD OF THE VARIABLE MAY BE THE HEAD OF AN OWNER SET
* OR IT MAY POINT TO ITSELF. THE OBJECT BECOMES THE LAST MEMBER
* OF THE POINTER CHAIN. THE POINTER KIND IS SET TO OWNER_SET;
*
 
    MOVE.L A4,-(A7)                   SAVE A4;
    MOVE.L 4(A3,D3.W),A4              A4:=LAST IN POINTER CHAIN;
    BSR.S  CHAIN_IT                   INSERT KERNEL PART NEXT TO LAST IN POINTER
*                                     A2:= OWNER POINTER;
    MOVE.L A3,OB_OWN(A0)              OBJECT.OWNER_STRUCT:=OWNER ENV OR CTX
    MOVE.W D3,OB_OFF(A0)              OBJECT.OWNER_OFFSET:=POINTER OFFSET
    MOVE.B #PT_OWN,PT_KIN(A2)         OWNER POINTER.KIND:=OWN_SET
    MOVE.L (A7)+,A4                   RESTORE A4;
    RTS                               RETURN;
 
CHAINMAN   EQU *  CHAIN ENVELOPE TO NIL POINTER OR MANAGER POINTER
*          CALL:                      RETURN:
* A0       ENVELOPE                   SAME
* A3       MANAGER ENVELOPE           SAME
* D3       POINTER OFFSET INTO MANAGER ENVELOPE  SAME
* A2       ANY                        MANAGER POINTER ADDRESS
*
* (A3,D3.W) MUST BE THE ADDRESS OF A POINTER VARIABLE.
* THE FIRST FIELDS OF AN ENVELOPE HAS THE SAME FORMAT AS AN OBJECT.
* THEREFORE CHAINOWN MAY BE USED.
*
 
    BSR.S  CHAINOWN                   CHAIN ENVELOPE AS LAST MEMBER OF THE CHAIN
    MOVE.B #PT_MAN,PT_KIN(A2)         MANAGER_POINTER.KIND:=MAN_SET;
    RTS                               RETURN;
 
    PAGE
SIMPLEPT   EQU *  CREATE SIMPLE POINTER TO OBJECT;
*          CALL                      RETURN
* A0       NIL POINTER               SAME
* A4       OBJECT                    SAME
*
    ENTRYSAV D0/D1/A2/A4,.SIMPLEP           SAVE REGS;
    MOVE.L A4,PT_REF(A0)              POINT.REF:=OBJECT;
    MOVE.B OB_KIN(A4),D0              D0.B:=OBJECT KIND;
    LEA    OB_REF(A4),A4              A4:=REF_OBJECT CHAIN HEAD;
    BSR.S  CHAIN_IT                   CHAIN POINTER TO REF_OBJECT CHAIN;
    MOVE.B #PT_OBJ,PT_KIN(A0)         POINTER KIND:=REF_OBJECT
    MOVE.W CC_MASK(PC),D1             D1:= CALLABLE OBJECT KINDS
    BTST.L D0,D1                      TEST THAT BIT # D0.B IS SET IN D1
  IF <NE> THEN.S                    IF CALL CAP MAKE SENSE THEN
    ORI.B  #1<<PT_CC,PT_INF(A0)       ASSIGN CALL CAP TO POINTER;
  ENDI                              ENDI;
    RETURN .SIMPLEP                   RETURN;
* CALLABLE OBJECTS:
CC_MASK DC.W  1<<OB_GEOB+1<<OB_GAOB+1<<OB_COOB+1<<OB_ALLO+1<<OB_SCHE
 
INIT_ENV   EQU *  INITIALIZE ENVELOPE
*          CALL                                RETURN:
* D0=SIZE OF ENVELOPE INCLUDING LOCAL POINTERS SAME
* A3=ENVELOPE ADDRESS                          SAME
* A6=SPACE_DESC OF OBJECT                      SAME
*
* FUNCTION: INITIALIZE AN EMPTY ENVELOPE ON TOP OF THE ENVELOPE STACK.
*
    ENTRYSAV A0/A2/A4,.INIT_EN        SAVE REGISTERS;
    LEA    EN_STK(A3),A0              A0:=STACK CHAIN_ELEMENT
    MOVE.L SP_ENV+4(A6),A4            A4:=LAST ELEMENT IN ENVELOPE STACK
    BSR.S  CHAIN_IT                   CHAIN ENVELOPE AS LAST IN ENVELOPE STACK
*                                     A2:=HEAD OF STACK CHAIN
    LEA    EN_REF(A3),A0              A0:=HEAD OF REF_ENV CHAIN
    MOVE.L A0,(A0)                    REF_ENV CHAIN:=EMPTY
    MOVE.L A0,4(A0)
 
    LEA    EN_SIZ(A3),A2              A2:=CURRENT POINTER:=FIRST POINTER;
* ASSIGN ZERO TO TERM_PROC, SIZE_VALUES, AND -COUNT.
    LEA    EN_TERM(A3),A4             A4:= FIRST WORD TO BE CLERED; A2=TOP;
  REPEAT                            REPEAT
    CLR.W  (A4)+                      ASSIGN ZERO; A4:=NEXT WORD;
  UNTIL.L A4 <EQ> A2                UNTIL NEXT_WORD = TOP;
    LEA    (A3,D0.W),A4               A4:=TOP LOCAL POINTER
    MOVE.L #$FFFFFF,EN_MAXU(A3)       SIZ_MAX:= MAX_VAL
    MOVE.W #$7FFF,EN_MAXK(A3)         .
    MOVE.W #1,EN_COU(A3)              INITIALLY=1 (OR 0?)
*INIT LOCAL POINTERS.
  WHILE.L A2 <NE> A4 DO.S           WHILE CURRENT_POINTER <> TOP POINTER DO
    MOVE.L A2,(A2)                      CURRENT POINTER CHAIN
    MOVE.L A2,4(A2)                     :=EMPTY;
    MOVE.W #PT_NIL<<8+1<<PT_LSC,PT_KIN(A2)  POINTER.KIND:=NIL
*                                           POINTER.INF:=LOCAL;
    LEA    PT_SIZ(A2),A2                CURRENT POINTER:=NEXT_POINTER;
  ENDW                              ENDW;
    PRT_MEM INIT_ENV,(A3),EN_SIZ+3*PT_SIZ  ********
    PRT_MEM TOPOFENV,(A4),SP_SIZ      ********
    RETURN .INIT_EN                   RETURN;
 
    PAGE
INITGEIN   EQU *  INITIALIZE GENERAL PART OF STUB OR SCHEDULER OBJECT.
* CALL                               RETURN
* A0=KERNEL PART OF OBJECT           SAME
* A1=ENTRY POINT OF CODE SEGMENT     UNDEF
*
* FUNCTION: ASSIGN ENTRY POINT, EX_CHAIN,CONTROL_PROC,NO_OF_TEMP POINTER/BYTES,
*           STACK ADDRESS, REQUIRED FREE STACK SPACE,OBJECT STATE;
 
 
    MOVE.L A1,GE_ENT(A0)              ENTRY POINT OF OBJECT:=A1;
    MOVE.W #OB_GEOB<<8+1<<OB_REEN+1<<OB_OCC,OB_KIN(A0)  OBJ.KIND:=GENERAL;
*                                     OBJ.STATE:= REENTRANT+OWNERS CALL CAP;
    MOVEA.L OB_SPA(A0),A1             A1:=SPACE DESCR OF OBJECT
    SUBI.W #(GE_SIZ-OB_SIZ),SP_FREK(A1)   RESERVE ROOM FOR GENERAL FIELDS
    ADDI.L #(GE_SIZ-OB_SIZ),SP_FIRK(A1)   .
    LEA    GE_EX(A0),A1               A1:=HEAD OF EXECUTED_BY CHAIN;
    MOVE.L A1,(A1)                    EXECUTED_BY CHAIN
    MOVE.L A1,4(A1)                     := EMPTY;
    CLR.W  GE_CON(A0)                 CONTROL PROCEDURE:=NO CONTROL:=0;
    MOVE.W #SCHTEMP,GE_TEMP(A0)       INITIALIZE NO_OF_TEMP POINTERS IN CONTEXT
    MOVE.L #SCHTEMD,GE_TEMD(A0)       INITIALIZE NO OF TEMP BYTES IN CONTEXTS;
    MOVE.L #FL_TEMPD,GE_STKM(A0)       LOGICAL ADDRESS OF FIRST OF STACK;
    CLR.W  GE_STK(A0)                 REQUIRED FREE CALL STACK
    CLR.L  GE_STK+2(A0)                 := 0;
    PRT_MEM INITGEIN,(A0),GE_SIZ      ********
    RTS
 
* NOTE, THAT SIZE OF REQUIRED FREE CALL STACK IS SET TO ZERO.
* MANY VALUES ARE REASSIGNED WHEN A0 IS THE STUB.
 
    PAGE
INITKNEL   EQU *  INITIALIZE OB AND SP FIELDS OF OBJECT.
*          CALL:                      RETURN:
* A0  KERNEL PART                     SAME
* D0  LENGTH OF KERNEL PART           SAME
*
* AFTER INITKNEL, THE FOLLOWING IS TRUE
* THE SPACE PART DESCRIBES THE FREE KERNEL BYTES
* THE ENVELOPE STACK IS EMPTY
* SIZE OF KERNEL PART AND ADDRESS OF SPACE PART IS SAVED IN OBJECT
* RESIDENT COUNT IS ZERO
* THE OWNER CHAIN AND REF_OBJECT CHAIN IS EMPTY.
 
    ENTRYSAV D0/A0/A1/A2,.INITKNE     SAVE REGISTERS;
    LEA    (A0,D0.L),A2               A2:=SPACE PART:=KNELPART+LENGTH OF KNELPA
    MOVE.L A2,OB_SPA(A0)              SAVE SPACE PART ADDRESS IN OBJECT;
    MOVE.W D0,OB_SIZK(A0)             SAVE LENGTH OF KNEL PART;
    SUBI.W #OB_SIZ+SP_SIZ,D0          D0:=FREE_KNEL_BYTES:=LENGTH-FIXED
    MOVE.W D0,SP_FREK(A2)             SAVE FREE_KNEL_BYTES IN SPACE PART OF OBJ
    LEA    OB_SIZ(A0),A1              A1:=FIRST FREE KERNEL BYTE;
    MOVE.L A1,SP_FIRK(A2)             SAVE FIRST_FREE_KERNEL_BYTE IN SPACE PART;
    CLR.L  SP_SIZU(A2)                NO USER PART IS PRESENT;
    LEA    SP_ENV(A2),A2              A2:=ENVELOPE STACK;
    MOVE.L A2,(A2)                    ENVELOPE STACK:=EMPTY;
    MOVE.L A2,4(A2)                   .
    MOVE.L A0,(A0)                    OWNSET CHAIN:=EMPTY;
    MOVE.L A0,4(A0)                   .
    LEA    OB_REF(A0),A2              A2:=OB_REF CHAIN HEAD;
    MOVE.L A2,(A2)                    OB_REF CHAIN:=EMPTY;
    MOVE.L A2,4(A2)                   .
    CLR.W  OB_RES(A0)                 RESIDENT COUNT:=0;
    PRT_MEM INITKNEL,(A0),OB_SIZ      ********
    RETURN .INITKNE                   RETURN
 
 
ASSIGNUS   EQU *  ASSIGN USER PART TO KERNEL PART OF OBJECT
*          CALL:                      RETURN:
* A0       USER PART OR ANY,          SAME, OR 0 WHEN D1=0
*          WHEN D1=0
* A1       KNEL PART                  SAME
* D1.L     SIZE OF USER PART OR 0     SAME
 
    ENTRYSAV  D0/A2,.ASSIGNU          SAVE REGS;
    TST.L  D1
  IF <EQ> THEN.S                    IF SIZE USER PART = 0 THEN
    MOVE.L D1,A0                      USER PART ADDRESS:=0;
  ENDI                              ENDI;
    MOVE.L OB_SPA(A1),A2              A2:=SPACE PART;
    MOVE.L A0,SP_FIRU(A2)             SAVE USER PART ADDRESS;
    MOVE.L D1,SP_FREU(A2)             FREE USER BYTES:=SIZE OF USER PART;
    MOVE.L D1,SP_SIZU(A2)             SAVE SIZE OF USER PART;
    PRT_MEM ASSIGNUS,-SP_SIZ(A2),SP_SIZ ********
    LEA    (A0,D1.L),A2               A2:=TOP USER PART:=USER PART+SIZE OF USER
  IF.W A2 <NE> #0 THEN.S            IF USER PART PRESENT THEN
    MOVE.L A2,D0                      D0:=TOP USER PART;
    BSR.L  PGTABSET                   PAGE TABLE (TOP USER PART):=KNEL PART=A1;
  ENDI                              ENDI;
    RETURN .ASSIGNU                   RETURN;
 
    PAGE
REMOVEUS   EQU *  REMOVE USER PART
*    CALL:                            RETURN:
* D1 NO_OF_BYTES                      SAME
* A1 USER PART ADDRESS                UNDEF
* A0 ANY                              UNDEF
*
 
    BSR.S  GETUREC                    A0:=MEM_REC HOLDING THE USER PART;
    BSR.L  RECHAINR                   REMOVE AND RESTRUCTURE (D1,A0,A1);
    RTS                               RETURN;
 
REMOVEKN   EQU *  REMOVE KNEL PART
*    CALL:                            RETURN:
* D1 NO_OF_BYTES                      SAME
* A1 KERNEL PART ADDRESS              UNDEF
* A0 ANY                              UNDEF
*
 
    BSR.S  GETKREC                    A0:=MEM_REC HOLDING THE KERNEL PART;
    BSR.L  RECHAINR                   REMOVE AND RESTRUCTURE (D1,A0,A1);
    RTS                               RETURN;
 
GETUREC    EQU *                      GET MEM_REC HOLDING USER PART
*    CALL:                            RETURN:
* A1 USER PART                        SAME
* A0 ANY                              MEM_REC
*
 
    MOVE.L L_ALLOC,A0                 A0:=LAST MEM_REC; "=DUMMY MEM_REC"
  REPEAT                            REPEAT
    MOVE.L MR_PRV(A0),A0              A0:=PRIOR MEM_REC;
  UNTIL.L A0 <LT> A1                UNTIL PRIOR MEM_REC HOLDS USER PART;
    RTS                               RETURN;
 
GETKREC    EQU *                      GET MEM_REC HOLDING KERNEL PART
*    CALL:                            RETURN:
* A1 KERNEL PART                      SAME
* A0 ANY                              MEM_REC
*
    MOVE.L F_ALLOC,A0                 A0:=FIRST MEM_REC; "NOT DUMMY"
  REPEAT                            REPEAT
    MOVE.L (A0),A0                    A0:=NEXT MEM_REC;
  UNTIL.L A0 <GT> A1                UNTIL NEXT MEM_REC IS NEXT TO KERNEL PART
    MOVE.L MR_PRV(A0),A0              A0:=PRIOR MEM_REC;
    RTS                               RETURN;
 
    PAGE
CREATEUS   EQU *  CREATE USER PART
*    CALL:                            RETURN:
* D1 NO_OF_BYTES IN USER PART         SAME
* A0 MEM_REC TO HOLD NEW USER PART    USER PART ADDRESS
*
 
    ENTRYSAV D7/A1,.CREATEU           SAVE REGS;
 
* COMPUTE ADDRESS OF NEW USER PART
* A USER PART IS ALWAYS LOCATED IN THE HIGH ADDRESS PART OF THE FREE AREA
 
    MOVE.L MR_FST(A0),A1              A1:=USER_PART_ADDRESS:=MEM_REC.FIRST_FREE
    ADDA.L MR_FRE(A0),A1                + MEM_REC.FREE_BYTES
    SUBA.L D1,A1                        - NUMBER OF BYTES IN USER PART;
 
    MOVEQ  #-1,D7                     D7:=-1=USER_PART_CREATE;
    BSR.S  RECHAINC                   CREATE AND RESTRUCTURE (D1,D7,A0,A1);
    MOVE.L A1,A0                      A0:=USER PART ADDRESS;
 
    RETURN .CREATEU                   RETURN;
 
CREATEKN   EQU *  CREATE KERNEL PART
*    CALL:                            RETURN:
* D1 NO OF BYTES IN KERNEL PART       SAME
* A0 MEM_REC TO HOLD NEW KERNEL PART  KERNEL PART ADDRESS
*
 
    ENTRYSAV D7/A1,.CREATEK           SAVE REGS;
 
* COMPUTE ADDRESS OF NEW KERNEL PART
* A KERNEL PART IS ALWAYS LOCATED IN THE LOW ADDRESS PART OF THE FREE AREA
 
    MOVE.L MR_FST(A0),A1              A1:=KERNEL PART ADDRESS:=MEM_REC.FIRST_FRE
 
    MOVEQ  #+1,D7                     D7:=1=KERNEL PART CREATE;
    BSR.S  RECHAINC                   CREATE AND RESTRUCTURE (D1,D7,A0,A1);
    MOVE.L A1,A0                      A0:=KERNEL PART ADDRESS;
 
    RETURN .CREATEK                   RETURN;
 
    PAGE
RECHAINC   EQU *  RECHAIN MEM_RECS AFTER CREATION OF A KERNEL OR USER PART
*    CALL:                            RETURN:
* D1 #BYTES                           SAME
* D7 TYPE: 1 = KERNEL,FF = USER       SAME
* A0 DESTIN_REC  "TO HOLD NEW PART"   UNDEF
* A1 PART  "TO BE CREATED"            SAME
*
* THE NEW PART IS ALLOCATED SOMEWHERE IN THE FREE AREA OF THE MEM_REC.
*
    ENTRYSAV D0/A1-A6,.CHAINC         SAVE REGS
    CLR.W D0                          D0:=FIX_TYP:=0 SHIFT 8
    MOVE.B MR_TYP(A0),D0                + DESTIN_REC.TYPE;
    MOVEM.L (A0),A5/A6                A5/A6:=DESTIN_REC.NEXT/PREV;
    LEA    (A1,D1.L),A2               A2:=TOP_PART:=PART+#BYTES;
    MOVE.L MR_FST(A0),A3              A3:=TOP_FREE:=DESTIN_REC.FIRST
    ADD.L  MR_FRE(A0),A3                + DESTIN_REC.FREE_BYTES;
 
  IF.L A1 <EQ> A0 THEN.S            IF DESTIN_REC=PART THEN "1 OR 4"
  IF.L A3 <EQ> A2 THEN.S            IF TOP_FREE=TOP_PART THEN
    LEA CASE_C4,A4                    A4:=PROC:=CREATE_4;
    PRT_REG C4                        ********
  ELSE.S                            ELSE
    LEA CASE_C1,A4                    A4:=PROC:=CREATE_1;
    PRT_REG C1                        ********
  ENDI                              ENDI;
  ELSE.S                            ELSE
  IF.L A3 <EQ> A2 THEN.S            IF TOP_FREE=TOP_PART THEN
    LEA CASE_C3,A4                    A4:=PROC:=CREATE_3;
    PRT_REG C3                        ********
  ELSE.S                            ELSE
    LEA CASE_C2,A4                    A4:=PROC:=CREATE_2;
    PRT_REG C2                        ********
  ENDI                              ENDI;
  ENDI                              ENDI;
 
    JSR    (A4)                       CALL PROC;
    PRT_MEM OLD_A0,(A0),MR_SIZ        ********
    PRT_MEM PRV_A6,(A6),MR_SIZ        ********
    PRT_MEM NEW_A2,(A2),MR_SIZ        ********
    PRT_MEM NXT_A5,(A5),MR_SIZ        ********
    RETURN .CHAINC                    RETURN;
 
    PAGE
CASE_C1    EQU *                  PROC CREATE_1;
* THIS CASE IS THE REVERSE OF CASE_R3.
* PART IS INCLUDED IN USED AREA OF PRIOR MEM_REC,
* CREATE NEW MEM_REC_HEAD AFTER PART
 
    SUB.L  A2,A3                      A3:=FREE:=TOP_FREE-TOP_PART;
    NEWHEAD A2,D0,A6,A5               NEWHEAD(NEW_HEAD,TYPE,PRIOR,NEXT);
    MOVE.L A3,MR_FRE(A2)              NEW_HEAD.FREE_BYTES:=FREE;
    MOVE.B D7,MR_TYP(A6)              PREV_REC.TYPE:=TYPE;
    RTS
 
CASE_C2    EQU *                  PROC CREATE_2;
* THIS CASE IS THE REVERSE OF CASE_R4.
* PART IS PLACED BETWEEN THE HEAD OF THE MEM_REC AND TOP_FREE.
* DESTIN_REC IS DIVIDED IN TWO MEM_REC:
 
    MOVE.L A0,A6                      A6=PRIOR_REC:=DESTIN_REC;
    BSR.S  CASE_C1                    CALL CREATE_1; A3:= TOP_FREE-TOP_PART;
    MOVE.L D1,D0                      D0:=REMOVE:= #BYTES
    ADD.L  A3,D0                        + (TOP_FREE-TOP_PART);
    SUB.L  D0,MR_FRE(A0)              DESTIN_REC.FREE_BYTES:=...-REMOVED;
    RTS                               RETURN;
 
CASE_C3    EQU *                  PROC CREATE_3;
* THIS CASE IS THE REVERSE OF CASE_R1
* PART IS PLACED IN THE HIGH ADDRESS END OF THE FREE AREA.
* ADJUST FREE_BYTES,
 
    SUB.L  D1,MR_FRE(A0)              DESTIN_REC.FREE_BYTES:=...-#BYTES;
    MOVE.B D7,MR_TYP(A0)              DESTIN_REC.TYPE := TYPE;
    RTS                               RETURN;
 
CASE_C4    EQU *                  PROC CREATE_4;
* THIS CASE IS THE REVERSE OF CASE_R2
* PART FITS THE HOLE BETWEEN TWO USED AREAS OF THE SAME TYPE (?)
* INCLUDED DESTIN_REC IN THE AREA OF THE PREVIOUS RECORD.
 
    MOVE.L A5,(A6)                    PREV_REC.NEXT:=NEXT_REC
    MOVE.L A6,MR_PRV(A5)              NEXT_REC.PREV:=PREV_REC
    RTS                               RETURN;
 
    PAGE
RECHAINR   EQU *  RECHAIN MEM_RECS AFTER REMOVAL OF KERNEL OR USER PART
*    CALL:                            RETURN:
* D1 #BYTES                           SAME
* A0 DESTIN_REC "HOLDS PART"          UNDEF
* A1 PART  "TO BE REMOVED"            UNDEF
*
* PART IS LOCATED SOMEWHERE IN THE USED AREA OF DESTIN_REC
*
 
    ENTRYSAV D0/D7/A2-A5,.CHAINR      SAVE REGS;
    CLR.W  D0                         D0:=FIX_TYP:=DYNAMIC SHIFT 8
    MOVE.B MR_TYP(A0),D0                + DESTIN_REC.TYPE;
    MOVE.L (A0),A5                    A5:=TOP_USED:=NEXT_REC:=DESTIN_REC.NEXT;
    LEA    (A1,D1.L),A2               A2:=TOP_PART:=PART+#BYTES;
    MOVE.L MR_FST(A0),A3              A3:=TOP_FREE:=SOURCE_REC.FIRST
    ADD.L  MR_FRE(A0),A3                + DESTIN_REC.FREE_BYTES;
 
  IF.L A1 <EQ> A3 THEN.S            IF PART=TOP_FREE THEN
  IF.L A2 <EQ> A5 THEN.S            IF TOP_PART=TOP_USED THEN
    LEA    CASE_R4,A4                 A4:=PROC:=REMOVED_4
    PRT_REG R4                        ********
  ELSE.S                            ELSE
    LEA    CASE_R1,A4                 A4:=PROC:=REMOVED_1
    PRT_REG R1                        ********
  ENDI                              ENDI;
  ELSE.S                            ELSE
  IF.L A2 <EQ> A5 THEN.S            IF TOP_PART=NEXT_REC THEN
    LEA    CASE_R3,A4                 A4:=PROC:=REMOVE_3;
    PRT_REG R3                        ********
  ELSE.S                            ELSE
    LEA    CASE_R2,A4                 A4:=PROC:=REMOVE_2;
    PRT_REG R2                        ********
  ENDI                              ENDI;
  ENDI                              ENDI;
 
    PRT_MEM OLD_A5,(A5),MR_SIZ        ********
    JSR    (A4)                       CALL PROC;
    PRT_MEM NNXTA5,(A5),MR_SIZ        ********
    PRT_MEM OLD_A0,(A0),MR_SIZ        ********
    PRT_MEM NEW_A1,(A1),MR_SIZ        ********
    RETURN .CHAINR                    RETURN;
 
    PAGE
CASE_R1    EQU *                  PROC REMOVE_1;
* THIS CASE IS THE REVERSE OF CASE_C3
* PART IS PRESENT AS THE FIRST BYTES OF THE USED AREA OF DESTIN_REC
* ADJUST FREE BYTES
 
    ADD.L  D1,MR_FRE(A0)              DESTIN_REC.FREE_BYTES:=...+#BYTES;
    RTS                               RETURN;
 
CASE_R2    EQU *                  PROC REMOVE_2;
* THIS CASE IS THE REVERSE OF CASE_C4
* PART IS PRESENT IN THE MIDDLE OF THE USED AREA OF DESTIN_REC
* DIVIDE DESTIN_REC IN TWO MEM_RECS.
 
    NEWHEAD A1,D0,A0,A5               NEWHEAD(NEW_HEAD,TYPE,PRIOR,NEXT);
    MOVE.L D1,MR_FRE(A1)              NEW_HEAD.FREE_BYTES:=#BYTES;
    RTS                               RETURN;
 
CASE_R3    EQU *                  PROC REMOVE_3;
* THIS CASE IS THE REVERSE OF CASE_C1
* PART IS PRESENT AT THE BOTTOM OF THE USED AREA OF DESTIN_REC;
* EXTEND THE FREE ARE OF NEXT_REC, UNLESS NEXT_REC IS AN INITIAL MEM_REC.
    TST.B  MR_FIX(A5)
  IF <NE> THEN.S                    IF NEXT_REC IS AN INITIAL MEM_REC THEN
    CLR.W  D0                         D0:=FIX_TYPE:=DYNAMIC+EMPTY
    BRA.S  CASE_R2                    GOTO REMOVE_2;
  ENDI                              ENDI;
    MOVE.B MR_TYP(A5),D0              D0:=FIX_TYPE:=DYNAMIC+NEXT_REC.TYPE;
    MOVE.L MR_FRE(A5),D7              D7:=NEXT_FREE:=NEXT_REC.FREE_BYTES
    MOVE.L (A5),A5                    A5:=NEXT_REC:=NEXT_REC.NEXT;
    BSR.S  CASE_R2                    CALL REMOVE_2
    ADD.L  D7,MR_FRE(A1)              NEW_HEAD.FREE_BYTES:=...+NEXT_FREE;
    RTS                               RETURN;
 
CASE_R4    EQU *                  PROC REMOVE_4
* THIS CASE IS THE REVERSE OF CASE_C2
* PART EQUALS THE WHOLE USED AREA OF DESTIN_REC
* COMBINE DESTIN_REC AND NEXT_REC, UNLESS NEXT_REC IS AN INITIAL MEM_REC.
    TST.B  MR_FIX(A5)
  IF <NE> THEN.S                    IF NEXT_REC IS AN INITIAL MEM_REC THEN
    CLR.B  MR_TYP(A0)                 DESTIN_REC.TYPE:=FREE:=0;
    BRA.S  CASE_R1                    GOTO REMOVE_1;
  ENDI                              ENDI;
    MOVE.L MR_FRE(A5),D7              D7:=EXTRA_FREE:=NEXT_REC.FREE_BYTES
    ADD.L  D1,D7                        + #BYTES
    ADD.L  D7,MR_FRE(A0)              DESTIN_REC.FREE_BYTES:=...+EXTRA_FREE;
    MOVE.B MR_TYP(A5),MR_TYP(A0)      DESTIN_REC.TYPE:= NEXT_REC.TYPE;
    MOVE.L (A5),A5                    A5:=NEXT_REC:=NEXT_REC.NEXT;
    MOVE.L A5,(A0)                    DESTIN_REC.NEXT:=NEXT_REC;
    MOVE.L A0,MR_PRV(A5)              NEXT_REC.PRV:= DESTIN_REC;
    RTS                               RETURN;
 
    PAGE
USERGET    EQU *       PARAMETER LIST TO GETROOM WHEN A KERNEL PART IS CREATED
 
    BRA.L  SEARCHUS               SEARCH FOR USER SPACE
    BRA.L  COMPUSER               COMPRESS USER PARTS
    BRA.L  COMPKNEL               COMPRESS KERNEL PARTS
 
KNELGET    EQU *       PARAMETER LIST TO GETROOM WHEN A USER PART IS CREATED.
 
    BRA.L  SEARCHKN               SEARCH FOR KERNEL SPACE
    BRA.L  COMPKNEL               COMPRESS KERNEL PARTS
    BRA.L  COMPUSER               COMPRESS USER PARTS
 
* OFFSETS INTO THE PARAMETER LISTS ABOVE
 
SEARCH     EQU     0              SEARCH PROCEDURE
COMPRESS   EQU     4              COMPRESS PROCEDURE
OPPOSITE   EQU     8              COMPRESS OPPOSITE PROCEDURE
 
GETROOM    EQU *       GET ROOM FOR A USER OR KERNEL PART
*    CALL:                        RETURN:
* D1 NO_OF_BYTES                  SAME
* A0 PROC ADDRESS                 MEM_RECORD, OR 0 WHEN NO ROOM.
*
 
    MOVE.L A1,-(A7)                 SAVE A1;
    MOVE.L A0,A1                    A1 := PROC ADDRESS;
 
    JSR    (A1)                     PROC.SEARCH(D1=NO_OF_BYTES,A0:=MEM_REC);
  IF.W A0 <NE> #0 THEN.S            IF MEM_REC WAS FOUND THEN
    MOVE.L (A7)+,A1                   RESTORE A1;
    RTS                               RETURN;
  ENDI                              ENDI;
 
    JSR    COMPRESS(A1)             PROC.COMPRESS(D1=NO_OF_BYTES,A0:=MEM_REC OR
  IF.W A0 <NE> #0 THEN.S            IF MEM_REC WAS FOUND THEN
    MOVE.L (A7)+,A1                   RESTORE A1;
    RTS                               RETURN;
  ENDI                              ENDI;
 
    MOVE.L D1,-(A7)                 SAVE D1;
    MOVE.L #$FFFFFF,D1              D1 := VERY BIG SIZE; "CANNOT BE FOUND"
    JSR    OPPOSITE(A1)             PROC.OPPOSITE(D1=VERY BIG, A0:=0);
    MOVE.L (A7)+,D1                 RESTORE D1;
    JSR    (A1)                     PROC.SEARCH(D1=#BYTES,A0:=MEM_REC/0);
 
    MOVE.L (A7)+,A1                 RESTORE A1;
    RTS                             RETURN;
 
    PAGE
SEARCHUS   EQU *   SEARCH FOR FREE SPACE TO A USER PART
*    CALL:         RETURN:
* D1 NO_OF_BYTES   SAME     "AN INTERGRAL NUMBER OF PAGES"
* A0 ANY           MEM_REC, OR 0 WHEN NO SPAVCE IS AVAILABLE
*
* A USER PART CAN BE ALLOCATED IN A MEM_REC CONTAINING OTHER USER PARTS,
* OR IN AN EMPTY MEM_REC POSITIONED AFTER THE LAST KERNEL PART IN THE SYSTEM
*
    ENTRYSAV D2/A1,.SEARCHU           SAVE REGS;
    MOVE.L L_ALLOC,A0                 A0:=CUR_MEM_REC:=LAST_DUMMY_MEM_REC;
  REPEAT                            REPEAT
    MOVE.L MR_PRV(A0),A0              A0:=CUR_MEM_REC:=CUR_MEM_REC.PREV;
    PRT_MEM SEARCHUS,(A0),MR_SIZ      ********
    TST.B MR_TYP(A0)
  IF <GT> THEN.S                    IF CUR_MEM_REC.TYPE=KERNEL THEN
NO_ROOM1   EQU *                  NO_ROOM:
    MOVE.W #0,A0                      A0:=NO_SPACE:=0;
    RETURN .SEARCHU                   RETURN;
  ENDI                              ENDI;
    MOVE.L MR_FRE(A0),D2              D2:=FREE:=CUR_MEM_REC.FREE_BYTES;
  UNTIL.L D1 <LE> D2                UNTIL NO_OF_BYTES<=FREE;
 
    MOVE.L MR_PRV(A0),A1            A1:=PREVCUR:=CUR_MEM_REC.PREV;
    TST.B  MR_TYP(A1)
  IF <GT> THEN.S                    IF PREVCUR.TYPE=KERNEL THEN
    TST.B  MR_FIX(A0)
  IF <NE> THEN.S                    IF CUR_MEM_REC IS NOT FIXED THEN
    SUBI.L #MR_SIZ,D2                 D2:=FREE:=FREE-HEAD_SIZE;
  IF.L D1 <GT> D2 THEN.S            IF NO_OF_BYTES > FREE THEN
    BRA.S  NO_ROOM1                   GOTO NO_ROOM;
  ENDI                              ENDI;
  ENDI                              ENDI;
  ENDI                              ENDI;
    RETURN .SEARCHU                   RETURN;
 
 
COMPUSER   EQU * COMPRESS USER PARTS TO GENERATE FREE SPACE.
*    CALL:        RETURN:
* D1 NO_OF_BYTES  SAME
* A0 ANY          MEM_REC, OR 0 WHEN REQUIRED SPACE WAS NOT GENERATED.
*
* MOVES ALL USER PARTS TOWARDS THE HIGH ADDRESS END OF THE RAM AREA.
* IF A HOLE IS CREATED DURING THIS PROCESS WITH A SIZE >= D1, THE PROCEDURE
* RETURNS IMMEDIATELY WITH A0 POINTING TO THAT HOLE.
*
  REPEAT                            REPEAT
    BSR.S  C_UPARTS                   COMPRESS EACH USER RAM(D1,A0:=MEM_REC);
    CMPA.W #0,A0                      TEST A0.L
  IF <NE> THEN.S                    IF A MEM_REC WAS RETURNED IN A0 THEN
    RTS                               RETURN, "OK"
  ENDI                              ENDI;
    BSR.L M_UACROS                    MOVE USER ACROSS NO_RAM(D1,A0:=RESULT)
    CMPA.W #0,A0                      TEST A0.L
  UNTIL <EQ>                        UNTIL  RESULT=NOTHING MOVED;
    RTS                               RETURN;
 
    PAGE
C_UPARTS   EQU *    COMPRESS EACH USER RAM AREA TO GENERATE FREE SPACE
*    CALL:          RETURN:
* D1 NO_OF_BYTES    SAME
* A0 ANY            MEM_REC, OR 0 WHEN REQUIRED SPACE WAS NOT AVAILABLE
*
* ALL CONSEQUTIVE RAM AREAS HOLDING USER PARTS ARE PROCESSED
* THE USER PARTS ARE PLACED IN THE HIGH ADDRESS PART OF ITS RAM AREA
    PROCSAVE D2/D3/D5/A1-A5,USAVPRT
    MOVE.L L_ALLOC,A2                 A2:=TOP_REC:=LAST DUMMY MEM_REC
  REPEAT   A2 IS A FIXED MEMREC     REPEAT "COMBINE LAST AND PREV"
    MOVE.L MR_PRV(A2),A0              A0:=LAST_REC:=TOP_REC.PREV;
    MOVE.L MR_PRV(A0),A4              A4:=PREV_REC:=LAST_REC.PREV;
    PRT_MEM C_U_PREV,(A4),MR_SIZ      ********
    TST.B  MR_TYP(A4)
  IF <GT>  THEN.S                    IF PREV_REC.TYPE = KERNEL THEN
    BRA.S  EXIT1                      GOTO EXIT; "ALL HAS BEEN PROCESSED"
  ENDI                              ENDI;
    TST.B  MR_FIX(A0)                 TEST WHETHER RAM_AREA=ONE MEM_REC
  IF <GT> THEN.S                    IF LAST_REC IS AN INITIAL MEM_REC THEN
    MOVE.L A0,A2                      A2:=TOP_REC:=LAST_REC;
  ELSE.S                            ELSE
* COMPUTE SOURCE ADDRESSES OF PARTS TO BE MOVED
    MOVE.L MR_FST(A4),D2              D2:=FIRST_SOURCE:=
    ADD.L  MR_FRE(A4),D2              PREV_REC.FIRST+PREV_REC.FREE_BYTES;
    MOVE.L A0,D3                      D3:=TOP_SOURCE:=LAST_MEM_REC;
* UPDATE MEM_REC STRUCTURE: COMBINE LAST AND PREV;
    MOVE.L A2,MR_NXT(A4)              TOP_REC.NEXT:=PREV_REC;
    MOVE.L A4,MR_PRV(A2)              PREV_REC.NEXT:=TOP_REC;
    MOVE.L MR_FRE(A0),D5              D5:= EXTRA:= LAST_REC.FREE_BYTES;
    ADD.L  D5,MR_FRE(A4)              PREV_REC.FREE_BYTES:= ... + EXTRA ;
    MOVE.B #-1,MR_TYP(A4)             PREV_REC.TYPE:=USER;
* COMPUTE DESTINATION ADDRESS OF PARTS TO BE MOVED: A0:=TOP_DESTIN:=
    ADD.L  MR_FRE(A0),A0              LAST_REC+LAST_REC.FREE_BYTES;
* MOVE USER PARTS,
  REPEAT   A4 CANNOT BE FREE        REPEAT "MOVE ONE USER PART"
    BSR.L  PGTABGET                   A1:=KERNEL PART:=PAGETABLE(D3);
    MOVE.L OB_SPA(A1),A5              A5:=TOP_KERNEL_PART;
    MOVE.L SP_SIZU(A5),D5             D5:=SIZE; "OF KERNEL PART TO BE MOVED"
    PRT_REG C_U_REGS                  ********
    BSR.L   MOVEUSER                  MOVEUSER (A0=TOP_DESTIN,A1=KERNEL PART);
    SUB.L  D5,A0                      TOP_DESTIN:=TOP_DESTIN-SIZE;
    SUB.L  D5,D3                      TOP_SOURCE:=TOP_SOURCE-SIZE;
  UNTIL.L  D2 <EQ> D3               UNTIL TOP_SOURCE=FIRST_SOURCE;
  ENDI                              ENDI;
  UNTIL.L  MR_FRE(A4) <GE> D1       UNTIL PREV_REC.FREE_BYTES>=NO_OF_BYTES;
    MOVE.L A4,A0                      A0:=LAST_REC:=PREV_REC;
    MOVE.L MR_PRV(A0),A4              A4:=PREV_REC:=LAST_REC.PREV;
    TST.B  MR_TYP(A4)
EXIT1      EQU *                  EXIT1:
  IF <GT> THEN.S                    IF PREV_REC.TYPE=KERNEL THEN
    MOVEQ  #MR_SIZ,D5                 D5:=HEAD_PLUS_BYTES:=HEAD_SIZE
    ADD.L  D1,D5                        + NO_OF_BYTES;
  IF.L D5 <GT> MR_FRE(A0) THEN.S    IF HEAD_PLUS_BYTES>LAST_REC.FREE_BYTES THEN
    MOVE.W #0,A0                      A0=LAST_REC:=NO SPACE:=0;
  ENDI                              ENDI;
  ENDI                              ENDI;
    PROCRTRN USAVPRT                  RETURN;  "OK/NOT_OK"
 
    PAGE
M_UACROS   EQU *  MOVE USER PARTS ACROSS NO_RAM AREAS.
*    CALL:     RETURN:
* D1 #BYTES    SAME "THE CALL VALUE IS NOT USED AT PRESENT
* A0 ANY       = 0 WHEN NOTHING HAS BEEN MOVED, <> 0 OTHERWISE
*
* USER PARTS FROM THE FIRST USER MEM_REC IS MOVED TO THE FREE PART OF
* MEM_RECS AT HIGHER ADDRESSES.
 
    PROCSAVE D0-D3/A1-A3,USAVCRS
    MOVE.L L_ALLOC,A2                 A2:=CUR_REC:=LAST MEM_REC;
  REPEAT                            REPEAT
    MOVE.L MR_PRV(A2),A2              CUR_REC:=CUR_REC.PREV;
    TST.B  MR_TYP(A2)
  IF <LT> THEN.S                    IF CUR_REC.TYPE=USER THEN
    MOVE.L A2,A3                      A3:=SOURCE_REC:=CUR_REC;
  ENDI                              ENDI;
  UNTIL <GT>                        UNTIL CUR_REC.TYPE=KERNEL;
    CLR.L  D0                         D0:=RESULT:=NOTHING_MOVED:=0;
    MOVE.L L_ALLOC,D2                 D2:=LAST MEM_REC;
    PRT_REG M_UACROS                  ********
  IF.L D2 <NE> MR_NXT(A3) THEN.S
*                                   IF LAST MEM_REC <> SOURCE_REC.NEXT THEN
 
    MOVE.L MR_FST(A3),D2              D2:=FIRST_SOURCE:=SOURCE_REC.FIRST
    ADD.L  MR_FRE(A3),D2                + SOURCE_REC.FREE_BYTES;
    MOVE.L (A3),A2                    A2:=LAST_TRY:=SOURCE_REC.NEXT;
    MOVE.L A2,D3                      D3:=TOP_SOURCE:=LAST_TRY;
  REPEAT                            REPEAT "NEXT USER PART"
    BSR.L  PGTABGET                   A1:=KERNEL PART OF D3;
    MOVE.L OB_SPA(A1),A0              A0:=TOP ADDRESS OF KERNEL PART;
    MOVE.L SP_SIZU(A0),D1             D1:=SIZE; "OF USER PART TO BE MOVED"
    MOVE.L L_ALLOC,A0                 A0:=CUR_REC:=LAST MEM_REC;
 
  REPEAT                            REPEAT "NEXT MEM_REC"
    MOVE.L MR_PRV(A0),A0              A0:=CUR_REC:=CUR_REC.PREV;
 
  IF.L MR_FRE(A0) <GE> D1 THEN.S    IF CUR_REC.FREE_BYTES >= SIZE THEN
 
    BSR.L  CREATEUS                   A0:=NEW_USER_PART(D1,A0);
    ADD.L  D1,A0                      A0:=TOP_DESTIN:=USER PART+SIZE;
    BSR.L  MOVEUSER                   MOVE USER PART (A0,A1);
    ADD.L  D1,D0                      D0=RESULT:=RESULT+SIZE; "RESULT <>0"
    MOVE.L D3,A1                      A1:=USER_PART:=TOP_SOURCE
    SUB.L  D1,A1                        - SIZE;
    MOVE.L A3,A0                      A0:=SOURCE_REC;
    BSR.L  RECHAINR                   REMOVE(D1,A0,A1); "A0,A1 SPOILED"
    MOVE.L A2,A0                      A0:=CUR_REC:=LAST_TRY; "EXIT REPEAT"
  ENDI                              ENDI;
 
  UNTIL.L  A0 <EQ> A2               UNTIL CUR_REC=LAST_TRY;
    SUB.L  D1,D3                      D3=TOP_SOURCE:=...-SIZE; "NEXT TOP"
 
  UNTIL.L  D3 <EQ> D2               UNTIL TOP_SOURCE=FIRST_SOURCE;
  ENDI                              ENDI;
    MOVE.L D0,A0                      A0:=RESULT;
    PROCRTRN USAVCRS                  RETURN;
 
    PAGE
SEARCHKN    EQU *  SEARCH FOR FREE SPACE TO A KERNEL PART
*    CALL:         RETURN:
* D1 NO_OF_BYTES   SAME     "AN INTEGRAL NUMBER OF WORDS"
* A0 ANY           MEM_REC,  OR 0 WHEN NO SPACE IS AVAILABLE
*
* A KERNEL PART CAN BE ALLOCATED IN A MEM_REC HOLDING OTHER KERNEL PARTS
* OR IN AN EMPTY MEM_REC POSITIONED BEFORE THE FIRST USER PART IN THE SYSTEM,
* OR IN THE FIRST MEM_REC HOLDING USER PARTS.
*
    ENTRYSAV  D0/D2,.SEARCHK
    MOVEQ  #MR_SIZ,D0                 D0:=HEAD_PLUS_BYTES:=HEAD_SIZE
    ADD.L  D1,D0                        + NO_OF_BYTES;
    MOVE.L F_ALLOC,A0                 A0:=CUR_MEM_REC:=FIRST MEM_REC;
  REPEAT                            REPEAT
    PRT_MEM SEARCHKN,(A0),MR_SIZ      ********
    MOVE.L MR_FRE(A0),D2              D2:=FREE:=CUR_MEM_REC.FREE_BYTES;
    CMP.L  D0,D2                      TEST D2-D0
    BGE.S  FOUND2                   WHEN HEAD_PLUS_BYTES<=FREE GOTO FOUND;
    CMP.L  D1,D2                      TEST D2-D1
    BEQ.S  TSTTYPE                  WHEN NO_OF_BYTES=FREE GOTO TSTTYPE;
  IF <GT> THEN.S                    WHEN D1 < D2 < D0 THEN
* ROOM FOR A MEM_REC HEAD IS NOT AVAILABLE, TEST WHETHER A HEAD IS NEEDED
    TST.B  MR_FIX(A0)
  IF <NE> THEN.S                    IF CUR_MEM_REC IS FIXED THEN
TSTTYPE   EQU *                   TSTTYPE: "TEST FOR USER MEM_REC"
    TST.B  MR_TYP(A0)                 IF CUR_MEM_REC IS NOT A USER MEMREC
    BGE.S  FOUND2                     THEN GOTO FOUND "NO HEAD NEEDED";
  ENDI                              ENDI;
  ENDI                              ENDI;
*                                     A0=CUR_MEM_REC= TOO_SMALL_REC
    TST.B  MR_TYP(A0)                 TEST THE TYPE OF TOO_SMALL_REC;
    MOVEA.L MR_NXT(A0),A0             A0=CUR_MEM_REC:=TOO_SMALL_REC.NEXT
  UNTIL <LT>                        UNTIL TOO_SMALL_REC.TYPE=USER;
    MOVE.W #0,A0                    A0:= NO_SPACE:=0;
FOUND2     EQU *                  FOUND:
    RETURN .SEARCHK                   RETURN;
 
 
COMPKNEL   EQU *  COMPRESS KERNEL PARTS TO GENERATE FREE SPACE;
*    CALL:        RETURN:
* D1 NO_OF_BYTES  SAME
* A0 ANY          MEM_REC, OR 0 WHEN REQUIRED SPACE WAS NOT GENERATED
*
* MOVES ALL KERNEL PARTS TOWARDS THE LOW ADDRESS END OF THE RAM AREA
* IF A HOLE IS CREATED DURING THIS PROCESS WITH A SIZE>=D1, THE PROCEDURE
* RETURNS IMMEDIATELY WITH A0 POINTING TO THAT HOLE
*
  REPEAT                            REPEAT
    BSR.S  C_KPARTS                   COMPRESS EACH KERNEL RAM(D1,A0:=MEM_REC);
    CMPA.W #0,A0                      TEST A0.L
  IF <NE>  THEN.S                   IF A MEM_REC WAS RETURNED IN A0 THEN
    RTS                               RETURN;  "OK"
  ENDI                              ENDI;
    BSR.L  M_KACROS                   MOVE KERNEL ACROSS NO_RAM(D1,A0:=RESULT);
    CMPA.W #0,A0                      TEST A0.L
  UNTIL <EQ>                        UNTIL RESULT=NOTHING MOVED;
    RTS                               RETURN;
 
    PAGE
C_KPARTS   EQU *  COMPRESS EACH KERNEL RAM AREA TO GENERATE FREE SPACE
*    CALL:           RETURN:
* D1 NO_OF_BYTES     SAME
* A0 ANY             MEM_REC, OR 0 WHEN REQUIRED SPACE WAS NOT AVAILABLE
*
* ALL CONSEQUTIVE RAM AREAS HOLDING KERNEL PARTS ARE PROCESSED;
* THE KERNEL PARTS ARE PLACED IN THE LOW ADDRESS END OF THE ENCLOSING RAM AREA
*
    PROCSAVE  D0/D2/D3/D7/A1-A3,KSAVPRT
    MOVEQ  #MR_SIZ,D0                 D0:=HEAD_PLUS_BYTES:=HEAD_SIZE
    ADD.L  D1,D0                        + NO_OF_BYTES;
    MOVE.L F_ALLOC,A3                 A0:=A3:=FIRST_REC:=FIRST MEM_REC;
    MOVE.L A3,A0                      .
    MOVE.L MR_FRE(A3),D7              D7:=FREE IN FIRST;
    TST.B  MR_TYP(A3)                 TEST TYPE OF FIRST;
 
  REPEAT                            REPEAT
* A3=A0 POINTS TO THE HEAD OF A RAM AREA, I.E. AN INITIAL MEM_REC_HEAD
* D7= FREE IN THE INITIAL MEM_REC, CCR=TYPE OF MEM_REC.
 
    PRT_MEM C_K_FIX,(A3),MR_SIZ     ********
    BLE.L  C_KPART2                 WHEN TYPE<>KNEL GOTO DON'T_COMPRESS_FIRST
 
* A3 = FIRST_REC IS A KERNEL MEM_REC,
 
    TST.L  D7
  IF <GT> THEN.S                    IF FIRST_REC.FREE_BYTES >0 THEN
    MOVE.L MR_FST(A3),A0              A0:=DESTINATION:=FIRST_REC.FIRST;
    MOVE.L A3,A2                      A2:=SOURCE_REC:=FIRST_REC;
    BRA.S  C_KPART1                   GOTO COMPRESS_FIRST;
  ENDI                              ENDI;
    MOVE.L MR_NXT(A3),A0              A0:=SOURCE_REC:=FIRST_REC.NEXT;
    MOVE.L MR_FRE(A0),D7              D7:=FREE:=SOURCE_REC.FREE_BYTES;
    TST.B  MR_FIX(A0)
  IF <GT> THEN.S                    IF SOURCE_REC IS AN INITIAL MEM_REC THEN
* BECAUSE FIRST_REC.FREE_BYTES=0(SEE ABOVE),THE WHOLE AREA MUST BE OCCUPIED
    MOVE.L A3,A0                      A0:=LAST REC IN AREA := FIRST_REC;
  ELSE.L                            ELSE  "SOURCE_REC IS NOT INITIAL"
    BRA.S  C_KPART2                   GOTO DON'T_COMPRESS_FIRST;
 
    PAGE
  REPEAT                            REPEAT  "NEXT KERNEL MEM_REC"
    MOVE.L A0,A2                      A2:=SOURCE_REC; "A0=DESTINATION"
C_KPART1   EQU *                    COMPRESS_FIRST:
* MOVE CAN BE PERFORMED ON ALL KERNEL PARTS OF SOURCE_REC
 
    MOVE.L MR_FST(A2),A1              A1:=KERNEL_PART:=SOURCE_REC.FIRST
    ADD.L  MR_FRE(A2),A1                + SOURCE_REC.FREE_BYTES;
 
    MOVE.L MR_NXT(A2),A2              A2:=NEXT_REC:=SOURCE_REC.NEXT;
 
  REPEAT                            REPEAT "NEXT KERNEL PART IN SOURCE_REC"
    MOVEQ  #0,D3
    MOVE.W OB_SIZK(A1),D3             D3.L:=SIZE:=KERNEL_PART.SIZE;
    PRT_REG C_K_REGS                  ********
    BSR.L  MOVEKNEL                   MOVE THE KERNEL PART (A0,A1);
    ADD.L  D3,A0                      A0:=DESTINATION:=...+SIZE;
    ADD.L  D3,A1                      A1:=KERNEL_PART:=...+SIZE;
  UNTIL.L  A1 <EQ> A2               UNTIL KERNEL_PART=NEXT_REC;
 
    MOVE.L A2,D7                      D7:=FREE:=NEXT_REC
    SUB.L  A0,D7                        - DESTINATION;
    TST.B  MR_FIX(A2)
  IF <GT> THEN.S                    IF NEXT_REC IS AN INITIAL MEM_REC THEN
    CLR.W  D2                         D2:=TYPE:=EMPTY:=0;
  ELSE.S                            ELSE
    MOVE.W MR_FIX(A2),D2              D2:=TYPE:=NEXT_REC.TYPE;
    ADD.L  MR_FRE(A2),D7              D7=FREE:=FREE+NEXT_REC.FREE_BYTES;
    MOVE.L MR_NXT(A2),A2              A2=NEXT_REC:=NEXT_REC.NEXT;
  ENDI                              ENDI;
 
    NEWHEAD A0,D2,A3,A2               A0:=SOURCE_REC:=CREATE NEW HEAD;
    MOVE.L D7,MR_FRE(A0)              D2=TYPE, A3=PREV, A2=NEXT, D7=FREE_BYTES.
    PRT_MEM C_K_NEW,(A0),MR_SIZ       ********
 
C_KPART2   EQU *                  DON'T_COMPRES_FIRST:
 
    TST.B  MR_TYP(A0)
  IF <LT> THEN.S                    IF SOURCE_REC.TYPE=USER THEN
    BRA.S  C_KPART3                   GOTO TERMINATE;
  ENDI                              ENDI;
 
  IF.L D7 <GE> D0 OR.L D7 <EQ> D1 THEN.S IF SPACE IS AVAIL. IN SOURCE_REC THEN
    PROCRTRN KSAVPRT                  RETURN;
  ENDI                              ENDI;
 
    TST.B  MR_TYP(A0)
  UNTIL <EQ>                        UNTIL SOURCE_REC.TYPE=EMPTY;"NEXT_REC FIXED
 
    PAGE
 
  ENDI                              ENDI;  "RAM WAS NOT TOTALLY OCCUPIED"
 
* A0 = LAST REC IN RAM AREA = LAST REC IN FRONT OF NEW RAM AREA
 
    MOVE.L MR_NXT(A0),A3              A0:=A3:=FIRST_REC:=LAST_REC.NEXT;
    MOVE.L A3,A0                      .
    MOVE.L MR_FRE(A3),D7              D7:= FREE IN FIRST;
    TST.B  MR_TYP(A3)
  UNTIL <LT>                        UNTIL FIRST_REC.TYPE=USER;
 
C_KPART3   EQU *                  TERMINATE:
 
* A0 = FIRST USER MEM_REC
  IF.L D0 <GT> MR_FRE(A0) THEN.S    IF NO SPACE IS AVAILABLE IN MEM_REC THEN
    MOVE.W #0,A0                      A0:=NO SPACE:=0;
  ENDI                              ENDI;
 
    PROCRTRN KSAVPRT                  RETURN;
 
    PAGE
 
M_KACROS   EQU *  MOVE KERNEL PARTS ACROSS NO_RAM AREAS.
*    CALL:      RETURN*
* D1 #BYTES     SAME     "THE CALL VALUE IS NOT USED AT PRESENT"
* A0 ANY        =0 WHEN NOTHING HAS BEEN MOVED, <> 0 OTHERWISE.
*
* KERNEL PARTS FROM THE LAST KERNEL MEM_REC IS MOVED TO THE FREE PART OF
* MEM_RECS AT LOWER ADDRESSES.
*
 
    PROCSAVE D0-D5/A1-A3,KSAVCRS
    MOVE.L F_ALLOC,A2                 A2:=CUR_REC:=FIRST MEM_REC;
  REPEAT                            REPEAT;
    TST.B  MR_TYP(A2)                 CURTYPE:=CUR_REC.TYPE
  IF <GT> THEN.S                    IF CURTYPE=KERNEL THEN
    MOVE.L A2,A3                      A3:=SOURCE_REC:=CUR_REC;
  ENDI                              ENDI;
    MOVE.L (A2),A2                    A2=CUR_REC:=CUR_REC.NEXT;
  UNTIL <LT>                        UNTIL CURTYPE=USER;
* SOURCE_REC IS A FIXED MEM_REC
    CLR.L  D0                         D0:=RESULT:=NOTHING_MOVED:=0;
    MOVE.L F_ALLOC,A0                 A0:=DESTIN_REC:=FIRST MEM_REC
    PRT_REG M_KACROSS
  IF.L A0 <NE> A3 THEN.S            IF FIRST MEM_REC <> SOURCE_REC THEN
    MOVE.L MR_FST(A3),D2              D2:=FIRST_SOURCE:=SOURCE_REC.FIRST
    ADD.L  MR_FRE(A3),D2                + SOURCE_REC.FREE_BYTES;
    MOVE.L (A3),D3                    D3:=TOP_SOURCE:=SOURCE_REC.NEXT;
 
  REPEAT                            REPEAT  "NEXT KERNEL PART"
    MOVE.L D2,A1                      A1:=KERNEL_PART:=FIRST_SOURCE;
    MOVEQ  #0,D1
    MOVE.W OB_SIZK(A1),D1             D1.L:=SIZE; "OF KERNEL PART"
    MOVEQ  #MR_SIZ,D4                 D4.L:=HEAD_PLUS_SIZE:=HEAD_SIZE
    ADD.L  D1,D4                          + SIZE;
    MOVE.L F_ALLOC,A0                 A0:= DESTIN_REC:= FIRST MEM_REC;
  REPEAT                            REPEAT  "NEXT DESTIN_REC"
    MOVE.L MR_FRE(A0),D5              D5:=FREE:=DESTIN_REC.FREE_BYTES;
  IF.L D5 <GE> D4 OR.L D5 <EQ> D1 THEN.S
*                                   IF SPACE AVAILABLE IN DESTIN_REC THEN
    BSR.L  CREATEKN                   A0:=NEW_KERNEL_PART (D1,A0);
    BSR.S  MOVEKNEL                   MOVE_KERNEL_PART (A0,A1);
    ADD.L  D1,D0                      D0=RESULT:=RESULT+SIZE;
    BSR.L  REMOVEKN                   REMOVEKN (D1,A0,A1); "A0,A1 SPOILED"
    MOVE.L A3,A0                      A0:=A3; "FORCE UNTIL LOOP TO EXIT"
  ELSE.S                            ELSE
    MOVE.L (A0),A0                    A0:=DESTIN_REC:=DESTIN_REC.NEXT;
  ENDI                              ENDI;
  UNTIL.L  A0 <EQ> A3               UNTIL DESTIN_REC=SOURCE_REC;
 
    ADD.L  D1,D2                      D2=FIRST_SOURCE:=...+SIZE;
  UNTIL.L D2 <EQ> D3                UNTIL FIRST_SOURCE=TOP_SOURCE;
  ENDI                              ENDI;
 
    MOVE.L D0,A0                      A0:=RESULT;
    PROCRTRN KSAVCRS                  RETURN;
 
    PAGE
MOVEKNEL   EQU *  MOVE KERNEL PART OF AN OBJECT
*
*          CALL:                      RETURN:
* A0 FUTURE KERNEL PART ADDRESS       SAME
* A1 KERNEL PART TO BE MOVED          SAME
* SR   20XX ->27XX/20XX DURING CALL-> 20XX
*
* ASSUMES THAT A0 < A1 OR NO OVERLAP BETWEEN THE TWO AREAS.
* STACK: PC,SR,A4/A5/A6 + AT LEAST 7 LONG WORDS.
*
* THE FOLLOWING STEPS APPEARS
*
* 1: MOVE OB_PART AND OBJECT_TYPE RELEATED INFORMATION
* 2: MOVE CONTEXTS, STARTING WITH TOP CONTEXT.
* 3: MOVE ENVELOPES, STARTING WITH TOP ENVELOPE.
* 4: MOVE SPACE DESCRIPTION
*
* SR = $2700 DURING 1 AND 4; AND WHILE RESIDENT ITEMS ARE MOVED
* SR = $2000 OTHERWISE.
*
* CHECKTIM IS CALLED AFTER EACH "CONTEXT/ENVELOPE", TO RELEASE
* THE PROCESSOR IN CASE THE TIME SLICE OF THE CALLING PROCESS
* HAS ELAPSED.
*
* ALL ADDRESSES, POINTING TO MOVED ITEMS ARE ADJUSTED AS AN
* INTEGRATED PART OF THE MOVE ALGORITHM.
*
    MOVE   #$2700,SR                  PREVENT INTERRUPTS
    PROCSAVE D0-D7/A0-A6,KSAVMOV      ALL REGISTERS ARE USED
* THE REGISTER SAVE AREA OF THE PROCESS IS USED TO SAVE SOME PART OF THE STACK
    PRT_MEM MOVEKNEL,(A7),60          ********
    MOVEM.L (A7)+,D0-D6               D0-D6:= PART OF STACK
    MOVE.L  KV_PROC,A2                A2:= CALLING PROCESS
    MOVEM.L D0-D6,PR_D0.A3(A2)        SAVE PART OF STACK IN PROC DESCR.
 
    MOVE.L A0,A2                      A2:=FUTURE KERNEL PART;
    MOVEQ  #0,D1
    MOVE.B OB_KIN(A1),D1              D1.W:=OBJECT_KIND
 
* MOVE THE FIXED ADDRESSABLE PART OF THE OBJECT ("THE OBJECT HEAD").
* A0 = DESTINATION OF OBJECT HEAD; A1 = SOURCE OF OBJECT HEAD
* A5 AND A6 ARE USED BY THE MACROES: MOVHD, MOVLM
* A2 = A0 = FUTURE KERNEL PART ADDRESS, D1.W = OBJECT KIND
 
    BSR.L  MOVEOBOB                   MOVE COMMON PART OF OBJECT
    MOVE.W D1,D2                  D2:=OBJECT KIND
    LSL.W #1,D2                     * 2;
    CASEJMP D2                    GOTO CASE OBJECT KIND OF "D2 BECOMES UNDEF";
    CASELAB MOVEGEOB                 (GENERAL OBJECT,
    CASELAB MOVEGAOB                  GATE OBJECT,
    CASELAB MOVECOOB                  CONDITION OBJECT,
    CASELAB ENDOFFIX                  END_OF_CASE, "OPEN OBJECT
    CASELAB MOVEPROB                  DORMANT OBJECT,
    CASELAB MOVEPROB                  PROCESS OBJECT,
    CASELAB MOVEEXOB                  EXTENSION OBJECT,
    CASELAB MOVESEOB                  SEGMENT OBJECT);
*   ALLOC AND SCHEDULER KERNELTYPES ARE NEVER MOVED.
 
    PAGE
*                                 MOVE
MOVEGEOB     EQU *                =GENERAL OBJECT=
    MOVHD  A1,A0,A2                   EXECUTED_BY CHAIN HEAD
    MOVE.L (A1)+,(A0)+                CONTROL_PROC + NO_OF_TEMPS
    MOVE.L (A1)+,(A0)+                NO_OF_TEMP_DATA IN STACK
    MOVE.L (A1)+,(A0)+                SIZE OF REQUIRED
    MOVE.W (A1)+,(A0)+                FREE CALL STACK
    MOVE.L (A1)+,(A0)+                LOGICAL STACK ADDRESS
    MOVE.L (A1)+,(A0)+                LOGICAL ENTRY ADDRESS
    BRA.L  ENDOFFIX
MOVEGAOB     EQU *                =GATE OBJECT=
    BSR.L  MOVESMPL                   SIMPLE POINTER TO SCHEDULER
    MOVHD  A1,A0,A2                   COND MANSET CHAIN HEAD
    MOVLM  A1,A0                      LOCKING START ELEMENT
    MOVLM  A1,A0                      RELOCKING START ELEMENT
    MOVLM  A1,A0                      SPEED RELOCKING START ELEMENT
    MOVE.W (A1)+,(A0)+                STATE + LEVEL
    BRA.L  ENDOFFIX
MOVECOOB   EQU *                  =CONDITION OBJECT=
    MOVLM  A1,A0                      MANAGER ELEMENT
    MOVE.L (A1)+,(A0)+                MANAGER ADDRESS
    MOVHD  A1,A0,A2                   COND.WAIT CHAIN HEAD
    BRA.L  ENDOFFIX
MOVEPROB   EQU *                  =PROCESS OBJECT=
    LEA    -OB_SIZ(A1),A5             A5 := OLD PROC ADDRESS;
  IF.L A5 <EQ> KV_PROC THEN.S       IF CURRENT PROC IS MOVED THEN
    PRT_MEM ***CUR_PROC_MOVED,(A2),OB_SIZ ********
    MOVE.L A2,KV_PROC                 CURRENT PROC ADDRESS:= NEW PROC ADDRESS;
  ENDI                              ENDI;
    MOVHD  A1,A0,A2                   CONTEXT CHAIN HEAD
    MOVHD  A1,A0,A2                   EXTENSION CHAIN HEAD
    ADDQ.L #4,A1
    MOVE.L A2,(A0)+                   PROCESS ADDRESS POINTS TO PROC ITSELF
    MOVLM  A1,A0                      MAIN QUEUE ELEMENT
    MOVLM  A1,A0                      AUX  QUEUE ELEMENT
    MOVE.L (A1)+,(A0)+                CONDITION ADDRESS
    MOVLM  A1,A0                      PROCTERM START ELEMENT
    BSR.L  MOVE68                     (12+2+2+1) LONG WORDS * 4;
    BRA.S  ENDOFFIX
MOVEEXOB   EQU *                  =EXTENSION OBJECT=
    MOVHD  A1,A0,A2                   CONTEXT CHAIN HEAD
    MOVLM  A1,A0                      EXTENSION ELEMENT
    MOVE.L (A1)+,(A0)+                PROCESS ADDRESS
    BRA.S  ENDOFFIX
MOVESEOB   EQU *                  =SEGMENT OBJECT=
    BSR.L  MOVESESU                   MOVE SE+SU FIELDS OF OBJECT
ENDOFFIX   EQU *                  END CASE;
 
    PAGE
* ADJUST SPACEDESCR OF OBJECT, AND ENVELOPES REF TO OBJECT
 
    MOVE.L OB_SPA(A2),A3              A3:=SPACE PART OF OBJECT
    MOVE.L A0,SP_FIRK(A3)             FIRST_FREE_KERNEL BYTE:=FIRST FREE DESTIN
    LEA    SP_ENV(A3),A5              A5:=ENVELOPE CHAIN HEAD;
    MOVE.L (A5),A6                    A6:=CUR:=FIRST_ENV;
  WHILE.L A6 <NE> A5 DO.S           WHILE CUR <> HEAD DO
    MOVE.L A2,CH_HOLD(A6)               CUR.OBJREF:=ADDR OF MOVED OBJECT HEAD;
    MOVE.L (A6),A6                      CUR:=CUR.NEXT;
  ENDW                              ENDW;
 
    SUBA.L A1,A0                      D2:=A0:=DISTANCE:=DESTINATION-SOURCE
    MOVE.L A0,D2                        (=(A0-A1));
 
    MOVE.L SP_SIZU(A3),D0             D0:= SIZE_USER_PART;
  IF <NE> THEN.S                    IF USER PART PRESENT THEN
    ADD.L  SP_FIRU(A3),D0             D0:=TOP USER PART:=D0 + FIRST_USER_PAR ;
    MOVE.L A2,A1                      A1:=KERNEL PART ADDRESS; "(THE NEW ONE)"
    BSR.L  PGTABSET                   PAGETABLE(D0=TOPADDRESS):=A1=KERNEL PART);
  ENDI                              ENDI; USER PART WAS PRESENT
 
    PAGE
 
* D2 = DISTANCE TO MOVE ENVELOPES, CONTEXTS AND SPACE DESCR OF OBJECT
* A2 = FIXED PART OF OBJECT, WHICH HAS BEEN MOVED
* D1 = OBJECT KIND
    MOVE.L A2,CUROBOB                 CUROBOB:=ADDRESS OF MOVED OBJECT HEAD
    MOVE.L D2,CURDIST                 CURDIST:=DISTANCE TO MOVE REST OF OBJECT
 
    MOVE   #$2000,SR                  "ALLOW DRIVER SCHEDULING"
 
  IF.B D1 <EQ> #OB_PROB OR.B D1 <EQ> #OB_EXOB THEN.L
*                                   IF OBJECT CONTAINS CONTEXTS THEN
* MOVE CONTEXTS ONE BY ONE. ALLOW SCHEDULER TO RUN OTHER PROCESSES IN BETWEEN
    LEA    EX_CTX(A2),A3              CURKHEAD:= A3:=
    MOVE.L A3,CURKHEAD                ADDRESS OF CONTEXT STACK HEAD
    MOVE.L 4(A3),A3                   A3:=CONTEXT:=CONTEXT_HEAD.LAST;
    MOVE.L A3,CURKMOVE                CURRENT MOVE CANDIDATE:=CONTEXT;
    LEA    -CT_STK(A3),A6             A6:= BASE OF CTX
  IF.L KV_CTX <EQ> A6 THEN.S        IF CUR_CTX IS MOVED THEN
    PRT_MEM ***CUR_CTX_MOVE,(A6),CT_SIZ+6*PT_SIZ ********
    ADD.L  D2,KV_CTX                  CUR_CTX:=... + DISTANCE
  ENDI                              ENDI;
  WHILE.L A3 <NE> CURKHEAD DO.L     WHILE MORE CONTEXT DO
    LEA    -CT_STK(A3),A3             MOVE COMMON PART OF CONTEXT,
    BSR.L  MOVEENCT                   A0:=DESTINATION, A1:=SOURCE,
*                                     A2:= NEW ADDRESS OF CONTEXT
    MOVE.L (A1)+,(A0)+                SIZE OF FREE STACK + TOP TEMP
    MOVE.L (A1)+,(A0)+                .
  FOR.W  D0=#1 TO #4 DO.S           FOR I = 1 TO 4 DO
    BSR.L  MOVESMPL                   MOVE MMU_POINTER;
    MOVE.L (A1)+,(A0)+                MOVE MMU_REGISTER CONTENT;
    MOVE.L (A1)+,(A0)+                .
  ENDF                              ENDF;
    JSR.L  MOVE000-(CT_SIZW+5)*2      MOVE WORK AREA AND 5 LONGS
    MOVE.L A3,A4                      A4:=ABS ADDR OF TOP TEMP POINTER :=
    ADDA.W CT_TOPT(A2),A4             CONTEXT + REL ADDR OF TOP TEMP;
  WHILE.L A1 <NE> A4 DO.S           WHILE MORE TEMP POINTERS DO
    BSR.L  MOVEPOIN                   MOVE ONE POINTER;
  ENDW                              ENDW;
 
    MOVE.L A3,A4                      A4:=ABS ADDR OF TOP FORMAL POINTER :=
    ADDA.W CT_TOPF(A2),A4             CONTEXT + RELADDR OF TOP FORMAL;
  WHILE.L A1 <NE> A4 DO.S           WHILE MORE FORMALS DO
    BSR.L  MOVEPOIN                   MOVE ONE POINTER;
    MOVLM  A1,A0                      MOVE ACTUAL CHAIN ELEMENT;
    MOVE.L (A1)+,(A0)+                MOVE REF TO CTX OR ENV;
    MOVE.W (A1)+,(A0)+                MOVE ACTUAL OFFSET;
  ENDW                              ENDW;
    PRT_MEM MOVED_CTX,(A2),CT_SIZ+6*PT_SIZ ********
    MOVE.L CT_STK+4(A2),CURKMOVE      CUR_MOVE_CANDIDATE:=PRIOR CONTEXT;
    MOVE   #$2700,SR                  PREVENT INTERRUPTS;
    BSR.L  TIMCHECK                   ALLOW OTHER PROCESSES TO RUN;
* CURKMOVE MAY HAVE BEEN CHANGED.
    MOVE   #$2000,SR                  ALLOW INTERRUPTS
    MOVE.L CURKMOVE,A3                A3:=NEXT CONTEXT TO MOVE
  ENDW                              ENDW MORE
 
  ENDI                              ENDI; CONTEXTS WAS PRESENT IN PROC ø EXT
    PAGE
* SR=$2000, CONTEXTS HAVE BEEN MOVED IF PRESENT, CUROBOB AND CURDIST ARE VALID
* MOVE ENVELOPES
 
    MOVE.L CUROBOB,A4                 A4:=ADDRESS OF MOVED OBJECT HEAD
    MOVE.L OB_SPA(A4),A4              A4:=SPACE PART OF     OBJECT
    LEA    SP_ENV(A4),A3              A3:=ENVELOPE STACK CHAIN HEAD
    MOVE.L A3,CURKHEAD                CURKHEAD:=ENVELOPE STACK CHAIN HEAD
    MOVE.L 4(A3),A3                   A3:=TOP ENVELOPE:=ENVELOPE_HEAD.LAST;
    MOVE.L A3,CURKMOVE                CURKMOVE:=TOP ENVELOPE;
 
  WHILE.L A3 <NE> CURKHEAD DO.S     WHILE MORE ENVELOPES DO
    LEA    -ST_STK(A3),A3             A3:=FIRST ADDRESS OF FIXED ENV FIELDS;
    BSR.L  MOVEENCT                   MOVE COMMON PART OF ENVELOPE =>
*                  A1:=SOURCE; A0:=DESTINATION; A2:= NEW ADDRESS OF CONTEXT;
    MOVHD  A1,A0,A2                   MOVE REF_ENV CHAIN HEAD;
    BSR.L  MOVE20                     MOVE TERM_PROC+SIZE_VALUES OF ENV;
    MOVE.W (A1)+,(A0)+                MOVE EN_COU OF ENVELOPE
 
    MOVE.L A3,A4                      A4:=ABS ADDR OF TOP LOCAL POINTER:=
    ADDA.W ST_TOP(A2),A4              ENVELOPE+REL ADDR OF TOP LOCAL;
  WHILE.L A1 <NE> A4 DO.S           WHILE MORE LOCAL POINTERS DO
    BSR.L  MOVEPOIN                   MOVE ONE POINTER;
  ENDW                              ENDW;
    PRT_MEM MOVED_ENV,(A2),EN_SIZ+3*PT_SIZ ********
    MOVE.L ST_STK+4(A2),CURKMOVE      CURKMOVE:=PRIOR ENVELOPE;
    MOVE   #$2700,SR                  PREVENT INTERRUPT PROCESSING;
    BSR.L  TIMCHECK                   ALLOW OTHER PROCESSES TO RUN;
* CURKMOVE MAY HAVE CHANGED AT RETURN
    MOVE   #$2000,SR                  ALLOW INTERRUPTS
    MOVE.L CURKMOVE,A3                A3:=CUR_MOVE_CANDIDATE;
  ENDW                              ENDW;
 
* MOVE SPACE DESCR OF OBJECT          REGISTER CONTENT ARE NOT USED BELOW
    MOVE.W #$2700,SR                  PREVENT INTERRUPTS
    CLR.L CURKMOVE                    NO MOVE CANDIDATE
    BSR.L TIMCHECK                    ENSURE ONE CHECK_CALL FOR EACH MOVE_CALL
    MOVE.L CUROBOB,A4                 A4:=OBJECT HEAD
    MOVE.L OB_SPA(A4),A1              A1:=SOURCE:=SPACE PART
    MOVE.L A1,A0                      A0:=DESTINATION:=SOURCE
    ADDA.L CURDIST,A0                   + DISTANCE;
    MOVE.L A0,OB_SPA(A4)              OBJECT.SPACE_PART:=DESTINATION;
    LEA    SP_ENV(A1),A1              A1:=SOURCE OF ENVELOPE STACK CHAIN HEAD
    LEA    SP_ENV(A0),A0              A0:=DESTIN OF ENVELOPE STACK CHAIN HEAD
    MOVLM  A1,A0                      MOVE ENVELOPE STACK CHAIN HEAD
    MOVE.L (A1)+,(A0)+                MOVE NO_OF_FREE USER BYTES;
    MOVE.W (A1)+,(A0)+                MOVE NO_OF_FREE KERNEL BYTES;
    MOVE.L (A1)+,(A0)+                MOVE FIRST FREE USER BYTE;
    MOVE.L (A1)+,(A0)+                MOVE FIRST FREE KERNEL BYTE;
    MOVE.L (A1)+,(A0)+                MOVE TOTAL SIZE OF USER PART;
 
* RESTORE SUPERVISOR STACK
    MOVE.L KV_PROC,A2                 A2:= CALLING PROCESS;
    MOVEM.L PR_D0.A3(A2),D0-D6        D0-D6:= SAVED STACK
    MOVEM.L D0-D6,-(A7)               RESTORE STACK
    MOVE.W #$2000,SR                  ALLOW INTERRUPTS
  PROCRTRN KSAVMOV                  RETURN
 
    PAGE
MOVEOBOB   EQU *  MOVE OB FIELDS OF OBJECT
*          CALL:                      RETURN:
* A1 = SOURCE                         SAME (UPDATED)
* A0 = DESTINATION                    SAME (UPDATED)
* A2 = FUTURE OBJECT (=A0)            SAME
* A5,A6 SPOILED BY MACROS
*
    PRT_MEM MOVEOBOB,(A1),PR_SIZ      ********
    MOVLM  A1,A0                      ; OWNER ELEMENT
    MOVE.L (A1)+,(A0)+                ; OWNER ADDRESS
    MOVE.W (A1)+,(A0)+                ; OWNER POINTER OFFSET
    MOVHD  A1,A0,A2                   REFERRED BY CHAIN HEAD
    MOVE.L (A1)+,(A0)+                RESIDENT COUNT+SIZE OF KNEL PART
    MOVE.W (A1)+,(A0)+                OBJECT KIND AND STATE
    MOVE.L (A1)+,(A0)+                ADDRESS OF SPACE PART
    RTS
 
 
MOVESESU   EQU *  MOVE SE(+SU) FIELDS OF OBJECT
*          CALL:                      RETURN:
* A1 = SOURCE                         SAME (UPDATED)
* A0 = DESTINATION                    SAME (UPDATED)
* A2 = SEGMENT OBJECT                 SAME
*
    MOVE.W (A1)+,(A0)+                IO_COUNT
    MOVE.L (A1)+,(A0)+                FIRST PHYSICAL ADDRESS
    MOVE.L (A1)+,(A0)+                LENGTH OF SEGMENT
    MOVLM  A1,A0                      CHAIN START OF WAITING PROCS.
    BTST.B #OB_SUB,OB_STA(A2)
  IF <NE> THEN.S                    IF SEGMENT IS A SUBSEGMENT THEN
    BSR.L  MOVESMPL                   MOVE SUBSEGMENT POINTER
  ENDI                              ENDI;
   RTS
 
 
* MOVELIST USED BY MOVEKNEL, CHECK THAT:   (CT_SIZW+5) <= (72/4) ;
MOVE72  MOVE.L  (A1)+,(A0)+
MOVE68  MOVE.L  (A1)+,(A0)+
MOVE64  MOVE.L  (A1)+,(A0)+
MOVE60  MOVE.L  (A1)+,(A0)+
MOVE56  MOVE.L  (A1)+,(A0)+
MOVE52  MOVE.L  (A1)+,(A0)+
MOVE48  MOVE.L  (A1)+,(A0)+
MOVE44  MOVE.L  (A1)+,(A0)+
MOVE40  MOVE.L  (A1)+,(A0)+
MOVE36  MOVE.L  (A1)+,(A0)+
MOVE32  MOVE.L  (A1)+,(A0)+
MOVE28  MOVE.L  (A1)+,(A0)+
MOVE24  MOVE.L  (A1)+,(A0)+
MOVE20  MOVE.L  (A1)+,(A0)+
MOVE16  MOVE.L  (A1)+,(A0)+
MOVE12  MOVE.L  (A1)+,(A0)+
MOVE08  MOVE.L  (A1)+,(A0)+
MOVE04  MOVE.L  (A1)+,(A0)+
MOVE000:  RTS
 
    PAGE
MOVEPOIN   EQU *  MOVE COMMON PART OF POINTER
*          CALL:                      RETURN:
* A0       DESTINATION                SAME (UPDATED)
* A1       SOURCE                     SAME (UPDATED)
* A2       MOVED CONTEXT OR ENVELOPE  SAME
* A5+A6+D6 ANY                        UNDEF
* A5+A6    ANY                        SPOILED BY MACROES
 
    MOVE.B PT_KIN(A1),D6              D6:= POINTER KIND;
  IF.B D6 <EQ> #PT_OWN OR.B D6 <EQ> #PT_MAN THEN.S   IF SET POINTER THEN
 
    MOVHD  A1,A0,A2                   MOVE POINTER SET HEAD
    ADDQ.L #4,A1                      SKIP 4 BYTES
    ADDQ.L #4,A0                      NOT USED
    MOVE.W (A1)+,(A0)+                MOVE KIND + INF
    RTS                               RETURN;
  ENDI                              ENDI;
 
  IF.B D6 <EQ> #PT_INT THEN.S            IF INTERRUPT POINTER THEN
 
    LEA    DRIV_TAB,A6                A6:=ADDRESS OF DRIVER_TABLE
    CLR.W  D6
    MOVE.B PT_INF(A1),D6              D6:=VECTOR INDEX:=POINTER INF
    LSL.W  #2,D6                        TABLE_OFFSET:=RECTOR INDEX *4;
    LEA    EN_STK(A2),A5              A5:= ENV.STACK;
    MOVE.L A5,(A6,D6.W)               DRIVERTABLE.TABLE_OFFSET:=ENV.STACK;
    PRT_MEM ***DRIV_MOVE,<(A6,D6.W)>,4 ********
* CONTINUE IN >> MOVE SIMPLE POINTER <<
  ENDI                              ENDI;
 
MOVESMPL   EQU * MOVE SIMPLE POINTER :  A2 IS NOT USED, A0/A1 IS UPDATED.
 
    MOVLM  A1,A0                      MOVE POINTER CHAIN ELEMENT
    MOVE.L (A1)+,(A0)+                MOVE POINTER REFFERENCE
    MOVE.W (A1)+,(A0)+                MOVE KIND + INF
    RTS
 
    PAGE
MOVEENCT   EQU *  MOVE COMMON PART OF ENVELOPE AND CONTEXT
*          CALL:                      RETURN:
* A3 = STACK ELEMENT (ENV/CTX)        SAME
* A2       ANY                        ENV/CTX     (AFTER MOVE)
* A0       ANY                        DESTINATION (AFTER MOVE)
* A1       ANY                        SOURCE      (AFTER MOVE)
* SR MAY BE CHANGED TO $2700
 
    MOVE.L A3,A1                      A1:=SOURCE:=STACK ELEMENT
    ADDA.W ST_FIR(A3),A1                +FIRST EMBEDDED;
    MOVE.L A1,A0                      A0:=DESTINATION:=SOURCE
    ADDA.L CURDIST,A0                   +DISTANCE;
  WHILE.L A1 <NE> A3 DO.S           WHILE EMBEDDED SEGMENT OBJECTS DO
    MOVE.L A0,A2                      A2:=FUTURE EMBEDDED OBJECT:= DESTINATION;
    TST.W  OB_RES(A2)
  IF <NE> THEN.S                    IF EMBEDDED.RESIDENT_COUNT <> 0 THEN
    MOVE   #$2700,SR                  NO INTERRUPT PROCESSING IS ALLOWED
  ENDI                              ENDI;
    BSR.L  MOVEOBOB                   MOVE OB OF EMBEDDED
    BSR.L  MOVESESU                   MOVE SESU OF EMBEDDED
    MOVE   #$2000,SR                  ALLOW INTERRUPT PROCESSING
  ENDW
 
  TST.W    ST_RES(A3)               IF STACK_ELEMENT.RESIDENT COUNT <> 0 THEN
  IF <NE> THEN.S                      ENSURE THAT INTERRUPT PROCESSING
    MOVE   #$2700,SR                  CANNOT BE SCHEDULED;
  ENDI                              ENDI;
 
* MOVE FIXED PART OF STACK ELEMENT
 
    MOVE.L A0,A2                      A2:=NEW POSITION OF CONTEXT/ENVELOPE
    MOVLM  A1,A0                      EXECUTED BY/MANAGED BY - ELEMENT
    MOVE.L (A1)+,(A0)+                OBJECT     /ENVELOPE
    MOVE.W (A1)+,(A0)+                MODE+SPEED /MANAGER POINTER
    MOVLM  A1,A0                      STACK ELEMENT-ELEMENT
    MOVE.L (A1)+,(A0)+                HOLDING OBJECT
    MOVHD  A1,A0,A2                   ACTUAL POINTER CHAIN HEAD
    MOVE.L (A1)+,(A0)+                TOP POINTER + FIRST EMBEDDED
    MOVE.L (A1)+,(A0)+                RESIDENT COUNT + IO_COUNT
 
    RTS                               RETURN
*                                     "SR MAY BE $2700 AT RETURN"
 
    PAGE
MOVEUSER   EQU *  MOVE USER PART
*          CALL:                     RETURN:
* A0 TOPADDRESS OF NEW USER PART     SAME
* A1 KERNEL PART ADDRESS             SAME
* SR  20XX ->27XX/20XX DURING CALL-> 20XX
* A0 > OLD TOP ADDRESS OR NO OVERLAP BETWEEN AREAS HOLDING THE USER PART
* STACK: PC,SR,A4/A5/A6 + AT LEAST 8 LONG WORDS
*
* MOVES THE USER PART (A1) TO THE NEW LOCATION (A0). THE HIGH ADDRESS
* PARTS ARE MOVED FIRST. THE PROCESS IS DIRECTED BY THE EMBEDDED
* SEGMENT OBJECTS OF THE ENVELOPES (OLDEST PROCESSED FIRST) AND
* CONTEXTS (OLDEST PROCESSED FIRST) AND BY THE OBJECT HEAD IN CASE
* A1 IS A NON EMBEDDED SEGMENT OBJECT.
* IN CASE THE RESIDENT COUNT OF THE SEGMENT IS NON ZERO. SR:=$2700
* WHILE THE MOVE IS PERFORMED. OTHERWISE SR:=$2000
* IN CASE THE IO_COUNT OF THE SEGMENT IS NON ZERO, THE PROCESS WAITS
* UNTIL IT BECOMES ZERO.
*
* IN CASE TIME_OUT HAS OCCURED, THE CURRENT PROCESS SUSPENDS ITSELF
*
* ALL ADDRESSES POINTING TO MOVED SEGMENTS ARE ADJUSTED AS AN
* INTEGRATED PART OF THE MOVE ALGORITHM.
 
    MOVE.W #$2700,SR                  PREVENT INTERRUPTS
    PRT_MEM MOVEUSER,(A7),80          ********
    PROCSAVE D0-D7/A0-A6,USAVMOV      SAVE REGISTERS
* SAVE PART OF THE STACK IN THE PROC DESCRIPTION OF CALLING PROCESS.
    MOVE.L KV_PROC,A2                 A2:= CALLING PROC.
    MOVEM.L (A7)+,D0-D7               D0-D7:= 8 LONG WORDS FROM STACK
    MOVEM.L D0-D7,PR_D0.A3(A2)        SAVE STACK IN PROC DESCR.
 
    MOVE.L OB_SPA(A1),A2              A2:=SPACE DESCR OF OBJECT;
    MOVE.L A0,D0                      D0:=NEW TOP ADDRESS;
    BSR.L  PGTABSET                   PAGETABLE(D0=TOP ADDRESS):=KNEL PART=A1;
    SUB.L  SP_SIZU(A2),D0             D0:=NEW FIRST ADDRESS:=NEW TOP ADDRESS-SIZ
    MOVE.L D0,USERDEST                USER DESTINATON:=NEW FIRST ADDRESS
    CLR.L  CURUMOVE                   CURRENT MOVE CANDIDATE:=NO CANDIDATE;
    SUB.L  SP_FIRU(A2),D0             D0:=DISTANCE:=NEW_FIRST-OLD_FIRST;
    MOVE.L D0,CURDIST                 CURDIST:=DISTANCE;
    MOVE.L A1,CUROBOB                 CUROBOB:=KERNEL PART;
    LEA    SP_ENV(A2),A3              A3:=CUR_HEAD:=
    MOVE.L A3,CUR_HEAD                ENVELOPE STACK CHAIN HEAD;
    MOVE.L (A3),A4                    A4:=FIRST_ENV:=CUR_HEAD.FIRST
    PAGE
MOVEUSXX   EQU *                  MOVE_EMBEDDED_SEGMENTS:
 
  WHILE.L A4 <NE> CUR_HEAD DO.S     WHILE MORE STACK ELEMENTS DO
    CLR.W  D4                         D4:=CUR_SEGM:=DUMMY SEGMENT;
  REPEAT                            REPETE "FOR ALL EMBEDDED SEGMENTS"
* FIND OLDEST SEGMENT YOUNGER THAN CUR_SEGM (CALLED NEXT_SEGM BELOW).
    CLR.W  D5                         D5:=NEXT SEGM:=NO NEXT:=0;
    MOVE.W ST_FIR-ST_STK(A4),D3       D3:=OLDER:=YOUNGEST SEGMENT;
  WHILE.W D3 <NE> D4 DO.S           WHILE OLDER <> CUR_SEGM DO
    BTST.B #OB_SUB,OB_STA-ST_STK(A4,D3.W)
  IF <EQ> THEN.S                    IF OLDER IS NOT A SUBSEGM THEN "ROOT SEGM"
    MOVE.W D3,D5                      D5:=NEXT_SEGM:=OLDER;
  ENDI                              ENDI;
    ADD.W  OB_SIZK-ST_STK(A4,D3.W),D3 D3:=OLDER:=SEGMENT OLDER THAN OLDER;
  ENDW                              ENDW;
    MOVE.W D5,D4                      CUR_SEGM:=NEXT_SEGM;
  IF <NE> THEN.S                    IF CUR_SEGM <> NO NEXT THEN
* MOVE THE SEGMENT DESCRIBED BY A4 AND D4.
    LEA    -ST_STK(A4,D4.W),A2        A2:=SEGMENT OBJECT;
    BSR.L  CHK_TIR                    CHECK FOR TIME_OUT,IO,RESIDENT
*                  D4/A2/A4 ARE STILL VALID, SR MAY HAVE BEEN CHANGED TO $2000
  IF <EQ> THEN.S                    IF SEGMENT DELETED THEN
    MOVE.L CUROBOB,A2                 A2:=KERNEL PART;
    BRA.S  MOVEUSEX                   GOTO NO_MORE_SEGMENTS;
  ENDI                              ENDI;
    BSR.L  MOVESEDA                   MOVE DATA AND ADJUST ADDRESS; SR:=$2700;
    TST.W  D4                         RESTORE CONDITION CODES TO <NE>
  ENDI                              ENDI;
  UNTIL <EQ>                        UNTIL NO MORE EMBEDDED SEGMENTS;
    MOVE.L (A4),A4                    A4:=NEXT STACK ELEMENT;
  ENDW                              ENDW; MORE STACK ELEMENTS
 
    MOVE.L CUROBOB,A2                 A2:=KERNEL PART;
    MOVE.L OB_SPA(A2),A4              A4:=SPACE DESCRIPTION;
    LEA    SP_ENV(A4),A3              A3:=ENVELOPE STACK;
  IF.L A3 <EQ> CUR_HEAD THEN.S      IF ENVELOPE DATA HAS JUST BEEN MOVED THEN
    MOVE.B OB_KIN(A2),D4              D4:= OBJECT KIND;
  IF.B D4 <EQ> #OB_PROB OR.B D4 <EQ> #OB_EXOB THEN.S
*                                   IF OBJECT CONTAINS CONTEXTS THEN
    LEA    EX_CTX(A2),A3              A3 AND CUR_HEAD :=
    MOVE.L A3,CUR_HEAD                CONTEXT STACK CHAIN HEAD
    MOVE.L (A3),A4                    A4:=FIRST_CTX:=CUR_HEAD.FIRST;
    BRA.S  MOVEUSXX                   GOTO MOVE_EMBEDDED_SEGMENTS;
  ENDI                              ENDI;
  ENDI                              ENDI;
 
    PAGE
  IF.B OB_KIN(A2) <EQ> #OB_SEOB THEN.S
    BTST.B #OB_SUB,OB_STA(A2)       IF SEGMENT OBJECT
  IF <EQ> THEN.S                    BUT NOT A SUBSEGMENT THEN
    BSR.S  CHK_TIR                    CHECK FOR TIME_OUT,IO,RESIDENT
  IF <NE> THEN.S                    IF SEGMENT NOT DELETED THEN
    BSR.L  MOVESEDA                   MOVE DATA AND ADJUST ADDRESSES;
  ENDI                              ENDI;
  ENDI                              ENDI;
  ENDI                              ENDI;
 
MOVEUSEX   EQU *                  NO_MORE_SEGMENTS:
    MOVE.L USERDEST,SP_FIRU(A4)       FIRST USER BYTE:=NEW FIRST ADDRESS;
    CLR.L  CURUMOVE                   CUR_MOVE_CANDIDATE:=NO CANDIDATE;
 
* RESTORE SUPERVISOR STACK
    BSR.L  TIMCHECK                   ENSURE ONE CHECK_CALL FOR EACH MOVE_CALL
    MOVE.L KV_PROC,A2                 A2:= CALLING PROCESS
    MOVEM.L PR_D0.A3(A2),D0-D7        D0-D7:= SAVED STACK FROM PROC DESCR.
    MOVEM.L D0-D7,-(A7)               RESTORE STACK;
    MOVE.W #$2000,SR                  ALLOW INTERRUPTS
    PROCRTRN USAVMOV                  RETURN;
 
    PAGE
CHK_TIR    EQU *  :  TIME_OUT,IO_COUNT, RESIDENT_COUNT
*
*          CALL:                      RETURN
* D4       ANY                        SAME
* A2       SEGMENT OBJECT             SAME, OR 0 <-> OBJECT DELETED
* A4       STACK ELEMENT WHEN A2 IS EMBEDDED   SAME
* CC       ANY                        <NE>, OR <EQ> <-> OBJECT DELETED
* SR       $27XX                      $20XX OR $27XX (SE RESIDENT CHECK)
* OTHER    ANY                        UNDEF
 
* TIME_OUT CHECK: SUSPEND CURRENT PROCESS IF TIME_OUT HAS OCCURED
* IO_CHECK      : WAIT UNTIL IO_COUNT=0
* RESIDENT CHECK: ALLOW INTERRUPTS IF RESIDENT_COUNT=0;
*
    PROCSAVE D4/A4,SAV_CHK             SAVE D4/A4
    MOVE.L A2,CURUMOVE                 SET CURRENT MOVE CANDIDATE
CHK_REP  EQU *                      CHECK_AGAIN:
    BSR.L  TIMCHECK                   IF TIME_OUT THEN SUSPEND PROCESSING
    MOVE.L CURUMOVE,A2                 A2:=CURRENT MOVE CANDIDATE
    CMPA.W #0,A2                       TEST A2.L = 0;
  IF <EQ> THEN.S                   IF CURRENT MOVE CANDIDATE DELETED THEN
    PROCRTRN SAV_CHK                  RETURN WITH CC=<EQ>
  ENDI                              ENDI;
    TST.W  SE_IO(A2)
  IF <NE> THEN.S                    IF IO_COUNT <> 0 THEN "IO PENDING"
    LEA    SE_WAIT(A2),A1             A1:=TERMINATION CONDITION OF SEGMENT;
    MOVE.L KV_CTX,A2                  A2:= CURRENT CONTEXT
    BSR.L  KNELWAIT                   KNELWAIT(A1=COND,A2=CTX);
    MOVE.L CURUMOVE,A2                A2:=CURRENT MOVE CANDIDATE;
    CMPA.W #0,A2                      TEST A2.L = 0;
  IF <EQ> THEN.S                   IF CURRENT MOVE CANDIDATE DELETED THEN
    PROCRTRN SAV_CHK                  RETURN WITH CC = <EQ>
  ENDI                              ENDI;
    BRA.S  CHK_REP                    GOTO CHECK_AGAIN;"REP. THE WHOLE CHECK"
  ENDI                              ENDI;
    TST.W  OB_RES(A2)
  IF <EQ> THEN.S                    IF RESIDENT COUNT = 0 THEN
    MOVE   #$2000,SR                  ALLOW INTERRUPTS; CC:=<NE>;
  ENDI                              ENDI;
    PROCRTRN SAV_CHK                  RETURN;
 
    PAGE
MOVESEDA   EQU *  MOVE SEGMENT DATA
*
*          CALL:                      RETURN:
* A2       SEGMENT OBJECT             SAME
* D0/D1/D2/D5/D6/D7/A3/A5 ANY         UNDEF
* SR       $20XX OR $27XX (SE CHK_TIR) $2700 (=PREVENT INTERUPTS)
*
* ALL REFERENCES TO THE DATA ARE ADJUSTED,
* AND THE DATA SEGMENT IS MOVED
*
    PRT_MEM MOVESEDA,(A2),SE_SIZ      ******
    BTST.B #OB_ABOR,OB_STA(A2)        ABORTED SEGM HAS NO VALID DATA PART
  IF <EQ> THEN.S                    IF NOT ABORTED THEN
    MOVE.L CURDIST,D1                 D1:=DISTANCE TO MOVE
    LEA    NEWSEFIR,A3
    BSR.S  MM_SETRE                   SCAN TREE(A2=ROOT,A3=NODE_PROC)
    MOVE.L SE_FIR(A2),A1              A1:=SOURCE
    MOVE.L A1,A0                      A0:=DESTINATION
    ADD.L  D1,A0                        :=SOURCE+DISTANCE
    MOVE.L A0,SE_FIR(A2)              SEGMENT.FIRST_BYTE:=DESTINATION;
    MOVE.L SE_LEN(A2),D5              D5:=SEGMENT.LENGTH
    BSR.L  MOVE_MEM                   MOVE_MEMORY(A0,A1,D5);
  ENDI                              ENDI;
    MOVE   #$2700,SR                  PREVENT INTERRUPTS
    RTS                               RETURN;
 
    PAGE
MM_SETRE   EQU *  SCAN TREE OF SUBSEGMENTS AND POINTERS.
*
* A3 NODE PROCEDURE EXECUTED FOR EACH NODE IN THE TREE
* A2 ROOT NODE OF THE TREE
* SPOILED D0,D6,D7,A0
* D1-D5/A1-A5 ARE UNCHANGED WHEN THE NODE PROCEDURE IS CALLED
* A0=CURRENT NODE WHEN THE NODE PROC IS CALLED
* D0=KIND OF CURRENT NODE WHEN NODE PROC IS CALLED
*
* THE NODE PROCEDURE IS NOT CALLED ON THE ROOT NODE
* THE NODE PROCEDURE IS CALLED BEFORE THE SUBTREE IS SCANNED
 
    LEA    OB_REF(A2),A0              A0:=ROOT_HEAD;
 
SCANBRAN EQU *      "RECURSIVE PROCEDURE", CALLED ON REF_CHAIN OF EACH SEGMENT;
    MOVE.L A0,D6                      D6:=CUR_HEAD;
    MOVE.L (A0),A0                    A0:=CUR_POINTER:=CUR_HEAD.FIRST
 
  WHILE.L A0 <NE> D6 DO.S           WHILE CUR_POINTER <> CUR_HEAD DO
    MOVE.B PT_KIN(A0),D0              D0:=POINTER KIND;
    JSR    (A3)                       CALL THE NODE PROCEDURE;
    PRT_MEM ADDR_ADJUST,SE_FIR-SU_P(A0),MM_SIZ-(SE_FIR-SU_P) ********
 
  IF.B D0 <EQ> #PT_SEG THEN.S       IF POINTER=SEGM_POINTER THEN
*   THE POINTER IS PART OF A SUBSEGMENT. A SUBSEGMENT NODE STARTS
*   A NEW BRANCH IN THE TREE. SCAN THAT BRANCH.
    LEA    (OB_REF-SU_P)(A0),A0       A0:=CUR_POINTER:=HEAD OF BRANCH
    BRA.S  SCANBRAN                   "RECURSIVE CALL" OF SCANBRAN
PREVBRAN EQU *                        "RECURSIVE CALL" RETURNS TO HERE
*   RESTORE REGISTERS TO REFLECT THE RESUMED BRANCH
    LEA    SU_P-OB_REF(A0),A0         A0:=CUR_POINTER:=POINTER OF SUBSEGM;
    MOVEQ.L #OB_REF,D6                D6:=CUR_HEAD:=HEAD OF POINTER CHAIN:=
    ADD.L  PT_REF(A0),D6                 REF_CHAIN_OFFSET+BASE OF SEGMENT;
  ENDI                              ENDI;
 
    MOVE.L (A0),A0                    A0:=CUR_POINTER:=NEXT_POINTER
  ENDW                              ENDW;
 
    BTST.B #OB_SUB,OB_STA-OB_REF(A0)  TEST FOR ROOT SEGM;
    BNE.S  PREVBRAN                 WHEN SUBSEGM RETURN TO PREVIOUS BRANCH
 
    RTS                               RETURN
 
    PAGE
NEWSEFIR   EQU *  NEW VALUE OF SE_FIR SHOULD BE REGISTERED
*          CALL:                      RETURN:
* A5       ANY                        UNDEF
* D2       ANY                        UNDEF
* A0       POINTER TO BE ADJUSTED     SAME
* D1       DISTANCE SEGM WAS MOVED    SAME
* D0       POINTER KIND OF POINTER    SAME
 
  IF.B D0 <EQ> #PT_OBJ THEN.S       IF POINTER KIND=REF_OBJECT THEN
    RTS                               RETURN
  ENDI                              ENDI
 
  IF.B D0 <EQ> #PT_SEG THEN.S       IF POINTER KIND=SEGMENT THEN
    ADD.L  D1,SE_FIR-SU_P(A0)         SUBSEG.FIRST_BYTE:=...+DISTANCE
    RTS                               RETURN;
  ENDI                              ENDI
 
  IF.B D0 <EQ> #PT_MMU THEN.S       IF POINTER_KIND=MMU_DESCRIPTION THEN
    MOVE.L D1,D2                      D2:=DISTANCE
    LSR.L  #8,D2                      D2.W:=PAGE DISTANCE:=DISTANCE //256
    ADD.W  D2,MM_REG+4(A0)             MMU.RELOCATION:=....+DISTANCE
    RTS                               RETURN;
  ENDI                              ENDI
 
* A DRIVER POINTER IS FOUND
    CLR.W  D2
    MOVE.B PT_INF(A0),D2              D2.W:=VECTOR NUMBER
    LSL.W  #2,D2                      D2.W:=VECTOR INDEX := VECTOR NUMBER << 2;
    LEA    KV_INVEC,A5                A5:=ADDRESS OF INT VECTOR (NORMALLY=0)
    ADD.L  D1,(A5,D2.W)               INT_VECT(VECT_INDEX):=...+DISTANCE;
    PRT_MEM ***INT_PROC_MOVED,<(A5,D2.W)>,4 ********
    RTS                               RETURN;
 
    PAGE
MOVE_MEM  EQU *  MOVES AN INTEGRAL NUMBER OF SEGMENTS
*
* A0       DESTINATION ADDRESS        SAME
* A1       SOURCE ADDRESS             SAME
* D5       SIZE                       UNDEF
* 0 < SIZE < 2**23 MUST BE FULFILLED.
 
* ROUND UP SIZE TO AN INTEGRAL NUMBER OF PAGES.
    ADD.L  #255,D5
    CLR.B  D5
 
    ADDA.L D5,A0                      A0:=TOP DESTINATION:=DESTINATION+SIZE
    ADDA.L D5,A1                      A1:=TOP SOURCE:=SOURCE+SIZE
 
* THE LAST PART OF THE SEGMENT IS MOVED FIRST
*                                     D5:=NO OF TIMES TO REPEATE THE LIST BELOW
    LSR.L  #7,D5                        :=SIZE//128
    SUBQ.L #1,D5                      ADJUST D5 TO MATCH THE WAY 'DBF' WORKS.
 
MOVAGAIN   EQU *
 
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    MOVE.L -(A1),-(A0)
    DBF    D5,MOVAGAIN
 
* NOTE:  A0/A1 WAS COUNTED DOWN TO THE CALL VALUE BY THE LOOP ABOVE.
    RTS                               RETURN
 
 
    END  !  END OF MMPROCS
 
«eof»