The Relogix translation

Note: This is a 100% automatic translation, with no human intervention or cleaning-up of the code. Relogix has automatically chosen the function prototypes, has chosen the variable types itself, and has placed the assembler comments in the translated source allowing for the re-arrangement of the code flow. It has also recovered complex control statements like 'for' loops and 'switch' statements.

The code is not quite ready to use. It needs a little tidying up of some of the variable declarations, and some minor changes to handle pointers correctly. However, it should be easy to see that the final code will be high quality C which is easy to read and maintain.



/************************************************************************
*                                                                       *
*       "xmodem.c" - Translated from file "xmodem.asm"                  *
*                                                                       *
*-----------------------------------------------------------------------*
*                                                                       *
*       Relogix Code Translator Copyright (c) MicroAPL 1990-2017        *
*                   All Rights Reserved Worldwide                       *
*                                                                       *
*************************************************************************
*/


/*
* Originally downloaded from: http://www.6502.org/source/io/xmodem/xmodem.txt
*
* XMODEM/CRC Sender/Receiver for the 65C02
*
* By Daryl Rictor Aug 2002
*
* A simple file transfer program to allow transfers between the SBC and a
* console device utilizing the x-modem/CRC transfer protocol.  Requires
* ~1200 bytes of either RAM or ROM, 132 bytes of RAM for the receive buffer,
* and 12 bytes of zero page RAM for variable storage.
*
***************************************************************************
* This implementation of XMODEM/CRC does NOT conform strictly to the
* XMODEM protocol standard in that it (1) does not accurately time character
* reception or (2) fall back to the Checksum mode.
* (1) For timing, it uses a crude timing loop to provide approximate
* delays.  These have been calibrated against a 1MHz CPU clock.  I have
* found that CPU clock speed of up to 5MHz also work but may not in
* every case.  Windows HyperTerminal worked quite well at both speeds!
*
* (2) Most modern terminal programs support XMODEM/CRC which can detect a
* wider range of transmission errors so the fallback to the simple checksum
* calculation was not implemented to save space.
***************************************************************************
*
* Files transferred via XMODEM-CRC will have the load address contained in
* the first two bytes in little-endian format:
*  FIRST BLOCK
*     offset(0) = lo(load start address),
*     offset(1) = hi(load start address)
*     offset(2) = data byte (0)
*     offset(n) = data byte (n-2)
*
* Subsequent blocks
*     offset(n) = data byte (n)
*
* One note, XMODEM send 128 byte blocks.  If the block of memory that
* you wish to save is smaller than the 128 byte block boundary, then
* the last block will be padded with zeros.  Upon reloading, the
* data will be written back to the original location.  In addition, the
* padded zeros WILL also be written into RAM, which could overwrite other
* data.
*
*-------------------------- The Code ----------------------------
*
* zero page variables (adjust these to suit your needs)
*
*
*/


#include "rlx6502.h"
#include "xmodem_hdr.h"
#include "xmodem.h"

#define lastblk              (*(char *) 0x00000035)
#define blkno                (*(unsigned char *) 0x00000036)
#define errcnt               (*(char *) 0x00000037)
#define bflag                (*(unsigned char *) 0x00000037)
#define crc                  (*(unsigned char *) 0x00000038)
#define crch                 (*(unsigned char *) 0x00000039)
#define ptr                  (*(unsigned char *) 0x0000003a)
#define ptrh                 (*(unsigned char *) 0x0000003b)
#define eofp                 (*(unsigned char *) 0x0000003c)
#define eofph                (*(unsigned char *) 0x0000003d)
#define retry                (*(char *) 0x0000003e)
#define retry2               (*(unsigned char *) 0x0000003f)
#define SOH                  0x01                /* start block */
#define EOT                  0x04                /* end of text marker */
#define ACK                  0x06                /* good block acknowledged */
#define NAK                  0x15                /* bad block acknowledged */
#define CR                   0x0d                /* carriage return */
#define LF                   0x0a                /* line feed */
#define ESC                  0x1b                /* ESC to exit */
#define ACIA_Data            (*(unsigned char *) 0x00007f70)
#define ACIA_Status          (*(unsigned char *) 0x00007f71)
#define ACIA_Command         (*(char *) 0x00007f72)
#define ACIA_Control         (*(char *) 0x00007f73)

