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];
}
}