// RaidProcs.bcpl. Supplementary procedures for RAID
// Last modified September 19, 1981  2:01 PM by Bill van Melle
// Last modified August 4, 1981  12:47 PM by Beau Sheil
// Last modified March 16, 1981  12:56 PM by Beau Sheil
// Last modified July 27, 1980  6:56 PM by Beau Sheil
// Last modified May 27, 1980  4:36 PM by Beau Sheil
get "AltoDefs.d"
get "Raid.decl"
get "Pup0.decl"
get "Pup1.decl"
get "Stats.decl"
get "Streams.d"
external [         // defined here
AtomNum; ShowRealCore; TeleRaid; uPCTracing; @uPCTraceAddr
DisplayVMinBitMap; DisplayVMbit; VMDisplay
]
external [         // procedures used
Zero; VirtualPage; CallSwat; CRLF; Ws; Wo; Bytes2
StealFromBcplDisplay; AddToBcplDisplay; DumpBlockToStats
RaidReset; UPCTrace; ShowDisplayStream
@BGetBase; @BGetBasePtr; @XPutBase; @XSetBR; @RRead; @RWrite
Dequeue; MoveWords; CompletePup; ExchangePorts; ReleasePBI
SetTimer; TimerHasExpired; EmAddr; Block
                  // statics used
EmulatorSpace; LastRealPageNo; @lvVPtr
dsp; @DisplayAddrHi
]
static  [
VMDisplay = 0
@uPCTraceAddr = 0
]
manifest [ LastSegment = 63// 22-bit address space
IMSize = #20000; DummyHeight = 32
VMbitMapOffset = 2+2*lDCB// bitmap within VMDisplay
VMDisplayWidth = 16; VMDisplayHeight = LastSegment+1
VMDisplayScanLines = DummyHeight + VMDisplayHeight
VMDisplaySize = VMbitMapOffset + VMDisplayWidth*VMDisplayHeight
TRRead       = #201// teleRaid constants
TRHereIsPage = #301
TRStore      = #200
TRStoreDone  = #300
TRGo         = #202
TRGoAck      = #302
TRGoReply    = #203
TRShow       = #210
TRShown      = #310
TRError      = #304
]
let AtomNum(S) = valof
// Finds atom number for BCPL string. pn char format is that of bcpl strings
 [ 
   let LEN = S>>String.length
   let NW = LEN rshift 1
// Compute the hash code for the name. Mimic the behavior of LISP
   let SS = 0
   for i = 1 to LEN do
     [ let h1 = ((SS & #7777) lshift 2) + SS
       SS = (h1 lshift 8) + h1 + S>>String.char↑i
     ]
   [
   let H = ((SS𒿑) rem (AHTSIZE lshift 8)) + AHTbase
// H is the virtual address in the AtomHashTable 
   let P = BGetBase(AHTspace, H)// P is address of hash table entry 
   if P eq 0 then RaidReset("No such atom")
// since P was non zero, it is the virtual address of an atom+1
   P = P-1
// fetch atom and check its pname. Check its characters (word-by-word)
// with those in pname. Count is first byte, and pad byte (if any) is 0.
   XSetBR(BGetBasePtr(PNPspace, PNPbase+(P lshift 1)))
   for i = 0 to NW do if RRead(i) ne S!i goto reprobe
   resultis P// pnames are the same; return virtual address
reprobe: SS = SS+HashInc
   ] repeat// comparison failed so reprobe
 ]
and ShowRealCore() be// Displays a segment page usage map
 [
   let base = vec LastSegment+2; Zero(base, LastSegment+3)
   for i = 0 to LastRealPageNo-1-PagesPerSegment do
     [ let bucket = VirtualPage(i)<<VP.segment
       base!bucket = base!bucket + 1
     ]
   base!EmulatorSpace = PagesPerSegment// Emulator done specially
   Ws(" Segment   Pages Contents*N")
   let total = 0
   for i = 0 to LastSegment do unless base!i eq 0
       do [ Ws("  "); Wo(i); Ws("  "); Wo(base!i)
            // The following is not a SELECT as the choices are not exclusive
            if i ge MDSLo & i le MDSHi then Ws(" Main data space")
            if i eq PNPspace then Ws(" PName pointers")
            if i eq DEFspace then Ws(" Definitions")
            if i eq TOPVALspace then Ws(" TopVals")
            if i eq PLISTspace then Ws(" Property lists")
            if i eq AHTspace then Ws(" Atom Hash Table")
            if i eq PMTspace then Ws(" Primary Page Map (PMT)")
            if i eq PAGEMAPspace then Ws(" Secondary Page Map")
            if i eq INTERFACEspace then Ws(" Interface page")
            if i eq MDSTYPEspace then Ws(" MDS type table")
            if i eq STATSspace then Ws(" Statistics")
            if i eq STACKspace then Ws(" Stack")
            if i eq EmulatorSpace then Ws(" Alto emulator")
            if i eq DisplayAddrHi & DisplayAddrHi ne 0
               then Ws(" Lisp display bitmap")
            if i ge PncLo & i le PncHi then Ws(" PName characters")
            if i ge ArrayLo & i le ArrayHi then Ws(" Arrays and strings")
            if i eq GCMainspace then Ws(" GC main table")
            if i eq GCCollspace then Ws(" GC collision table")
            CRLF()
            total = total + base!i ]
   Ws("   Total  "); Wo(total); CRLF() 
   let sp = "          "
   Ws(sp); Wo(base!(LastSegment+1)); Ws(" Empty"); CRLF()
   Ws(sp); Wo(base!(LastSegment+2)); Ws(" Unavailable"); CRLF()
   Ws(sp); Wo(LastRealPageNo); Ws(" Total pages"); CRLF()
  ]
and TeleRaid(s) = valof
  [ 
    Block()// let inbound packets in
    let pb = Dequeue(lv s>>PupSoc.iQ)
    unless pb do resultis 0
    let pup = lv pb>>PBI.pup
//    let lastid = 0// cache low order bits of id
//    test lastid eq pup>>Pup.id↑2
//      ifso  [ ReleasePBI(pb); resultis 0 ]// duplicate suppression
//      ifnot [ lastid = pup>>Pup.id↑2 ]
    switchon pup>>Pup.type into
      [ case TRRead:  [ TellUser("Read")// ID is virtual addr of desired page
                        MoveWords(lv pup>>Pup.id↑1, EmAddr(lv pup>>Pup.words↑1),
                                  WordsPerPage)
                        ExchangePorts(pb)
                        CompletePup(pb, TRHereIsPage, WordsPerPage lshift 1 + pupOvBytes)
                        endcase
                      ]
        case TRStore: [ TellUser("Store")// First two words are VA, third word is value to store
                        XPutBase(lv pup>>Pup.words↑1, pup>>Pup.words↑3)
                        ExchangePorts(pb)
                        CompletePup(pb, TRStoreDone, pupOvBytes)
                        endcase
                      ]
        case TRGo:    [ TellUser("Go")
                        ExchangePorts(pb)
                        CompletePup(pb, TRGoAck, pupOvBytes)
                        let Timer = nil
                        SetTimer(lv Timer, 1000)// 10 sec timeout
                        [ pb = Dequeue(lv s>>PupSoc.iQ)
                          if pb % TimerHasExpired(lv Timer) then break
                          Block() ] repeat
                        test pb & (pb>>PBI.pup.type eq TRGoReply)
                          ifso  [ // ↑N = go!
ReleasePBI(pb)
resultis #16 ]
                          ifnot [ TellUser("Go abort")
                                  if pb then [ ExchangePorts(pb)
                                               CompletePup(pb, TRError, pupOvBytes) ] ]
                        endcase
                      ]
        case TRShow:  [ // Body is a bcpl string to print
                        Ws(lv pup>>Pup.bytes↑1)
                        ExchangePorts(pb)
                        CompletePup(pb, TRShown, pupOvBytes)
                        endcase
                      ]
        default:      [ TellUser("Error")
                        ExchangePorts(pb)
                        CompletePup(pb, TRError, 0)
                        ]
     ]
    resultis 0
  ]
and TellUser(s) be [ Ws("["); Ws(s); Ws("]") ]
and uPCTracing(flg) be
  [
   test uPCTraceAddr eq 0
    ifso [// turn tracing on
         uPCTraceAddr = StealFromBcplDisplay(IMSize, 0)
         UPCTrace(uPCTraceAddr)
        ]
    ifnot [// turn tracing off and dump to lisp.stats
         UPCTrace(0)
         DumpBlockToStats(uPCevent, uPCTraceAddr, IMSize/128, 128)
         if flg then AddToBcplDisplay(uPCTraceAddr, IMSize, 0)
         uPCTraceAddr = 0
        ]
  ]
and DisplayVMinBitMap() be
  [
   if VMDisplay then
    [ VMDisplay = 0
      AddToBcplDisplay(VMDisplay, VMDisplaySize, VMDisplayScanLines)
      return
    ]
   VMDisplay = StealFromBcplDisplay(VMDisplaySize, VMDisplayScanLines)
   let VMDisplayDCB = VMDisplay + 2
// insert a null dcb DummyHeight/2 scan lines high first
   VMDisplay>>DS.fdcb = VMDisplayDCB
   VMDisplayDCB>>DCB.next = VMDisplayDCB + lDCB
   VMDisplayDCB>>DCB.width = 0
   VMDisplayDCB>>DCB.bitmap = 0
   VMDisplayDCB>>DCB.height = DummyHeight/2
   VMDisplayDCB = VMDisplayDCB + lDCB
   VMDisplay>>DS.ldcb = VMDisplayDCB
   VMDisplayDCB>>DCB.next = 0
   VMDisplayDCB>>DCB.height = VMDisplayHeight/2
   VMDisplayDCB>>DCB.indentation = 1
   VMDisplayDCB>>DCB.width = VMDisplayWidth
   VMDisplayDCB>>DCB.bitmap = VMDisplay + VMbitMapOffset
   // Turn on bits for pages already in core. Emulator done specially.
   for i=0 to PagesPerSegment-1 do DisplayVMbit(Bytes2(EmulatorSpace,i), true)
   for i=0 to LastRealPageNo-1-PagesPerSegment do
     [ let vp = VirtualPage(i)
       unless vp<<VP.segment gr LastSegment do DisplayVMbit(vp, true)
     ]
   ShowDisplayStream(VMDisplay, DSabove, dsp)
  ]
and DisplayVMbit(vp, turnOnBit) be
  [
    // segement selects row; hi order 4 bits address word; lo order 4 get bit
    let bitInWord = #100000 rshift (vp<<VP.page & #17)
    let Addr = VMDisplay + VMbitMapOffset + (vp<<VP.segment*VMDisplayWidth) +
               vp<<VP.page rshift 4
    @Addr = turnOnBit ? @Addr % bitInWord, @Addr & not bitInWord
  ]