It is currently Sat Dec 16, 2017 7:42 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 10 posts ] 
Author Message
PostPosted: Tue Aug 02, 2005 2:13 pm 
Offline
User avatar

Joined: Tue Dec 21, 2004 8:35 pm
Posts: 600
Location: Argentina
i was wondering how to manage memory in the way so when games that have mappers i dont have to copy directly to the cpu mem, let say an example in MMC3: if we do a STA $8000, we are changing the control register, but the value its not written to the real $8000 mem, i mean if this value changes it compromises code execution.
In other words: how to threat memory so i dont have to swap in and out an entire block from mapper memory to the one the cpu uses.

_________________
ANes


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 02, 2005 6:17 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19353
Location: NE Indiana, USA (NTSC)
It involves pointers.

For each 4 KB of address space, store the start address of the ROM bank mapped into that address space.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 7:42 pm 
If your OS supports mmap() functionality, and the memory page size is small enough, you can malloc() 64KiB of memory, and mmap() the appropriate emulated PRG ROM pages and RAM in.


Top
  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 8:36 pm 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
My advice:

Every Read/Write should be done through a function (with the acception of zero page reads/writes, which can always be done with RAM directly, as Zero page will always be RAM).

Set up several Read and Write functions which cover different areas of addressing space. For example:

Read_RAM (for $0000-1FFF)
Read_2xxx (for $2000-3FFF)
Read_4xxx (for $4000-4FFF)
Read_OpenBus (for $5000-5FFF)
Read_SRAM (for $6000-7FFF on supported games)
Read_PRG (for $8000-FFFF)

Keep 16 function pointers for reading, and 16 function pointers for writing, each which covers a 4k page of addressing space.

Code:
// make the pointers
ReadProc   ReadMemory[0x10];

// set up the pointers
ReadMemory[0x0] = Read_RAM;
ReadMemory[0x1] = Read_RAM;
ReadMemory[0x2] = Read_2xxx;
...
ReadMemory[0xF] = Read_PRG;



When the game performs a Read/Write, simply call the function which represents that area of addressing space. You can do this easily by pulling the high digit of the address and using it as an index (simple right shift by 12)

Code:
#define CPU_READ(adr)   ReadMemory[(adr) >> 12](adr)


This provides many benefits:

1) You can avoid doing several if-else chains for every read
2) When games adjust addressing space (by disabling WRAM, or putting RAM @ $8000 and up, or other weird things), this can easily be accomidated by changing the function pointer.
3) Mappers can easily catch their register writes by having their own write function and changing function pointers so that it gets called whenever the game writes there.



PRG/CHR swapping can be done easily by keeping one large buffer which holds ALL the game's PRG/CHR data.. and by keeping several pointers to represent PRG/CHR banks.

Code:
u8 nPRGBuffer[ size_of_games_prg ]; /* this should be allocated dynamically on ROM load with malloc() or new[] or whatever */

u8* pPRG[8];  // 8 pointers, each represents 4k of PRG space


With that above code -- each of the 8 pPRG pointers represents a 4k page of PRG. pPRG[0] would be cpu$8000, pPRG[1] would be cpu$9000, etc. To work this into the above mentioned Read_PRG function:

Code:
u8 Read_PRG(u16 adr)
{
  return pPRG[(adr >> 12) - 8][adr & 0x0FFF];
}


That will return the appropriate byte from the appropriate bank which was swapped in.

With this method, bankswapping can be done VERY easily by just changing a few pointers:

Code:
void Swap8kPRG(int where, int page)
{
  page *= 0x2000;
  pPRG[where] = &nPRGBuffer[ page ];
  pPRG[where + 1] = &nPRGBuffer[ page + 0x1000 ];
}


This allows you to swap PRG without having to copy large chunks of memory. 4k banks are the max size you should go with, as the smallest swap size is 4k (NSFs -- I don't know of any actual ROMs which swap any less than 8k).

CHR can be done the same way -- only you should go with 1k or smaller banks (many games have 1k banks -- I think that's the smallest any game swaps -- although I heard rumors that the mapper being used for Grandtheftendo will have 512 byte swapping, so you may want to prepare for it).


This exact same logic can be applied to Nametable mirroring. Simply use 4 pointers for each nametable, and when the game changes mirroring modes, simply change your pointers to accomidate the new mode.

That's what I'd recommend. I'd be happy to answer Qs or clarify if needed.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 10, 2005 10:47 pm 
Offline
User avatar

Joined: Tue Dec 21, 2004 8:35 pm
Posts: 600
Location: Argentina
thxs disch, i have just saved the post for future implementation (dont you get scared if i ask a doubt to you again about this topic) :)

_________________
ANes


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 11, 2005 7:46 pm 
Offline
User avatar

Joined: Tue Dec 21, 2004 8:35 pm
Posts: 600
Location: Argentina
Quote:
// make the pointers
ReadProc ReadMemory[0x10];


how do i define a function pointer type?
i Mean how do i obtain "ReadProc"?


[/code]

_________________
ANes


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 11, 2005 7:46 pm 
Offline
User avatar

Joined: Tue Dec 21, 2004 8:35 pm
Posts: 600
Location: Argentina
Quote:
// make the pointers
ReadProc ReadMemory[0x10];


how do i define a function pointer type?
i Mean how do i obtain "ReadProc"?

_________________
ANes


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 11, 2005 8:41 pm 
Offline
User avatar

Joined: Thu Oct 21, 2004 4:02 pm
Posts: 210
Location: San Diego
Anes wrote:
Quote:
// make the pointers
ReadProc ReadMemory[0x10];


how do i define a function pointer type?
i Mean how do i obtain "ReadProc"?


there is good info at http://function-pointer.org/


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 12, 2005 12:54 am 
You can take out the "REGPARM" bits if you like. It usually is a bit faster than passing variables on the stack, but some C compilers will occassionally produce bad code when optimizations are enabled.

Code:
#ifdef MSVC
#define REGPARM __fastcall
#else
#define REGPARM
#endif

typedef void (REGPARM *WriteProc)(uint32 A, uint8 V);
typedef uint8 (REGPARM *ReadProc)(uint32 A);

WriteProc WriteMemory[0x10];
ReadProc ReadMemory[0x10];

uint8 REGPARM ReadRAM(uint32 A)
{
 return(RAM[A & 0x7FF]);
}

void REGPARM WriteRAM(uint32 A, uint8 V)
{
 RAM[A & 0x7FF] = V;
}

void SomeFunction(void)
{
 ReadMemory[0] = ReadMemory[1] = ReadRAM;
 WriteMemory[0] = WriteMemory[1] = WriteRAM;

 WriteMemory[0x123 >> 8](0x123, 0x45);
 printf("%02x\n", ReadMemory[0x123 >> 8](0x123));
}


Top
  
 
 Post subject:
PostPosted: Fri Aug 12, 2005 12:55 am 
Ack, that should be >> 12, not >> 8. x_x


Top
  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 10 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group