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

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

【深圳 IO 攻略】第 26 關(guān):電子門鎖

2022-06-09 12:21 作者:ココアお姉ちゃん  | 我要投稿

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

關(guān)卡展示

本關(guān)的讀卡器會不定期地發(fā)送一些長度為 10 的數(shù)據(jù)包。收到數(shù)據(jù)包時(shí),若【學(xué)習(xí)】信號處于激活狀態(tài),則將門鎖密碼更改為剛才收到的 10 位數(shù)字。若【學(xué)習(xí)】信號處于非激活狀態(tài),則表示嘗試用對應(yīng)密碼的房卡開鎖。僅當(dāng)房卡密碼與門鎖密碼一致,或房卡密碼為 9999999999(10 個(gè) 9)時(shí)才能打開房門,生成時(shí)長 6 秒的解鎖信號。題目保證【學(xué)習(xí)】時(shí)不會將門鎖密碼置為萬能密碼 9999999999。

這道題我們分兩部分完成。首先完成【學(xué)習(xí)】信號激活時(shí)存儲新密碼的部分。

第一塊芯片連接讀卡器需要用掉一個(gè) x 口,連接 RAM 需要用掉兩個(gè) x 口,至此已經(jīng)占用掉了三個(gè) x 口。因此,第一塊芯片已經(jīng)不可能用 MC4000,要么 MC4000X,要么 MC6000。這里我們還剩下一個(gè) p 口的【學(xué)習(xí)】信號需要連接??紤]到只有 7 行代碼,且沒用到 dat 寄存器,這里我們選擇使用 MC4000X 型號的芯片,同時(shí)再用一塊 DX-300 將【學(xué)習(xí)】p 信號轉(zhuǎn)成 x?信號。因?yàn)?MC4000X + DX-300 組合可以比單純的?MC6000 省下 1 塊錢的成本。

首先我們等待讀卡器將芯片喚醒(slx x0)。喚醒后將 RAM 的地址口清零,準(zhǔn)備讀取或?qū)懭肟ㄌ枺╩ov 0 x3)。此時(shí)檢查學(xué)習(xí)信號是否處于激活狀態(tài)(tcp?x1?0)。若不處于激活狀態(tài),則表明當(dāng)前不處于學(xué)習(xí)狀態(tài),而處于開房狀態(tài)。而判定開房與否,不是這塊芯片的工作,是之后放在右側(cè)的芯片的工作。對于左側(cè)芯片來說,關(guān)閉一切 + - 號指令,直接跳到最后睡覺就完事了(slp 6)。僅當(dāng)學(xué)習(xí)信號處于激活狀態(tài)時(shí),才輪到這塊芯片干事。我們需要不斷將讀卡器中的數(shù)字寫入 RAM。每寫入一位卡號(+?mov x0?x2),就檢查是否存滿了 10 位數(shù)(+ tlt x3 10)。尚未存滿 10 位數(shù)時(shí),跳回到第 4?行繼續(xù)存(+?jmp 4),直到存滿 10 位數(shù)后,休眠 6 秒(slp 6),等待下一個(gè)數(shù)據(jù)包。

現(xiàn)在開始嘗試寫解鎖部分的代碼。滿足以下條件之一即可解鎖:①房卡密碼與 RAM 中前 10 位完全對應(yīng);②房卡密碼是 9999999999。那么,我們可以使用位運(yùn)算的方法來判斷兩個(gè)條件是否滿足任意一個(gè)。當(dāng)任意一位數(shù)不是 9 時(shí),就將個(gè)位置 1,不再回 0;當(dāng)任意一位數(shù)與 RAM 無法對應(yīng)時(shí),就將十位置?1,不再回 0。10 位數(shù)字都判斷完之后,如果最終 acc 的值是 11(房卡密碼既不與 RAM 完全對應(yīng),又出現(xiàn)了至少一個(gè)非 9 數(shù)字),那么就不開門。如果是其他的值,那么就開門。

最終的電路圖和代碼如下:

注意一下電路圖里的導(dǎo)線。讀卡器既和左側(cè)芯片的 x0 相連,又和右側(cè)芯片的 x3 相連。但是因?yàn)閮蓧K芯片最多只會有一塊處于工作狀態(tài),不會同時(shí)處于工作狀態(tài),所以在讀卡號時(shí)不會出現(xiàn)兩塊芯片競爭讀取的現(xiàn)象,也就不會導(dǎo)致最終讀取到錯(cuò)誤的卡號。

