Skip to main content

25 posts tagged with "科技"

View All Tags

一次奇怪的 Ente Photo 自架 BUG

· 4 min read

Ente Photo 已經成為我的主力相簿軟體兩個月了,用起來比什麼 Immich 或是 PhotoPrism 什麼的都要好,但就在昨天的時候因為我在優化我寫的 deployment 檔,順便把一些 pod 轉成 deployment,結果轉完之後我就發現到為什麼 Ente 開不起來了?

我的 pod 一直卡在 ContainerCreating,通常第一個想到的就是用 kubectl describe pod 這條指令來看一下,結果我看到就是一切正常,成功的把 image 給 pull 下來,而且也沒有寫什麼問題。

到這了這裡我就去問了一下 Google 大神和 GenAI,兩邊給我的答案都差不多,像是什麼 PVC 掛載失敗、網路無法連接等等,但老實說這些我一看到就直接跳過了,因為我知道不可能是這些。我之前也有發生過這種錯誤,通常在第一步的時候用指令一看就會很明顯的看到了。

最後我想到的一個方法就是我把 deployment 裡面的 containers 一個一個拿掉,看看到底是哪裡卡住,這個方法也確實生效了。最後我發現到 minio 壞掉,好消息是發現了什麼壞掉,壞消息是我不知道怎麼處理。我進一步研究,發現是 minio 的 lifecycle 有問題。

lifecycle:
postStart:
exec:
command:
- sh
- -c
- |
#!/bin/sh
while ! mc config host add h0 http://localhost:3200 $(MINIO_ROOT_USER) $(MINIO_ROOT_PASSWORD) 2>/dev/null
do
echo "Waiting for minio..."
sleep 0.5
done
cd /data
mc mb -p b2-eu-cen

但到了這裡還不夠,我再次使用相同的方法,就是一點一點的刪,看哪裡可行就知道哪裡有問題,最後找到這段:

while ! mc config host add h0 http://localhost:3200 $(MINIO_ROOT_USER) $(MINIO_ROOT_PASSWORD) 2>/dev/null

等等,為什麼一個迴圈會突然壞掉?這個問題讓我想了很久,因為 Ente 也用了兩個月了,而且更氣人的是我發現到只要使用 systemctl restart k3s.service 把 k3s 整個重啟,一切就像什麼事都沒發生一樣,所有的 log 一切都正常。

接下來我就仔細的想了一下,我想到會不會是 minio 有更新?因為我在一次又一次的檢查我的 deployment.yml 檔的時候,我發現到我的 minio 沒有固定版本。如果有固定版本會像下面這樣:

有固定版本的 minio 範例

可以發現到有一段 RELEASE...30Z,那個就是固定版本,而之前是沒有的,因此我就去 docker hub 看看,果然是昨天有新的版本。

新版本的 docker hub image

因為預設 k8s 會自動發現有沒有新版的,如果有就把目前的版本更新上去,結果一更就壞掉了。

因為此我就把版本換回到一個月前的,之後就好了。

舊版本的 docker hub image

這樣的問題我居然卡了快十個小時,沒想到原因這麼簡單,算是學到了一課。

大戰系統程式 - 控制段組譯與連結載入

· 9 min read

前面我們講了基本的組譯流程,而這篇主要要講的是怎麼樣合併兩個程式,因為通常我們寫完的程式碼不會只有一份檔案,而是會有數份。

這裡的細節爆炸多,所以一定要仔細看。第一步的組譯過程和之前一樣,我會把不一樣的地方寫出來,其它相同的地方不會再重複。

下方的內容如果有錯誤,歡迎來信更正!

組譯

下方的題目取自席家年教授的練習題,如有侵權請來信告知!

題目 PDF 下載

下方為練習卷 練習卷

可以發現到有兩份程式碼需要組譯,而且兩份程式碼裡面都出現了之前沒有出現的字。第一個步驟就是先把位址寫一寫,這裡位址和之前一樣。

第一份程式:

地址標記符號指令運算元
0000TSTPGMSTART0
EXTDEFREF4
EXTREFNXTREF
...
0029REF1LDX#ENDA-TSTA
002CREF2LDATSTA,X
002FREF3+LDSNXTREF-24
0033REF4EQU*
...
0050TSTAEDU*
...
0084ENDAEDU*
0084REF5WORDTSTA+8
0087RESB12
0093ENDREF1

上面這張表格跟之前的其實沒有什麼差別,但可以發現到有兩個比較特別的是 EXTDEF 和 EXTREF,這兩個就是外部引用的程式。

還有一個東西是 EDU,後面的運算元是一個 *,而這個我們就當成保留,不佔用空間。

第二份程式碼發現到地址幫我們上好,但我一樣寫在下面。

地址標記符號指令運算元
0000NXTPGMSTART0
EXTDEFNXTREF
...
0052NXTREFLDANXTREF+20
0055NXT1LDB#NXTREF
...
0090NXT2WORDNXT2-6
0093RESW5
00A2END

