C — 32-битная и 16-битная арифметика на 8-битном микропроцессоре

Я пишу код для старого 8-битного микропроцессора (Hu6280 — производная от WDC 65C02 в старой консоли NEC PC-Engine) с 32 КБ оперативной памяти и до 2,5 МБ данных/кода. Язык представляет собой вариант Small-C, но ограничен только двумя следующими основными типами:

char (1 byte)
int (2 byte)

Он не имеет поддержки struct и поддержки long int.

Я пишу библиотеку файловой системы FAT для взаимодействия с устройством чтения SD-карт, которое было в первую очередь разработано для загрузки образов игрового ПЗУ, однако предприимчивый хакер написал некоторую сборку, позволяющую читать необработанные сектора со стороны консоли. Он достигает этого, вставляя 4 8-битных значения 32-битного адреса сектора в 4 последовательных адреса памяти (char address[4];). Мой код C использует его работу для чтения (на данный момент) загрузочного сектора DOS MBR и информации о типе раздела с SD-карты. У меня работает проверка контрольной суммы MBR и обнаружение разделов FAT.

Однако, поскольку мне нужно поддерживать FAT32 (это то, что поддерживает FPGA на устройстве с SD-картой), большая часть арифметических операций с секторами и кластерами для поиска записей каталогов и файлов будет основана на значениях 32-битных секторов LBA.

Какие простые механизмы у меня есть для добавления/вычитания/умножения 8/16/32-битных целых чисел, исходя из вышеуказанных ограничений? У кого-нибудь есть готовые подпрограммы C, чтобы справиться с этим? Может быть, что-то вроде:

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);`

РЕДАКТИРОВАТЬ: Основываясь на приведенных выше ответах, это то, что я придумал до сих пор - это еще не проверено, и мне нужно будет сделать то же самое для умножения и вычитания:

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

}

РЕДАКТИРОВАТЬ 2: вот обновленная и протестированная версия эмулированного 32-битного + 32-битного целочисленного кода добавления. Он работает со всеми значениями, которые я пробовал до сих пор. Переполнение для значений, превышающих 32-битное целое число без знака, не обрабатывается (для моих целей это не требуется):

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

}

Я также нашел некоторые ссылки на 32-битную арифметику на некоторых контроллерах PIC после поиска SO немного больше:

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

Несмотря на то, что в их коде добавления/вычитания есть некоторая встроенная сборка PIC, есть несколько полезных функций C, не зависящих от платформы, основанных на char, которые уже реализовали сдвиги, сравнения, увеличение/уменьшение и т. д., которые будут очень полезны. Я посмотрю на вычитание и умножение в следующий раз - спасибо за информацию; Думаю, я смотрел на вещи и думал, что они намного сложнее, чем должны быть.


person John Snowdon    schedule 07.01.2014    source источник
comment
16-битная арифметика на 8-битном процессоре — известная и давно решаемая проблема, быстрый поиск в любимом поисковике должен дать много результатов. Расширение этого до 32-битной арифметики будет работать точно так же.   -  person Some programmer dude    schedule 07.01.2014


Ответы (1)


Я знаю, что ты знаешь, как это сделать. вернитесь к школьной математике...

Когда вы умножаете на числа, основание 10

 12
x34
====

Вы делаете четыре умножения правильно, а затем складываете четыре числа, верно?

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

тогда

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

Теперь, что о дополнении

 12
+34
===

Мы научились разбивать это на два дополнения

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

С этими знаниями, которые у вас уже есть с детства, вы затем просто применяете их. помните, что базовая математика работает независимо от того, имеем ли мы 2 цифры, оперированные 2 цифрами, или 2 миллиона цифр, оперированные 2 миллионами цифр.

Вышеупомянутое использует одиночные десятичные числа, но математика работает, если это были одиночные числа с основанием 16 или одиночные биты, восьмеричные или байты и т. д.

Ваш компилятор C уже должен обрабатывать эти вещи за вас, но если вам нужно их синтезировать, вы можете это сделать, умножение в самой простой форме для цифровых вычислений — это использование битов.

сложение проще с байтами, использующими ассемблер, потому что перенос находится прямо здесь, C не имеет переноса, поэтому вам нужно выполнить упражнение по вычислению переноса с использованием 8-битной математики (его можно определить) без необходимости 9-го бита . или вы можете просто сделать что-то меньшее, чем 8-битная математика, 7 или 4 или что-то еще.

Как отметил Иоахим, эта тема была избита до смерти десятки/столетия назад. В то же время он настолько прост, что часто не требует большого обсуждения. StackOverflow определенно освещал эту тему несколько раз.

person old_timer    schedule 07.01.2014