簡體   English   中英

LMC 中的輸出和復位列表

[英]Output and Reset Lists in LMC

我正在處理這個編碼挑戰:

為 Little Man Computer 編寫一個程序,允許用戶管理值列表。 它應該以一個空列表開始,然后按如下方式處理輸入:

如果輸入是:

  • 小於 100:將此值添加到列表中,除非列表已有 10 個值,在這種情況下該值將被忽略
  • 995:使列表為空
  • 996:輸出列表當前具有的值的數量
  • 997:輸出列表當前具有的每個值,按照它們添加到列表中的順序
  • 998:以相反的順序輸出列表當前具有的每個值
  • 999:結束程序
  • 任何其他值都將被忽略

只要輸入值不是 999,輸入值的處理就會繼續。

當輸入 997 時,我在獲取代碼以按正向順序打印存儲的列表時遇到問題。 我想我可能混淆了ADDSUB指令。 當輸入 995 時,我也無法正確重置存儲的列表。

我能夠正確編程的其他一切。

下面是我的代碼:

START   INP
        STA TEMP
        SUB NINES
        BRZ end

        LDA TEMP
        SUB EIGHT
        BRZ PRIT

        lda temp
        sub seven
        brz printf

        LDA TEMP
        SUB SIX
        BRZ DOOUT

        LDA TEMP
        SUB FIVE
        BRZ RESET

        LDA COUNT
        SUB TEN
        BRZ START

        LDA TEMP
        SUB HUND
        BRP START

SIT     LDA SINST
        ADD COUNT
        STA SLOC
        LDA TEMP
SLOC    DAT 0
        LDA COUNT
        ADD ONE
        STA COUNT
        BRA START
PRIT    LDA COUNT
        BRZ END
PRINTR  LDA LINST
        ADD COUNT
        SUB ONE
        STA LDIT
LDIT    DAT 0
        OUT
        LDA COUNT
        SUB ONE
        STA COUNT
        BRZ END
        BRA PRINTR

---PRINTF  LDA LINST
        ADD COUNT
        add ONE
        STA LDIT
LDIT    DAT 0
        OUT
        LDA COUNT
        SUB ONE
        STA COUNT
        BRZ END
        BRA PRINTF 

doout   lda count
        out
        bra start

reset   lda zero
        sta count
        bra start

END     HLT

TEMP    DAT 0
COUNT   DAT 0
ONE     DAT 1
TWO     DAT 2
TEN     DAT 10
HUND    DAT 100
SINST   DAT 380
LINST   DAT 580
five    dat 995
six     dat 996
seven   dat 997
eight   dat 998
NINES   DAT 999

程序中的問題可以分為以下幾類:

  1. 與好的模擬器在運行程序之前應檢測到的標簽相關的問題
  2. 與程序邏輯相關的問題,使程序產生錯誤的輸出
  3. 與代碼風格相關的問題

1. 標簽

一個好的模擬器不應該接受以下錯誤:

  • PRINTF標簽未定義。

    有一個brz printf指令,但該標簽未定義,因為---使其成為注釋。 顯然,需要刪除---以使標簽引用有效。

  • LDIT標簽重復:

    標簽 LDIT 定義了兩次。 這將產生不可靠的行為。 一個好的模擬器應該給出一個關於這個的錯誤信息,但其他模擬器只會采用一個定義並忽略重復項。 無論哪種方式,該程序的意圖是一個STA LDIT使用第一個LDIT 位置,而第二個STA LDIT使用第二個LDIT 位置。 如果有的話,這將不會發生。 因此,重命名兩個標簽之一,並相應地調整STA LDIT指令之一。

  • zero標簽未定義:

    重置計數器的代碼引用了未定義的標簽zero 同樣,好的模擬器在加載程序時會產生錯誤,但其他模擬器可能會默默地使用郵箱 0,這會導致不良行為。 所以定義zero標簽為

    zero DAT 0

