#!/usr/bin/perl -w
#  about: auto generate comments based on assembly with C-like statements.
# author: David Sobon <dsobon+supramania@gmail.com>
#   date: 16 Apr 2011 (v0.002)
#####
# reference information:
#   http://www.textfiles.com/programming/CARDS/6800
#   http://e-tradition.net/bytes/6502/6502_instruction_set.html
#####
#
# CCR register:
# +---+---+---+---+---+---+---+---+
# | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
# +---+---+---+---+---+---+---+---+
# | - | - | H | I | N | Z | V | C |
# +---+---+---+---+---+---+---+---+
#
# CCR flags:
# H - half (lower nibble) carry bit (eg: triggered by 0b.0000.1111 + 0b.0000.0001)
# I - interrupt enable/disable
# N - negative
# Z - zero
# V - overflow
# C - carry
#
###################################
# Registers
#
# A   - 8 bit accumulator
# B   - 8 bit accumulator
# D   - (A << 8) + B  (??)
# X   - 16 bit index register
# Y   - 16 bit inex register
# SP  - 16 bit stack pointer
# PC  - 16 bit program counter
# CCR - 8 bit condition register.
#
###################################

use strict;

&main();

sub main {
	#
	# Usage.
	#
	if (!@ARGV) {
		print "::: Usage: $0 <file.asm>\n";
		exit(0);
	}
	my $file = shift(@ARGV);

	if (!open(FILE, $file)) {
		print "::: error: could not open file $file - $!\n";
		die();
	}

	#
	# loopy.
	#
	while (<FILE>) {
		chop;
		chop;
		s#;.*?$##;
		s#[\s\t]+$##g;
		my $_line = $_;

		#
		# instruction line.
		#
		if ($_line =~ m#^\t{2}\s{4}#) {
			my $i = &parse_line( $_line );

			if ($i != 0) {
				# i=1
			} else {
				print "::: UNKNOWN: $_line\n";
				# i=0
			}

			next;
		}

		print "::: OTHER: $_\n";
	}
	close(FILE);
}

