2011-11-07 19 views
21

Ho avuto modo di implementare l'ADD A, r set di opcode sul mio core Z80. Ho avuto un po 'di confusione sulle bandiere di trasporto e di overflow che penso di aver inchiodato, ma volevo metterlo alla comunità per verificare che avessi ragione.Flag di overflow e carry su Z80

Fondamentalmente, da quello che vedo, l'ALU nella Z80 non interessa le operazioni firmate/non firmate, ma aggiunge solo bit. Ciò significa che se due valori a 8 bit vengono sommati insieme e causano un valore a 9 bit come risultato della loro aggiunta, verrà impostato il flag carry. Ciò include l'aggiunta di due numeri di complemento a due negativi, ad esempio -20 (11101100) e -40 (11011000), poiché sebbene il risultato sia -60 (11000100), il risultato è in realtà un valore a 9 bit 1 1100 0100. Ciò significa sicuramente se si aggiungono due valori del complemento a due negativi, il flag di riporto sarà sempre impostato, anche quando non ci sono condizioni di overflow - ho ragione?

In secondo luogo, ho deciso che per rilevare un overflow in questa istruzione, avrei XOR bit 7 di entrambi gli operandi, e se il risultato era 10000000, quindi non c'è sicuramente overflow - se il risultato di questo è 00000000 allora potrebbe essere un overflow come i segni sono gli stessi, e quindi vorrei XOR bit 7 del risultato dell'aggiunta con il bit 7 di entrambi gli operandi, e se il risultato di questo è 10000000 allora si è verificato un overflow e ho impostato il P/V bandiera di overflow. Sono qui anche qui?

Ci scusiamo per una domanda così complicata, sono abbastanza sicuro di aver ragione ma ho bisogno di sapere prima di continuare con innumerevoli altre istruzioni basate su questa logica. Grazie molto.

risposta

31

I bit del risultato si ottengono dalla somma troncata di numeri interi senza segno. L'aggiunta di istruzioni non interessa il segno qui, né interessa la tua interpretazione degli interi come firmati o non firmati. Aggiunge solo come se i numeri non fossero firmati.

Il flag di trasporto (o prendere in prestito in caso di sottrazione) è quel 9 ° bit inesistente dall'aggiunta degli interi senza segno a 8 bit. In effetti, questo flag indica un overflow/underflow per aggiungere/sub di interi non firmati. Ancora una volta, aggiungere non si preoccupa dei segni qui, aggiunge solo come se i numeri non fossero firmati.

L'aggiunta di due numeri di complemento a 2 negativi comporterà l'impostazione del flag di trasporto su 1, corretta.

Il flag di overflow indica se c'è stato un overflow/underflow per aggiungere/sub di interi con segno. Per impostare il flag di overflow, l'istruzione considera i numeri come firmati (proprio come li considera come non firmati per il flag carry e gli 8 bit del risultato).

L'idea alla base dell'impostazione del flag di overflow è semplice. Supponete di firmare-estendere i vostri interi con segno a 8 bit a 9 bit, cioè copiare semplicemente il 7 ° bit in un 8 ° bit in più. Si verificherà un overflow/underflow se la somma/differenza di 9 bit di questi numeri interi con segno a 9 bit ha valori diversi nei bit 7 e 8, il che significa che l'addizione/sottrazione ha perso il segno del risultato nel 7 ° bit e lo ha usato per grandezza del risultato, o, in altre parole, gli 8 bit non possono contenere il bit del segno e una grandezza così grande.

Ora, il bit 7 del risultato può essere diverso dal bit 8 del segno immaginario se e solo se il trasferimento nel bit 7 e il trasferimento nel bit 8 (= esecuzione del bit 7) sono diversi. Questo perché iniziamo con gli addendi che hanno bit 7 = bit 8 e solo diversi carry-in in essi possono influenzarli nel risultato in modi diversi.

bandiera Così trabocco = carry-out bandiera XOR trasportare da bit 6 nel bit 7.

