forked from Spritetm/plexus_20_emu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmapper.c
258 lines (220 loc) · 7.22 KB
/
mapper.c
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
/*
Simulation of the mapper memory space and functions
*/
/*
SPDX-License-Identifier: MIT
Copyright (c) 2024 Sprite_tm <[email protected]>
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#include "csr.h"
#include "emu.h"
#include "log.h"
#include "mapper.h"
#include "ramrom.h"
// Debug logging
#define MAPPER_LOG(msg_level, format_and_args...) \
log_printf(LOG_SRC_MAPPER, msg_level, format_and_args)
#define MAPPER_LOG_DEBUG(format_and_args...) MAPPER_LOG(LOG_DEBUG, format_and_args)
#define MAPPER_LOG_INFO(format_and_args...) MAPPER_LOG(LOG_INFO, format_and_args)
/*
On the MMU:
We have 2048 entries (well, x2, one set for sys and one for usr)
This is virtual memory; if the address space is 8M, this means
a page size of 4K.
We have space for 8K physical pages; this means we could map
to 32MiB of physical RAM.
Note that W1 and W0 are kinda messed up here... in the docs, W1 is
the word written when a[1]=1 and W0 the one written when a[1]=0. The
68K is big-endian, so when parsed as a 32-bit field, the result would
be (W0<<16)+W1. However, this code does it the other way around, and
when parsing access permissions, it checks against (W1<<16)+W0.
*/
typedef struct {
uint16_t w0;
uint16_t w1;
} desc_t;
#define SYS_ENTRY_START 2048
//Note: on write of 32-bit, w0 is msb and w1 lsb
//Note the RWX bits *disable* that access when 1.
#define W1_R 0x8000
#define W1_W 0x4000
#define W1_X 0x2000
#define W1_PAGE_MASK 0x1FFF
#define W0_REFD 0x2 //Page is accesed
#define W0_ALTRD 0x1 //Page is written to
#define W0_UID_SHIFT 8
#define W0_UID_MASK 0xff
struct mapper_t {
//2K entries for usr, 2K for sys
desc_t desc[4096];
ram_t *physram;
int sysmode; //indicates if next accesses are in sysmode or not
int cur_id; //current mapper ID
int yolo; //'yolo-hack' enable flag
};
void mapper_set_mapid(mapper_t *m, uint8_t id) {
if (m->cur_id!=id) MAPPER_LOG_DEBUG("Switching to map id %d\n", id);
m->cur_id=id;
}
//returns fault indicator, or 0 if allowed
static int access_allowed_page(mapper_t *m, unsigned int page, int access_flags) {
assert(page<4096);
unsigned int ac=(m->desc[page].w1<<16)+m->desc[page].w0;
//Set fault to the access flags we need but are not set in the page.
int fault=(ac&access_flags)&(ACCESS_R|ACCESS_W|ACCESS_X);
int uid=(ac>>W0_UID_SHIFT)&W0_UID_MASK;
if ((access_flags&ACCESS_SYSTEM)==0) {
//If there's an uid fault, we set the lower 8 bits to 0xff
//and make the next 8 bits the uid.
if (uid != m->cur_id) fault=(uid<<8|0xff);
}
if (fault) {
MAPPER_LOG_DEBUG("Mapper: Access fault: page ent %x req %x, fault %x (", ac, access_flags, fault);
if (fault&(W1_W<<16)) MAPPER_LOG_DEBUG("write violation ");
if (fault&(W1_R<<16)) MAPPER_LOG_DEBUG("read violation ");
if (fault&(W1_X<<16)) MAPPER_LOG_DEBUG("execute violation ");
if (fault&0xff) MAPPER_LOG_DEBUG("proc uid %d page uid %d ", m->cur_id, uid);
MAPPER_LOG_DEBUG(")\n");
}
return fault;
}
//Returns either ACCESS_ERROR_OK if address can be accessed, or ACCESS_ERROR_A for a
//permission issue, or ACCESS_ERROR_U for a mapper ID mismatch.
int mapper_access_allowed(mapper_t *m, unsigned int a, int access_flags) {
if (a>=0x800000) {
//Anything except RAM does not go through the mapper, but is only
//accessible in system mode.
int ret=(access_flags&ACCESS_SYSTEM)?ACCESS_ERROR_OK:ACCESS_ERROR_A;
if (ret==ACCESS_ERROR_A) {
MAPPER_LOG_INFO("mapper_access_allowed: address %x not accessible in user mode\n", a);
}
return ret;
}
//Map virtual page to phyical page.
int p=a>>12; //4K pages
assert(p<=2048 && "out of range addr");
if (access_flags&ACCESS_SYSTEM) p+=2048;
int r=access_allowed_page(m, p, access_flags);
//The 'yolo' hack: if enabled, the first 2 2 32-bit words in RAM are never
//write-protected in system mode. The system needs these to be written for
//'init 2' to work, but at that time page 0 is write-protected. Up until now,
//we haven't found anything, either in hardware or software, that can allow
//this write through. Thanks to @ewen for the hack and the name.
if (p==2048 && a<8 && m->yolo) {
r&=~(W1_W<<16);
}
if (r && log_level_active(LOG_SRC_MAPPER, LOG_DEBUG)) {
MAPPER_LOG_DEBUG("Mapper: Access fault at addr %x page %d. CPU state:\n", a, p);
dump_cpu_state();
dump_callstack();
MAPPER_LOG_DEBUG("Mapper: Dump done.\n");
}
int x=ACCESS_ERROR_OK;
if (r) x=ACCESS_ERROR_A;
if (r&0xff00) x=ACCESS_ERROR_U;
return x;
}
void mapper_write16(void *obj, unsigned int a, unsigned int val) {
assert((a&1)==0);
if (emu_get_cur_cpu()==0) return; //seems writes from dma cpu are not allowed
mapper_t *m=(mapper_t*)obj;
a=a/2; //word addr
if (a&1) {
m->desc[a/2].w1=val;
} else {
m->desc[a/2].w0=val;
}
}
void mapper_write32(void *obj, unsigned int a, unsigned int val) {
mapper_write16(obj, a, val>>16);
mapper_write16(obj, a+2, val&0xffff);
}
unsigned int mapper_read16(void *obj, unsigned int a) {
assert((a&1)==0);
mapper_t *m=(mapper_t*)obj;
a=a/2; //word addr
if (a&1) {
return m->desc[a/2].w1;
} else {
return m->desc[a/2].w0;
}
}
void mapper_write8(void *obj, unsigned int a, unsigned int val) {
int v=mapper_read16(obj, a&~1);
if (a&1) {
v=(v&0xFF00)|(val&0xff);
} else {
v=(v&0xFF)|((val<<8)&0xff00);
}
mapper_write16(obj, a&~1, v);
}
unsigned int mapper_read8(void *obj, unsigned int a) {
int v=mapper_read16(obj, a&~1);
if (a&1) {
return v&0xff;
} else {
return v>>8;
}
}
unsigned int mapper_read32(void *obj, unsigned int a) {
return (mapper_read16(obj,a)<<16)+mapper_read16(obj, a+2);
}
void mapper_set_sysmode(mapper_t *m, int cpu_in_sysmode) {
m->sysmode=cpu_in_sysmode;
}
//Map virtual page to phyical page. Note: does not check access rights.
//Returns corresponding physical address for virtual address a.
//Also modifies altered/referenced bits as needed.
int do_map(mapper_t *m, unsigned int a, unsigned int is_write) {
int p=a>>12; //4K pages
assert(p<2048);
if (m->sysmode) p+=SYS_ENTRY_START;
m->desc[p].w0|=W0_REFD;
if (is_write) m->desc[p].w0|=W0_ALTRD;
int phys_p=m->desc[p].w1&W1_PAGE_MASK;
int phys=(a&0xFFF)|(phys_p<<12);
//map to 8MiB physical memory at max
phys&=((8*1024*1024)-1);
return phys;
}
void mapper_ram_write8(void *obj, unsigned int a, unsigned int val) {
mapper_t *m=(mapper_t*)obj;
a=do_map(m, a, 1);
if (a<0) return;
ram_write8(m->physram, a, val);
}
void mapper_ram_write16(void *obj, unsigned int a, unsigned int val) {
assert((a&1)==0);
mapper_t *m=(mapper_t*)obj;
a=do_map(m, a, 1);
if (a<0) return;
ram_write16(m->physram, a, val);
}
void mapper_ram_write32(void *obj, unsigned int a, unsigned int val) {
mapper_ram_write16(obj, a, val>>16);
mapper_ram_write16(obj, a+2, val&0xffff);
}
unsigned int mapper_ram_read8(void *obj, unsigned int a) {
mapper_t *m=(mapper_t*)obj;
a=do_map(m, a, 0);
if (a<0) return 0;
return ram_read8(m->physram, a);
}
unsigned int mapper_ram_read16(void *obj, unsigned int a) {
mapper_t *m=(mapper_t*)obj;
a=do_map(m, a, 0);
if (a<0) return 0;
return ram_read16(m->physram, a);
}
unsigned int mapper_ram_read32(void *obj, unsigned int a) {
return (mapper_ram_read16(obj, a)<<16) | (mapper_ram_read16(obj, a+2)&0xffff);
}
mapper_t *mapper_new(ram_t *physram, int size, int yolo) {
mapper_t *ret=calloc(sizeof(mapper_t), 1);
ret->physram=physram;
ret->yolo=yolo;
return ret;
}