然后是 RAM 的地址口和數(shù)據(jù)口。左側(cè)芯片的 x3,以及右側(cè)芯片的 x0 都連在了 RAM 的 a0 口上;左側(cè)芯片的 x2,以及右側(cè)芯片的 x1 都連在了 RAM 的 d0 口上,也就是說,雖然 RAM 提供了左右兩個(gè)數(shù)據(jù)指針,但是這里,我們選擇了左右芯片共享同一個(gè)數(shù)據(jù)指針??蔀槭裁匆@樣做呢,明明右邊連 a1 和 d1 更美觀一點(diǎn)?。?strong>因?yàn)檫@樣做實(shí)屬無奈。因?yàn)榭ㄌ柺?10 位,不是 14 位,所以我們在讀/寫 RAM 前,都是需要將 RAM 指針歸零的,否則會讀到錯(cuò)誤的卡號/將卡號寫在錯(cuò)誤的位置上。而右側(cè)芯片的代碼邏輯已經(jīng)寫滿了整整 14 行,無法再多容納哪怕“將地址清零”這樣一行代碼。所以我們在這里選擇了這樣的一種行為模式:兩塊芯片共享同一個(gè)數(shù)據(jù)指針,一旦讀卡器有信號傳來,就由左側(cè)的芯片統(tǒng)一將指針歸零。這樣右邊的芯片就可以省下關(guān)鍵的一行“將 a1 指針清零”的代碼。

現(xiàn)在開始看代碼。首先我們等待讀卡器將芯片喚醒(slx x3),喚醒后檢查學(xué)習(xí)信號是否處于激活狀態(tài)(tcp x2 0)。若處于激活狀態(tài),說明現(xiàn)在正處于設(shè)置新密碼的狀態(tài)。而這些事是左側(cè)芯片的工作,右側(cè)芯片這時(shí)候只需要跳到最后去睡覺就完事了(+ jmp d)。僅當(dāng)學(xué)習(xí)信號不處于激活狀態(tài)時(shí),才輪到這塊芯片干事。此時(shí)我們需要從讀卡器中讀取一位數(shù)。因?yàn)槭盏降倪@一位數(shù)需要判斷兩次,先判斷是否是 9,再判斷是否與 RAM 里的數(shù)字對應(yīng)。所以我們需要先將它緩存到 dat 寄存器里(mov x3 dat)。緩存完后,首先判斷這位數(shù)是否是 9(teq dat 9)。如果是 9,不改變個(gè)位數(shù)。一旦出現(xiàn)非 9 數(shù)字,就將個(gè)位數(shù)置為 1(- dst 0 1)。再判斷這位數(shù)是否與 RAM 里對應(yīng)位置的數(shù)一致(teq dat x1)。一致時(shí),不改變十位數(shù)。一旦出現(xiàn)不一致的數(shù)字,就將十位數(shù)置為 1(- dst 1 1)。至此,判斷 RAM 的指針是否小于 10(tlt x0?10)。如果小于 10,就說明仍有等待接收的數(shù)字,跳回到第 4?行繼續(xù)接收(+?jmp 4),直到 10 個(gè)數(shù)字全部接收完畢后,判斷 acc 的值是否是 11(teq acc 11)。如果 acc 是 11,說明密碼【既不和 RAM 中的數(shù)字一致,又出現(xiàn)了至少一位非 9 數(shù)字】,直接睡覺,不開房門(slp 6)。如果 acc 不是 11,就說明【密碼一致】、【密碼全 9】兩個(gè)條件至少滿足一個(gè),打開房門(- mov 100 p1, slp 6)。做完以上操作后,將 acc 和解鎖信號都清除,等待下一次開門任務(wù)的來臨(mov p1 acc)。

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

優(yōu)化成本和代碼行數(shù)

其實(shí)即使是 21 行代碼,也是可以壓縮到 14 行,合并到一塊 MC6000 里的。關(guān)鍵的操作在于【邏輯合并】,即使處于學(xué)習(xí)狀態(tài),我也照樣進(jìn)行位運(yùn)算。只需要學(xué)習(xí)狀態(tài)下,每一位密碼都“視為和原密碼不一致”,恒定地將十位置為 1 即可。這樣就不用害怕會“錯(cuò)誤地打開房門”了。電路圖和代碼如下:

