Add info about supported arches.
[v86d.git] / v86_mem.c
blobc6bdebbd460d969abbe52bc10d9965ccbff5932e
1 #include <errno.h>
2 #include <fcntl.h>
3 #include <string.h>
4 #include <sys/mman.h>
5 #include <unistd.h>
6 #include "v86.h"
8 #define REAL_MEM_BLOCKS 0x100
10 u8 *mem_low; /* 0x000000 - 0x001000 */
11 u8 *mem_real; /* 0x010000 - 0x09ffff */
12 u8 *mem_bios; /* 0x0a0000 - 0x10ffef */
14 struct mem_block {
15 unsigned int size : 20;
16 unsigned int free : 1;
19 static struct {
20 int ready;
21 int count;
22 struct mem_block blocks[REAL_MEM_BLOCKS];
23 } mem_info = { 0 };
25 void *vptr(u32 addr) {
26 if (addr < IVTBDA_SIZE)
27 return (mem_low + addr);
28 else if (addr >= REAL_MEM_BASE && addr < REAL_MEM_BASE + REAL_MEM_SIZE)
29 return (mem_real + addr - REAL_MEM_BASE);
30 else if (addr >= BIOS_BASE && addr < BIOS_BASE + BIOS_SIZE)
31 return (mem_bios + addr - BIOS_BASE);
32 else {
33 ulog("trying to access an unsupported memory region at %x", addr);
34 return NULL;
38 /* We don't care about memory accesses at boundaries of different memory
39 * regions, since our v86 memory is non contiguous anyway. */
40 u8 v_rdb(u32 addr) {
41 return *(u8*) vptr(addr);
44 u16 v_rdw(u32 addr) {
45 return *(u16*) vptr(addr);
48 u32 v_rdl(u32 addr) {
49 return *(u32*) vptr(addr);
52 void v_wrb(u32 addr, u8 val) {
53 u8 *t = vptr(addr);
54 *t = val;
57 void v_wrw(u32 addr, u16 val) {
58 u16 *t = vptr(addr);
59 *t = val;
62 void v_wrl(u32 addr, u32 val) {
63 u32 *t = vptr(addr);
64 *t = val;
67 static void *map_file(void *start, size_t length, int prot, int flags, char *name, long offset)
69 void *m;
70 int fd;
72 fd = open(name, (flags & MAP_SHARED) ? O_RDWR : O_RDONLY);
74 if (fd == -1) {
75 ulog("open '%s' failed with: %s\n", name, strerror(errno));
76 return NULL;
79 m = mmap(start, length, prot, flags, fd, offset);
81 if (m == (void *)-1) {
82 ulog("mmap '%s' failed with: %s\n", name, strerror(errno));
83 close(fd);
84 return NULL;
87 close(fd);
88 return m;
91 static int real_mem_init(void)
93 if (mem_info.ready)
94 return 0;
96 mem_real = map_file(NULL, REAL_MEM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
97 MAP_PRIVATE, "/dev/zero", 0);
98 if (!mem_real)
99 return 0;
101 mem_info.ready = 1;
102 mem_info.count = 1;
103 mem_info.blocks[0].size = REAL_MEM_SIZE;
104 mem_info.blocks[0].free = 1;
106 return 0;
109 static void real_mem_deinit(void)
111 if (mem_info.ready) {
112 munmap(mem_real, REAL_MEM_SIZE);
113 mem_info.ready = 0;
117 static void insert_block(int i)
119 memmove(mem_info.blocks + i + 1, mem_info.blocks + i,
120 (mem_info.count - i) * sizeof(struct mem_block));
121 mem_info.count++;
124 static void delete_block(int i)
126 mem_info.count--;
127 memmove(mem_info.blocks + i, mem_info.blocks + i + 1,
128 (mem_info.count - i) * sizeof(struct mem_block));
131 u32 v86_mem_alloc(int size)
133 int i;
134 u32 r = REAL_MEM_BASE;
136 if (!mem_info.ready)
137 return 0;
139 if (mem_info.count == REAL_MEM_BLOCKS)
140 return 0;
142 size = (size + 15) & ~15;
144 for (i = 0; i < mem_info.count; i++) {
145 if (mem_info.blocks[i].free && size < mem_info.blocks[i].size) {
146 insert_block(i);
148 mem_info.blocks[i].size = size;
149 mem_info.blocks[i].free = 0;
150 mem_info.blocks[i + 1].size -= size;
152 return r;
155 r += mem_info.blocks[i].size;
158 return 0;
161 void v86_mem_free(u32 m)
163 int i;
164 u32 r = REAL_MEM_BASE;
166 if (!mem_info.ready)
167 return;
169 i = 0;
170 while (m != r) {
171 r += mem_info.blocks[i].size;
172 i++;
173 if (i == mem_info.count)
174 return;
177 mem_info.blocks[i].free = 1;
179 if (i + 1 < mem_info.count && mem_info.blocks[i + 1].free) {
180 mem_info.blocks[i].size += mem_info.blocks[i + 1].size;
181 delete_block(i + 1);
184 if (i - 1 >= 0 && mem_info.blocks[i - 1].free) {
185 mem_info.blocks[i - 1].size += mem_info.blocks[i].size;
186 delete_block(i);
190 int v86_mem_init(void)
192 if (real_mem_init())
193 return 1;
196 * We have to map the IVTBDA as shared. Without it, setting video
197 * modes will not work correctly on some cards (e.g. nVidia GeForce
198 * 8600M, PCI ID 10de:0425).
200 mem_low = map_file(NULL, IVTBDA_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
201 MAP_SHARED, "/dev/mem", IVTBDA_BASE);
202 if (!mem_low) {
203 real_mem_deinit();
204 return 1;
207 mem_bios = map_file(NULL, BIOS_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
208 MAP_SHARED, "/dev/mem", BIOS_BASE);
209 if (!mem_bios) {
210 munmap(mem_low, IVTBDA_SIZE);
211 real_mem_deinit();
212 return 1;
215 return 0;
218 void v86_mem_cleanup(void)
220 munmap(mem_low, IVTBDA_SIZE);
221 munmap(mem_bios, BIOS_SIZE);
223 real_mem_deinit();