#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

/*
 * original tables written by
 *
 *      Graham E. Kinns  <gekinns@iee.org>  Apr '97
 *
 * Program rewritten by Hans Petter Selasky 2002
 */

#define VERSION 0
#define STEP 1

static const char * const keyword[256] =
{

 /* Undefined tokens: */

        [0xe8] = "($e8)",
        [0xe9] = "($e9)",
        [0xea] = "($ea)",
        [0xeb] = "($eb)",
        [0xec] = "($ec)",
        [0xed] = "($ed)",
        [0xee] = "($ee)",
        [0xef] = "($ef)",
        [0xf0] = "($f0)",
        [0xf1] = "($f1)",
        [0xf2] = "($f2)",
        [0xf3] = "($f3)",
        [0xf4] = "($f4)",
        [0xf5] = "($f5)",
        [0xf6] = "($f6)",
        [0xf7] = "($f7)",
        [0xf8] = "($f8)",
        [0xf9] = "($f9)",
        [0xfa] = "($fa)",
        [0xfb] = "($fb)",
        [0xfc] = "($fc)",
        [0xfd] = "($fd)",
        [0xfe] = "($fe)",

 /* Printable letters: */

        [0x00] = "\000", /* end of line */
        [0x01] = "\001",
        [0x02] = "\002",
        [0x03] = "\003",
        [0x04] = "\004",
        [0x05] = "\005",
        [0x06] = "\006",
        [0x07] = "\007",
        [0x08] = "\010",
        [0x09] = "\011",
        [0x0a] = "\012",
        [0x0b] = "\013",
        [0x0c] = "\014",
        [0x0d] = "\015",
        [0x0e] = "\016",
        [0x0f] = "\017",
        [0x10] = "\020",
        [0x11] = "\021",
        [0x12] = "\022",
        [0x13] = "\023",
        [0x14] = "\024",
        [0x15] = "\025",
        [0x16] = "\026",
        [0x17] = "\027",
        [0x18] = "\030",
        [0x19] = "\031",
        [0x1a] = "\032",
        [0x1b] = "\033",
        [0x1c] = "\034",
        [0x1d] = "\035",
        [0x1e] = "\036",
        [0x1f] = "\037",
        [0x20] = "\040",
        [0x21] = "\041",
        [0x22] = "\042",
        [0x23] = "\043",
        [0x24] = "\044",
        [0x25] = "\045",
        [0x26] = "\046",
        [0x27] = "\047",
        [0x28] = "\050",
        [0x29] = "\051",
        [0x2a] = "\052",
        [0x2b] = "\053",
        [0x2c] = "\054",
        [0x2d] = "\055",
        [0x2e] = "\056",
        [0x2f] = "\057",
        [0x30] = "\060",
        [0x31] = "\061",
        [0x32] = "\062",
        [0x33] = "\063",
        [0x34] = "\064",
        [0x35] = "\065",
        [0x36] = "\066",
        [0x37] = "\067",
        [0x38] = "\070",
        [0x39] = "\071",
        [0x3a] = "\072",
        [0x3b] = "\073",
        [0x3c] = "\074",
        [0x3d] = "\075",
        [0x3e] = "\076",
        [0x3f] = "\077",
        [0x40] = "\100",
        [0x41] = "\101",
        [0x42] = "\102",
        [0x43] = "\103",
        [0x44] = "\104",
        [0x45] = "\105",
        [0x46] = "\106",
        [0x47] = "\107",
        [0x48] = "\110",
        [0x49] = "\111",
        [0x4a] = "\112",
        [0x4b] = "\113",
        [0x4c] = "\114",
        [0x4d] = "\115",
        [0x4e] = "\116",
        [0x4f] = "\117",
        [0x50] = "\120",
        [0x51] = "\121",
        [0x52] = "\122",
        [0x53] = "\123",
        [0x54] = "\124",
        [0x55] = "\125",
        [0x56] = "\126",
        [0x57] = "\127",
        [0x58] = "\130",
        [0x59] = "\131",
        [0x5a] = "\132",
        [0x5b] = "\133",
        [0x5c] = "\134",
        [0x5d] = "\135",
        [0x5e] = "\136",
        [0x5f] = "\137",
        [0x60] = "\140",
        [0x61] = "\141",
        [0x62] = "\142",
        [0x63] = "\143",
        [0x64] = "\144",
        [0x65] = "\145",
        [0x66] = "\146",
        [0x67] = "\147",
        [0x68] = "\150",
        [0x69] = "\151",
        [0x6a] = "\152",
        [0x6b] = "\153",
        [0x6c] = "\154",
        [0x6d] = "\155",
        [0x6e] = "\156",
        [0x6f] = "\157",
        [0x70] = "\160",
        [0x71] = "\161",
        [0x72] = "\162",
        [0x73] = "\163",
        [0x74] = "\164",
        [0x75] = "\165",
        [0x76] = "\166",
        [0x77] = "\167",
        [0x78] = "\170",
        [0x79] = "\171",
        [0x7a] = "\172",
        [0x7b] = "\173",
        [0x7c] = "\174",
        [0x7d] = "\175",
        [0x7e] = "\176",
        [0x7f] = "\177",

 /* Basic tokens: */

        [0x80] = " FOR "            ,
        [0x81] = " GO "             ,
        [0x82] = " REM "            ,
        [0x83] = "'"                ,
        [0x84] = " ELSE "           ,
        [0x85] = " IF "             ,
        [0x86] = " DATA "           ,
        [0x87] = " PRINT "          ,
        [0x88] = " ON "             ,
        [0x89] = " INPUT "          ,
        [0x8a] = " END "            ,
        [0x8b] = " NEXT "           ,
        [0x8c] = " DIM "            ,
        [0x8d] = " READ "           ,
        [0x8e] = " LET "            ,
        [0x8f] = " RUN "            ,
        [0x90] = " RESTORE "        ,
        [0x91] = " RETURN "         ,
        [0x92] = " STOP "           ,
        [0x93] = " POKE "           ,
        [0x94] = " CONT "           ,
        [0x95] = " LIST "           ,
        [0x96] = " CLEAR "          ,
        [0x97] = " NEW "            ,
        [0x98] = " DEF "            ,
        [0x99] = " CLOAD "          ,
        [0x9a] = " CSAVE "          ,
        [0x9b] = " OPEN "           ,
        [0x9c] = " CLOSE "          ,
        [0x9d] = " LLIST "          ,
        [0x9e] = " SET "            ,
        [0x9f] = " RESET "          ,
        [0xa0] = " CLS "            ,
        [0xa1] = " MOTOR "          ,
        [0xa2] = " SOUND "          ,
        [0xa3] = " AUDIO "          ,
        [0xa4] = " EXEC "           ,
        [0xa5] = " SKIPF "          ,
        [0xa6] = " DELETE "         ,
        [0xa7] = " EDIT "           ,
        [0xa8] = " TRON "           ,
        [0xa9] = " TROFF "          ,
        [0xaa] = " LINE "           ,
        [0xab] = " PCLS "           ,
        [0xac] = " PSET "           ,
        [0xad] = " PRESET "         ,
        [0xae] = " SCREEN "         ,
        [0xaf] = " PCLEAR "         ,
        [0xb0] = " COLOR "          ,
        [0xb1] = " CIRCLE "         ,
        [0xb2] = " PAINT "          ,
        [0xb3] = " GET "            ,
        [0xb4] = " PUT "            ,
        [0xb5] = " DRAW "           ,
        [0xb6] = " PCOPY "          ,
        [0xb7] = " PMODE "          ,
        [0xb8] = " PLAY "           ,
        [0xb9] = " DLOAD "          ,
        [0xba] = " RENUM "          ,
        [0xbb] = " TAB("            ,
        [0xbc] = " TO "             ,
        [0xbd] = " SUB "            ,
        [0xbe] = " FN "             ,
        [0xbf] = " THEN "           ,
        [0xc0] = " NOT "            ,
        [0xc1] = " STEP "           ,
        [0xc2] = " OFF "            ,
        [0xc3] = "+"                ,
        [0xc4] = "-"                ,
        [0xc5] = "*"                ,
        [0xc6] = "/"                ,
        [0xc7] = "^"                ,
        [0xc8] = " AND "            ,
        [0xc9] = " OR "             ,
        [0xca] = ">"                ,
        [0xcb] = "="                ,
        [0xcc] = "<"                ,
        [0xcd] = " USING "          ,
        [0xff] = "($ff) "           , /* function escape */

/*  Dragon DOS v1.0 tokens: */

        [0xce] = " AUTO "           ,
        [0xcf] = " BACKUP "         ,
        [0xd0] = " BEEP "           ,
        [0xd1] = " BOOT "           ,
        [0xd2] = " CHAIN "          ,
        [0xd3] = " COPY "           ,
        [0xd4] = " CREATE  "        ,
        [0xd5] = " DIR "            ,
        [0xd6] = " DRIVE "          ,
        [0xd7] = " DSKINIT "        ,
        [0xd8] = " FREAD "          ,
        [0xd9] = " FWRITE "         ,
        [0xda] = " ERROR "          ,
        [0xdb] = " KILL "           ,
        [0xdc] = " LOAD "           ,
        [0xdd] = " MERGE "          ,
        [0xde] = " PROTECT "        ,
        [0xdf] = " WAIT "           ,
        [0xe0] = " RENAME "         ,
        [0xe1] = " SAVE "           ,
        [0xe2] = " SREAD "          ,
        [0xe3] = " SWRITE "         ,
        [0xe4] = " VERIFY "         ,
        [0xe5] = " FROM "           ,
        [0xe6] = " FLREAD "         ,
        [0xe7] = " SWAP "           ,
};

