2017-05-27 1 views
3

Je fais un émulateur Z80 dans Ada. Je suis le JR de mise en œuvre (saut relatif) de la famille, mais je ne suis pas satisfait de mon code:Trop de conversions de types dans un émulateur Z80 dans Ada

with Ada.Text_IO; 

procedure main is 
    type UInt16 is mod 2 ** 16; 
    type UInt8 is mod 2 ** 8; 
    type Int8 is range -128 .. 127; 

    package UInt16_IO is new Ada.Text_IO.Modular_IO (UInt16); 

    function Two_Complement(N : UInt8) return Int8 is 
    begin 
     if N <= 127 then 
      return Int8 (N); 
     end if; 
     return Int8 (Integer (N) - 256); 
    end Two_Complement; 

    -- Relative jump 
    function Jr (Address : UInt16; D: UInt8) return UInt16 is 
    begin 
     return UInt16 (Integer (Address) + Integer (Two_Complement (D) + 2)); 
    end Jr; 

    Address : UInt16; 
begin 
    Address := 16#683#; 
    UInt16_IO.Put (Item => Jr (Address, 16#F1#), Base => 16); -- Get 16#676# which is good ! 
end main; 

Il semble fonctionner, mais je trouve qu'il ya trop de conversions de types. Avez-vous un conseil?

Merci,

Olivier.

+0

Avec: retour Adresse + UInt16 (Two_Complement (D) + 2); J'ai un CONSTRAINT_ERROR, ce qui est normal car Two_Complement peut retourner des nombres négatifs. "Trop": parce que j'ai un fond C/C++ et j'essaye d'écrire du bon Ada! – ols

+0

Je n'ai pas remarqué qu'il renvoie int8. –

+1

Note de côté: si vous utilisez le type 'Integer' de la bibliothèque standard Ada et non votre propre type (de préférence avec range et' 'Size' spécifié), alors le premier type pourrait bien être un type 16 bits. Avec Janus/Ada, il est, et donc une tentative de conversion à partir de 'UInt16' pourrait soulever une erreur de contrainte de plage. – B98

Répondre

1

Dans le cas où deux types entiers sont très étroitement liés, au moins forment un certain point de vue, s'ils ne diffèrent que dans le sous-ensemble des valeurs mais ne fonctionnent pas, considérons les sous-types.

Je pense que le choix de sous-types pourrait brouiller les choses, d'un point de vue conceptuel. Donc, si je peux spéculer, en utilisant vos connaissances sur le but de ces entiers d'évoluer des noms comme Offset (deviner) augmentera la valeur des noms en transmettant leur but: ce qu'ils signifient, non seulement combien de bits ils ont, ou qu'ils sont signés. Peut-être que cela adoucit aussi l'expérience des conversions de types, car alors les paramètres deviennent des objets des types si nommés. Les effets d'exécution (ou de compilation) seront les mêmes. En termes de C, un alias de type est également une possibilité pour XintNN_t; l'alias pourrait même inclure le int -ness si c'est souhaitable.

+0

Je suis d'accord! Mais il n'est pas facile de trouver de bons noms de domaine pour augmenter le niveau d'abstraction, car l'émulation de domaine Z80 est très technique avec beaucoup de bits et d'octets. Le code que je fournis n'est pas le vrai code. Je simplifie pour me concentrer sur mon problème. Dans le vrai, j'ai: sous-type Register8 est UInt8; sous-type Register16 est UInt16; Sous-type L'adresse 16 est UInt16; Et ainsi de suite! – ols

6

Vous pouvez regarder

function Jr (Address : UInt16; D: UInt8) return UInt16 is 
    Offset : constant Uint16 
    := Uint16 (D) + (if D >= 16#80# then 16#ff00# else 0); 
begin 
    return Address + Offset + 2; 
end Jr; 

mais cela dépend plutôt de ce que vous devez arriver quand - par exemple - l'adresse est 0 et D est, par exemple, 16#80 (le code ci-dessus renvoie 16#ff82#).

+0

Nice! Pour le cas de l'adresse 0, je dois vérifier dans un manuel Z80, ou faire quelques tests sur un vrai. Je suis sûr que nous avons un effet modulo. – ols

1

Depuis Ada se concentre sur la sécurité de type, les deux définitions de types suivants ne sont pas directement compatibles comme on le voit par le compilateur Ada:

type UInt8 is mod 2 ** 8; 
type UInt_8 is mod 2 ** 8; 

Ceci est la raison pour laquelle une conversion de type est nécessaire quand ils sont utilisés dans le même expression. Une façon de résoudre ce problème consiste à définir un type commun. Par exemple,

type Int32 is range -2 ** 31 .. 2 ** 31 - 1; 

subtype UInt8 is Int32 range  0 .. 2 ** 8 - 1; 
subtype Int8 is Int32 range -2 ** 7 .. 2 ** 7 - 1; 

Ensuite, vous avez pas besoin autant de conversions que le compilateur utilisera le type Int32 comme le type de base pour le calcul. Par exemple, l'instruction return Int8 (Integer (N) - 256); dans la procédure Two_Сomplement peut être simplifiée en return Int8 (N - 256);. En outre, vous pouvez également utiliser la bibliothèque Interfaces pour assurer les tailles appropriées pour les types. En outre, la bibliothèque a des opérations pratiques telles que Shift_Left, Shift_Right etc.

+0

Si vous faites de UInt8 un sous-type de l'Int32 signé alors vous n'obtiendrez pas d'arithmétique modulaire; 'UInt8 '(255) + 1' sera 256, et non 0, et vous pouvez bien obtenir CE, en fonction de ce que vous lui attribuez. Et OP n'a pas 'UInt_8' n'importe où, je pense que vous vouliez dire' UInt16'. La suggestion 'Interfaces' est une bonne suggestion. –

+0

En effet, l'arithmétique modulaire ne sera pas disponible dans ce cas. D'un autre côté, je ne pense pas que l'utilisation de l'arithmétique modulaire aura un quelconque avantage dans ce code. Au contraire, si deux entiers non signés sont soustraits 'UInt8 '(125) - 130', le résultat sera enveloppant, ne rentrera pas dans le type et le compilateur ne pourra pas" voir "cela. Par conséquent, un CE sera également soulevé. Je pense que si aucune arithmétique modulaire n'est disponible et dans votre exemple 'UInt8 '(255) + 1', le compilateur donnera au moins un avertissement (ou une erreur si configuré) sur le résultat. – NeoSer

+0

Je jetterai un oeil sur le paquet 'Interfaces'. – ols

1

Je soupçonne qu'une légère modification dans la dénomination pourrait aider les choses ici.

Vous pouvez utiliser ceci:

Subtype Address is UInt16; 

Function "+"(Location : Address; Offset: Int8) return Address is 
    (if Offset < 0 then Location - UInt16(ABS Offset) 
    else Location + UInt16(Offset)); 

Ce qui vous permettra de reformuler Jr à ceci:

-- Relative jump 
function Jr (Location : Address; D: UInt8) return UInt16 is 
    Offset : Constant Int8 := Two_Complement(D) + 2; 
begin 
    return Location + Offset; 
end Jr;