/*
 * Test of 6809 instructions.
 *
 * The strategy is to allocate one kilobyte for a test area, which will
 * allow the code to load data at locations under and over the code itself.
 * There is a small control routine that is set and then calls the test
 * at location 500. It expects the test to return with a RTS instruction.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cputest.h"
#include "framework.h"

void systemf1(const char *command, const char *param) {
  char tmp[strlen(command)+strlen(param)+2];
  sprintf(tmp, command, param);
  //fprintf(stderr, "Executing %s\n", tmp);
  system(tmp);
}


/* Allocate enough memory to run the instructions in that allows
 * for jumps back and forth. Stack pointer is set at top.
 * initregs must follow immediately.
 */

// CODESTRT: ORG 0
// memory: ORG 0 - 99
// ctlmemory: ORG 100 - 1xx

unsigned char memory[MEMSIZE+CTLMEMSIZE+REGSIZE+4]; // 4 for save_s and dpLoc
/* Small code segment to save registers and call test.
 */
unsigned char *ctlmemory /* [100] */ = &memory[MEMSIZE]; /* WARNING! Host byte-sex sensitive */
struct registers *initregs = (struct registers *)&memory[MEMSIZE+CTLMEMSIZE];
char *currtest;


#define CODESTRT 0


/* Store the initial value of the stack pointer. */
unsigned short *save_s = (unsigned short *)&memory[MEMSIZE+CTLMEMSIZE+REGSIZE];
unsigned short *dpLoc  = (unsigned short *)&memory[MEMSIZE+CTLMEMSIZE+REGSIZE+2];

/**
 * Copy the instructions to test.
 * Instructions must begin at CODESTRT
 * The instructions must end with an RTS
 */
void copydata(int start, char *insv, int insc)
{
    register int i;

    for (i = 0; i < insc; i++) {
        memory[start++] = insv[i];
    }
}

void writebyte(int loc, char value)
{
    memory[loc] = value;
}

unsigned setMem(int addr, char value)
{
    memory[addr] = value;
    return (unsigned) addr;
}

void writeword(int loc, unsigned value)
{
  memory[loc++] = Micro(value) >> 8;
  memory[loc]   = Micro(value) & 0xFF;
}

/**
 * Calculate a valid value for the DP register.
 */
static unsigned calcDP(void)
{
    //register unsigned t = 0 /* (unsigned) memory*/;
    //t = (t & 0xff00) + ((t & 0xff) != 0 ? 0x100 : 0x0);
    //return t;
    return 0x0200; // let's always put DP in page 2
}

/*
 * Start location is the argument on the stack.
 */
static int codelen = 0;
void jsrtest(int startloc)
{
  //fprintf(stderr, "Jsr %04x\n", startloc&0xffff);
#ifdef SBT
    // SBT
  int memp;
  FILE *bin, *dat;
  dat = fopen("code/prog.dat", "w");
  fprintf(dat, "code 0000\n");
  fprintf(dat, "label 0000 %s\n", currtest);
  fprintf(dat, "data %04x-03FF\n", codelen);
  fprintf(dat, "code 0400\n");
  fprintf(dat, "label 0400 ENTRYPT\n");
  fprintf(dat, "data 0418-0464\n");
  fclose(dat);
  bin = fopen("code/prog.bin", "wb");
  for (memp = 0; memp < MEMSIZE+CTLMEMSIZE; memp++) {
    fputc(memory[memp], bin);
  }
  fclose(bin);
  systemf1("../6809dasm --sbt code/prog.bin code/prog.dat > code/%s.c", currtest);
#else
#ifdef EMULATOR
    // Emulator
#else
    // Native 6809
#asm
*   ldu 4,s
*   stu save_s,y
    jsr [4,s]
#endasm
#endif
#endif /* SBT */

}

void runcode(char *ctest, char *insv, int insc)
{
    codelen = insc; // length of test code if using SBT.
    copydata(CODESTRT, insv, insc);
    runtest(ctest);
}

void runtest(char *ctest)
{
    currtest = ctest;
#ifdef SBT
    // SBT
#else
#ifdef EMULATOR
    // Emulator
#else
    // Native 6809
#endif
#endif /* SBT */
    jsrtest((char *)ctlmemory-(char *)memory);
}

