; InOutLd.asm - InLd, OutLd, BootFrom ; Copyright Xerox Corporation 1979 ; last modified November 29, 1979 7:54 PM by Taft .titl InOutLd .ent OutLd, InLd, BootFrom .ent OutLdContu .bext LastLdCB .srel OutLd: .OutLd InLd: .InLd BootFrom: .BootFrom OutLdContu: .OutLdContu LastLdCB: 0 ; OutLd and InLd are used to save Alto memory images. ; A complete save-restore cycle happens like this: ; ; OutLd(fp,ptr-to-OutLd-message-area) ; writes a loader on file described by fp ; writes pages 2,3,... to 253. ; writes pages 1,0 ; returns 0 ; InLd(fp,ptr-to-InLd-message) ; reads the fp file's loader into page 0, ; copies InLd-message into page 0 ; and jumps to the loader. ; the loader reads in pages 2,3,... to 253. ; transfers to the restored outld ; outld copies the message into the OutLd-message-area ; restores page 1, via page 0 ; restores page 0 ; returns 1 from OutLd with INTERRUPTS OFF ; ; BootFrom(fp) simulates a machine boot on the file fp. ; The loader that is written makes the file bootable directly. It is just ; like the InLd call above, but the corresponding return from OutLd is ; 2, so that it can discover it was booted (no message). ; ; Immediately after a return from OutLd, LastLdCB (static) points to the ; disk command block for the last page of the file that was InLd'ed or ; Booted. ; Small print passage: Because OutLd and InLd are not reentrant ; and because Swat uses them, you should copy the disk command ; block quickly -- do not let Swat hit a breakpoint or whatever ; before this is copied, or you will simply copy the corresponding ; information for one of the Swat swapping files! ; ; If either ptr-to-xxx-message lies in page 0 or 1, the message will ; not be correctly delivered. So if you don't want to receive a message, ; simply pass that argument as 0. ; ; Both routines preserve AC2, and return to call+2, so are BCPL-callable. ; ; These routines are tightly intertwined with the "disk" routines and ; the page 0 loader. ; ; Note that OutLd and InLd themselves are NOT reentrant. ; ; Unless you know what you are doing, you should turn off interrupts ; when calling OutLd. If interrupts are popping off while state is being ; written on the file, strange things can happen (e.g., the "active" word ; can reflect the state inside an interrupt, with certain interrupt channels ; off!). ; ; structure fp: ; [ ; SerialNumber word 2 ; Version word ; blank word ; Page1RealDA word //**** differs from OS defn of FP **** ; ] ; ; OutLd/InLd file is: ; loader (1 page) ; images of pages 2 - 253. ; image of page 1 ; image of page 0 InLdMessageSize = 23 ; size of message area MaxAddr = 176777 ; highest real memory address in machine .NREL ; TLABL takes an FP pointer in ac0 and returns a label block ; pointer in ac1 -- Warning: destroys ac2. ; Therefore, returns ac2 = 0 as a handy side-effect. TLABL: STA 3 LblBlk+LblPageNum ; return address -- ok to use, CONTC resets it JSR TLABL2 ; put ptr into ac3 LblBlk: .BLK 10 ; fabricated label goes here TLABL2: MOV 0 2 ; 2 => fp LDA 0 0,2 STA 0 LblSerial1,3 LDA 0 1,2 STA 0 LblSerial2,3 LDA 0 2,2 STA 0 LblVersion,3 LDA 0 4,2 ; get disk address STA 0 LblNext,3 ; and put it in "next" MOV 3 1 ; answer ... ac1 => label block MKZERO 2 2 JMP @LblBlk+LblPageNum ; return ; Routines to copy parts of pages around: ; AC0: first source address, AC1: first destination address. ; Return with AC0 and AC1 updated to point to first words not touched. ; JSR COPY ; negative of number of words to copy ; returns here ; JSR SKIPCOPY ; negative of number of words to skip over first ; negative of number of words to copy ; returns here SKIPCOPY: STA 3 T1,2 ISZ T1 2 ; -> - words to copy for COPY LDA 3 0,3 ; - words to skip SUB 3 0 SUB 3 1 SKP COPY: STA 3 T1,2 MKONE 3 3 SUB 3 0 ; ac0← first source address -1 LDA 3 @T1,2 ; - words to copy ADC 3 1 ; ac1← last destination address BLT ; updates ac0 to last source address +1 INC 1 1 ; ac1← last destination address +1 LDA 3 T1,2 JMP 1,3 ; storage for outld and inld WRITEa: writeD Booted: .BLK 1 STACK: .BLK 1 RETRN: .BLK 1 ; outld's ra CVEC: .BLK 1 ; caller's message vector (InLd or OutLd) TVEC: MVEC-BootEntry+1 ; page 0 address of mvec TVECplus1: MVEC-BootEntry+2 ; 1+ page 0 address of mvec LabelOffseta: LABEL ; offset into KCB where label is .LastLdCB: LastLdCB ; will point to last CB used to InLd ;*********** OUTLD *********** ; AC0 = FP ; AC1 = an InLdMessageSize word vector to receive message upon resumption .OutLd: STA 3 RETRN STA 2 STACK ; Save it for returns! STA 1 CVEC ; output message vector JSR TLABL ; transform ac0 fid into ac1 -> label block! STA 2 @.BOOTFLAG ; 0 => Swatee was created by OutLd JSR GETFullDK ; get pointer to FullDK MOV 3 2 LDA 0 WRITEa ; write and check command JSRII ISETU ; set up command blocks LDA 0 @LOADRP JSR PAGEC ; write out loader LDA 0 N400 ; page 2 address-400 ; note CUR=KCB2 now STA 0 KCB2+CMADD,2 ; BMPPAGE will increment it by 400. JSRII ITRANSFER ; write out bulk of mem. (pages 2 through end) LDA 0 N400 ; write page 1 JSR PAGEC JSR PAGEC0 ; write page 0 MKZERO 0 0 ; outld finished, return 0 JMP EXIT .BOOTFLAG: 706 ; Many moons later, the other guy calls inld on what was just written ; above. He yanks in and runs the page 0 loader, which comes here ; when the core is transferred: ; The page 0 loader returns here when file is restored (all but page 0 & 1). ; interrupts are OFF. ; AC2 = pointer to FullDK (in REAL page 0) ; tvec = message vector in page 0 .OutLdContu: LDA 0 CUR,2 LDA 1 LabelOffseta ADD 0 1 ; AC 1 points to label when xfer finished JSR GETFullDK ; get the FullDK NOT in page 0 MOV 3 2 LDA 0 READ JSRII ISETU ; setup to read page 1 LDA 0 @TVEC STA 0 Booted SNZ 0 0 ; message present? JMP RP1 ; no LDA 0 TVECplus1 ; yes, ac0 ← @tvec+1 LDA 1 CVEC ; pointer outload got eons ago JSR COPY ; copy message into user's area -InLdMessageSize ; (if user's area in page 0 or 1, will be lost) RP1: JSR PAGEC0 ; read in putative page 1 into page 0 ; move it carefully into its rightful place. MKZERO 0 0 ; start at loc 0 LDA 1 N400 ; ac1 runs through page 1 JSR COPY 400-430 ; restore 400 to 427 inclusive JSR SKIPCOPY 430-431 ; skip real time clock 431-521 ; restore 431 to 520 inclusive JSR SKIPCOPY 521-524 ; skip disk command area 524-570 ; restore 524 to 567 inclusive JSR SKIPCOPY 570-600 ; skip clock storage area 600-1000 ; restore 600 to 777 inclusive JSR PAGEC0 ; page 1 restored. read in page 0. LDA 0 CUR,2 ; get pointer to current KCB STA 0 @.LastLdCB ; save for user's scrutiny LDA 0 Booted ; see if we had a message SNZ 0 0 INCZL 0 0 ; if it was zero, we booted. return 2 EXIT: LDA 2 STACK LDA 3 RETRN JMP 1,3 ; assume called from bcpl ; static pointers ISETU: DiskIOSetup ITRANSFER: DiskIOTransfer LOADRP: Page0Loader ;************ INLD ************ ; Many eons later inload is invoked by a running program, passing ; the file id of the file we just wrote on ... ; AC0 = FP of file to boot .BootFrom: MKMINUSONE 3 3 SKP ; AC0 = FP of saved memory image ; AC1 = message to be passed to it .InLd: MKZERO 3 3 STA 3 Booted ; True if we are to use boot conventions STA 1 CVEC ; input message, no need to save ac3 DIR ; no interrupts on JSR TLABL ; translates ac0 fid into ac1 label, ac2←0 STA 2 @N420 ; clear screen JSR GETFullDK MOV 3 2 LDA 0 READ ; read and check command JSRII ISETU MKONE 0 0 ; read loader into 1,2,...,400 (page 0) JSR PAGEC ; error will bomb and show lights LDA 0 CUR,2 ; get at current label block LDA 1 LabelOffseta ADD 1 0 ; ac0 => label after the read LDA 1 N402 ; copy label block JSR COPY ; to 402,403,... -10 ; because that's where boot microcode puts it ISZ Booted ; see if we're supposed to boot MKONE 3 3 SKP ; must be one (see below) JMP 1 ; yes!!!! STA 3 @TVEC ; signal message present LDA 0 CVEC ; copy message LDA 1 TVECplus1 ; to page 0 JSR COPY -InLdMessageSize JMP 3 ; jump to page 0 loader! GETFullDK: JMP DKBlock ; can't reach DKBlock from where it is needed ; Simple one-page transfer using DiskIO routines below. PAGEC0: MKZERO 0 0 ; enter here to transfer page 0 ; ac0 = main mem address to save or restore process and check page PAGEC: STA 3 T1,2 LDA 3 N400 SUB 3 0 ; compensate for BMPPAGE's work. LDA 3 CUR,2 STA 0 CMADD,3 ; and save in current block. JSR CONTC ; make up a command block JSR EXCHCB ; promote the actors STA 1 @KBLK ; start the disk JSR WAITC ; and wait for completion JMP @T1,2 ; Disk I/O routines. Used by InLd/OutLd swapper and can also be used ; by others. The special organization for a boot loader is for ; InLd/OutLd purposes. ; ;The routines all require a storage area of size DKlen to work ; with, passed in Ac2. For a template, see FullDK below. Notice that it ; has actual code in it. ;DiskIOSetup ; ac0 = disk command [read (44120) or write (44130)] ; ac1 => label block for PREVIOUS page of file. The ; LblNext entry of this label is the next DA to operate on. ; ac2 => work area of length DKlen ; returns with ac2 set up to go -- pass to DiskIOTransfer ;DiskIOTransfer ; ac2 => work area, set up. ; calls BMPPAGE (in work area; see template) to compute label and ; disk command block for the next page to transfer. ; (if you are doing a simple read or write, all you need ; to compute is the core address, because CONTC has ; computed the rest -- see code below) ; if an error occurs, transfer to BAD (in work area; see template) ; returns with ac2 full of left-over goodies (e.g., next disk address) ; In particular, ac2!CUR => KCB for last page transferred ; (perhaps in error). That KCB!LABEL is, of course, the label. .srel .ent DiskIOSetup,DiskIOTransfer DiskIOSetup: .DiskIOSetup DiskIOTransfer: .DiskIOTransfer Page0Loader: BootEntry .nrel ; Definitions for a disk label block: LblNext = 0 ; DA for next page of file LblBack = 1 ; DA for previous page of file LblSpare = 2 ; Not currently used LblNumChars = 3 ; Number of chars in last page of file LblPageNum = 4 ; Page number in file LblVersion = 5 ; File version number LblSerial1 = 6 ; File serial number (high order) LblSerial2 = 7 ; File serial number (low order) ; Definitions for a disk control block (augmented for our use) NXTCM = 0 ; Next dcb pointer STATU = 1 ; Status CMMD = 2 ; Command HDRPT = 3 ; Header block pointer (points to HDR1) LBLPT = 4 ; Label block pointer (points to NEXTP) CMADD = 5 ; Data block pointer OKINT = 6 ; No-error interrupt bit mask ERRIN = 7 ; Error interrupt bit mask HDR1 = 10 ; Here is where header gets read in CURRE = 11 ; Disk address for the command LABEL = 12 ; A place to hold a label: NEXTP = LblNext+LABEL BACKP = LblBack+LABEL UNUSE = LblSpare+LABEL NUMCH = LblNumChars+LABEL PN = LblPageNum+LABEL VN = LblVersion+LABEL SN1 = LblSerial1+LABEL SN2 = LblSerial2+LABEL KCBSize = SN2+1 ; Length of KCB's we use ; Now for the layout of the temporary storage area (DK) CUR = 0 ; Points to "current" KCB ALT = 1 ; Points to the "alternate" KCB KCB1 = 2 ; First Disk Command block -- MUST be 2 (see SetupC) KCB2 = KCB1+KCBSize ; Second command block TRYCT = KCB2+KCBSize ; Temporaries (use with care) T1 = TRYCT+1 T2 = T1+1 T3 = T2+1 BAD = T3+1 ; Control transfers here when error encountered BMPPAGE = BAD+1 ; Control transfers here to "increment" page readD = 44120 ; check header, check label, read data writeD = 44130 ; check header, check label, write data ; The page 0 loader - See BuildBoot documentation for details ; ; This code (next 256 words) was written on the saved file ; and executes in page 0. If it is booted or read in by Inld, location ; BootEntry will be 1. It expects the label for the page on which it resides ; to be found at 402, 403,... (Boot does this; so does InLd) ; ; To Boot (following is EXACTLY what boot button does): ; Turn off the display ; Disable interrupts ; Read in the first data page of the file to 1,2,... 400 ; Store the label for the page read at 402,403,...411 ; Jump to location 1. ; ; To Inld: ; Turn off the display ; Disable interrupts ; Read in the first data page of the file to 1,2,....400 ; Store the label for the page read at 402,403,...411 ; Let MessageSignalAddress=@2 ; Set @MessageSignalAddress ←1 ; Copy message to MessageSignalAddress+1,"+2,..."+InLdMessageSize ; Jump to location 3. ; ; Note: the remainder of this file must fit in 256 words. ; If it does, nothing is to be gained by further code-bumming, since ; there is a .BLK statement immediately prior to MVEC to force ; MVEC to appear at a fixed place. Loader0 = .-1 ; Relocation address ;Location 1: BootEntry: JMP BootClr-Loader0 ; Go clear some things. ;Location 2: MVEC-Loader0 ; Address for messagesignal ; (this word clobbered if booting) ;Location 3: InldEntry: jmp DoIt ; Come here to really start the load .blk 2 ; date file was built put here DoIt: JSR DKBlock MOV 3 2 LDA 0 READ LDA 1 N402 ; get label left by InLd or Boot JSR .DiskIOSetup ; format the command blocks LDA 0 N400 ; init address for bumping command STA 0 KCB1+CMADD,2 ; set CUR's address JSR .DiskIOTransfer-Loader0 ; read in bulk of file ; CUR holds next page info. JSRII .+1 ; go back -- ac2 => FullDK OutLdContu ; address in restored code READ: readD N400: 400 N402: 402 ;**** FullDK -- template that corresponds to "work area" **** ; It is set up with a BMPPAGE that will transfer all of a 64K machine ; and a BAD that will display the offending KCB in the "lights" DKBlock: JSR 0,3 FullDK: .BLK 2 ; CUR, ALT .BLK KCBSize .BLK KCBSize .BLK 4 ; TRYCT,T1,T2,T3 JMP GVUP ; BAD: Come here on error ; BMPPAGE: Here to compute next core address STA 3 T3,2 ; save return address LDA 3 CUR,2 ; get current block LDA 0 CMADD,3 ; and its core address LDA 1 N400 ; AC0← address of next page ADD 1 0 LDA 1 MAXAD ; past end of real memory? SGEU 0 1 ISZ T3 2 ; no, skip return JMP @T3 2 MAXAD: MaxAddr N420: 420 ;**** End of template **** EXCHCB: LDA 0 CUR,2 ; exchange CUR and ALT LDA 1 ALT,2 STA 1 CUR,2 STA 0 ALT,2 JMP 0,3 WAITC: ; d.c. probably active, working on cur STA 3 T2,2 MKZERO 3 3 ; init retry count STA 3 TRYCT,2 JMP RETRY ; here to avoid small address problems ; Sets up alternate command block -- d.c. may be active. ; WARNING: if you call this yourself, be careful that BMPPAGE ; does not think you are transferring beyond the last page, because ; you will return in a funny way. ; The disk address for this one will be the LblNext entry ; from the current command block. ; This routine sets up the new label to check everything ; except LblNext and LblPageNum ; Calls BMPPAGE to compute next core address. CONTC: STA 3 T2,2 LDA 3 CUR,2 LDA 0 NEXTP,3 ; CUR's next is our current LDA 1 CURRE,3 ; 0 the first time, so no checking LDA 3 ALT,2 STA 0 CURRE,3 ; new disk address STA 1 BACKP,3 MKZERO 0 0 STA 0 NXTCM,3 ; no command after it yet STA 0 STATU,3 ; zero status STA 0 NEXTP,3 ; do not check this label entry STA 0 PN,3 ; Note: LbLSpare and LblNumChars will be checked because there is ; an old value left over from the last transfer that used this KCB. ; Version and Serial numbers will be checked if they were non-zero ; in the label passed to DiskIOSetup ; Call the "BumpPage" routine to compute a core address for ; reading or writing the next page. Routine accepts: ; CUR = pointer to KCB for current page ; Routine skip returns in ac0 the new core address. ; If no skip return, there are no more pages to transfer. JSR BMPPAGE,2 ; AC0 ← new core address to use. JMP LAST ; Warning: this returns from DiskIOTransfer. LDA 3 ALT,2 ; Skip return => ok STA 0 CMADD,3 ; Save address STA 3 @CUR,2 ; put link in active command JMP @T2,2 ; ALT ready before d.c. follows CUR.NXTCM. LAST: JSR WAITC ; come here from CONTC on last page JMP @T1,2 ; return from DiskIOTransfer KBLK: 521 ; mail box for disk controller MinusKCBSize: -KCBSize N377: 377 ; Disk controller is dormant ; AC0 = disk (read or write) command ; AC1 = label block whose nextp is address to begin with ; Initializes command/label blocks so that nextp in cur ; is the disk address to start with. .DiskIOSetup: sta 3 T1 2 ; save return address sta 1 T3 2 ; save label ptr -1 dsz T3 2 sta 0 KCB1+CMMD 2 ; store command in KCBs sta 0 KCB2+CMMD 2 lda 3 N2 sta 3 T2 2 ; Init loop count (used below) add 2 3 ; Know KCB1 = 2 sta 3 CUR 2 ; Establish current CB ; Don't need to initialize NEXTCM or STATU because they are set up by ; DiskIOTransfer. CMADD is initialized by caller of DiskIOTransfer. SETUPL: mkzero 0 0 sta 0 OKINT 3 ; Zero out cells that have to be zero sta 0 ERRIN 3 sta 0 HDR1 3 sta 0 CURRE 3 lda 1 M10 ; Header pointer -- point to HDR1 word of KCB sub 1 3 sta 3 HDRPT-HDR1 3 inc 3 1 ; Label pointer -- know LABEL = HDR1+2 inc 1 1 sta 1 LBLPT-HDR1 3 lda 0 T3 2 ; Source label block -1 lda 3 M10 ; - label size adc 3 1 ; AC1← last word of destination label blt ; Move label into CB (AC1 unchanged by BLT) dsz T2 2 ; Second iteration? inc 1 3 skp ; No, make pointer to second CB jmp @T1 2 ; Yes, done sta 3 ALT 2 ; Establish alternate CB jmp SETUPL M10: -10 ; -Offset of HDR1 word in CB, and -size of label ; d.c. dormant -- cur and alt have been set up. ; This code is asynchronous and took several trys to get right! ; It's supposed to be timing independent. .DiskIOTransfer: STA 3,T1,2 ; save return address JSR CONTC ; set up ALT block SWTCH: JSR EXCHCB ; exchange CUR and ALT ; STA 1 @KBLK ; &&&&& ; All pages before CUR are processed and checked. ; Link of CUR is 0. One of the following is true: ; d.c. is active, working on CUR ; d.c. is dormant, never started CUR ; d.c. is dormant, finished CUR WAITL: LDA 3 CUR,2 LDA 1 NEXTP,3 LDA 0 @KBLK SZ 0 0 JMP WAITL1 ; d.c. active, started CUR LDA 0 STATU,3 SZ 0 0 JMP FUNNY ; d.c. finished, probably an error STA 3 @KBLK ; d.c. never started cur JMP WAITL ; Warning about this skip: if we are writing labels, chances are that NEXTP is ; full with something, so skip will happen immediately. But never fear, ; WAITC will really wait. This does mean that the write will never be ; initiated unless the caller does it (sta 1,@KBLK above, commented with &&&&, ; is risky because of possibility of interrupts). WAITL1: SZ 1 1 ; is NextP zero? JMP LBLIN ; d.c. started CUR, working or finished. JMP WAITL ; d.c. active but label hasn't arrived yet. FUNNY: JSR WAITC ; Have WAIT check for errors; it may retry. ; Label as well as data are in. ; transfer of CUR streaming in. Time to set up ALT for next page LBLIN: JSR CONTC JSR WAITC ; for CUR's data JMP SWTCH ; WAITC transfers here when all is lost because of disk errors. ; AC3 = disk control block causing error. ; Display "lights" showing offending disk control block. ; store DCB's etc. at 2,...,61 GVUP: NEG 3 3 ; store bad block at 16...37 COM 3 0 ; ac0 = offending block -1 LDA 1 N37 LDA 3 MinusKCBSize BLT JSR SDCB ; AC3← DCB1-1 MARKW: 111111 ; this marks the beginning! DCB1: 6 ; this word goes at 2 0 N15: 2+(4*3)-1 ; bitmap address of this dcb is irrelevant 200 ; skip 200 scan lines DCB2: 12 ; this goes at word 6 100000+KCBSize 16 ; ptr to command block 1 DCB3: 0 100000+KCBSize 40 ; ptr to mark words 1 SDCB: MOV 3 0 LDA 1 N15 ; store dcb's in 2...15 LDA 3 M14 BLT LDA 0 MARKW ; store marks in 40...61 LDA 1 N61 LDA 3 MinusKCBSize BLKS LDA 0 N2 ; start display at 2 STA 0 @N420 JMP . M14: -(4*3) ; N37: 16+KCBSize-1 ; N61: 40+KCBSize-1 ; Following is remainder of WAITC. ; At this point we know that the transfer has been started, so it's ; safe to spin waiting for the cb's status to become nonzero. RETRY: LDA 3 CUR,2 LDA 0 STATU,3 SNZ 0 0 JMP RETRY ; status still zero -- transfer not done yet LDA 1 N377 AND 0 1 SNR JMP @T2,2 ; return if no error ISZ TRYCT 2 ; increment retry count LDA 1 TRYCT 2 ; see if exceeded max retry count LDA 0 N37 SLE 1 0 JMP BAD 2 ; return to fail point in working block MKZERO 0 0 ; prepare to retry the command STA 0 STATU,3 STA 0 HDR1,3 ; clear header word not to be checked STA 0 NEXTP,3 ; clear label words not to be checked STA 0 BACKP,3 STA 0 UNUSE,3 STA 0 NUMCH,3 STA 0 PN,3 LDA 0 N7 ; do a restore every 8 tries AND 0 1 SNR JMP DOREST DORST1: STA 3 @KBLK ; and try it again (or issue the restore) JMP RETRY DOREST: STA 3 RestCB+NEXTP ; retry this command after restore LDA 0 CURRE 3 ; get disk address LDA 1 N2 ; isolate just drive number AND 1 0 ; (in particular, set cylinder # to zero) INC 1 1 ; set Restore bit STA 1 RestCB+CURRE ; store address in Restore command block JSR DORST1 ; issue RestCB followed by original command RestCB: 0 ; NEXTP -- filled in with ptr to command that failed 0 ; STATU 44002 ; CMMD -- no transfer (seek only) N7: 7 ; HDRPT -- *** these words are irrelevant, so I use N37: 37 ; LBLPT -- *** them for constants N2: 2 ; CMADD -- *** 0 ; OKINT 0 ; ERRIN N61: 61 ; HDR1 -- *** 1 ; CURRE -- filled in with drive # and Restore (only) ; ***** Move the following code beyond MVEC if it doesn't fit here ***** ; The following code is ONLY called on a boot. Hence it can use ; absolute addresses. BootClr: mkminusone 0 0 ; Correct parity lda 1 MAXAD-Loader0 com 1 3 blt ; copy memory to itself (AC3←0) sta 3 @N420-Loader0 ; turn off display jmp InldEntry-Loader0 ; and go... ; ***** ; ***** If the following gives an error (negative block size), ; ***** move the BootClr routine beyond MVEC. .BLK (400-(InLdMessageSize+1))-(.-Loader0) ; spacer(must be ge 0) MVEC: 0 ; this will appear at 400-InLdMessageSize-1 when loaded ; 0 signals no message unless inld revises ; Message vector of length InLdMessageSize is placed here. ; If BootClr routine is put here, it gets clobbered during an InLd, ; but that's ok because BootClr is called only during a boot. ; Fancy instruction to decide whether we overflowed allowed 256 words. ; ***** Needed only if BootClr routine put after MVEC. ; ***** jmp Loader0+200 ; causes asm error if too big. ; (Note: this word is itself not in page 0) ;******* End of Page 0 Loader **************************** .END