Sia il mio e il tuo modo di calcolare il flag di overflow sono corrette. Infatti, entrambi sono descritti nella sezione Z80 CPU User's Manual nella sezione "Indicatori di stato dello Z80".

Ecco come è possibile emulare la maggior parte delle istruzioni ADC in C, in cui non si dispone di accesso diretto alle bandiere della CPU e non può sfruttare appieno l'istruzione ADC della CPU emulare:

#include <stdio.h> 
#include <limits.h> 

#if CHAR_BIT != 8 
#error char expected to have exactly 8 bits. 
#endif 

typedef unsigned char uint8; 
typedef signed char int8; 

#define FLAGS_CY_SHIFT 0 
#define FLAGS_OV_SHIFT 1 
#define FLAGS_CY_MASK (1 << FLAGS_CY_SHIFT) 
#define FLAGS_OV_MASK (1 << FLAGS_OV_SHIFT) 

void Adc(uint8* acc, uint8 b, uint8* flags) 
{ 
    uint8 a = *acc; 
    uint8 carryIns; 
    uint8 carryOut; 

    // Calculate the carry-out depending on the carry-in and addends. 
    // 
    // carry-in = 0: carry-out = 1 IFF (a + b > 0xFF) or, 
    // equivalently, but avoiding overflow in C: (a > 0xFF - b). 
    // 
    // carry-in = 1: carry-out = 1 IFF (a + b + 1 > 0xFF) or, 
    // equivalently, (a + b >= 0xFF) or, 
    // equivalently, but avoiding overflow in C: (a >= 0xFF - b). 
    // 
    // Also calculate the sum bits. 
    if (*flags & FLAGS_CY_MASK) 
    { 
    carryOut = (a >= 0xFF - b); 
    *acc = a + b + 1; 
    } 
    else 
    { 
    carryOut = (a > 0xFF - b); 
    *acc = a + b; 
    } 

#if 0 
    // Calculate the overflow by sign comparison. 
    carryIns = ((a^b)^0x80) & 0x80; 
    if (carryIns) // if addend signs are different 
    { 
    // overflow if the sum sign differs from the sign of either of addends 
    carryIns = ((*acc^a) & 0x80) != 0; 
    } 
#else 
    // Calculate all carry-ins. 
    // Remembering that each bit of the sum = 
    // addend a's bit XOR addend b's bit XOR carry-in, 
    // we can work out all carry-ins from a, b and their sum. 
    carryIns = *acc^a^b; 

    // Calculate the overflow using the carry-out and 
    // most significant carry-in. 
    carryIns = (carryIns >> 7)^carryOut; 
#endif 

    // Update flags. 
    *flags &= ~(FLAGS_CY_MASK | FLAGS_OV_MASK); 
    *flags |= (carryOut << FLAGS_CY_SHIFT) | (carryIns << FLAGS_OV_SHIFT); 
} 

void Sbb(uint8* acc, uint8 b, uint8* flags) 
{ 
    // a - b - c = a + ~b + 1 - c = a + ~b + !c 
    *flags ^= FLAGS_CY_MASK; 
    Adc(acc, ~b, flags); 
    *flags ^= FLAGS_CY_MASK; 
} 

const uint8 testData[] = 
{ 
    0, 
    1, 
    0x7F, 
    0x80, 
    0x81, 
    0xFF 
}; 

