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

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

【TIS-100 攻略】第 4~5 關(guān):信號比較器、信號疊加器

2022-10-18 22:38 作者:ココアお姉ちゃん  | 我要投稿

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

第 4 關(guān)《信號比較器》(Signal Comparator)關(guān)卡展示

本關(guān)要求從 IN 收到正數(shù)時,向 OUT.G 寫?1;從 IN 收到 0 時,向 OUT.E 寫 1;從 IN 收到負(fù)數(shù)時,向 OUT.L 寫 1。因?yàn)橐陨先齻€條件,任何時候都必然有且僅有一條成立,所以同一時刻,三個輸出口只能有一個被寫入 1,其余兩個條件不成立的端口需要同時被寫入 0。

本關(guān)我們需要接觸 5 條用于控制流程的跳轉(zhuǎn)指令,其中 1 條是無條件跳轉(zhuǎn),另外 4 條都是有條件跳轉(zhuǎn)。

無條件跳轉(zhuǎn)指令:jmp <label>,無視原先的執(zhí)行順序,強(qiáng)制跳轉(zhuǎn)到標(biāo)記為 label 的代碼行處執(zhí)行。

零跳轉(zhuǎn)指令:jez <label> (Jump if Equal to Zero),當(dāng) acc 為零時,跳轉(zhuǎn)到標(biāo)記為 label 的代碼行處執(zhí)行,否則按順序繼續(xù)執(zhí)行。

正跳轉(zhuǎn)指令:jgz <label> (Jump if Greater than Zero),當(dāng) acc 大于零時,跳轉(zhuǎn)到標(biāo)記為 label 的代碼行處執(zhí)行,否則按順序繼續(xù)執(zhí)行。

負(fù)跳轉(zhuǎn)指令:jlz <label> (Jump if Less than Zero),當(dāng) acc 小于零時,跳轉(zhuǎn)到標(biāo)記為 label 的代碼行處執(zhí)行,否則按順序繼續(xù)執(zhí)行。

非零跳轉(zhuǎn)指令:jnz <label> (Jump if Non Zero),當(dāng) acc 不為零時,跳轉(zhuǎn)到標(biāo)記為 label 的代碼行處執(zhí)行,否則按順序繼續(xù)執(zhí)行。

本關(guān)的輸出和輸入是否【大于 0】、【等于 0】、【小于 0】相關(guān),是練習(xí)各大跳轉(zhuǎn)指令的入門關(guān)卡。本關(guān)的代碼如下:

左邊的三個節(jié)點(diǎn)全部是用于傳話的(mov up down, mov up down, mov up right),一筆帶過。

最先收到 IN 口值的是位于 OUT.G 上方的節(jié)點(diǎn)。它從左邊收到數(shù)字后,把這個數(shù)字存入自己的 acc,為接下來的按條件跳轉(zhuǎn)做準(zhǔn)備(mov left acc)。存好后,我們要將這個值再傳一份給右邊的節(jié)點(diǎn),因?yàn)橛疫叺墓?jié)點(diǎn)也需要根據(jù)這個值向 OUT.E 口寫輸出值(mov acc right)。

接下來是重頭戲:我們按照題目規(guī)則,判定輸入的值是否大于 0,并根據(jù)判定結(jié)果跳轉(zhuǎn)到不同的代碼塊去執(zhí)行代碼。第 3 行代碼 jgz 6 的作用是:如果 acc 大于 0,則跳轉(zhuǎn)到第 6 行去執(zhí)行。這就導(dǎo)致了一個現(xiàn)象:如果 acc 大于 0,按照要求要跳過第 4、5 行代碼,所以第 4、5 行代碼是僅當(dāng) acc <= 0 時才會執(zhí)行到的。當(dāng) acc <= 0 時,我們需要給 OUT.G 口輸出 0 信號(mov 0 down)。與此同時,第 6 行代碼是僅當(dāng) acc > 0 時才能執(zhí)行的,所以當(dāng) acc <= 0 時,第 4 行代碼執(zhí)行完畢后,我們還需要強(qiáng)制跳回第 1 行,避免執(zhí)行第 6 行的代碼(jmp 1)。接下來第 6 行,acc > 0 時,給 OUT.G 口輸出 1 信號(mov 1 down)。第 6 行代碼執(zhí)行完畢后,根據(jù)游戲設(shè)定,會自動跳回第 1 行,所以到這里就不需要再寫一行多余的 jmp 1 了。

我們現(xiàn)在畫一個流程圖來解釋 OUT.G 上方的節(jié)點(diǎn)究竟在做什么:

