#!/usr/local/bin/perl
#
# $Id: convop,v 1.10 1996/07/30 08:37:37 ettore Exp ettore $
#
# This proggy generates ophack.c
#
# You don't need to understand this code  ;)
# ...or then you have to, after all. Tough luck.
#
# Written by
#   Jarkko Sonninen (sonninen@lut.fi)
#   Ettore Perazzoli (ettore@comm2000.it)
#
# $Log: convop,v $
# Revision 1.10  1996/07/30 08:37:37  ettore
# Produces the code for the new memory access scheme.
# rmw_flag for emulation of the Read-Modify-Write bug.
# Miscellaneous fixes.
#
#

# Input files
$OP    = "op.c";
$TABLE = "table.c";
$MAIN  = "cpu.c";
@DEFINES = ("cpu.h", "op.h");

# Output files
$TTARGET = "tablehack.c";
$HTARGET = "ophack.h";
$OTARGET = "ophack.c";
$CTARGET = "ophack2.c";


# Scan the addressing modes ...

open (M, "<$MAIN");
#open (M, "/lib/cpp -P $MAIN |");

$_ = <M> while (!/switch\s*\(\s*op\s*->\s*source\)/);	# switch
print "sources:\n";
while (($_ =<M>) && !/default:/) {
    if (($mode) = /case\s*(.*)\s*:/) {
	$_ = <M>;
	while (/#/) {$_ = <M>};				# skip ifdef stuff 
	($SRC{$mode}) =  /\s*src\s*=\s*(.*)\s*\;/;
	print "\t$mode:\t$SRC{$mode}\n";
    }
}
$SRC{"M_INDY"} = "LOAD(LOAD_ZERO_ADDR(p1) + YR)";


$_ = <M> while (!/switch\s*\(\s*op\s*->\s*destination\s*\)/);	#switch
print "dests:\n";
while (($_ =<M>) && !/default:/) {
    if (($mode) = /case\s*(.*)\s*:/) {
	$_ = <M>;
	while (/#/) {$_ = <M>};				# skip ifdef stuff 
	s/break\;//g;					# remove break;
	/\s*(.*)/; $DEST{$mode} = $1;
	print "\t$mode:\t$DEST{$mode}\n";
    }
}
close (M);

# ... the defines

foreach (@DEFINES) {
    open (M, "<$_") || die ("\nCannot open $_\n");

    while (<M>) {
	if (/^#define\s+(\S*)\s+(.*)/) {
	    print "define $1=$2.\n";
	    if ($DEFINE{$1} ne "") {
		print "already defined to $DEFINE{$1}\n";
	    } else {
		$DEFINE{$1} = $2;
	    }
	}
    }
}
close (M);

# ...and finally, the functions.

open (M, "<$OP");
print "opcodes:\n";
while ($_ =<M>) {
    if (/unsigned\s+int\s+(\S*)\s*\(\s*unsigned\s+int\s+src\s*/) {
	$name = $1;
	($c = getc(M)) while ($c ne '{');
	$s = ""; $bc = 1;

	do {
	    $c = getc(M);
	    ($c eq '{') && $bc++;
	    ($c eq '}') && $bc--;
	    $bc && ($s .= $c);
	    #print "$bc,$c\n";
	} while (!eof(M) && $bc); 
	print "func $name\n<$s>\n";
	$FUNC{$name} = $s;
    } else {
	!/^$|^#/ && print O $_;
    }
}
close (M);

# oolrait nytten on muistissa osoitusmuodot ja functiot
# ja eikun koodin tekoon.

# Open output files

open (O, ">$OTARGET");
print O "\n/* $OTARGET\n * This file is generated from op.c -do not edit.\n */\n\n";
print O "#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n\
#include \"op.h\"\n#include \"macro.h\"\n#include \"vmachine.h\"\n#include \"extern.h\"\n\n";

open (C, ">$CTARGET");
print C "\n/* $CTARGET\n * this file is generated from op.c -do not edit.\n */\n\n";
print C "#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n";
print C "#include \"cpu.h\"\n#include \"op.h\"\n#include \"macro.h\"\n#include \"vmachine.h\"\n#include \"extern.h\"\n#include \"interrupt.h\"\n\n";

# Variable declarations for mainloop

print C "\n#ifdef DEBUG\n#include \"memory.h\"\n\nint     rd[RAMSIZE];\n";
print C "int     wr[RAMSIZE];\nlong    instructions[256];\n#endif\n";
print C "#ifdef LED_ARRAY\nint     ledarr[16];\n#endif\n";

print C "\n\n/* CPU internals */\n\nADDRESS program_counter;\n";
print C "BYTE    accumulator, status_register;\nBYTE    x_register, y_register, stack_pointer;\n";
print C "\n\nint     zero_flag;\nint     sign_flag;\nint     overflow_flag;\nint     break_flag;\nint     decimal_flag;\nint     interrupt_flag;\nint     carry_flag;\n";
print C "int     rmw_flag = 0;\t/* emulates the RMW bug when necessary */\n";
print C "\n\nCLOCK   clk;\nextern void test_irq(CLOCK clock);\nextern void DoRaster(void);\n\n";


# mainloop for ophack2.c

print C "void    mainloop(ADDRESS addr)\n{\n    register BYTE b;\n    extern int     next_int;\n";
print C "    register BYTE p1;\n    register unsigned int p2;\n    register unsigned int src;\n    register unsigned int temp;\n    CLOCK video_clk = CYCLES_PER_LINE;\n";

print C "\n#ifdef NEW_TIMER_CODE__\n    ;\n#endif\n";

print C "    SET_SR(0x20);SP = 0;\n    PC = addr;\n    while (1) {\n\twhile (clk > next_int_clk)\n\t    PC = intr(next_int, PC);\n\n";
print C "\n\tif (cpu_check_irq) PC = cpu_do_int(PC);\n";

print C "#ifdef VIC_II\n\tif (clk >= video_clk) { DoRaster(); video_clk += CYCLES_PER_LINE; }\n#endif\n";
print C "\n#ifdef NEW_TIMER_CODE__\n\talarm_handler();\n#endif\n";

print C "
#ifndef C128
	if (!memtab_r[PC]) {
		b = (*memrd[PC>>8])(PC);
		p1 = (*memrd[(PC+1)>>8])(PC+1);
		p2 = p1 | (((*memrd[(PC+2)>>8])(PC+2))<<8);
	} else {
		BYTE *pp = memtab_r[PC];
		b = pp[0];
		p1 = pp[1];
		p2 = p1 | (pp[2]<<8);
	}
#else
	b = LOAD(PC);
	p1 = LOAD(PC+1);
	p2 = p1 | (LOAD(PC+2) << 8);
#endif
";

print C "\n#ifdef DEBUG\n\tinstructions[b]++;\n\n\tif (traceflg)\n\t  printf(\".%04x\\t%s\\n\", PC, sprint_opcode(PC, 1));\n\t  /*  show(); */\n#endif\n";
print C "#ifdef LED_ARRAY\n\tif (ledflg) {\n\t    int  eff;\n\t    ++ledarr[PC >> 12];\n\t	    if ((eff = eff_address(PC, 1)) >= 0)\n\t\t++ledarr[eff >> 12];\n\t};\n#endif\n";

print C "\n\nswitch (b) {\n";

#open (M, "/lib/cpp -C -P $TABLE |");
open (M, "<$TABLE");
open (T, ">$TTARGET");

print T "\n/* $TTARGET\n * This file is generated from table.c -do not edit.\n */\n\n";
print T "#include \"cpu.h\"\n#include \"mnemonic.h\"\n";
print T "#include \"$HTARGET\"\n\n";

open (H, ">$HTARGET");
print H "\n/* $HTARGET\n * This file is generated from table.c -do not edit.\n */\n\n";

print H "#include \"vmachine.h\"\n\n/* generated prototypes */\n\n";


print "table:\n";
$count = 0;
while ($_ =<M>) {
    if (/int\s+clength\s*\[\s*\]\s*=\s*\{(.*)\}/) {
	@len = split(",", $1);
	print "lengths found : @len\n\n";
	print T $_;
    }

# /* 00 */ {brki,  BRK,  IMPLIED, M_NONE, M_PC,  7,  0},   /* Pseudo Absolute */
#         \{ $1 ,   $2 ,    $3  ,  $4   ,  $5 , $6 , $7 \}

    elsif (/([^}]*)\{\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\}/) {

	if (!$FUNC{$2}) {
	    if ($DEFINE{$2}) {
		$e = $DEFINE{$2};
		$f = "${e}_" . sprintf("%02x", $count);
		print "$2 defined to $e\n";
	    } else {
	        $f = $e = "noop";
	    }
	    $s = $FUNC{$e};
	} else {
	    $f = "${2}_" . sprintf("%02x", $count);
	    $s = $FUNC{$2};
	}

	$mne = $3;
	$len = $len[$DEFINE{$4}];
	$ss  = $SRC{$5};
	$dd  = $DEST{$6};
	$clk = $7;
	$pbcfix = $8;
	$iss = $5;


	#print C "case $count:\n{\n";
	print C "case $count:\t/* $3,\t$4,\t$5,\t$6,\t$7,\t$8 */\n{\n";

	print   "   { $f,\t$3,\t$4,\t$5,\t$6,\t$7,\t$8 },\n";
	print T "   { $f,\t$3,\t$4,\t$5,\t$6,\t$7,\t$8 },\n";

	# die ("no such function ($2) $s\n");
	# print "$2  src $5($ss) dest $6($dd) len $len\n";	# debug


	## Undocumented Instruction

	print H "void $f ();\n";

	$start = "";
	$end = "}\n\n";
	$mid = "";


	## Find the return value

	if ($dd) {
	    $s =~ /return\s*(.+)\s*\;/;
	    $tmp = $1;				# Damn. They patched perl ...
	    $dd =~ s/result/$tmp/g;
	    $s .= "    " . $dd . ";\n";
	}
	$s =~ s/return.*\;//g;

	$para = "";
	if ($len == 3) {
#	    $para = "    register p2 = LOAD(PC+1)|(LOAD(PC+2)<<8);\n";
	    $mid =  "\n    PC += 3;\n";
	} elsif ($len == 2) {
#	    $para = "    register p1 = LOAD(PC+1);\n";
	    $mid =  "\n    PC += 2;\n";
	} elsif ($len == 1) {
	    $mid .= "    PC++;\n";
	} else {
	    $mid .= "/* hmm */";
	}

	#$ss =~ s/p2/p1/g;
	#$s =~ s/p2/p1/g;

	if ($ss =~ /p1|p2/ || $s =~ /p1|p2/) {
	    $start .= $para;
	}

	if ($s =~ /src/ || $dd =~ /src/) {
	    if ($ss) {
		if ($pbcfix && $iss eq "M_INDY") {
		    $start .= "\n    p2 = LOAD_ZERO_ADDR(p1);";
		    $start .= "\n    src = LOAD(p2 + YR);\n";
		}
		else {
		    $start .= "    src = $ss;\n";
		}
	    } else {
#		$start .= "    src;\n";
	    }
	}

	# Read-Modify-Write bug implementation -- Jul 20, 1996 EP

	if ($mne =~ /ASL|LSR|ROL|ROR|INC|DEC|SLO|SRE|RLA|RRA|ISB|DCP/) {
	    $rmw = 1;
	    $start .= "\n    rmw_flag = 1;\t/* Read-Modify-Write instruction */\n";
	} else {
	    $rmw = 0;
	}
	
	@b = split("\n", $s);
	$start .= join ("\n", grep (/register|unsigned/,@b)); 
	$mid .= join ("\n",grep (!/register|unsigned/,@b)); 


	# Cycle fix on Page Boundary Crossing
	# ADC, AND, CMP, EOR, LAS, LAX, LDA, LDX, LDY, NOOP, ORA, SBC

	if ($pbcfix && $mne ne "NOOP") {
	    if ($iss eq "M_ABSX") { $reg = "XR"; } else { $reg = "YR"; }
	    $mid .= "\n\tif ((p2 & 0xFF) + $reg > 0x100)\t/* PBC */\n\t\t++clk;\n";
	}

	if ($rmw) { $mid .= "\n    rmw_flag = 0;\n"; }
	
	if ($clk) { $mid .= "    clk += $clk;\n"; }

	print O "void $f () {\n",$start,$mid,$end;
	print C $start,$mid,$end;
	print C "break;\n\n";
	# print $start,$mid,$end;		# debug

	$count++;
    }
    else {
	!/^$|^#/ && print T $_;
    }
}

close (M);

print T "\n";
close (T);
print H "\n";
close (H);
print O "\n";
close (O);

print C "\n\n}\n}\n}\n";

print C "#ifndef NO_UNDOC_CMDS\n\n";
print C "void    u_stoshr(unsigned int val, ADDRESS address, BYTE index)\n";
print C "{\n     val &= (((address >> 8) + 1) & 0xff);\n";
print C "    if (((address & 0xff) + index) > 0xff)\n";
print C "	address = val;\n    STORE((address + index), val);\n}\n";
print C "#endif   /* UNDOC */\n\n";

close (C);
