//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
//
//  Disasm - A KA ECU disassembly aid
//           Copyright 2005 by Eric Honsch
//           Use this any damn way you want but leave my copyright notice intact.
//
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------

// SVX version

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <ctype.h>
#include <process.h>
#include <io.h>

#include <list>
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;

struct PeripheralRegister
{
    PeripheralRegister(unsigned add, char *n, char *abb)
    {
        address = add;
        strncpy(name, n, 63);
        strncpy(abbreviation, abb, 15);
    }
    unsigned address;
    char     name[64];
    char     abbreviation[16];
};


std::vector<PeripheralRegister> Registers;

char *InterruptVectors[22] = { "A/D interrupt control",		//22
                               "UART0 tx interrupt ",		//21
                               "UART0 rx interrupt ",		//20
                               "UART1 tx interrupt ",		//19
                               "UART1 rx interrupt ",		//18
                               "Timer D interrupt ",		//17
                               "PWM1 interrupt ",			//16
                               "Timer B1 interrupt ",		//15
                               "TG2 interrupt ",			//14
                               "TG1 interrupt ",			//13
                               "Timer B4 interrupt",		//12
                               "Timer B3 interrupt",		//11
                               "Timer C2 overflow",			//10
                               "Timer C1 overflow",			//9
                               "Timer C2 strobe interrupt",	//8
                               "Timer C1 strobe interrupt",	//7
                               "External INT interrupt",	//6
                               "Watchdog timer",			//5
                               "Debug (unused?)",			//4
                               "BRK instruction",			//3
                               "Divide by zero",			//2
                               "Reset vector"				//1 
                              };

