// MICRO Macros // last edited February 13, 1981 10:20 AM // Copyright Xerox Corporation 1979, 1981 get "micdecl.d" external // O.S. [ MoveBlock; SetBlock CleanupDiskStream // for trace listing ] // literals manifest [ argbl = 600 // argument buffer length ] // Static Storage static [ @argbuf // bottom of argument buffer @argend // end of argument buffer @argp // points to current bottom of arg buffer ] let argstr(nc) = valof // Allocate an n-char. argument string test (argp-nc-1 ls argbuf) ifso errx("Macro argument storage full") ifnot [ argp = argp-nc-1 @argp = nc+1 resultis argp+1 ] and colargs() = valof // Collect macro arguments, return # of args. // Note: the statement reading routine guarantees proper nesting // of parens and brackets, and nothing in the language can destroy it, // so no stack is necessary here. [ let stptr = sttop // Define first for assembly code let nest = 0 // Ditto let nargs = 0 let osptr = stptr [ static [ @achar ] // Process character // other characters goto achar // Stbuf!-1 contains endc, no need for end check let initmac() be // Initialize switch for colargs [ argbuf = alloctemp(argbl) argend = argbuf+argbl argp = argend let aswitch = alloctemp(#200) // no characters above #177 let arpb = table[ #11005 // ISZ nest,2 #20405 // achar: LDA 0 aswitch #15004 // achar1: DSZ stptr,2 #37004 // LDA 3 @stptr,2 #117000 // ADD 0 3 #3400 // JMP @0,3 0 // aswitch: #15004 // avalc: DSZ stptr,2 #772 // JMP achar1 ] achar = arpb+1 achar!5 = aswitch let avalc = arpb+7 SetBlock(aswitch, achar+1, #200) aswitch!$( = alpar aswitch!$) = arpb aswitch!$, = acomma aswitch!$[ = albr aswitch!$] = arpb aswitch!endc = aend aswitch!symc = avalc aswitch!numc = avalc aswitch!num6c = avalc ] aend: // ran off stbuf -- can't happen error("BRACKETS MISPAIRED -- BUG") alpar: // ( nest = nest-1 goto achar albr: // [ if nest ne 0 then [ nest = nest-1 goto achar ] @stptr = osptr-stptr nargs = nargs+1 break acomma: // , if nest ne 0 goto achar // enclosed in () or [] @stptr = osptr-stptr nargs = nargs+1 osptr = stptr goto achar ] repeat let nw = sttop-stptr sttop = stptr test argp-nw ls argbuf ifso [ errx("MACRO ARGUMENT STORAGE FULL") nargs = 0 ] ifnot [ argp = argp-nw MoveBlock(argp, stptr, nw) ] resultis nargs ] let mcall(ep, nargs) be // Do macro call [ if tracecalls then tracemcall(ep, nargs) let dp = nil // define first for assembly code let typ = ep!stype test typ gr maxtype ifso // Expand macro [ dp = fstop-(ep!mcsp) let free = tlbot-sttop-@dp test free le 0 ifso errx("STATEMENT TOO LONG") ifnot [ static [ @cleft; @cright; @cac1 ] let np, nl = nil, nil // argument pointer, length let param = nil // argument # let mask = nil // left/right mask goto cleft let initmac1() be [ let cswitch = alloctemp(#240) let cchar = table [ 11006b // cleft: ISZ dp,2 23006b // LDA 0 @dp,2 24415b // LDA 1 c177400 123700b // ANDS 1 0 34412b // LDA 3 cswitch 117000b // ADD 0 3 7400b // JSR @0,3 23006b // cright: LDA 0 @dp,2 24410b // LDA 1 c377 123400b // AND 1 0 34404b // LDA 3 cswitch 117000b // ADD 0 3 7400b // JSR @0,3 763b // JMP cleft 0 // cswitch: .-. 177400b // c177400: 177400 377b // c377: 377 0 // ccopy: STA 0 @sttop 0 // ISZ sttop 1400b // cskip: JMP 0,3 ] cleft = cchar cright = cchar+7 cchar!16b = cswitch cchar!21b = #42000 + lv sttop cchar!22b = #10000 + lv sttop SetBlock(cswitch, cchar+21b, #200) cswitch!Aend = actend cswitch!Aargn = actargn cswitch!Aarg2 = actarg2 cswitch!Aarg1 = actarg1 cswitch!Anargs = actnargs cswitch!Askip = cchar+23b cswitch!symc = actval cswitch!numc = actval cswitch!num6c = actval SetBlock(cswitch+200b, actlong, 40b) cac1 = table [ 121000b // MOV 1 0 1401b // JMP 1,3 ] ] actargn: // Copy param'th argument mask = not cac1() test mask ls 0 ifso [ dp = dp+1; param = (@dp)<<lh ] ifnot param = (@dp)<<rh if param gr nargs goto cnext np = argp for i = 2 to param do np = np+@np goto argn actarg2: // Copy argument 2 mask = cac1() if nargs le 1 goto cnext np = argp+@argp goto argn actarg1: // Copy argument 1 mask = cac1() if nargs eq 0 goto cnext np = argp argn: // Copy argument nl = @np-1 if nl eq 0 goto cnext if nl ge free then [ errx("STATEMENT TOO LONG") goto actend ] free = free-nl MoveBlock(sttop, np+1, nl) sttop = sttop+nl cnext: test mask ls 0 ifso goto cright ifnot goto cleft actnargs: // Give number of args mask = cac1() if nargs ge 8 then [ @sttop = (nargs rshift 3) + $0 sttop = sttop + 1 ] @sttop = (nargs&7) + $0 sttop = sttop + 1 goto cnext actval: // Packed value, short test cac1() ls 0 ifso // just did left byte [ @sttop, sttop!1 = (@dp)<<rh, (@dp)<<lh sttop = sttop+2 goto cleft ] ifnot // just did right byte [ sttop!1 = (@dp)<<rh dp = dp+1 @sttop = (@dp)<<lh sttop = sttop+2 goto cright ] actlong: // Packed value, long test cac1() ls 0 ifso // just did left byte [ sttop!1 = (@dp)<<lh - 200b @sttop = (@dp lshift 8) + ((dp!1)<<lh) sttop, dp = sttop+2, dp+1 goto cright ] ifnot // just did right byte [ sttop!1 = (@dp)<<rh - 200b dp = dp+1 @sttop = @dp sttop = sttop+2 goto cleft ] actend: // End of definition ] ] ifnot switchon typ into [ case fldtype: test nargs eq 1 ifso dofld(ep,argp+1,@argp-1,true) ifnot mcerr(ep) endcase case bitype: dobi(ep,nargs,argp) endcase case memtype: test nargs eq 2 ifso [ let sp, l = argp+1, @argp-1 expand(lv sp) let a2p = argp+@argp doaddr(sp,l,ep,evarg(a2p+1, @a2p-1)) ] ifnot mcerr(ep) endcase default: test typ ls 0 ifso test nargs eq 1 ifso dosta(ep,argp+1,@argp-1) ifnot mcerr(ep) ifnot errx("@S MAY NOT BE FOLLOWED BY []",false,ep) ] // clear away arglist for i = 1 to nargs do argp = argp+@argp ] and mcerr(ep) be errx("WRONG # OF ARGS FOR @S", false, ep) and tracemcall(ep, nargs) be // Trace the call for debugging [ let old = lchan lchan = erlchan lchr($**) lsym(ep) let ap = argp for i = 1 to nargs do [ llstr("*N***T") lblk(ap+1, @ap-1) ap = ap+@ap ] lcrlf() CleanupDiskStream(lchan) lchan = old ] and macdef(ap, l) = valof [ // Parse macro definition. Ok to do this "in place" since // arg buffer is scratch storage, and parsed version is // always smaller than unpacked original. if l eq 0 then // avoids negative di below [ let dp = alloc(2) @dp, dp!1 = 0, Aend lshift 8 resultis dp ] let endp = ap+l let cp, di = endp, (l lshift 1) - 1 let cklen = l // Length for initial check at call time let ch = -1 until cp eq ap do [ cp, di = cp-1, di-1 let lastch = ch ch = @cp test (ch eq $#) & (lastch ge $0) & (lastch le $9) ifso // Argument [ di = di+1 // overwrite digit let ac = cp!1 ch = selecton ac into [ case $0: Anargs case $1: Aarg1 case $2: Aarg2 default: valof [ ap>>bytes↑di = ac-$0 di = di-1 resultis Aargn ] ] if ac ne $0 then cklen = cklen-2 ] ifnot if ch ls 40b then // Packed value [ cp = cp-1 let val = @cp ap>>bytes↑di = val<<rh if val<<lh ne 0 then // need long format [ ch, di = ch+200b, di-1 ap>>bytes↑di = val<<lh ] di = di-1 ] ap>>bytes↑di = ch ] let nw = l - (di rshift 1) // space for packed body let dp = alloc(nw+1) MoveBlock(dp+1, endp-nw, nw) if (di & 1) ne 0 then dp>>bytes↑2 = Askip // skip first byte (dp+nw)>>rh = Aend // mark end @dp = cklen resultis dp ]