/* dis6809.c -- 6809 disassembler
   Copyright (C) 1998 Jerome Thoen

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <avr/pgmspace.h>
#include "AtomBusMon.h"

unsigned char get_memb(unsigned int addr) {
  loadAddr(addr);
  return readMemByteInc();
}

#include <stdio.h>

typedef unsigned char tt_u8;
typedef signed char tt_s8;
typedef unsigned short tt_u16;
typedef signed short tt_s16;


unsigned int get_memw(unsigned int addr) {
  loadAddr(addr);
  return (readMemByteInc() << 8) + readMemByteInc();
}

enum opcodes {
  OP_UU  ,
  OP_XX  ,
  OP_ABX ,
  OP_ADCA,
  OP_ADCB,
  OP_ADDA,
  OP_ADDB,
  OP_ADDD,
  OP_ANDA,
  OP_ANDB,
  OP_ANDC,
  OP_ASL ,
  OP_ASLA,
  OP_ASR ,
  OP_ASRA,
  OP_ASRB,
  OP_BCC ,
  OP_BEQ ,
  OP_BGE ,
  OP_BGT ,
  OP_BHI ,
  OP_BITA,
  OP_BITB,
  OP_BLE ,
  OP_BLO ,
  OP_BLS ,
  OP_BLT ,
  OP_BMI ,
  OP_BNE ,
  OP_BPL ,
  OP_BRA ,
  OP_BRN ,
  OP_BSR ,
  OP_BVC ,
  OP_BVS ,
  OP_CLR ,
  OP_CLRA,
  OP_CLRB,
  OP_CMPA,
  OP_CMPB,
  OP_CMPD,
  OP_CMPS,
  OP_CMPU,
  OP_CMPX,
  OP_CMPY,
  OP_COM ,
  OP_COMA,
  OP_COMB,
  OP_CWAI,
  OP_DAA ,
  OP_DEC ,
  OP_DECA,
  OP_DECB,
  OP_EORA,
  OP_EORB,
  OP_EXG ,
  OP_INC ,
  OP_INCA,
  OP_INCB,
  OP_JMP ,
  OP_JSR ,
  OP_LBCC,
  OP_LBEQ,
  OP_LBGE,
  OP_LBGT,
  OP_LBHI,
  OP_LBLE,
  OP_LBLO,
  OP_LBLS,
  OP_LBLT,
  OP_LBMI,
  OP_LBNE,
  OP_LBPL,
  OP_LBRA,
  OP_LBRN,
  OP_LBSR,
  OP_LBVC,
  OP_LBVS,
  OP_LDA ,
  OP_LDB ,
  OP_LDD ,
  OP_LDS ,
  OP_LDU ,
  OP_LDX ,
  OP_LDY ,
  OP_LEAS,
  OP_LEAU,
  OP_LEAX,
  OP_LEAY,
  OP_LSLB,
  OP_LSR ,
  OP_LSRA,
  OP_LSRB,
  OP_MUL ,
  OP_NEG ,
  OP_NEGA,
  OP_NEGB,
  OP_NOP ,
  OP_ORA ,
  OP_ORB ,
  OP_ORCC,
  OP_PSHS,
  OP_PSHU,
  OP_PULS,
  OP_PULU,
  OP_ROL ,
  OP_ROLA,
  OP_ROLB,
  OP_ROR ,
  OP_RORA,
  OP_RORB,
  OP_RTI ,
  OP_RTS ,
  OP_SBCA,
  OP_SBCB,
  OP_SEX ,
  OP_STA ,
  OP_STB ,
  OP_STD ,
  OP_STS ,
  OP_STU ,
  OP_STX ,
  OP_STY ,
  OP_SUBA,
  OP_SUBB,
  OP_SUBD,
  OP_SWI ,
  OP_SWI2,
  OP_SWI3,
  OP_SYNC,
  OP_TFR ,
  OP_TST ,
  OP_TSTA,
  OP_TSTB
};

static const char inst[] PROGMEM = "\
--  \
??  \
ABX \
ADCA\
ADCB\
ADDA\
ADDB\
ADDD\
ANDA\
ANDB\
ANDC\
ASL \
ASLA\
ASR \
ASRA\
ASRB\
BCC \
BEQ \
BGE \
BGT \
BHI \
BITA\
BITB\
BLE \
BLO \
BLS \
BLT \
BMI \
BNE \
BPL \
BRA \
BRN \
BSR \
BVC \
BVS \
CLR \
CLRA\
CLRB\
CMPA\
CMPB\
CMPD\
CMPS\
CMPU\
CMPX\
CMPY\
COM \
COMA\
COMB\
CWAI\
DAA \
DEC \
DECA\
DECB\
EORA\
EORB\
EXG \
INC \
INCA\
INCB\
JMP \
JSR \
LBCC\
LBEQ\
LBGE\
LBGT\
LBHI\
LBLE\
LBLO\
LBLS\
LBLT\
LBMI\
LBNE\
LBPL\
LBRA\
LBRN\
LBSR\
LBVC\
LBVS\
LDA \
LDB \
LDD \
LDS \
LDU \
LDX \
LDY \
LEAS\
LEAU\
LEAX\
LEAY\
LSLB\
LSR \
LSRA\
LSRB\
MUL \
NEG \
NEGA\
NEGB\
NOP \
ORA \
ORB \
ORCC\
PSHS\
PSHU\
PULS\
PULU\
ROL \
ROLA\
ROLB\
ROR \
RORA\
RORB\
RTI \
RTS \
SBCA\
SBCB\
SEX \
STA \
STB \
STD \
STS \
STU \
STX \
STY \
SUBA\
SUBB\
SUBD\
SWI \
SWI2\
SWI3\
SYNC\
TFR \
TST \
TSTA\
TSTB";


// The first byte is the opcode index
// The second byte is <length><mode>
//	modes:
//	1 immediate
//	2 direct    
//	3 indexed   
//	4 extended  
//	5 inherent
//	6 relative

static const unsigned char map0[] PROGMEM = {
   OP_NEG , 0x22,
   OP_XX  , 0x22,
   OP_XX  , 0x12,
   OP_COM , 0x22,
   OP_LSR , 0x22,
   OP_XX  , 0x12,
   OP_ROR , 0x22,
   OP_ASR , 0x22,
   OP_ASL , 0x22,
   OP_ROL , 0x22,
   OP_DEC , 0x22,
   OP_XX  , 0x12,
   OP_INC , 0x22,
   OP_TST , 0x22,
   OP_JMP , 0x22,
   OP_CLR , 0x22,
   OP_UU  , 0x10,
   OP_UU  , 0x10,
   OP_NOP , 0x15,
   OP_SYNC, 0x15,
   OP_XX  , 0x10,
   OP_XX  , 0x10,
   OP_LBRA, 0x36,
   OP_LBSR, 0x36,
   OP_XX  , 0x10,
   OP_DAA , 0x15,
   OP_ORCC, 0x25,
   OP_XX  , 0x10,
   OP_ANDC, 0x25,
   OP_SEX , 0x15,
   OP_EXG , 0x25,
   OP_TFR , 0x25,
   OP_BRA , 0x26,
   OP_BRN , 0x26,
   OP_BHI , 0x26,
   OP_BLS , 0x26,
   OP_BCC , 0x26,
   OP_BLO , 0x26,
   OP_BNE , 0x26,
   OP_BEQ , 0x26,
   OP_BVC , 0x26,
   OP_BVS , 0x26,
   OP_BPL , 0x26,
   OP_BMI , 0x26,
   OP_BGE , 0x26,
   OP_BLT , 0x26,
   OP_BGT , 0x26,
   OP_BLE , 0x26,
   OP_LEAX, 0x23,
   OP_LEAY, 0x23,
   OP_LEAS, 0x23,
   OP_LEAU, 0x23,
   OP_PSHS, 0x25,
   OP_PULS, 0x25,
   OP_PSHU, 0x25,
   OP_PULU, 0x25,
   OP_XX  , 0x15,
   OP_RTS , 0x15,
   OP_ABX , 0x15,
   OP_RTI , 0x15,
   OP_CWAI, 0x25,
   OP_MUL , 0x15,
   OP_XX  , 0x15,
   OP_SWI , 0x15,
   OP_NEGA, 0x15,
   OP_XX  , 0x15,
   OP_XX  , 0x15,
   OP_COMA, 0x15,
   OP_LSRA, 0x15,
   OP_XX  , 0x15,
   OP_RORA, 0x15,
   OP_ASRA, 0x15,
   OP_ASLA, 0x15,
   OP_ROLA, 0x15,
   OP_DECA, 0x15,
   OP_XX  , 0x15,
   OP_INCA, 0x15,
   OP_TSTA, 0x15,
   OP_XX  , 0x15,
   OP_CLRA, 0x15,
   OP_NEGB, 0x15,
   OP_XX  , 0x15,
   OP_XX  , 0x15,
   OP_COMB, 0x15,
   OP_LSRB, 0x15,
   OP_XX  , 0x15,
   OP_RORB, 0x15,
   OP_ASRB, 0x15,
   OP_LSLB, 0x15,
   OP_ROLB, 0x15,
   OP_DECB, 0x15,
   OP_XX  , 0x15,
   OP_INCB, 0x15,
   OP_TSTB, 0x15,
   OP_XX  , 0x15,
   OP_CLRB, 0x15,
   OP_NEG , 0x23,
   OP_XX  , 0x13,
   OP_XX  , 0x13,
   OP_COM , 0x23,
   OP_LSR , 0x23,
   OP_XX  , 0x13,
   OP_ROR , 0x23,
   OP_ASR , 0x23,
   OP_ASL , 0x23,
   OP_ROL , 0x23,
   OP_DEC , 0x23,
   OP_XX  , 0x13,
   OP_INC , 0x23,
   OP_TST , 0x23,
   OP_JMP , 0x23,
   OP_CLR , 0x23,
   OP_NEG , 0x34,
   OP_XX  , 0x14,
   OP_XX  , 0x14,
   OP_COM , 0x34,
   OP_LSR , 0x34,
   OP_XX  , 0x14,
   OP_ROR , 0x34,
   OP_ASR , 0x34,
   OP_ASL , 0x34,
   OP_ROL , 0x34,
   OP_DEC , 0x34,
   OP_XX  , 0x14,
   OP_INC , 0x34,
   OP_TST , 0x34,
   OP_JMP , 0x34,
   OP_CLR , 0x34,
   OP_SUBA, 0x21,
   OP_CMPA, 0x21,
   OP_SBCA, 0x21,
   OP_SUBD, 0x31,
   OP_ANDA, 0x21,
   OP_BITA, 0x21,
   OP_LDA , 0x21,
   OP_XX  , 0x11,
   OP_EORA, 0x21,
   OP_ADCA, 0x21,
   OP_ORA , 0x21,
   OP_ADDA, 0x21,
   OP_CMPX, 0x31,
   OP_BSR , 0x26,
   OP_LDX , 0x31,
   OP_XX  , 0x10,
   OP_SUBA, 0x22,
   OP_CMPA, 0x22,
   OP_SBCA, 0x22,
   OP_SUBD, 0x22,
   OP_ANDA, 0x22,
   OP_BITA, 0x22,
   OP_LDA , 0x22,
   OP_STA , 0x22,
   OP_EORA, 0x22,
   OP_ADCA, 0x22,
   OP_ORA , 0x22,
   OP_ADDA, 0x22,
   OP_CMPX, 0x22,
   OP_JSR , 0x22,
   OP_LDX , 0x22,
   OP_STX , 0x22,
   OP_SUBA, 0x23,
   OP_CMPA, 0x23,
   OP_SBCA, 0x23,
   OP_SUBD, 0x23,
   OP_ANDA, 0x23,
   OP_BITA, 0x23,
   OP_LDA , 0x23,
   OP_STA , 0x23,
   OP_EORA, 0x23,
   OP_ADCA, 0x23,
   OP_ORA , 0x23,
   OP_ADDA, 0x23,
   OP_CMPX, 0x23,
   OP_JSR , 0x23,
   OP_LDX , 0x23,
   OP_STX , 0x23,
   OP_SUBA, 0x34,
   OP_CMPA, 0x34,
   OP_SBCA, 0x34,
   OP_SUBD, 0x34,
   OP_ANDA, 0x34,
   OP_BITA, 0x34,
   OP_LDA , 0x34,
   OP_STA , 0x34,
   OP_EORA, 0x34,
   OP_ADCA, 0x34,
   OP_ORA , 0x34,
   OP_ADDA, 0x34,
   OP_CMPX, 0x34,
   OP_JSR , 0x34,
   OP_LDX , 0x34,
   OP_STX , 0x34,
   OP_SUBB, 0x21,
   OP_CMPB, 0x21,
   OP_SBCB, 0x21,
   OP_ADDD, 0x31,
   OP_ANDB, 0x21,
   OP_BITB, 0x21,
   OP_LDB , 0x21,
   OP_XX  , 0x11,
   OP_EORB, 0x21,
   OP_ADCB, 0x21,
   OP_ORB , 0x21,
   OP_ADDB, 0x21,
   OP_LDD , 0x31,
   OP_XX  , 0x11,
   OP_LDU , 0x31,
   OP_XX  , 0x10,
   OP_SUBB, 0x22,
   OP_CMPB, 0x22,
   OP_SBCB, 0x22,
   OP_ADDD, 0x22,
   OP_ANDB, 0x22,
   OP_BITB, 0x22,
   OP_LDB , 0x22,
   OP_STB , 0x22,
   OP_EORB, 0x22,
   OP_ADCB, 0x22,
   OP_ORB , 0x22,
   OP_ADDB, 0x22,
   OP_LDD , 0x22,
   OP_STD , 0x22,
   OP_LDU , 0x22,
   OP_STU , 0x22,
   OP_SUBB, 0x23,
   OP_CMPB, 0x23,
   OP_SBCB, 0x23,
   OP_ADDD, 0x23,
   OP_ANDB, 0x23,
   OP_BITB, 0x23,
   OP_LDB , 0x23,
   OP_STB , 0x23,
   OP_EORB, 0x23,
   OP_ADCB, 0x23,
   OP_ORB , 0x23,
   OP_ADDB, 0x23,
   OP_LDD , 0x23,
   OP_STD , 0x23,
   OP_LDU , 0x23,
   OP_STU , 0x23,
   OP_SUBB, 0x34,
   OP_CMPB, 0x34,
   OP_SBCB, 0x34,
   OP_ADDD, 0x34,
   OP_ANDB, 0x34,
   OP_BITB, 0x34,
   OP_LDB , 0x34,
   OP_STB , 0x34,
   OP_EORB, 0x34,
   OP_ADCB, 0x34,
   OP_ORB , 0x34,
   OP_ADDB, 0x34,
   OP_LDD , 0x34,
   OP_STD , 0x34,
   OP_LDU , 0x34,
   OP_STU , 0x34,
};

static const unsigned char map1[] PROGMEM = {
    33,	OP_LBRN, 0x46,
    34,	OP_LBHI, 0x46,
    35,	OP_LBLS, 0x46,
    36,	OP_LBCC, 0x46,
    37,	OP_LBLO, 0x46,
    38,	OP_LBNE, 0x46,
    39,	OP_LBEQ, 0x46,
    40,	OP_LBVC, 0x46,
    41,	OP_LBVS, 0x46,
    42,	OP_LBPL, 0x46,
    43,	OP_LBMI, 0x46,
    44,	OP_LBGE, 0x46,
    45,	OP_LBLT, 0x46,
    46,	OP_LBGT, 0x46,
    47,	OP_LBLE, 0x46,
    63,	OP_SWI2, 0x25,
   131,	OP_CMPD, 0x41,
   140,	OP_CMPY, 0x41,
   142,	OP_LDY , 0x41,
   147,	OP_CMPD, 0x32,
   156,	OP_CMPY, 0x32,
   158,	OP_LDY , 0x32,
   159,	OP_STY , 0x32,
   163,	OP_CMPD, 0x33,
   172,	OP_CMPY, 0x33,
   174,	OP_LDY , 0x33,
   175,	OP_STY , 0x33,
   179,	OP_CMPD, 0x44,
   188,	OP_CMPY, 0x44,
   190,	OP_LDY , 0x44,
   191,	OP_STY , 0x44,
   206,	OP_LDS , 0x41,
   222,	OP_LDS , 0x32,
   223,	OP_STS , 0x32,
   238,	OP_LDS , 0x33,
   239,	OP_STS , 0x33,
   254,	OP_LDS , 0x44,
   255,	OP_STS , 0x44,
};

static const unsigned char map2[] PROGMEM = {
    63,	OP_SWI3, 0x25,
   131,	OP_CMPU, 0x41,
   140,	OP_CMPS, 0x41,
   147,	OP_CMPU, 0x32,
   156,	OP_CMPS, 0x32,
   163,	OP_CMPU, 0x33,
   172,	OP_CMPS, 0x33,
   179,	OP_CMPU, 0x44,
   188,	OP_CMPS, 0x44,
   255, OP_XX  , 0x10
};

static const char regi[] = { 'X', 'Y', 'U', 'S' };

static const char *exgi[] = { "D", "X", "Y", "U", "S", "PC", "??", "??", "A",
			      "B", "CC", "DP", "??", "??", "??", "??" };

static const char *pshsregi[] = { "PC", "U", "Y", "X", "DP", "B", "A", "CC" };
static const char *pshuregi[] = { "PC", "S", "Y", "X", "DP", "B", "A", "CC" };

/* disassemble one instruction at adress adr and return its size */

