简体   繁体   中英

C - 32bit & 16bit arithmetic on 8bit microprocessor

I'm writing some code for an old 8-bit microprocessor (the Hu6280 - a WDC 65C02 derivative in the old NEC PC-Engine console) with 32kb of ram and up to 2.5mbytes of data/code rom. The language is a variant of Small-C but is limited to just the two following basic types:

char (1 byte)
int (2 byte)

It has no struct support and no long int support.

I'm writing a FAT filesystem library to interface with a SD card reader that was primarily developed for loading game ROM images, however an enterprising hacker has written some assembly to allow raw sector reading from the console side. He achieves this by stuffing the 4 8bit values of a 32bit sector address into 4 consecutive memory addresses (char address[4];). My C code leverages his work to read (for the moment) the dos MBR boot sector and partition type information off the SD card. I've got MBR checksum verifcation and FAT partition detection working.

However, as I need to support FAT32 (which is what the FPGA on the SD card device supports), most of the sector and cluster arithmetic to look up directory entries and files will be based on 32bit LBA sector values.

What easy mechanisms do I have to do add/subtract/multiply 8/16/32bit integers, based on the above limitations? Does anyone have any ready made C routines to handle this? Maybe something along the lines of:

char int1[4], int2[4], int3[4];

int1[0] = 1;
int1[1] = 2;
int1[2] = 3;
int1[3] = 4;

int2[0] = 4;
int2[1] = 3;
int2[2] = 2;
int2[3] = 1;

int3 = mul_32(int1, int2);
int3 = add_32(int1, int2);
int3 = sub_32(int1, int2);`

EDIT: Based on the above replies, this is what I've come up with so far - this is untested as yet and I'll need to do similar for multiplication and subtraction:

char_to_int32(int32_result, int8)
char* int32_result;
char int8;
{
/* 
    Takes an unsigned 8bit number
    and converts to a packed 4 byte array
*/
int32_result[0] = 0x00;
int32_result[1] = 0x00;
int32_result[2] = 0x00;
int32_result[3] = int8;

return 0;
}

int_to_int32(int32_result, int16)
char*   int32_result;
int         int16;
{
/* 
    Takes an unsigned 16bit number
    and converts to a packed 4 byte array
*/

int32_result[0] = 0x00;
int32_result[1] = 0x00;
int32_result[2] = (int16 >> 8);
int32_result[3] = (int16 & 0xff);

return 0;
}


int32_is_zero(int32)
char*   int32;
{
/* 
    Is a packed 4 byte array == 0
    returns 1 if true, otherwise 0
*/

if ((int32[0] == 0) & (int32[1] == 0) & (int32[2] == 0) & (int32[3] == 0)) {
    return 1;
} else {
    return 0;
}

}

add_32(int32_result, int32_a, int32_b)
char*   int32_result;
char*   int32_a;
char*   int32_b;
{
/* 
    Takes two 32bit values, stored as 4 bytes each - 
    adds and stores the result.

    Returns 0 on success, 1 on error or overflow.
*/

int         sum;
char    i;
char    carry;

carry = 0x00;
/* loop over each byte of the 4byte array */
for (i = 4; i != 0; i--) {
    /* sum the two 1 byte numbers as a 2 byte int */
    sum = int32_a[i-1] + int32_b[i-1] + carry;
    /* would integer overflow occur with this sum? */
    if (sum > 0x00ff) {
        /* store the most significant byte for next loop */
        carry = (sum >> 8);
    } else {
        /* no carry needed */
        carry = 0x00        
    }
    /* store the least significant byte */
    int32_result[i+1] = (sum & 0xff);
}

/* Has overflow occured (ie number > 32bit) */
if (carry != 0) {
    return 1;
} else {
    return 0;
}

}

EDIT 2: Here's an updated and tested version of the emulated 32bit + 32bit integer add code. It works with all values I've tried so far. Overflow for values bigger than a 32bit unsigned integer is not handled (will not be required for my purposes):

add_int32(int32_result, int32_a, int32_b)
char*   int32_result;
char*   int32_a;
char*   int32_b;
    {
    /* 
        Takes two 32bit values, stored as 4 bytes each - 
        adds and stores the result.
        Returns 0 on success, 1 on error or overflow.
    */

    int     sum;
    char    i, pos;
    char    carry;

    zero_int32(int32_result);

    carry = 0x00;
    /* loop over each byte of the 4byte array from lsb to msb */
    for (i = 1; i < 5; i++) {
        pos = 4 - i; 
        /* sum the two 1 byte numbers as a 2 byte int */
        sum = int32_a[pos] + int32_b[pos] + carry;
        /* would integer overflow occur with this sum? */
        if (sum > 0x00ff) {
            /* store the most significant byte for next loop */
            carry = (sum >> 8);
        } else {
            /* no carry needed */
            carry = 0x00;   
        }
        /* store the least significant byte */
        int32_result[pos] = (sum & 0x00ff);
    }

    /* Has overflow occured (ie number > 32bit) */
    if (carry != 0) {
        return 1;
    } else {
        return 0;
    }

}

I also found some references to 32bit arithmetic on some PIC controllers after searching SO a bit more:

http://web.media.mit.edu/~stefanm/yano/picc_Math32.html

Although there is some PIC assembly inline in their add/subtract code, there are some useful platform agnostic char-based C functions there that have already implemented shifts, comparisons, increment/decrement etc, which will be very useful. I will look into subtract and multiply next - thanks for the info; I guess I was looking at things and thinking they were much harder than they needed to be.

I know you know how to do this. go back to your grade school math...

When you multiply to numbers, base 10

 12
x34
====

You do four multiplications right and then add four numbers together right?

4x2 = 8
4x1 = 4
3x2 = 6
3x1 = 3

then

   12
  x34
 ====
 0008
 0040
 0060
+0300
======

Now what about addition

 12
+34
===

We learned to break that down into two additions

2+4 = 6 carry a zero
1+3+carryin of 0 = 4

With that knowledge that you already have from childhood, you then simply apply that. remember that basic math works whether we have 2 digits operated on 2 digits or 2 million digits operated on 2 million digits.

The above uses single decimal numbers, but the math works if it were single base 16 numbers or single bits or octal or bytes, etc.

Your C compiler should already be handling these things for you but if you need to synthesize them you can, multiplication at the easiest form for digital is to use bits.

addition is easier with bytes using assembly because the carry out is right there, C does not have a carry out so you have to do the exercise of figuring out the carry out using 8 bit math (it can be determined) without needing a 9th bit. or you can just do something less than 8 bit math, 7 or 4 or whatever.

As Joachim pointed out, this topic hsa been beat to death decades/centuries ago. At the same time it is so simple that it often doesnt warrent a lot of discussion. StackOverflow certainly has this topic covered several times over.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM