Files
PSFree/module/mem.mjs
T
2024-01-27 20:25:49 -06:00

229 lines
5.6 KiB
JavaScript

/* Copyright (C) 2023-2024 anonymous
This file is part of PSFree.
PSFree is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
PSFree 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { Int } from './int64.mjs';
import {
read16,
read32,
read64,
write16,
write32,
write64,
} from './rw.mjs';
import * as o from './offset.mjs';
export let mem = null;
function init_module(memory) {
mem = memory;
}
export class Addr extends Int {
read8(offset) {
const addr = this.add(offset);
return mem.read8(addr);
}
read16(offset) {
const addr = this.add(offset);
return mem.read16(addr);
}
read32(offset) {
const addr = this.add(offset);
return mem.read32(addr);
}
read64(offset) {
const addr = this.add(offset);
return mem.read64(addr);
}
// returns a pointer instead of an Int
readp(offset) {
const addr = this.add(offset);
return mem.readp(addr);
}
write8(offset, value) {
const addr = this.add(offset);
mem.write8(addr, value);
}
write16(offset, value) {
const addr = this.add(offset);
mem.write16(addr, value);
}
write32(offset, value) {
const addr = this.add(offset);
mem.write32(addr, value);
}
write64(offset, value) {
const addr = this.add(offset);
mem.write64(addr, value);
}
}
class MemoryBase {
_addrof(obj) {
if (typeof obj !== 'object'
&& typeof obj !== 'function'
) {
throw TypeError('addrof argument not a JS object');
}
this.worker.a = obj;
write64(this.main, o.view_m_vector, this.butterfly.sub(0x10));
let res = read64(this.worker, 0);
write64(this.main, o.view_m_vector, this._current_addr);
return res;
}
addrof(obj) {
return new Addr(this._addrof(obj));
}
set_addr(addr) {
if (!(addr instanceof Int)) {
throw TypeError('addr must be an Int');
}
this._current_addr = addr;
write64(this.main, o.view_m_vector, this._current_addr);
}
get_addr() {
return this._current_addr;
}
// write0() is for when you want to write to address 0. You can't use for
// example: "mem.write32(Int.Zero, 0)", since you can't set by index the
// view when it isDetached(). isDetached() == true when m_mode >=
// WastefulTypedArray and m_vector == 0.
//
// Functions like write32() will index mem.worker via write() from rw.mjs.
//
// size is the number of bits to read/write.
//
// The constraint is 0 <= offset + 1 < 2**32.
//
// PS4 firmwares >= 9.00 and any PS5 version can write to address 0
// directly. All firmwares (PS4 and PS5) can read address 0 directly.
//
// See setIndex() from
// WebKit/Source/JavaScriptCore/runtime/JSGenericTypedArrayView.h at PS4
// 8.03 for more information. Affected firmwares will get this error:
//
// TypeError: Underlying ArrayBuffer has been detached from the view
write0(size, offset, value) {
const i = offset + 1;
if (i >= 2**32 || i < 0) {
throw RangeError(`read0() invalid offset: ${offset}`);
}
this.set_addr(new Int(-1));
switch (size) {
case 8: {
this.worker[i] = value;
}
case 16: {
write16(this.worker, i, value);
}
case 32: {
write32(this.worker, i, value);
}
case 64: {
write64(this.worker, i, value);
}
default: {
throw RangeError(`write0() invalid size: ${size}`);
}
}
}
read8(addr) {
this.set_addr(addr);
return this.worker[0];
}
read16(addr) {
this.set_addr(addr);
return read16(this.worker, 0);
}
read32(addr) {
this.set_addr(addr);
return read32(this.worker, 0);
}
read64(addr) {
this.set_addr(addr);
return read64(this.worker, 0);
}
// returns a pointer instead of an Int
readp(addr) {
return new Addr(this.read64(addr));
}
write8(addr, value) {
this.set_addr(addr);
this.worker[0] = value;
}
write16(addr, value) {
this.set_addr(addr);
write16(this.worker, 0, value);
}
write32(addr, value) {
this.set_addr(addr);
write32(this.worker, 0, value);
}
write64(addr, value) {
this.set_addr(addr);
write64(this.worker, 0, value);
}
}
export class Memory extends MemoryBase {
constructor(main, worker) {
super();
this.main = main;
this.worker = worker;
// The initial creation of the "a" property will change the butterfly
// address. Do it now so we can cache it for addrof().
worker.a = 0; // dummy value, we just want to create the "a" property
this.butterfly = read64(main, o.js_butterfly);
write32(main, o.view_m_length, 0xffffffff);
this._current_addr = Int.Zero;
init_module(this);
}
}