static const char * const function[256] =
{

 /* Undefined tokens: */

        [0x00] = "($FF00)", /* end of line */
        [0x01] = "($FF01)",
        [0x02] = "($FF02)",
        [0x03] = "($FF03)",
        [0x04] = "($FF04)",
        [0x05] = "($FF05)",
        [0x06] = "($FF06)",
        [0x07] = "($FF07)",
        [0x08] = "($FF08)",
        [0x09] = "($FF09)",
        [0x0A] = "($FF0A)",
        [0x0B] = "($FF0B)",
        [0x0C] = "($FF0C)",
        [0x0D] = "($FF0D)",
        [0x0E] = "($FF0E)",
        [0x0F] = "($FF0F)",
        [0x10] = "($FF10)",
        [0x11] = "($FF11)",
        [0x12] = "($FF12)",
        [0x13] = "($FF13)",
        [0x14] = "($FF14)",
        [0x15] = "($FF15)",
        [0x16] = "($FF16)",
        [0x17] = "($FF17)",
        [0x18] = "($FF18)",
        [0x19] = "($FF19)",
        [0x1A] = "($FF1A)",
        [0x1B] = "($FF1B)",
        [0x1C] = "($FF1C)",
        [0x1D] = "($FF1D)",
        [0x1E] = "($FF1E)",
        [0x1F] = "($FF1F)",
        [0x20] = "($FF20)",
        [0x21] = "($FF21)",
        [0x22] = "($FF22)",
        [0x23] = "($FF23)",
        [0x24] = "($FF24)",
        [0x25] = "($FF25)",
        [0x26] = "($FF26)",
        [0x27] = "($FF27)",
        [0x28] = "($FF28)",
        [0x29] = "($FF29)",
        [0x2A] = "($FF2A)",
        [0x2B] = "($FF2B)",
        [0x2C] = "($FF2C)",
        [0x2D] = "($FF2D)",
        [0x2E] = "($FF2E)",
        [0x2F] = "($FF2F)",
        [0x30] = "($FF30)",
        [0x31] = "($FF31)",
        [0x32] = "($FF32)",
        [0x33] = "($FF33)",
        [0x34] = "($FF34)",
        [0x35] = "($FF35)",
        [0x36] = "($FF36)",
        [0x37] = "($FF37)",
        [0x38] = "($FF38)",
        [0x39] = "($FF39)",
        [0x3A] = "($FF3A)",
        [0x3B] = "($FF3B)",
        [0x3C] = "($FF3C)",
        [0x3D] = "($FF3D)",
        [0x3E] = "($FF3E)",
        [0x3F] = "($FF3F)",
        [0x40] = "($FF40)",
        [0x41] = "($FF41)",
        [0x42] = "($FF42)",
        [0x43] = "($FF43)",
        [0x44] = "($FF44)",
        [0x45] = "($FF45)",
        [0x46] = "($FF46)",
        [0x47] = "($FF47)",
        [0x48] = "($FF48)",
        [0x49] = "($FF49)",
        [0x4A] = "($FF4A)",
        [0x4B] = "($FF4B)",
        [0x4C] = "($FF4C)",
        [0x4D] = "($FF4D)",
        [0x4E] = "($FF4E)",
        [0x4F] = "($FF4F)",
        [0x50] = "($FF50)",
        [0x51] = "($FF51)",
        [0x52] = "($FF52)",
        [0x53] = "($FF53)",
        [0x54] = "($FF54)",
        [0x55] = "($FF55)",
        [0x56] = "($FF56)",
        [0x57] = "($FF57)",
        [0x58] = "($FF58)",
        [0x59] = "($FF59)",
        [0x5A] = "($FF5A)",
        [0x5B] = "($FF5B)",
        [0x5C] = "($FF5C)",
        [0x5D] = "($FF5D)",
        [0x5E] = "($FF5E)",
        [0x5F] = "($FF5F)",
        [0x60] = "($FF60)",
        [0x61] = "($FF61)",
        [0x62] = "($FF62)",
        [0x63] = "($FF63)",
        [0x64] = "($FF64)",
        [0x65] = "($FF65)",
        [0x66] = "($FF66)",
        [0x67] = "($FF67)",
        [0x68] = "($FF68)",
        [0x69] = "($FF69)",
        [0x6A] = "($FF6A)",
        [0x6B] = "($FF6B)",
        [0x6C] = "($FF6C)",
        [0x6D] = "($FF6D)",
        [0x6E] = "($FF6E)",
        [0x6F] = "($FF6F)",
        [0x70] = "($FF70)",
        [0x71] = "($FF71)",
        [0x72] = "($FF72)",
        [0x73] = "($FF73)",
        [0x74] = "($FF74)",
        [0x75] = "($FF75)",
        [0x76] = "($FF76)",
        [0x77] = "($FF77)",
        [0x78] = "($FF78)",
        [0x79] = "($FF79)",
        [0x7A] = "($FF7A)",
        [0x7B] = "($FF7B)",
        [0x7C] = "($FF7C)",
        [0x7D] = "($FF7D)",
        [0x7E] = "($FF7E)",
        [0x7F] = "($FF7F)",
        [0xa9] = "($FFA9)",
        [0xaa] = "($FFAA)",
        [0xab] = "($FFAB)",
        [0xac] = "($FFAC)",
        [0xad] = "($FFAD)",
        [0xae] = "($FFAE)",
        [0xaf] = "($FFAF)",
        [0xb0] = "($FFB0)",
        [0xb1] = "($FFB1)",
        [0xb2] = "($FFB2)",
        [0xb3] = "($FFB3)",
        [0xb4] = "($FFB4)",
        [0xb5] = "($FFB5)",
        [0xb6] = "($FFB6)",
        [0xb7] = "($FFB7)",
        [0xb8] = "($FFB8)",
        [0xb9] = "($FFB9)",
        [0xba] = "($FFBA)",
        [0xbb] = "($FFBB)",
        [0xbc] = "($FFBC)",
        [0xbd] = "($FFBD)",
        [0xbe] = "($FFBE)",
        [0xbf] = "($FFBF)",
        [0xc0] = "($FFC0)",
        [0xc1] = "($FFC1)",
        [0xc2] = "($FFC2)",
        [0xc3] = "($FFC3)",
        [0xc4] = "($FFC4)",
        [0xc5] = "($FFC5)",
        [0xc6] = "($FFC6)",
        [0xc7] = "($FFC7)",
        [0xc8] = "($FFC8)",
        [0xc9] = "($FFC9)",
        [0xca] = "($FFCA)",
        [0xcb] = "($FFCB)",
        [0xcc] = "($FFCC)",
        [0xcd] = "($FFCD)",
        [0xce] = "($FFCE)",
        [0xcf] = "($FFCF)",
        [0xd0] = "($FFD0)",
        [0xd1] = "($FFD1)",
        [0xd2] = "($FFD2)",
        [0xd3] = "($FFD3)",
        [0xd4] = "($FFD4)",
        [0xd5] = "($FFD5)",
        [0xd6] = "($FFD6)",
        [0xd7] = "($FFD7)",
        [0xd8] = "($FFD8)",
        [0xd9] = "($FFD9)",
        [0xda] = "($FFDA)",
        [0xdb] = "($FFDB)",
        [0xdc] = "($FFDC)",
        [0xdd] = "($FFDD)",
        [0xde] = "($FFDE)",
        [0xdf] = "($FFDF)",
        [0xe0] = "($FFE0)",
        [0xe1] = "($FFE1)",
        [0xe2] = "($FFE2)",
        [0xe3] = "($FFE3)",
        [0xe4] = "($FFE4)",
        [0xe5] = "($FFE5)",
        [0xe6] = "($FFE6)",
        [0xe7] = "($FFE7)",
        [0xe8] = "($FFE8)",
        [0xe9] = "($FFE9)",
        [0xea] = "($FFEA)",
        [0xeb] = "($FFEB)",
        [0xec] = "($FFEC)",
        [0xed] = "($FFED)",
        [0xee] = "($FFEE)",
        [0xef] = "($FFEF)",
        [0xf0] = "($FFF0)",
        [0xf1] = "($FFF1)",
        [0xf2] = "($FFF2)",
        [0xf3] = "($FFF3)",
        [0xf4] = "($FFF4)",
        [0xf5] = "($FFF5)",
        [0xf6] = "($FFF6)",
        [0xf7] = "($FFF7)",
        [0xf8] = "($FFF8)",
        [0xf9] = "($FFF9)",
        [0xfa] = "($FFFA)",
        [0xfb] = "($FFFB)",
        [0xfc] = "($FFFC)",
        [0xfd] = "($FFFD)",
        [0xfe] = "($FFFE)",
        [0xff] = "($FFFF)",

/* Dragon functions: */

        [0x80] = " SGN "            ,
        [0x81] = " INT "            ,
        [0x82] = " ABS "            ,
        [0x83] = " POS "            ,
        [0x84] = " RND "            ,
        [0x85] = " SQR "            ,
        [0x86] = " LOG "            ,
        [0x87] = " EXP "            ,
        [0x88] = " SIN "            ,
        [0x89] = " COS "            ,
        [0x8a] = " TAN "            ,
        [0x8b] = " ATN "            ,
        [0x8c] = " PEEK "           ,
        [0x8d] = " LEN "            ,
        [0x8e] = " STR$ "           ,
        [0x8f] = " VAL "            ,
        [0x90] = " ASC "            ,
        [0x91] = " CHR$ "           ,
        [0x92] = " EOF "            ,
        [0x93] = " JOYSTK "         ,
        [0x94] = " FIX "            ,
        [0x95] = " HEX$ "           ,
        [0x96] = " LEFT$ "          ,
        [0x97] = " RIGHT$ "         ,
        [0x98] = " MID$ "           ,
        [0x99] = " POINT "          ,
        [0x9a] = " INKEY$ "         ,
        [0x9b] = " MEM "            ,
        [0x9c] = " VARPTR "         ,
        [0x9d] = " INSTR "          ,
        [0x9e] = " TIMER "          ,
        [0x9f] = " PPOINT "         ,
        [0xa0] = " STRING$ "        ,
        [0xa1] = " USR "            ,

/*  Dragon DOS v1.0 tokens:  */

        [0xa2] = " LOF "            ,
        [0xa3] = " FREE "           ,
        [0xa4] = " ERL "            ,
        [0xa5] = " ERR "            ,
        [0xa6] = " HIMEM "          ,
        [0xa7] = " LOC "            ,
        [0xa8] = " FRE$ "           ,
};

