#!/usr/pkg/bin/perl

open FILE, "input.bin" or die $!;

binmode FILE;

$bytecount = 0;


# Addressing modes
#
# 0) (nothing)
# 1) immediate value (#imm) [4,8,16,32]
# 2) compressed direct address (dir)
# 3) direct address (addr16)
# 4) register direct (Ri, RWi, RLi,
#		A, AL, SP, PCB, DTB, USB, SSB, ADB, DPR,
#		PS, CCR, RP, ILM)
# 5) register indirect (@RWj 0<=j<=3)
# 6) register indirect with post-incrementing (@RWj + 0<=j<=3)
# 7) register indirect with displacement (@RWi + disp8  0<=i<=7)
#					 (@RWj + disp16 0<=j<=3)
# 8) register indirect with base index (@RW0 + RW7, @RW1 + RW7)
# 9) program counter indirect with displacement (@PC + disp16)
#10) accumulator indirect (@A)
#11) direct I/O (io)
#12) long register indirect with displacement (@RLi + disp8 0<=i<=3)
#13) compressed direct bit address (dir:bp)
#14) I/O direct bit address (io:bp)
#15) direct bit address (addr16:bp)
#16) register list (rlst)
#17) program counter relative branching address (rel)
#18) direct branching address (addr16)
#19) physical direct branching address (addr24)
#20) accumulator indirect branching address (@A)
#21) vector address (#vct)
#22) indirect specification branching address (@ear)
#23) indirect specification branching address (@eam)


# Instructions which should terminate disassembly:
#  RET
#  RETP
#  RETI
#  BRA
#  JMP
#  JMPP
#  Undefined instruction

@memory = ();
@targets = (0xff19f6);
$stopat = 0xffc645;

$baseaddr = 0xff0000;

%labels = {};
$labelnum = 0;

if (open LABELS, "<input.labels") {
    while (<LABELS>) {
	chomp;
	if (($name, $addr) = /(.*): (.*)/) {
	    $addr = hex $addr;
	    $labels{$addr} = $name;
	    if ($name =~ /^L(\d+)$/) {
		$labelnum = $1 + 1 if ($labelnum >= $1);
	    }
	}
    }
}

close LABELS;

$output = 0;

%io = (
	0x01 => "PDR1",
	0x02 => "PDR2",
	0x03 => "PDR3",
	0x04 => "PDR3",
	0x05 => "PDR3",
	0x06 => "PDR3",
	0x07 => "PDR3",
	0x08 => "PDR3",
	0x09 => "PDR3",
	0x0A => "PDRA",
	0x11 => "DDR1",
	0x12 => "DDR2",
	0x13 => "DDR3",
	0x14 => "DDR4",
	0x15 => "DDR5",
	0x16 => "ADER",
	0x17 => "DDR7",
	0x18 => "DDR8",
	0x19 => "DDR9",
	0x1A => "DDRA",
	0x20 => "SMR0",
	0x21 => "SCR0",
	0x22 => "S[IO]DR0",
	0x23 => "SSR0",
	0x24 => "SMR1",
	0x25 => "SCR1",
	0x26 => "S[IO]DR1",
	0x27 => "SSR1",
	0x28 => "ENIR",
	0x29 => "EIRR",
	0x2A => "ELVRl",
	0x2B => "ELVRh",
	0x2C => "ADCSl",
	0x2D => "ADCSh",
	0x2E => "ADCRl",
	0x2F => "ADCRh",
	0x30 => "PPGC0",
	0x31 => "PPGC1",
	0x34 => "PRL0l",
	0x35 => "PRL0h",
	0x36 => "PRL1l",
	0x37 => "PRL1h",
	0x38 => "TMCSR0l",
	0x39 => "TMCSR0h",
	0x3A => "TMR(LR)0l",
	0x3B => "TMR(LR)0h",
	0x3C => "TMCSR1l",
	0x3D => "TMCSR1h",
	0x3E => "TMR(LR)1l",
	0x3F => "TMR(LR)1h",
	0x44 => "SMR2",
	0x45 => "SCR2",
	0x46 => "S[IO]DR2",
	0x47 => "SSR2",
	0x48 => "CSCR0",
	0x49 => "CSCR1",
	0x4A => "CSCR2",
	0x4B => "CSCR3",
	0x4C => "CSCR4",
	0x4D => "CSCR5",
	0x4E => "CSCR6",
	0x4F => "CSCR7",
	0x51 => "CDCR0",
	0x53 => "CDCR1",
	0x9F => "DIRR",
	0xA0 => "LPMCR",
	0xA1 => "CKSCR",
	0xA5 => "ARSR",
	0xA6 => "HACR",
	0xA7 => "ECSR",
	0xA8 => "WDTC",
	0xA9 => "TBTC",
	0xB0 => "ICR00",
	0xB1 => "ICR01",
	0xB2 => "ICR02",
	0xB3 => "ICR03",
	0xB4 => "ICR04",
	0xB5 => "ICR05",
	0xB6 => "ICR06",
	0xB7 => "ICR07",
	0xB8 => "ICR08",
	0xB9 => "ICR09",
	0xBA => "ICR10",
	0xBB => "ICR11",
	0xBC => "ICR22",
	0xBD => "ICR33",
	0xBE => "ICR44",
	0xBF => "ICR15",
# Specific to LC2412
	0xC0 => "KEY0",
	0xC1 => "KEY1",
	0xC2 => "KEY2",
	0xC3 => "KEY3",
	0xC4 => "KEY4",
	0xC5 => "LEDC",
	0xC6 => "LCD",
	0xC8 => "PC_ADL",
	0xC9 => "PC_ADM",
	0xCA => "PC_ADH",
	0xCB => "PC_DW",
	0xCC => "PC_DR",
	0xCD => "PC_CR",
);


while (1)
{
    my $byte = getc FILE;
    last unless defined($byte);
    $byte = ord($byte);
    push(@memory, $byte);
}

close(FILE);

@initialtargets = @targets;

for my $pass (0..1) {
    @targets = @initialtargets;
    %targets_all = {};
    foreach $target (@targets) {
        $targets_all{$target} = 1;
    }

    $output = $pass;
    while (@targets) {
        $target = shift @targets;
        printf "Disassembling from %x:\n", $target if $output;
        disassemble($target);
	@targets = (); # XXX stop here.
    }
}

open LABELS, ">input.labels" or die $!;

