C: How to do this without flooding the stack

Programming, for all ages and all languages.
Post Reply
earlz
Member
Member
Posts: 1546
Joined: Thu Jul 07, 2005 11:00 pm
Contact:

C: How to do this without flooding the stack

Post by earlz »

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?
Ready4Dis
Member
Member
Posts: 571
Joined: Sat Nov 18, 2006 9:11 am

Re: C: How to do this without flooding the stack

Post by Ready4Dis »

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?
Read up on the A20 ;). Seriously though, if the A20 is disabled, it simply wraps around...

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];
}
This would suffice, unless A20 was enabled... then it's a valid address, so either way there is no actual problem.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: C: How to do this without flooding the stack

Post by Brendan »

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:

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);
    }
}
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
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.
earlz
Member
Member
Posts: 1546
Joined: Thu Jul 07, 2005 11:00 pm
Contact:

Post by earlz »

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?
User avatar
JAAman
Member
Member
Posts: 879
Joined: Wed Oct 27, 2004 11:00 pm
Location: WA

Post by JAAman »

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
Post Reply