//----------------------------------------------------------------------------------
// I love cut and paste
void InitRegisters(void)
{
    Registers.push_back(PeripheralRegister(0x08, "Port 0", "P0"));    
	Registers.push_back(PeripheralRegister(0x09, "Port 1", "P1"));
    Registers.push_back(PeripheralRegister(0x0A, "Port 2", "P2"));
	Registers.push_back(PeripheralRegister(0x0B, "Port 3", "P3"));    
    Registers.push_back(PeripheralRegister(0x0C, "Port 4", "P4"));    
    Registers.push_back(PeripheralRegister(0x0D, "Port 5", "P5"));    
    Registers.push_back(PeripheralRegister(0x0E, "Port 6", "P6"));    
    Registers.push_back(PeripheralRegister(0x0F, "Port 7", "P7"));    
    Registers.push_back(PeripheralRegister(0x10, "Port 0 direction register", "P0D"));    
    Registers.push_back(PeripheralRegister(0x11, "Port 1 direction register", "P1D"));    
    Registers.push_back(PeripheralRegister(0x12, "Port 2 direction register", "P2D"));     
	Registers.push_back(PeripheralRegister(0x13, "Port 3 direction register", "P3D"));    
    Registers.push_back(PeripheralRegister(0x14, "Port 4 direction register", "P4D"));    
    Registers.push_back(PeripheralRegister(0x15, "Port 5 direction register", "P5D"));    
    Registers.push_back(PeripheralRegister(0x16, "Port 6 direction register", "P6D"));    
    Registers.push_back(PeripheralRegister(0x17, "Port 7 direction register", "P7D"));    
    Registers.push_back(PeripheralRegister(0x1C, "Port 4 operation mode register", "P4OM"));    
    Registers.push_back(PeripheralRegister(0x1D, "Port 5 operation mode register", "P5OM"));    
    Registers.push_back(PeripheralRegister(0x1E, "Port 6 operation mode register", "P6OM"));    
    Registers.push_back(PeripheralRegister(0x1F, "Port 7 operation mode register", "P7OM"));    
    Registers.push_back(PeripheralRegister(0x20, "A/D control register", "ADCON"));    
    Registers.push_back(PeripheralRegister(0x22, "A/D successive approximation register", "ADVAL"));    
    Registers.push_back(PeripheralRegister(0x30, "UART0 tx/rx mode register", "S0MR"));    
    Registers.push_back(PeripheralRegister(0x31, "Baud rate generator", "S0BRG"));    
    Registers.push_back(PeripheralRegister(0x32, "Transmit buffer", "S0TB"));    
    Registers.push_back(PeripheralRegister(0x32, "Transmit buffer low byte", "S0TBL"));    
    Registers.push_back(PeripheralRegister(0x33, "Transmit buffer high byte", "S0TBH"));    
    Registers.push_back(PeripheralRegister(0x34, "Control register", "S0C"));    
    Registers.push_back(PeripheralRegister(0x34, "Control register low byte", "S0CL"));    
    Registers.push_back(PeripheralRegister(0x35, "Control register high byte", "S0CH"));    
    Registers.push_back(PeripheralRegister(0x36, "Receive buffer", "S0RB"));    
    Registers.push_back(PeripheralRegister(0x36, "Receive buffer low byte", "S0RBL"));    
    Registers.push_back(PeripheralRegister(0x37, "Receive buffer high byte", "S0RBH")); 
    Registers.push_back(PeripheralRegister(0x38, "UART1 tx/rx mode register", "S1MR"));    
    Registers.push_back(PeripheralRegister(0x39, "Baud rate generator", "S1BRG"));    
    Registers.push_back(PeripheralRegister(0x3A, "Transmit buffer", "S1TB"));    
    Registers.push_back(PeripheralRegister(0x3A, "Transmit buffer low byte", "S1TBL"));    
    Registers.push_back(PeripheralRegister(0x3B, "Transmit buffer high byte", "S1TBH"));    
    Registers.push_back(PeripheralRegister(0x3C, "Control register", "S1C"));    
    Registers.push_back(PeripheralRegister(0x3C, "Control register low byte", "S1CL"));    
    Registers.push_back(PeripheralRegister(0x3D, "Control register high byte", "S1CH"));    
    Registers.push_back(PeripheralRegister(0x3E, "Receive buffer", "S1RB"));    
    Registers.push_back(PeripheralRegister(0x3E, "Receive buffer low byte", "S1RBL"));    
    Registers.push_back(PeripheralRegister(0x3F, "Receive buffer high byte", "S1RBH"));
    Registers.push_back(PeripheralRegister(0x40, "Timer A1 counter", "TA1CNT"));    
    Registers.push_back(PeripheralRegister(0x44, "Timer A2 counter", "TA2CNT"));    
    Registers.push_back(PeripheralRegister(0x48, "Timer A3 counter", "TA3CNT"));    
    Registers.push_back(PeripheralRegister(0x50, "Timer A4 counter", "TA4CNT"));    
    Registers.push_back(PeripheralRegister(0x54, "Timer A5 counter", "TA5CNT"));    
    Registers.push_back(PeripheralRegister(0x58, "Timer A6 counter", "TA6CNT"));    
    Registers.push_back(PeripheralRegister(0x40, "Timer A1 counter low byte", "TA1CNTL"));    
    Registers.push_back(PeripheralRegister(0x41, "Timer A1 counter high byte", "TA1CNTH"));    
    Registers.push_back(PeripheralRegister(0x44, "Timer A2 counter low byte", "TA2CNTL"));    
    Registers.push_back(PeripheralRegister(0x45, "Timer A2 counter high byte", "TA2CNTH"));    
    Registers.push_back(PeripheralRegister(0x48, "Timer A3 counter low byte", "TA3CNTL"));    
    Registers.push_back(PeripheralRegister(0x49, "Timer A3 counter high byte", "TA3CNTH"));    
    Registers.push_back(PeripheralRegister(0x50, "Timer A4 counter low byte", "TA4CNTL"));    
    Registers.push_back(PeripheralRegister(0x51, "Timer A4 counter high byte", "TA4CNTH"));    
    Registers.push_back(PeripheralRegister(0x54, "Timer A5 counter low byte", "TA5CNTL"));    
    Registers.push_back(PeripheralRegister(0x55, "Timer A5 counter high byte", "TA5CNTH"));    
    Registers.push_back(PeripheralRegister(0x58, "Timer A6 counter low byte", "TA6CNTL"));    
    Registers.push_back(PeripheralRegister(0x59, "Timer A6 counter high byte", "TA6CNTH"));    
    Registers.push_back(PeripheralRegister(0x42, "Timer A1 reload register", "TA1LOAD"));    
    Registers.push_back(PeripheralRegister(0x46, "Timer A2 reload register", "TA2LOAD"));    
    Registers.push_back(PeripheralRegister(0x4A, "Timer A3 reload register", "TA3LOAD"));    
    Registers.push_back(PeripheralRegister(0x52, "Timer A4 reload register", "TA4LOAD"));    
    Registers.push_back(PeripheralRegister(0x56, "Timer A5 reload register", "TA5LOAD"));    
    Registers.push_back(PeripheralRegister(0x5A, "Timer A6 reload register", "TA6LOAD"));        
    Registers.push_back(PeripheralRegister(0x42, "Timer A1 reload register low byte", "TA1LOADL"));    
    Registers.push_back(PeripheralRegister(0x43, "Timer A1 reload register high byte", "TA1LOADH"));    
    Registers.push_back(PeripheralRegister(0x46, "Timer A2 reload register low byte", "TA2LOADL"));    
    Registers.push_back(PeripheralRegister(0x47, "Timer A2 reload register high byte", "TA2LOADH"));    
    Registers.push_back(PeripheralRegister(0x4A, "Timer A3 reload register low byte", "TA3LOADL"));    
    Registers.push_back(PeripheralRegister(0x4B, "Timer A3 reload register high byte", "TA3LOADH"));    
    Registers.push_back(PeripheralRegister(0x52, "Timer A4 reload register low byte", "TA4LOADL"));    
    Registers.push_back(PeripheralRegister(0x53, "Timer A4 reload register high byte", "TA4LOADH"));    
    Registers.push_back(PeripheralRegister(0x56, "Timer A5 reload register low byte", "TA5LOADL"));    
    Registers.push_back(PeripheralRegister(0x57, "Timer A5 reload register high byte", "TA5LOADH"));    
    Registers.push_back(PeripheralRegister(0x5A, "Timer A6 reload register low byte", "TA6LOADL"));    
    Registers.push_back(PeripheralRegister(0x5B, "Timer A6 reload register high byte", "TA6LOADH"));    
    Registers.push_back(PeripheralRegister(0x60, "TG1 prescaler", "TG1"));    
    Registers.push_back(PeripheralRegister(0x61, "TG2 prescaler", "TG2"));    
    Registers.push_back(PeripheralRegister(0x62, "Timer A enable and protect", "TAENPR"));    
    Registers.push_back(PeripheralRegister(0x62, "Timer A enable", "TAEN"));    
    Registers.push_back(PeripheralRegister(0x63, "Timer A protect", "TAPR"));    
    Registers.push_back(PeripheralRegister(0x64, "Timer A control register EC/W", "TACW"));    
    Registers.push_back(PeripheralRegister(0x65, "Timer A control register EP/N", "TAPN"));    
    Registers.push_back(PeripheralRegister(0x66, "Timer A interrupt mask", "TAIM"));    
    Registers.push_back(PeripheralRegister(0x67, "Timer A interrupt status", "TAIS"));    
    Registers.push_back(PeripheralRegister(0x68, "Timer A1 PISO register", "TA1PISO"));    
    Registers.push_back(PeripheralRegister(0x69, "Timer A2 PISO register", "TA2PISO"));    
    Registers.push_back(PeripheralRegister(0x6A, "Timer A3 PISO register", "TA3PISO"));    
    Registers.push_back(PeripheralRegister(0x6B, "Timer A4 PISO register", "TA4PISO"));    
    Registers.push_back(PeripheralRegister(0x6C, "Timer A5 PISO register", "TA5PISO"));    
    Registers.push_back(PeripheralRegister(0x6D, "Timer A6 PISO register", "TA6PISO"));    
    Registers.push_back(PeripheralRegister(0x80, "Timer B1 counter", "TB1CNT"));    
    Registers.push_back(PeripheralRegister(0x84, "Timer B2 counter", "TB2CNT"));    
    Registers.push_back(PeripheralRegister(0x88, "Timer B3 counter", "TB3CNT"));    
    Registers.push_back(PeripheralRegister(0x8C, "Timer B4 counter", "TB4CNT"));    
    Registers.push_back(PeripheralRegister(0x80, "Timer B1 counter low byte", "TB1CNTL"));    
    Registers.push_back(PeripheralRegister(0x81, "Timer B1 counter high byte", "TB1CNTH"));    
    Registers.push_back(PeripheralRegister(0x84, "Timer B2 counter low byte", "TB2CNTL"));    
    Registers.push_back(PeripheralRegister(0x85, "Timer B2 counter high byte", "TB2CNTH"));    
    Registers.push_back(PeripheralRegister(0x88, "Timer B3 counter low byte", "TB3CNTL"));    
    Registers.push_back(PeripheralRegister(0x89, "Timer B3 counter high byte", "TB3CNTH"));    
    Registers.push_back(PeripheralRegister(0x8C, "Timer B4 counter low byte", "TB4CNTL"));    
    Registers.push_back(PeripheralRegister(0x8D, "Timer B4 counter high byte", "TB4CNTH"));    
    Registers.push_back(PeripheralRegister(0x82, "Timer B1 reload register", "TB1LOAD"));    
    Registers.push_back(PeripheralRegister(0x86, "Timer B2 reload register", "TB2LOAD"));    
    Registers.push_back(PeripheralRegister(0x8A, "Timer B3 reload register", "TB3LOAD"));    
    Registers.push_back(PeripheralRegister(0x8E, "Timer B4 reload register", "TB4LOAD"));    
    Registers.push_back(PeripheralRegister(0x82, "Timer B1 reload register low byte", "TB1LOADL"));    
    Registers.push_back(PeripheralRegister(0x83, "Timer B1 reload register high byte", "TB1LOADH"));    
    Registers.push_back(PeripheralRegister(0x86, "Timer B2 reload register low byte", "TB2LOADL"));    
    Registers.push_back(PeripheralRegister(0x87, "Timer B2 reload register high byte", "TB2LOADH"));    
    Registers.push_back(PeripheralRegister(0x8A, "Timer B3 reload register low byte", "TB3LOADL"));    
    Registers.push_back(PeripheralRegister(0x8B, "Timer B3 reload register high byte", "TB3LOADH"));    
    Registers.push_back(PeripheralRegister(0x8E, "Timer B4 reload register low byte", "TB4LOADL"));    
    Registers.push_back(PeripheralRegister(0x8F, "Timer B4 reload register high byte", "TB4LOADH"));    
    Registers.push_back(PeripheralRegister(0x90, "Timer B prescaler", "TBSC"));    
    Registers.push_back(PeripheralRegister(0x92, "Timer B control register enable", "TBEN"));    
    Registers.push_back(PeripheralRegister(0x94, "Timer B operation control", "TBCTL"));    
    Registers.push_back(PeripheralRegister(0x96, "Timer B1 PISO register", "TB1PISO"));    
    Registers.push_back(PeripheralRegister(0x98, "Timer B2 PISO register", "TB2PISO"));    
    Registers.push_back(PeripheralRegister(0xA0, "Timer C1 counter", "TC1CNT"));    
    Registers.push_back(PeripheralRegister(0xA4, "Timer C2 counter", "TC2CNT"));    
    Registers.push_back(PeripheralRegister(0xA8, "Timer C3 counter", "TC3CNT"));    
    Registers.push_back(PeripheralRegister(0xA0, "Timer C1 counter low byte", "TC1CNTL"));    
    Registers.push_back(PeripheralRegister(0xA1, "Timer C1 counter high byte", "TC1CNTH"));    
    Registers.push_back(PeripheralRegister(0xA4, "Timer C2 counter low byte", "TC2CNTL"));    
    Registers.push_back(PeripheralRegister(0xA5, "Timer C2 counter high byte", "TC2CNTH"));    
    Registers.push_back(PeripheralRegister(0xB0, "Timer C1 prescaler", "TC1SC"));    
    Registers.push_back(PeripheralRegister(0xB1, "Timer C2 prescaler", "TC2SC"));    
    Registers.push_back(PeripheralRegister(0xB4, "Timer C1 control register", "TC1CTRL"));    
    Registers.push_back(PeripheralRegister(0xB5, "Timer C2 control register", "TC2CTRL"));    
    Registers.push_back(PeripheralRegister(0x70, "Timer D counter", "TDCNT"));    
    Registers.push_back(PeripheralRegister(0x70, "Timer D counter low byte", "TDCNTL"));    
    Registers.push_back(PeripheralRegister(0x71, "Timer D counter high byte", "TDCNH"));    
    Registers.push_back(PeripheralRegister(0x72, "Timer D reload register", "TDLOAD"));    
    Registers.push_back(PeripheralRegister(0x72, "Timer D reload register low byte", "TDLOADL"));    
    Registers.push_back(PeripheralRegister(0x73, "Timer D reload register high byte", "TDLOADH"));    
    Registers.push_back(PeripheralRegister(0x74, "Timer D control register", "TDCTRL"));    
    Registers.push_back(PeripheralRegister(0xC0, "PWM1 counter", "PWM1CNT"));
    Registers.push_back(PeripheralRegister(0xC0, "PWM1 counter low byte", "PWM1CNTL"));
    Registers.push_back(PeripheralRegister(0xC1, "PWM1 counter high byte", "PWM1CNTH"));
    Registers.push_back(PeripheralRegister(0xC2, "PWM1 operation control register", "PWM1CTRL"));    
    Registers.push_back(PeripheralRegister(0xC4, "PWM2 counter", "PWM2CNT"));
    Registers.push_back(PeripheralRegister(0xC4, "PWM2 counter low byte", "PWM2CNTL"));
    Registers.push_back(PeripheralRegister(0xC5, "PWM2 counter high byte", "PWM2CNTH"));
    Registers.push_back(PeripheralRegister(0xC6, "PWM2 operation control register", "PWM2CTRL"));    
    Registers.push_back(PeripheralRegister(0xD0, "Watchdog timer", "WDT"));    
    Registers.push_back(PeripheralRegister(0xD1, "Watchdog timer frequency select flag", "WDC"));    
    Registers.push_back(PeripheralRegister(0xD8, "Processor Operation Control register", "CPUCTRL"));    
    Registers.push_back(PeripheralRegister(0xEF, "A/D interrupt control register", "ADIC"));    
    Registers.push_back(PeripheralRegister(0xF0, "UART0 tx interrupt control register", "STIC"));    
    Registers.push_back(PeripheralRegister(0xF1, "UART0 rx interrupt control register", "SRIC"));
    Registers.push_back(PeripheralRegister(0xF2, "UART1 tx interrupt control register", "STIC"));    
    Registers.push_back(PeripheralRegister(0xF3, "UART1 rx interrupt control register", "SRIC"));
    Registers.push_back(PeripheralRegister(0xF4, "Timer D interrupt control register", "TDIC"));    
    Registers.push_back(PeripheralRegister(0xF5, "PWM1 interrupt control register", "PWM1IC"));    
    Registers.push_back(PeripheralRegister(0xF6, "Timer B1 interrupt control register", "TB1IC"));    
    Registers.push_back(PeripheralRegister(0xF7, "TG2 interrupt control register", "TG2IC"));    
    Registers.push_back(PeripheralRegister(0xF8, "TG1 interrupt control register", "TG1IC"));    
    Registers.push_back(PeripheralRegister(0xF9, "Timer B4 interrupt control register", "TB4IC"));    
    Registers.push_back(PeripheralRegister(0xFA, "Timer B3 interrupt control register", "TB3IC"));    
    Registers.push_back(PeripheralRegister(0xFB, "Timer C2 overflow interrupt control register", "TC2OIC"));    
    Registers.push_back(PeripheralRegister(0xFC, "Timer C1 overflow interrupt control register", "TC1OIC"));    
    Registers.push_back(PeripheralRegister(0xFD, "Timer C2 strobe interrupt control register", "TC2SIC"));    
    Registers.push_back(PeripheralRegister(0xFE, "Timer C1 strobe interrupt control register", "TC1SIC"));    
    Registers.push_back(PeripheralRegister(0xFF, "INT interrupt control register", "INTIC"));    
    
	// Example of added memory location
	//Registers.push_back(PeripheralRegister(0x41D2, "Example memory location","----"));
	
	//Registers.push_back(PeripheralRegister(0x4800, "Versatile ROM Port A", "VR:PORTA"));    
    //Registers.push_back(PeripheralRegister(0x4802, "Versatile ROM Port B", "VR:PORTB"));    
    //Registers.push_back(PeripheralRegister(0x4804, "Versatile ROM Port C Dir", "VR:PORTCDIR"));    
    //Registers.push_back(PeripheralRegister(0x4806, "Versatile ROM Port C", "VR:PORTC"));    
    //Registers.push_back(PeripheralRegister(0x4808, "Versatile ROM Prescaler overflow", "VR:PSCOV"));
    //Registers.push_back(PeripheralRegister(0x480A, "Versatile ROM Counter overflow", "VR:CNTOV"));
    //Registers.push_back(PeripheralRegister(0x480C, "Versatile ROM Counter mode", "VR:CNTMODE"));
    //Registers.push_back(PeripheralRegister(0x480E, "Versatile ROM Port D", "VR:PORTD"));

	// Example of added ROM location
	//Registers.push_back(PeripheralRegister(0x8240, "Example ROM location","----"));

	
	// Read from file 'memory.txt' known locations
	string string_memory,line;
	unsigned known_memory;
	string known_description;
	

	ifstream memory ("memory.txt");
	if (memory.is_open())
	{
		while (! memory.eof() )
		{
			getline(memory,line);
			stringstream(line.substr(0,6)) >> std::hex >> known_memory;
			known_description = line.substr(7,50);
			const char *str = known_description.c_str();
			char str2[50];
			strcpy (str2, str);
			Registers.push_back(PeripheralRegister(known_memory, str2, "----"));
		}
		memory.close();
	}	
    
}