接下來就是把目地的碼算出來,過程跟之前都一樣。

第一份程式碼:

地址標記符號指令運算元目地碼
0000TSTPGMSTART0
EXTDEFREF4
EXTREFNXTREF
...
0029REF1LDX#ENDA-TSTA050034
002CREF2LDATSTA,X03A021
002FREF3+LDSNXTREF-246F1FFFF8
0033REF4EQU*
...
0050TSTAEDU*
...
0084ENDAEDU*
0084REF5WORDTSTA+8000058
0087RESB12
0093ENDREF1

第二份程式碼:

地址標記符號指令運算元目地碼
0000NXTPGMSTART0
EXTDEFNXTREF
...
0052NXTREFLDANXTREF+20032011
0055NXT1LDB#NXTREF692FFA
...
0090NXT2WORDNXT2-600008A
0093RESW5
00A2END

目的程式

跟之前一樣會有兩份目的程式,但這裡跟之前不太一樣,差別在於有 EXTREF 和 EXTDEF,這兩個都會生成比較特別的紀錄。

第一份

先從第一份程式碼開始,一樣的先從表頭紀錄開始

H,TSTPGM,000000,000093

接下來我們會有兩種新的紀錄,分別是 D 紀錄和 R 紀錄,D 紀錄在代表的是自己給別人用的程式,而 R 紀錄正好相反,代表的是自己使用別人的程式。

先寫 D 紀錄,要依序寫入的資訊如下:

  • 開頭字母 D
  • 六格的標紀符號,對應到 EXTDEF 後面所寫的標紀錄號
  • 六格的標紀符號所在位址

D,REF4☐☐,000033

再來寫 R 紀錄,和 D 紀錄相同,要依序寫入的資訊如下:

  • 開頭字母 R
  • 六格的標紀符號,對應到 EXTREF 後面所寫的標紀符號

R,NXTREF

接下來的本文紀錄和之前相同,下面不再重複。

T,000029,0A,050034,03A021,6F1FFFE8

T,000084,03,000058

再來是修正紀錄基本上和之前一樣,但是有一個紀錄不太一樣。

注意到標紀位址在 REF3 那一行,可以發現到計算的時候使用到了 NXTREF 這個標紀符號,仔細看就會發現到這個使用到了 EXTREF 所標紀的符號,所以修正紀錄也要基於 NXTREF,而不是 START 的 TSTPGM,換句話說就是這裡是基於外部的程式。

M,000030,05,+NXTREF

還有一行需要修正的是 REF5,這行的話沒有什麼特別的,修正紀錄如下:

M,000084,06,+TSTPGM

最後的結束紀錄和之前一樣,沒什麼特別的

E,000029

第二份

表頭紀錄如下:

H,NXTPGM,000000,0000A2

D 紀錄如下:

D,NXTREF,000052

本文紀錄如下:

T,000052,06,032011,692FFA

T,000090,03,00008A

修正紀錄一個,如下:

M,000090,06,+NXTPGM

結束紀錄就一個 E

連結載入

接下來就是要模擬在電腦中實際載入兩份程式碼之後會發生的事,假設載入的起始位址的 5000。

而外部符號表要處理的地方比較多,控制段其實就是程式的外稱,像 TSTPGM 指的就是第一份程式碼。符號名稱就是 EXTDEF 後面寫的標紀符號,位址的話就是 START 的位址跟 END 的位址,長度就是兩者相減。

第二份程式碼跟第一份的步驟相同,但是位址要接在第一份之後。

控制段符號名稱位址長度
TSTPGMREF45000~509393
NXTPGMNXTREF5093~50E5A8

實際上執行的時候修正紀錄就會發揮作用,下面舉幾個例子,也就是練習卷的題目。

REF3 +LDS NXTREF-24 載入到記憶體的位址為 502F,修正後的目的碼為 6F1050CD

目的碼的算法就是看修正紀錄怎麼寫就怎麼做,像這題的修正紀錄是 M,000030,05,+NXTREF,所以我們要做的就是把 1FFFF8 拿出來加上 NXTREF,而 NXTREF 的值為 50E5,可以對應到剛剛的外部符號表。

REF5 WORD TST+8 載入到記憶體的位址為 5084,修正後的目的碼為 005058

NXT1 LDB #NXTREF 載入到記憶體的位址為 50E8

而最後,程式開始的地方就是第一條本文記錄開始的地方,需要注意一下要再加上初始地址,得到 5029

網頁版應用程式

· 2 min read

昨天因為我在玩 Hail 的時候不小心把手機玩到壞掉,所以我就直接重置了整個系統,重置完之後我就在想有哪些應用程式要再下載的。

這個時候我就又突然想到這篇,大意就是 Meta 的應用程式會使用一些特殊的手段來追蹤在網頁上的瀏覽紀錄等,而防止的方法就是有其中一個就是使用網頁版應用程式。