foreach my $addr (sort keys %labels) {
    printf LABELS "%s: %.6x\n", $labels{$addr}, $addr
	if defined($labels{$addr});
}

close LABELS;

sub disassemble
{
    my ($start) = @_;
    setaddress($start);
    $finish_disassembly = 0;
    $newline = 0;
    while (!$finish_disassembly)
    {
        local @instr = ();
        my $byte = getbyte();
        last unless defined($byte);
        local $address = $bytecount-1;
        decode_instruction($byte);
	$finish_disassembly = 1 if ($address >= $stopat);
    }
}

sub setaddress
{
    my ($address) = @_;
    $bytecount = $address;
}

sub getbyte
{
    return undef if ($bytecount - $baseaddr > @memory);
    return undef if ($bytecount - $baseaddr < 0);
    my $ret = $memory[$bytecount++ - $baseaddr];
    push(@instr, $ret);
    return $ret;
}

sub decode_instruction
{
    my ($byte) = @_;
    local $amode = undef;
    local @operands = ();
    local $comment = "";

    if      ($byte == 0x00) {
	output_instruction("NOP");
    } elsif ($byte == 0x01) {
	output_instruction("INT9");
    } elsif ($byte == 0x02) {
	set_operand("A");
	output_instruction("ADDDC");
    } elsif ($byte == 0x03) {
	set_operand("A");
	output_instruction("NEG");
    } elsif ($byte == 0x04) {
	output_instruction("PCB");
    } elsif ($byte == 0x05) {
	output_instruction("DTB");
    } elsif ($byte == 0x06) {
	output_instruction("ADB");
    } elsif ($byte == 0x07) {
	output_instruction("SDB");
    } elsif ($byte == 0x08) {
	read_operand_imm8();
	output_instruction("LINK");
    } elsif ($byte == 0x09) {
	output_instruction("UNLINK");
    } elsif ($byte == 0x0A) {
	set_operand("RP");
	read_operand_imm8();
	output_instruction("MOV");
    } elsif ($byte == 0x0B) {
	set_operand("A");
	output_instruction("NEGW");
    } elsif ($byte == 0x0C) {
	set_operand("A");
	output_instruction("LSLW");
    } elsif ($byte == 0x0E) {
	set_operand("A");
	output_instruction("ASRW");
    } elsif ($byte == 0x0F) {
	set_operand("A");
	output_instruction("LSRW");
    } elsif ($byte == 0x10) {
	output_instruction("CMR");
    } elsif ($byte == 0x11) {
	output_instruction("NCC");
    } elsif ($byte == 0x12) {
	set_operand("A");
	output_instruction("SUBDC");
    } elsif ($byte == 0x13) {
	set_operand('@A');
	output_instruction("JCTX");
    } elsif ($byte == 0x14) {
	output_instruction("EXT");
    } elsif ($byte == 0x15) {
	output_instruction("ZEXT");
    } elsif ($byte == 0x16) {
	output_instruction("SWAP");
    } elsif ($byte == 0x17) {
	read_operand_imm8();
	output_instruction("ADDSP");
    } elsif ($byte == 0x18) {
	set_operand("A");
	read_operand_imm32();
	output_instruction("ADDL");
    } elsif ($byte == 0x19) {
	set_operand("A");
	read_operand_imm32();
	output_instruction("SUBL");
    } elsif ($byte == 0x1A) {
	set_operand("ILM");
	read_operand_imm8();
	output_instruction("MOV");
    } elsif ($byte == 0x1B) {
	set_operand("A");
	read_operand_imm32();
	output_instruction("CMPL");
    } elsif ($byte == 0x1C) {
	output_instruction("EXTW");
    } elsif ($byte == 0x1D) {
	output_instruction("ZEXTW");
    } elsif ($byte == 0x1E) {
	output_instruction("SWAPW");
    } elsif ($byte == 0x1F) {
	read_operand_imm16();
	output_instruction("ADDSP");
    } elsif ($byte == 0x20) {
	set_operand("A");
	read_operand_dir();
	output_instruction("ADD");
    } elsif ($byte == 0x21) {
	set_operand("A");
	read_operand_dir();
	output_instruction("SUB");
    } elsif ($byte == 0x22) {
	set_operand("A");
	output_instruction("ADDC");
    } elsif ($byte == 0x23) {
	set_operand("A");
	output_instruction("CMP");
    } elsif ($byte == 0x24) {
	set_operand("CCR");
	read_operand_imm8();
	output_instruction("AND");
    } elsif ($byte == 0x25) {
	set_operand("CCR");
	read_operand_imm8();
	output_instruction("OR");
    } elsif ($byte == 0x26) {
	set_operand("A");
	output_instruction("DIVU");
    } elsif ($byte == 0x27) {
	set_operand("A");
	output_instruction("MULU");
    } elsif ($byte == 0x28) {
	set_operand("A");
	output_instruction("ADDW");
    } elsif ($byte == 0x29) {
	set_operand("A");
	output_instruction("SUBW");
    } elsif ($byte == 0x2A) {
	set_operand("A");
	read_operand_imm8();
	read_operand_rel();
	output_instruction("CBNE");
    } elsif ($byte == 0x2B) {
	set_operand("A");
	output_instruction("CMPW");
    } elsif ($byte == 0x2C) {
	set_operand("A");
	output_instruction("ANDW");
    } elsif ($byte == 0x2D) {
	set_operand("A");
	output_instruction("ORW");
    } elsif ($byte == 0x2E) {
	set_operand("A");
	output_instruction("XORW");
    } elsif ($byte == 0x2F) {
	set_operand("A");
	output_instruction("MULUW");
    } elsif ($byte == 0x30) {
	set_operand("A");
	read_operand_imm8();
	output_instruction("ADD");
    } elsif ($byte == 0x31) {
	set_operand("A");
	read_operand_imm8();
	output_instruction("SUB");
    } elsif ($byte == 0x32) {
	set_operand("A");
	output_instruction("SUBC");
    } elsif ($byte == 0x33) {
	set_operand("A");
	read_operand_imm8();
	output_instruction("CMP");
    } elsif ($byte == 0x34) {
	set_operand("A");
	read_operand_imm8();
	output_instruction("AND");
    } elsif ($byte == 0x35) {
	set_operand("A");
	read_operand_imm8();
	output_instruction("OR");
    } elsif ($byte == 0x36) {
	set_operand("A");
	read_operand_imm8();
	output_instruction("XOR");
    } elsif ($byte == 0x37) {
	set_operand("A");
	output_instruction("NOT");
    } elsif ($byte == 0x38) {
	set_operand("A");
	read_operand_imm16();
	output_instruction("ADDW");
    } elsif ($byte == 0x39) {
	set_operand("A");
	read_operand_imm16();
	output_instruction("SUBW");
    } elsif ($byte == 0x3A) {
	set_operand("A");
	read_operand_imm16();
	read_operand_rel();
	output_instruction("CWBNE");
    } elsif ($byte == 0x3B) {
	set_operand("A");
	read_operand_imm16();
	output_instruction("CMPW");
    } elsif ($byte == 0x3C) {
	set_operand("A");
	read_operand_imm16();
	output_instruction("ANDW");
    } elsif ($byte == 0x3D) {
	set_operand("A");
	read_operand_imm16();
	output_instruction("ORW");
    } elsif ($byte == 0x3E) {
	set_operand("A");
	read_operand_imm16();
	output_instruction("XORW");
    } elsif ($byte == 0x3F) {
	set_operand("A");
	output_instruction("NOTW");
    } elsif ($byte == 0x40) {
	set_operand("A");
	read_operand_dir();
	output_instruction("MOV");
    } elsif ($byte == 0x41) {
	read_operand_dir();
	set_operand("A");
	output_instruction("MOV");
    } elsif ($byte == 0x42) {
	set_operand("A");
	read_operand_imm8();
	output_instruction("MOV");
    } elsif ($byte == 0x43) {
	set_operand("A");
	read_operand_imm8();
	output_instruction("MOVX");
    } elsif ($byte == 0x44) {
	read_operand_dir();
	read_operand_imm8();
	output_instruction("MOV");
    } elsif ($byte == 0x45) {
	set_operand("A");
	read_operand_dir();
	output_instruction("MOVX");
    } elsif ($byte == 0x46) {
	set_operand("A");
	set_operand("SP");
	output_instruction("MOVW");
    } elsif ($byte == 0x47) {
	set_operand("SP");
	set_operand("A");
	output_instruction("MOVW");
    } elsif ($byte == 0x48) {
	set_operand("A");
	read_operand_dir();
	output_instruction("MOVW");
    } elsif ($byte == 0x49) {
	read_operand_dir();
	set_operand("A");
	output_instruction("MOVW");
    } elsif ($byte == 0x4A) {
	set_operand("A");
	read_operand_imm16();
	output_instruction("MOVW");
    } elsif ($byte == 0x4B) {
	set_operand("A");
	read_operand_imm32();
	output_instruction("MOVL");
    } elsif ($byte == 0x4C) {
	set_operand("A");
	output_instruction("PUSHW");
    } elsif ($byte == 0x4D) {
	set_operand("AH");
	output_instruction("PUSHW");
    } elsif ($byte == 0x4E) {
	set_operand("PS");
	output_instruction("PUSHW");
    } elsif ($byte == 0x4F) {
	read_operand_rlst();
	output_instruction("PUSHW");
    } elsif ($byte == 0x50) {
	set_operand("A");
	read_operand_io();
	output_instruction("MOV");
    } elsif ($byte == 0x51) {
	read_operand_io();
	set_operand("A");
	output_instruction("MOV");
    } elsif ($byte == 0x52) {
	set_operand("A");
	read_operand_addr16();
	output_instruction("MOV");
    } elsif ($byte == 0x53) {
	read_operand_addr16();
	set_operand("A");
	output_instruction("MOV");
    } elsif ($byte == 0x54) {
	read_operand_io();
	read_operand_imm8();
	output_instruction("MOV");
    } elsif ($byte == 0x55) {
	set_operand("A");
	read_operand_io();
	output_instruction("MOVX");
    } elsif ($byte == 0x56) {
	read_operand_io();
	read_operand_imm16();
	output_instruction("MOVW");
    } elsif ($byte == 0x57) {
	set_operand("A");
	read_operand_addr16();
	output_instruction("MOVX");
    } elsif ($byte == 0x58) {
	set_operand("A");
	read_operand_io();
	output_instruction("MOVW");
    } elsif ($byte == 0x59) {
	read_operand_io();
	set_operand("A");
	output_instruction("MOVW");
    } elsif ($byte == 0x5A) {
	set_operand("A");
	read_operand_addr16();
	output_instruction("MOVW");
    } elsif ($byte == 0x5B) {
	read_operand_addr16();
	set_operand("A");
	output_instruction("MOVW");
    } elsif ($byte == 0x5C) {
	set_operand("A");
	output_instruction("POPW");
    } elsif ($byte == 0x5D) {
	set_operand("AH");
	output_instruction("POPW");
    } elsif ($byte == 0x5E) {
	set_operand("PS");
	output_instruction("POPW");
    } elsif ($byte == 0x5F) {
	read_operand_rlst();
	output_instruction("POPW");
    } elsif ($byte == 0x60) {
	read_operand_rel();
	unconditional_branch();
	output_instruction("BRA");
    } elsif ($byte == 0x61) {
	set_operand('@A');
	unconditional_branch();
	output_instruction("JMP");
    } elsif ($byte == 0x62) {
	read_operand_addr16();
	unconditional_branch();
	output_instruction("JMP");
    } elsif ($byte == 0x63) {
	read_operand_addr24();
	unconditional_branch();
	output_instruction("JMPP");
    } elsif ($byte == 0x64) {
	read_operand_addr16();
	output_instruction("CALL");
    } elsif ($byte == 0x65) {
	read_operand_addr24();
	output_instruction("CALL");
    } elsif ($byte == 0x66) {
	unconditional_branch();
	output_instruction("RETP");
    } elsif ($byte == 0x67) {
	unconditional_branch();
	output_instruction("RET");
    } elsif ($byte == 0x68) {
	read_operand_vct8();
	output_instruction("INT");
    } elsif ($byte == 0x68) {
	read_operand_vct8();
	output_instruction("INT");
    } elsif ($byte == 0x69) {
	read_operand_addr16();
	output_instruction("INT");
    } elsif ($byte == 0x6A) {
	read_operand_addr24();
	output_instruction("INTP");
    } elsif ($byte == 0x6B) {
	unconditional_branch();
	output_instruction("RETI");
    } elsif ($byte == 0x6C) {
	decode_bit_manipulation();
    } elsif ($byte == 0x6E) {
	decode_string_manipulation();
    } elsif ($byte == 0x6F) {
	decode_two_byte();
    } elsif ($byte == 0x70) {
	decode_ea_70();
    } elsif ($byte == 0x71) {
	decode_ea_71();
    } elsif ($byte == 0x72) {
	decode_ea_72();
    } elsif ($byte == 0x73) {
	decode_ea_73();
    } elsif ($byte == 0x74) {
	decode_ea_74();
    } elsif ($byte == 0x75) {
	decode_ea_75();
    } elsif ($byte == 0x76) {
	decode_ea_76();
    } elsif ($byte == 0x77) {
	decode_ea_77();
    } elsif ($byte == 0x78) {
	decode_ea_78();
    } elsif ($byte == 0x79) {
	decode_ea_79();
    } elsif ($byte == 0x7A) {
	decode_ea_7a();
    } elsif ($byte == 0x7B) {
	decode_ea_7b();
    } elsif ($byte == 0x7C) {
	decode_ea_7c();
    } elsif ($byte == 0x7D) {
	decode_ea_7d();
    } elsif ($byte == 0x7E) {
	decode_ea_7e();
    } elsif ($byte == 0x7F) {
	decode_ea_7f();
    } elsif (($byte & 0xf8) == 0x80) {
	set_operand("A");
	set_operand_register("R", $byte & 0x7);
	output_instruction("MOV");
    } elsif (($byte & 0xf8) == 0x88) {
	set_operand("A");
	set_operand_register("RW", $byte & 0x7);
	output_instruction("MOVW");
    } elsif (($byte & 0xf8) == 0x90) {
	set_operand_register("R", $byte & 0x7);
	set_operand("A");
	output_instruction("MOV");
    } elsif (($byte & 0xf8) == 0x98) {
	set_operand_register("RW", $byte & 0x7);
	set_operand("A");
	output_instruction("MOVW");
    } elsif (($byte & 0xf8) == 0xA0) {
	set_operand_register("R", $byte & 0x7);
	read_operand_imm8();
	output_instruction("MOV");
    } elsif (($byte & 0xf8) == 0xA8) {
	set_operand_register("RW", $byte & 0x7);
	read_operand_imm16();
	output_instruction("MOVW");
    } elsif (($byte & 0xf8) == 0xB0) {
	set_operand("A");
	set_operand_register("R", $byte & 0x7);
	output_instruction("MOVX");
    } elsif (($byte & 0xf8) == 0xB8) {
	set_operand("A");
	set_operand_register_disp8("RW", $byte & 0x7);
	output_instruction("MOVW");
    } elsif (($byte & 0xf8) == 0xC0) {
	set_operand("A");
	set_operand_register_disp8("RW", $byte & 0x7);
	output_instruction("MOVX");
    } elsif (($byte & 0xf8) == 0xC8) {
	set_operand_register_disp8("RW", $byte & 0x7);
	set_operand("A");
	output_instruction("MOVW");
    } elsif (($byte & 0xf0) == 0xD0) {
	set_operand("A");
	set_operand_imm4($byte & 0xf);
	output_instruction("MOVN");
    } elsif (($byte & 0xf0) == 0xE0) {
	set_operand_imm4($byte & 0xf);
	output_instruction("CALLV");
    } elsif ($byte == 0xF0) {
	read_operand_rel();
	output_instruction("BZ");
    } elsif ($byte == 0xF1) {
	read_operand_rel();
	output_instruction("BNZ");
    } elsif ($byte == 0xF2) {
	read_operand_rel();
	output_instruction("BC");
    } elsif ($byte == 0xF3) {
	read_operand_rel();
	output_instruction("BNC");
    } elsif ($byte == 0xF4) {
	read_operand_rel();
	output_instruction("BN");
    } elsif ($byte == 0xF5) {
	read_operand_rel();
	output_instruction("BP");
    } elsif ($byte == 0xF6) {
	read_operand_rel();
	output_instruction("BV");
    } elsif ($byte == 0xF7) {
	read_operand_rel();
	output_instruction("BNV");
    } elsif ($byte == 0xF8) {
	read_operand_rel();
	output_instruction("BT");
    } elsif ($byte == 0xF9) {
	read_operand_rel();
	output_instruction("BNT");
    } elsif ($byte == 0xFA) {
	read_operand_rel();
	output_instruction("BLT");
    } elsif ($byte == 0xFB) {
	read_operand_rel();
	output_instruction("BGE");
    } elsif ($byte == 0xFC) {
	read_operand_rel();
	output_instruction("BLE");
    } elsif ($byte == 0xFD) {
	read_operand_rel();
	output_instruction("BGT");
    } elsif ($byte == 0xFE) {
	read_operand_rel();
	output_instruction("BLS");
    } elsif ($byte == 0xFF) {
	read_operand_rel();
	output_instruction("BHI");
    } else {
	undefined_instruction("first byte");
    }
}