//----------------------------------------------------------------------------------
PeripheralRegister *FindRegister(unsigned address)
{
    //if ((address < 0xFF) || ((address >= 0x4800) && (address < 0x4810)))
    //if (address < 0xFF)
	//{
        unsigned a;
        for (a = 0; a < Registers.size(); a++)
        {
            if (Registers[a].address == address)
            {
                return &(Registers[a]);
            }
        }
    //}
    return NULL;
}


//-----------------------------------------------------------------------------------
unsigned HexToUnsigned(char *hex)
{
    if (hex == NULL) return 0;

    size_t pos = 0;
    size_t len = strlen(hex);

    unsigned retval = 0;

    while (pos < len)
    {
        char c = toupper(hex[pos]);
        ++pos;

        if (isdigit(c))
        {
            retval <<= 4;
            retval += (c - '0');
        }
        else if (isalpha(c) && ((c - 'A') < 6))
        {
            retval <<= 4;
            retval += (10 + (c - 'A'));
        }
    }

    return retval;
}


//-----------------------------------------------------------------------------------
class AsmLine
{
  public:

    AsmLine(void);
    ~AsmLine();

    bool Init(char *initString, bool m, bool x, unsigned addressOffest);

    unsigned Address(void)  { return mAddress; }
    unsigned NumBytes(void) { return mNumBytes; }
    unsigned GetData(unsigned char *dest);  // returns length
    
