cosmopolitan/tool/build/lib/stack.c

308 lines
9.1 KiB
C

/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ This program is free software; you can redistribute it and/or modify │
│ it under the terms of the GNU General Public License as published by │
│ the Free Software Foundation; version 2 of the License. │
│ │
│ This program is distributed in the hope that it will be useful, but │
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
│ General Public License for more details. │
│ │
│ You should have received a copy of the GNU General Public License │
│ along with this program; if not, write to the Free Software │
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
│ 02110-1301 USA │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/log/check.h"
#include "libc/macros.h"
#include "tool/build/lib/address.h"
#include "tool/build/lib/endian.h"
#include "tool/build/lib/memory.h"
#include "tool/build/lib/modrm.h"
#include "tool/build/lib/stack.h"
#include "tool/build/lib/throw.h"
static const uint8_t kStackOsz[2][3] = {
[0][XED_MODE_REAL] = 2, [0][XED_MODE_LEGACY] = 4, [0][XED_MODE_LONG] = 8,
[1][XED_MODE_REAL] = 4, [1][XED_MODE_LEGACY] = 2, [1][XED_MODE_LONG] = 2,
};
static const uint8_t kCallOsz[2][3] = {
[0][XED_MODE_REAL] = 2, [0][XED_MODE_LEGACY] = 4, [0][XED_MODE_LONG] = 8,
[1][XED_MODE_REAL] = 4, [1][XED_MODE_LEGACY] = 2, [1][XED_MODE_LONG] = 8,
};
static void WriteStackWord(uint8_t *p, uint32_t rde, uint32_t osz, uint64_t x) {
if (osz == 8) {
Write64(p, x);
} else if (osz == 2) {
Write16(p, x);
} else {
Write32(p, x);
}
}
static uint64_t ReadStackWord(uint8_t *p, uint32_t osz) {
if (osz == 8) {
return Read64(p);
} else if (osz == 2) {
return Read16(p);
} else {
return Read32(p);
}
}
static void PushN(struct Machine *m, uint32_t rde, uint64_t x, unsigned osz) {
uint64_t v;
void *p[2];
uint8_t b[8];
switch (Eamode(rde)) {
case XED_MODE_REAL:
v = (Read32(m->sp) - osz) & 0xffff;
Write16(m->sp, v);
v += Read64(m->ss);
break;
case XED_MODE_LEGACY:
v = (Read32(m->sp) - osz) & 0xffffffff;
Write64(m->sp, v);
v += Read64(m->ss);
break;
case XED_MODE_LONG:
v = (Read64(m->sp) - osz) & 0xffffffffffffffff;
Write64(m->sp, v);
break;
default:
unreachable;
}
WriteStackWord(AccessRam(m, v, osz, p, b, false), rde, osz, x);
EndStore(m, v, osz, p, b);
}
void Push(struct Machine *m, uint32_t rde, uint64_t x) {
PushN(m, rde, x, kStackOsz[m->xedd->op.osz][Mode(rde)]);
}
void OpPushZvq(struct Machine *m, uint32_t rde) {
unsigned osz;
osz = kStackOsz[m->xedd->op.osz][Mode(rde)];
PushN(m, rde, ReadStackWord(RegRexbSrm(m, rde), osz), osz);
}
static uint64_t PopN(struct Machine *m, uint32_t rde, uint16_t extra,
unsigned osz) {
uint64_t v;
void *p[2];
uint8_t b[8];
switch (Eamode(rde)) {
case XED_MODE_LONG:
v = Read64(m->sp);
Write64(m->sp, v + osz + extra);
break;
case XED_MODE_LEGACY:
v = Read32(m->sp);
Write64(m->sp, (v + osz + extra) & 0xffffffff);
v += Read64(m->ss);
break;
case XED_MODE_REAL:
v = Read32(m->sp);
Write16(m->sp, v + osz + extra);
v += Read64(m->ss);
break;
default:
unreachable;
}
return ReadStackWord(AccessRam(m, v, osz, p, b, true), osz);
}
uint64_t Pop(struct Machine *m, uint32_t rde, uint16_t extra) {
return PopN(m, rde, extra, kStackOsz[m->xedd->op.osz][Mode(rde)]);
}
void OpPopZvq(struct Machine *m, uint32_t rde) {
uint64_t x;
unsigned osz;
osz = kStackOsz[m->xedd->op.osz][Mode(rde)];
x = PopN(m, rde, 0, osz);
switch (osz) {
case 8:
case 4:
Write64(RegRexbSrm(m, rde), x);
break;
case 2:
Write16(RegRexbSrm(m, rde), x);
break;
default:
unreachable;
}
}
static void OpCall(struct Machine *m, uint32_t rde, uint64_t func) {
Push(m, rde, m->ip);
m->ip = func;
}
void OpCallJvds(struct Machine *m, uint32_t rde) {
OpCall(m, rde, m->ip + m->xedd->op.disp);
}
static uint64_t LoadAddressFromMemory(struct Machine *m, uint32_t rde) {
unsigned osz;
osz = kCallOsz[m->xedd->op.osz][Mode(rde)];
return ReadStackWord(GetModrmRegisterWordPointerRead(m, rde, osz), osz);
}
void OpCallEq(struct Machine *m, uint32_t rde) {
OpCall(m, rde, LoadAddressFromMemory(m, rde));
}
void OpJmpEq(struct Machine *m, uint32_t rde) {
m->ip = LoadAddressFromMemory(m, rde);
}
void OpLeave(struct Machine *m, uint32_t rde) {
switch (Eamode(rde)) {
case XED_MODE_LONG:
Write64(m->sp, Read64(m->bp));
Write64(m->bp, Pop(m, rde, 0));
break;
case XED_MODE_LEGACY:
Write64(m->sp, Read32(m->bp));
Write64(m->bp, Pop(m, rde, 0));
break;
case XED_MODE_REAL:
Write16(m->sp, Read16(m->bp));
Write16(m->bp, Pop(m, rde, 0));
break;
default:
unreachable;
}
}
void OpRet(struct Machine *m, uint32_t rde) {
m->ip = Pop(m, rde, m->xedd->op.uimm0);
}
void OpPushEvq(struct Machine *m, uint32_t rde) {
unsigned osz;
osz = kStackOsz[m->xedd->op.osz][Mode(rde)];
Push(m, rde,
ReadStackWord(GetModrmRegisterWordPointerRead(m, rde, osz), osz));
}
void OpPopEvq(struct Machine *m, uint32_t rde) {
unsigned osz;
osz = kStackOsz[m->xedd->op.osz][Mode(rde)];
WriteStackWord(GetModrmRegisterWordPointerWrite(m, rde, osz), rde, osz,
Pop(m, rde, 0));
}
static relegated void Pushaw(struct Machine *m, uint32_t rde) {
uint16_t v;
uint8_t b[8][2];
memcpy(b[0], m->di, 2);
memcpy(b[1], m->si, 2);
memcpy(b[2], m->bp, 2);
memcpy(b[3], m->sp, 2);
memcpy(b[4], m->bx, 2);
memcpy(b[5], m->dx, 2);
memcpy(b[6], m->cx, 2);
memcpy(b[7], m->ax, 2);
Write16(m->sp, (v = (Read16(m->sp) - sizeof(b)) & 0xffff));
VirtualRecv(m, Read64(m->ss) + v, b, sizeof(b));
}
static relegated void Pushad(struct Machine *m, uint32_t rde) {
uint32_t v;
uint8_t b[8][4];
memcpy(b[0], m->di, 4);
memcpy(b[1], m->si, 4);
memcpy(b[2], m->bp, 4);
memcpy(b[3], m->sp, 4);
memcpy(b[4], m->bx, 4);
memcpy(b[5], m->dx, 4);
memcpy(b[6], m->cx, 4);
memcpy(b[7], m->ax, 4);
Write64(m->sp, (v = (Read32(m->sp) - sizeof(b)) & 0xffffffff));
VirtualRecv(m, Read64(m->ss) + v, b, sizeof(b));
}
static relegated void Popaw(struct Machine *m, uint32_t rde) {
uint8_t b[8][2];
VirtualSend(m, b, Read64(m->ss) + Read16(m->sp), sizeof(b));
Write16(m->sp, (Read32(m->sp) + sizeof(b)) & 0xffff);
memcpy(m->di, b[0], 2);
memcpy(m->si, b[1], 2);
memcpy(m->bp, b[2], 2);
memcpy(m->sp, b[3], 2);
memcpy(m->bx, b[4], 2);
memcpy(m->dx, b[5], 2);
memcpy(m->cx, b[6], 2);
memcpy(m->ax, b[7], 2);
}
static relegated void Popad(struct Machine *m, uint32_t rde) {
uint8_t b[8][4];
VirtualSend(m, b, Read64(m->ss) + Read32(m->sp), sizeof(b));
Write64(m->sp, (Read32(m->sp) + sizeof(b)) & 0xffffffff);
memcpy(m->di, b[0], 4);
memcpy(m->si, b[1], 4);
memcpy(m->bp, b[2], 4);
memcpy(m->sp, b[3], 4);
memcpy(m->bx, b[4], 4);
memcpy(m->dx, b[5], 4);
memcpy(m->cx, b[6], 4);
memcpy(m->ax, b[7], 4);
}
relegated void OpPusha(struct Machine *m, uint32_t rde) {
switch (Eamode(rde)) {
case XED_MODE_REAL:
Pushaw(m, rde);
break;
case XED_MODE_LEGACY:
Pushad(m, rde);
break;
case XED_MODE_LONG:
OpUd(m, rde);
default:
unreachable;
}
}
relegated void OpPopa(struct Machine *m, uint32_t rde) {
switch (Eamode(rde)) {
case XED_MODE_REAL:
Popaw(m, rde);
break;
case XED_MODE_LEGACY:
Popad(m, rde);
break;
case XED_MODE_LONG:
OpUd(m, rde);
default:
unreachable;
}
}
relegated void OpCallf(struct Machine *m, uint32_t rde) {
Push(m, rde, Read64(m->cs) >> 4);
Push(m, rde, m->ip);
Write64(m->cs, m->xedd->op.uimm0 << 4);
m->ip = m->xedd->op.disp & (Osz(rde) ? 0xffff : 0xffffffff);
if (m->onlongbranch) {
m->onlongbranch(m);
}
}
relegated void OpRetf(struct Machine *m, uint32_t rde) {
m->ip = Pop(m, rde, 0);
Write64(m->cs, Pop(m, rde, m->xedd->op.uimm0) << 4);
if (m->onlongbranch) {
m->onlongbranch(m);
}
}