【深圳 IO 攻略】第 29 關(guān):變色鞋

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

本關(guān)需要在按鈕按下時(shí),根據(jù)傳感器所表示的波長信息,將波長轉(zhuǎn)換成顏色,并統(tǒng)計(jì)每種顏色的出現(xiàn)頻率;并在按鈕抬起時(shí),找到出現(xiàn)頻率最高的顏色,將它轉(zhuǎn)換成對應(yīng)的 SmartDye 顏色,再進(jìn)一步轉(zhuǎn)換成墨水 k 和墨水 n 信號輸出。
每種顏色和光的波長,以及墨水 k、墨水 n 的映射關(guān)系參考數(shù)據(jù)手冊:

本關(guān)是【深圳龍騰有限公司】系列里公認(rèn)的最難關(guān),其難度可以媲美【阿瓦隆城】的部分關(guān)卡。本關(guān)至少要三塊 MC6000,還要同時(shí)配上一塊 ROM 和一塊 RAM 才能完成。這一關(guān)的接線圖如下所示:

現(xiàn)在,我們像之前一樣,把大問題分解成若干小問題來完成。
左邊芯片的任務(wù)是:當(dāng)按鈕按下時(shí),統(tǒng)計(jì)各種顏色的出現(xiàn)頻率;當(dāng)按鈕抬起時(shí),通知中間的芯片找到最頻繁出現(xiàn)的顏色。
中間芯片的任務(wù)是:收到左邊芯片的通知信號后,找到最頻繁出現(xiàn)的顏色,并將對應(yīng)的顏色編號告知右邊的芯片。統(tǒng)計(jì)完畢后,將 RAM 中的統(tǒng)計(jì)信息清除。
右邊芯片的任務(wù)是:收到中間芯片的通知信號后,把對應(yīng)的最頻繁顏色映射成 SmartDye 色彩空間的顏色,并進(jìn)一步轉(zhuǎn)換成墨水 k 和墨水 n 的信號輸出。



首先我們來看最左邊的芯片。根據(jù)數(shù)據(jù)手冊里的說明,我們可以知道:顏色只跟傳感器值的十位數(shù)相關(guān),紅 = 十位數(shù)是 2 或 3;橙 = 十位數(shù)是 4;黃 = 十位數(shù)是 5;綠 = 十位數(shù)是 6;藍(lán) = 十位數(shù)是 7;紫 = 十位數(shù)是 8。
首先我們檢測按鈕是否按下(tcp p1 50)。當(dāng)按下時(shí),讀取傳感器當(dāng)前的值(+ mov p0 acc),并提取出十位(+ dgt 1),將該值作為 RAM 地址,改寫對應(yīng)位置的值。因?yàn)樾枰磸?fù)使用到這個(gè)地址,所以我們首先將該地址在 dat 里暫存一份(+ mov acc dat)。我們將 RAM 的地址設(shè)置為該地址(+ mov dat x3),提取出里面的頻度數(shù)字(+ mov x2 acc),將頻度 +1 后準(zhǔn)備送回原處(+ add 1)。由于讀取 RAM 后地址會(huì)自增,所以我們首先要將地址還原(+ mov dat x3),再將 +1 后的值送回原處(+ mov acc x2)。如此,便完成了當(dāng)前這一秒的統(tǒng)計(jì)工作。跳到最后休眠一秒,進(jìn)入下一個(gè)時(shí)鐘周期(slp 1)。
如果按鈕沒按下時(shí),檢查 dat 是否為 0(- teq dat 0)。因?yàn)橹灰覀冇|發(fā)了統(tǒng)計(jì)過程,dat 的值一定會(huì)被更新為某一刻傳感器值的十位數(shù)。所以當(dāng) dat 為 0 時(shí),表示尚未觸發(fā)統(tǒng)計(jì)過程,就不需要通知右邊的芯片“找最大值”。只有當(dāng)統(tǒng)計(jì)完畢,剛剛抬起按鈕時(shí),才需要通知右邊的芯片去“找最大值”。這時(shí)候我們將 dat 歸零(- mov 0 dat),回到“等待統(tǒng)計(jì)”的狀態(tài),然后給右邊芯片發(fā)送一個(gè) 2(- mov 2 x1),通知右邊的芯片去尋找最大值。至于這個(gè) 2 有什么用,我們后面再說。做完以上事情后,休眠一秒,進(jìn)入下一個(gè)時(shí)鐘周期(slp 1)。
有小伙伴可能會(huì)說,十位數(shù)不論是 2 還是 3 都是紅色呀,但是你在以上的統(tǒng)計(jì)中只看了十位,十位是 2 以及十位是 3 的數(shù)字?jǐn)?shù)量被分散在了兩個(gè)不同的格子里,被視為了兩種不同的顏色了呀?其實(shí),這點(diǎn)我是有所考慮的。只是因?yàn)榇a行數(shù)的限制,我無法在統(tǒng)計(jì)階段就將兩種紅色合并,我們只能在后期“尋找最大值”的過程中將兩種紅色合并。