    void AddComment(char *comment); // duplicates string
    char *Print(void);              // returns a string, you must delete it

    // returns true if this imstruction modifies the M bit in the status register
    // puts the new value into newValue 
    bool SetsM(bool &newValue);
    // Same as above except for X bit
    bool SetsX(bool &newValue);

    bool GetM(void) { return mMBit; }
    bool GetX(void) { return mXBit; }

    enum BranchType { Branch, Jump, Call, Return };
    enum AccessType { Immediate, Direct, DirectIndexedX, DirectIndexedY,     
                      DirectIndirect, DirectIndexedXIndirect, DirectIndirectIndexedY, DirectIndirectLong,
                      DirectIndirectLongIndexedY, Absolute, AbsoluteIndexedX, AbsoluteIndexedY,
                      AbsoluteLong, AbsoluteLongIndexedX, AbsoluteIndirect, AbsoluteIndexedXIndirect,
                      Stack, StackRelative, StackRelativeIndirectIndexedY };




    bool SetsPC(unsigned &destAddress, bool &conditional, BranchType &type);

    bool ReadsMemory(unsigned &destAddress, AccessType &type, unsigned &value);
    bool SetsMemory(unsigned &destAddress, AccessType &type, unsigned &value);

  protected:

    char           *mRawAsm;
    unsigned        mAddress;
    unsigned        mNumBytes;
    unsigned char   mData[16];
    char            mInstruction[64];
    char           *mComments;
    bool            mMBit;
    bool            mXBit;
};


//-----------------------------------------------------------------------------
AsmLine::AsmLine(void) :
    mRawAsm(NULL),
    mAddress(0),
    mNumBytes(0),
    mComments(NULL),
    mMBit(false),
    mXBit(false)
{
    mData[0] = 0;
    mInstruction[0] = 0;
}


//-----------------------------------------------------------------------------
AsmLine::~AsmLine()
{
    delete[] mRawAsm;
    delete[] mComments;
    mRawAsm = NULL;
    mComments = NULL;
}

//-----------------------------------------------------------------------------
bool AsmLine::Init(char *initString, bool m, bool x, unsigned addressOffset)
{
    if (mRawAsm != NULL) return false;
    if (initString == NULL) return false;

    mMBit = m;
    mXBit = x;

    size_t len = strlen(initString);
    if (len > 511) return false;
    if (len < 8)   return false;

    mRawAsm = new char [len + 1];
    strcpy(mRawAsm, initString);

    char temp[512];
    strcpy(temp, mRawAsm);

    temp[6] = 0;
    mAddress = HexToUnsigned(temp) + addressOffset;

    unsigned pos = 7;
    mNumBytes = 0;
    while (temp[pos] != ' ')
    {
        char foo[3];
        foo[0] = temp[pos];
        foo[1] = temp[pos + 1];
        foo[2] = 0;
        mData[mNumBytes] = HexToUnsigned(foo);
        ++mNumBytes;
        pos += 2;
    }
    
    while (temp[pos] == ' ') ++pos;
    strcpy(mInstruction, &temp[pos]);

    // fixup branch dest address strings
    unsigned nextAddress;
    bool isConditional;
    BranchType type;
    bool isBranch = SetsPC(nextAddress, isConditional, type);
    if (isBranch && (type == Branch))
    {
        char goo[16];
        sprintf(goo, "%04x", nextAddress - addressOffset);
        char *dest = strstr(mInstruction, goo);
        sprintf(goo, "%04x", nextAddress);
        if (dest != NULL)
        {
            dest[0] = goo[0];
            dest[1] = goo[1];
            dest[2] = goo[2];
            dest[3] = goo[3];
        }
    }


    return true;
}

//-----------------------------------------------------------------------------
unsigned AsmLine::GetData(unsigned char *dest)
{
    memcpy(dest, mData, mNumBytes);
    return mNumBytes;
}
    
//-----------------------------------------------------------------------------
void AsmLine::AddComment(char *comment)
{
    if (comment == NULL) return;

    if (mComments == NULL)
    {
        mComments = new char[strlen(comment) + 1];
        strcpy(mComments, comment);
        return;
    }

    size_t newLen = strlen(mComments) + strlen(comment) + 3;
    char *newComments = new char[newLen];
    strcpy(newComments, mComments);
    strcat(newComments, ", ");
    strcat(newComments, comment);
    delete[] mComments;
    mComments = newComments;
}


//-----------------------------------------------------------------------------
char *AsmLine::Print(void)
{
    const int instructionColumn = 24;
    const int commentColumn = 64;
    size_t len = instructionColumn + strlen(mInstruction);
    if (mComments != NULL)
    {
        if (len <= commentColumn) len = 64;
        len += strlen(mComments) + 2;
    }
    char *line = new char[len + 1];
    sprintf(line, "%06X    ", mAddress);
    
    unsigned a;
    for (a = 0; a < mNumBytes; a++)
    {
        sprintf(&line[(a * 2) + 10], "%02X", mData[a]);
    }
    
    len = strlen(line);
    while (len < instructionColumn)
    {
        line[len++] = ' ';
    }
    line[len] = 0;

    strcat(line, mInstruction);

    if (mComments != NULL)
    {
        len = strlen(line);
        while (len < commentColumn)
        {
            line[len++] = ' ';
        }
        line[len] = 0;
        strcat(line, "; ");
        strcat(line, mComments);
    }
    return line;
}

//-----------------------------------------------------------------------------
// returns true if this imstruction modifies the M bit in the status register
// puts the new value into newValue 
bool AsmLine::SetsM(bool &newValue)
{
    if (mNumBytes == 1)
    {
        if (mData[0] == 0xD8) // CLM
        {
            newValue = false;
            return true;
        }
        
        if (mData[0] == 0xF8) // SEM
        {
            newValue = true;
            return true;
        }
    }

    if (mNumBytes == 2)
    {
        if ((mData[0] == 0xC2) && ((mData[1] & 0x20) != 0)) // CLP and M bit is set
        {
            newValue = false;
            return true;
        }
        if ((mData[0] == 0xE2) && ((mData[1] & 0x20) != 0)) // SEP and M bit is set
        {
            newValue = true;
            return true;
        }
    }

    return false;
}


//-----------------------------------------------------------------------------
// Same as above except for X bit
bool AsmLine::SetsX(bool &newValue)
{
    if (mNumBytes == 2)
    {
        if ((mData[0] == 0xC2) && ((mData[1] & 0x10) != 0)) // CLP and X bit is set
        {
            newValue = false;
            return true;
        }
        if ((mData[0] == 0xE2) && ((mData[1] & 0x10) != 0)) // SEP and X bit is set
        {
            newValue = true;
            return true;
        }
    }

    return false;
}