/* Prototypes for private routines translated to C */
static void Put_Chr (unsigned char a);
static void Flush (void);
static void PrintMsg (void);
static void Print_Err (void);
static void Print_Good (void);
static void CalcCRC (void);



/* Private file-scope variables */

/* non-zero page variables and buffers */
static unsigned char Rbuff [0];                  /* temp 132 byte receive buffer
                                                    (place anywhere, page aligned) */
static unsigned char Msg [] = 
{
  "Begin XMODEM/CRC transfer.  Press <Esc> to abort..."
  "\r\n"
};

static unsigned char ErrMsg [] = 
{
  "Transfer Error!"
  "\r\n"
};

static unsigned char GoodMsg [] = 
{
  EOT, CR,  LF,  EOT, CR,  LF,  EOT, CR,  LF,  CR,  LF, 
  'T', 'r', 'a', 'n', 's', 'f', 'e', 'r', ' ', 'S', 'u', 'c', 
  'c', 'e', 's', 's', 'f', 'u', 'l', '!', 
  CR,  LF, 
        0
};

/*  Alternate solution is to build the two lookup tables at run-time.  This might
    be desirable if the program is running from ram to reduce binary upload time.
    The following code generates the data for the lookup tables.  You would need to
    un-comment the variable declarations for crclo & crchi in the Tables and Constants
    section above and call this routine to build the tables before calling the
    "xmodem" routine.
   MAKECRCTABLE
                  ldx     #$00
                  LDA     #$00
   zeroloop       sta     crclo,x
                  sta     crchi,x
                  inx
                  bne     zeroloop
                  ldx     #$00
   fetch          txa
                  eor     crchi,x
                  sta     crchi,x
                  ldy     #$08
   fetch1         asl     crclo,x
                  rol     crchi,x
                  bcc     fetch2
                  lda     crchi,x
                  eor     #$10
                  sta     crchi,x
                  lda     crclo,x
                  eor     #$21
                  sta     crclo,x
   fetch2         dey
                  bne     fetch1
                  inx
                  bne     fetch
                  rts
    The following tables are used to calculate the CRC for the 128 bytes
    in the xmodem data blocks.  You can use these tables if you plan to
    store this program in ROM.  If you choose to build them at run-time,
    then just delete them and define the two labels: crclo & crchi.
    low byte CRC lookup table (should be page aligned) */