下面我們來看中間的芯片。首先我們等待右邊芯片發(fā)來“給我找最大值”的信號(slx x3)。收到信號后,我們將 RAM 的右指針置為 2(mov x3 x1),然后連續(xù)讀取兩格 RAM,將它們的值相加,得到“紅色”的出現(xiàn)次數(shù)(mov x0?acc,?add x0)。我們先假設(shè)最頻繁的顏色是紅色,將“紅色”的地址發(fā)送給右邊的芯片(mov x1?x2)。由于 RAM 讀取后地址會(huì)自增,所以實(shí)際發(fā)送的地址是 +1 后的地址。然后我們開始統(tǒng)計(jì)其他顏色的出現(xiàn)頻數(shù)。我們將新顏色的頻數(shù)放入 dat 中(mov x0?dat),檢查新顏色的頻數(shù)是否大于已找到的最大頻數(shù)(tcp dat acc)。若大于,則說明新顏色才是迄今為止的最頻繁顏色,我們覆蓋掉原先較小的頻數(shù)(+ mov dat acc),然后將新顏色的地址傳給右邊的芯片(+ mov?x1?x2)。右邊的芯片收到數(shù)據(jù)后,會(huì)改寫墨水 k 和墨水 n 的值。而我們知道,同一秒內(nèi)可以反復(fù)改寫 p 口的值,最終只有最后一次改寫的值會(huì)生效。所以,我們可以反復(fù)將找到的“迄今為止的最大值”傳給右邊的芯片,而不用擔(dān)心是否會(huì)出現(xiàn)錯(cuò)誤的信號。接下來,我們判定地址值是否到達(dá)了 9(teq x1?9)。尚未到達(dá) 9 時(shí),跳回到第 6 行,繼續(xù)判斷別的顏色是否可能是更頻繁的顏色(- jmp 6)。直到將所有顏色都統(tǒng)計(jì)完畢后,用循環(huán)的方式將整個(gè) RAM 都寫為 0,清除所有的顏色統(tǒng)計(jì)信息(mov 0 x0, teq x1?9, - jmp c)。

最后我們看右邊的芯片。首先我們將墨水 k 和墨水 n 都初始化為 50(@ mov 50 p1, @ mov 50 p0)。收到中間芯片發(fā)來的地址值后(slx x2),我們將該地址值 ×2(mov x2 acc, add acc),然后到 ROM 中尋找相應(yīng)顏色所對應(yīng)的兩個(gè)連續(xù)數(shù)字(mov acc x1),讀取它們并依次發(fā)送到墨水 k 和墨水 n 端口(mov x0?p1, mov x0?p0)。由于中間芯片發(fā)送的是 +1 后的地址,因此紅色對應(yīng)的地址值是 4,橙色對應(yīng)的地址值是 5,依此類推。我們將地址值 ×2 后,可以發(fā)現(xiàn),8、9 兩格對應(yīng)的值是【血色】的 k、n 值(95,5),10、11 兩格對應(yīng)的值是【深玉米橙】的 k、n 值(50,5),依此類推。也就是我們將原始顏色的地址值 ×2 后,在 ROM 中的對應(yīng)位置就可以匹配上相應(yīng)的 SmartDye 顏色的 k、n 值。

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