cosmopolitan/tool/build/lib/string.c

229 lines
7.8 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/assert.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/runtime/runtime.h"
#include "tool/build/lib/alu.h"
#include "tool/build/lib/endian.h"
#include "tool/build/lib/flags.h"
#include "tool/build/lib/ioports.h"
#include "tool/build/lib/machine.h"
#include "tool/build/lib/memory.h"
#include "tool/build/lib/modrm.h"
#include "tool/build/lib/string.h"
#include "tool/build/lib/throw.h"
static uint64_t ReadInt(uint8_t p[8], unsigned long w) {
switch (w) {
case 0:
return Read8(p);
case 1:
return Read16(p);
case 2:
return Read32(p);
case 3:
return Read64(p);
default:
unreachable;
}
}
static void WriteInt(uint8_t p[8], uint64_t x, unsigned long w) {
switch (w) {
case 0:
Write8(p, x);
break;
case 1:
Write16(p, x);
break;
case 2:
Write64(p, x);
break;
case 3:
Write64(p, x);
break;
default:
unreachable;
}
}
void OpString(struct Machine *m, uint32_t rde, int op) {
void *p[2];
unsigned n;
uint64_t asz;
bool compare;
int64_t sgn, v;
uint8_t s[3][8];
sgn = GetFlag(m->flags, FLAGS_DF) ? -1 : 1;
asz = Asz(rde) ? 0xffffffff : 0xffffffffffffffff;
n = 1 << RegLog2(rde);
for (;;) {
if (Rep(rde) && !Read64(m->cx)) break;
v = 0;
*p = NULL;
compare = false;
switch (op) {
case STRING_CMPS:
Alu(RegLog2(rde), ALU_SUB,
ReadInt(Load(m, (Read64(m->si) + GetSegment(m)) & asz, n, s[2]),
RegLog2(rde)),
ReadInt(Load(m, Read64(m->di) & asz, n, s[1]), RegLog2(rde)),
&m->flags);
Write64(m->di, (Read64(m->di) + sgn * n) & asz);
Write64(m->si, (Read64(m->si) + sgn * n) & asz);
compare = true;
break;
case STRING_MOVS:
memcpy(BeginStore(m, (v = Read64(m->di) & asz), n, p, s[0]),
Load(m, (Read64(m->si) + GetSegment(m)) & asz, n, s[1]), n);
Write64(m->di, (Read64(m->di) + sgn * n) & asz);
Write64(m->si, (Read64(m->si) + sgn * n) & asz);
break;
case STRING_STOS:
memcpy(BeginStore(m, (v = Read64(m->di) & asz), n, p, s[0]), m->ax, n);
Write64(m->di, (Read64(m->di) + sgn * n) & asz);
break;
case STRING_LODS:
memcpy(m->ax, Load(m, (Read64(m->si) + GetSegment(m)) & asz, n, s[1]),
n);
Write64(m->si, (Read64(m->si) + sgn * n) & asz);
break;
case STRING_SCAS:
Alu(RegLog2(rde), ALU_SUB,
ReadInt(Load(m, Read64(m->di) & asz, n, s[1]), RegLog2(rde)),
ReadInt(m->ax, RegLog2(rde)), &m->flags);
Write64(m->di, (Read64(m->di) + sgn * n) & asz);
compare = true;
break;
case STRING_OUTS:
OpOut(m, Read16(m->dx),
ReadInt(Load(m, (Read64(m->si) + GetSegment(m)) & asz, n, s[1]),
RegLog2(rde)));
Write64(m->si, (Read64(m->si) + sgn * n) & asz);
break;
case STRING_INS:
WriteInt(BeginStore(m, (v = Read64(m->di) & asz), n, p, s[0]),
OpIn(m, Read16(m->dx)), RegLog2(rde));
Write64(m->di, (Read64(m->di) + sgn * n) & asz);
break;
default:
abort();
}
EndStore(m, v, n, p, s[0]);
if (!Rep(rde)) break;
Write64(m->cx, Read64(m->cx) - 1);
if (compare) {
if (Rep(rde) == 2 && GetFlag(m->flags, FLAGS_ZF)) break;
if (Rep(rde) == 3 && !GetFlag(m->flags, FLAGS_ZF)) break;
}
}
}
void OpRepMovsbEnhanced(struct Machine *m, uint32_t rde) {
bool failed;
uint8_t *direal, *sireal;
unsigned diremain, siremain, i, n;
uint64_t divirtual, sivirtual, diactual, siactual, failaddr, asz, cx;
if (!(cx = Read64(m->cx))) return;
failed = false;
failaddr = 0;
asz = Asz(rde) ? 0xffffffff : 0xffffffffffffffff;
divirtual = Read64(m->di) & asz;
sivirtual = Read64(m->si) & asz;
SetWriteAddr(m, (GetSegment(m) + divirtual) & asz, cx);
SetReadAddr(m, (GetSegment(m) + sivirtual) & asz, cx);
do {
diactual = (GetSegment(m) + divirtual) & asz;
siactual = (GetSegment(m) + sivirtual) & asz;
if (!(direal = FindReal(m, diactual))) {
failaddr = diactual;
failed = true;
break;
}
if (!(sireal = FindReal(m, siactual))) {
failaddr = siactual;
failed = true;
break;
}
diremain = 0x1000 - (divirtual & 0xfff);
siremain = 0x1000 - (sivirtual & 0xfff);
n = MIN(cx, MIN(diremain, siremain));
for (i = 0; i < n; ++i) {
direal[i] = sireal[i];
}
cx -= n;
divirtual = (divirtual + n) & asz;
sivirtual = (sivirtual + n) & asz;
} while (cx);
Write64(m->cx, cx);
Write64(m->di, divirtual);
Write64(m->si, sivirtual);
if (failed) ThrowSegmentationFault(m, failaddr);
}
void OpRepStosbEnhanced(struct Machine *m, uint32_t rde) {
bool failed;
uint8_t *direal, al;
unsigned diremain, i, n;
uint64_t divirtual, diactual, failaddr, asz, cx;
if (!(cx = Read64(m->cx))) return;
failaddr = 0;
failed = false;
al = Read8(m->ax);
asz = Asz(rde) ? 0xffffffff : 0xffffffffffffffff;
divirtual = Read64(m->di) & asz;
SetWriteAddr(m, (GetSegment(m) + divirtual) & asz, cx);
do {
diactual = (GetSegment(m) + divirtual) & asz;
if (!(direal = FindReal(m, diactual))) {
failaddr = diactual;
failed = true;
break;
}
diremain = 0x1000 - (divirtual & 0xfff);
n = MIN(cx, diremain);
for (i = 0; i < n; ++i) {
direal[i] = al;
}
cx -= n;
divirtual = (divirtual + n) & asz;
} while (cx);
Write64(m->cx, cx);
Write64(m->di, divirtual);
if (failed) ThrowSegmentationFault(m, failaddr);
}
void OpMovsb(struct Machine *m, uint32_t rde) {
if (Rep(rde) && !GetFlag(m->flags, FLAGS_DF)) {
OpRepMovsbEnhanced(m, rde);
} else {
OpString(m, rde, STRING_MOVS);
}
}
void OpStosb(struct Machine *m, uint32_t rde) {
if (Rep(rde) && !GetFlag(m->flags, FLAGS_DF)) {
OpRepStosbEnhanced(m, rde);
} else {
OpString(m, rde, STRING_STOS);
}
}