static unsigned char crclo [] = 
{
        0x00, 0x21, 0x42, 0x63, 0x84, 0xa5, 0xc6, 0xe7, 0x08, 0x29, 0x4a, 0x6b, 0x8c, 0xad, 0xce, 0xef, 
        0x31, 0x10, 0x73, 0x52, 0xb5, 0x94, 0xf7, 0xd6, 0x39, 0x18, 0x7b, 0x5a, 0xbd, 0x9c, 0xff, 0xde, 
        0x62, 0x43, 0x20, 0x01, 0xe6, 0xc7, 0xa4, 0x85, 0x6a, 0x4b, 0x28, 0x09, 0xee, 0xcf, 0xac, 0x8d, 
        0x53, 0x72, 0x11, 0x30, 0xd7, 0xf6, 0x95, 0xb4, 0x5b, 0x7a, 0x19, 0x38, 0xdf, 0xfe, 0x9d, 0xbc, 
        0xc4, 0xe5, 0x86, 0xa7, 0x40, 0x61, 0x02, 0x23, 0xcc, 0xed, 0x8e, 0xaf, 0x48, 0x69, 0x0a, 0x2b, 
        0xf5, 0xd4, 0xb7, 0x96, 0x71, 0x50, 0x33, 0x12, 0xfd, 0xdc, 0xbf, 0x9e, 0x79, 0x58, 0x3b, 0x1a, 
        0xa6, 0x87, 0xe4, 0xc5, 0x22, 0x03, 0x60, 0x41, 0xae, 0x8f, 0xec, 0xcd, 0x2a, 0x0b, 0x68, 0x49, 
        0x97, 0xb6, 0xd5, 0xf4, 0x13, 0x32, 0x51, 0x70, 0x9f, 0xbe, 0xdd, 0xfc, 0x1b, 0x3a, 0x59, 0x78, 
        0x88, 0xa9, 0xca, 0xeb, 0x0c, 0x2d, 0x4e, 0x6f, 0x80, 0xa1, 0xc2, 0xe3, 0x04, 0x25, 0x46, 0x67, 
        0xb9, 0x98, 0xfb, 0xda, 0x3d, 0x1c, 0x7f, 0x5e, 0xb1, 0x90, 0xf3, 0xd2, 0x35, 0x14, 0x77, 0x56, 
        0xea, 0xcb, 0xa8, 0x89, 0x6e, 0x4f, 0x2c, 0x0d, 0xe2, 0xc3, 0xa0, 0x81, 0x66, 0x47, 0x24, 0x05, 
        0xdb, 0xfa, 0x99, 0xb8, 0x5f, 0x7e, 0x1d, 0x3c, 0xd3, 0xf2, 0x91, 0xb0, 0x57, 0x76, 0x15, 0x34, 
        0x4c, 0x6d, 0x0e, 0x2f, 0xc8, 0xe9, 0x8a, 0xab, 0x44, 0x65, 0x06, 0x27, 0xc0, 0xe1, 0x82, 0xa3, 
        0x7d, 0x5c, 0x3f, 0x1e, 0xf9, 0xd8, 0xbb, 0x9a, 0x75, 0x54, 0x37, 0x16, 0xf1, 0xd0, 0xb3, 0x92, 
        0x2e, 0x0f, 0x6c, 0x4d, 0xaa, 0x8b, 0xe8, 0xc9, 0x26, 0x07, 0x64, 0x45, 0xa2, 0x83, 0xe0, 0xc1, 
        0x1f, 0x3e, 0x5d, 0x7c, 0x9b, 0xba, 0xd9, 0xf8, 0x17, 0x36, 0x55, 0x74, 0x93, 0xb2, 0xd1, 0xf0
};

/* hi byte CRC lookup table (should be page aligned) */
static unsigned char crchi [] = 
{
        0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x81, 0x91, 0xa1, 0xb1, 0xc1, 0xd1, 0xe1, 0xf1, 
        0x12, 0x02, 0x32, 0x22, 0x52, 0x42, 0x72, 0x62, 0x93, 0x83, 0xb3, 0xa3, 0xd3, 0xc3, 0xf3, 0xe3, 
        0x24, 0x34, 0x04, 0x14, 0x64, 0x74, 0x44, 0x54, 0xa5, 0xb5, 0x85, 0x95, 0xe5, 0xf5, 0xc5, 0xd5, 
        0x36, 0x26, 0x16, 0x06, 0x76, 0x66, 0x56, 0x46, 0xb7, 0xa7, 0x97, 0x87, 0xf7, 0xe7, 0xd7, 0xc7, 
        0x48, 0x58, 0x68, 0x78, 0x08, 0x18, 0x28, 0x38, 0xc9, 0xd9, 0xe9, 0xf9, 0x89, 0x99, 0xa9, 0xb9, 
        0x5a, 0x4a, 0x7a, 0x6a, 0x1a, 0x0a, 0x3a, 0x2a, 0xdb, 0xcb, 0xfb, 0xeb, 0x9b, 0x8b, 0xbb, 0xab, 
        0x6c, 0x7c, 0x4c, 0x5c, 0x2c, 0x3c, 0x0c, 0x1c, 0xed, 0xfd, 0xcd, 0xdd, 0xad, 0xbd, 0x8d, 0x9d, 
        0x7e, 0x6e, 0x5e, 0x4e, 0x3e, 0x2e, 0x1e, 0x0e, 0xff, 0xef, 0xdf, 0xcf, 0xbf, 0xaf, 0x9f, 0x8f, 
        0x91, 0x81, 0xb1, 0xa1, 0xd1, 0xc1, 0xf1, 0xe1, 0x10, 0x00, 0x30, 0x20, 0x50, 0x40, 0x70, 0x60, 
        0x83, 0x93, 0xa3, 0xb3, 0xc3, 0xd3, 0xe3, 0xf3, 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 
        0xb5, 0xa5, 0x95, 0x85, 0xf5, 0xe5, 0xd5, 0xc5, 0x34, 0x24, 0x14, 0x04, 0x74, 0x64, 0x54, 0x44, 
        0xa7, 0xb7, 0x87, 0x97, 0xe7, 0xf7, 0xc7, 0xd7, 0x26, 0x36, 0x06, 0x16, 0x66, 0x76, 0x46, 0x56, 
        0xd9, 0xc9, 0xf9, 0xe9, 0x99, 0x89, 0xb9, 0xa9, 0x58, 0x48, 0x78, 0x68, 0x18, 0x08, 0x38, 0x28, 
        0xcb, 0xdb, 0xeb, 0xfb, 0x8b, 0x9b, 0xab, 0xbb, 0x4a, 0x5a, 0x6a, 0x7a, 0x0a, 0x1a, 0x2a, 0x3a, 
        0xfd, 0xed, 0xdd, 0xcd, 0xbd, 0xad, 0x9d, 0x8d, 0x7c, 0x6c, 0x5c, 0x4c, 0x3c, 0x2c, 0x1c, 0x0c, 
        0xef, 0xff, 0xcf, 0xdf, 0xaf, 0xbf, 0x8f, 0x9f, 0x6e, 0x7e, 0x4e, 0x5e, 0x2e, 0x3e, 0x0e, 0x1e
};