然后看右邊的位于 OUT.E 上方的節(jié)點(diǎn)。該節(jié)點(diǎn)從左邊接收傳來的輸入(mov left acc),并將該輸入量再發(fā)送一份給右邊的節(jié)點(diǎn),讓它處理 OUT.L 口的輸出(mov acc right)。該節(jié)點(diǎn)用了非零跳轉(zhuǎn)(jnz 6),導(dǎo)致的結(jié)果就是:acc = 0 時,執(zhí)行第 4、5 行代碼,向 OUT.E 輸出 1(mov 1 down, jmp 1);acc ≠ 0 時,執(zhí)行第 6 行代碼,向 OUT.E 輸出 0(mov 0 down)。

最后是右下角的位于 OUT.L 上方的節(jié)點(diǎn)。該節(jié)點(diǎn)同樣從左邊接收傳來的輸入(mov left acc),但由于它已經(jīng)位于右邊緣,它的右側(cè)已經(jīng)沒有更多節(jié)點(diǎn)等待它傳話了,所以這個節(jié)點(diǎn)里的 mov acc right 這條指令被注釋掉了。該節(jié)點(diǎn)用了負(fù)跳轉(zhuǎn),導(dǎo)致的結(jié)果就是:acc >= 0 時,執(zhí)行第 4、5 行代碼,向 OUT.L 輸出 0(mov 0 down, jmp 1);acc < 0 時,執(zhí)行第 6 行代碼,向 OUT.L 輸出 1(mov 1 down)。

匯編里的“按條件跳轉(zhuǎn)”本質(zhì)上和高級語言的 if...else... 是一回事。C 語言的

等價于這樣的匯編代碼:

匯編在語序上沒有高級語言的 if...else... 直觀,因?yàn)槭菨M足條件后跳轉(zhuǎn)執(zhí)行,所以先寫的其實(shí)是不滿足條件時的代碼塊,也就是 else 部分在前,then 部分在后。

那么,我們現(xiàn)在嘗試將 OUT.G 上方的匯編代碼轉(zhuǎn)寫成 C 語言代碼。該節(jié)點(diǎn)的匯編代碼如下:

轉(zhuǎn)寫成的 C 語言代碼如下:

語義很明顯:acc > 0 時給下方輸出 1,否則給下方輸出 0。另外兩個節(jié)點(diǎn)大同小異,我就不再細(xì)說了。

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

不錯,三項(xiàng)指標(biāo)都在直方圖的最左端,所以本方案應(yīng)該是這道題的十全十美方案了。

第 5 關(guān)《信號疊加器》(Signal Multiplexer)關(guān)卡展示

本關(guān)要求從 IN.S 讀入 -1 時,輸出 IN.A 的值,舍棄掉 IN.B 的值;從 IN.S 讀入 1 時,輸出 IN.B 的值,舍棄掉 IN.A 的值;從 IN.S 讀入 0 時,輸出 IN.A + IN.B 的值。

這一關(guān)其實(shí)不算太難,就是將 IN.S 讀入 acc,然后判斷其是否小于/等于/大于 0,并根據(jù)判定結(jié)果選擇三個代碼塊中的一個執(zhí)行。代碼如下:

IN.A 下方的節(jié)點(diǎn),以及 IN.B 下方的節(jié)點(diǎn),做的事情都是把讀到的值匯總到中央節(jié)點(diǎn),取舍均由中央節(jié)點(diǎn)自行決定(mov up right, mov up left)。

IN.S 下方的節(jié)點(diǎn)(中央節(jié)點(diǎn))按照計劃,從 IN.S 讀入一個數(shù)字(mov up acc),并根據(jù)它的符號決定接下來要執(zhí)行的代碼塊:若為正數(shù),則跳轉(zhuǎn)到第 11 行執(zhí)行(jgz b);若為 0,則跳轉(zhuǎn)到第 7?行執(zhí)行(jez 7);若為負(fù)數(shù),則按順序繼續(xù)往下執(zhí)行。那么我們很自然地就把這些代碼分成了三部分:當(dāng) acc < 0 時,執(zhí)行第 4~6 行代碼;當(dāng) acc = 0 時,執(zhí)行第 7~10 行代碼;當(dāng) acc > 0 時,執(zhí)行第 11~12 行代碼。

那么我們先看第 4~6 行代碼:當(dāng) acc < 0 時,根據(jù)題目要求,要輸出 IN.A 的值,舍棄掉 IN.B 的值。那么我們在代碼里,自然就是從左邊節(jié)點(diǎn)拿到 IN.A 后往下傳(mov left down),從右邊節(jié)點(diǎn)拿到 IN.B 后自己吞掉,不傳下去(mov right acc, jmp 1)。