網頁版應用程式聽起來好像很高級,但其實就是在手機上直接去開 Facebook 這個網頁,那這樣就是一個網頁版應用程式。我發現到這種方法其實好處不少欸,因為之前我從抓包就發現到 FB 會一直偷傳封包還有 IG 也是,這樣一來只要網頁關掉就完全沒有問題了。

功能上面也不再像是五年前會發現有些東西不能用的問題,一切都是那麼的正常,唯一的缺點是開的時候要等一下,因為它就不再像是 APP 一樣一直接常駐在後台,而是要用的時候再跟伺服器握手拿資料,我自己是比較喜歡這樣啦,光想到我的資料一直被傳出去就覺得很不安。

而且像我自己在用的 Firefox 還會做出這種美美的圖標,不管是使用上或者美觀上都很有 APP 的感覺。

破爛 Google SEO

· 4 min read

這個 blog 也架了快要一個月了,提供我一個可以開心的寫文章發文的地方,我自己是非常滿意我架的兩個 blog。一個可以用來放心情的長文一個放短文或者教學文,非常的剛好。

但有一件事我這個月一直在處理,直到前天我才發現到原來我是個小丑。

Google SEO

這是個你可能不知道,但你一定有用過的東西,平常在 Google 搜尋的時候會出現的內容,像我們輸入「蘋果」會出現一堆跟蘋果有關的紀錄,而 SEO 簡單來說就是要把網站掛到上面去,才能被搜尋到。

但這東西也是非常的破爛,我以前有一個使用 GitHub Page 架的網站有被掛上去,到現在都還能查到,因為該死的 Google 死就是不更新到我新的域名。

github page 搜尋紀錄

Sitemap

如果一個網站有多個頁面,像我的 blog 就有非常多的文章,但這樣總不可能一頁一頁的掛到 Google 上,所以就有了 Sitemap 這種東西,可以只掛一個 XML 檔,而之後 Google 的機器人就會自己去裡面找文章,聽起來十分的美好,我一開始也是這樣想的。

直到我發現到 Google 根本就不會去看,我不知道是不是我的人緣不好,直到寫這篇文的時候,還是沒有去爬我的 Sitemap。

couldn't fetch 的 sitemap

如果你有注意到為什麼有一個名為「sitemap_new.xml」的東西,這是前幾天我想要實驗到底是我的網站有問題,還是 Google 根本沒有去爬我的 Sitemap,結果我等等公開。

如果 Sitemap 有被正確的爬到,會像是 Bing 的後台這樣,不得不說 Bing 處理非常的快,而 DuckDuckGo 也同步上架了,非常開心。

bing 後台 sitemap 截圖

Cloudflare Workers 確定了這一切

我之前就一直很懷疑 Google 的機器人根本不會去看我的網頁,而一直顯示的「Couldn't fetch」只是個騙局,所以我就特別用 Cloudflare Workers 架了一個 sitemap_new.xml 來看看到底有沒有被爬到。

結果如下: cloudflare workers 後台

答案就是完全沒有,裡面的兩個紀錄是我用 curl 指令測試的時候留下的,其它完全沒有任何的紀錄,所以從頭到尾 Google 的機器人就沒來過,顯示個東西在那邊騙人。

我一直以為是內容或者是 robots.txt 之類的東西擋到,結果根本就不是,因為我已經把所有能修的東西都用好了,不管是 robots.txt 或是 Cloudflare 的設定,全部都調到完美的狀態,但是機器人就是因為未知的原因不來。

小結

後來想想也就算了,因為我的 blog 不管是在 Instagram 或是 Discord 都有超連結可以連過來,好像也沒那麼大的影響就是了。但主要是教學文會不好被找到,這個比較可惜一點。

好好整理音樂

· 3 min read

最近我發現到我聽的音樂開始多了起來,主要是因為常常會有人推薦或是我剛好聽到真的很棒的,我就會把整張專輯聽起來,所以音樂就多到我快無法管理了。

各種自架服務

我第一個想到的一定是有什麼可以自己架起來,像是 Spotify 那樣的軟體,但是很顯然的我沒找到。不管是哪一款用起來就是都不滿意,而且用起來不順手或是手機端 UI/UX 很爛,所以我就直接放棄了這條路。

但音樂這種東西不管理是真的不行,我最常聽音樂的地方是 Youtube,但我覺得我有必要保留一份本地的檔案,我會這樣說是因為之前我有一首很喜歡的歌在 Spotify 上直接沒辦法播,前一天還好好的過一天就直死了,我不把音樂收藏起來哪天就不見了。

夢回兩千年初

我想到我小時候用電腦的時候常常會用音樂播放器,就有什麼專輯、作者等等的分類方式,所以我就想到不如找回來看看吧!我問了一下 GenAI,我就得到了目前電腦端最好用的軟體 Clementine,使用上非常的直覺,但這裡我沒有要教怎麼用,大概自己摸個幾分鐘就會用了。