/*
*************************************************************************
* XModemSend                                                            *
*************************************************************************
*/
void XModemSend (void)
{
    unsigned char i;                             /* [Originally in X] */
    unsigned char rx_char;                       /* [Originally in A] */

    PrintMsg ();                                 /* send prompt and info */
    errcnt = 0x00;                               /* error counter set to 0 */
    lastblk = 0x00;                              /* set flag to false */
    blkno = 0x01;                                /* set block # to 1 */

    while (True) {
        retry2 = 0xff;                           /* 3 seconds */
        if (GetByte (&rx_char)) {
            /* is it the "C" to start a CRC xfer? */
            if (rx_char == 'C')
                break;

            /* is it a cancel? <Esc> Key */
            if (rx_char == ESC) {
                Flush ();                        /* yes, too many errors, flush buffer, */
                Print_Err ();
                return;
            }
        }
    }

    i = 0x04;                                    /* init data block offset to 0
                                                    preload X to Receive buffer */
    Rbuff [0] = 0x01;                            /* manually load blk number
                                                    into 1st byte */
    Rbuff [1] = 0xfe;                            /* load 1's comp of block #
                                                    into 2nd byte */
    Rbuff [2] = ptr;                             /* load low byte of start address
                                                    into 3rd byte */
    Rbuff [3] = ptrh;                            /* load hi byte of start address
                                                    into 4th byte
                                                    jump into buffer load routine */

    while (True) {
        Rbuff [i] = *(unsigned char *) *(short *) &ptr;
                                                 /* save 128 bytes of data */

        /* Are we at the last address? */
        if (eofp == ptr && eofph == ptrh) {
            lastblk++;                           /* Yes, Set last byte flag */
            /* Are we at the end of the 128 byte block? */
            while (++i != 0x82)
                Rbuff [i] = 0x00;                /* Fill rest of 128 bytes with $00
                                                    Branch always */
        } else {
            if (++ptr == 0)
                ptrh++;

            /* last byte in block? */
            if (++i != 0x82)
                continue;
        }

        CalcCRC ();
        Rbuff [0x82] = crch;                     /* save Hi byte of CRC to buffer */
        Rbuff [0x83] = crc;                      /* save lo byte of CRC to buffer */

        while (True) {
            Put_Chr (SOH);                       /* send SOH */
            for (i = 0x00; i < 0x84; i++)
                Put_Chr (Rbuff [i]);             /* Send 132 bytes in buffer to the console */

            retry2 = 0xff;                       /* yes, set 3 second delay
                                                    and */

            /* Wait for Ack/Nack */
            if (GetByte (&rx_char)) {
                /* Chr received... is it:
                   ACK, send next block */
                if (rx_char == ACK)
                    break;

                /* NAK, inc errors and resend */
                if (rx_char == ESC && ESC != NAK) {
                    Flush ();                    /* yes, too many errors, flush buffer, */
                    Print_Err ();
                    return;
                }
            }

            /* are there 10 errors? (Xmodem spec for failure) */
            if (++errcnt == 0x0a) {
                Flush ();                        /* yes, too many errors, flush buffer, */
                Print_Err ();
                return;
            }
        }

        /* Was the last block sent? */
        if (lastblk != 0)
            break;
        i = 0x02;                                /* init pointers */
        Rbuff [0] = ++blkno;                     /* save in 1st byte of buffer */
        Rbuff [1] = ~blkno;                      /* save 1's comp of blkno next */
    }

    Print_Good ();
}