//-----------------------------------------------------------------------------
bool AsmLine::SetsPC(unsigned &destAddress, bool &conditional, BranchType &type)
{
    destAddress = mAddress + mNumBytes;
    int offset;

    switch (mData[0])
    {
        
        case 0x34:  // BBC, direct bit relative
        case 0x24:  // BBS, direct bit relative
            if (mMBit)  destAddress += static_cast<signed char>(mData[3]);
            else        destAddress += static_cast<signed char>(mData[4]);
            conditional = true;
            type = Branch;
            return true;
            break;
        
        case 0x3C:  // BBC, absolute
        case 0x2C:  // BBS, absolute
            if (mMBit)  destAddress += static_cast<signed char>(mData[4]);
            else        destAddress += static_cast<signed char>(mData[5]);
            conditional = true;
            type = Branch;
            return true;
            break;
        
        case 0x10:  // BPL
        case 0x30:  // BMI
        case 0x50:  // BVC
        case 0x70:  // BVS
        case 0x90:  // BCC
        case 0xB0:  // BCS
        case 0xD0:  // BNE
        case 0xF0:  // BEQ
            destAddress += static_cast<signed char>(mData[1]);
            conditional = true;
            type = Branch;
            return true;
            break;

        case 0x80:  // BRA
            destAddress += static_cast<signed char>(mData[1]);
            conditional = false;
            type = Branch;
            return true;
            break;

        case 0x82:  // BRAL
            offset = (mData[1] + (mData[2] << 8));
            destAddress += offset;
            conditional = false;
            type = Branch;
            return true;
            break;

        case 0x4C: // JMP Absolute  
        case 0x5C: // JMP Absolute long
            offset = (mData[1] + (mData[2] << 8));
            if (offset < 0x4000)
            {
                destAddress = 0;
                AddComment("Jump to RAM!");
            }
            else
            {
                destAddress = offset;
            }
            conditional = false;
            type = Jump;
            return true;
            break;
            

        case 0x6C: // JMP Absolute indirect         NOTE: Can't know the answer here
        case 0x7C: // JMP Absolute index x indirect NOTE: Can't know the answer here
        case 0xDC: // JMP Absolute indirect long    NOTE: Can't know the answer here
            destAddress = 0;
            conditional = false;
            type = Jump;
            return true;
            break;


        case 0x20: // JSR Absolute
        case 0x22: // JSR Absolute long
            offset = (mData[1] + (mData[2] << 8));
            if (offset < 0x4000)
            {
                destAddress = 0;
                AddComment("Jump to RAM!");
            }
            else
            {
                destAddress = offset;
            }
            conditional = false;
            type = Call;
            return true;
            break;
               
        case 0xFC: // JSR Absolute indexed X indirect NOTE Can't know the answer here, relies on X and memory
            destAddress = 0;
            conditional = false;
            type = Call;
            return true;
            break;

        case 0x60: // RTS  NOTE: I can't tell where this goes, only that it changes the PC
        case 0x6B: // RTL  NOTE: I can't tell where this goes, only that it changes the PC
            destAddress = 0;
            conditional = false;
            type = Return;
            return true;
            break;

        case 0x40: // RTI  NOTE: I can't tell where this goes, only that it changes the PC
            destAddress = 0;
            conditional = false;
            type = Return;
            return true;
            break;

    }

    return false;
}




//-----------------------------------------------------------------------------------
bool AsmLine::ReadsMemory(unsigned &destAddress, AccessType &type, unsigned &value)
{
    value = 0xFFFFFFFF;
    bool retval = false;

    // uses accumulator b
    int index = 0;
    if (mData[0] == 0x42) index = 1;

    #define DT_REG  0

    switch (mData[index])
    {
        // Direct
        case 0x34:  // BBC
        case 0x24:  // BBS
        case 0xC5:  // CMP
        case 0xE4:  // CPX
        case 0xC4:  // CPY
        case 0x45:  // EOR
        case 0xA5:  // LDA
        case 0xA6:  // LDX
        case 0xA4:  // LDY
        case 0x05:  // ORA
        case 0xE5:  // SBC
            type = Direct;
            retval = true;
            destAddress = DT_REG + mData[index + 1];
            break;

        // Absolute
        case 0x3C:  // BBC
        case 0x2C:  // BBS
        case 0xCD:  // CMP
        case 0xEC:  // CPX
        case 0xCC:  // CPY
        case 0x4D:  // EOR
        case 0xAD:  // LDA
        case 0xAE:  // LDX
        case 0xAC:  // LDY
        case 0x0D:  // ORA
        case 0xED:  // SBC
            type = Absolute;
            retval = true;
            destAddress = mData[index + 1] + (mData[index + 2] << 8);
            break;

    }

    return retval;
}



//-----------------------------------------------------------------------------------
bool AsmLine::SetsMemory(unsigned &destAddress, AccessType &type, unsigned &value)
{
    value = 0xFFFFFFFF;
    bool retval = false;

    // uses accumulator b
    int index = 0;
    if (mData[0] == 0x42) index = 1;

    #define DT_REG  0

    switch (mData[index])
    {
		// Immediate
		case 0xA2:	// LDX
			type = Immediate;
			retval = true;
			destAddress = mData[index + 1] + (mData[index + 2] << 8);
			break;
		// Direct
        case 0x06:  // ASL
        case 0x14:  // CLB
        case 0xC6:  // DEC
        case 0xE6:  // INC
        case 0x64:  // LDM
        case 0x46:  // LSR
        case 0x26:  // ROL
        case 0x66:  // ROR
        case 0x04:  // SEB
        case 0x85:  // STA
        case 0x86:  // STX
        case 0x84:  // STY
            type = Direct;
            retval = true;
            destAddress = DT_REG + mData[index + 1];
            if ((mData[index] & 0x8F) == 0x04) // clb, ldm, seb
            {
                value = mData[index + 2];
                if (!mMBit) value += (mData[index + 3] << 8);
            }

            break;

        // DirectIndexedX
        case 0x16:  // ASL
        case 0xD6:  // DEC
        case 0xF6:  // INC
        case 0x74:  // LDM
        case 0x56:  // LSR
        case 0x36:  // ROL
        case 0x76:  // ROR
        case 0x95:  // STA
        case 0x94:  // STY
            type = DirectIndexedX;
            retval = true;
            destAddress = DT_REG + mData[index + 1];
            if (mData[index] == 0x74)
            {
                value = mData[index + 2];
                if (!mMBit) value += (mData[index + 3] << 8);
            }
            break;

        // DirectIndexedY
        case 0x96:  // STX
            type = DirectIndexedY;
            retval = true;
            destAddress = DT_REG + mData[index + 1];
            break;

        // DirectIndirect
        case 0x92:  // STA
            type = DirectIndirect;
            retval = true;
            destAddress = DT_REG + mData[index + 1];
            break;

        // DirectIndexedXIndirect
        case 0x81:  // STA
            type = DirectIndexedXIndirect;
            retval = true;
            destAddress = DT_REG + mData[index + 1];
            break;

        // DirectIndirectIndexedY
        case 0x91:  // STA
            type = DirectIndirectIndexedY;
            retval = true;
            destAddress = DT_REG + mData[index + 1];
            break;

        // DirectIndirectLong
        case 0x87:  // STA
            type = DirectIndirectLong;
            retval = true;
            destAddress = DT_REG + mData[index + 1];
            break;

        // DirectIndirectLongIndexedY
        case 0x97:  // STA
            type = DirectIndirectLongIndexedY;
            retval = true;
            destAddress = DT_REG + mData[index + 1];
            break;

        // Absolute
        case 0x0E:  // ASL
        case 0x1C:  // CLB
        case 0xCE:  // DEC
        case 0xEE:  // INC
        case 0x9C:  // LDM
        case 0x4E:  // LSR
        case 0x2E:  // ROL
        case 0x6E:  // ROR
        case 0x0C:  // SEB
        case 0x8D:  // STA
        case 0x8E:  // STX
        case 0x8C:  // STY
            type = Absolute;
            retval = true;
            destAddress = mData[index + 1] + (mData[index + 2] << 8);
            if (((mData[index] & 0x0F) == 0x0C) && // clb, ldm, seb
                 (mData[index] != 0x8C)) // NOT STY!
            {
                value = mData[index + 3];
                if (!mMBit) value += (mData[index + 4] << 8);
            }
            break;

        // AbsoluteIndexedX
        case 0x1E:  // ASL
        case 0xDE:  // DEC
        case 0xFE:  // INC
        case 0x9E:  // LDM
        case 0x5E:  // LSR
        case 0x3E:  // ROL
        case 0x7E:  // ROR
        case 0x9D:  // STA
            type = AbsoluteIndexedX;
            retval = true;
            destAddress = mData[index + 1] + (mData[index + 2] << 8);
            if (mData[index] == 0x9E)
            {
                value = mData[index + 3];
                if (!mMBit) value += (mData[index + 4] << 8);
            }
            break;

        // AbsoluteIndexedY
        case 0x99:  // STA
            type = AbsoluteIndexedY;
            retval = true;
            destAddress = mData[index + 1] + (mData[index + 2] << 8);
            break;

        // AbsoluteLong
        case 0x8F:  // STA
            type = AbsoluteLong;
            retval = true;
            destAddress = mData[index + 1] + (mData[index + 2] << 8) + (mData[index + 3] << 16);
            break;

        // AbsoluteLongIndexedX
        case 0x9F:  // STA
            type = AbsoluteLongIndexedX;
            retval = true;
            destAddress = mData[index + 1] + (mData[index + 2] << 8) + (mData[index + 3] << 16);
            break;

        // StackRelative
        case 0x83:  // STA
            type = StackRelative;
            retval = true;
            destAddress = mData[index + 1];
            break;

        // StackRelativeIndirectIndexedY
        case 0x93:  // STA
            type = StackRelativeIndirectIndexedY;
            retval = true;
            destAddress = mData[index + 1];
            break;
    }

    return retval;
}