clementine 截圖

手機端目前我使用的是 Auxio,整個畫面非常的簡單但是又功能又都沒有少,但目前我沒辦法確定會不會一直用這款,因為這個不像是電腦端的功能那麼全面,就像歌詞目前還沒有辦法顯示。

這裡應該會有人想知道我怎麼同步的,其實方法就是使用 Syncthing,我還有筆電也是用這個軟體,非常的穩定好用。

我現在才想到那麼多服務沒想到最好用的早在我小時候就準備好了,只是那個時候我不聽音樂所以不知道而已,這一切都那麼的完美,而且那種音樂就在自己手裡的感覺真的很好,不再有被消失的問題。

大戰系統程式 - SIC/XE 指令分析

· 10 min read

系統程式這門課應該是所有資工系必修的課,這門課連我自己都覺得有點硬,我會這麼說是因為網路上的資料真的不多,而且連 GenAI 也常常給出錯的答案。因此我決定寫個筆記,給我自己也給未來的大二生看。

如果你不知道這門課在學什麼,簡單來說就是跟組合語言有關的東西,像編譯、組譯、連結等等,而這些都有非常多的細節,理解後非常有趣。

下方的內容如果有錯誤,歡迎來信更正!

基礎指令分析

下方的題目取自席家年教授的練習題,如有侵權請來信告知!

題目 PDF 下載

針對每條指令我們要處理的問題如下:

  • 助憶碼
  • 指令格式
  • Target Address
  • 被改變的目標
  • 內容值

分析最重要的就是這張資料表,放在這方便對照:

分析所需的資料表

9080

第一題我們來分析最簡單的指令,雖然說是最簡單的但是如果其它題目熟悉之後,反而常常會忘掉這種指令要怎麼解。

先拆成二進位,我們可以從得到 1001 0000 1000 0000。我們馬上可以發現到長度是 16 位元,再看到上方提供的資料可以知道是格式二。

按照我提供的資料表可以發現到前六碼是運算碼,接著四碼是 r1 再接著四碼是 r2。

前六碼為 1001 00,需要注意如果只有六個位元,則我們要補到八個位元,補完後為 1001 0000,可以得到十六進位的 90。然後再看到 SIC/XE 部分指令那邊,使用 opcode 去對,就可以得到 ADDR r1, r2 這個助憶碼。

而 r1 和 r2 分別是多少呢?把 r1 換成十進位得到 8,r2 得到 0,有了這兩個之後再去剛剛的部分指令表下方的暫存器區,使用編號去對應就可以知道 r1 是 PC,而 r2 是 A。

現在我們可以來回答問題了:

  • 助憶碼就是上方的 ADDR r1, r2
  • 指令格式是 2
  • 被改變的目標可以在 SIC/XE 部分指令表的 Effect 發現是 r2,也就是 A
  • 內容值就是把 r1 跟 r2 加在一起,也就是 12345A + 003000,可以得到 12645A

032600

同樣的先換成二進位 0000 0011 0010 0110 0000 0000,得到長度是 24,而 24 對應到的格式有兩種,可能是 SIC 或格式三。

那我們要怎麼知道是哪一種?我們可以直接去看第七跟第八個位元,如果是 00 則是 SIC 指令,而這裡是 11,因此確定是格式三。

確定了之後就看著表格,取出前六位分析,得到十六進位的 00,助憶碼就是 LDA m

這題可以發現到在運算碼之後還有一個六位元的 nixbpe,而這裡就是最麻煩的地方,在看的時候要分成 ni 和 xbp 去看,而 e 我們先不管它,這個只有在組譯的時候會用到,這篇用不到。

ni 有三種可能,分別如下:

  • 10 indirect 間接定址
  • 11 direct 直接定址
  • 01 immediate 立即定址

這裡是 11,可以知道是直接定址。

xbp 和我們最後計算出來的 Target Address(TA)有關,算法如下

  • 假設 x 為 1,則我們就先把 X 暫存器的值記起來
  • b 和 x 做法相同
  • p 和 x 的做法也相同,但這裡的 P 指的是 PC 暫存器

現在我們先把 PC 的值計起來,得到 003000

接著還有一位 e,那個直接不理它跳過就好。

現在和上題一樣,把剩下的 12 位元轉換成十六進位得到 600,再把 600003000 加起來就是 TA。

而我們還有一個內容值要看,這裡和定址模式有關:

  • 間接定址代表我們要在記憶體 dereference 兩次,換句話說就是我們要先用位址去找到值,再拿值當位址找一次。
  • 直接定址代表我們直接去位址找值
  • 立即定址代表算出來的 TA 就是值,不用找

