1 /*
2 * Copyright (C) 2002-2010 The DOSBox Team
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 /* $Id: memory.cpp,v 1.56 2009-05-27 09:15:41 qbix79 Exp $ */
20
21 #include "dosbox.h"
22 #include "mem.h"
23 #include "inout.h"
24 #include "setup.h"
25 #include "paging.h"
26 #include "regs.h"
27
28 #include <string.h>
29
30 #define PAGES_IN_BLOCK ((1024*1024)/MEM_PAGE_SIZE)
31 #define SAFE_MEMORY 32
32 #define MAX_MEMORY 64
33 #define MAX_PAGE_ENTRIES (MAX_MEMORY*1024*1024/4096)
34 #define LFB_PAGES 512
35 #define MAX_LINKS ((MAX_MEMORY*1024/4)+4096) //Hopefully enough
36
37 struct LinkBlock {
38 Bitu used;
39 Bit32u pages[MAX_LINKS];
40 };
41
42 static struct MemoryBlock {
43 Bitu pages;
44 PageHandler * * phandlers;
45 MemHandle * mhandles;
46 LinkBlock links;
47 struct {
48 Bitu start_page;
49 Bitu end_page;
50 Bitu pages;
51 PageHandler *handler;
52 PageHandler *mmiohandler;
53 } lfb;
54 struct {
55 bool enabled;
56 Bit8u controlport;
57 } a20;
58 } memory;
59
60 HostPt MemBase;
61
62 class IllegalPageHandler : public PageHandler {
63 public:
IllegalPageHandler()64 IllegalPageHandler() {
65 flags=PFLAG_INIT|PFLAG_NOCODE;
66 }
readb(PhysPt addr)67 Bitu readb(PhysPt addr) {
68 #if C_DEBUG
69 LOG_MSG("Illegal read from %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip);
70 #else
71 static Bits lcount=0;
72 if (lcount<1000) {
73 lcount++;
74 LOG_MSG("Illegal read from %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip);
75 }
76 #endif
77 return 0;
78 }
writeb(PhysPt addr,Bitu val)79 void writeb(PhysPt addr,Bitu val) {
80 #if C_DEBUG
81 LOG_MSG("Illegal write to %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip);
82 #else
83 static Bits lcount=0;
84 if (lcount<1000) {
85 lcount++;
86 LOG_MSG("Illegal write to %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip);
87 }
88 #endif
89 }
90 };
91
92 class RAMPageHandler : public PageHandler {
93 public:
RAMPageHandler()94 RAMPageHandler() {
95 flags=PFLAG_READABLE|PFLAG_WRITEABLE;
96 }
GetHostReadPt(Bitu phys_page)97 HostPt GetHostReadPt(Bitu phys_page) {
98 return MemBase+phys_page*MEM_PAGESIZE;
99 }
GetHostWritePt(Bitu phys_page)100 HostPt GetHostWritePt(Bitu phys_page) {
101 return MemBase+phys_page*MEM_PAGESIZE;
102 }
103 };
104
105 class ROMPageHandler : public RAMPageHandler {
106 public:
ROMPageHandler()107 ROMPageHandler() {
108 flags=PFLAG_READABLE|PFLAG_HASROM;
109 }
writeb(PhysPt addr,Bitu val)110 void writeb(PhysPt addr,Bitu val){
111 LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",val,addr);
112 }
writew(PhysPt addr,Bitu val)113 void writew(PhysPt addr,Bitu val){
114 LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",val,addr);
115 }
writed(PhysPt addr,Bitu val)116 void writed(PhysPt addr,Bitu val){
117 LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",val,addr);
118 }
119 };
120
121
122
123 static IllegalPageHandler illegal_page_handler;
124 static RAMPageHandler ram_page_handler;
125 static ROMPageHandler rom_page_handler;
126
MEM_SetLFB(Bitu page,Bitu pages,PageHandler * handler,PageHandler * mmiohandler)127 void MEM_SetLFB(Bitu page, Bitu pages, PageHandler *handler, PageHandler *mmiohandler) {
128 memory.lfb.handler=handler;
129 memory.lfb.mmiohandler=mmiohandler;
130 memory.lfb.start_page=page;
131 memory.lfb.end_page=page+pages;
132 memory.lfb.pages=pages;
133 PAGING_ClearTLB();
134 }
135
MEM_GetPageHandler(Bitu phys_page)136 PageHandler * MEM_GetPageHandler(Bitu phys_page) {
137 if (phys_page<memory.pages) {
138 return memory.phandlers[phys_page];
139 } else if ((phys_page>=memory.lfb.start_page) && (phys_page<memory.lfb.end_page)) {
140 return memory.lfb.handler;
141 } else if ((phys_page>=memory.lfb.start_page+0x01000000/4096) &&
142 (phys_page<memory.lfb.start_page+0x01000000/4096+16)) {
143 return memory.lfb.mmiohandler;
144 }
145 return &illegal_page_handler;
146 }
147
MEM_SetPageHandler(Bitu phys_page,Bitu pages,PageHandler * handler)148 void MEM_SetPageHandler(Bitu phys_page,Bitu pages,PageHandler * handler) {
149 for (;pages>0;pages--) {
150 memory.phandlers[phys_page]=handler;
151 phys_page++;
152 }
153 }
154
MEM_ResetPageHandler(Bitu phys_page,Bitu pages)155 void MEM_ResetPageHandler(Bitu phys_page, Bitu pages) {
156 for (;pages>0;pages--) {
157 memory.phandlers[phys_page]=&ram_page_handler;
158 phys_page++;
159 }
160 }
161
mem_strlen(PhysPt pt)162 Bitu mem_strlen(PhysPt pt) {
163 Bitu x=0;
164 while (x<1024) {
165 if (!mem_readb_inline(pt+x)) return x;
166 x++;
167 }
168 return 0; //Hope this doesn't happen
169 }
170
mem_strcpy(PhysPt dest,PhysPt src)171 void mem_strcpy(PhysPt dest,PhysPt src) {
172 Bit8u r;
173 while ( (r = mem_readb(src++)) ) mem_writeb_inline(dest++,r);
174 mem_writeb_inline(dest,0);
175 }
176
mem_memcpy(PhysPt dest,PhysPt src,Bitu size)177 void mem_memcpy(PhysPt dest,PhysPt src,Bitu size) {
178 while (size--) mem_writeb_inline(dest++,mem_readb_inline(src++));
179 }
180
MEM_BlockRead(PhysPt pt,void * data,Bitu size)181 void MEM_BlockRead(PhysPt pt,void * data,Bitu size) {
182 Bit8u * write=reinterpret_cast<Bit8u *>(data);
183 while (size--) {
184 *write++=mem_readb_inline(pt++);
185 }
186 }
187
MEM_BlockWrite(PhysPt pt,void const * const data,Bitu size)188 void MEM_BlockWrite(PhysPt pt,void const * const data,Bitu size) {
189 Bit8u const * read = reinterpret_cast<Bit8u const * const>(data);
190 while (size--) {
191 mem_writeb_inline(pt++,*read++);
192 }
193 }
194
MEM_BlockCopy(PhysPt dest,PhysPt src,Bitu size)195 void MEM_BlockCopy(PhysPt dest,PhysPt src,Bitu size) {
196 mem_memcpy(dest,src,size);
197 }
198
MEM_StrCopy(PhysPt pt,char * data,Bitu size)199 void MEM_StrCopy(PhysPt pt,char * data,Bitu size) {
200 while (size--) {
201 Bit8u r=mem_readb_inline(pt++);
202 if (!r) break;
203 *data++=r;
204 }
205 *data=0;
206 }
207
MEM_TotalPages(void)208 Bitu MEM_TotalPages(void) {
209 return memory.pages;
210 }
211
MEM_FreeLargest(void)212 Bitu MEM_FreeLargest(void) {
213 Bitu size=0;Bitu largest=0;
214 Bitu index=XMS_START;
215 while (index<memory.pages) {
216 if (!memory.mhandles[index]) {
217 size++;
218 } else {
219 if (size>largest) largest=size;
220 size=0;
221 }
222 index++;
223 }
224 if (size>largest) largest=size;
225 return largest;
226 }
227
MEM_FreeTotal(void)228 Bitu MEM_FreeTotal(void) {
229 Bitu free=0;
230 Bitu index=XMS_START;
231 while (index<memory.pages) {
232 if (!memory.mhandles[index]) free++;
233 index++;
234 }
235 return free;
236 }
237
MEM_AllocatedPages(MemHandle handle)238 Bitu MEM_AllocatedPages(MemHandle handle)
239 {
240 Bitu pages = 0;
241 while (handle>0) {
242 pages++;
243 handle=memory.mhandles[handle];
244 }
245 return pages;
246 }
247
248 //TODO Maybe some protection for this whole allocation scheme
249
BestMatch(Bitu size)250 INLINE Bitu BestMatch(Bitu size) {
251 Bitu index=XMS_START;
252 Bitu first=0;
253 Bitu best=0xfffffff;
254 Bitu best_first=0;
255 while (index<memory.pages) {
256 /* Check if we are searching for first free page */
257 if (!first) {
258 /* Check if this is a free page */
259 if (!memory.mhandles[index]) {
260 first=index;
261 }
262 } else {
263 /* Check if this still is used page */
264 if (memory.mhandles[index]) {
265 Bitu pages=index-first;
266 if (pages==size) {
267 return first;
268 } else if (pages>size) {
269 if (pages<best) {
270 best=pages;
271 best_first=first;
272 }
273 }
274 first=0; //Always reset for new search
275 }
276 }
277 index++;
278 }
279 /* Check for the final block if we can */
280 if (first && (index-first>=size) && (index-first<best)) {
281 return first;
282 }
283 return best_first;
284 }
285
MEM_AllocatePages(Bitu pages,bool sequence)286 MemHandle MEM_AllocatePages(Bitu pages,bool sequence) {
287 MemHandle ret;
288 if (!pages) return 0;
289 if (sequence) {
290 Bitu index=BestMatch(pages);
291 if (!index) return 0;
292 MemHandle * next=&ret;
293 while (pages) {
294 *next=index;
295 next=&memory.mhandles[index];
296 index++;pages--;
297 }
298 *next=-1;
299 } else {
300 if (MEM_FreeTotal()<pages) return 0;
301 MemHandle * next=&ret;
302 while (pages) {
303 Bitu index=BestMatch(1);
304 if (!index) E_Exit("MEM:corruption during allocate");
305 while (pages && (!memory.mhandles[index])) {
306 *next=index;
307 next=&memory.mhandles[index];
308 index++;pages--;
309 }
310 *next=-1; //Invalidate it in case we need another match
311 }
312 }
313 return ret;
314 }
315
MEM_GetNextFreePage(void)316 MemHandle MEM_GetNextFreePage(void) {
317 return (MemHandle)BestMatch(1);
318 }
319
MEM_ReleasePages(MemHandle handle)320 void MEM_ReleasePages(MemHandle handle) {
321 while (handle>0) {
322 MemHandle next=memory.mhandles[handle];
323 memory.mhandles[handle]=0;
324 handle=next;
325 }
326 }
327
MEM_ReAllocatePages(MemHandle & handle,Bitu pages,bool sequence)328 bool MEM_ReAllocatePages(MemHandle & handle,Bitu pages,bool sequence) {
329 if (handle<=0) {
330 if (!pages) return true;
331 handle=MEM_AllocatePages(pages,sequence);
332 return (handle>0);
333 }
334 if (!pages) {
335 MEM_ReleasePages(handle);
336 handle=-1;
337 return true;
338 }
339 MemHandle index=handle;
340 MemHandle last;Bitu old_pages=0;
341 while (index>0) {
342 old_pages++;
343 last=index;
344 index=memory.mhandles[index];
345 }
346 if (old_pages == pages) return true;
347 if (old_pages > pages) {
348 /* Decrease size */
349 pages--;index=handle;old_pages--;
350 while (pages) {
351 index=memory.mhandles[index];
352 pages--;old_pages--;
353 }
354 MemHandle next=memory.mhandles[index];
355 memory.mhandles[index]=-1;
356 index=next;
357 while (old_pages) {
358 next=memory.mhandles[index];
359 memory.mhandles[index]=0;
360 index=next;
361 old_pages--;
362 }
363 return true;
364 } else {
365 /* Increase size, check for enough free space */
366 Bitu need=pages-old_pages;
367 if (sequence) {
368 index=last+1;
369 Bitu free=0;
370 while ((index<(MemHandle)memory.pages) && !memory.mhandles[index]) {
371 index++;free++;
372 }
373 if (free>=need) {
374 /* Enough space allocate more pages */
375 index=last;
376 while (need) {
377 memory.mhandles[index]=index+1;
378 need--;index++;
379 }
380 memory.mhandles[index]=-1;
381 return true;
382 } else {
383 /* Not Enough space allocate new block and copy */
384 MemHandle newhandle=MEM_AllocatePages(pages,true);
385 if (!newhandle) return false;
386 MEM_BlockCopy(newhandle*4096,handle*4096,old_pages*4096);
387 MEM_ReleasePages(handle);
388 handle=newhandle;
389 return true;
390 }
391 } else {
392 MemHandle rem=MEM_AllocatePages(need,false);
393 if (!rem) return false;
394 memory.mhandles[last]=rem;
395 return true;
396 }
397 }
398 return 0;
399 }
400
MEM_NextHandle(MemHandle handle)401 MemHandle MEM_NextHandle(MemHandle handle) {
402 return memory.mhandles[handle];
403 }
404
MEM_NextHandleAt(MemHandle handle,Bitu where)405 MemHandle MEM_NextHandleAt(MemHandle handle,Bitu where) {
406 while (where) {
407 where--;
408 handle=memory.mhandles[handle];
409 }
410 return handle;
411 }
412
413
414 /*
415 A20 line handling,
416 Basically maps the 4 pages at the 1mb to 0mb in the default page directory
417 */
MEM_A20_Enabled(void)418 bool MEM_A20_Enabled(void) {
419 return memory.a20.enabled;
420 }
421
MEM_A20_Enable(bool enabled)422 void MEM_A20_Enable(bool enabled) {
423 Bitu phys_base=enabled ? (1024/4) : 0;
424 for (Bitu i=0;i<16;i++) PAGING_MapPage((1024/4)+i,phys_base+i);
425 memory.a20.enabled=enabled;
426 }
427
428
429 /* Memory access functions */
mem_unalignedreadw(PhysPt address)430 Bit16u mem_unalignedreadw(PhysPt address) {
431 Bit16u ret = mem_readb_inline(address);
432 ret |= mem_readb_inline(address+1) << 8;
433 return ret;
434 }
435
mem_unalignedreadd(PhysPt address)436 Bit32u mem_unalignedreadd(PhysPt address) {
437 Bit32u ret = mem_readb_inline(address);
438 ret |= mem_readb_inline(address+1) << 8;
439 ret |= mem_readb_inline(address+2) << 16;
440 ret |= mem_readb_inline(address+3) << 24;
441 return ret;
442 }
443
444
mem_unalignedwritew(PhysPt address,Bit16u val)445 void mem_unalignedwritew(PhysPt address,Bit16u val) {
446 mem_writeb_inline(address,(Bit8u)val);val>>=8;
447 mem_writeb_inline(address+1,(Bit8u)val);
448 }
449
mem_unalignedwrited(PhysPt address,Bit32u val)450 void mem_unalignedwrited(PhysPt address,Bit32u val) {
451 mem_writeb_inline(address,(Bit8u)val);val>>=8;
452 mem_writeb_inline(address+1,(Bit8u)val);val>>=8;
453 mem_writeb_inline(address+2,(Bit8u)val);val>>=8;
454 mem_writeb_inline(address+3,(Bit8u)val);
455 }
456
457
mem_unalignedreadw_checked(PhysPt address,Bit16u * val)458 bool mem_unalignedreadw_checked(PhysPt address, Bit16u * val) {
459 Bit8u rval1,rval2;
460 if (mem_readb_checked(address+0, &rval1)) return true;
461 if (mem_readb_checked(address+1, &rval2)) return true;
462 *val=(Bit16u)(((Bit8u)rval1) | (((Bit8u)rval2) << 8));
463 return false;
464 }
465
mem_unalignedreadd_checked(PhysPt address,Bit32u * val)466 bool mem_unalignedreadd_checked(PhysPt address, Bit32u * val) {
467 Bit8u rval1,rval2,rval3,rval4;
468 if (mem_readb_checked(address+0, &rval1)) return true;
469 if (mem_readb_checked(address+1, &rval2)) return true;
470 if (mem_readb_checked(address+2, &rval3)) return true;
471 if (mem_readb_checked(address+3, &rval4)) return true;
472 *val=(Bit32u)(((Bit8u)rval1) | (((Bit8u)rval2) << 8) | (((Bit8u)rval3) << 16) | (((Bit8u)rval4) << 24));
473 return false;
474 }
475
mem_unalignedwritew_checked(PhysPt address,Bit16u val)476 bool mem_unalignedwritew_checked(PhysPt address,Bit16u val) {
477 if (mem_writeb_checked(address,(Bit8u)(val & 0xff))) return true;val>>=8;
478 if (mem_writeb_checked(address+1,(Bit8u)(val & 0xff))) return true;
479 return false;
480 }
481
mem_unalignedwrited_checked(PhysPt address,Bit32u val)482 bool mem_unalignedwrited_checked(PhysPt address,Bit32u val) {
483 if (mem_writeb_checked(address,(Bit8u)(val & 0xff))) return true;val>>=8;
484 if (mem_writeb_checked(address+1,(Bit8u)(val & 0xff))) return true;val>>=8;
485 if (mem_writeb_checked(address+2,(Bit8u)(val & 0xff))) return true;val>>=8;
486 if (mem_writeb_checked(address+3,(Bit8u)(val & 0xff))) return true;
487 return false;
488 }
489
mem_readb(PhysPt address)490 Bit8u mem_readb(PhysPt address) {
491 return mem_readb_inline(address);
492 }
493
mem_readw(PhysPt address)494 Bit16u mem_readw(PhysPt address) {
495 return mem_readw_inline(address);
496 }
497
mem_readd(PhysPt address)498 Bit32u mem_readd(PhysPt address) {
499 return mem_readd_inline(address);
500 }
501
mem_writeb(PhysPt address,Bit8u val)502 void mem_writeb(PhysPt address,Bit8u val) {
503 mem_writeb_inline(address,val);
504 }
505
mem_writew(PhysPt address,Bit16u val)506 void mem_writew(PhysPt address,Bit16u val) {
507 mem_writew_inline(address,val);
508 }
509
mem_writed(PhysPt address,Bit32u val)510 void mem_writed(PhysPt address,Bit32u val) {
511 mem_writed_inline(address,val);
512 }
513
write_p92(Bitu port,Bitu val,Bitu iolen)514 static void write_p92(Bitu port,Bitu val,Bitu iolen) {
515 // Bit 0 = system reset (switch back to real mode)
516 if (val&1) E_Exit("XMS: CPU reset via port 0x92 not supported.");
517 memory.a20.controlport = val & ~2;
518 MEM_A20_Enable((val & 2)>0);
519 }
520
read_p92(Bitu port,Bitu iolen)521 static Bitu read_p92(Bitu port,Bitu iolen) {
522 return memory.a20.controlport | (memory.a20.enabled ? 0x02 : 0);
523 }
524
RemoveEMSPageFrame(void)525 void RemoveEMSPageFrame(void) {
526 /* Setup rom at 0xe0000-0xf0000 */
527 for (Bitu ct=0xe0;ct<0xf0;ct++) {
528 memory.phandlers[ct] = &rom_page_handler;
529 }
530 }
531
PreparePCJRCartRom(void)532 void PreparePCJRCartRom(void) {
533 /* Setup rom at 0xd0000-0xe0000 */
534 for (Bitu ct=0xd0;ct<0xe0;ct++) {
535 memory.phandlers[ct] = &rom_page_handler;
536 }
537 }
538
GetMemBase(void)539 HostPt GetMemBase(void) { return MemBase; }
540
541 class MEMORY:public Module_base{
542 private:
543 IO_ReadHandleObject ReadHandler;
544 IO_WriteHandleObject WriteHandler;
545 public:
MEMORY(Section * configuration)546 MEMORY(Section* configuration):Module_base(configuration){
547 Bitu i;
548 Section_prop * section=static_cast<Section_prop *>(configuration);
549
550 /* Setup the Physical Page Links */
551 Bitu memsize=section->Get_int("memsize");
552
553 if (memsize < 1) memsize = 1;
554 /* max 63 to solve problems with certain xms handlers */
555 if (memsize > MAX_MEMORY-1) {
556 LOG_MSG("Maximum memory size is %d MB",MAX_MEMORY - 1);
557 memsize = MAX_MEMORY-1;
558 }
559 if (memsize > SAFE_MEMORY-1) {
560 LOG_MSG("Memory sizes above %d MB are NOT recommended.",SAFE_MEMORY - 1);
561 LOG_MSG("Stick with the default values unless you are absolutely certain.");
562 }
563 MemBase = new Bit8u[memsize*1024*1024];
564 if (!MemBase) E_Exit("Can't allocate main memory of %d MB",memsize);
565 /* Clear the memory, as new doesn't always give zeroed memory
566 * (Visual C debug mode). We want zeroed memory though. */
567 memset((void*)MemBase,0,memsize*1024*1024);
568 memory.pages = (memsize*1024*1024)/4096;
569 /* Allocate the data for the different page information blocks */
570 memory.phandlers=new PageHandler * [memory.pages];
571 memory.mhandles=new MemHandle [memory.pages];
572 for (i = 0;i < memory.pages;i++) {
573 memory.phandlers[i] = &ram_page_handler;
574 memory.mhandles[i] = 0; //Set to 0 for memory allocation
575 }
576 /* Setup rom at 0xc0000-0xc8000 */
577 for (i=0xc0;i<0xc8;i++) {
578 memory.phandlers[i] = &rom_page_handler;
579 }
580 /* Setup rom at 0xf0000-0x100000 */
581 for (i=0xf0;i<0x100;i++) {
582 memory.phandlers[i] = &rom_page_handler;
583 }
584 if (machine==MCH_PCJR) {
585 /* Setup cartridge rom at 0xe0000-0xf0000 */
586 for (i=0xe0;i<0xf0;i++) {
587 memory.phandlers[i] = &rom_page_handler;
588 }
589 }
590 /* Reset some links */
591 memory.links.used = 0;
592 // A20 Line - PS/2 system control port A
593 WriteHandler.Install(0x92,write_p92,IO_MB);
594 ReadHandler.Install(0x92,read_p92,IO_MB);
595 MEM_A20_Enable(false);
596 }
~MEMORY()597 ~MEMORY(){
598 delete [] MemBase;
599 delete [] memory.phandlers;
600 delete [] memory.mhandles;
601 }
602 };
603
604
605 static MEMORY* test;
606
MEM_ShutDown(Section * sec)607 static void MEM_ShutDown(Section * sec) {
608 delete test;
609 }
610
MEM_Init(Section * sec)611 void MEM_Init(Section * sec) {
612 /* shutdown function */
613 test = new MEMORY(sec);
614 sec->AddDestroyFunction(&MEM_ShutDown);
615 }
616