-
Notifications
You must be signed in to change notification settings - Fork 0
/
memory.cpp
222 lines (186 loc) · 5.64 KB
/
memory.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// Copyright 2010-2021 Chris Spiegel.
//
// This file is part of Bocfel.
//
// Bocfel is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version
// 2 or 3, as published by the Free Software Foundation.
//
// Bocfel 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 Bocfel. If not, see <http://www.gnu.org/licenses/>.
#include <cstdlib>
#include <iomanip>
#include <ios>
#include <sstream>
#include <string>
#include "memory.h"
#include "branch.h"
#include "meta.h"
#include "process.h"
#include "screen.h"
#include "types.h"
#include "util.h"
#include "zterp.h"
// Story files do not have access to memory beyond 64K. If they do
// something that would cause such access, wrap appropriately. This is
// the approach Frotz uses (at least for @loadw/@loadb), and is endorsed
// by Andrew Plotkin (see http://www.intfiction.org/forum/viewtopic.php?f=38&t=2052).
// The standard isn’t exactly clear on the issue, and this appears to be
// the most sensible way to deal with the problem.
uint8_t *memory, *dynamic_memory;
uint32_t memory_size;
bool in_globals(uint16_t addr)
{
return addr >= header.globals && addr < header.globals + 480;
}
bool is_global(uint16_t addr)
{
return in_globals(addr) && (addr - header.globals) % 2 == 0;
}
static unsigned long addr_to_global(uint16_t addr)
{
return (addr - header.globals) / 2;
}
std::string addrstring(uint16_t addr)
{
std::ostringstream ss;
if (is_global(addr)) {
ss << "G" << std::setw(2) << std::setfill('0') << std::hex << addr_to_global(addr);
} else {
ss << "0x" << std::hex << addr;
}
return ss.str();
}
uint8_t byte(uint32_t addr)
{
return memory[addr];
}
void store_byte(uint32_t addr, uint8_t val)
{
memory[addr] = val;
}
uint8_t user_byte(uint16_t addr)
{
ZASSERT(addr < header.static_end, "attempt to read out-of-bounds address 0x%lx", static_cast<unsigned long>(addr));
return byte(addr);
}
void user_store_byte(uint16_t addr, uint8_t v)
{
// If safety checks are off, there’s no point in checking these
// special cases.
#ifndef ZTERP_NO_SAFETY_CHECKS
if (addr == 0x01) {
ZASSERT(v == byte(addr) || (byte(addr) ^ v) == 8, "not allowed to modify any bits but 3 at 0x0001");
} else if (addr == 0x10 && byte(addr) == v) {
// 0x10 can’t be modified, but let it slide if the story is
// storing the same value that’s already there. This is useful
// because the flags at 0x10 are stored in a word, so the story
// possibly could use @storew at 0x10 to modify the bits in 0x11.
return;
} else
#endif
if (addr == 0x11) {
ZASSERT((byte(addr) ^ v) < 8, "not allowed to modify bits 3-7 at 0x0011");
bool transcript_set = (v & FLAGS2_TRANSCRIPT) == FLAGS2_TRANSCRIPT;
if (!output_stream(transcript_set ? OSTREAM_SCRIPT : -OSTREAM_SCRIPT, 0)) {
v &= ~FLAGS2_TRANSCRIPT;
}
bool fixed_set = (v & FLAGS2_FIXED) == FLAGS2_FIXED;
screen_set_header_bit(fixed_set);
} else {
ZASSERT(addr >= 0x40 && addr < header.static_start, "attempt to write to read-only address 0x%lx", static_cast<unsigned long>(addr));
}
store_byte(addr, v);
}
uint16_t word(uint32_t addr)
{
#ifndef ZTERP_NO_CHEAT
uint16_t cheat_val;
if (cheat_find_freeze(addr, cheat_val)) {
return cheat_val;
}
#endif
return (memory[addr] << 8) | memory[addr + 1];
}
void store_word(uint32_t addr, uint16_t val)
{
#ifndef ZTERP_NO_WATCHPOINTS
if (addr < header.static_start - 1) {
watch_check(addr, word(addr), val);
}
#endif
memory[addr + 0] = val >> 8;
memory[addr + 1] = val & 0xff;
}
uint16_t user_word(uint16_t addr)
{
ZASSERT(addr < header.static_end - 1, "attempt to read out-of-bounds address 0x%lx", static_cast<unsigned long>(addr));
return word(addr);
}
void user_store_word(uint16_t addr, uint16_t v)
{
#ifndef ZTERP_NO_WATCHPOINTS
watch_check(addr, user_word(addr), v);
#endif
user_store_byte(addr + 0, v >> 8);
user_store_byte(addr + 1, v & 0xff);
}
void zcopy_table()
{
uint16_t first = zargs[0], second = zargs[1], size = zargs[2];
if (second == 0) {
for (uint16_t i = 0; i < size; i++) {
user_store_byte(first + i, 0);
}
} else if (first > second || as_signed(size) < 0) {
long n = std::labs(as_signed(size));
for (long i = 0; i < n; i++) {
user_store_byte(second + i, user_byte(first + i));
}
} else {
for (uint16_t i = 0; i < size; i++) {
user_store_byte(second + size - i - 1, user_byte(first + size - i - 1));
}
}
}
void zscan_table()
{
uint16_t addr = zargs[1];
if (znargs < 4) {
zargs[3] = 0x82;
}
for (uint16_t i = 0; i < zargs[2]; i++) {
if (
((zargs[3] & 0x80) == 0x80 && (user_word(addr) == zargs[0])) ||
((zargs[3] & 0x80) != 0x80 && (user_byte(addr) == zargs[0]))
) {
store(addr);
branch_if(true);
return;
}
addr += zargs[3] & 0x7f;
}
store(0);
branch_if(false);
}
void zloadw()
{
store(user_word(zargs[0] + (2 * zargs[1])));
}
void zloadb()
{
store(user_byte(zargs[0] + zargs[1]));
}
void zstoreb()
{
user_store_byte(zargs[0] + zargs[1], zargs[2]);
}
void zstorew()
{
user_store_word(zargs[0] + (2 * zargs[1]), zargs[2]);
}