現在我們可以來回答問題了:

  • 助憶碼就是上方的 LDA m
  • 指令格式是 3
  • TA 為 003600
  • 定址模式為直接定址
  • 被改變的目標是 A,可以參考 SIC/XE 部分指令表
  • 內容值可以在 003600 這格記憶體找到,答案為 103000

03C300

接下來的題目除非有比較特別的地方,不然都跟前面那題的解法相同。

這題和上題比較不同的是 xbp 的地方,可以發現到是 110,因此我們可以知道要把 x 和 b 的值加起來,之後再拿後 12 位換成十六進位之後加上去,TA 可以得到 6390

答案:

  • 助憶碼 LDA m
  • 指令格式是 3
  • TA 為 6390
  • 定址模式為直接定址
  • 被改變的目標是 A
  • 內容值可以在 6390 這格記憶體找到,答案為 C303

022030

ni 可以發現到是 10,可以發現到是間接定址。

內容值要拿兩次,TA 在計算過後可以得到 3030,先看一次 3030 的值為 003600,之後再對位址看一次得到內容值 103000

答案:

  • 助憶碼 LDA m
  • 指令格式是 3
  • TA 為 3030
  • 定址模式為間接定址
  • 被改變的目標是 A
  • 內容值為 103000

010030

ni 可以發現到是 01,發現到是立即定址。

內容值就是 TA,也就是 30

答案:

  • 助憶碼 LDA m
  • 指令格式是 3
  • TA 為 30
  • 定址模式為立即定址
  • 被改變的目標是 A
  • 內容值為 30

003600

這題可以發現到 ni 是 00,也就是說這題是一種 SIC 指令。

如果 x 為 1 則稱為索引定址,但這張練習卷沒有這種模式,處理方法就是 TA 要再加上 X 暫存器的值。而像這題的 X 為 0,就直接當成前面講過的直接定址即可。

答案:

  • 助憶碼 LDA m
  • 指令格式是 SIC
  • TA 為 3600
  • 定址模式為直接定址
  • 被改變的目標是 A
  • 內容值為 103000

0310C303

這題換成二進位之後發現到長度有 32 碼,是格式四的指令。

答案:

  • 助憶碼 LDA m
  • 指令格式是 4
  • TA 為 C303
  • 定址模式為直接定址
  • 被改變的目標是 A
  • 內容值為 003030

012FFE

這題可以發現到最後的 12 位是 1111 1111 1110,需要注意如果開頭是 1,像這題的情況就需要做負數處理。

做法是先把這個數字做一次二的補數,之後再把數字加上負號,其於的步驟都相同,只是最後的 12 位位址是負的。

這題的 TA 計算為 3000 + (-2),得到 2FFE

答案:

  • 助憶碼 LDA m
  • 指令格式是 3
  • TA 為 2FFE
  • 定址模式為立即定址
  • 被改變的目標是 A
  • 內容值為 2FFE

6BC300

這題其實如果有照我前面的指示應該不會有問題,但是最一開始的助憶碼要記得補兩個零,不然就會發現到找不到指令。

前六碼是 0110 10,如果你直接換成十六進位會發現找不到指令,因為要補零變成 0110 1000 得到 68,對應到的助憶碼為 LDB m

答案:

  • 助憶碼 LDB m
  • 指令格式是 3
  • TA 為 6390
  • 定址模式為直接
  • 被改變的目標是 B
  • 內容值為 C303

大戰系統程式 - SIC 及 SIC/XE 組譯

· 14 min read

繼上一篇指令分析,這篇要做的是組譯,組譯指的是我們寫完的組合語言變成機器語言的過程。

和之前的指令分析一樣,有非常多的細節要注意。這裡的練習分寫 SIC 和 SIC/XE,有些地方不同且 SIC/XE 還有多東西要處理。

下方的內容如果有錯誤,歡迎來信更正!

SIC 組譯

下方的題目取自席家年教授的練習題,如有侵權請來信告知!

題目 PDF 下載

下方為練習卷 練習卷

而我們要做的第一件事就是幫每條指令寫上位址,有些指令會發現沒有在 SIC 指令表內,而這些不在表內的指令稱為組譯指示,也就是說是寫給組譯看的,並不會實際產生程式碼,但這不代表不會佔用位址空間。

位址標記符號指令運算元
2100EX2START2100
2100TSTLDAONE
2103STADATA
2106LDXONE
2109LDCHSTR,X
210CSTCHCX
210FRSUB
2112DATARESW1
2115ONEWORD1
2118STRBYTEC'01'
211ACXRESB1
ENDTST

上方這張表多了位址這個欄位,而欄位的計算重點如下:

  • 位址開始從 START 指令的運算元開始
  • START 後的第一條指令的位址和 START 相同,因為 START 不會佔用位址空間
  • 位址是使用 byte 做為計數,而 SIC 表內的每條指令都佔用 24 個 bit,也就是 3 個 byte,因此可以發現到表內指令的位址皆為 +3
  • 所有的運算元皆使用十進位,除了 START 之外
  • RESW 代表此處保留指定數量的 word,一個 word 代表三個 byte
  • WORD 代表此處要有一個長度為 word 的整數,運算元即為整數的值
  • RESB 代表此處保留指定數量的 byte
  • BYTE 的運算元 C'01' 的 01 代表的是字元,因此此處可以知道有兩個字元,也就是兩 byte