首先我們等待讀卡器將芯片喚醒(slx x0)。喚醒后,我們讀取一位數(shù)字,并放到 dat 里(mov x0 dat)。首先判斷這位數(shù)字是不是 9(teq dat 9),若是 9,則無事發(fā)生;若不是 9,則將 acc 的個(gè)位置 1(- dst 0 1)。接下來,我們檢查當(dāng)前是否處于學(xué)習(xí)狀態(tài)(teq p0 0)。處于學(xué)習(xí)狀態(tài)時(shí),覆蓋 RAM 對應(yīng)位置原先的數(shù),并一律視為和原先的數(shù)不相等,直接將 acc 的十位置 1(- mov dat x2, - dst 1 1)。不處于學(xué)習(xí)狀態(tài)時(shí),我們從 RAM 中讀取一位數(shù),并判斷 dat 是否與之一致(+ teq dat x2)。一致時(shí),無事發(fā)生;不一致時(shí),將 acc 的十位置 1(- dst 1 1)。每讀/寫完一位數(shù),就判斷 RAM 的指針是否小于 10(tcp x3 10)。如果小于 10,就說明仍有等待接收的數(shù)字,跳回到第 2 行繼續(xù)接收(- jmp 2),直到 10 個(gè)數(shù)字全部接收完畢后,判斷 acc 的值是否是 11(teq acc 11)。如果 acc 是 11,可能是處于學(xué)習(xí)狀態(tài),還可能是密碼既不是全 9,又與門鎖密碼不一致。不論是上面所說的兩種情況中的哪種,我們都不開門。僅當(dāng)不處于學(xué)習(xí)狀態(tài)時(shí),房卡密碼【與門鎖密碼一致】、【是全 9】滿足兩者之一時(shí),打開房門(- gen p1 6 0)。做完這些事后,將用于位運(yùn)算的 acc 和用于讀/寫密碼的 RAM 地址全部歸零,準(zhǔn)備迎接下一次任務(wù)(mov 0 x3, sub acc)。

本方案里,耗電上升到了 484。這是因?yàn)?,我們將【學(xué)習(xí)】和【開房】的邏輯做了大規(guī)模合并,在【學(xué)習(xí)】的過程中我們做了很多多余的操作:①學(xué)習(xí)時(shí)我們依然跟開房時(shí)一樣,將數(shù)據(jù)包中的數(shù)放入 dat 緩存;②學(xué)習(xí)時(shí)我們依然跟開房時(shí)一樣在進(jìn)行位運(yùn)算,并以位運(yùn)算的結(jié)果作為最后是否應(yīng)該開門的判定依據(jù);③為了壓縮到 14 行代碼,開門時(shí)使用了 gen 指令,涵蓋在其中的 mov 0 p1 和 slp 0 這兩條“空操作”指令帶來了額外的電量消耗;④因?yàn)閷W(xué)習(xí)的過程中依然跟開房時(shí)一樣用到了 acc 和 dat,所以學(xué)習(xí)結(jié)束后依然要清除這兩個(gè)寄存器。以上,我提到了三次“跟開房時(shí)一樣”,這就是【邏輯合并】的表現(xiàn)。邏輯合并固然帶來無謂的電量消耗,但好處是我們成功將代碼壓縮到了 14 行,成功將代碼放進(jìn)了一塊 MC6000 里,節(jié)省代碼行數(shù)的同時(shí),也節(jié)省了成本。

成本 ¥5 以及電量 117 的方案是怎么實(shí)現(xiàn)的?你不用 RAM 存門鎖密碼的嗎?

有讀者可能會問了,看你這個(gè)方案里,三項(xiàng)指標(biāo)都沒有達(dá)到你的最佳,你是不是有所保留了?我的回答如下:本關(guān)的主題是【安全門鎖】,一切設(shè)計(jì)方案都需要首先保證安全。如果為了優(yōu)化三項(xiàng)指標(biāo)而犧牲安全性,將錯(cuò)誤的密碼判斷成了正確的密碼,那就只是騙過了測試樣例,實(shí)際卻不符合題意了。

我曾經(jīng)寫出過的一切“優(yōu)化過三項(xiàng)指標(biāo)”的方案都是不安全的方案,存在用【和門鎖密碼不一致,且不是?9999999999】的房卡開鎖的可能性,安全性無法得到保障。在不犧牲安全性的前提下,以上方案是我能想到的最優(yōu)方案。如果讀者有更好的,不以犧牲安全性為代價(jià)的方案,請給我留言。

