最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

【深圳 IO 攻略】第 24 關(guān):交通信號

2022-06-08 10:41 作者:ココアお姉ちゃん  | 我要投稿

本文首發(fā)于 B 站《深圳 IO》文集(https://www.bilibili.com/read/readlist/rl569860)。原創(chuàng)不易,轉(zhuǎn)載請注明出處。

關(guān)卡展示

本關(guān)要求你控制一個六叉路口的交通信號燈。在沒有緊急車輛的時候,按照【燈 0】亮【階段 0】秒→【燈 1】亮【階段 1】秒→【燈 2】亮【階段 2】秒→【燈?0】亮【階段 0】秒→……的方式循環(huán)。而當(dāng)出現(xiàn)緊急車輛時,關(guān)閉所有信號燈,直到緊急車輛離開時,恢復(fù)原先的交通模式(不保留之前的計時)。

本關(guān)如果沒有緊急車輛,我們只需要將三個常數(shù)寄存器連到芯片的三個 x 口上,然后用一個 DX-300 連接右側(cè)的三個信號燈輸出口,并按照下面的方式設(shè)計程序即可:

給 DX-300 賦 100 點亮燈 0,睡 x0 秒;給 DX-300 賦 10?點亮燈 1,睡 x1?秒;給 DX-300 賦 1?點亮燈 2,睡 x2?秒。如此循環(huán)即可。

但是現(xiàn)在有緊急車輛,你就不能在點燈過程中一直睡眠。如果在睡眠過程中來了緊急車輛,你總不能告訴我你還沒睡醒,信號燈滅不掉呀?

我們現(xiàn)在的思路是:用 dat 寄存器記錄當(dāng)前亮的燈是哪一個(和 DX-300 語言一致,0 都不亮,100 亮燈 0,10 亮燈 1,1 亮燈 2),用 acc 寄存器記錄當(dāng)前燈還要亮多久。當(dāng)遇到緊急車輛時,將 acc 和 dat 強行清零。當(dāng)沒有緊急車輛時,acc 若未減到 1,則 acc -1,不改變燈的狀態(tài);當(dāng) acc 減到 1?時,根據(jù)當(dāng)前點燈狀態(tài),判斷接下來的點燈狀態(tài)和持續(xù)時長,更新 dat 和 acc 寄存器。

這些邏輯看似不復(fù)雜,你首先想到的應(yīng)該是將所有邏輯寫在一塊 MC6000 里:

結(jié)果非??上В辽傩枰?15 行代碼,一塊 MC6000 寫不下。我們來分析一下為什么要占用 15 行,為什么省不掉任何一行代碼:

緊急車輛出現(xiàn)時需要關(guān)閉所有信號燈,并將計時器重置為 1,這部分的代碼占用了 5 行(tcp p0 50, + mov x3 dat, + mov 2 acc, + sub 1, + jmp f)。因為 x3 口連接著 DX-300,進而連接著三個只寫 p 口。所以讀 x3 口會讀到立即數(shù) 0,同時清除三個只寫 p 口數(shù)據(jù)。我們在清除三個 p 口數(shù)據(jù)的同時,可以將讀到的這個?0?數(shù)字立刻寫入 dat 寄存器,一舉兩得。后兩條邏輯是和下一條判斷共用的,凈占用為 3 行。

沒有緊急車輛出現(xiàn),且計時器尚未減到 1 時,令計時器減 1,這部分的代碼占用了 3?行(- tgt acc 1, + sub 1, + jmp f)。因為下方的 +/- 號指令會修改信號燈的狀態(tài)和計時器。而當(dāng)計時器尚未減到 1 時,當(dāng)前信號仍在正常倒計時,信號燈狀態(tài)不應(yīng)被改變。所以這里不得不使用 jmp 指令強制跳到最后休眠,以避免錯誤地執(zhí)行下方用于修改信號燈狀態(tài)的 + 號指令。緊急車輛的部分也是一樣要強制跳到最后,不能讓任意一個信號燈點亮。至此,代碼已占用掉了 6 行。

沒有緊急車輛出現(xiàn),且計時器減到 1 時,我們需要用多達 8 行代碼來判斷下一個信號燈狀態(tài),并向?DX-300 寫入該值以便修改信號燈的狀態(tài)。首先檢查當(dāng)前的信號燈狀態(tài)是否是燈 1 亮的中間狀態(tài)(tcp dat 10)。我們先假設(shè)是中間狀態(tài),將下一個狀態(tài)置為燈 2 亮,持續(xù)時長為【階段 2】秒(mov 1 dat, mov x2 acc)。如果當(dāng)前狀態(tài)是端點狀態(tài),再撤銷之前的設(shè)置。如果 dat 是 100,也就是當(dāng)前狀態(tài)是燈 0 亮,那么下一個狀態(tài)為燈 1 亮,持續(xù) x1 秒(+ mov 10 dat, + mov x1 acc)。如果 dat 是?0 或 1,也就是當(dāng)前狀態(tài)是燈 2 亮,或緊急車輛剛離開,那么下一個狀態(tài)為燈 0 亮,持續(xù) x0 秒(- mov 100 dat, - mov x0 acc)。設(shè)置完新的信號燈狀態(tài)后,將該狀態(tài)值發(fā)給 DX-300(mov dat x3)。至此,14 行代碼空間已經(jīng)占用完畢。然而我們還需要最后一行代碼:slp 1,休眠一秒,進入下一個時鐘周期!很遺憾,即使只多一行代碼,我們也無法將所有邏輯寫在同一塊芯片里。

無奈,只能分工合作。三態(tài)判定的部分要占用 8 行,太難受了。

改進思路:將【三態(tài)判定】任務(wù)分給另一塊芯片做

我們一旦進入了三態(tài)判定,就需要用多達 8 行代碼來更新 acc 和 dat 兩個寄存器的值。我們現(xiàn)在可以考慮將這么多行代碼邏輯寫到另一塊芯片里,然后用芯片間通訊的辦法,由這另一塊芯片告知本芯片,下一個信號燈狀態(tài)是什么,以及持續(xù)時長是多少。電路圖和代碼如下:

這里我留了個彩蛋,將導(dǎo)線繞了幾個圈,布置成了【六叉路口信號燈】的樣子。不用擔(dān)心電流會往回流,因為電流和水流類似,只會從高電勢的方向往低電勢的方向流動,不會在原地轉(zhuǎn)圈,更不會往高電勢的起點回流

幾乎就是上一版方案的代碼“分裂”到兩塊芯片里的結(jié)果。

我們先看上方的芯片,和上一版方案相比,改動如下:

首先,去掉了一個 jmp 指令。因為三態(tài)判定移動到了下方芯片里,【誤執(zhí)行改變信號燈狀態(tài)的 + 號指令】這樣的前提已經(jīng)不存在了。所以我們只需要將不滿足 - tgt acc 1 判斷時要執(zhí)行的邏輯全部加上 -?號前綴即可,完全不需要使用 jmp 指令。

然后,當(dāng)需要改變信號燈狀態(tài)時,我們將當(dāng)前狀態(tài)通過 x2 口發(fā)給下面的芯片(- mov dat x2),由下方芯片執(zhí)行三態(tài)判定,計算下一個信號燈狀態(tài)值和持續(xù)時長,并傳給上方的芯片。上方的芯片只要依次接收完這兩個值后(- mov x2 dat, - mov x2 acc),將新的狀態(tài)值發(fā)送給 DX-300(- mov dat x3)就算完成任務(wù)。

然后我們看下方的芯片。第 2~8 行代碼和上一版方案的第 7~13 行代碼相比,除了三態(tài)判定由 tcp dat 10 改成了 tcp x3 10 外,其余的地方完全一致。因為在這版方案里,“當(dāng)前狀態(tài)”不是存在下方芯片的 dat 里的,而是存在上方芯片的 dat 里的。上方芯片通過 x3 口將它的 dat 發(fā)過來,我們的三態(tài)判定需要和上方的 dat,也就是和?x3 口作比較,而不是和自己的 dat 做比較

再然后,加上了第 1 行的等待接收指令(slx x3),以及第 9~10 行的發(fā)送指令,依次將本芯片的 dat, acc 寄存器的值發(fā)給上方芯片,覆蓋上方芯片原始的 dat 和 acc 寄存器(mov dat x3, mov acc x3)。至此,本設(shè)計方案相比于上一版已廢棄的設(shè)計方案的改動就說明完畢了。

點擊左下角的【模擬】,運行程序:

緊急車輛出現(xiàn)時,信號燈形狀的導(dǎo)線會被點亮,非常漂亮。稍等片刻,便會彈出結(jié)算界面:

優(yōu)化成本

我們的第一個成品方案里,用了兩塊 MC6000,且都只寫了 10 行代碼。資源沒有得到充分利用。我們?nèi)绻軐蓧K芯片的分工做一個微調(diào),讓 A 多干點事,B 少干點事,那么 A 可以用足一塊大芯片的 14 行代碼空間,B 也可以將代碼壓縮到 9 行以內(nèi),進而將芯片由 MC6000 替換成 MC4000X。這樣我們就可以物盡其用,節(jié)省成本。