struct df {
    /* header */
    unsigned id_0       : 8; /* [0] */
    unsigned id_1       : 8; /* [1] */
    unsigned start_hi   : 8; /* [2] */
    unsigned start_lo   : 8; /* [3] */
    unsigned len_hi     : 8; /* [4] */
    unsigned len_lo     : 8; /* [5] */
    unsigned exec_hi    : 8; /* [6] */
    unsigned exec_lo    : 8; /* [7] */
    unsigned id_8       : 8; /* [8] */

    /* data */
    u_int8_t data[65536 / 2];
};

int
dragon2txt(int offset) {

  /*
   * Layout of a Dragon file
   */

  struct df df;

  u_int16_t df_code_len;
  u_int16_t df_code_start;
  u_int16_t df_exec_start;

  register u_int8_t *ptr;

  fseek(stdin, offset, SEEK_SET);

  while(1) {

    if(fread(&df,9,1,stdin) == 0)
    {
        fprintf (stderr, "Error: file too short.\n");
	return 1;
        /* break; */
    }

    if(df.id_0 != 0x55 ||
       df.id_1 != 0x01 ||
       df.id_8 != 0xaa)
    {
        if(offset == 0)
	{
	  fprintf(stderr, "Error: not a Dragon DOS Basic file.\n");
	}
        break;
    }

    df_code_len   = (df.len_hi   << 8) | (df.len_lo);
    df_code_start = (df.start_hi << 8) | (df.start_lo);
    df_exec_start = (df.exec_hi  << 8) | (df.exec_lo);

    if(df_code_len >= (65536 / 2))
    {
        fprintf(stderr, "Error: file too long(>%d).\n", (65536 / 2));
        break;
    }

    fprintf(stdout,
            "REM "                                               "\n"
            "REM *************************************************\n"
	    "REM * Dragon file header:                           *\n"
            "REM * Start: $%04x Length: $%04x Exec: $%04x        *\n"
	    "REM *(Start: %05d Length: %05d Exec: %05d)       " "*\n"
            "REM *************************************************\n"
            "REM "                                               "\n",
            df_code_start, df_code_len, df_exec_start,
            df_code_start, df_code_len, df_exec_start);

    if(fread(&df.data[0], df_code_len, 1, stdin) == 0)
    {
        fprintf (stderr, "Error: file too short.\n");
        break;
    }

    ptr = &df.data[0];

    while(1) {
      u_int16_t next = *(ptr + 1) | (*(ptr    ) << 8);
      u_int16_t line = *(ptr + 3) | (*(ptr + 2) << 8);

      if(!next) break;

      fprintf(stdout,"%5u ", line);

      /* line of text */

      for(ptr += 4; ; ptr++)
      {
        if(ptr >= &df.data[(65536 / 2) -1])
        {
          fprintf(stderr, "Error: corrupted text line.\n");
          break;
        }

        if(*ptr == 0) break; /* end of line */

        if(*ptr == 0xff)
        {
                /* Dragon function */

                ptr++;

                if(*ptr == 0) break; /* end of line */

                fprintf(stdout, function[*ptr]);

        } else {

                /* Dragon keyword / letter */

		fprintf(stdout, keyword[*ptr]);
        }
      }

      fprintf(stdout, "\n");

      ptr = &df.data[next - df_code_start];

      if(ptr >= &df.data[(65536 / 2) -1])
      {
          fprintf(stderr, "Error: Illegal pointer to next line.\n");
          break;
      }
    }
  }

  return 0;
}