sub decode_bit_manipulation
{
    my $byte = getbyte();
    if (($byte & 0xf8) == 0x00) {
	set_operand("A");
	read_operand_io_bp($byte & 0x7);
	output_instruction("MOVB");
    } elsif (($byte & 0xf8) == 0x08) {
	set_operand("A");
	read_operand_dir_bp($byte & 0x7);
	output_instruction("MOVB");
    } elsif (($byte & 0xf8) == 0x18) {
	set_operand("A");
	read_operand_addr16_bp($byte & 0x7);
	output_instruction("MOVB");
    } elsif (($byte & 0xf8) == 0x20) {
	read_operand_io_bp($byte & 0x7);
	set_operand("A");
	output_instruction("MOVB");
    } elsif (($byte & 0xf8) == 0x28) {
	read_operand_dir_bp($byte & 0x7);
	set_operand("A");
	output_instruction("MOVB");
    } elsif (($byte & 0xf8) == 0x38) {
	read_operand_addr16_bp($byte & 0x7);
	set_operand("A");
	output_instruction("MOVB");
    } elsif (($byte & 0xf8) == 0x40) {
	read_operand_io_bp($byte & 0x7);
	output_instruction("CLRB");
    } elsif (($byte & 0xf8) == 0x48) {
	read_operand_dir_bp($byte & 0x7);
	output_instruction("CLRB");
    } elsif (($byte & 0xf8) == 0x58) {
	read_operand_addr16_bp($byte & 0x7);
	output_instruction("CLRB");
    } elsif (($byte & 0xf8) == 0x60) {
	read_operand_io_bp($byte & 0x7);
	output_instruction("SETB");
    } elsif (($byte & 0xf8) == 0x68) {
	read_operand_dir_bp($byte & 0x7);
	output_instruction("SETB");
    } elsif (($byte & 0xf8) == 0x78) {
	read_operand_addr16_bp($byte & 0x7);
	output_instruction("SETB");
    } elsif (($byte & 0xf8) == 0x80) {
	read_operand_io_bp($byte & 0x7);
	read_operand_rel();
	output_instruction("BBC");
    } elsif (($byte & 0xf8) == 0x88) {
	read_operand_dir_bp($byte & 0x7);
	read_operand_rel();
	output_instruction("BBC");
    } elsif (($byte & 0xf8) == 0x98) {
	read_operand_addr16_bp($byte & 0x7);
	read_operand_rel();
	output_instruction("BBC");
    } elsif (($byte & 0xf8) == 0xA0) {
	read_operand_io_bp($byte & 0x7);
	read_operand_rel();
	output_instruction("BBS");
    } elsif (($byte & 0xf8) == 0xA8) {
	read_operand_dir_bp($byte & 0x7);
	read_operand_rel();
	output_instruction("BBS");
    } elsif (($byte & 0xf8) == 0xB8) {
	read_operand_addr16_bp($byte & 0x7);
	read_operand_rel();
	output_instruction("BBS");
    } elsif (($byte & 0xf8) == 0xC0) {
	read_operand_io_bp($byte & 0x7);
	output_instruction("WBTS");
    } elsif (($byte & 0xf8) == 0xE0) {
	read_operand_io_bp($byte & 0x7);
	output_instruction("WBTC");
    } elsif (($byte & 0xf8) == 0xF8) {
	read_operand_io_bp($byte & 0x7);
	output_instruction("SBBS");
    } else {
	undefined_instruction("bit manipulation");
    }
}