#####
#  Usage: &parse_line( $_line );
# Return: $i - 1 for match, 0 for no match.
#
sub parse_line {
	my ($_line) = @_;
	my $line    = $_line;
	$line       =~ s#^\t{2}\s{4}##;
	my $pad     = " " x (32 - length($line));

	#
	# mov a, b
	# Register Transfer
	#
	if ($line =~ m#^mov\t(\S), (\S+|\S+ \+ \S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s = %s\n", $_line, $pad, $b, $a;

		return 1;
	}

	#
	# mov a, b
	# Register Exchange
	#
	if ($line =~ m#^xch\t(\S), (\S+|\S+ \+ \S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; _TEMP = %s; %s = %s; %s = _TEMP\n", 
			$_line, $pad, $a, $a, $b, $b;

		return 1;
	}

	#
	# add a, b
	#
	if ($line =~ m#^add\t(\S), (\S+|\S+ \+ \S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s = %s + %s\n", $_line, $pad, $a, $a, $b;

		return 1;
	}

	#
	# addc a, b
	#
	if ($line =~ m#^addc (\S), (\S+|\S+ \+ \S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s = %s + %s + CARRY\n", $_line, $pad, $a, $a, $b;

		return 1;
	}

	#
	# sub a, b
	#
	if ($line =~ m#^sub\t(\S), (\S+|\S+ \+ \S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s = %s - %s  ( %s -= %s )\n", $_line, $pad, $a, $a, $b, $a, $b;

		return 1;
	}

	#
	# subc a, b
	#
	if ($line =~ m#^subc (\S), (\S+|\S+ \+ \S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s = %s - %s - CARRY\n", $_line, $pad, $a, $a, $b;

		return 1;
	}

	#
	# mul a, b
	#
	if ($line =~ m#^mul\t(\S), (\S+|\S+ \+ \S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s = %s * %s\n", $_line, $pad, $a, $a, $b;

		return 1;
	}

	#
	# neg a
	#
	if ($line =~ m#^neg\t(\S)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; %s = -%s  (%s = ~%s + 1)\n", $_line, $pad, $a, $a, $a, $a;

		return 1;
	}

	#
	# inc a
	#
	if ($line =~ m#^inc\t(\S)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; %s++\n", $_line, $pad, $a;

		return 1;
	}

	#
	# dec a
	#
	if ($line =~ m#^dec\t(\S)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; %s--\n", $_line, $pad, $a;

		return 1;
	}

	#
	# clr a
	#
	if ($line =~ m#^clr\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; %s = 0\n", $_line, $pad, $a;

		return 1;
	}

	#
	# shr a
	#
	if ($line =~ m#^shr\t(\S)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; %s >>= 1\n", $_line, $pad, $a;

		return 1;
	}

	#
	# shra a
	# Arithmetic Shift Right
	#
	if ($line =~ m#^shra (\S)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; %s /= 2\n", $_line, $pad, $a;

		return 1;
	}

	#
	# shl a
	#
	if ($line =~ m#^shl\t(\S)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; %s <<= 1\n", $_line, $pad, $a;

		return 1;
	}

	#
	# rorc a
	#
	if ($line =~ m#^rorc (\S)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; rotate.right.carry( %s )\n", $_line, $pad, $a;

		return 1;
	}

	#
	# rolc a
	#
	if ($line =~ m#^rolc (\S)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; rotate.left.carry( %s )\n", $_line, $pad, $a;

		return 1;
	}

	#
	# and a, b
	# todo: convert b to bit mask (eg: 0b.1111.0000 or #1111.0000b)?
	#
	if ($line =~ m#^and\t(\S), (\S+ \+ \S+|\S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s &= %s\n", $_line, $pad, $a, $b;

		return 1;
	}

	#
	# or a, b
	#
	if ($line =~ m#^or\t(\S), (\S+ \+ \S+|\S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s |= %s\n", $_line, $pad, $a, $b;

		return 1;
	}

	#
	# xor a, b
	#
	if ($line =~ m#^xor\t(\S), (\S+ \+ \S+|\S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s ^= %s\n", $_line, $pad, $a, $b;

		return 1;
	}

	#
	# cmp a, b
	#
	if ($line =~ m#^cmp\t(\S+), (\S+ \+ \S+|\S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; D = %s - %s\n", $_line, $pad, $a, $b;

		return 1;
	}

	#
	# cmpb a, b
	#
	if ($line =~ m#^cmpb (\S), (\S+ \+ \S+|\S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; D = %s & %s (??)\n", $_line, $pad, $a, $b;

		return 1;
	}

	#
	# cmpz a
	#
	if ($line =~ m#^cmpz (\S)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; D = %s - 0 (??)\n", $_line, $pad, $a, $a;

		return 1;
	}

	#
	# di
	#
	if ($line =~ m#^di$#) {
		printf "::: DEBUG: %s%s; disable INTERRUPTS (I = 0)\n", $_line, $pad;

		return 1;
	}

	#
	# ei
	#
	if ($line =~ m#^ei$#) {
		printf "::: DEBUG: %s%s; enable INTERRUPTS (I = 1)\n", $_line, $pad;

		return 1;
	}

	#
	# setv
	#
	if ($line =~ m#^setv$#) {
		printf "::: DEBUG: %s%s; set OVERFLOW (V = 1)\n", $_line, $pad;

		return 1;
	}

	#
	# setc
	#
	if ($line =~ m#^setc$#) {
		printf "::: DEBUG: %s%s; set CARRY (C = 1)\n", $_line, $pad;

		return 1;
	}

	#
	# setb a, b
	#
	if ($line =~ m#^setb (\S+), (\S+ \+ \S+|\S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s.%s = 1\n", $_line, $pad, $b, $a;

		return 1;
	}

	#
	# clrb a, b
	#
	if ($line =~ m#^clrb (\S+), (\S+ \+ \S+|\S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s.%s = 0\n", $_line, $pad, $b, $a;

		return 1;
	}

	#
	# ld a, b
	#
	if ($line =~ m#^ld\t(\S+), (\S+ \+ \S+|\S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s = %s\n", $_line, $pad, $a, $b;

		return 1;
	}

	#
	# st a, b
	# NOTE: is this correct??? B = A ???
	#
	if ($line =~ m#^st\t(\S+), (\S+ \+ \S+|\S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; %s = %s\n", $_line, $pad, $b, $a;

		return 1;
	}

	#
	# push a
	#
	if ($line =~ m#^push (\S)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; STACK.push( %s )\n", $_line, $pad, $a;

		return 1;
	}

	#
	# pull a
	#
	if ($line =~ m#^pull (\S)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; STACK.pop( %s )\n", $_line, $pad, $a;

		return 1;
	}

	#
	# tbs a, b
	# Test Bit Set
	#
	if ($line =~ m#^tbs\t(\S+), (\S+)#) {
		my $a = $1;
		my $b = $2;

		printf "::: DEBUG: %s%s; if (%s.%s == 1) then ???\n", $_line, $pad, $b, $a;

		return 1;
	}

	#
	# tbbc a, b, c
	# Test Bit, Branch Clear
	#
	if ($line =~ m#^tbbc (\S+), (\S+),[\t\s](\S+)#) {
		my $a = $1;
		my $b = $2;
		my $c = $3;

		printf "::: DEBUG: %s%s; if (%s.%s == 0) then JUMP to %s\n", $_line, $pad, $b, $a, $c;

		return 1;
	}

	#
	# tbbs a, b, c
	# Test Bit, Branch Set
	#
	if ($line =~ m#^tbbs (\S+), (\S+), (\S+)#) {
		my $a = $1;
		my $b = $2;
		my $c = $3;

		printf "::: DEBUG: %s%s; if (%s.%s == 1) then JUMP to %s\n", $_line, $pad, $b, $a, $c;

		return 1;
	}

	#
	# bcs a
	# Branch Carry Set (C==1)
	#
	if ($line =~ m#^bcs\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; if (CARRY == 1) then JUMP %s\n", $_line, $pad, $a;

		return 1;
	}

	#
	# bcc a
	# Branch Carry Clear (C==0)
	#
	if ($line =~ m#^bcc\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; if (CARRY == 0) then JUMP %s\n", $_line, $pad, $a;

		return 1;
	}

	#
	# beq a
	# Branch Equal (Z==1)
	#
	if ($line =~ m#^beq\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; if (ZERO == 1) then JUMP %s\n", $_line, $pad, $a;

		return 1;
	}

	#
	# bne a
	# Branch Not Equal (Z==0)
	#
	if ($line =~ m#^bne\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; if (ZERO == 0) then JUMP %s\n", $_line, $pad, $a;

		return 1;
	}

	#
	# bgt a
	# Branch Greater Than
	#
	if ($line =~ m#^bgt\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; if (D > 0) then JUMP %s\n", $_line, $pad, $a;

		return 1;
	}

	#
	# ble a
	# Branch Less than or Equal
	#
	if ($line =~ m#^ble\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; if (D <= 0) then JUMP %s\n", $_line, $pad, $a;

		return 1;
	}

	#
	# bmi a
	# Branch on Minus (N==1)
	#
	if ($line =~ m#^bmi\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; if (NEGATIVE == 1) then relative JUMP %s\n", $_line, $pad, $a;

		return 1;
	}

	#
	# bpz a
	# Branch on Plus Zero (N==0)
	#
	if ($line =~ m#^bpz\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; if (NEGATIVE == 0 || D > 0) then JUMP %s\n", $_line, $pad, $a;

		return 1;
	}

	#
	# bsr a
	#
	if ($line =~ m#^bsr\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; relative JUMP to %s then RETURN\n", $_line, $pad, $a;

		return 1;
	}

	#
	# bra a
	#
	if ($line =~ m#^bra\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; relative JUMP to %s\n", $_line, $pad, $a;

		return 1;
	}

	#
	# jmp a
	#
	if ($line =~ m#^jmp\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; JUMP to %s\n", $_line, $pad, $a;

		return 1;
	}

	#
	# jsr a
	#
	if ($line =~ m#^jsr\t(\S+)#) {
		my $a = $1;

		printf "::: DEBUG: %s%s; JUMP to %s then RETURN\n", $_line, $pad, $a;

		return 1;
	}

	#
	# UNKNOWN.
	#
	return 0;
}