void
dragon2txt_raw() {

	 u_int16_t df_code_len;
	 u_int16_t df_code_start;
	 u_int16_t df_exec_start;
	 u_int16_t df_last_line_num = -1;
register u_int16_t x;
	 u_int16_t y;

	 u_int8_t   buffer[65536 / 2];
	 u_int8_t  *buf = &buffer[sizeof(buffer)];

  memset(&buffer,0xff,sizeof(buffer));

  while(1)
  {
    if(buf >= &buffer[sizeof(buffer) /2])
    {

      if(feof(stdin)) break;

      do {
	bcopy(&buffer[sizeof(buffer) / 2], &buffer[0], sizeof(buffer) / 2);

	if(fread(&buffer[sizeof(buffer) / 2],
		         sizeof(buffer) / 2, 1, stdin) != 0)
	{
	    /* check if end of file is reached! */

	    /* goto done; */
	}

	buf -= sizeof(buffer) / 2;

      } while (buf >= &buffer[sizeof(buffer) /2]);
    }

#define GET_NEXT_LINE(ptr) (*(((u_int8_t *)ptr) + 1) | (*(((u_int8_t *)ptr)    ) << 8))
#define GET_LINE_NUM(ptr) (*(((u_int8_t *)ptr) + 3) | (*(((u_int8_t *)ptr) + 2) << 8))

#define df ((struct df *)&buf[0])

    /* check for file header */
    if(df->id_0 == 0x55 &&
       df->id_1 == 0x01 &&
       df->id_8 == 0xaa)
    {
      df_code_len   = (df->len_hi   << 8) | (df->len_lo);
      df_code_start = (df->start_hi << 8) | (df->start_lo);
      df_exec_start = (df->exec_hi  << 8) | (df->exec_lo);

      if(df_code_len < (65536 / 2))
      {
	fprintf(stdout, "\n"
            "REM "                                               "\n"
            "REM *************************************************\n"
	    "REM * Dragon file header detected:                  *\n"
            "REM * Start: $%04x Length: $%04x Exec: $%04x        *\n"
	    "REM *(Start: %05d Length: %05d Exec: %05d)       " "*\n"
            "REM *************************************************\n"
            "REM "                                               "\n",
		df_code_start, df_code_len, df_exec_start,
		df_code_start, df_code_len, df_exec_start);

	fprintf(stdout,"%5u ", (df_last_line_num = GET_LINE_NUM(&buf[9])));

	/* skip header */
	buf += 9 + 4;
      }
    }

#undef df

    /* skip repeating data */
    if(*(u_int32_t *)&buf[0] == 0xf6f6f6f6 ||
       *(u_int32_t *)&buf[0] == 0xe5e5e5e5 ||
       *(u_int32_t *)&buf[0] == 0xffffffff) { buf += 4; continue; }

    /* check line end */
    if(buf[0] == 0)
    {
      u_int16_t tmp;
      u_int8_t token = 0;

      x = GET_LINE_NUM(&buf[1]);

#if 0
      printf("0x%x%x 0x%x%x\n", buf[0], buf[1], buf[2], buf[3]);
#endif

      /* zero is usually end of a line */
      fprintf(stdout, "\n");

      /* check if line numbering breaks */
      if(x <= df_last_line_num)
      {
      check_next_line:

	/* scan for the next line (assuming max 2048 letters) */
	for(y = 4; y < (sizeof(buffer) / 16); y++)
	{
	  /* next line has a token */
	  if(buf[y] & 0x80)
	  {
	    token = 1;
	  }

	  /* end of line */
	  if(buf[y] == 0)
	  {
	    if(token)
	    {
	      if(GET_LINE_NUM(&buf[y+1]) <= x)
	      {
		/* this is no BASIC file */
		break;
	      }

	      goto valid;
	    }

	    /* this is no BASIC file */
	    break;
	  }
	}

	if(df_last_line_num != (u_int16_t)-1) {
	   df_last_line_num = -1;

	   fprintf(stdout, 
		   "REM \n"
		   "REM line numbering breaks here!\n"
		   "REM \n");
	}
      }
      else
      {
	tmp = x - df_last_line_num;

	if(tmp >= 2000)
	{
	  goto check_next_line;
	}

      valid:

	df_last_line_num = x;

	fprintf(stdout, "%5d ", x); /* print line number */
	
	buf += 4;
      }
    }
    else
    if(buf[0] == 0xff)
    {
      /* Dragon function */

      if(buf[1] != 0x00)
      {
	fprintf(stdout, function[buf[1]]);

	/* next byte */
	buf += 1;
      }
    }
    else
    {
      /* Dragon keyword / letter */

      fprintf(stdout, keyword[buf[0]]);
    }

    /* next byte */
    buf += 1;
  }

  return;
}