int main(void) 
{ 
    unsigned aidx, bidx, c; 

    printf("ADC:\n"); 
    for (c = 0; c <= 1; c++) 
    for (aidx = 0; aidx < sizeof(testData)/sizeof(testData[0]); aidx++) 
     for (bidx = 0; bidx < sizeof(testData)/sizeof(testData[0]); bidx++) 
     { 
     uint8 a = testData[aidx]; 
     uint8 b = testData[bidx]; 
     uint8 flags = c << FLAGS_CY_SHIFT; 
     printf("%3d(%4d) + %3d(%4d) + %u = ", 
       a, (int8)a, b, (int8)b, c); 
     Adc(&a, b, &flags); 
     printf("%3d(%4d) CY=%d OV=%d\n", 
       a, (int8)a, (flags & FLAGS_CY_MASK) != 0, (flags & FLAGS_OV_MASK) != 0); 
     } 

    printf("SBB:\n"); 
    for (c = 0; c <= 1; c++) 
    for (aidx = 0; aidx < sizeof(testData)/sizeof(testData[0]); aidx++) 
     for (bidx = 0; bidx < sizeof(testData)/sizeof(testData[0]); bidx++) 
     { 
     uint8 a = testData[aidx]; 
     uint8 b = testData[bidx]; 
     uint8 flags = c << FLAGS_CY_SHIFT; 
     printf("%3d(%4d) - %3d(%4d) - %u = ", 
       a, (int8)a, b, (int8)b, c); 
     Sbb(&a, b, &flags); 
     printf("%3d(%4d) CY=%d OV=%d\n", 
       a, (int8)a, (flags & FLAGS_CY_MASK) != 0, (flags & FLAGS_OV_MASK) != 0); 
     } 

    return 0; 
} 

uscita:

ADC: 
    0( 0) + 0( 0) + 0 = 0( 0) CY=0 OV=0 
    0( 0) + 1( 1) + 0 = 1( 1) CY=0 OV=0 
    0( 0) + 127(127) + 0 = 127(127) CY=0 OV=0 
    0( 0) + 128(-128) + 0 = 128(-128) CY=0 OV=0 
    0( 0) + 129(-127) + 0 = 129(-127) CY=0 OV=0 
    0( 0) + 255( -1) + 0 = 255( -1) CY=0 OV=0 
    1( 1) + 0( 0) + 0 = 1( 1) CY=0 OV=0 
    1( 1) + 1( 1) + 0 = 2( 2) CY=0 OV=0 
    1( 1) + 127(127) + 0 = 128(-128) CY=0 OV=1 
    1( 1) + 128(-128) + 0 = 129(-127) CY=0 OV=0 
    1( 1) + 129(-127) + 0 = 130(-126) CY=0 OV=0 
    1( 1) + 255( -1) + 0 = 0( 0) CY=1 OV=0 
127(127) + 0( 0) + 0 = 127(127) CY=0 OV=0 
127(127) + 1( 1) + 0 = 128(-128) CY=0 OV=1 
127(127) + 127(127) + 0 = 254( -2) CY=0 OV=1 
127(127) + 128(-128) + 0 = 255( -1) CY=0 OV=0 
127(127) + 129(-127) + 0 = 0( 0) CY=1 OV=0 
127(127) + 255( -1) + 0 = 126(126) CY=1 OV=0 
128(-128) + 0( 0) + 0 = 128(-128) CY=0 OV=0 
128(-128) + 1( 1) + 0 = 129(-127) CY=0 OV=0 
128(-128) + 127(127) + 0 = 255( -1) CY=0 OV=0 
128(-128) + 128(-128) + 0 = 0( 0) CY=1 OV=1 
128(-128) + 129(-127) + 0 = 1( 1) CY=1 OV=1 
128(-128) + 255( -1) + 0 = 127(127) CY=1 OV=1 
129(-127) + 0( 0) + 0 = 129(-127) CY=0 OV=0 
129(-127) + 1( 1) + 0 = 130(-126) CY=0 OV=0 
129(-127) + 127(127) + 0 = 0( 0) CY=1 OV=0 
129(-127) + 128(-128) + 0 = 1( 1) CY=1 OV=1 
129(-127) + 129(-127) + 0 = 2( 2) CY=1 OV=1 
129(-127) + 255( -1) + 0 = 128(-128) CY=1 OV=0 
255( -1) + 0( 0) + 0 = 255( -1) CY=0 OV=0 
255( -1) + 1( 1) + 0 = 0( 0) CY=1 OV=0 
255( -1) + 127(127) + 0 = 126(126) CY=1 OV=0 
255( -1) + 128(-128) + 0 = 127(127) CY=1 OV=1 
255( -1) + 129(-127) + 0 = 128(-128) CY=1 OV=0 
255( -1) + 255( -1) + 0 = 254( -2) CY=1 OV=0 
    0( 0) + 0( 0) + 1 = 1( 1) CY=0 OV=0 
    0( 0) + 1( 1) + 1 = 2( 2) CY=0 OV=0 
    0( 0) + 127(127) + 1 = 128(-128) CY=0 OV=1 
    0( 0) + 128(-128) + 1 = 129(-127) CY=0 OV=0 
    0( 0) + 129(-127) + 1 = 130(-126) CY=0 OV=0 
    0( 0) + 255( -1) + 1 = 0( 0) CY=1 OV=0 
    1( 1) + 0( 0) + 1 = 2( 2) CY=0 OV=0 
    1( 1) + 1( 1) + 1 = 3( 3) CY=0 OV=0 
    1( 1) + 127(127) + 1 = 129(-127) CY=0 OV=1 
    1( 1) + 128(-128) + 1 = 130(-126) CY=0 OV=0 
    1( 1) + 129(-127) + 1 = 131(-125) CY=0 OV=0 
    1( 1) + 255( -1) + 1 = 1( 1) CY=1 OV=0 