接下來是第 7~10 行代碼:當(dāng) acc = 0 時,根據(jù)題目要求,要輸出 IN.A + IN.B 的值。那我們在代碼里,自然是先從左邊節(jié)點(diǎn)拿到 IN.A 的值放到 acc 里(mov left acc),然后從右邊節(jié)點(diǎn)拿到 IN.B 的值累加到 acc 里(add right),計算完畢后,將算好的值往下傳(mov acc down, jmp 1)。

最后是第 11~12 行代碼:當(dāng) acc > 0 時,根據(jù)題目要求,要輸出 IN.B 的值,舍棄掉 IN.A 的值。此時我們的行為跟第一部分的代碼正好相反,從右邊節(jié)點(diǎn)拿到 IN.B 后往下傳(mov right down),而從左邊節(jié)點(diǎn)拿到 IN.A 后自己吞掉(mov left acc)。

將以上過程畫成流程圖,如下:

以上匯編代碼轉(zhuǎn)換成 C 語言代碼后,是這個樣子的:

有讀者可能要問了:為什么用不到的值非得讀一下然后再自己吞掉,不能不讀它嗎?這就不得不提一下這個游戲里的設(shè)定了:任何時候,當(dāng)一個節(jié)點(diǎn)使用 mov 指令給相鄰節(jié)點(diǎn)傳值時,對應(yīng)的相鄰節(jié)點(diǎn)必須要接收這個值。若不接收,傳值的節(jié)點(diǎn)就會一直阻塞下去,直到對應(yīng)的節(jié)點(diǎn)接收掉相應(yīng)的值為止。也就是說,傳話的節(jié)點(diǎn)自己是不知道該不該丟棄掉這個值的。如果中央節(jié)點(diǎn)不去主動讀那個需要丟棄的值,傳話的節(jié)點(diǎn)就會一直等待下去,下次中央節(jié)點(diǎn)再來讀的時候,讀到的反而是前若干個回合里早就該丟棄掉的廢值,這樣我們反而會得到錯誤的結(jié)果。所以,即使對于不需要的值,接收方也是需要去讀一下然后丟棄掉的,這樣可以釋放掉傳話方的阻塞信號,以便傳話方繼續(xù)準(zhǔn)備下一次要傳遞的內(nèi)容。

中央節(jié)點(diǎn)下方的兩個節(jié)點(diǎn)也沒啥好說的,就是給最終輸出口 OUT 傳話的(mov up down, mov up down)。

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

解鎖成就 UNCONDITIONAL

該成就的說明是 Solve SIGNAL COMPARATOR without using the JGZ, JLZ, JEZ, or JNZ instructions,不使用四大有條件跳轉(zhuǎn)指令完成第 4 關(guān)。

一開始看到這個成就的時候,你可能覺得這是無解的。但是如果你仔細(xì)閱讀了手冊,你會發(fā)現(xiàn)手冊上介紹了一個魔法般的跳轉(zhuǎn)指令:jro 指令。

jro 即 Jump Relative Offset,按相對偏移量跳轉(zhuǎn)。它的用法是:jro <src>,從數(shù)據(jù)源處讀入一個值,并跳轉(zhuǎn)到當(dāng)前行后的這么多行處執(zhí)行。如果 src 為負(fù)數(shù),則改為往前跳轉(zhuǎn)?|src|?行。如果 src 為 0,則本次會停留在這一行,直到從數(shù)據(jù)源處讀入的數(shù)不為 0 為止。

手冊里給 jro 指令的用法舉了這么幾個例子:

前三種用法因?yàn)樘D(zhuǎn)到的位置是固定的,所以本質(zhì)上和 jmp 指令沒什么兩樣。jro 指令真正厲害的地方在于第四種用法:jro acc,根據(jù) acc 里的值動態(tài)決定要往哪跳。以及由此衍生出的 jro up/down/left/right,由鄰居節(jié)點(diǎn)來控制你往哪跳!這是無條件跳轉(zhuǎn)的 jmp 指令,以及之前介紹的四大條件跳轉(zhuǎn)指令做不到的點(diǎn)。

介紹完了 jro 指令后,我們回到題目。

這題的輸入只有 -2、-1、0、1、2 這五種,每種輸入都對應(yīng)著唯一一種輸出。因此我們可以將節(jié)點(diǎn)里的代碼分成五部分,并對輸入量做一定的處理,使之變成 jro 指令里的偏移量,將程序引導(dǎo)到相應(yīng)的代碼塊處執(zhí)行。由于 jro 0 會將程序阻塞在當(dāng)前行不動,所以我們需要將原始的輸入量 +3,使其變成 1、2、3、4、5 后,再使用 jro acc 做相對跳轉(zhuǎn)。程序代碼如下:

左上方和左側(cè)居中的節(jié)點(diǎn)跟之前一樣純傳話,但是左下角的節(jié)點(diǎn)有了改變,將輸入量 +3 后再傳給右邊(mov 3 acc, add up, mov acc right)。這樣 -2、-1、0、1、2 的輸入量就映射成了 1、2、3、4、5。

現(xiàn)在我們來看 OUT.G 上方的節(jié)點(diǎn)有什么變化。題目要求 IN 為正數(shù)時,OUT.G 輸出 1,否則輸出 0。由于輸入量固定在 -2 ~ +2 范圍內(nèi),所以題目事實(shí)上變成了:當(dāng) IN 為 -2、-1 或 0 時,OUT.G 輸出 0;當(dāng) IN 為 1、2 時,OUT.G 輸出 1。我們從左側(cè)節(jié)點(diǎn)收到并存入 acc 的是 IN + 3 的值,所以我們需要做的事就是:當(dāng) acc 分別為 1、2、3、4、5 時,分別對 acc 做一定的運(yùn)算,將其變?yōu)?0、0、0、1、1 并輸出。

首先我們從左側(cè)節(jié)點(diǎn)接收 IN + 3 的值存入 acc(mov left acc),同時復(fù)制一份發(fā)給右邊的節(jié)點(diǎn)(mov acc right)。接下來就是關(guān)鍵的 jro 指令。由于 acc 只可能是 1~5 中的一個,所以 jro acc 的含義是:

  • acc 為 1(IN 為 -2)時,向下移動 1 行執(zhí)行;

  • acc 為 2(IN 為 -1)時,向下移動 2 行執(zhí)行;

  • acc 為 3(IN 為 0)時,向下移動?3 行執(zhí)行;

  • acc 為 4(IN 為 1)時,向下移動 4 行執(zhí)行;

  • acc 為 5(IN 為 2)時,向下移動 5?行執(zhí)行。

我們依次驗(yàn)證:

  • 當(dāng) acc 為 5 時,向下移動 5 行,依次執(zhí)行 sub 4, mov acc down。由于 5 - 4 = 1,所以輸出的值是 1。

  • 當(dāng) acc 為 4 時,向下移動 4 行,依次執(zhí)行 add 1, sub 4, mov acc down。由于 4 + 1 - 4 = 1,所以輸出的值是 1。

  • 當(dāng) acc 為 3 時,向下移動 3 行,依次執(zhí)行 nop, add 1, sub 4, mov acc down。由于 3 + 1 - 4 = 0,所以輸出的值是 0。

  • 當(dāng) acc 為 2 時,向下移動 2 行,依次執(zhí)行 add 1, nop, add 1, sub 4, mov acc down。由于 2 + 1 + 1 - 4 = 0,所以輸出的值是 0。

  • 當(dāng) acc 為 1 時,向下移動 1 行,依次執(zhí)行 add 1, add 1, nop, add 1, sub 4, mov acc down。由于 1 + 1 + 1 + 1 - 4 = 0,所以輸出的值是 0。

以上我們接觸到了一條新指令:空操作指令 nop。這條指令本身除了占用一個機(jī)器周期,以及占用一行寶貴的代碼空間外,不產(chǎn)生任何效果。那么我們?yōu)槭裁催€需要寫這么一條空操作指令呢?因?yàn)椋?strong>以上代碼里,由于 acc 為 3 和 4 時都只需要執(zhí)行 +1 和 -4 兩條運(yùn)算,但 acc 為 3 和 4 時,jro 的跳轉(zhuǎn)點(diǎn)又不同。所以向下跳轉(zhuǎn) 3 行時,我們只能在這個地方加一條 nop 空操作指令占位,讓兩個跳轉(zhuǎn)點(diǎn)事實(shí)上執(zhí)行的操作完全一致。它可以被等效替換成 add 0, sub 0, jro 1 等指令,畢竟將 acc 加上 0 或減去 0,或者只是正常跳轉(zhuǎn)到下一行去執(zhí)行,事實(shí)上也等效于本行代碼沒有執(zhí)行任何操作。