int setupCtl(void)
{
  int start;

    start = 0;
    
    /* printf("Memory addr: %X\n", memory); */
    *dpLoc = Micro(calcDP());

    /* Push existing register state on normal stack */
    ctlmemory[start++] = 0x34; ctlmemory[start++] = 0x7F; //  0x347F   (PshS all regs?)

    /* Remember SP value */
    ctlmemory[start++] = 0x10; ctlmemory[start++] = 0xFF; //  0x10FF /* STS */

    ctlmemory[start++] = ((unsigned short)((char *)save_s-(char *)&memory[0])) >> 8;
    ctlmemory[start++] = ((unsigned short)((char *)save_s-(char *)&memory[0])) & 0xFF; //  &save_s;

    /* Change SP to initregs */
    ctlmemory[start++] = 0x10; ctlmemory[start++] = 0xCE; //  0x10CE /* LDS */

    ctlmemory[start++] = ((unsigned short)(unsigned int)((char *)initregs-(char *)&memory[0])) >> 8;
    ctlmemory[start++] = ((unsigned short)(unsigned int)((char *)initregs-(char *)&memory[0])) & 0xff; //  &initregs;

    /* Pull all registers */
    ctlmemory[start++] = 0x35; ctlmemory[start++] = 0x7F; //  0x357F /* PulS all regs */

    /* Call subroutine */
    ctlmemory[start++] = 0xBD; /* JSR */

    ctlmemory[start++] = ((unsigned short)(unsigned int)(CODESTRT)) >> 8;
    ctlmemory[start++] = ((unsigned short)(unsigned int)(CODESTRT)) & 0xFF; //  &memory[CODESTRT] (0000)

    /* Push registers back to initregs */
    ctlmemory[start++] = 0x34; ctlmemory[start++] = 0x7F; //  0x347F /* PshS all regs */

    /* Reset SP to normal stack by loading content of save_s */
                                                                                 // NOT ADDRESS OF save_s ??
    ctlmemory[start++] = 0x10; ctlmemory[start++] = 0xFE; //  0x10FE /* LDS */

    // THIS MAY BE WRONG:
    ctlmemory[start++] = ((unsigned short)((char *)save_s-(char *)&memory[0])) >> 8;
    ctlmemory[start++] = ((unsigned short)((char *)save_s-(char *)&memory[0])) & 0xFF; //  &save_s;

    /* Pull all registers */
    ctlmemory[start++] = 0x35; ctlmemory[start++] = 0x7F; //  0x357F /* PulS all regs */
    ctlmemory[start++] = 0x39; /* RTS */

    
    return start;
}

void printMem(int start, int size)
{
    register int i;

    for (i = start; i < start+size; i++) {
      fprintf(stderr, "%02X", memory[i]);
        if (i % 12 == 11) {
          fprintf(stderr, "\n");
        } else {
          fprintf(stderr, ":");
        }
    }
}

// to use: printCtl(CTLMEMSIZE);
void printCtl(int ctlsize)
{
    register int i;

    // should use 'dumpfile' format from my 'hexdump' program...
    
    /* This is all assuming a 16-bit host! Totally fails when host is an emulator on 32- or 64-bit machine */
    //fprintf(stderr, "Save_s addr: %04x\n", (char *)save_s-(char *)&memory[0]);  /* not interesting */
    //  fprintf(stderr, "Save_s content: %04x\n", (save_s[0]&255)<<8 | (save_s[1]&255));
    // fprintf(stderr, "Memory addr: %04x\n", &memory[0]-&memory[0]); /* always 0 */
    fprintf(stderr, "Initregs addr: %04x  size: %d\n", (char *)initregs-(char *)&memory[0], REGSIZE);
    printMem((char *)initregs-(char *)&memory[0], REGSIZE);
    // fprintf(stderr, "Preloaded Stack addr: %04x\n", 0x0464); printMem(0x0464, 10); /* same as initregs */
    fprintf(stderr, "\nCtlMem addr: %04x  size: %d\n", (char *)ctlmemory-(char *)&memory[0], CTLMEMSIZE);

    // updated for portability
    for (i = 0; i < ctlsize; i+=2) {
      fprintf(stderr, "%04X", ((ctlmemory[i]&255)<<8) | (ctlmemory[i+1]&255) );
        if (i % 12 == 11) {
          fprintf(stderr, "\n");
        } else {
          fprintf(stderr, ":");
        }
    }
    fprintf(stderr, "\n");
}

/**
 * Read a value from the direct page.
 */
int readDPloc(int offset)
{
    int loc = Host(*dpLoc) /* - (unsigned) memory */ + offset;
    //fprintf(stderr, "readDPloc(%d): memory[%04x] = 0x%02x\n", offset, loc, memory[loc] & 0xFF);
    return memory[loc] & 0xFF;
}

/**
 * Write a value into the direct page.
 */
void writeDPloc(int offset, unsigned char value)
{
    int loc = Host(*dpLoc) /* - (unsigned) memory */ + offset;
    memory[loc] = value;
    //fprintf(stderr, "writeDPloc(%d,%u): memory[%04x] = 0x%02x\n", offset, value, loc, value);
}