節(jié)省成本的電路圖和代碼如下:

同樣是 20 行代碼,現(xiàn)在我們微調(diào)了一下兩者的分工。以前是各 10 行代碼,現(xiàn)在變成了上方 14 行代碼,下方 6 行代碼。這就變成了:上方的芯片物盡其用,下方的芯片成功替換成了成本更低的?MC4000X,整體的設(shè)計成本就這么降低了。

我們的三態(tài)判定是要完成兩個任務(wù)的:①得到下一個信號燈的狀態(tài)值;②得到下一個狀態(tài)值的持續(xù)時長。第一個成品方案里,我們將三態(tài)判定的兩項任務(wù)都交給下方芯片來完成,所以下方芯片也需要 10 行代碼和 acc、dat 兩個寄存器。

這個方案里,我們微調(diào)了一下分工。當(dāng)需要改變信號燈狀態(tài)時,接下來的狀態(tài)值由上方芯片自己算,下方芯片只負責(zé)計算持續(xù)時長。下方芯片的代碼改動很簡單,就是在上一版方案的基礎(chǔ)上,把含有 dat 的指令全部刪除了而已。改變了分工后,下方芯片只需要 6 行代碼,寄存器也只需要 acc 一個了。因此成功替換成 MC4000X。上方芯片的代碼改動也很簡單,就是在前一個成品方案的基礎(chǔ)上,改為自己推斷下一個信號燈的狀態(tài)值。具體我不再做邏輯推演,請讀者自行推演。

