Spoc CPU軟核 Part 3-軟件(即程序員)模型
Spoc 有一個小指令集和一些尋址模式。
這使得 Spoc 程序員的模型易于學習。
指令集
Spoc 目前支持 8 條指令:
例子:
inc RA2 // increments register RA2 dec A // decrements accumulator sel WA10 // selects WA10 do #0xBA00 + WA22 > A // adds value 0xBA00 to register WA22, and writes the result to the accumulator do A and #0x5555 -> @ // logical AND between accumulator and value goes to memory do A xnor #0x5555 > RA0 // logical XNOR between accumulator and value goes to register RA0, selects RA0 not RA1 // inverts all the bits in register RA1, selects RA1 jsr A+RA10 // calculates A+RA10, and jumps to this address (subroutine, returns with RET) jmp #loop // jmp to label "loop"
尋址模式
Spoc 支持 3 種尋址模式:
例子:
do #1234 -> A // Immediate addressing: moves value 1234 to accumulator do A -> RA4 // Direct addressing: writes accumulator value to register RA4 do @ -> A // Indirect addressing: reads memory to accumulator do A -> @ // Indirect addressing: writes accumulator to memory
可以對源操作數和目標操作數使用間接尋址。
do @ -> @ do @ + #0x22 -> @ do A or @ -> @
間接尋址與“選定的寄存器”有關(參見下面的“存儲器和寄存器文件”段落)。
DO指令
DO 指令是最強大的,因為它可以使用多達 2 個源執行操作,并將結果寫入多達 2 個目的地。
要寫入 2 個目標,請用逗號分隔它們。
示例:寫入 2 個目標時,一個始終是累加器,另一個是寄存器 (RAxx/WAxx) 或存儲器 (@)。
do #22 -> A, RA0 do A + #22 -> A, WA1 do A - @ -> A, WA1 do A or RA3 -> WA4, A do @ and #22 -> A, @ do WA6 xor #22 -> A, @
累加器和數據大小
每個 spoc 指令都從可能的大小列表中指定一個數據大小。
Spoc0 具有 4 種有效數據大?。?、8、16 和 32 位。 默認值為 16 位(未指定數據大小時)。
數據大小是通過后綴指令(.bit、.byte、.word 和 .dw)來指定的。
Spoc 還為每個有效數據大小提供了一個累加器。使用 Spoc0,這為我們提供了 4 個累加器。
累加器是獨立的(寫入一個不會影響其他累加器)。
例子:
do.bit #1 -> A // writes 1 to the 1-bit accumulator inc.byte A // increments the 8-bits accumulator do.word A + #0x1000-> A // adds 0x1000 to the 16-bits accumulator do.dw #0x12DECF80 -> A // writes 0x12DECF80 to the 32-bits accumulator
分支
指令 JMP 用于分支到新的程序位置。
指令 JSR 用于分支到新的程序位置,最終使用指令 RET 從該位置返回。
這些指令可以有條件地執行(借助 C 和 Z 標志,請參閱下一段)。
例子:
jmp #gothere // jumps unconditionally jsr #gotosubroutine // jumps unconditionally to subroutine jmp.Z=0 #gothere // jumps conditionally (if flag Z is 0)
分支指令可以使用計算地址進行分支。 例如,可以有子例程表。
注意:在任何分支指令之后,將取消選擇當前寄存器(有關“選定”寄存器的討論,請參閱后面的內容)。
疊
在使用 JSR 指令之前,請確保初始化 SP(堆棧指針)寄存器。
例:
do #0x0C00 > SP // stack from 0x0C00 to 0x0FFF,enough for a depth of 64 subroutine calls jsr #mysubroutine
堆棧用于存儲子例程返回地址。 堆棧使用內存并向上增長(在 Spoc0 中,SP 指針對于每個 JSR 指令遞增 16,對于每個 RET 遞減 16)。
標志和條件執行
標志用于有條件地執行其他指令。
Spoc0 使用 2 個標志:
Z(零)標志。用于檢測零值或“相等”比較。
C(攜帶)標志。對于無符號數字的“大于”比較很有用(還可以檢測無符號添加/子操作的溢出)。
如果上一個操作的結果為 0,則 Z 標志為 0。
如果上一個操作的結果不是 1,則 Z 標志為 0(注意:許多 CPU 采用相反的約定......
標志由所有執行的指令設置。
例子:
do #3 > A dec A // A becomes 0x0002, C is 0, Z is 1 dec A // A becomes 0x0001, C is 0, Z is 1 dec A // A becomes 0x0000, C is 0, Z is 0 dec A // A becomes 0xFFFF, C is 1, Z is 1 dec A // A becomes 0xFFFE, C is 0, Z is 1 dec A // A becomes 0xFFFD, C is 0, Z is 1
您可以執行沒有目標的 DO 操作。 在這種情況下,將執行操作,結果將丟失,但標志仍會更新。
例子:
do #3 -> WA0 // writes 3 to register WA0 // now we are going to do 3 subtractions, without saving the results. But the flags are still updated. do WA0-#2 // WA0>2, so we get C=0, Z=1 do WA0-#3 // WA0=3, so we get C=0, Z=0 do WA0-#4 // WA0<4, so we get C=1, Z=1 // now run some conditional instructions jmp.c=0 #mylabel1 // conditional jump, not executed since C=1 add.z=0 WA0 + A > RA2 // conditional addition, not executed since Z=1 jmp.z=1 #mylabel2 // conditional jump, executed since Z=1
最后,如果未執行條件指令,則標志也不會更新。
例:
do #1 > A // A is 0x0001, C is 0, Z is 1 dec.z=0 A // not executed, and flags not changed
將標志作為操作數
進位標志也可以在源操作數中使用(在源操作數中,進位標志可以命名為“C”或“CY”或“carry”)。
例:
do CY -> A do A + #22 + C -> A do A xor CARRY -> RA0
對于算術運算,進位標志值不是有符號擴展的,但對于邏輯運算,它是有符號擴展的(換句話說,對于算術運算,進位值為 0 或 1,對于邏輯運算,進位值為 0 和 0xFFFF)。
例:
do #0 -> A dec A // A=0xFFFF, C=1 do CY + #22 -> A // arithmetic operation, so A=23 do #0 -> A dec A // A=0xFFFF, C=1 do CY xor #0x1111 -> A // logical operation, so A=0xEEEE
存儲器和寄存器文件
符號“@”用于表示內存訪問。
它與名為“RAxx”和“WAxx”的寄存器結合使用。
內存讀取示例(從地址0x200讀?。?/p>
do #0x0200 -> RA0 do @ -> A // reads memory 0x200, and puts the value in accumulator
內存寫入(寫入地址0x200)示例:
do #0x0200 -> WA17 do RA3 -> @ // writes content of RA3 to memory 0x200
讀取存儲器訪問的地址由“RAxx”寄存器給出。
寫存儲器訪問的地址由“WAxx”寄存器給出。
寄存器是寄存器文件的一部分,因此您可以使用其中的許多寄存器。
Spoc0 有 32 個 RA 寄存器,命名為 RA0 到 RA31,以及 32 個 WA 寄存器,命名為 WA0 到 WA31。
每個文件的一個寄存器在給定的執行時間被“選擇”。 要選擇寄存器,您可以使用“sel”指令,也可以寫入寄存器。
當指令進行內存訪問時,所選寄存器的值用作內存地址。
在每次內存訪問期間,寄存器都會自動遞增。
例:
do #0x0200 -> RA5 // writes 0x200 to RA5, and selects it do #0x0300 -> WA7 // writes 0x300 to WA7, and selects it // RA5 and WA7 are both selected do @ -> @ // copies the value from memory location 0x200 to memory location 0x300 // now RA5=0x210 and WA7=0x310 do WA7 + #0x20 -> RA6 // RA6 is now selected with the value 0x330 do @ -> A // memory location 0x330 is read and copied to the accumulator sel RA5 // re-select RA5. Since it was non-persistent, its value is back to 0x0200 (see later for explanation on persistent registers)
寄存器 RAxx/WAxx 還可用于存儲器訪問以外的其他目的。 它們具有與累加器幾乎相同的功能,因此可以遞增、添加、異或編輯......
它們還可用于子例程參數傳遞。
內存空間
使用 Spoc0 時,每個寄存器 WAxx 和 RAxx 的寬度為 16 位 (.word),因此它可以尋址 64K。
數據空間是“位可尋址”的。 因此,您可以在沒有任何對齊限制的情況下訪問它的任何部分。 例如,可以將一個 32 位值寫入尋址0xABCD,然后在地址0xABC3寫入另一個值,最后從地址0xABC7讀?。ㄗx取兩個值的一部分)。
代碼空間通常無法直接訪問。 它用于保存要執行的指令。 但是有一個鉤子可以訪問代碼空間,這樣你就可以存儲表、數據、字符串......
Spoc0 使用寄存器 CS 訪問代碼空間。
例:
do #GreetingString -> CS // CS points to a string, and selects CS do.byte @ -> A // read the "H" into the accumulator ... GreetingString: data.byte "Hello world!", 13
另一個保留值是“$”。 它代表實際的PC(程序代碼位置,當前執行指令的位置)。 這是一個只讀值。
例:
jmp $ // forever loop (if you want spoc to "die")
當您使用 blockram 時(如在 Spoc0 中),代碼空間是位可尋址的,如果使用串行閃存,則代碼空間是字節可尋址的。 這為 Spoc64 提供了 0Kbits 的空間,而使用串行閃存時則為 64KB 的空間。
預留空間
Spoc0 使用一個塊 (4Kbits) 作為數據空間,它填充0x0000到0x0FFF的地址。
在此之上,0x1000 0xFFFF的空間是“外部存儲器”。它應該用于連接外部外圍設備。
此外,保留 blockram 數據空間的前 1Kbit(地址 0x0000 到 0x03FF)來保存寄存器 RAxx/WAxx 的值。 如果你知道自己在做什么,你仍然可以使用它......
保留寄存器
寄存器 WA30/31 和 RA30/31 是保留的。 不要使用它們。
例如,在 Spoc0 中,寄存器 WA31 用作 PC...所以如果你要使用 WA31,你實際上會跳到某個地方......
持久寄存器
當一個寄存器被取消選擇,然后在以后重新選擇時,它的值可以是“持久的”(它在最后一次自動遞增之后恢復該值),也可以是“非持久的”(它恢復最初受影響的值,即在任何內存訪問/自動遞增之前)。
您可以選擇是否希望在選擇寄存器時將其持久化(通過使用或不使用“.p”后綴)。
例:
do #0x0200 -> RA5 // RA5 is selected (not persistent) do @ -> A // copies the value from memory location 0x200 to accumulator // RA5 value is now 0x210 do #0x0300 -> RA6.p // RA6 is selected (persistent) do @ -> A // copies the value from memory location 0x300 to accumulator // RA6 value is now 0x310 sel RA5.p // re-selects RA5. Since it was non-persistent, its value is back to 0x0200 // note that it is now persistent! do @ -> A // copies the value from memory location 0x200 to accumulator // RA5 value is now 0x210 sel RA6 // re-selects RA6. Since it was persistent, its value is 0x0310 do @ -> A // copies the value from memory location 0x310 to accumulator // RA6 value is now 0x320 sel RA5 // re-selects RA5 (not persistent). But since it was persistent, its value is 0x0210 do @ -> A // copies the value from memory location 0x210 to accumulator // RA5 value is now 0x220
評論