sub decode_string_manipulation
{
    my $byte = getbyte();
    my $arg1 = ($byte & 0xc) >> 2;
    my $arg2 = ($byte & 0x3);
    my $op = ($byte) >> 4;
    my @r = ("PCB", "DTB", "ADB", "SPB");
    my @i = ("MOVSI", "MOVSD", "MOVSWI", "MOVSWD", "", "", "", "",
	"SCEQI", "SCEQD", "SCWEQI", "SCWEQD", "FILSI", "", "FILSWI", "");
    if ($op < 4) {
	set_operand($r[$arg1]);
	set_operand($r[$arg2]);
	output_instruction($i[$op]);
    } elsif (((($op >= 8) && ($op <= 0xc)) || ($op == 0xe)) &&
	($arg1 == 0)) {
	set_operand($r[$arg2]);
	output_instruction($i[$op]);
    } else {
	undefined_instruction("string manipulation");
    }
}

sub decode_two_byte
{
    my $byte = getbyte();
    if ($byte == 0x00) {
	set_operand("A");
	set_operand("DTB");
	output_instruction("MOV");
    } elsif ($byte == 0x01) {
	set_operand("A");
	set_operand("ADB");
	output_instruction("MOV");
    } elsif ($byte == 0x02) {
	set_operand("A");
	set_operand("SSB");
	output_instruction("MOV");
    } elsif ($byte == 0x03) {
	set_operand("A");
	set_operand("USB");
	output_instruction("MOV");
    } elsif ($byte == 0x04) {
	set_operand("A");
	set_operand("DPR");
	output_instruction("MOV");
    } elsif ($byte == 0x05) {
	set_operand("A");
	set_operand('@A');
	output_instruction("MOV");
    } elsif ($byte == 0x06) {
	set_operand("A");
	set_operand("PCB");
	output_instruction("MOV");
    } elsif ($byte == 0x07) {
	set_operand("A");
	output_instruction("ROLC");
    } elsif ($byte == 0x0C) {
	set_operand("A");
	set_operand_register("R", 0);
	output_instruction("LSLW");
    } elsif ($byte == 0x0D) {
	set_operand("A");
	set_operand('@A');
	output_instruction("MOVW");
    } elsif ($byte == 0x0E) {
	set_operand("A");
	set_operand_register("R", 0);
	output_instruction("ASRW");
    } elsif ($byte == 0x0F) {
	set_operand("A");
	set_operand_register("R", 0);
	output_instruction("LSRW");
    } elsif ($byte == 0x10) {
	set_operand("DTB");
	set_operand("A");
	output_instruction("MOV");
    } elsif ($byte == 0x11) {
	set_operand("ADB");
	set_operand("A");
	output_instruction("MOV");
    } elsif ($byte == 0x12) {
	set_operand("SSB");
	set_operand("A");
	output_instruction("MOV");
    } elsif ($byte == 0x13) {
	set_operand("USB");
	set_operand("A");
	output_instruction("MOV");
    } elsif ($byte == 0x14) {
	set_operand("DPR");
	set_operand("A");
	output_instruction("MOV");
    } elsif ($byte == 0x15) {
	set_operand('@AL');
	set_operand("AH");
	output_instruction("MOV");
    } elsif ($byte == 0x16) {
	set_operand("A");
	set_operand('@A');
	output_instruction("MOV");
    } elsif ($byte == 0x17) {
	set_operand("A");
	output_instruction("RORC");
    } elsif ($byte == 0x1C) {
	set_operand("A");
	set_operand_register("R", 0);
	output_instruction("LSLL");
    } elsif ($byte == 0x1D) {
	set_operand('@AL');
	set_operand("AH");
	output_instruction("MOVW");
    } elsif ($byte == 0x1E) {
	set_operand("A");
	set_operand_register("R", 0);
	output_instruction("ASRL");
    } elsif ($byte == 0x1F) {
	set_operand("A");
	set_operand_register("R", 0);
	output_instruction("LSRL");
    } elsif ($byte == 0x2C) {
	set_operand("A");
	set_operand_register("R", 0);
	output_instruction("LSL");
    } elsif ($byte == 0x2D) {
	set_operand("A");
	set_operand_register("R", 0);
	output_instruction("NRML");
    } elsif ($byte == 0x2E) {
	set_operand("A");
	set_operand_register("R", 0);
	output_instruction("ASR");
    } elsif ($byte == 0x2F) {
	set_operand("A");
	set_operand_register("R", 0);
	output_instruction("LSR");
    } elsif (($byte & 0xf9) == 0x20) {
	set_operand("A");
	set_operand_register_disp8("RL", ($byte & 7) >>1);
	output_instruction("MOVX");
    } elsif (($byte & 0xf9) == 0x30) {
	set_operand_register_disp8("RL", ($byte & 7) >>1);
	set_operand("A");
	output_instruction("MOV");
    } elsif (($byte & 0xf9) == 0x38) {
	set_operand_register_disp8("RL", ($byte & 7) >>1);
	set_operand("A");
	output_instruction("MOVW");
    } elsif (($byte & 0xf9) == 0x40) {
	set_operand("A");
	set_operand_register_disp8("RL", ($byte & 7) >>1);
	output_instruction("MOV");
    } elsif (($byte & 0xf9) == 0x48) {
	set_operand("A");
	set_operand_register_disp8("RL", ($byte & 7) >>1);
	output_instruction("MOVW");
    } else {
	undefined_instruction("two byte");
    }
}