算出所有的位址之後就下以完成「標記符號位址表(SYMTAB)」這個表格

標記符號位址
EX22100
TST2100
DATA2112
ONE2115
STR2118
CX211A

接著我們要開始寫目的程式,但在寫之前要先把在 SIC 表內的指令轉成目的碼,其實就是把我前一篇指令分析的步驟倒過來。

標註指令運算元位址目的碼
EX2START21002100
TSTLDAONE2100002115
STADATA21030C2112
LDXONE2106042115
LDCHSTR,X210950A118
STCHCX210C54211A
RSUB210F4C0000
DATARESW12112
ONEWORD12115000001
STRBYTEC'01'21183031
CXRESB1211A
ENDTST211B

上方這張表多了目的碼這個欄位,而欄位的計算重點如下:

  • 指令可以透過 SIC 指令表取得對應的 opcode,而 opcode 就是前八位二進位
  • 如果運算元有 ,X 表示要使用索引定址,則我們把二進位的第九位設為 1
  • 其於的 15 位則可以參考標記符號位址表填入
  • BYTE 直接把字元換成 ASCII 再換成十六進制寫入
  • WORD 直接寫入運算元換成十六進制的值即可,但要記得保留前面的 0,總共要有六位。

下面以 LDCH STR,X 這條指令為例:

我們取得 LDCH 的 opcode 為 50,二進位為 0101 0000,這代表前八位。因為有 ,x,因此要使用索引定址,我們把第九位設為 1,反之為 0

最後把剩下的 STR 的位址 2118 轉為二進位,得到 010 0001 0001 1000,可以發現到第一組只有三位,因為只有 15 位。

接著把所有的二進位組合起來 0101 0000 1 010 0001 0001 1000,再換回十六進位就可以得到 50A118

目的程式

接下來真的可以開始寫目的程式了,目的程式要一行一行的寫,且所有的地方都以十六進位表示。這邊特別提一下,正常的程式不會有 ,,這裡是為了清楚顯示才加的。

第一行是表頭紀錄,要依序寫入的資訊如下:

  • 開頭字母 H
  • 六格的程式名稱,如果不足則直接補空格
  • 六格的開始位址,如果不足則直接補零
  • 六格的程式長度,如果不足則直接補零

以這份程式為例,表頭紀錄如下:

H,EX2☐☐☐,002100,00001B

值得注意的是程式長度的算法就是 END 位址減掉 START 的位址

接下來會有數行的本文紀錄,要依序寫入的資訊如下:

  • 開頭字母 T
  • 六格的開始位址,如果不足則直接補零
  • 兩格的程式長度,如果不足則直接補零。這個可以最後再寫
  • 從上到下的目的地,連續往後寫不加空格,直到遇到指令沒有目的碼,則直接重新開始一行

以這份的程式為例,可以寫出兩行如下: T,002100,12,002115,0C2112,042115,50A118,54211A,4C0000 T,002115,05,000001,3031

最後一行是結束紀錄,但是這裡因為剛好遇到有指令沒有目的碼,因此可以直接不寫 END。

這裡就完成了 SIC 的指令組譯了。

SIC/XE 組譯

大部分的步驟都和 SIC 組譯相同,我會特別針對不同的地方點出來,相同的地方我不會再提。

題目 PDF 下載

下方為練習卷 SIC/XE 組譯練習卷

和 SIC 大部分相同,先把位址寫一寫,寫完的結果如下:

位址標記符號指令運算元
0START0
0BBLDB#BB
-BASEBB
3LDAKK
6+STANN
AKKWORD15
DBUFRESB4096
100DLDA@NN
1010STABUF,X
1013NNRESW1
1016ENDBB

特別注意到 +STA,這個代表的是它佔了四個 byte,因此計算的時候要 +4。

接著我們就直接寫目的碼,寫完如下:

位址標記符號指令運算元目的碼
0TESTSTART0
0BBLDB#BB692FFD
-BASEBB
3LDAKK032004
6+STANN0F101013
AKKWORD1500000F
DBUFRESB4096
100DLDA@NN022003
1010STABUF,X0FC00D
1013NNRESW1
1016ENDBB

在開始之前請確保已經學會了指令分析,因為很多東西我不會再提。這裡我會選幾條需要講解的來做,其它的做完可以參考上方。

LDB #BB

同樣的先找到 LDB 的 opcode 68,之後就可以發現到運算元前面有個 # 的符號,這和 ni 有關,可以參考下方的整理:

  • 無符號 11
  • # 01
  • @ 10

因此這裡可以知道 ni 要是 01