127(127) + 0( 0) + 1 = 128(-128) CY=0 OV=1 
127(127) + 1( 1) + 1 = 129(-127) CY=0 OV=1 
127(127) + 127(127) + 1 = 255( -1) CY=0 OV=1 
127(127) + 128(-128) + 1 = 0( 0) CY=1 OV=0 
127(127) + 129(-127) + 1 = 1( 1) CY=1 OV=0 
127(127) + 255( -1) + 1 = 127(127) CY=1 OV=0 
128(-128) + 0( 0) + 1 = 129(-127) CY=0 OV=0 
128(-128) + 1( 1) + 1 = 130(-126) CY=0 OV=0 
128(-128) + 127(127) + 1 = 0( 0) CY=1 OV=0 
128(-128) + 128(-128) + 1 = 1( 1) CY=1 OV=1 
128(-128) + 129(-127) + 1 = 2( 2) CY=1 OV=1 
128(-128) + 255( -1) + 1 = 128(-128) CY=1 OV=0 
129(-127) + 0( 0) + 1 = 130(-126) CY=0 OV=0 
129(-127) + 1( 1) + 1 = 131(-125) CY=0 OV=0 
129(-127) + 127(127) + 1 = 1( 1) CY=1 OV=0 
129(-127) + 128(-128) + 1 = 2( 2) CY=1 OV=1 
129(-127) + 129(-127) + 1 = 3( 3) CY=1 OV=1 
129(-127) + 255( -1) + 1 = 129(-127) CY=1 OV=0 
255( -1) + 0( 0) + 1 = 0( 0) CY=1 OV=0 
255( -1) + 1( 1) + 1 = 1( 1) CY=1 OV=0 
255( -1) + 127(127) + 1 = 127(127) CY=1 OV=0 
255( -1) + 128(-128) + 1 = 128(-128) CY=1 OV=0 
255( -1) + 129(-127) + 1 = 129(-127) CY=1 OV=0 
255( -1) + 255( -1) + 1 = 255( -1) CY=1 OV=0 
SBB: 
    0( 0) - 0( 0) - 0 = 0( 0) CY=0 OV=0 
    0( 0) - 1( 1) - 0 = 255( -1) CY=1 OV=0 
    0( 0) - 127(127) - 0 = 129(-127) CY=1 OV=0 
    0( 0) - 128(-128) - 0 = 128(-128) CY=1 OV=1 
    0( 0) - 129(-127) - 0 = 127(127) CY=1 OV=0 
    0( 0) - 255( -1) - 0 = 1( 1) CY=1 OV=0 
    1( 1) - 0( 0) - 0 = 1( 1) CY=0 OV=0 
    1( 1) - 1( 1) - 0 = 0( 0) CY=0 OV=0 
    1( 1) - 127(127) - 0 = 130(-126) CY=1 OV=0 
    1( 1) - 128(-128) - 0 = 129(-127) CY=1 OV=1 
    1( 1) - 129(-127) - 0 = 128(-128) CY=1 OV=1 
    1( 1) - 255( -1) - 0 = 2( 2) CY=1 OV=0 
