I am working on an emulator, the problem I'm having with mov's or well a lot of things is well I'm attempting to check memory to make sure it's writable and such, which right now(in 8086) is just checking that the segment isn't CS and that the effective address isn't over 0xFFFFF, but well if it is, then what am I suppose to do?
like my code right now for reading memory is just called like MemRead8(segment,offset);
but what do I do if an error happens in MemRead8(), if I was to just call the interrupt, then when it eventually returns to MemRead8() it will return again to the calling function/opcode that called MemRead8() and then it will continue as if nothign had happened, which of course will cause problems
edit:
on the other hand if MemRead8() was to call the interrupt and then return to the instruction loop, it would leave a few values on the stack, which overtime would flood the stack
so does anyone have any ideas?
C: How to do this without flooding the stack
Re: C: How to do this without flooding the stack
Read up on the A20 . Seriously though, if the A20 is disabled, it simply wraps around...hckr83 wrote:I am working on an emulator, the problem I'm having with mov's or well a lot of things is well I'm attempting to check memory to make sure it's writable and such, which right now(in 8086) is just checking that the segment isn't CS and that the effective address isn't over 0xFFFFF, but well if it is, then what am I suppose to do?
like my code right now for reading memory is just called like MemRead8(segment,offset);
but what do I do if an error happens in MemRead8(), if I was to just call the interrupt, then when it eventually returns to MemRead8() it will return again to the calling function/opcode that called MemRead8() and then it will continue as if nothign had happened, which of course will cause problems
edit:
on the other hand if MemRead8() was to call the interrupt and then return to the instruction loop, it would leave a few values on the stack, which overtime would flood the stack
so does anyone have any ideas?
Code: Select all
inline char MemRead8(u16 &Segment, u16 &Offset)
{
u32 Physical;
Physical = Segment;
Physical<<=4;
Physical+=Offset;
Physical&=0xfffff; //Wrap us around
return Memory[Physical];
}
Re: C: How to do this without flooding the stack
Hi,
First, you should have functions to read and write to physical addresses, then another function to convert segment and offset into a physical address (including page table lookup, if paging is enabled). This second function would do access checking and return some sort of status, for example:
At least that's roughly what I'd try....
I'd also have 4 seperate functions for "convertToPhysical()" where the actual function used depends on whether or not paging and/or real mode addressing are enabled (including one for paging and real mode addressing, for virtual 8086 mode). I'd use a pointer to this function to prevent the need to check before calling (so it compiles to "call [currentConvertToPhysicaFunctionAddress]") and then change this function pointer when paging and/or real mode segmentation is enabled or disabled.
BTW for something like "mov ax,[0xFFFF]" I believe it's CPU specific - for 8086 there is no exception, and it's similar to doing "mov al,[0xFFFF]" followed by "mov ah,[0x0000]". For later CPUs (in real mode) you get a general protection fault. I'm not entirely sure though (it's been a long time since I've done anything that isn't for 80386 or later). There's also other differences between 8086 and later CPUs - I remember something about "push sp" storing a different value, but there's probably several other differences.
Also, in real mode there's nothing wrong with something like "mov [cs:0x1234],ax" (it won't generate an exception).
Cheers,
Brendan
First, you should have functions to read and write to physical addresses, then another function to convert segment and offset into a physical address (including page table lookup, if paging is enabled). This second function would do access checking and return some sort of status, for example:
Code: Select all
void someInstruction() {
unsigned int address;
unsigned int errorCode;
int exception;
unsigned char byte;
address = convertToPhysical(&exception, &errorCode, READ_ACCESS, segment, offset, 1);
if(exception >= 0) {
generateException(exception, errorCode);
return;
}
byte = MemRead8(address);
/* More stuff */
}
unsigned char MemRead8(unsigned int address) {
if( !A20_ENABLED() ) {
address = address & 0x0xFFEFFFFF;
}
if( (address >= 0x00100000) && (address < topOfRAM) ) {
return memory[address];
}
if(address < 0x000A0000) {
return memory[address];
}
if(address >= startOfBIOS) {
/* BIOS ROM mapped immediately below 4 GB */
return BIOS_memory[address - startOfBIOS];
}
if( (address >= 0x000A0000) && (address < 0x00100000) ) {
if(address <= 0x000BFFFF) {
if( SMM_ENABLED() ) {
return memory[address];
}
}
if( checkMemoryControllerRegisters(READ_ACCESS, address) == RAM) {
return memory[address];
}
}
return PCIbusRead8(address);
}
unsigned short MemRead16(unsigned int address) {
return MemRead8(address) || (MemRead8(address + 1) << 8);
}
unsigned short MemRead32(unsigned int address) {
if( (address & 3 == 0) && (address >= localAPICbase) && (address < localAPICbase + 0x1000) {
return localAPICread32(address - localAPICbase);
}
return MemRead8(address) || (MemRead8(address + 1) << 8) || (MemRead8(address + 2) << 16) || (MemRead8(address + 3) << 24);
}
void generateException(int exception, unsigned int errorCode) {
switch(exception) {
case GPF_EXCEPTION:
/* Push stuff onto stack */
/* Change CS:EIP to exception handler (from IDT/IVT) */
break;
case PGF_EXCEPTION:
/* Push stuff onto stack */
/* Change CS:EIP to exception handler (from IDT/IVT) */
break;
default:
printf("Unknown exception %d generated\n", exception);
}
}
I'd also have 4 seperate functions for "convertToPhysical()" where the actual function used depends on whether or not paging and/or real mode addressing are enabled (including one for paging and real mode addressing, for virtual 8086 mode). I'd use a pointer to this function to prevent the need to check before calling (so it compiles to "call [currentConvertToPhysicaFunctionAddress]") and then change this function pointer when paging and/or real mode segmentation is enabled or disabled.
BTW for something like "mov ax,[0xFFFF]" I believe it's CPU specific - for 8086 there is no exception, and it's similar to doing "mov al,[0xFFFF]" followed by "mov ah,[0x0000]". For later CPUs (in real mode) you get a general protection fault. I'm not entirely sure though (it's been a long time since I've done anything that isn't for 80386 or later). There's also other differences between 8086 and later CPUs - I remember something about "push sp" storing a different value, but there's probably several other differences.
Also, in real mode there's nothing wrong with something like "mov [cs:0x1234],ax" (it won't generate an exception).
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
ok, well I got a system to do it now, though I have a few questions about just 8086...
ok well basically in my instruction loop I have a "check for interrupts" type function which checks for both irqs and exceptions
but anyway...
if the segment is CS (and writing) or if your trying to write above 0xC0000, does an 8086 just reboot(no exception) or does it do something else?
ok well basically in my instruction loop I have a "check for interrupts" type function which checks for both irqs and exceptions
but anyway...
if the segment is CS (and writing) or if your trying to write above 0xC0000, does an 8086 just reboot(no exception) or does it do something else?
no computer should do either one for that
it will simply try to write to that address
if it contains ROM, then it will either be ignored or (on newer systems) possibly write to the RAM under it (this was a common technique for duplicating parts of ROM into RAM banks on the C-64 -- not sure if any PCs would do it or not)
if there is nothing at that location, it will be written to the RAM located under it on newer systems (on the 8088/86 there was never any RAM under it, so it would just be a write to an unused memory location -- nothing would happen -- hopefully)
if it contains memory mapped I/O ports (on an add-in card), then it will be written to those ports
if it contains RAM (like cache on a hard-disk controller, for example) it will be written there -- with unknown consequences -- which is why its not a good idea to write to areas you dont know for certain contain usable system RAM
it will simply try to write to that address
if it contains ROM, then it will either be ignored or (on newer systems) possibly write to the RAM under it (this was a common technique for duplicating parts of ROM into RAM banks on the C-64 -- not sure if any PCs would do it or not)
if there is nothing at that location, it will be written to the RAM located under it on newer systems (on the 8088/86 there was never any RAM under it, so it would just be a write to an unused memory location -- nothing would happen -- hopefully)
if it contains memory mapped I/O ports (on an add-in card), then it will be written to those ports
if it contains RAM (like cache on a hard-disk controller, for example) it will be written there -- with unknown consequences -- which is why its not a good idea to write to areas you dont know for certain contain usable system RAM