//-----------------------------------------------------------------------------------
class Disassembler
{
  public:
    Disassembler(void) { mChunks.clear(); }
   ~Disassembler();

    bool Prep(char *binname, unsigned binStartAddress, unsigned *vectorTable, unsigned chunkSize = 16384);
    AsmLine *DisassembleLine(unsigned address, bool m, bool x);
    
  protected:

    struct Chunk
    {
        char name[256];
        unsigned chunkStartAddress;
        unsigned chunkSize;
    };

    std::vector<Chunk> mChunks;
};

 

//-----------------------------------------------------------------------------------
Disassembler::~Disassembler()
{
    unsigned a;
    for (a = 0; a < mChunks.size(); a++)
    {
        _unlink(mChunks[a].name);
    }

}


//-----------------------------------------------------------------------------------
bool Disassembler::Prep(char *binname, unsigned binStartAddress, unsigned *vectorTable, unsigned chunkSize)
{
    FILE *inf = fopen(binname, "rb");
    fseek(inf, 0, SEEK_END);
    fpos_t pos;
    fgetpos(inf, &pos);
    fseek(inf, 0, SEEK_SET);

    unsigned binLength = (unsigned)pos;

    unsigned char *bin = new unsigned char[binLength];
    fread(bin, 1, binLength, inf);
    fclose(inf);

    if (binLength <= chunkSize)
    {
        Chunk c;
        strcpy(c.name, binname);
        c.chunkStartAddress = binStartAddress;
        c.chunkSize = binLength;
        mChunks.push_back(c);
        return true;
    }

    char chunkBaseName[512];
    strcpy(chunkBaseName, binname);
    char *ext = strstr(chunkBaseName, ".");
    if (ext != NULL)
    {
        *ext = 0;
    }

    unsigned remain = binLength;
    unsigned count = 0;
    unsigned address = binStartAddress;
    while (remain != 0)
    {
        Chunk c;
        sprintf(c.name, "%s.%d", chunkBaseName, count);
        c.chunkStartAddress = address;
        if (remain >= chunkSize) c.chunkSize = chunkSize;
        else                     c.chunkSize = remain;
        
        ++count;
        address += c.chunkSize;
        remain  -= c.chunkSize;

        FILE *outf = fopen(c.name, "wb");
        if (outf == NULL)
        {
            printf("Error creating temp file %s.  Make sure that no file exists with than name.\n");
            return false;
        }

        fwrite(&bin[c.chunkStartAddress - binStartAddress], 1, c.chunkSize, outf);
        fclose(outf);

        mChunks.push_back(c);
    }

    // copy vector table
    unsigned vecBase = 0xFFD4;
    unsigned binEnd = binStartAddress + binLength;
    unsigned a;
    for (a = 0; a < 22; a++)
    {
        vectorTable[a] = 0;
        if ((vecBase+1) < binEnd)
        {
            vectorTable[a] = bin[vecBase - binStartAddress] + (bin[(vecBase + 1) - binStartAddress] << 8);
        }
        vecBase += 2;
    }

    return true;
}


//-----------------------------------------------------------------------------------
AsmLine *Disassembler::DisassembleLine(unsigned address, bool m, bool x)
{
    Chunk *c = NULL;
    unsigned a;
    for (a = 0; a < mChunks.size(); a++)
    {
        if ((address >= mChunks[a].chunkStartAddress) &&
            (address < (mChunks[a].chunkStartAddress + mChunks[a].chunkSize)))
        {
            c = &(mChunks[a]);
            break;
        }
    }

    if (c == NULL) return NULL;

    FILE *fStd=fopen("temp.txt", "wt");
    FILE *fErr=fopen("err.txt", "wt");

    int prevStdout = _dup(_fileno(stdout));
    int prevStderr = _dup(_fileno(stderr));

    _dup2(_fileno(fStd), _fileno(stdout));
    _dup2(_fileno(fErr), _fileno(stderr));
    
    char start[32], end[32];
    sprintf(start, "%X", address - c->chunkStartAddress);
    sprintf(end, "%X", (address - c->chunkStartAddress) + 1);

    char ms[2] = { '0', 0 };
    char xs[2] = { '0', 0 };

    if (m) ms[0] = '1';
    if (x) xs[0] = '1';

    _spawnl( _P_WAIT, "dasm77.exe", "dasm77.exe", c->name, start, end, xs, ms, NULL );

    fclose(fStd);
    fclose(fErr);

    _dup2(prevStdout, _fileno(stdout));
    _dup2(prevStderr, _fileno(stderr));

    close(prevStderr);
    close(prevStdout);

    fStd = fopen("temp.txt", "rt");
    if (fStd == NULL)
    {
        return NULL;
    }

    char dest[512];
    dest[0] = 0;
    fgets(dest, 510, fStd);
    fclose(fStd);

    _unlink("temp.txt");
    _unlink("err.txt");

    // end strng at cr/lf
    unsigned pos = 0;
    while ((pos < 510) && (dest[pos] != 0))
    {
        if ((dest[pos] == 0x0D) || (dest[pos] == 0x0A))
        {
            dest[pos] = 0;
            break;
        }
        ++pos;
    }

    if (strlen(dest) < 8) return NULL;

    AsmLine *line = new AsmLine;

    bool ok = line->Init(dest, m, x, c->chunkStartAddress);
    if (!ok)
    {
        delete line;
        return NULL;
    }

    return line;
}