2023?年 2?月 21?日更新

時(shí)隔半年,我竟然收到了 Discord?上熱心網(wǎng)友的留言

ta?說,可以記錄并比較前綴和。因?yàn)槭幻艽a都是 0~9,所以和最多為 90,多出來的百位可以用于標(biāo)記是否出現(xiàn)了不匹配的數(shù)字。這樣就把原先的兩個(gè)標(biāo)記位減少為了一個(gè)。優(yōu)化后的代碼如下:

我們先看處于學(xué)習(xí)狀態(tài)(即 p0 = 100)時(shí)會發(fā)生什么。忽略掉不會執(zhí)行的代碼后,代碼看起來是下面這樣的:

讀卡器信號出現(xiàn)后(slx x0),將 acc?初始化為 100(mov 100 acc)。接下來,每從讀卡器讀取一位數(shù)字,就將讀到的數(shù)字累加到 acc?里(add x0),并將實(shí)時(shí)的前綴和寫入 RAM(tcp p0 50, + mov acc x2)。至此,判斷 RAM 的指針是否小于 10(tcp?x3?10)。如果小于 10,就說明仍有等待接收的數(shù)字,跳回到第 3 行繼續(xù)接收(-?jmp 3)。直到 10?個(gè)數(shù)字全部接收完畢后,將 RAM?指針歸零(mov p1 x3)。

接下來再看處于非學(xué)習(xí)狀態(tài)(即 p0 = 0)時(shí)會發(fā)生什么。忽略掉不會執(zhí)行的代碼后,代碼看起來是下面這樣的:

也就是只忽略掉了第 5?行代碼。讀卡器信號出現(xiàn)后(slx x0),將 acc?初始化為?100(mov 100 acc)。接下來,每從讀卡器讀取一位數(shù)字,就將讀到的數(shù)字累加到 acc?里(add x0),并判斷此時(shí)的 acc?和 RAM?里對應(yīng)位置的前綴和是否匹配(tcp p0 50, - teq acc x2)。匹配時(shí),無事發(fā)生;一旦出現(xiàn)了不匹配的數(shù)字,就將 acc?的百位置為 0(- dst 2 0)。至此,判斷 RAM?的指針是否小于 10(tcp?x3?10)。如果小于 10,就說明仍有等待接收的數(shù)字,跳回到第 3?行繼續(xù)接收(-?jmp 3)。直到 10?個(gè)數(shù)字全部接收完畢后,判斷 acc?的值是否大于 89(teq p0 0, + tcp acc 89)。

開門的前提條件是以下兩者之一:①十位房卡密碼完全匹配;②房卡密碼是 9999999999。我們知道,在循環(huán)的過程中,如果出現(xiàn)了不匹配的前綴和,百位會被立刻置零。而又因?yàn)榉靠艽a自身的和不會是三位數(shù),acc?的百位僅用作標(biāo)記,所以根據(jù)原命題和逆否命題等價(jià)的原則,只要最終的?acc 是三位數(shù),就說明房卡密碼完全匹配。而如果累加和是兩位數(shù)的話,就說明至少有一位不匹配的數(shù)字。當(dāng)出現(xiàn)了不匹配的數(shù)字時(shí),僅當(dāng) acc 為 90,即每一位都為 9?時(shí),才能打開房門。綜上所述,僅當(dāng) acc?的值大于 89?時(shí),才觸發(fā) 6?秒的解鎖信號(+ mov 100 p1, + slp 6)。執(zhí)行完畢后,同時(shí)將解鎖信號和 RAM?地址歸零(mov p1 x3)。

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

電量 484→357,其他兩項(xiàng)指標(biāo)不變,完爆了我的上一版方案。

【深圳 IO 攻略】第 26 關(guān):電子門鎖的評論 (共 條)

分享到微博請遵守國家法律
陵川县| 罗田县| 田阳县| 麦盖提县| 高清| 突泉县| 石屏县| 长寿区| 彩票| 枣强县| 齐齐哈尔市| 宜兰县| 铁力市| 富平县| 富锦市| 安化县| 佛学| 河源市| 天峨县| 平安县| 北辰区| 察雅县| 丰镇市| 突泉县| 车险| 四川省| 自贡市| 黔西县| 嘉义市| 建瓯市| 鱼台县| 綦江县| 中西区| 连云港市| 宁蒗| 茶陵县| 太康县| 班戈县| 武隆县| 虞城县| 大邑县|