127(127) - 0( 0) - 0 = 127(127) CY=0 OV=0 
127(127) - 1( 1) - 0 = 126(126) CY=0 OV=0 
127(127) - 127(127) - 0 = 0( 0) CY=0 OV=0 
127(127) - 128(-128) - 0 = 255( -1) CY=1 OV=1 
127(127) - 129(-127) - 0 = 254( -2) CY=1 OV=1 
127(127) - 255( -1) - 0 = 128(-128) CY=1 OV=1 
128(-128) - 0( 0) - 0 = 128(-128) CY=0 OV=0 
128(-128) - 1( 1) - 0 = 127(127) CY=0 OV=1 
128(-128) - 127(127) - 0 = 1( 1) CY=0 OV=1 
128(-128) - 128(-128) - 0 = 0( 0) CY=0 OV=0 
128(-128) - 129(-127) - 0 = 255( -1) CY=1 OV=0 
128(-128) - 255( -1) - 0 = 129(-127) CY=1 OV=0 
129(-127) - 0( 0) - 0 = 129(-127) CY=0 OV=0 
129(-127) - 1( 1) - 0 = 128(-128) CY=0 OV=0 
129(-127) - 127(127) - 0 = 2( 2) CY=0 OV=1 
129(-127) - 128(-128) - 0 = 1( 1) CY=0 OV=0 
129(-127) - 129(-127) - 0 = 0( 0) CY=0 OV=0 
129(-127) - 255( -1) - 0 = 130(-126) CY=1 OV=0 
255( -1) - 0( 0) - 0 = 255( -1) CY=0 OV=0 
255( -1) - 1( 1) - 0 = 254( -2) CY=0 OV=0 
255( -1) - 127(127) - 0 = 128(-128) CY=0 OV=0 
255( -1) - 128(-128) - 0 = 127(127) CY=0 OV=0 
255( -1) - 129(-127) - 0 = 126(126) CY=0 OV=0 
255( -1) - 255( -1) - 0 = 0( 0) CY=0 OV=0 
    0( 0) - 0( 0) - 1 = 255( -1) CY=1 OV=0 
    0( 0) - 1( 1) - 1 = 254( -2) CY=1 OV=0 
    0( 0) - 127(127) - 1 = 128(-128) CY=1 OV=0 
    0( 0) - 128(-128) - 1 = 127(127) CY=1 OV=0 
    0( 0) - 129(-127) - 1 = 126(126) CY=1 OV=0 
    0( 0) - 255( -1) - 1 = 0( 0) CY=1 OV=0 
    1( 1) - 0( 0) - 1 = 0( 0) CY=0 OV=0 
    1( 1) - 1( 1) - 1 = 255( -1) CY=1 OV=0 
    1( 1) - 127(127) - 1 = 129(-127) CY=1 OV=0 
    1( 1) - 128(-128) - 1 = 128(-128) CY=1 OV=1 
    1( 1) - 129(-127) - 1 = 127(127) CY=1 OV=0 
    1( 1) - 255( -1) - 1 = 1( 1) CY=1 OV=0 
