/*****************************************************************************/
/*                                                                           */
/*				   output.c				     */
/*                                                                           */
/*			 Disassembler output routines			     */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/* (C) 2000-2009, Ullrich von Bassewitz                                      */
/*                Roemerstrasse 52                                           */
/*                D-70794 Filderstadt                                        */
/* EMail:         uz@cc65.org                                                */
/*                                                                           */
/*                                                                           */
/* This software is provided 'as-is', without any expressed or implied       */
/* warranty.  In no event will the authors be held liable for any damages    */
/* arising from the use of this software.                                    */
/*                                                                           */
/* Permission is granted to anyone to use this software for any purpose,     */
/* including commercial applications, and to alter it and redistribute it    */
/* freely, subject to the following restrictions:                            */
/*                                                                           */
/* 1. The origin of this software must not be misrepresented; you must not   */
/*    claim that you wrote the original software. If you use this software   */
/*    in a product, an acknowledgment in the product documentation would be  */
/*    appreciated but is not required.                                       */
/* 2. Altered source versions must be plainly marked as such, and must not   */
/*    be misrepresented as being the original software.                      */
/* 3. This notice may not be removed or altered from any source              */
/*    distribution.                                                          */
/*                                                                           */
/*****************************************************************************/



#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

/* common */
#include "addrsize.h"
#include "cpu.h"
#include "version.h"

/* da65 */
#include "code.h"
#include "error.h"
#include "global.h"
#include "output.h"



/*****************************************************************************/
/*	      	  		     Data				     */
/*****************************************************************************/



static FILE* 	F 	= 0;		/* Output stream */
static unsigned	Col    	= 1;		/* Current column */
static unsigned Line   	= 0;		/* Current line on page */
static unsigned Page	= 1;		/* Current output page */



/*****************************************************************************/
/*	      	  		     Code				     */
/*****************************************************************************/



static void PageHeader (void)
/* Print a page header */
{
    fprintf (F,
       	     "; da65 V%s - (C) Copyright 2000-2009,  Ullrich von Bassewitz\n"
             "; Created:    %s\n"
     	     "; Input file: %s\n"
     	     "; Page:       %u\n\n",
       	     GetVersionAsString (),
             Now,
     	     InFile,
     	     Page);
}



void OpenOutput (const char* Name)
/* Open the given file for output */
{
    /* If we have a name given, open the output file, otherwise use stdout */
    if (Name != 0) {
        F = fopen (Name, "w");
        if (F == 0) {
            Error ("Cannot open `%s': %s", Name, strerror (errno));
        }
    } else {
        F = stdout;
    }

    /* Output the header and initialize stuff */
    PageHeader ();
    Line = 5;
    Col  = 1;
}



void CloseOutput (void)
/* Close the output file */
{
    if (F != stdout && fclose (F) != 0) {
	Error ("Error closing output file: %s", strerror (errno));
    }
}



void Output (const char* Format, ...)
/* Write to the output file */
{
    if (Pass == PassCount) {
	va_list ap;
	va_start (ap, Format);
	Col += vfprintf (F, Format, ap);
	va_end (ap);
    }
}



void Indent (unsigned N)
/* Make sure the current line column is at position N (zero based) */
{
    if (Pass == PassCount) {
	while (Col < N) {
	    fputc (' ', F);
	    ++Col;
	}
    }
}



void LineFeed (void)
/* Add a linefeed to the output file */
{
    if (Pass == PassCount) {
	fputc ('\n', F);
       	if (PageLength > 0 && ++Line >= PageLength) {
	    if (FormFeeds) {
		fputc ('\f', F);
	    }
	    ++Page;
	    PageHeader ();
	    Line = 5;
 	}
 	Col = 1;
    }
}



void DefLabel (const char* Name)
/* Define a label with the given name */
{
    Output ("%s:", Name);
    /* If the label is longer than the configured maximum, or if it runs into
     * the opcode column, start a new line.
     */
    if (Col > LBreak+2 || Col > MCol) {
     	LineFeed ();
    }
}



void DefForward (const char* Name, const char* Comment, unsigned Offs)
/* Define a label as "* + x", where x is the offset relative to the
 * current PC.
 */
{
    if (Pass == PassCount) {
        /* Flush existing output if necessary */
        if (Col > 1) {
            LineFeed ();
        }

        /* Output the forward definition */
        Output ("%s", Name);
        Indent (ACol);
        if (UseHexOffs) {
            Output (":= * + $%04X", Offs);
        } else {
            Output (":= * + %u", Offs);
        }
        if (Comment) {
            Indent (CCol);
            Output ("; %s", Comment);
        }
        LineFeed ();
    }
}