sub decode_ea_70
{
    my $byte = getbyte();
    my $op = $byte & 0xe0;
    if ($op == 0x00) {
	set_operand("A");
	set_operand_ea("RL", $byte);
	output_instruction("ADDL");
    } elsif ($op == 0x20) {
	set_operand("A");
	set_operand_ea("RL", $byte);
	output_instruction("SUBL");
    } elsif ($op == 0x40) {
	set_operand_ea("RW", $byte);
	read_operand_imm16();
	read_operand_rel();
	output_instruction("CWBNE");
    } elsif ($op == 0x60) {
	set_operand("A");
	set_operand_ea("RL", $byte);
	output_instruction("CMPL");
    } elsif ($op == 0x80) {
	set_operand("A");
	set_operand_ea("RL", $byte);
	output_instruction("ANDL");
    } elsif ($op == 0xA0) {
	set_operand("A");
	set_operand_ea("RL", $byte);
	output_instruction("ORL");
    } elsif ($op == 0xC0) {
	set_operand("A");
	set_operand_ea("RL", $byte);
	output_instruction("XORL");
    } elsif ($op == 0xE0) {
	set_operand_ea("R", $byte);
	read_operand_imm8();
	read_operand_rel();
	output_instruction("CBNE");
    } else {
	undefined_instruction("ea (70)");
    }
}