//-----------------------------------------------------------------------------

struct CodeBlock
{
    CodeBlock(void) { name[0] = 0; }

    unsigned address;
	unsigned calledby;
    unsigned length;
    bool subroutine;
    bool m;
    bool x;
    std::vector<AsmLine *> disassembly;

    char name[64];

    bool operator<(const CodeBlock &right)
    {
        return address < right.address;
    }

    bool operator==(const CodeBlock &other)
    {
        return (address == other.address) && 
               (subroutine == other.subroutine) &&
               (m == other.m) && 
               (x == other.x);
    }
};


//-----------------------------------------------------------------------------
AsmLine *FindAsmLine(std::list<CodeBlock> &blocks, unsigned address)
{
    if (blocks.empty()) return NULL;

    std::list<CodeBlock>::iterator cbit;
    for (cbit = blocks.begin(); cbit != blocks.end(); cbit++)
    {
        CodeBlock &cb = *cbit;

        if (address < cb.address) continue;
        if (address > (cb.address + cb.length)) continue; // could be return null if blocks are sorted

        unsigned a;
        for (a = 0; a < cb.disassembly.size(); a++)
        {
            if (cb.disassembly[a]->Address() == address) return cb.disassembly[a];
        }
    }
    return NULL;
}


//-----------------------------------------------------------------------------
bool IsNewCode(std::list<CodeBlock> l, CodeBlock &cb)
{
    if (l.empty())
    {
        return true;
    }

    std::list<CodeBlock>::iterator it;

    for (it = l.begin(); it != l.end(); it++)
    {
        if (*it == cb) return false;
        unsigned start = (*it).address;
        unsigned end   = start + (*it).length;

        if ((cb.address >= start) && (cb.address < end))
        {
            unsigned a;
            for (a = 0; a < (*it).disassembly.size(); a++)
            {
                AsmLine *al = (*it).disassembly[a];
                if ((al->Address() == cb.address) && (al->GetM() == cb.m) && (al->GetX() == cb.x))
                {
                    return false;
                }
            }
        }
    }

    return true;
}



//-----------------------------------------------------------------------------
//Change this for different memory sizes
void CommentWrites(AsmLine *line, unsigned address, AsmLine::AccessType type, unsigned value)
{
    char comment[80];
    comment[0] = 0;

    PeripheralRegister *reg;

    switch (type)
    {
		// New case for immediate LDX
		case AsmLine::Immediate:
			if (address > 0xE000)
			{
				reg = FindRegister(address);
                if (reg != NULL)
                {
					sprintf(comment, "Load X: %s", reg->name);
                }
			}
			break;
		case AsmLine::Direct:
        case AsmLine::DirectIndirectLong:
        case AsmLine::Absolute:
        case AsmLine::AbsoluteLong:
            if (address < 0x100)
            // Commenting peripheral addys writes
			{
				reg = FindRegister(address);
                if (reg != NULL)
                {
                    sprintf(comment, "Write to %s", reg->name);
                }
                else
                {
                    sprintf(comment, "Write to unknown peripheral [%02X]", address);
                }            
            }
			// Commenting memory writes
			else if (address < 0x8000)
			{
				reg = FindRegister(address);
                if (reg != NULL)
                {
					sprintf(comment, "Write to RAM: %s", reg->name);
                }
			}
			// Commenting ROM writes
			else if (address >= 0x8000)
			{
				reg = FindRegister(address);
                if (reg != NULL)
                {
					sprintf(comment, "Write to ROM: %s", reg->name);
                }
			}
            break;
    }
    if (comment[0] != 0)
    line->AddComment(comment);
}

//-----------------------------------------------------------------------------
void CommentReads(AsmLine *line, unsigned address, AsmLine::AccessType type, unsigned value)
{
    char comment[80];
    comment[0] = 0;

    PeripheralRegister *reg;

    switch (type)
    {
        case AsmLine::Direct:
        case AsmLine::Absolute:
            // Commenting peripheral addys reads
			if (address < 0x100)
            {
                reg = FindRegister(address);
                if (reg != NULL)
                {
                    sprintf(comment, "Read from %s", reg->name);
                }
                else
                {
                    sprintf(comment, "Read from unknown peripheral [%02X]", address);
                }
			}
			// Commenting memory writes
			else if (address < 0x8000)
			{
				reg = FindRegister(address);
                if (reg != NULL)
                {
					sprintf(comment, "Read from RAM: %s", reg->name);
                }
			}
			// Commenting ROM writes
			else if (address >= 0x8000)
			{
				reg = FindRegister(address);
                if (reg != NULL)
                {
					sprintf(comment, "Read from ROM: %s", reg->name);
                }
			}
            break;
    }
    if (comment[0] != 0)
    line->AddComment(comment);
}



