簡(jiǎn)評(píng):誰(shuí)能有原作者會(huì)玩屉佳?活脫脫把一個(gè)表格處理工具當(dāng)作像微處理器來(lái)運(yùn)行煤墙,感覺(jué)以后「微型計(jì)算機(jī)原理」都可以用這個(gè)做實(shí)驗(yàn)了。
我最近注意到,Google Docs 有一個(gè)叫 Apps Script 的全功能腳本系統(tǒng)跃须,它可以讓你使用JavaScript 做一些非常有用的事情:
- 運(yùn)行代碼來(lái)響應(yīng)文檔打開(kāi)事件或單元格更改事件搁吓;
- 為公式制作自定義 Google Sheets 電子表格函數(shù)抹缕;
- 使用像 Google 這樣的服務(wù)來(lái)翻譯文本或電子郵件安接;
- 使用自定義功能將新菜單項(xiàng)添加到 Google Docs 的界面中。
但雾家,我用它做卻做了 (? ??_??)? 一些奇奇怪怪的事铃彰,看啊,Google Sheets 虛擬機(jī)竟然在生成斐波納契數(shù)列了榜贴!
運(yùn)行原理
虛擬機(jī)有 100 個(gè)單元的內(nèi)存區(qū)域豌研,索引為 0-99,每個(gè)單元格都可以包含一個(gè)指令或一個(gè)整數(shù)值唬党。
還有一個(gè)堆棧鹃共,它從內(nèi)存區(qū)域的底部開(kāi)始,然后向上增長(zhǎng)驶拱。
下圖是當(dāng)這個(gè)虛擬機(jī)為空的時(shí)候的樣子:
注意一下事項(xiàng):
- RA霜浴,RB,RC 和 RD 是通用寄存器
- RI 是指令指針蓝纲,它指向要在內(nèi)存區(qū)域執(zhí)行的下一條指令阴孟,該指示燈亮起綠色。
- RS 是堆棧指針税迷,它指向堆棧頂部的存儲(chǔ)單元永丝,這亮起藍(lán)色
- Output 顯示程序的輸出
- Error 顯示在解析或執(zhí)行指令時(shí)遇到的任何錯(cuò)誤
- Memory 是 100 個(gè)存儲(chǔ)單元的區(qū)域
要運(yùn)行指令,后臺(tái)的 Apps Script 會(huì)檢查 RI 的值箭养,查看接下來(lái)要執(zhí)行的指令慕嚷,它讀取 RI 指向的指令并解析它。
指令在內(nèi)存和寄存器之間操作數(shù)據(jù)毕泌,操作堆椇燃欤或執(zhí)行條件判斷。
指令執(zhí)行完畢后撼泛,**RI **的值增加挠说,指向存儲(chǔ)器中的下一個(gè)單元。
用法
下面是一個(gè)名為 Computer 的自定義菜單愿题,其中包含一些用于控制虛擬機(jī)的命令:
- **Run **將運(yùn)行當(dāng)前程序损俭,直到它結(jié)束或遇到錯(cuò)誤
- **Step **運(yùn)行一條指令蛙奖,然后暫停
- **Reset **清除所有寄存器和輸出字段,這使程序可以再次運(yùn)行
- **Load Factorial Program **從另一個(gè) Sheet 中加載 factorial 示例
- **Load Fibonacci Program **從另一個(gè) Sheet 中加載 fibonacci 示例
指令
有一些已經(jīng)實(shí)現(xiàn)的指令:
General
- mov dst src
數(shù)學(xué)運(yùn)算
- add dst src
- sub dst src
- mul dst src
堆棧操作
- push src
- pop dst
跳轉(zhuǎn)和條件
- jmp target
- jl cmp1 cmp2 target
函數(shù)
- call target
- ret
其他
- output src
- end
尋址模式
上述指令中的操作數(shù)可以采取幾種形式
Immediates (立即數(shù))是嵌入到指令中的數(shù)值撩炊,例如:7 和 123外永。 將值 7 復(fù)制到寄存器 ra中:
mov ra 7
**Registers **寄存器尋址崎脉,例如:ra拧咳,rb,rc囚灼。將值從 rc 復(fù)制到 rb 中:
mov rb rc
**Memory **指存儲(chǔ)區(qū)域的單元格內(nèi)的值骆膝,例如:$0,$10灶体,$99阅签。將 ra 中的值復(fù)制到第一個(gè)存儲(chǔ)單元中:
mov $0 ra
將值從最后一個(gè)存儲(chǔ)單元復(fù)制到 rd:
mov rd $99
**Indirect **是指內(nèi)存單元指向的值,例如:@15蝎抽,@50政钟。因此,如果存儲(chǔ)器單元 10 包含值 20樟结,并且存儲(chǔ)器單元 20 包含值 30养交,則可以將值 30 像這樣地復(fù)制:
mov ra @10
它查看存儲(chǔ)器單元 10 找到值 20 ,然后瓢宦,它查看存儲(chǔ)器單元 20 找到值 30碎连,并將該值復(fù)制到ra 中。
遞歸
還可以使用堆棧驮履、 call 和 ret 指令進(jìn)行遞歸調(diào)用鱼辙。這是一個(gè)使用遞歸來(lái)生成 5 的階乘的例子:
從 jl ra 2 50 中開(kāi)始的代碼是一個(gè)函數(shù),它接受 ra 中輸入值玫镐,并將返回結(jié)果存入 rd倒戏,它會(huì)遞歸地調(diào)用它來(lái)計(jì)算 ra 中值的階乘。
一起玩啊
如果你也想玩恐似, 戳戳這
使用工具和腳本編輯器杜跷,就可以看到應(yīng)用程序腳本代碼
原文:Google Sheets Virtual Machine
擴(kuò)展閱讀:
- 編譯器和解釋器之間有什么區(qū)別
- 歡迎關(guān)注知乎專欄「極光日?qǐng)?bào)」,每天為 Makers 導(dǎo)讀三篇優(yōu)質(zhì)英文文章蹂喻。