sub decode_ea_71
{
    my $byte = getbyte();
    my $op = $byte & 0xe0;
    if ($op == 0x00) {
	set_operand_ea('@RL', $byte);
	unconditional_branch();
	output_instruction("JMPP");
    } elsif ($op == 0x20) {
	set_operand_ea('@RL', $byte);
	output_instruction("CALLP");
    } elsif ($op == 0x40) {
	set_operand_ea("RL", $byte);
	output_instruction("INCL");
    } elsif ($op == 0x60) {
	set_operand_ea("RL", $byte);
	output_instruction("DECL");
    } elsif ($op == 0x80) {
	set_operand("A");
	set_operand_ea("RL", $byte);
	output_instruction("MOVL");
    } elsif ($op == 0xA0) {
	set_operand_ea("RL", $byte);
	set_operand("A");
	output_instruction("MOVL");
    } elsif ($op == 0xC0) {
	set_operand_ea("R", $byte);
	read_operand_imm8();
	output_instruction("MOV");
    } elsif ($op == 0xE0) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("MOVEA");
    } else {
	undefined_instruction("ea (71)");
    }
}

sub decode_ea_72
{
    my $byte = getbyte();
    my $op = $byte & 0xe0;
    if ($op == 0x00) {
	set_operand_ea("R", $byte);
	output_instruction("ROLC");
    } elsif ($op == 0x20) {
	set_operand_ea("R", $byte);
	output_instruction("RORC");
    } elsif ($op == 0x40) {
	set_operand_ea("R", $byte);
	output_instruction("INC");
    } elsif ($op == 0x60) {
	set_operand_ea("R", $byte);
	output_instruction("DEC");
    } elsif ($op == 0x80) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("MOV");
    } elsif ($op == 0xA0) {
	set_operand_ea("R", $byte);
	set_operand("A");
	output_instruction("MOV");
    } elsif ($op == 0xC0) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("MOVX");
    } elsif ($op == 0xE0) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("XCH");
    } else {
	undefined_instruction("ea (72)");
    }
}

sub decode_ea_73
{
    my $byte = getbyte();
    my $op = $byte & 0xe0;
    if ($op == 0x00) {
	set_operand_ea('@RW', $byte);
	unconditional_branch();
	output_instruction("JMP");
    } elsif ($op == 0x20) {
	set_operand_ea('@RW', $byte);
	output_instruction("CALL");
    } elsif ($op == 0x40) {
	set_operand_ea("RW", $byte);
	output_instruction("INCW");
    } elsif ($op == 0x60) {
	set_operand_ea("RW", $byte);
	output_instruction("DECW");
    } elsif ($op == 0x80) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("MOVW");
    } elsif ($op == 0xA0) {
	set_operand_ea("RW", $byte);
	set_operand("A");
	output_instruction("MOVW");
    } elsif ($op == 0xC0) {
	set_operand_ea("RW", $byte);
	read_operand_imm16();
	output_instruction("MOVW");
    } elsif ($op == 0xE0) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("XCHW");
    } else {
	undefined_instruction("ea (73)");
    }
}

sub decode_ea_74
{
    my $byte = getbyte();
    my $op = $byte & 0xe0;
    if ($op == 0x00) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("ADD");
    } elsif ($op == 0x20) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("SUB");
    } elsif ($op == 0x40) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("ADDC");
    } elsif ($op == 0x60) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("CMP");
    } elsif ($op == 0x80) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("AND");
    } elsif ($op == 0xA0) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("OR");
    } elsif ($op == 0xC0) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("XOR");
    } elsif ($op == 0xE0) {
	set_operand_ea("R", $byte);
	read_operand_rel();
	output_instruction("DBNZ");
    } else {
	undefined_instruction("ea (74)");
    }
}