//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int main(int argc, char **argv)
{

    if (argc != 3)
    {
        printf("disasm 1.3 by erich in 2005, modified slightly by calum in 2006\n");
        printf("USAGE: disasm binname outputfile\n");
        exit(0);
    }

    char *binname = argv[1];
    //char *baseAddrString = argv[2];
	char *outfilename = argv[2];

    FILE *infile = fopen(binname, "rt");
    if (infile == NULL)
    {
       printf("Unable to open %s for reading.\n", binname);
        exit(0);
    }
    fclose(infile);
	
	FILE *resultf = fopen(outfilename, "wt");
    if (resultf == NULL)
    {
       printf("Unable to open %s for reading.\n", outfilename);
        exit(0);
    }


    InitRegisters();

    //unsigned ROMBaseAddress = HexToUnsigned(baseAddrString);
	unsigned ROMBaseAddress = 0;

    Disassembler disassembler;

	//Ok, so these vars could probably be placed in a better spot
	std::vector< unsigned > AddressStack;	// AddressStack
	std::vector< unsigned > BadAddress;		// Suspected bad dissassembly points
	bool tracing = true;					// Halt trace variable

    unsigned vectorTable[22];
    bool ok = disassembler.Prep(binname, ROMBaseAddress, vectorTable);
    if (!ok)
    {
        exit(0);
    }

    std::list<CodeBlock> codeBlocks;
    std::list<CodeBlock> evalList;

    CodeBlock cb;
    unsigned a = 8;
    for (a = 0; a < 22; a++)
    {
        cb.address = vectorTable[a];
        cb.m = false;
        cb.x = false;
        cb.subroutine = false;
        strcpy(cb.name, InterruptVectors[a]);

        if (vectorTable[a] != 0) evalList.push_back(cb);
    }

    while (!evalList.empty())
    {
        cb = *(evalList.begin());
        evalList.pop_front();

        cb.length = 0;
        unsigned address = cb.address;
		unsigned caller = address;
        bool m = cb.m;
        bool x = cb.x;

        if (!IsNewCode(codeBlocks, cb))
        {
            continue;
        }

        bool needAdd = false;
        
		//  Debug lines
		printf("Dis Code block address:    %X   M:%d X:%d \n", address, m, x);
		//getchar ();
		
		while (1)
        {
            AsmLine *al = disassembler.DisassembleLine(address, m, x);
            
			//printf("	Line address: %X   M:%d X:%d \n", address, m, x);
			//getchar ();
            
            if (al == NULL)
			{
				//printf("	end of block - code is null");
				//getchar ();
				break;
			}

            needAdd = true;

            cb.length += al->NumBytes();
			caller = address;
            address   += al->NumBytes();
            cb.disassembly.push_back(al);

            // these only modify the input when they would return true
            bool mod = false;
            mod  = al->SetsM(m);
            mod |= al->SetsX(x);

            if (mod)
            {
                char s[10];
                sprintf(s, "m:%d x:%d", (int)m, (int)x);
                al->AddComment(s);
            }

            unsigned newAddress;
            bool isConditional;
            AsmLine::BranchType type;
            if (al->SetsPC(newAddress, isConditional, type))
            {
                if (newAddress == 0)
                {
                    //printf("	end of block - branch of type %d",type);
					//getchar ();
					break;
                }

                CodeBlock b;
                b.address = newAddress;
				b.calledby = caller;
                b.length  = 0;
                b.subroutine = (type == AsmLine::Call);
                b.x = x;
                b.m = m;
                b.name[0] = 0;


				// Scan JSRs for m & x flag changes
				printf("Adding Code block address: %X   M:%d X:%d \n", newAddress, m, x);
				// Check for JSR (type = 2)
				if (type == 2)
				{
					// printf("JSR %X\n", newAddress);
					// scan JSR here!
					tracing = true;

					while(tracing)
					{
						AsmLine *trace = disassembler.DisassembleLine(newAddress, m, x);
						//printf("		trace address: %X   M:%d X:%d\n", newAddress, m, x);
						newAddress   += trace->NumBytes();  //set newAdd to next address in the trace
						trace->SetsM(m);
						trace->SetsX(x);
						unsigned branchAddress;
						bool newisConditional;
						AsmLine::BranchType newtype;
						if (trace->SetsPC(branchAddress, newisConditional, newtype))  //check for RTS or another JSR
						{
							if (newtype == 2)
							{
								//printf("nested JSR %X\n", branchAddress);
								if (branchAddress != 0)
								{
									AddressStack.push_back(newAddress);  // put old address on the stack
									newAddress = branchAddress;
								}
								else
								{
									BadAddress.push_back(newAddress);
									//printf("fooked dissassembly!");
									//getchar();
								}
							}
							else if ( newtype == 3)
							{
								//printf("End of trace level %d\n",AddressStack.size());
								//getchar ();
								if (AddressStack.empty())
								{
									tracing = false;
									//printf("\n		end trace");
									//getchar();
								}
								else
								{
									newAddress = AddressStack[AddressStack.size()-1];
									AddressStack.pop_back();
								}
							}
							else
							{
								//printf(" - ignore branch type %d \n",newtype);
							}
						}
						else
						{
							//printf("\n");
						}
					}
				}
			
				//getchar ();



                evalList.push_back(b);

                if (!isConditional && (type != AsmLine::Call))
                {
                    //printf("	end of block - 3");
					//getchar ();
					break;
                }
			}
		}

        if (needAdd)
        {
            if (IsNewCode(codeBlocks, cb) && !cb.disassembly.empty())
            {
                codeBlocks.push_back(cb);
            }
        }
    }


    // Sort code blocks
	if (!codeBlocks.empty())
    {
        codeBlocks.sort();
        std::list<CodeBlock>::iterator cbit, nextIt;

        // merge blocks
        for (cbit = codeBlocks.begin(); cbit != codeBlocks.end(); cbit++)
        {
            nextIt = cbit;
            nextIt++;
            CodeBlock &cb = *cbit;
            if (nextIt == codeBlocks.end()) break;

            CodeBlock &next = *nextIt;

            // do these overlap?
            unsigned end = cb.address + cb.length;
            if (next.address < end)
            {
                unsigned a;
                for (a = 0; a < next.disassembly.size(); a++)
                {
                    if (next.disassembly[a]->Address() > end)
                    {
                        cb.disassembly.push_back(next.disassembly[a]);
                        cb.length += next.disassembly[a]->NumBytes();
                        end       += next.disassembly[a]->NumBytes();
                        next.disassembly[a] = NULL;
                    }
                }
                codeBlocks.erase(nextIt);

                nextIt = cbit;
                nextIt++;
                CodeBlock &cb = *cbit;
                if (nextIt == codeBlocks.end()) break;
            }

            AsmLine *last = cb.disassembly[cb.disassembly.size() - 1];
            unsigned nextAddress;
            AsmLine::BranchType type;
            bool isConditional;
            bool isBranch = last->SetsPC(nextAddress, isConditional, type);
            if (isBranch && (type == AsmLine::Branch) && (nextAddress == next.address))
            {
                unsigned a;
                for (a = 0; a < next.disassembly.size(); a++)
                {
                    cb.disassembly.push_back(next.disassembly[a]);
                    cb.length += next.disassembly[a]->NumBytes();
                    end       += next.disassembly[a]->NumBytes();
                    next.disassembly[a] = NULL;
                }
                codeBlocks.erase(nextIt);
            }
        }

        
		// add comments
        for (cbit = codeBlocks.begin(); cbit != codeBlocks.end(); cbit++)
        {
            CodeBlock &cb = *cbit;

            unsigned a;
            for (a = 0; a < cb.disassembly.size(); a++)
            {
                unsigned address;
                unsigned value;

                // Memory comments
                AsmLine::AccessType accessType;
                if (cb.disassembly[a]->SetsMemory(address, accessType, value))
                {
                    CommentWrites(cb.disassembly[a], address, accessType, value);
                }
                if (cb.disassembly[a]->ReadsMemory(address, accessType, value))
                {
                    CommentReads(cb.disassembly[a], address, accessType, value);
                }

                // branch comments
                bool isConditional;
                AsmLine::BranchType branchType;
                if (cb.disassembly[a]->SetsPC(address, isConditional, branchType))
                {
                    AsmLine *dest = FindAsmLine(codeBlocks, address);
                    if (dest != NULL)
                    {
                        char s[64];
                        if (branchType == AsmLine::Call)
                        {
                            sprintf(s, "Call target from %04X", cb.disassembly[a]->Address());
                        }
                        else
                        {
                            sprintf(s, "Branch target from %04X", cb.disassembly[a]->Address());
                        }
                        dest->AddComment(s);
                    }
                }
            }
        }


		//Note possible bad dissassembly points
		printf("\n");
		printf("Number of suspect spots: %d\n",BadAddress.size());
		while (!BadAddress.empty())
		{
			printf("Suspect disassembly at %X\n",BadAddress[BadAddress.size()-1]);
			BadAddress.pop_back();
		}
		
		
		// Print results to file
		printf("\n");
		printf("Writing results to file...\n");
        fprintf(resultf,"Code dump of %d blocks.\n", codeBlocks.size());
        for (cbit = codeBlocks.begin(); cbit != codeBlocks.end(); cbit++)
        {
            CodeBlock &cb = *cbit;

            if (cb.name[0] == 0)
            {
				fprintf(resultf,"\nCode block address: %X   Length: %d  M:%d X:%d called by: %X\n", cb.address, cb.length, (int)cb.m, (int)cb.x, cb.calledby);
            }
            else
            {
				fprintf(resultf,"\nVECTOR: %s address: %X   Length: %d  M:%d X:%d\n", cb.name, cb.address, cb.length, (int)cb.m, (int)cb.x);
            }
            unsigned a;
            for (a = 0; a < cb.disassembly.size(); a++)
            {
                char *s = cb.disassembly[a]->Print();
                fputs(s,resultf);
                fprintf(resultf,"\n");
                delete[] s;
            }
        }
    }
	fclose(resultf);
    return 0;
}
