/* Copyright (c) 1982 Regents of the University of California */
static char sccsid
[] = "@(#)runtime.c 1.3 %G%";
* Runtime organization dependent routines, mostly dealing with
typedef struct Frame
*Frame
;
Integer condition_handler
;
Address save_ap
; /* argument pointer */
Address save_fp
; /* frame pointer */
Address save_pc
; /* program counter */
Word save_reg
[NSAVEREG
]; /* not necessarily there */
private Boolean walkingstack
= false;
* Set a frame to the current activation record.
frp
->save_ap
= reg(ARGP
);
frp
->save_pc
= reg(PROGCTR
);
for (i
= 0; i
< NSAVEREG
; i
++) {
frp
->save_reg
[i
] = reg(i
);
* Return a pointer to the next activation record up the stack.
* Return nil if there is none.
* Writes over space pointed to by given argument.
#define bis(b, n) ((b & (1 << (n))) != 0)
private Frame
nextframe(frp
)
register Integer i
, j
, mask
;
dread(&frame
, newfrp
->save_fp
, sizeof(struct Frame
));
if (frame
.save_fp
== nil
) {
mask
= ((frame
.mask
>> 16) & 0x0fff);
for (i
= 0; i
< NSAVEREG
; i
++) {
newfrp
->save_reg
[i
] = frame
.save_reg
[j
];
newfrp
->condition_handler
= frame
.condition_handler
;
newfrp
->save_ap
= frame
.save_ap
;
newfrp
->save_fp
= frame
.save_fp
;
newfrp
->save_pc
= frame
.save_pc
;
* Return the frame associated with the given function.
* If the function is nil, return the most recently activated frame.
* Static allocation for the frame.
public Frame
findframe(f
)
static struct Frame frame
;
while (frp
!= nil
and whatblock(frp
->save_pc
) != f
) {
* Find the return address of the current procedure/function.
public Address
return_addr()
* Push the value associated with the current function.
public pushretval(len
, isindirect
)
rpush((Address
) r0
, len
);
if (len
== sizeof(Word
)) {
} else if (len
== 2*sizeof(Word
)) {
panic("not indirect in pushretval?");
* Return the base address for locals in the given frame.
public Address
locals_base(frp
)
return (frp
== nil
) ? reg(FRP
) : frp
->save_fp
;
* Return the base address for arguments in the given frame.
public Address
args_base(frp
)
return (frp
== nil
) ? reg(ARGP
) : frp
->save_ap
;
* Return saved register n from the given frame.
public Word
savereg(n
, frp
)
assert(n
>= 0 and n
< NSAVEREG
);
* Return the nth argument to the current procedure.
dread(&w
, args_base(frp
) + (n
* sizeof(Word
)), sizeof(w
));
* Calculate the entry address for a procedure or function parameter,
* given the address of the descriptor.
public Address
fparamaddr(a
)
* Print a list of currently active blocks starting with most recent.
* Dump the world to the given file.
* Like "where", but variables are dumped also.
* Walk the stack of active procedures printing information
* about each active procedure.
#define lastfunc(f) (f == program)
private walkstack(dumpvariables
)
if (notstarted(process
)) {
error("program is not active");
f
= whatblock(frp
->save_pc
);
printf("%s", symname(f
));
line
= srcline(frp
->save_pc
- 1);
printf(", line %d", line
);
printf(" in \"%s\"\n", srcfilename(frp
->save_pc
- 1));
printf(" at 0x%x\n", frp
->save_pc
);
f
= whatblock(frp
->save_pc
);
} while (frp
!= nil
and not lastfunc(f
));
printf("in \"%s\":\n", symname(program
));
* Find the entry point of a procedure or function.
f
->symvalue
.funcv
.beginaddr
+= 2;
* Return the address corresponding to the first line in a function.
public Address
firstline(f
)
while (linelookup(addr
) == 0 and addr
< objsize
) {
* Catcher drops strike three ...
while (linelookup(addr
) == 0 and addr
< objsize
) {
* Return the address corresponding to the end of the program.
* We look for the entry to "exit".
public Address
lastaddr()
s
= lookup(identname("exit", true));
panic("can't find exit");
* Decide if the given function is currently active.
* We avoid calls to "findframe" during a stack trace for efficiency.
* Presumably information evaluated while walking the stack is active.
public Boolean
isactive(f
)
if (isfinished(process
)) {
if (walkingstack
or f
== program
or
(ismodule(f
) and isactive(container(f
)))) {
b
= (Boolean
) (findframe(f
) != nil
);
* Evaluate a call to a procedure.
public callproc(procnode
, arglist
)
if (procnode
->op
!= O_SYM
) {
fprintf(stderr
, "can't call \"");
prtree(stderr
, procnode
);
assert(procnode
->op
== O_SYM
);
proc
= procnode
->value
.sym
;
error("\"%s\" is not a procedure or function", symname(proc
));
argc
= pushargs(proc
, arglist
);
event_once(build(O_EQ
, build(O_SYM
, pcsym
), build(O_SYM
, retaddrsym
)),
buildcmdlist(build(O_PROCRTN
, proc
)));
* Push the arguments on the process' stack. We do this by first
* evaluating them on the "eval" stack, then copying into the process'
private Integer
pushargs(proc
, arglist
)
argc
= evalargs(proc
, arglist
);
setreg(STKP
, reg(STKP
) - args_size
);
dwrite(savesp
, reg(STKP
), args_size
);
* Evaluate arguments left-to-right.
private Integer
evalargs(proc
, arglist
)
for (p
= arglist
; p
!= nil
; p
= p
->value
.arg
[1]) {
panic("evalargs: arglist missing comma");
error("too many parameters to %s", symname(proc
));
if (not compatible(arg
->type
, exp
->nodetype
)) {
error("expression for parameter %s is of wrong type", symname(arg
));
error("variable expected for parameter \"%s\"", symname(arg
));
addr
= lval(exp
->value
.arg
[0]);
error("not enough parameters to %s", symname(proc
));
printf(" returns successfully\n", symname(f
));
* Push the current environment.
push(Boolean
, isstopped
);
push(Word
, reg(PROGCTR
));
* Pop back to the real world.
register String filename
;
setreg(PROGCTR
, pop(Word
));
isstopped
= pop(Boolean
);
* Flush the debuggee's standard output.
* This is VERY dependent on the use of stdio.
p
= lookup(identname("fflush", true));
while (p
!= nil
and not isblock(p
)) {
iob
= lookup(identname("_iob", true));
push(long, address(iob
, nil
) + sizeof(struct _iobuf
));
setreg(STKP
, reg(STKP
) - sizeof(long));
dwrite(savesp
, reg(STKP
), sizeof(long));