127(127) - 0( 0) - 1 = 126(126) CY=0 OV=0 
127(127) - 1( 1) - 1 = 125(125) CY=0 OV=0 
127(127) - 127(127) - 1 = 255( -1) CY=1 OV=0 
127(127) - 128(-128) - 1 = 254( -2) CY=1 OV=1 
127(127) - 129(-127) - 1 = 253( -3) CY=1 OV=1 
127(127) - 255( -1) - 1 = 127(127) CY=1 OV=0 
128(-128) - 0( 0) - 1 = 127(127) CY=0 OV=1 
128(-128) - 1( 1) - 1 = 126(126) CY=0 OV=1 
128(-128) - 127(127) - 1 = 0( 0) CY=0 OV=1 
128(-128) - 128(-128) - 1 = 255( -1) CY=1 OV=0 
128(-128) - 129(-127) - 1 = 254( -2) CY=1 OV=0 
128(-128) - 255( -1) - 1 = 128(-128) CY=1 OV=0 
129(-127) - 0( 0) - 1 = 128(-128) CY=0 OV=0 
129(-127) - 1( 1) - 1 = 127(127) CY=0 OV=1 
129(-127) - 127(127) - 1 = 1( 1) CY=0 OV=1 
129(-127) - 128(-128) - 1 = 0( 0) CY=0 OV=0 
129(-127) - 129(-127) - 1 = 255( -1) CY=1 OV=0 
129(-127) - 255( -1) - 1 = 129(-127) CY=1 OV=0 
255( -1) - 0( 0) - 1 = 254( -2) CY=0 OV=0 
255( -1) - 1( 1) - 1 = 253( -3) CY=0 OV=0 
255( -1) - 127(127) - 1 = 127(127) CY=0 OV=1 
255( -1) - 128(-128) - 1 = 126(126) CY=0 OV=0 
255( -1) - 129(-127) - 1 = 125(125) CY=0 OV=0 
255( -1) - 255( -1) - 1 = 255( -1) CY=1 OV=0 

È possibile modificare #if 0 a #if 1 utilizzare il metodo di accesso basato sui confronti per il calcolo di troppo pieno. Il risultato sarà lo stesso. A prima vista, è un po 'sorprendente che il metodo basato sui segnali si occupi anche del carry-in.

Si prega di notare che usando il mio metodo in cui calcolare tutte carry-in in bit da 0 a 7, si ottiene anche gratuitamente il valore della bandiera half-carry (portare da bit 3 al bit 4) che serve per la DAA istruzioni.

MODIFICA: Ho aggiunto una funzione per sottrazione con prestito (istruzione SBC/SBB) e risultati per esso.

+0

Questo è perfetto - vi ringrazio molto :-) sapevo di essere nella giusta direzione. Grazie anche per il tuo esempio di codice. In realtà sto usando Java (dato che sto puntando a questo per essere un emulatore di sistema master completamente multipiattaforma quando è fatto), anche se posso capire abbastanza C per vedere di cosa stai parlando. Scusate se la mia domanda sembrava semplice, è solo che mi sto insegnando un sacco di matematica binaria mentre vado con questo progetto, finora mi sembra di averlo afferrato però :-) – PhilPotter1987

+0

Grazie per la spiegazione, è davvero preciso . Ho scoperto la bandiera halfcarry facendo 'halfCarryOut = carryIn? ((a & 0x0F)> = 0x0F - (a & 0x0F)): ((a & 0x0F)> 0x0F - (a & 0x0F)); halfCarryOut = ((res^a^b) >> 4)^halfCarryOut; ', dovrebbe essere corretto. – Jack

+1

@Jack Se lo hai testato e funziona, OK (non ho intenzione di convalidarlo). Ma può essere fatto più semplice, come ho indicato alla fine della risposta. Usa la variante del codice tra #else e #endif. Dopo 'carryIns = * acc^a^b;' fai 'halfCarryOut = (carryIns >> 4) & 1;', questo è tutto ciò che devi aggiungere. –

4

Un altro modo per vedere questo che è forse più facile da capire. Quando si esegue una somma:

  • Segno è sempre impostato al bit 7 del risultato
  • Zero viene impostato se il risultato è 0x00
  • mezza portare è impostato quando il bocconcino destra somma degli operandi overflow
  • Overflow viene impostato quando entrambi gli operandi sono positivi e la somma firmata è negativa oppure entrambi gli operandi sono negativi e la somma firmata è positiva
  • Add/Sub viene ripristinato
  • Carry è impostato se la somma non firmato trabocca 0xFF
+1

Bel riassunto. Per chiarire ancora un po ': "Add/Sub is reset" perché 'N' ottiene solo * set * esplicitamente se l'ultima operazione era una sottrazione. (Penso che questo sia usato solo per le istruzioni 'DAA'.) – usr2564301

+0

Cosa intendi con 'bit 7'? 7, contando all'indietro, a partire da 0? – lakesare

Problemi correlati