而我們還有 xbp 要決定,決定的方法如下:

  • x 只有運算元指定的時候才使用
  • b 參考 p
  • p 只要是運算元是標記符號就使用,但需要注意如果 TA 會超出範圍 2048PC2047-2048 \leq PC \leq 2047,則換成 b。
  • 如果是格式四則全為 0

最後還有一個 e,這個的決定就是運算子前面有沒有 + 號,如果有的話就會變成格式四。

但到這裡我們只決定了 nixbpe,我們還有最後的 12 位要算。還記得之前做指令分析的時候會把最後 12 位加上 xbp 之類的暫存器的位址嗎?這裡我們要反算,我們要讓算出來的值剛好落在我們想要的地方。

這裡我們的目標是 BB,也就是 0 這個位址,而 PC 此時是多少呢?PC 指的是下一行的位址,在這裡是 3,因此我們就知道這 12 byte 要是 -3,這樣一來才能跟 PC 做抵銷,來到達我們想要的地。注意一下 -3 要使用二的補數來表達。

而這樣我們就全部都有了,我們就能寫出 0110 1001 0010 1111 1111 1101,換成十六進位就是 692FFD

+STA NN

這條可以發現到前面有 + 號,因此要使用格式四的方式來寫,也就是說會有 32 位,寫出來的目的碼也會明顯比較長。

除此之外,xbp 都應該要是 0

LDA @NN

這條沒有什麼特別的,但可以發現到運算元前面有 @,也就是說 ni 要是 10

STA BUF,X

這條可以發現到有一個 ,X,可以知道 xbp 的 x 要設為 1。除此之外還會發現到後 12 位算完會超出範圍,這個時候就要把 p 改為 0 且把 b 改為 1

這個時候的後 12 位就不是在使用 PC 去做計算,而是要使用 BASE 所在的位址,也就是 0

因為剛好是零,因此後 12 位就剛好是 BUF 的位址。

目的程式

當寫完全部的目的碼之後就可以開始寫目的程式,第一行的表頭紀錄和 SIC 一樣,如下:

H,TEST☐☐,000000,001016

接下來一樣會有數行的本文紀錄,跟之前完全一樣,如下:

T,000000,0D,692FFD,032004,0F101013,00000F

T,00100D,06,022003,0FC00D

再接下來會有一個跟 SIC 不同的地方,稱為修正紀錄。為什麼會需要這個紀錄是因為有時候我們使用的定址是直接寫死的,也就是說不是用 PC 之類的方法算出來的,而是直接寫死某個地方,但程式會被放在記憶體的哪裡我們不知道,因此寫死的位址就會在運作的時候出錯。

以這題為例,可以發現到 +STA NN 的 xbp 都是 0,也就是說它的位址寫死了,因此它就需要修正。

每一個需要修正的指令都要一行修正紀錄,內容依序寫入:

  • 開頭字母 M
  • 六格要修正的指令後 20 位或後 12 位所在的位址
  • 兩格要修正的位數,位數要再除於四
  • 符號 +
  • 六格 START 位址的標記符號

以這份的程式為例,修正紀錄如下:

M,000007,05,+,TSET☐☐

這裡的長度其實只會有兩種可能,如果是格式三就一定是 3,如果是格式四就一定是 5。

結束紀錄如下,就填上 BB 代表的位址即可:

E,000000

AI 繪圖 - 腦中的畫面

· 2 min read

老實說我覺得這個時候發 AI 繪圖都晚了,因為早在 2022 年就開始非常多人在用 AI 繪圖,但因為當時就真的沒有用到也沒有動力特別去研究,而且當時還要各種提示詞。但在 2025 年一切都不同了,我們可以直接把我們想要畫的用說的說出來,當然也是不說每次都一試就中,還是要很多的微調。

想換大頭貼了

我先前有過兩代大頭貼,都是我用 Minecraft 的地圖加上 Mine-imator 還有 Photoshop 之類的工具用出來的,但老實說我覺得也該換換風格了,而且我自己也不再像以前那麼常玩,比較難想出非常符合的圖,所以這次我就使用 AI 來幫我畫畫出我腦中的畫面。

下面兩幅是我在調整的過程中我最喜歡的兩張,其中一張是做為之後的大頭貼使用的:

穿躍時空的我第一張 穿躍時空的我第二張

我自己是非常喜歡,因為有把我腦中的圖片非常完美的給畫出來。我所使用的是最新的 Copilot,可能這篇文之後又會再有更強的模型出現,但我應該不會重新生成這兩張了,因為真的非常的美。

Linux 第一個要學會的東西

· 5 min read

話說在前面,這篇不是教學文,只是我覺得一定要讓每個踏入 Linux 世界的初學者一定要會的東西:「script」。