void
dream2txt() {
  u_int len;
  u_int8_t buf[256];
  register u_int8_t *ptr = &buf[0];

  while(1) {

    len = fread(&buf[0], sizeof(buf), 1, stdin);

    if(len != 256) {
      if(len != 0) {
	fprintf(stderr,
		"Warning: Data block was "
		"less than 256 bytes.\n");
      }
      break;
    }

    /*
     * Process data block
     */

    ptr = &buf[0];
    len = sizeof(buf);

    while(len--) {
	  ptr++;

	  /* *(ptr -1) == length of text */

	  if(*(ptr -1) == 0) break;
	  if(*(ptr -1) > len) {
	    fprintf(stderr,
		    "Warning: Truncating data block to %d bytes.\n",
		    len);
	    *(ptr -1) = len;
	  }

	  fwrite(ptr, *(ptr -1), 1, stdout);

	  len -= *(ptr -1);
	  ptr += *(ptr -1);

	  /* add newline */

	  fwrite("\n", sizeof("\n"), 1, stdout);
    }
  }
}

int
main(int argc, char **argv) {

  int c;

  while ((c = getopt(argc, argv, "bBdDrRsS")) != -1)
  {
    switch(c) {
	case 's':
	case 'S':
		dragon2txt_raw();
		goto done;
	case 'r':
	case 'R':
		c = 0;
		do { } while(dragon2txt(c++) == 0);
		goto done;

	case 'b':
	case 'B':
		dragon2txt(0);
		goto done;

	case 'd':
	case 'D':
		dream2txt();
		goto done;

	default:
		goto usage;
    }
  }

 usage:
  fprintf(stderr,
  "dragon2txt - Dragon converter, version %d.%d, compiled %s %s"               "\n"
  "usage: cat *.BAS | dragon2txt -b | more"                                    "\n"
  "       -s"                                                                  "\n"
  "       -S       scan for Dragon DOS BASIC data"			       "\n"
  "       -r"                                                                  "\n"
  "       -R       recover Dragon DOS BASIC data to plain text"	               "\n"
  "       -b"                                                                  "\n"
  "       -B       convert Dragon DOS BASIC (.BAS) file to plain text"	       "\n"
  "       -d"                                                                  "\n"
  "       -D       convert Dragon DOS Dream (.DRM) file to plain text"	       "\n"
  "       -r"                                                                  "\n"
  "       -R       reverse conversion (not implemented yet)"                   "\n"
  , VERSION, STEP, __DATE__, __TIME__ );

 done:
  return 0;
}
