/*
    Memory addressing
    =================

    Written by BERO
    Modified by LDChen
    - Thanks to ROOR for the suggestions.
*/

#include "fpse.h"

// #define SHOW_READ
// #define SHOW_WRITE
// #define ENABLE_AR_EMULATION

void printpc(void);

static int writeok=1;
static UINT8 *memPSXrd[0x10000];
static UINT8 *memPSXwr[0x10000];

static void mem_ctrl(UINT32 data)
{
    int x;

/* キャッシュフラッシュ用？ */
    PRINTF("memctrl %08x\n",(int)data);
    switch(data) {
    case 0x00000800:
    case 0x00000804:
/* キャッシュメモリを 0x0000000? */
        writeok = 0;
        break;
    case 0x0001e988:
/* メインメモリ */
        writeok = 1;
        break;
    default:
        printf("unknown memctrl %08x\n",(int)data);
        break;
    }
    if (writeok) {
        for (x=0; x<0x80; x++)
        {
            memPSXwr[x] = ram + (x & 0x3F) * 0x10000;
            memPSXwr[0xA000 + x] = memPSXwr[x];
            memPSXwr[0x8000 + x] = memPSXwr[x];
        }
    } else {
        for (x=0; x<0x80; x++)
        {
            memPSXwr[x] = NULL;
            memPSXwr[0xA000 + x] = NULL;
            memPSXwr[0x8000 + x] = NULL;
        }
    }
}

static char extrom_init[0x100] = {
        0xb4,0x00,0x00,0x1f,0x4c,0x69,0x63,0x65,0x6e,0x73,0x65,0x64,0x20,0x62,0x79,0x20,
        0x53,0x6f,0x6e,0x79,0x20,0x43,0x6f,0x6d,0x70,0x75,0x74,0x65,0x72,0x20,0x45,0x6e,
        0x74,0x65,0x72,0x74,0x61,0x69,0x6e,0x6d,0x65,0x6e,0x74,0x20,0x49,0x6e,0x63,0x2e,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0xb4,0x00,0x00,0x1f,0x4c,0x69,0x63,0x65,0x6e,0x73,0x65,0x64,0x20,0x62,0x79,0x20,
        0x53,0x6f,0x6e,0x79,0x20,0x43,0x6f,0x6d,0x70,0x75,0x74,0x65,0x72,0x20,0x45,0x6e,
        0x74,0x65,0x72,0x74,0x61,0x69,0x6e,0x6d,0x65,0x6e,0x74,0x20,0x49,0x6e,0x63,0x2e,
        0x00,0x00,0x00,0x00,0x08,0x00,0xe0,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};

void memalloc(void)
{
    int x;

//                  ram      rom  extrom  scratchpad
    ram = malloc(0x200000+0x80000+0x20000+0x10000);
    memset(ram,0,0x200000+0x80000+0x20000+0x10000);

    rom        = ram    + 0x200000;
    extrom     = rom    + 0x80000;
    scratchpad = extrom + 0x20000;

    memset(extrom,0xff,0x20000);
    memcpy(extrom,extrom_init,sizeof(extrom_init));

    for (x=0; x<0x10000; x++)
    {
        if ((x>=0x8000 && x<0x8080) ||
            (x>=0xa000 && x<0xa080) ||
            (x>=0x0000 && x<0x0080))
                memPSXwr[x] = memPSXrd[x] = ram + (x & 0x3F) * 0x10000;
        else
        if ((x>=0xbfc0 && x<0xbfc8) ||
            (x>=0x1fc0 && x<0x1fc8) ||
            (x>=0x9fc0 && x<0x9fc8)) {
                memPSXrd[x] = rom + (x & 0x07) * 0x10000;
                memPSXwr[x] = NULL;
            }
        else
        if (x>=0x1f00 && x<0x1f02)
            memPSXwr[x] = memPSXrd[x] = extrom + (x & 0x01) * 0x10000;
        else memPSXwr[x] = memPSXrd[x] = NULL;
    }

    memPSXwr[0x1F80] = memPSXrd[0x1F80] = scratchpad;
}

void memfree(void)
{
    free(ram);
}

UINT8 read8(UINT32 adr)
{
    UINT8 *Pointer;

    Pointer = memPSXrd[adr >> 16];
    if (Pointer != NULL) {
#ifdef SHOW_READ
        PRINTF("%08x: READ8 [%08x]=%02x\n",PC,adr,Pointer[adr & 0xffff]);
#endif
        if (adr>=0x1f801000 && adr<0x1f808000) return hw_read8(adr);
                                          else return Pointer[adr & 0xFFFF];
    } else {
#ifdef ENABLE_AR_EMULATION
        if (adr>=0x1f000000 && adr<0x1f100000) return hw_romread(adr);
#endif
        printpc();
        printf("can't read8 %08x at %08x\n",(int)adr,(int)PC);
        return -1;
    }
}

