2017-05-27 80 views
3

我正在Ada中做Z80仿真器。 我實現JR(相對跳轉)的家庭,但我不滿意我的代碼:Ada中的Z80仿真器中的類型轉換過多

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; 

看來工作,但是我發現有太多類型的轉換。 你有什麼建議嗎?

謝謝,

奧利維爾。

+0

有了:返回地址+ UINT16(Two_Complement(d)+ 2);我有一個CONSTRAINT_ERROR,這是正常的,因爲Two_Complement可以返回負數。 「太多了」:因爲我有一個C/C++背景,我試圖寫出好的Ada! – ols

+0

我沒有注意到它返回int8。 –

+1

注意:如果您使用Ada標準庫中的Integer類型,而不是您自己的類型(最好使用range和指定的Size),那麼前一種類型可能是16位類型。由於Janus/Ada,它是,所以嘗試從'UInt16'轉換可能會引起範圍約束錯誤。 – B98

回答

1

如果兩個整數類型非常緊密相關,至少形成一個特定的視角,如果它們只在值的子集中有所不同,但不是函數,請考慮子類型。

我懷疑,從概念的角度來看,選擇子類型可能會模糊問題。因此,如果我可以推測,利用你對這些整數目的的瞭解來演變Offset(猜測)這樣的名字將通過傳達它們的目的來增加名稱的價值:它們的含義不僅僅是它們有多少位,已簽署。也許這也會減少類型轉換的經驗,因爲然後參數會變成命名類型=概念的對象。運行時(或編譯時)的效果將是相同的。

在C語言中,類型別名也是XintNN_t的可能性;如果需要的話,別名甚至可能包含int

+0

我同意!但是要找到好的「域」名稱來提高抽象級別並不容易,因爲Z80域仿真非常技術性強,並且有很多位和字節。我提供的代碼不是真正的代碼。我簡化它以專注於我的問題。在真正的一箇中,我有: 子類型Register8是UInt8; 子類型Register16是UInt16; 子類型地址16是UInt16; 依此類推! – ols

6

你可以看看

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; 

而是將其取決於你需要的時候發生的事情 - 例如 - 地址是0,d是,比方說,16#80(上面的代碼返回16#ff82#)。

+0

不錯!對於地址0的情況,我需要檢查Z80手冊,或者對真實的手冊進行一些測試。我很確定我們有模數效應。 – ols

1

由於小梅的重點是類型安全,所看到的Ada編譯如下兩個類型定義不直接兼容:

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

這就是爲什麼當他們在相同的使用需要一個類型轉換表達。解決這個問題的一種方法是定義一個通用類型。例如,

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; 

那麼你就不需要這麼多的轉換,因爲編譯器將使用Int32類型爲基本類型的計算。例如,Two_Сomplement程序中的語句return Int8 (Integer (N) - 256);可以簡化爲return Int8 (N - 256);

作爲便箋,您還可以使用Interfaces庫來確保類型的正確尺寸。另外,圖書館還有便利的操作,如Shift_Left,Shift_Right等。

+0

如果你使'UInt8'成爲簽名'Int32'的子類型,那麼你將不會得到模塊化算術; 'UInt8'(255)+ 1'將是256,而不是0,並且您可能會獲得CE,具體取決於您分配的內容。而OP在任何地方都沒有'UInt_8',我想你的意思是'UInt16'。 '接口'建議是一個很好的建議。 –

+0

事實上,在這種情況下模塊化算術將不可用。另一方面,我不認爲模塊化算術的使用會對此代碼有任何好處。相反,如果兩個無符號整數被減去'UInt8'(125) - 130',結果將環繞,不適合該類型,編譯器將無法「看到」這個。因此,行政長官也會被提出。我認爲,如果沒有模塊化算法可用,並且在你的例子'UInt8'(255)+ 1'中,編譯器將至少給出一個關於結果的警告(或者如果配置了錯誤)。 – NeoSer

+0

我會看看'Interfaces'包。 – ols

1

我懷疑命名上的一點改動可能會有幫助。

您可以使用此:

Subtype Address is UInt16; 

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

這將讓你重新制定Jr這樣的:

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