sub decode_ea_75
{
    my $byte = getbyte();
    my $op = $byte & 0xe0;
    if ($op == 0x00) {
	set_operand_ea("R", $byte);
	set_operand("A");
	output_instruction("ADD");
    } elsif ($op == 0x20) {
	set_operand_ea("R", $byte);
	set_operand("A");
	output_instruction("SUB");
    } elsif ($op == 0x40) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("SUBC");
    } elsif ($op == 0x60) {
	set_operand_ea("R", $byte);
	output_instruction("NEG");
    } elsif ($op == 0x80) {
	set_operand_ea("R", $byte);
	set_operand("A");
	output_instruction("AND");
    } elsif ($op == 0xA0) {
	set_operand_ea("R", $byte);
	set_operand("A");
	output_instruction("OR");
    } elsif ($op == 0xC0) {
	set_operand_ea("R", $byte);
	set_operand("A");
	output_instruction("XOR");
    } elsif ($op == 0xE0) {
	set_operand_ea("R", $byte);
	output_instruction("NOT");
    } else {
	undefined_instruction("ea (75)");
    }
}

sub decode_ea_76
{
    my $byte = getbyte();
    my $op = $byte & 0xe0;
    if ($op == 0x00) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("ADDW");
    } elsif ($op == 0x20) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("SUBW");
    } elsif ($op == 0x40) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("ADDCW");
    } elsif ($op == 0x60) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("CMPW");
    } elsif ($op == 0x80) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("ANDW");
    } elsif ($op == 0xA0) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("ORW");
    } elsif ($op == 0xC0) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("XORW");
    } elsif ($op == 0xE0) {
	set_operand_ea("RW", $byte);
	read_operand_rel();
	output_instruction("DWBNZ");
    } else {
	undefined_instruction("ea (76)");
    }
}

sub decode_ea_77
{
    my $byte = getbyte();
    my $op = $byte & 0xe0;
    if ($op == 0x00) {
	set_operand_ea("RW", $byte);
	set_operand("A");
	output_instruction("ADDW");
    } elsif ($op == 0x20) {
	set_operand_ea("RW", $byte);
	set_operand("A");
	output_instruction("SUBW");
    } elsif ($op == 0x40) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("SUBCW");
    } elsif ($op == 0x60) {
	set_operand_ea("RW", $byte);
	output_instruction("NEGW");
    } elsif ($op == 0x80) {
	set_operand_ea("RW", $byte);
	set_operand("A");
	output_instruction("ANDW");
    } elsif ($op == 0xA0) {
	set_operand_ea("RW", $byte);
	set_operand("A");
	output_instruction("ORW");
    } elsif ($op == 0xC0) {
	set_operand_ea("RW", $byte);
	set_operand("A");
	output_instruction("XORW");
    } elsif ($op == 0xE0) {
	set_operand_ea("RW", $byte);
	output_instruction("NOTW");
    } else {
	undefined_instruction("ea (77)");
    }
}

sub decode_ea_78
{
    my $byte = getbyte();
    my $op = $byte & 0xe0;
    if ($op == 0x00) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("MULU");
    } elsif ($op == 0x20) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("MULUW");
    } elsif ($op == 0x80) {
	set_operand("A");
	set_operand_ea("R", $byte);
	output_instruction("DIVU");
    } elsif ($op == 0xA0) {
	set_operand("A");
	set_operand_ea("RW", $byte);
	output_instruction("DIVUW");
    } else {
	undefined_instruction("ea (78)");
    }
}

sub decode_ea_79
{
    my $byte = getbyte();
    my $reg = ($byte & 0xe0) >> 5;
    set_operand_register("RW", $reg);
    set_operand_ea("RW", $byte);
    output_instruction("MOVEA");
}

sub decode_ea_7a
{
    my $byte = getbyte();
    my $reg = ($byte & 0xe0) >> 5;
    set_operand_register("R", $reg);
    set_operand_ea("R", $byte);
    output_instruction("MOV");
}

sub decode_ea_7b
{
    my $byte = getbyte();
    my $reg = ($byte & 0xe0) >> 5;
    set_operand_register("RW", $reg);
    set_operand_ea("RW", $byte);
    output_instruction("MOVW");
}

sub decode_ea_7c
{
    my $byte = getbyte();
    my $reg = ($byte & 0xe0) >> 5;
    set_operand_ea("R", $byte);
    set_operand_register("R", $reg);
    output_instruction("MOV");
}

sub decode_ea_7d
{
    my $byte = getbyte();
    my $reg = ($byte & 0xe0) >> 5;
    set_operand_ea("RW", $byte);
    set_operand_register("RW", $reg);
    output_instruction("MOVW");
}

sub decode_ea_7e
{
    my $byte = getbyte();
    my $reg = ($byte & 0xe0) >> 5;
    set_operand_register("R", $reg);
    set_operand_ea("R", $byte);
    output_instruction("XCH");
}

sub decode_ea_7f
{
    my $byte = getbyte();
    my $reg = ($byte & 0xe0) >> 5;
    set_operand_register("RW", $reg);
    set_operand_ea("RW", $byte);
    output_instruction("XCHW");
}

sub signextend8
{
    my ($byte) = @_;
    if ($byte & 0x80) {
	$byte -= 0x100;
    }
    return $byte;
}

sub signextend16
{
    my ($word) = @_;
    if ($word & 0x8000) {
	$word -= 0x10000;
    }
    return $word;
}

sub set_operand
{
    my ($operand) = @_;
    push (@operands, $operand);
}

sub read_operand_dir
{
    my $byte = getbyte();
    set_operand(sprintf("dir:0x%.2x", $byte));
}

sub read_operand_dir_bp
{
    my ($bp) = @_;
    my $byte = getbyte();
    set_operand(sprintf("dir:0x%.2x:%d", $byte, $bp));
}

sub read_operand_io
{
    my $byte = getbyte();
    if ($io{$byte}) {
        set_operand(sprintf("I:%s", $io{$byte}));
        add_comment(sprintf("0x%.2x", $byte));
    } else {
        set_operand(sprintf("I:0x%.2x", $byte));
    }
}

sub read_operand_io_bp
{
    my ($bp) = @_;
    my $byte = getbyte();
    if ($io{$byte}) {
        set_operand(sprintf("I:%s:%d", $io{$byte}, $bp));
        add_comment(sprintf("0x%.2x", $byte));
    } else {
        set_operand(sprintf("I:0x%.2x:%d", $byte, $bp));
    }
}

sub read_operand_vct8
{
    my $byte = getbyte();
    set_operand(sprintf("#vct:0x%.2x", $byte));
}

sub read_operand_addr16
{
    my ($at) = @_;
    my $byte1 = getbyte();
    my $byte2 = getbyte();
    my $addr = $byte1;
    $addr += $byte2 << 8;
    set_operand(sprintf("%sD:0x%.4x", $at, $addr));
}

sub read_operand_addr16_bp
{
    my ($bp) = @_;
    my $byte1 = getbyte();
    my $byte2 = getbyte();
    my $addr = $byte1;
    $addr += $byte2 << 8;
    set_operand(sprintf("D:0x%.4x:%d", $addr, $bp));
}

sub read_operand_addr24
{
    my $byte1 = getbyte();
    my $byte2 = getbyte();
    my $byte3 = getbyte();
    my $addr = $byte1;
    $addr += $byte2 << 8;
    $addr += $byte3 << 16;
    set_operand(sprintf("0x%.6x", $addr));
}

## ASSUMPTION: This is always the last operand!!!
sub read_operand_rel
{
    my $byte = getbyte();
    my $addr = $bytecount + signextend8($byte);
    $addr = $addr & 0xffff;
    $addr = $addr | ($address &~0xffff);
#    add_target($addr);
    set_operand(get_label($addr));
    add_comment(sprintf("0x%x", $addr));
}

sub get_label
{
    ($addr) = @_;
    return $labels{$addr} if defined($labels{$addr});
    $labels{$addr}="L" . $labelnum++;
    return $labels{$addr};
}

sub add_target
{
    ($addr) = @_;
    return if defined($targets_all{$addr});
    push(@targets, $addr);
    $targets_all{$addr}=1;
}

sub add_comment
{
    ($text) = @_;
    $comment = $comment . " " unless ($comment eq "");
    $comment = $comment . $text;
}

sub read_operand_rlst
{
    my $byte = getbyte();
    my $string = "[";
    my $comma = "";
    my $inrange = 0;
    my $reg = 0;
    while ($byte != 0) {
	if ($byte & 1) {
	    if ($inrange) {
		if (($byte & 2) == 0) {
		    $string .= "-RW" . $reg;
		    $inrange = 0;
		}
	    } else {
		$string .= $comma . "RW" . $reg;
		$comma = ", ";
		if ($byte & 2) {
		    $inrange = 1;
		}
	    }
	}
	$byte = $byte >> 1;
	$reg++;
    }
    $string .= "]";
    set_operand($string);
}

sub set_operand_imm4
{
    my ($byte) = @_;
    set_operand(sprintf("#%d", $byte));
}

sub read_operand_imm8
{
    my $byte = getbyte();
    set_operand(sprintf("#%d", $byte));
}

sub read_operand_imm16
{
    my $byte1 = getbyte();
    my $byte2 = getbyte();
    my $word = $byte1;
    $word += ($byte2 << 8);
    set_operand(sprintf("#%d", $word));
}

sub read_operand_imm32
{
    my $byte1 = getbyte();
    my $byte2 = getbyte();
    my $byte3 = getbyte();
    my $byte4 = getbyte();
    my $word = $byte1;
    $word += ($byte2 << 8);
    $word += ($byte3 << 16);
    $word += ($byte4 << 24);
    set_operand(sprintf("#0x%x", $word));
}

sub set_operand_register
{
    my ($type, $reg) = @_;
    set_operand(sprintf("%s%d", $type, $reg));
}

sub set_operand_register_disp8
{
    my ($type, $reg) = @_;
    my $byte = getbyte();
    my $disp = signextend8($byte);
    set_operand(sprintf("\@%s%d%+d", $type, $reg, $disp));
}

sub set_operand_register_disp16
{
    my ($type, $reg) = @_;
    my $byte1 = getbyte();
    my $byte2 = getbyte();
    my $word = $byte1 + ($byte2 << 8);
    my $disp = signextend16($word);
    set_operand(sprintf("\@%s%d%+d", $type, $reg, $disp));
}

sub set_operand_pc_disp16
{
    my ($at) = @_;
    my $byte1 = getbyte();
    my $byte2 = getbyte();
    my $word = $byte1 + ($byte2 << 8);
    my $disp = signextend16($word);
    set_operand(sprintf("%s\@PC%+d", $at, $disp));
}

sub set_operand_ea
{
    my ($type, $byte) = @_;
    my $at = "";
    $at = '@' if ($type =~ /@/);
    $byte = $byte & 0x1f;
    if ($byte < 0x08) {
	my $rn = $byte;
	$rn = $rn >> 1 if ($type =~ /RL/);
	set_operand(sprintf("%s%d", $type, $rn));
    } elsif ($byte < 0x0c) {
	set_operand(sprintf('%s@RW%d', $at, $byte & 3));
    } elsif ($byte < 0x10) {
	set_operand(sprintf('%s@RW%d+', $at, $byte & 3));
    } elsif ($byte < 0x18) {
	set_operand_register_disp8($at."RW", $byte & 7);
    } elsif ($byte < 0x1c) {
	set_operand_register_disp16($at."RW", $byte & 3);
    } elsif ($byte == 0x1c) {
	set_operand($at . '@RW0+RW7');
    } elsif ($byte == 0x1d) {
	set_operand($at . '@RW1+RW7');
    } elsif ($byte == 0x1e) {
	set_operand_pc_disp16($at);
    } elsif ($byte == 0x1f) {
	read_operand_addr16($at);
    } else {
	printf "Unknown ea operand: %x\n", $byte if $output;
    }
}

sub unconditional_branch
{
#    $finish_disassembly = 1;
    $newline = 1;
}

sub undefined_instruction
{
    my ($type) = @_;

    @operands = ();
    output_instruction("Undefined instruction: $type");
}

sub output_instruction
{
    my ($decode) = @_;
    my $spaces = 16;
    my $comma = "";

    return unless $output;

    printf "\n%s:\n", $labels{$address} if ($labels{$address});

    printf "%.8x: ", $address;
    for $byte (@instr) {
	printf "%.2x ", $byte;
	$spaces = $spaces - 3;
    }
    for (0..$spaces) {
	print " ";
    }
    printf "%s", $decode;
    for $operand (@operands) {
	printf "%s %s", $comma, $operand;
	$comma = ",";
    }
    if ($comment ne "") {
	printf "  ; %s", $comment;
    }
    print "\n";

    print "\n" if $newline;
    $newline = 0;
}
