|
|
researchv10 Norman
#include "task.h"
// MC68000 frame fudger
/* careful -- stack frame not self-describing */
// STACK GROWS DOWN
#define FP(p) ( (int*)(&p+1) )
#define AP() 0 /* unnecessary on mc68000 */
#define OLD_AP(fp) 0 /* unnecessary on mc68000 */
#define OLD_FP(fp) (*fp)
struct FrameLayout {
short offset; // of top of frame from fp (size of locals + regs)
unsigned short mask; // of registers saved in frame
FrameLayout(int*); // called with frame pointer
};
/*
* This code figures out the layout of the frame of the user's constructor. Note
* the user's class is directly derived from class task and the call to the
* constructor is automatically generated.
*/
FrameLayout::FrameLayout(int* fp)
{
unsigned short* ret_addr = (unsigned short*)*(fp+1);
/*
* find the starting address of the function. The idea is that the instruction
* immediately before the return address must be the function call. Only works
* if the function was called by name.
*/
/*
* On the 68000, the four forms of function call generated by the compiler are
* 047271 jsr followed by the function address (long)
* 060777 bsrl followed by the function offset (long) (68020 only)
* 060400 bsr followed by the function offset (short)
* 0x61NN bsrb low order byte is the offset (not actually seen or tested)
*/
unsigned short* func_addr =
(ret_addr[-3] == 047271) ?
(unsigned short*)*(int*)(ret_addr-2) :
(ret_addr[-3] == 060777) ?
(unsigned short*) ((char*)(ret_addr-2) + *(int*)(ret_addr-2)) :
(ret_addr[-2] == 060400) ?
(unsigned short*) ((char*)(ret_addr-1) + *(short*)(ret_addr-1)) :
(*(char*)(ret_addr-1) == 0x61) ?
(unsigned short*) ((char*)ret_addr + *((char*)ret_addr-1)) :
(unsigned short*)((object*)0)->task_error(E_FUNCS);
/*
* the first instruction in the function is linkw a6,xxx
* if -O was used, xxx is the frame size in bytes, otherwise
* xxx is 0 and the next instruction is addl #yyy, sp
* where yyy is the frame size
*/
if (*func_addr == 047126) { // linkw instruction
if ((offset = (short)*++func_addr / 4) == 0)
if (*++func_addr == 0157774) // addl
offset = (short)*(func_addr += 2) / 4;
} else ((object*)0)->task_error(E_FRAMES);
/*
* The next instruction saves the registers in the frame. The possibilities are
* movem mask,@sp | save the register specified by the mask word
* movl a5,@sp | just save a5
* movl d7,@sp | just save d7
* The mask uses the standard 68000 layout
*/
switch (*++func_addr) {
case 027215: // movl a5,sp@
mask = 020000; // just a5
break;
case 044327: // movem mask,@sp
mask = *++func_addr; // mask word
break;
case 027207: // movl d7,sp@
mask = 0200; // just d7
break;
default: // no saved registers is also possible
mask = 0;
break;
}
}
/*
* fix a frame so that it returns like the arg
* The idea is that task_fp points to the task::task() stack frame which
* contains all the saved registers. t_framep points to the stack frame
* that contains the return address and old fp (and perhaps some registers).
* fill the frame pointed to by t_framep with the registers from task_fp.
*/
void
task::fudge_return(int* task_fp, int offset, task* next)
{
int* fp = t_framep + offset;
task_fp += offset; // in case this is SHARED
FrameLayout this_lo(fp);
FrameLayout task_lo(task_fp);
// copy any saved registers from this to task frame
register int* task_rp = task_fp + task_lo.offset;
register int* this_rp = fp + this_lo.offset;
register int reg_count = 0;
for (register mask = 1; mask != 0x10000; mask <<= 1) {
if (mask & task_lo.mask) {
reg_count++;
if (mask & this_lo.mask)
*task_rp++ = *this_rp++;
else task_rp++;
} else if (mask & this_lo.mask)
task_error(E_FUDGE);
}
// next copy all the registers from the task frame to this (using task offset)
task_rp = task_fp + task_lo.offset + reg_count;
this_rp = fp + task_lo.offset + reg_count;
while (reg_count--)
*--this_rp = *--task_rp;
if (next) next->restore();
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.