本方案的成本降到了 9 塊錢,代碼行數(shù)不變,但是電量稍有增加。原因在于:原先只要一次三態(tài)判定就能完成兩項任務(wù),現(xiàn)在這兩項任務(wù)被分配到了兩塊芯片中,所以變成了兩塊芯片都要做一次三態(tài)判定。資源損耗就來源于此。

使用 RAM 打表,極致優(yōu)化電量和代碼行數(shù)

相比于【三態(tài)判定法】,我們可以改用【RAM 打表法】,將三路信號燈的狀態(tài)碼和時長都寫入 RAM 中。實際執(zhí)行時,遇到需要切換信號燈的場合,直接讀兩格 RAM 就切換到了下一個信號燈狀態(tài),免去了【對當(dāng)前狀態(tài)做三態(tài)判定】的過程。如此,便可大幅減少電量和代碼行數(shù)。而且,由于 RAM 一共有 14 格空間,所以改用 RAM 打表后,何止 6 叉路口,14 叉路口都能給你控制得服服帖帖的!電路圖和代碼如下:

由于階段 0~2 的值(三個燈的持續(xù)時長)只在同一個樣例里保持不變,而不同的樣例間,這三個階段時長是不同的。所以本題我們不能使用 ROM 將三個信號燈的持續(xù)時長預(yù)先寫好,只能【把 RAM 當(dāng) ROM 用】,在上電初始化的過程中把三個狀態(tài)值和三個持續(xù)時長填入 RAM 中,后續(xù)只讀不寫。

我們左下角的芯片做的就是這樣的事。上電的時候,我們將燈 0 的狀態(tài)碼 100,階段 0 的時長 x1,燈 1 的狀態(tài)碼 10,階段 1 的時長 x2,燈 2 的狀態(tài)碼 1,階段 2 的時長 x3,這六個數(shù)寫入 RAM。而且只需要寫一次,后續(xù)就沒有任何其他的事情要做了。所以這 6 條指令都加上了 @ 前綴,執(zhí)行完畢后就永久睡眠了,直到進入下一個樣例,重新上電時才會再次執(zhí)行這六條初始化指令。

因為使用了 RAM,上方的芯片僅用 9 行代碼就能完成所有的信號燈控制邏輯。RAM 的右指針永遠指向當(dāng)前階段計時完畢后,下一個信號燈的狀態(tài)值。