現(xiàn)在我們看 OUT.E 上方的節(jié)點(diǎn)。它會從左邊的節(jié)點(diǎn)收到一份?IN + 3(mov left acc),同樣復(fù)制一份傳給右邊的節(jié)點(diǎn)(mov acc right)。OUT.E 要求 IN 為 0 時輸出 1,IN 為 -2、-1、1、2 時輸出 0。這相當(dāng)于 acc 為 3 時輸出 1,為 1、2、4、5 時輸出 0。那我們看看本節(jié)點(diǎn)執(zhí)行 jro acc 后的效果:

  • 當(dāng) acc 為 5 時,向下移動 5 行,依次執(zhí)行 sub 5, mov acc down。由于 5?- 5 =?0,所以輸出的值是 0。

  • 當(dāng) acc 為 4 時,向下移動 4 行,依次執(zhí)行 add 1, sub 5, mov acc down。由于 4 + 1 - 5?= 0,所以輸出的值是 0。

  • 當(dāng) acc 為 3 時,向下移動 3 行,依次執(zhí)行 add 2, add 1, sub 5, mov acc down。由于 3 + 2 + 1 -?5 = 1,所以輸出的值是 1。

  • 當(dāng)?acc 為 2 時,向下移動 2 行,依次執(zhí)行 nop, add 2,?add 1, sub 5, mov acc down。由于 2 + 2 + 1 - 5?= 0,所以輸出的值是 0。

  • 當(dāng) acc 為 1 時,向下移動 1 行,依次執(zhí)行 add 1, nop, add 2, add 1, sub 5, mov acc down。由于 1 + 1 + 2 + 1 -?5?= 0,所以輸出的值是 0。

最后我們來看 OUT.L 上方的節(jié)點(diǎn)。它會從左邊的節(jié)點(diǎn)收到一份?IN + 3(mov left acc),但是它右邊沒有節(jié)點(diǎn)了,所以不需要再傳給右邊了,我在原先第二行的代碼 mov acc right?前加上了 # 號,讓它變成了注釋。OUT.L 要求 IN 為 -2、-1 時輸出 1,IN 為 0、1、2 時輸出 0。這相當(dāng)于 acc 為 1、2 時輸出 1,為 3、4、5 時輸出 0。那我們看看本節(jié)點(diǎn)執(zhí)行 jro acc 后的效果:

  • 當(dāng) acc 為 5 時,向下移動 5 行,依次執(zhí)行 sub 5, mov acc down。由于 5?- 5 =?0,所以輸出的值是 0。

  • 當(dāng) acc 為 4 時,向下移動 4 行,依次執(zhí)行 add 1, sub 5, mov acc down。由于 4 + 1 - 5?= 0,所以輸出的值是 0。

  • 當(dāng) acc 為 3 時,向下移動 3 行,依次執(zhí)行 add 1, add 1, sub 5, mov acc down。由于 3 + 1 + 1 -?5 =?0,所以輸出的值是 0。

  • 當(dāng)?acc 為 2 時,向下移動 2 行,依次執(zhí)行 add 2,?add 1, add 1, sub 5, mov acc down。由于 2 + 2 + 1 + 1 - 5?=?1,所以輸出的值是 1。

  • 當(dāng) acc 為 1 時,向下移動 1 行,依次執(zhí)行 add 1, add 2, add 1, add 1, sub 5, mov acc down。由于 1 + 1 + 2 + 1 + 1 -?5?= 1,所以輸出的值是 1。

點(diǎn)擊左下角的【RUN】,稍等片刻,彈出結(jié)算界面后,便可解鎖 UNCONDITIONAL 成就。

當(dāng)然,你目前只接觸到了 jro 指令的這種初步的用法。后續(xù)的關(guān)卡里,節(jié)點(diǎn)間的分工合作會越來越復(fù)雜,有部分關(guān)卡離開了 jro 指令就直接無解。到了相應(yīng)的關(guān)卡里時,我會詳細(xì)解說 jro 指令的高級用法。

【TIS-100 攻略】第 4~5 關(guān):信號比較器、信號疊加器的評論 (共 條)

分享到微博請遵守國家法律
三明市| 龙井市| 礼泉县| 道孚县| 五寨县| 安新县| 武安市| 河北区| 沛县| 利川市| 永顺县| 沙湾县| 百色市| 富阳市| 尉氏县| 开江县| 古浪县| 尼勒克县| 瓦房店市| 聊城市| 黄梅县| 新和县| 阿坝| 宣武区| 铜陵市| 霍山县| 永安市| 贵港市| 江陵县| 曲阜市| 平潭县| 德化县| 滨海县| 乌拉特后旗| 武冈市| 惠东县| 泸州市| 巢湖市| 濉溪县| 七台河市| 宜兰县|