void DefConst (const char* Name, const char* Comment, unsigned Addr)
/* Define an address constant */
{
    if (Pass == PassCount) {
        Output ("%s", Name);
        Indent (ACol);
        Output (":= $%04X", Addr);
        if (Comment) {
            Indent (CCol);
            Output ("; %s", Comment);
        }
        LineFeed ();
    }
}



void StartSegment (const char* Name, unsigned AddrSize)
/* Start a segment */
{
    if (Pass == PassCount) {
        Output (".segment");
        Indent (ACol);
        if (AddrSize == ADDR_SIZE_DEFAULT) {
            Output ("\"%s\"", Name);
        } else {
            Output ("\"%s\": %s", Name, AddrSizeToStr (AddrSize));
        }
        LineFeed ();
    }
}



void DataByteLine (unsigned ByteCount)
/* Output a line with bytes */
{
    unsigned I;

    Indent (MCol);
    Output (".byte");
    Indent (ACol);
    for (I = 0; I < ByteCount; ++I) {
 	if (I > 0) {
 	    Output (",$%02X", CodeBuf[PC+I]);
 	} else {
 	    Output ("$%02X", CodeBuf[PC+I]);
 	}
    }
    LineComment (PC, ByteCount);
    LineFeed ();
}



void DataDByteLine (unsigned ByteCount)
/* Output a line with dbytes */
{
    unsigned I;

    Indent (MCol);
    Output (".dbyt");
    Indent (ACol);
    for (I = 0; I < ByteCount; I += 2) {
	if (I > 0) {
       	    Output (",$%04X", GetCodeDByte (PC+I));
	} else {
	    Output ("$%04X", GetCodeDByte (PC+I));
	}
    }
    LineComment (PC, ByteCount);
    LineFeed ();
}



void DataWordLine (unsigned ByteCount)
/* Output a line with words */
{
    unsigned I;

    Indent (MCol);
    Output (".word");
    Indent (ACol);
    for (I = 0; I < ByteCount; I += 2) {
	if (I > 0) {
	    Output (",$%04X", GetCodeWord (PC+I));
	} else {
	    Output ("$%04X", GetCodeWord (PC+I));
	}
    }
    LineComment (PC, ByteCount);
    LineFeed ();
}



void DataDWordLine (unsigned ByteCount)
/* Output a line with dwords */
{
    unsigned I;

    Indent (MCol);
    Output (".dword");
    Indent (ACol);
    for (I = 0; I < ByteCount; I += 4) {
	if (I > 0) {
	    Output (",$%08lX", GetCodeDWord (PC+I));
	} else {
	    Output ("$%08lX", GetCodeDWord (PC+I));
	}
    }
    LineComment (PC, ByteCount);
    LineFeed ();
}



void SeparatorLine (void)
/* Print a separator line */
{
    if (Pass == PassCount && Comments >= 1) {
	Output ("; ----------------------------------------------------------------------------");
	LineFeed ();
    }
}



void UserComment (const char* Comment)
/* Output a comment line */
{
    Output ("; %s", Comment);
    LineFeed ();
}



void LineComment (unsigned PC, unsigned Count)
/* Add a line comment with the PC and data bytes */
{
    unsigned I;

    if (Pass == PassCount && Comments >= 2) {
	Indent (CCol);
	Output ("; %04X", PC);
	if (Comments >= 3) {
	    for (I = 0; I < Count; ++I) {
	      	Output (" %02X", CodeBuf [PC+I]);
	    }
	    if (Comments >= 4) {
	      	Indent (TCol);
	      	for (I = 0; I < Count; ++I) {
	      	    unsigned char C = CodeBuf [PC+I];
	      	    if (!isprint (C)) {
	      		C = '.';
	      	    }
	      	    Output ("%c", C);
	      	}
	    }
	}
    }
}



void OutputSettings (void)
/* Output CPU and other settings */
{
    LineFeed ();
    Indent (MCol);
    Output (".setcpu");
    Indent (ACol);
    Output ("\"%s\"", CPUNames[CPU]);
    LineFeed ();
    LineFeed ();
}