首先檢查是否出現(xiàn)了緊急車輛(tcp p1 50)。當(dāng)出現(xiàn)緊急車輛時,我們需要將信號燈和計時器都清零(+ mov x3 acc,讀連接著若干只寫 p 口的 DX-300 時,會讀到 0,同時清除這些 p 口的數(shù)據(jù),一舉兩得),然后將 RAM 的右指針也清零,將“下一個信號燈狀態(tài)”還原成初始狀態(tài)(+ mov 0 x1)。做完這些后,休眠一秒,進入下一個時鐘周期(+ sub 1, slp 1)。這里的 + sub 1 原本是當(dāng)?shù)谖逍械?- tgt acc 1 指令成立后執(zhí)行的,在這里執(zhí)行只是“省去 jmp 指令所帶來的副作用”,會將 acc 的值由 0 減為 -1。當(dāng)然,由于對 acc 的判斷是 tgt acc 1,也就是實際 acc 的值不論是 1、0 還是 -1,最終要執(zhí)行的操作都是一樣的,這里 acc 變?yōu)?-1 不會給整個程序帶來影響最終輸出結(jié)果的 bug,甚至可以因此省下關(guān)鍵的一行 jmp 指令。

回到開頭,當(dāng)沒有出現(xiàn)緊急車輛時,需要判定 RAM 的地址指針是否指到了 6(- teq x1 6)。當(dāng)?shù)刂分羔樦傅搅?6 時,由于我們在初始化時只給 RAM 的前 6 格寫上了數(shù)據(jù),因此當(dāng)?shù)刂分羔樦赶蛄擞行?shù)據(jù)范圍之外的地址時,我們需要強制讓地址指針歸零,確保信號燈正常循環(huán)(+ mov 0 x1)。僅當(dāng)上一秒鐘讀取了【燈 2 的狀態(tài)值及階段 2 的持續(xù)時長】時,RAM 的地址指針才會到達 6。所以 RAM 的地址是 6 時,當(dāng)前一定處在階段 2 的第 1 秒鐘,所以本秒不需要判定計時器是否減到了 1,直接令計時器正常 -1 即可(+ sub 1)。如此,便又省掉了一行 jmp 指令。

沒有緊急車輛,RAM 指針也正常時,我們判斷計時器是否減到了 1(- tgt acc 1)。尚未減到 1 時,令計時器 -1(+ sub 1)。減到 1 后,我們只需要從 RAM 中連讀兩格數(shù)據(jù),獲得下一個信號燈狀態(tài)及持續(xù)時長即可(- mov x0 x3, mov x0 acc)。做完這些操作后,休眠一秒,進入下一個時鐘周期(slp 1)。

然后我們發(fā)現(xiàn),上方的 MC6000 芯片沒用到 dat 寄存器,且只寫了 9 行代碼。只是接口方面用了三個 x 口,一個 p 口。那么我們完全可以將這個 p 口用 DX-300 轉(zhuǎn)接一下,變成四個 x 口,用 MC4000X + DX-300 組合代替 MC6000,省下一塊錢的成本。最終電路圖和代碼如下:

上方芯片原先是用 x3 口接輸出用的?DX-300 的,現(xiàn)在換到了 x2 口,所以原先代碼里的 x3 全部換成 x2。而原先的 p1?信號現(xiàn)在經(jīng)過?DX-300 的 p2 口轉(zhuǎn)接,輸入到了 x3 口里,所以我們將第一行的 p1 改成 x3。其余的代碼都不用改變。

點擊左下角的【模擬】,稍等片刻,便會彈出結(jié)算界面:

元器件上,上一版方案是 MC4000X + MC6000 + DX300(¥9),這一版方案是 2MC4000X +?2DX-300 + RAM(¥10),貴了一塊錢。但是另兩項指標(biāo),電量驟降到 250,代碼行數(shù)驟減到 15 行,都是質(zhì)的飛躍。用一塊錢換來了這么大的性能提升,值!

【深圳 IO 攻略】第 24 關(guān):交通信號的評論 (共 條)

分享到微博請遵守國家法律
满城县| 漯河市| 塘沽区| 铜陵市| 贵溪市| 望谟县| 桐柏县| 湘阴县| 区。| 喜德县| 汕尾市| 宿迁市| 象州县| 无锡市| 伊春市| 西乌珠穆沁旗| 封丘县| 红桥区| 桦甸市| 甘洛县| 南城县| 广丰县| 芜湖市| 淅川县| 光山县| 耿马| 古丈县| 永寿县| 东辽县| 肥城市| 新和县| 慈溪市| 塔城市| 佛学| 绩溪县| 浦东新区| 白水县| 佛学| 新津县| 三原县| 无为县|