2

如果可能的话,我试图了解如何解析每 4 位的二进制文件。

例如:我有 2 字节的代码需要解析以确定使用哪条指令

#{1NNN}其中前 4 位表示哪条指令,NNN 表示内存位置(即#{1033}跳转到内存地址#{0033}

使用完整字节似乎很容易做到这一点,但使用半字节则不然:

parse #{1022} [#{10} {#22}] 

因为#{1}无效binary!

到目前为止,我已经使用了巨大的 switch 语句:#{1033} AND #{F000} = #{1000}为了处理这些,但想知道一个更成熟的 reboler 可能会如何做到这一点。

4

1 回答 1

1

这是一个相当大的条目,但它满足了您的需求并稍微展示了 PARSE。

这基本上是一个工作的,虽然简单的 VM,它使用你上面描述的内存布局。

我设置了一个简单的 RAM 块,它是一个实际程序,当我将 PARSE 与模拟器语法规则一起使用时,它会执行它......基本上,它会增加一个地址,然后跳转到该地址,跳过一个 NOP。

然后它击中了一些非法操作并死亡。

REBOL [
    title:  "simple VM using Parse, from scratch, using no external libraries"
    author: "Maxim Olivier-Adlhoch"
    date:    2013-11-15
]

;----
; builds a bitset with all low-order bits of a byte set, 
; so only the high bits have any weight
;----
quaternary: func [value][
    bs: make bitset! 
    reduce [
        to-char (value * 16)
        '- 
        to-char ((value * 16) + 15)
    ]
]

;------
; get the 12 least significant bits of a 16 bit value
LSB-12: func [address [string! binary!] ][
    as-binary (address AND #{0FFF})
]

;------
i32-to-binary: func [
    n [integer!] 
    /rev
][
    n: load join "#{" [form to-hex to-integer n "}"]
    either rev [head reverse n][n]
]

;------
; load value at given address. (doesn't clear the opcode).
LVAL: func [addr [binary!]][
    to-integer copy/part at RAM ( (to-integer addr) + 1) 2
]


;------
; implement the opcodes which are executed by the CPU
JMP: func [addr][
    print ["jumping to " addr]
    continue: at RAM ((to-integer addr) + 1) ; 0 based address but 1 based indexing ;-)
]

INC: func [addr][
    print ["increment value at address: " addr]
    new-val: 1 + LVAL addr
    addr: 1 + to-integer addr
    bin-val: at (i32-to-binary new-val) 3
    change at RAM addr bin-val
]

DEC: func [addr][
    print ["decrement value at address: " addr]
]

NOP: func [addr][
    print "skipping Nop opcode"
]



;------
; build the bitsets to match op codes
op1: quaternary 1
op2: quaternary 2
op3: quaternary 3
op4: quaternary 4


;------
; build up our CPU emulator grammar
emulator: [ 
    some [
        [
            here:
            [ op1 (op: 'JMP)  | op2 (op: 'INC)  | op3 (op: 'DEC)  | op4 (op: 'NOP)] ; choose op code
            :here 

            copy addr 2 skip (addr: LSB-12 addr) ; get unary op data
            continue:
            (do reduce [op addr])
            :continue
        ]
        | 2 skip (
            print ["^/^/^/ERROR:  illegal opcode AT: " to-binary here " offset[" -1 + index? here "]"] ; graceful crash!
        )
    ]
]



;------
; generate a bit of binary RAM for our emulator/VM to run...

       0   2   4   6   8    ; note ... don't need comments, Rebol just skips them.
RAM: #{2002100540FF30015FFF}
RAM-blowup: { 2 002  1 005  4 0FF  3 001  5 FFF } ; just to make it easier to trace op & data


parse/all RAM emulator


print  "^/^/Yes that error is on purpose, I added the 5FFF bytes^/in the 'RAM' just to trigger it  :-)^/"

print "notice that it doesn't run the NOP (at address #0006), ^/since we used the JMP opcode to jump over it.^/"

print "also notice that the first instruction is an increment ^/for the address which is jumped (which is misaligned on 'boot')^/"

ask "press enter to continue"

输出如下:

increment value at address:  #{0002}
jumping to  #{0006}
decrement value at address:  #{0001}



ERROR:  illegal opcode AT:  #{5FFF}  offset[ 8 ]


Yes that error is on purpose, I added the 5FFF bytes
in the 'RAM' just to trigger it  :-)

notice that it doesn't run the NOP (at address #0006),
since we used the JMP opcode to jump over it.

also notice that the first instruction is an increment
for the address which is jumped (which is misaligned on 'boot')

press enter to continue
于 2013-11-15T20:23:42.980 回答