/******************************************************************************
*
* Copyright (C) 2019-2020  Brian Berlin
* 
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************
*
* FILENAME: test_cpu.c
*
* DESCRIPTION:  This contains tests for the cpu module.
*
* AUTHORS:  bberlin
*
* CHANGE LOG
*
*  VER     DATE     CHANGED BY   DESCRIPTION OF CHANGE
* ------  --------  ----------   ------------------------------------
* 0.9.0   12/31/19  bberlin      Creation
******************************************************************************/
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include "../src/core/boom6502.h"
#include "../src/core/memory.h"

extern t_cpu cpu6502;
extern KBYTE memory[0x10000];

KBYTE pc_old = 0;
int failure = 0;
char current_file[1024];

void output_result ( int result, const char *test, const char *value, 
        const char *error ) {

    if ( result ) {
        printf ( "\e[32m[PASS]\e[m %s Result=%s\n", test, value );
    }
    else {
        failure++;
        printf ( "\e[31m[FAIL]\e[m %s %s Result=%s\n", test, error, value );
    }

}

void handle_badop () {
    char result_string[100];

    sprintf ( result_string, "pc = 0x%02x", pc_old );
    output_result ( 0, current_file, result_string, "KIL occured" );
}

/******************************************************************************
**  Function   :  main
**                                                
**  Objective  :  This is the main used to test the toml module
**                                               
**  Parameters :  NONE                          
**                                             
**  return     :  0 if success                
**                -1 if failure               
******************************************************************************/ 
int main ( int argc, char *argv[]  ) {

    int i,j;
    FILE *fp;
    char result_string[256] = "";
    int num_loops = 0;

    if ( argc < 2 ) {
        printf ( "test_cpu - input file required\n" );
        return -1;
    }

    /* Setup memory functions */
    mem_init();
    for ( i = 0x00; i < 0x100; i++ ) {
        cpu6502.read_mem[i] = read_5200_ram;
        cpu6502.write_mem[i] = write_5200_ram;
    }
    cpu6502.read_mem[0x01] = read_5200_stack;
    cpu6502.write_mem[0x01] = write_5200_stack;

    /* Assign any badops or kils to our handler here */
    cpu6502.do_instruct[ 0x02 ] = handle_badop;
    cpu6502.do_instruct[ 0x12 ] = handle_badop;
    cpu6502.do_instruct[ 0x22 ] = handle_badop;
    cpu6502.do_instruct[ 0x32 ] = handle_badop;
    cpu6502.do_instruct[ 0x42 ] = handle_badop;
    cpu6502.do_instruct[ 0x52 ] = handle_badop;
    cpu6502.do_instruct[ 0x62 ] = handle_badop;
    cpu6502.do_instruct[ 0x72 ] = handle_badop;
    cpu6502.do_instruct[ 0x92 ] = handle_badop;
    cpu6502.do_instruct[ 0xb2 ] = handle_badop;
    cpu6502.do_instruct[ 0xd2 ] = handle_badop;
    cpu6502.do_instruct[ 0xf2 ] = handle_badop;

    /*
     * Go through each of the input files and test 
     */
    for ( i = 1; i < argc; ++i ) {
        fp = fopen(argv[i], "rb");
        for ( j = 0; j < 0x10000; ++j ) 
            fread(&memory[j],1,1,fp);
        fclose ( fp );
        sprintf ( current_file, "CPU - %s", argv[i] );

        reset_6502();
        cpu6502.pc = 0x400;

        /* run the cpu */
        int run = 1;
        while ( run ) {

            /* If we hit the start again we are done */
            if ( num_loops && cpu6502.pc == 0x400 )
                break;

            ++num_loops;
            cpu6502.t_reg = rd_6502(cpu6502.pc);

            /* Check loop on program counter for failure */
            if ( cpu6502.pc == pc_old ) {

                /* check for end jmp 0x0400 as next*/
                if ( cpu6502.pc < 0xfffa &&
                        rd_6502(cpu6502.pc+3) == 0x4c && 
                        rd_6502(cpu6502.pc+4) == 0x00 && 
                        rd_6502(cpu6502.pc+5) == 0x04 )
                    break;

                sprintf ( result_string, " pc=0x%04x instr=%d status=%2x", pc_old, num_loops, cpu6502.status );
                output_result ( 0, current_file, result_string, "Instruction error occured" );
                break;
            }

            pc_old = cpu6502.pc;

            cpu6502.pc++;

            printf ( "%02x ", cpu6502.t_reg );
            printf ( "%-12s ", cpu6502.op_name[cpu6502.t_reg] );
            printf ( "status=%02x ", cpu6502.status );
            printf ( "a_reg=%02x ", cpu6502.a_reg );
            printf ( "y_reg=%02x ", cpu6502.y_reg );
            printf ( "pc=%04x \n", cpu6502.pc-1 );
            cpu6502.do_instruct[ cpu6502.t_reg ] ();
        }
    }

    if ( !failure )
        output_result ( 1, current_file, "All Tests Passed", "" );

    return failure;

} /* end main */

