【深圳 IO 攻略】隱藏關(guān)第 2 關(guān):個人三明治組裝機

本文首發(fā)于 B 站《深圳 IO》文集(https://www.bilibili.com/read/readlist/rl569860)。原創(chuàng)不易,轉(zhuǎn)載請注明出處。
點擊主頁上的【控制面板】,在【謎題檔案】里輸入數(shù)字 2241,打開隱藏關(guān)第 2 關(guān)《個人三明治組裝機》。

本關(guān)是龍騰第 14 關(guān)《個人三明治制作機》的升級關(guān)卡,尚未完成龍騰第 14 關(guān)的同學(xué)建議先閱讀龍騰第 14 關(guān)的攻略,完成第 14 關(guān)后再來挑戰(zhàn)本關(guān):【深圳 IO 攻略】第 14 關(guān):三明治制作機
本關(guān)和龍騰第 14 關(guān)都需要根據(jù)小鍵盤的輸入做出符合要求的三明治。但第 14 關(guān)的小鍵盤是一次性輸入(1 標(biāo)準,2 不加奶酪,3 多加芥末),本關(guān)的小鍵盤是組合輸入:
輸入 1 表示要求下一份三明治不加奶酪
輸入 2 表示要求下一份三明治多加芥末
輸入 -1 表示撤銷之前的一切個性化定制需求
輸入 3 表示完成定制,開始制作三明治
也就是說,在輸入 3 之前,都處于【接收定制規(guī)格】的階段,定制接下來要制作的三明治的類型。我們不難發(fā)現(xiàn),本關(guān)一共支持制作四種類型的三明治:
加奶酪和 1 份芥末的三明治
加奶酪和 2 份芥末的三明治
不加奶酪,加 1 份芥末的三明治
不加奶酪,加 2 份芥末的三明治
相比于第 14 關(guān),多出了【既不要奶酪,又要多加芥末】的三明治。
和第 14 關(guān)一樣的地方在于,四種三明治都要加一片下層面包、一塊肉和一片上層面包,僅在添加的奶酪和芥末數(shù)量上有區(qū)別。我們在這里可以使用位運算的方法來接收用戶的定制:
小鍵盤按下 -1 時,將 acc 重置為 000;
小鍵盤按下 1 時,說明客戶不要奶酪,將 acc 的十位置 1;
小鍵盤按下 2 時,說明客戶要多加芥末,將 acc 的百位置 1;
小鍵盤按下 3 時,acc 的值可能是 000、010、100、110 四種之一,依次對應(yīng)標(biāo)準三明治、不加奶酪的三明治、多加芥末的三明治,以及既不加奶酪又多加芥末的三明治。根據(jù) acc 的值制作出對應(yīng)的三明治即可。
電路圖和代碼如下:


左邊的芯片用來接收用戶的定制,右邊的芯片用來制作成品三明治。我們先看左邊芯片的代碼。
當(dāng)用戶按下小鍵盤的數(shù)字后(slx x1),將該數(shù)字放入 dat 寄存(mov x1 dat)。如果按下的數(shù)字是 3(teq dat 3),則將定制的三明治編號發(fā)給右邊的芯片,委托它來制作三明治(+ mov acc x3)。這里要注意一下,芥末的份數(shù)是跟三明治編號的百位相關(guān)的。百位為 0 的兩種三明治,芥末時長為 1 秒;百位為 1 的兩種三明治,芥末時長為 2 秒。因此芥末時長 = 百位 +1。這里由左邊芯片提前把芥末時長算好發(fā)給右邊的芯片(+ dgt 2, + add 1, + mov acc x3),因為右邊的芯片沒有多余的代碼空間來親自計算芥末時長了。操作完成后,清除 acc,準備接收下一筆訂單(+ mov 0 acc)。如果按下的數(shù)字是 -1,同樣清除 acc(- teq dat -1, + mov 0 acc)。當(dāng)按下的既不是 3 也不是 -1 時,視按下的數(shù)字的不同,將 acc 的對應(yīng)位置 1:按下 1 時將十位置 1,按下 2 時將百位置 1(- dst dat 1)。
右邊的芯片用來依照訂單制作三明治。首先等待左邊的芯片將訂單發(fā)來(slx x1),收到訂單后,依次將三明治編號和芥末時長存入 acc 和 dat 寄存器(mov x1 acc, mov x1 dat)。首先是四種三明治都一樣的下層面包(gen p1 1 0)和一塊肉(mov 100 x3, slp 1)。然后,僅當(dāng)三明治編號的十位為 0 時,也就是 acc 的值為 000 或 100 時,才加一份奶酪。這里的測試指令用 - 串聯(lián),形成了【或】關(guān)系(teq acc 0, - teq acc 100, + mov 10 x3, + slp 1)。接著加 dat 秒的芥末(mov 1 x3, slp dat)。最后是四種三明治都一樣的上層面包(gen p1 1 x3,此處讀 x3 會讀到 0,同時清除 DX-300 的三路輸出信號,一舉兩得)和 3 秒國旗信號(gen p0 3 0)。
點擊左下角的【模擬】,稍等片刻,便會彈出結(jié)算界面:

優(yōu)化電量和代碼行數(shù)
四種三明治的編號分別是 000、010、100、110,它們除以 14 的余數(shù)分別是:
0 mod 14 = 0
10 mod 14 = 10
100 mod 14 = 2
110 mod 14 = 12
這四個余數(shù)既不相同也不相鄰,那么我們完全可以把四種三明治的奶酪時長和芥末時長放入 ROM 中,實際程序中通過查表的方式獲得奶酪時長和芥末時長。如此做,便可以節(jié)省下一部分電量和代碼行數(shù),而且成本也不會增加哦,可以說是完爆上一個方案了。
明明多了一個 ROM,為什么說成本沒有增加呢?其實,正是因為有了這塊 ROM,左邊的芯片才能節(jié)約成本換成 MC4000X。ROM 多花 2 塊錢,芯片降級成 MC4000X 省掉 2 塊錢,一正一負正好抵消。
上一版方案里,左邊的芯片寫了 10 行代碼,且用到了 dat 寄存器。為什么一塊 ROM 就既能把 dat 去掉,又能減少代碼行數(shù)呢?首先我們看為什么能減少代碼行數(shù)。上一版方案中,我們向右邊芯片傳了三明治編號后,又計算了百位 +1 的值(芥末時長)發(fā)送了過去。這版方案里,芥末時長是寫在 ROM 里的,根本不需要計算,因此這部分的 3 行代碼就去掉了。
然后我們再看看為什么能去掉 dat。小鍵盤有 -1、1、2、3 四種輸入,一個 tcp 三態(tài)判定是涵蓋不了所有情況的,至少要兩次判定才能涵蓋所有情況。因此收到小鍵盤的輸入后,我們必須要將輸入的值暫存起來以便后續(xù)能夠多次判定。存到 acc 里是肯定不行的,那里是存三明治編號的地方。MC6000 我們還有個 dat 能用,但 MC4000X 怎么辦呢?其實,我們可以借用 ROM 的地址口來暫存這個值。小鍵盤的輸入只有 -1、1、2、3 四種,映射成 ROM 地址后變成了 13、1、2、3 這四個數(shù)字,完全沒有沖突。
其實,ROM/RAM 的地址口除了用來指示下一個要讀取的位置之外,還有一個重要的作用:可以把它作為芯片的一個額外寄存器來使用。它是一個只能存 0~13 數(shù)字的,只有 +1 并?mod 14 這一種運算的 tinyacc。但有那么些時候,我們只是需要一個額外的寄存器而已,而且并不需要存復(fù)雜的數(shù)字。能做到這些的寄存器,往往就已經(jīng)足夠了。本方案里,我們借助 ROM 的這個特性,就可以成功去掉左邊芯片中的 dat 寄存器,并順帶將其換成 MC4000X 以節(jié)約成本。電路圖和代碼如下:


四種三明治的奶酪時長、芥末時長如下:
000 號:奶酪 1,芥末 1
010 號:奶酪 0,芥末 1
100 號:奶酪 1,芥末 2
110 號:奶酪 0,芥末 2
因此,我們將 1、1 寫在 ROM 的 0 號地址處,將 0、1 寫在 ROM 的 10 號地址處,將 1、2 寫在 ROM 的 2 號地址處(100 mod 14 = 2),將 0、2 寫在 ROM 的 12 號地址處(110 mod 14 = 12)。這樣,制作三明治時,我們先將 ROM 的地址置為和三明治編號一致,然后連讀兩格 ROM,就能快速獲得當(dāng)前這種三明治的奶酪時長和芥末時長了。
現(xiàn)在我們來看左邊的芯片。收到小鍵盤的數(shù)字后(slx x0)后,首先將該數(shù)字寄存到 ROM 的地址口(mov x0 x3)。如果按下的數(shù)字是 3(teq x3 3),則將定制的三明治編號發(fā)給右邊的芯片(無需再計算芥末時長),委托它來制作三明治(+ mov acc x2)。操作完成后,清除 acc,準備接收下一筆訂單(+ mov 0 acc)。如果按下的數(shù)字是 -1,映射到 ROM 的地址口上會變成 13。如果我們從地址口讀到了 13,同樣清除 acc(- teq x3 13, + mov 0 acc)。當(dāng)按下的既不是 3 也不是 -1 時,視按下的數(shù)字的不同,將 acc 的對應(yīng)位置 1:按下 1 時將十位置 1,按下 2 時將百位置 1(- dst x3?1)。
右邊的芯片收到訂單后(slx x2),將三明治編號設(shè)置為 ROM 地址,會自動對 14 取模(mov x2 x1)。首先是四種三明治都一樣的下層面包(gen p1 1 0)和一塊肉(mov 100 x3, slp 1)。然后,讀一格 ROM,獲得當(dāng)前三明治的奶酪時長,令機器加這么多分量的奶酪(mov 10 x3, slp x0)。接著,再讀一格 ROM,獲得當(dāng)前三明治的芥末時長,令機器加這么多分量的芥末(mov 1 x3, slp x0)。最后是四種三明治都一樣的上層面包(gen p1 1 x3)和 3 秒國旗信號(gen p0 3 0)。
右邊這塊芯片里一條判斷指令都沒寫,就這樣通過 ROM 查表的方式完成了一次三明治的制作。點擊左下角的【模擬】,稍等片刻,便會彈出結(jié)算界面:

成本 ¥11(沒有任何增加),電量 141→123(減少了 18 格電),代碼行數(shù) 18(減少了 6 行代碼)。完爆了上一個方案。
【深圳 IO 攻略】隱藏關(guān)第 2 關(guān):個人三明治組裝機的評論 (共 條)