/*
*************************************************************************
* XModemRcv                                                             *
*************************************************************************
*/
void XModemRcv (void)
{
    Boolean break_to_badcrc;
    unsigned char i;                             /* [Originally in X] */
    unsigned char rx_char;                       /* [Originally in A] */

    PrintMsg ();                                 /* send prompt and info */
    blkno = 0x01;                                /* set block # to 1 */
    bflag = 0x01;                                /* set flag to get address from block 1 */
    do {
        Put_Chr ('C');                           /* "C" start with CRC mode
                                                    send it */
        retry2 = 0xff;                           /* set loop counter for ~3 sec delay */
        crc = 0x00;
        crch = 0x00;                             /* init CRC value */
    } while (!GetByte (&rx_char));

    while (True) {
        /* quitting? */
        switch (rx_char) {
        case SOH:
            break_to_badcrc = False;

            for (i = 0x00; i < 0x84; i++) {
                retry2 = 0xff;                   /* 3 sec window to receive characters */

                /* get next character */
                if (!GetByte (&rx_char)) {
                    break_to_badcrc = True;
                    break;
                }
                Rbuff [i] = rx_char;             /* good char, save it in the rcv buffer */
            }

            if (break_to_badcrc) {
                Flush ();                        /* flush the input port */
                Put_Chr (NAK);                   /* send NAK to resend block */
            } else if (Rbuff [0] == blkno) {
                /* 1's comp of block #
                   compare with expected 1's comp of block #
                   matched! */
                if (~Rbuff [0] != Rbuff [0x1]) {
                    Print_Err ();                /* Unexpected block number - abort */
                    Flush ();                    /* mismatched - flush buffer and then do BRK */
                    return;
                }
                CalcCRC ();                      /* calc CRC */

                /* get hi CRC from buffer
                   compare to calculated hi CRC
                   bad crc, send NAK
                   get lo CRC from buffer
                   compare to calculated lo CRC
                   good CRC */
                if (Rbuff [0x82] == crch && Rbuff [0x83] == crc) {
                    i = 0x02;

                    /* get the block number
                       1st block?
                       is it really block 1, not block 257, 513 etc. */
                    if (blkno == 0x01 && bflag != 0) {
                        ptr = Rbuff [0x02];      /* get target address from 1st 2 bytes of blk 1
                                                    save lo address */
                        ptrh = Rbuff [0x3];      /* get hi address
                                                    save it */
                        i = 0x4;                 /* point to first byte of data */
                        bflag--;                 /* set the flag so we won't get another address */
                    }

                    do {
                        *(unsigned char *) *(short *) &ptr = Rbuff [i];
                                                 /* set offset to zero
                                                    get data byte from buffer
                                                    save to target */
                        if (++ptr == 0)
                            ptrh++;              /* adjust high address for page crossing */
                    } while (++i != 0x82);

                    blkno++;                     /* done.  Inc the block # */
                    Put_Chr (ACK);               /* send ACK */

                } else {
                    Flush ();                    /* flush the input port */
                    Put_Chr (NAK);               /* send NAK to resend block */
                }
            } else {
                Print_Err ();                    /* Unexpected block number - abort */
                Flush ();                        /* mismatched - flush buffer and then do BRK */
                return;
            }
            break;

        case EOT:
            Put_Chr (ACK);                       /* last block, send ACK and exit. */
            Flush ();                            /* get leftover characters, if any */
            Print_Good ();
            return;

        case ESC:
            return;

        default:
            Flush ();                            /* flush the input port */
            Put_Chr (NAK);                       /* send NAK to resend block */
            break;
        }

        do {
            retry2 = 0xff;                       /* set loop counter for ~3 sec delay */
        } while (!GetByte (&rx_char));
    }

    Put_Chr (ACK);                               /* last block, send ACK and exit. */
    Flush ();                                    /* get leftover characters, if any */
    Print_Good ();
}


/*
*************************************************************************
* Get_Chr                                                               *
*************************************************************************
*
* input chr from ACIA (no waiting)
*
* Parameters:
*
*    unsigned char *rx_char                         [Originally in A; Out]
*
* Result:
*
*    Boolean                                        [Originally condition CS]
*/
Boolean Get_Chr (unsigned char *rx_char)
{
    Boolean result;                              /* [Originally condition CS] */

    result = False;                              /* no chr present */
    *rx_char = ACIA_Status & 0x08;               /* get Serial port status
                                                    mask rcvr full bit
                                                    if not chr, done */
    if (*rx_char != 0) {
        *rx_char = ACIA_Data;                    /* else get chr */
        result = True;                           /* and set the Carry Flag */
    }
    return result;
}


/*
*************************************************************************
* Put_Chr                                                               *
*************************************************************************
*
* output to OutPut Port
*
* Parameters:
*
*    unsigned char a                                [Originally in A; In]
*/
static void Put_Chr (unsigned char a)
{
    /* serial port status
       is tx buffer empty */
    while ((ACIA_Status & 0x10) == 0)
        ;

    ACIA_Data = a;                               /* put character to Port */
}


/*
*************************************************************************
* GetByte                                                               *
*************************************************************************
*
*
* subroutines
*
* Parameters:
*
*    unsigned char *rx_char                         [Originally in A; Out]
*
* Result:
*
*    Boolean                                        [Originally condition CS]
*/
Boolean GetByte (unsigned char *rx_char)
{
    retry = 0x00;                                /* wait for chr input and cycle timing loop
                                                    set low value of timing loop */
    do {
        /* get chr from serial port, don't wait */
        if (Get_Chr (rx_char))
            return True;
    } while (--retry != 0 || --retry2 != 0);

    return False;                                /* if loop times out, CLC, else SEC and return */
}


/*
*************************************************************************
* Flush                                                                 *
*************************************************************************
*/
static void Flush (void)
{
    unsigned char rx_char;                       /* [Originally in A] */

    do {
        retry2 = 0x70;                           /* flush receive buffer
                                                    flush until empty for ~1 sec. */
    } while (GetByte (&rx_char));
}


/*
*************************************************************************
* PrintMsg                                                              *
*************************************************************************
*/
static void PrintMsg (void)
{
    unsigned char i;                             /* [Originally in X] */

    i = 0x00;                                    /* PRINT starting message */
    do {
        if (Msg [i] == 0)
            break;
        Put_Chr (Msg [i]);
    } while (++i != 0);
}


/*
*************************************************************************
* Print_Err                                                             *
*************************************************************************
*/
static void Print_Err (void)
{
    unsigned char i;                             /* [Originally in X] */

    i = 0x00;                                    /* PRINT Error message */
    do {
        if (ErrMsg [i] == 0)
            break;
        Put_Chr (ErrMsg [i]);
    } while (++i != 0);
}


/*
*************************************************************************
* Print_Good                                                            *
*************************************************************************
*/
static void Print_Good (void)
{
    unsigned char i;                             /* [Originally in X] */

    i = 0x00;                                    /* PRINT Good Transfer message */
    do {
        if (GoodMsg [i] == 0)
            break;
        Put_Chr (GoodMsg [i]);
    } while (++i != 0);
}


/*
*************************************************************************
* CalcCRC                                                               *
*************************************************************************
*
*
*  CRC subroutines
*/
static void CalcCRC (void)
{
    unsigned char i;                             /* [Originally in Y] */
    unsigned char x;                             /* [Originally in X] */

    crc = 0x00;                                  /* yes, calculate the CRC for the 128 bytes */
    crch = 0x00;
    for (i = 0x02; i < 0x82; i++) {
        x = Rbuff [i] ^ crch;                    /* Quick CRC computation with lookup tables
                                                    updates the two bytes at crc & crc+1 */
        crch = crc ^ crchi [x];                  /* with the byte send in the "A" register */
        crc = crclo [x];
    }
}