我來到 Linux 的世界也快要兩年了,時間真的過的很快,高中畢業那年,我決定把我的電腦換成 Linux 當主要作業系統。當然,有時我也是會到 Windows 打個遊戲,因為支援性還是有差。在 Linux 這條路上,我覺得有樣東西我一定要推薦給每個剛入坑的人,不管是學校上課要用還是你要個人使用,麻煩請先學會寫 script。

不能再逃了

我先說我自己是非常不喜歡為了一件小事就特別寫一個程式,除非是真的沒有辦法,像是之前我的 Google 相簿匯出檔整理真的太多,我才會特別寫 Python 的腳本來用。不喜歡的原因有點兩點:第一點是很怕寫錯把資料用壞,第二是因為我覺得寫程式的時間,都夠我手動用完全部了。一直到我學會了 script 之後,我才發現並非如此。

要我說說學會之前跟之後的差別,大概就像是從從腳踏車換到摩托車一樣,就是差那麼多。但為什麼我要突然開始學怎麼寫腳本呢?有趣的是,的是因為生成式 AI 常常會給我一些建議。因為像是重複的複製檔案這種煩人但是又不難的事情,我問生成式 AI 就會直接推薦我寫一個 script,看久了我就覺得不如來學一下好了。

學會就遇到雜事一堆

就在我還在學會怎麼寫的時候,我就遇到了一堆事要處理。我們教授給了一堆題目要我出,出題是件非常麻煩的事,因為每題都要準備很多的東西。我之前有過了一套可以跑 generator 跟 solution 來生測資的軟體,但是要複製東複製西的,非常的麻煩,而且萬年的爛 ZeroJudge 對於檔案的命名又有很特別的規定,所以我就自己寫了一個 script 來處理重新命名測資跟複製程式碼,那種點一下就好的感覺是真的很爽,後來我才發現到原來寫這個這麼的簡單,是之前我的心魔一直覺得很可怕,但其實不然。

我相信很多人遇到我上面要處理的東西,應該就是手動處理吧!但接下來的東西可就麻煩多了,因為我自架了一套伺服器,功能真的滿多的,而伺服器其中一個很重要的部分就是備份。之前處理備份,我都是寫一份檔案來檢查哪些檔案備份了?幾號備份?等等的資訊,後來我就直接寫個 script,現在我只要插上硬碟,輸入 bash backup.sh <路徑> 之後去看部影片,就處理完了全部都自動化,超級開心。

接下來我處理的是讓我 script 的語法大幅進步的腳本,因為我要使用 gpg 來加密大量的檔案,如果我一個一個輸入大概會輸入到死,所以我就研究怎麼樣傳參數等等。寫完之後加解密一樣只要一行,全部幫我處理到好。

投資

我發現到這就像是個投資一樣,前面學的時候真的要花一點時間,一直瘋狂的除錯。但只要你學會之後,什麼事都很輕鬆,就像不會有人不學怎麼騎摩托車一樣,因為學會可是終身受用阿!一起來加入 script 的行列吧。

花式追蹤大法

· 3 min read

昨天終於把我想要追很久的 Web Tracking 筆記 追完了,我只能說內容非常地有創意。我很喜歡的一句話是「利益總會讓人創意無限」,在 web tracking 這個技術上完全體現出來了。

Cross-site Tracking

如果有全部看完會發現到一切都是為了 Cross-site Tracking,因為如果追蹤只能在單一個域名下,那就完全沒有意義了,要做到的是追蹤使用者點了什麼後又做了什麼事,一切都是以此為出發點。

Browser Fingerprinting

這大概是我覺得最驚人的技術,因為要反制 browser fingerprint 異常的困難,如果我們讓 fingerprint 消失或者用亂數又會變的太有特徵,但如果用成大家都一樣又一定會有破綻或者網頁的功能壞掉。

各大瀏覽器怎麼做?

我現在在使用的是 Librewolf,也就是 Firefox 的 fork,裡面多了很多保護隱私的功能。其實本來是 Firefox 就做的很好了,事實上相比於 Chrome,像 Safari、Brave 都做了很多的努力,我本來以為像 Safari 這種非常壟斷的瀏覽器會安裝更多的追蹤器等等,但後來我才發現到 Safari 一直在這方面做努力。其中 Tor 的做法非常的暴力,如果一個功能會有被用來追蹤的可能,那就直接刪掉,但可想而之很多功能也會跟著壞掉。

我的看法

我自己是非常不喜歡被追蹤,這點我之前應該也有提過,但我也知道沒有辦法真的完全做到反追蹤,不然這樣很多的功能都會壞掉,就像我們不可能上網一直開著 Tor 一樣。我做的其實跟大部分的人一樣,安裝 uBlock Origin 等等的插件,還有使用 Adguard Home,當從源頭防止就從源頭防止,當然我也是有遇過網站壞掉,但老實說是少數,我就直接不看。我自己非常喜歡的一點是在文章最後面各大瀏覽器廠商提出的方案,做到了追蹤但是又不會可以區別到每個獨立的使用者。