-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmem.lua
312 lines (251 loc) · 6.4 KB
/
mem.lua
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
function mem_init(bootrom, rom, ppu, bitops)
local bootrom_visible = bootrom ~= nil
local ppu_read_byte = ppu.read_byte
local ppu_write_byte = ppu.write_byte
local reg_if, reg_ie = 0, 0
local reg_joyp = 0
-- Lower nibble of ^ depending on selected column
local joyp_arrows, joyp_action = 0xF, 0xF
-- Offset applied when accessing ROM at 0x4000.
-- Can be negative for true bank 0.
local rom_bank_offset = 0
-- Whether ext ram is enabled and the selected ext ram bank
local extram_enabled, extram_bank = false, 0
-- Fake timer for the DIV register. Incremented on every non-ROM read,
-- only meant to serve as source of randomness.
local reg_fake_timer = 0
local ret = {}
local wram = {}
for i = 1, 0x2000, 1 do
wram[i] = 0
end
local hram = {}
for i = 1, 0x7F, 1 do
hram[i] = 0
end
local vram = {}
ret.vram = vram
for i = 1, 0x2000 do
vram[i] = 0
end
local oam = {}
ret.oam = oam
for i = 1, 0xA0 do
oam[i] = 0
end
local extram = {}
for i = 1, 0x8000 do
extram[i] = 0
end
local cart_type = rom[1+0x147]
if cart_type > 3 and cart_type ~= 0x1b then
warn(string.format("Cartridge type 0x%02x not supported", cart_type))
end
-- Key constants for joyp_press and joyp_release
ret.btn_a, ret.btn_right = 1, 1
ret.btn_b, ret.btn_left = 2, 2
ret.btn_select, ret.btn_up = 4, 4
ret.btn_start, ret.btn_down = 8, 8
local function update_joyp()
-- Keep only matrix select lines
reg_joyp = bitops.tbl_and[0x3001 + reg_joyp]
if reg_joyp == 0x30 then
reg_joyp = reg_joyp + 0xF
elseif reg_joyp == 0x20 then
reg_joyp = reg_joyp + joyp_arrows
elseif reg_joyp == 0x10 then
reg_joyp = reg_joyp + joyp_action
else
warn("Both joyp columns selected")
end
end
function ret.joyp_press(arrows, action)
-- Clear bits
joyp_arrows = joyp_arrows - bitops.tbl_and[1 + 0x100*joyp_arrows + arrows]
joyp_action = joyp_action - bitops.tbl_and[1 + 0x100*joyp_action + action]
update_joyp()
end
function ret.joyp_release(arrows, action)
-- Set bits
joyp_arrows = bitops.tbl_or[1 + 0x100*joyp_arrows + arrows]
joyp_action = bitops.tbl_or[1 + 0x100*joyp_action + action]
update_joyp()
end
function ret.read_byte(address)
--if address < 0x100 and bootrom_visible then
-- return bootrom[1 + address]
--end
if address < 0x4000 then
return rom[1 + address]
end
if address < 0x8000 then
return rom[1 + address + rom_bank_offset]
end
if address < 0xA000 then
return vram[address - 0x7FFF]
end
if address < 0xC000 and extram_enabled then
return extram[address - 0x9FFF + extram_bank * 0x2000]
end
reg_fake_timer = reg_fake_timer + 1
if address == 0xFF04 then
return reg_fake_timer % 0x100
end
if address >= 0xC000 and address < 0xE000 then
return wram[address - 0xBFFF]
end
if address == 0xFF0F then
return reg_if
end
if address == 0xFF00 then
return reg_joyp
end
if address >= 0xFF10 and address < 0xFF40 then
return 0 -- Just ignore audio
end
if address >= 0xFF40 and address < 0xFF70 then
return ppu_read_byte(address)
end
if address >= 0xFF80 and address < 0xFFFF then
return hram[address - 0xFF7F]
end
if address == 0xFFFF then
return reg_ie
end
warn(string.format("UNIMPL: read_byte 0x%04x", address))
return 0
end
local read_byte = ret.read_byte
local sb = 0
function ret.write_byte(address, value)
if address < 0x2000 then
extram_enabled = bitops.tbl_and[0x0F01 + value] == 0x0A
return
end
if address >= 0x2000 and address < 0x3000 then
if cart_type <= 3 then
if value == 0 then
rom_bank_offset = 0
elseif value < 0x20 then
rom_bank_offset = (value - 1) * 0x4000
else
warn("UNIMPL: High rom bank bits")
end
else
assert(cart_type == 0x1b)
rom_bank_offset = (value - 1) * 0x4000
end
return
end
if address >= 0x4000 and address < 0x6000 then
extram_bank = bitops.tbl_and[0x0F01 + value]
return
end
if address >= 0x8000 and address < 0xA000 then
vram[address - 0x7FFF] = value
return
end
if address < 0xC000 and extram_enabled then
extram[address - 0x9FFF + extram_bank * 0x2000] = value
return
end
if address >= 0xC000 and address < 0xE000 then
wram[address - 0xBFFF] = value
return
end
if address >= 0xFE00 and address < 0xFEA0 then
oam[address - 0xFDFF] = value
return
end
if address >= 0xFEA0 and address < 0xFF00 then
return -- ignore
end
if address == 0xFF00 then
reg_joyp = value
update_joyp()
return
end
if address == 0xFF01 then
sb = value
return
end
if address == 0xFF02 and value == 0x81 then
if sb >= 0x80 then
sb = sb - 0x80
end
print(string.format("Recv: %c", sb))
return
end
if address == 0xFF0F then
reg_if = value
return
end
if address >= 0xFF10 and address < 0xFF40 then
return -- Just ignore audio
end
if address >= 0xFF40 and address < 0xFF70 then
if address == 0xFF46 then
-- OAM DMA. Recognize and speed up wait loops here?
local src = value * 0x100
for off = 0, 0x9F do
oam[1+off] = read_byte(src + off)
end
return
end
return ppu_write_byte(address, value)
end
if address >= 0xFF80 and address < 0xFFFF then
hram[address - 0xFF7F] = value
return
end
if address == 0xFFFF then
reg_ie = value
return
end
warn(string.format("UNIMPL: write_byte 0x%04x value %02x", address, value))
return
end
local write_byte = ret.write_byte
function ret.read_word(address)
return read_byte(address) + 0x100*read_byte(address + 1)
end
function ret.write_word(address, value)
write_byte(address, value % 0x100)
write_byte(address + 1, math.floor(value / 0x100))
end
function ret.has_bootrom()
return bootrom ~= nil
end
function ret.get_rom_bank(pc)
if pc >= 0x4000 then
return (rom_bank_offset + 0x4000) / 0x4000
else
return 0
end
end
-- Sets the corresponding bits in reg_if
function ret.raise_irq(bits)
reg_if = bitops.tbl_or[1 + 0x100*bits + reg_if]
end
-- Returns the number of the lowest set bit in reg_if®_ie
-- and clears it in reg_if
function ret.next_irq()
if reg_if == 0 or reg_ie == 0 then
return nil
end
local pending = bitops.tbl_and[1 + 0x100*reg_if + reg_ie]
if pending == 0 then
return nil
end
local mask = 0x01
for bit = 0, 5 do
if bitops.tbl_and[1 + 0x100*pending + mask] ~= 0 then
reg_if = reg_if - mask
return bit
end
mask = mask * 2
end
assert(false, "unreachable")
end
return ret
end