UINT16 read16(UINT32 adr)
{
    UINT8 *Pointer;

    Pointer = memPSXrd[adr >> 16];
    if (Pointer != NULL) {
#ifdef SHOW_READ
        PRINTF("%08x: READ16 [%08x]=%04x\n",
               PC,adr,*(UINT16 *)(Pointer + (adr & 0xFFFF));
#endif
        if (adr>=0x1f801000 && adr<0x1f808000) return hw_read16(adr);
        else return SWAP16(*(UINT16 *)(Pointer + (adr & 0xFFFF)));
    } else {
        printpc();
        printf("can't read16 %08x at %08x\n",(int)adr,(int)PC);
        return -1;
    }
}

UINT32 read32(UINT32 adr)
{
    UINT8 *Pointer;

    Pointer = memPSXrd[adr >> 16];
    if (Pointer != NULL) {
#ifdef SHOW_READ
        PRINTF("%08x: READ32 [%08x]=%08x\n",
               PC,adr,*(UINT32 *)(Pointer + (adr & 0xFFFF));
#endif
        if (adr>=0x1f801000 && adr<0x1f808000) return hw_read32(adr);
        else return SWAP32(*(UINT32 *)(Pointer + (adr & 0xFFFF)));
    } else {
        printpc();
        printf("can't read32 %08x at %08x\n",(int)adr,(int)PC);
        return -1;
    }
}

void write8(UINT32 adr,UINT32 data)
{
    UINT8 *Pointer;

    Pointer = memPSXwr[adr >> 16];
    if (Pointer != NULL) {
#ifdef SHOW_WRITE
        PRINTF("%08x: WRITE8 [%08x]=%02x\n",PC,adr,data);
#endif
        Pointer[adr & 0xFFFF] = (UINT8)(data);
//        CompileFlush(adr,adr);
        if (adr>=0x1f801000 && adr<0x1f808000) hw_write8(adr,data);
    } else {
#ifdef ENABLE_AR_EMULATION
        if (adr>=0x1f000000 && adr<0x1f100000) hw_romwrite(adr,data);
        else
#endif
        {
            printpc();
            printf("can't write8 %08x at %08x\n",(int)adr,(int)PC);
        }
    }
}

void write16(UINT32 adr,UINT32 data)
{
    UINT8 *Pointer;

    Pointer = memPSXwr[adr >> 16];
    if (Pointer != NULL) {
#ifdef SHOW_WRITE
        PRINTF("%08x: WRITE16 [%08x]=%04x\n",PC,adr,data);
#endif
//        CompileFlush(adr,adr);
        *(UINT16 *)(Pointer + (adr & 0xFFFF)) = SWAP16((UINT16)(data));
        if (adr>=0x1f801000 && adr<0x1f808000) hw_write16(adr,data);
    } else {
        printpc();
        printf("can't write16 %08x at %08x\n",(int)adr,(int)PC);
    }
}

extern int writebreak;

void write32(UINT32 adr,UINT32 data)
{
    UINT8 *Pointer;

    Pointer = memPSXwr[adr >> 16];
    if (Pointer != NULL) {
#ifdef SHOW_WRITE
        PRINTF("%08x: WRITE32 [%08x]=%08x\n",PC,adr,data);
#endif
//        CompileFlush(adr,adr);
        *(UINT32 *)(Pointer + (adr & 0xFFFF)) = SWAP32(data);
        if (adr>=0x1f801000 && adr<0x1f808000) hw_write32(adr,data);
    } else {
        if (adr==0xfffe0130) mem_ctrl(data);
        else if (writeok) {
            printpc();
            printf("can't write32 %08x at %08x\n",(int)adr,(int)PC);
        }
    }
}

void *baseaddr(UINT32 adr)
{
    if ((adr>=0x80000000 && adr<0x80800000) ||
        (adr>=0xa0000000 && adr<0xa0800000) ||
        (adr>=0x00000000 && adr<0x00800000)) return ram - (adr&~0x1fffff);

    if ((adr>=0xbfc00000 && adr<0xbfc80000) ||
        (adr>=0x1fc00000 && adr<0x1fc80000) ||
        (adr>=0x9fc00000 && adr<0x9fc80000)) return rom - (adr & ~0x7FFFF);

    else if (adr>=0x1f800000 && adr<0x1f801000) return scratchpad - 0x1f800000;
    else if (adr>=0x1f000000 && adr<0x1f020000) return extrom - 0x1f000000;
    else { FPSE_Flags |= STOP;
               printpc();
               printf("can't exec %08x\n",(int)adr);
    }
    return NULL;
}

#if 0
void *baseaddr2(UINT32 adr)
{
    if ((adr>=0x80000000 && adr<0x80800000) ||
        (adr>=0xa0000000 && adr<0xa0800000) ||
        (adr>=0x00000000 && adr<0x00800000)) return ram - (adr&~0x1fffff);

    if ((adr>=0xbfc00000 && adr<0xbfc80000) ||
        (adr>=0x1fc00000 && adr<0x1fc80000) ||
        (adr>=0x9fc00000 && adr<0x9fc80000)) return rom - (adr & ~0x7FFFF);

    else if (adr>=0x1f800000 && adr<0x1f801000) return scratchpad - 0x1f800000;
    return NULL;
}
#endif