2. 邏輯

  • 用於打印列表的代碼,無論是向前還是向后,都會改變COUNT的值,但是這樣代碼會丟失列表的大小 例如,如果在這樣的遍歷之后你會選擇動作 996——即查詢列表的大小——它不會給出正確的結果。 解決方案是使用不同的變量遍歷列表,並保持COUNT不變。 僅當您向列表添加值或重置列表時,才應更改COUNT

  • 打印代碼以跳轉到END ,但似乎應該允許用戶繼續使用另一個“菜單”選項,因此與其跳轉到END ,不如跳轉到START 跳轉到END的唯一原因應該是因為用戶輸入了選項 999。

  • 對於正向打印,您不應通過將COUNT添加到動態加載指令開始,因為您需要從列表的第一個元素開始,而不是最后一個元素。 所以在第一次打印之前不要做ADD COUNTADD ONE 相反,增加動態加載指令直到它與原始加載指令的差異——即計算列表中的相對偏移量——至少為COUNT

  • LMC 規范有一個特別奇怪的地方:它沒有定義當減法會導致負結果時累加器的值是多少。 累加器不能存儲負值,只能標記負結果。 因此,當您剛剛執行可能導致負值的SUB指令時,執行BRZ是不安全的(因為奇怪的是,當減法為負時,模擬器可能會對累加器中的零值做出反應)。 簡而言之,如果可以的話,最好使用BRP而不是BRZ ,或者在使用BRZ之前至少使用BRP 注意:即使是有關該主題的教師也不總是意識到這一點。

  • LMC 有一個重置“句柄”,它將程序計數器設置回程序的開始。 發生這種情況時,您將需要從頭開始,並將COUNT重置為 0。因此,在程序的最頂部添加重置代碼。

這將解決程序中的語義問題。

3. 代碼風格

  • 我建議您使用更有意義的名稱。 doout不是很有啟發性,因為您的程序旨在輸出不同的內容(按向前順序列出, doout序列出,列表的大小)。 例如,使用output_size而不是doout 諸如LDITPRIT等名稱非常神秘。 當程序使用描述性名稱而不使用只有您才能理解的縮寫時,它會更容易閱讀和理解。

  • 動態指令基於SINST (存儲指令)和LINST (加載指令),它們被硬編碼為:

     SINST DAT 380 LINST DAT 580

    但遺憾的是,您已經對它們進行了這樣的硬編碼。 首先,他們假設郵箱 80 可以免費存儲列表,其次它需要了解這些指令(3 和 5)的操作碼。 然而,有了好的匯編程序,您就不必這樣做了。 所以我建議這樣做:

     store_instruction STA list load_instruction LDA list

    ...並在代碼的最底部使用DAT指令定義list (因為那里有可用的郵箱)。 我什至會在它之后添加虛擬DAT行,以便非常清楚屬於該列表的郵箱——它只是讓某人更容易理解代碼:

     list DAT DAT DAT DAT DAT DAT DAT DAT DAT DAT

    這樣,列表可能不會存儲在郵箱 80 中,但我們不在乎。 我們把它留給匯編器來為我們的列表分配下一個空閑郵箱。 在“數據部分”中間放置STALDA指令可能看起來也很奇怪,它們永遠不會被執行,但 LMC 架構(馮諾依曼架構)的原理是代碼和數據使用相同的內存,所以這很好。 LDASTA指令將從那里復制到實際程序中。

修正程序

考慮到以上所有因素,該程序可能如下所示:

 #input:1 2 3 997 998 999 clear_list LDA zero # Start by resetting the list STA list_size start INP STA input SUB menu_nine BRP end LDA input SUB menu_eight BRP output_reversed LDA input SUB menu_seven BRP output_forward LDA input SUB menu_six BRP output_size LDA input SUB menu_five BRP clear_list LDA list_size SUB max_list_size BRP start LDA input SUB input_limit BRP start LDA store_instruction ADD list_size STA store LDA input store DAT 0 LDA list_size ADD one STA list_size BRA start output_reversed LDA load_instruction ADD list_size loop_reversed SUB one STA load_reversed SUB load_instruction # are we still within the list? BRP load_reversed # yes, continue printing BRA start load_reversed DAT 0 OUT LDA load_reversed # decrement the dynamic LDA instruction BRA loop_reversed output_forward LDA load_instruction loop_forward STA load_forward SUB load_instruction # get relative offset in the list SUB list_size # are we still within the list? BRP start # no, stop printing load_forward DAT 0 OUT LDA load_forward # increment the dynamic LDA instruction ADD one BRA loop_forward output_size LDA list_size OUT BRA start end HLT # constants zero DAT 0 one DAT 1 two DAT 2 max_list_size DAT 10 input_limit DAT 100 load_instruction LDA list store_instruction STA list menu_five DAT 995 menu_six DAT 996 menu_seven DAT 997 menu_eight DAT 998 menu_nine DAT 999 # variables input DAT 0 list_size DAT 0 list DAT DAT DAT DAT DAT DAT DAT DAT DAT DAT <script src="https://cdn.jsdelivr.net/gh/trincot/lmc@v0.816/lmc.js"></script>

您可以使用此代碼段在此處運行代碼。 單擊Run Code Snippet激活 LMC 模擬器,然后使用右側的面板與其進行交互。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM