2015-03-30 2 views
3

В настоящее время я разрабатываю эмулятор игрового себе и проверяю правильность своего эмулятора. Я использую GBDK для компиляции c-программ для моего эмулятора.Ошибка с умножением силы двух в компиляторе GBDK

Я заметил, что компилятор (как и ожидалось) оптимизирует умножения с константами, которые являются силой двух, делая вращения. Однако кажется, что он не генерирует правильное количество вращений для данной мощности.

Например, следующая очень простая программа:

#include <gb/gb.h> 

unsigned char one() { return 1; } 

void main() 
{ 
    unsigned char r; 

    // Force compiler to generate muliplication by deferring the '1' 
    r = one() * 32; 

    // Store result somewhere 
    *((unsigned char*)(0xFFFE)) = r; 
} 

Создает следующие сборки:

___main_start: 
_main: 
    push bc 
; reproduce.c 14 
; genCall 
    call _one 
    ld c,e 
; genLeftShift 
    ld a,c 
    rr a 
    rr a 
    rr a 
    and a,#0xE0 
    ld c,a 
; reproduce.c 16 
; genAssign 
    ld de,#0xFFFE 
; genAssign (pointer) 
    ld a,c 
    ld (de),a 
; genLabel 
00101$: 
; genEndFunction 
    pop bc 
    ret 
___main_end: 
    .area _CODE 

Что мне кажется неправильным, как команда RR на самом деле вращается через флаг переноса, эффективно что делает его 9-битным вращением. Это означает, что должен быть дополнительный поворот для получения правильного результата вместо текущего (0x40) неправильного результата.

Визуализация:

Start: A = 00000001 Carry = 0 
RR A: A = 00000000 Carry = 1 
RR A: A = 10000000 Carry = 0 
RR A: A = 01000000 Carry = 0 <- WRONG! 

Может кто-нибудь проверить, что это действительно ошибка в SDCC компилятор, который поставляется с GBDK? Я также заинтересован в использовании команды и инструкции после поворота.

Использование последней версии (3-2.93) версии GBDK для окон из исходной формы.

+1

Версия SDCC, которая поставляется с GBDK, как представляется, 2.2.1, что-то вроде 15 лет , Последний стабильный выпуск SDCC - 3.4.0, который с апреля 2014 года. Получаете ли вы тот же сгенерированный код, если обновляете версию SDCC? – Michael

+0

Я считаю, что SDCC, который поставляется с GBDK, является вилкой, которая модифицирована для создания gb-совместимого кода z80 (по крайней мере исходный код sdcc включен в источник gbdk). Но да, GBDK древний, но я бы подумал, что что-то общее, как это будет работать. – monoceres

+0

Возможно, у проекта SDCC не было поддержки GB-Z80 в то время. Но я считаю, что это происходит сейчас. Я согласен, что это похоже на общий сценарий. OTOH, SDCC, очевидно, не имеет столько ресурсов разработчика, как, например, GCC или LLVM. В прошлом я столкнулся с странными ошибками в компиляторе SD80 от SDCC, например, чтобы заменить «memcpy» на петли, чтобы копия работала правильно. – Michael

ответ

3

Это не ошибка с эмулятором - другие эмуляторы я проверил также дать 64 на следующий код:

#include <stdio.h> 

unsigned char one() { return 1; } 

void main() 
{ 
    unsigned int r; 

    // Force compiler to generate multiplication by deferring the '1' 
    r = one() * 32; 

    printf("1 * 32 = %u", r); 
} 

Здесь на BGB(версия 1.5.1):

BGB version 1.5.1: 1 * 32 = 64

И это на VBA-M(версия SVN1149):

VBA-M version SVN1149: 1 * 32 = 64


А почему and a,#0xE0 включен? Это на самом деле просто. Это просто гарантирует, что переполнение не испортит значение.

Прежде всего, предположим, что умножение действительно сработало правильно, а 1 * 32 все равно равно 32. (я просто добавлю дополнительный RR A).

Для 1 * 32, это выглядит следующим образом:

Start  ; A = 00000001 Carry = 0 
RR A  ; A = 00000000 Carry = 1 
RR A  ; A = 10000000 Carry = 0 
RR A  ; A = 01000000 Carry = 0 
RR A  ; A = 00100000 Carry = 0 
AND A,0xE0 ; A = 00100000 Carry = 0 

Здесь и не имеет никакого эффекта. Но, предположим, что мы умноженное на то, что бы вызвать переполнение, например, 17 * 32:

Start  ; A = 00010001 Carry = 0 
RR A  ; A = 00001000 Carry = 1 
RR A  ; A = 10000100 Carry = 0 
RR A  ; A = 01000010 Carry = 0 
RR A  ; A = 00100001 Carry = 1 
AND A,0xE0 ; A = 00100000 Carry = 0 

Без и мы бы получили 17 * 32 = 33, а не правильный ответ на 1 байт (32). Хотя ни один из этих ответов не является истинным ответом (544), 32 - правильное значение для первого байта.

2

Я проверил его, используя CPCtelera (который включает SDCC 3.4.3), и результат кажется правильным. Я использовал этот код:

#include <stdio.h> 

unsigned char one() { return 1; } 

void main() { 
    unsigned char r; 
    r = one() * 32; 
    printf("1 * 32 = %d\n\r", r); 
    while (1); 
} 

И это сгенерированный код (релевантно только часть кода):

_main:: 
;src/main.c:28: r = one() * 32; 
call _one 
ld a,l 
rrca 
rrca 
rrca 
and a,#0xE0 

Кажется, мне ясно, что ты прав, а версия SDCC вас неверно используют rr вместо rrca.

Наконец, это выход из программы, на WinAPE Amstrad CPC эмулятора:

Output on WinAPE 2.0. Alpha 18