char hexdigit(tt_u16 v)
{
  v &= 0xf;
  if (v <= 9)
    return '0' + v;
  else
    return 'A' - 10 + v;
}

char *hex8str(tt_u8 v)
{
  static char tmpbuf[3] = "  ";

  tmpbuf[1] = hexdigit(v);
  tmpbuf[0] = hexdigit(v >> 4);

  return tmpbuf;
}

char *hex16str(tt_u16 v)
{
  static char tmpbuf[5] = "    ";

  tmpbuf[3] = hexdigit(v);
  v >>= 4;
  tmpbuf[2] = hexdigit(v);
  v >>= 4;
  tmpbuf[1] = hexdigit(v);
  v >>= 4;
  tmpbuf[0] = hexdigit(v);

  return tmpbuf;
}

extern char *statusString;

char *ccstr(tt_u8 val)
{
  static char tempbuf[9] = "        ";
  int i;

  for (i = 0; i < 8; i++) {
    if (val & 0x80)
      tempbuf[i] = statusString[i];
    else
      tempbuf[i] = '.';
    val <<= 1;
  }

  return tempbuf;
}

unsigned int disassemble(unsigned int addr)
{
  int d = get_memb(addr);
  int s, i;
  tt_u8 pb;
  char reg;
  const unsigned char *map = NULL;

  // Default for most undefined opcodes
  unsigned char sm = 0x10; // size_mode byte
  unsigned char oi = OP_XX; // opcode index

  FILE *stream = &ser0stream;

  if (d == 0x10) {
    d = get_memb(addr + 1); 
    map = map1;
  }

  if (d == 0x11) {
    d = get_memb(addr + 1);
    map = map2;
  }

  if (map) {
    // Search for the opcode in map1 or map2
    map -= 3;
    do {
      map += 3;
      if (pgm_read_byte(map) == d) {
	oi = pgm_read_byte(++map);
	sm = pgm_read_byte(++map);
	break;
      }
    } while (*map < 255);
  } else {
    // Lookup directly in map0
    map = map0 + 2 * d;
    oi = pgm_read_byte(map++);
    sm = pgm_read_byte(map++);
  }

  s = sm >> 4;

  fprintf(stream, "%04X ", addr);

  for (i = 0; i < s; i++) {
    fputs(hex8str(get_memb(addr + i)), stream);
    fputc(' ', stream);
  }
  for (i = s; i < 4; i++) {
    fputs("   ", stream);
  }

  const char *ip = inst + oi * 4; 
  for (i = 0; i < 4; i++)
    fputc(pgm_read_byte(ip++), stream);

  fputs("  ", stream);

  
  switch(sm & 15) {
  case 1:             /* immediate */
    fputs("#$", stream);
    if (s == 2)
      fputs(hex8str(get_memb(addr + 1)), stream);
    else
      fputs(hex16str(get_memw(addr + s - 2)), stream);
    break;
  case 2:             /* direct */
    fputs("$", stream);
    fputs(hex8str(get_memb(addr + s - 1)), stream);
    break;
  case 3:             /* indexed */
    pb = get_memb(addr + s - 1);
    reg = regi[(pb >> 5) & 0x03];

    if (!(pb & 0x80)) {       /* n4,R */
      if (pb & 0x10)
	fprintf(stream, "-$%s,%c", hex8str(((pb & 0x0f) ^ 0x0f) + 1), reg);
      else
	fprintf(stream, "$%s,%c", hex8str(pb & 0x0f), reg);
    }
    else {
      if (pb & 0x10)
	fputc('[', stream);
      switch (pb & 0x0f) {
      case 0:                 /* ,R+ */
	fprintf(stream, ",%c+", reg);
	break;
      case 1:                 /* ,R++ */
	fprintf(stream, ",%c++", reg);
	break;
      case 2:                 /* ,-R */
	fprintf(stream, ",-%c", reg);
	break;
      case 3:                 /* ,--R */
	fprintf(stream, ",--%c", reg);
	break;
      case 4:                 /* ,R */
	fprintf(stream, ",%c", reg);
	break;
      case 5:                 /* B,R */
	fprintf(stream, "B,%c", reg);
	break;
      case 6:                 /* A,R */
	fprintf(stream, "A,%c", reg);
	break;
      case 8:                 /* n7,R */
	s += 1;
	fprintf(stream, "$%s,%c", hex8str(get_memb(addr + s - 1)), reg);
	break;
      case 9:                 /* n15,R */
	s += 2;
	fprintf(stream, "$%s,%c", hex16str(get_memw(addr + s - 2)), reg);
	break;
      case 11:                /* D,R */
	fprintf(stream, "D,%c", reg);
	break;
      case 12:                /* n7,PCR */
	s += 1;
	fprintf(stream, "$%s,PCR", hex8str(get_memb(addr + s - 1)));
	break;
      case 13:                /* n15,PCR */
	s += 2;
	fprintf(stream, "$%s,PCR", hex16str(get_memw(addr + s - 2)));
	break;
      case 15:                /* [n] */
	s += 2;
	fprintf(stream, "$%s", hex16str(get_memw(addr + s - 2)));
	break;
      default:
	fputs("??", stream);
	break; }
      if (pb & 0x10)
	fputc(']', stream);
    }
    break;
  case 4:          /* extended */
    fprintf(stream, "$%s", hex16str(get_memw(addr + s - 2)));
    break;
  case 5:          /* inherent */
    pb = get_memb(addr + 1);
    switch (d) {
    case 0x1e: case 0x1f:              /* exg tfr */
      fprintf(stream, "%s,%s", exgi[(pb >> 4) & 0x0f], exgi[pb & 0x0f]);
      break; 
    case 0x1a: case 0x1c: case 0x3c:   /* orcc andcc cwai */
      fprintf(stream, "#$%s=%s", hex8str(pb), ccstr(pb));
      break;
    case 0x34:                         /* pshs */
      {
	int p = 0;

	for (i = 0; i < 8; i++) {
	  if (pb & 0x80) {
	    if (p)
	      fputc(',', stream);
	    fputs(pshsregi[i], stream);
	    p = 1;
	  }
	  pb <<= 1;
	}
      }
      break;
    case 0x35:                         /* puls */
      {
	int p = 0;

	for (i = 7; i >= 0; i--) {
	  if (pb & 0x01) {
	    if (p)
	      fputc(',', stream);
	    fputs(pshsregi[i], stream);
	    p = 1;
	  }
	  pb >>= 1;
	}
      }
      break;
    case 0x36:                         /* pshu */
      {
	int p = 0;

	for (i = 0; i < 8; i++) {
	  if (pb & 0x80) {
	    if (p)
	      fputc(',', stream);
	    fputs(pshuregi[i], stream);
	    p = 1;
	  }
	  pb <<= 1;
	}
      }
      break;
    case 0x37:                         /* pulu */
      {
	int p = 0;

	for (i = 7; i >= 0; i--) {
	  if (pb & 0x01) {
	    if (p)
	      fputc(',', stream);
	    fputs(pshuregi[i], stream);
	    p = 1;
	  }
	  pb >>= 1;
	}
      }
      break;
    }
    break; 
  case 6:             /* relative */
    {
      tt_s16 v;

      if (s == 2)
	v = (tt_s16)(tt_s8)get_memb(addr + 1);
      else
	v = (tt_s16)get_memw(addr + s - 2);
      fprintf(stream, "$%s", hex16str(addr + (tt_u16)s + v));
      break;
    }
  }
  fputc('\n', stream);

  return addr + s;
}
