Zacktron-33

所属分类:编程语言基础
开发工具:Zig
文件大小:0KB
下载次数:0
上传日期:2022-10-23 01:23:48
上 传 者sh-1993
说明:  在Zig中实现的假想Calcutron-33 CPU的汇编程序和反汇编程序,
(Assembler and disassembler for the imaginary Calcutron-33 CPU implemented in Zig,)

文件列表:
LICENSE (1109, 2022-10-22)
build.zig (2437, 2022-10-22)
examples/ (0, 2022-10-22)
examples/adder.ct33 (67, 2022-10-22)
examples/doubler.ct33 (56, 2022-10-22)
examples/maximizer.ct33 (134, 2022-10-22)
examples/maximizer.machine (40, 2022-10-22)
examples/simplemult.ct33 (130, 2022-10-22)
src/ (0, 2022-10-22)
src/assembler.zig (10224, 2022-10-22)
src/colors.zig (1376, 2022-10-22)
src/common.zig (1492, 2022-10-22)
src/computer.zig (12220, 2022-10-22)
src/debugger.zig (2234, 2022-10-22)
src/disassembler.zig (4004, 2022-10-22)
src/main.zig (123, 2022-10-22)
src/simulator.zig (1298, 2022-10-22)
testdata/ (0, 2022-10-22)
testdata/adder.ct33 (67, 2022-10-22)
testdata/adder.machine (25, 2022-10-22)
testdata/doubler.ct33 (56, 2022-10-22)
testdata/doubler.machine (20, 2022-10-22)
testdata/isa.ct33 (303, 2022-10-22)
testdata/isa.machine (85, 2022-10-22)
testdata/labels-nocode.ct33 (36, 2022-10-22)
testdata/labels-nocode.machine (0, 2022-10-22)
testdata/maximizer.ct33 (134, 2022-10-22)
testdata/maximizer.machine (40, 2022-10-22)
testdata/roundtrip.ct33 (105, 2022-10-22)
testdata/simplemult.ct33 (130, 2022-10-22)
testdata/simplemult.machine (40, 2022-10-22)

# Zacktron-33, The Decimal RISC CPU This is an assembler, disassembler and simulator for an imaginary CPU called Calcutron-33. The rational for this CPU was described first time in [this medium article](https://medium.com/@Jernfrost/decimal-risc-cpu-a13968922812). The original version of this was written in Julia and this is really an exercise in learning and using the Zig programming language. To not confused the Zig version from the Julia version I a calling this the Zacktron-33. ## Example This is a simple example of the assembly language. In this example we are repeatedly reading two input numbers, multiplying them and writing the result to output. loop: INP x1 INP x2 CLR x3 multiply: ADD x3, x1 DEC x2 BGT x2, multiply OUT x3 BRA loop Unlike Little Man Computer, which has only one register this has a more RISC like architecture with 9 register `x1` to `x9`. Branching is done similar to MIPS. One compares the contents of a register to 0. So e.g. `BGT x2, multiply` will make a jump to `multiply` if the contents of `x2` register is larger than 0. ## Install and Usage You can use the the `zig build --help` command to get overview over how to build and use the files. zig build --help Usage: zig build [steps] [options] Steps: install (default) Copy build artifacts to prefix path uninstall Remove build artifacts from prefix path assemble Run the assembler simulate Run the simulator disassemble Run the disassembler debug Run the debugger Running `zig build` will create three executables `assemble`, `simulate` and `disassemble` in the `zig-out` directory. You can install built executable anywhere with the `zig build install` command. To install in the current directory, you can write: zig build install -p . --prefix-exe-dir . To build and run an executable directly you can write the following: zig build assemble zig build simulate Once you have the `assemble` and `simulate` programs you can use them to assemble `.ct33` programs into `.machine` code which can be run by the simulator. For instance this will assemble the bundled `adder.ct33` program: assemble testdata/adder.ct33 > adder.machine You can later run the this program in the simulator. Programs read input from stdin, so you can either type input on the keyboard when the program runs or you can redirect some input. Input numbers are separated by space or newline. echo 2 3 8 4 | simulate adder.machine 0: 8190; LD x1, 90 1: 8290; LD x2, 90 2: 1112; ADD x1, x1, x2 3: 9191; ST x1, 91 4: 6000; BRZ x0, 0 0: 8190; LD x1, 90 1: 8290; LD x2, 90 2: 1112; ADD x1, x1, x2 3: 9191; ST x1, 91 4: 6000; BRZ x0, 0 0: 8190; CPU state PC: 0 x0: 0, x1: 12, x2: 4, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, Inputs: Output: 5, 12, Here we feed in the numbers 2, 3, 8 and 4 into the simulator as inputs. You can then see what line of code is executed in sequence. The first machine code instruction executed is 8190 which disassembled turns into a load instruction `LD x1, 90`. The 5th machine code instruction is 6000 which causes a branch to the start of the program. That is why you see the 1st machine code instruction over again. When there is no more input or a `HLT` instruction is hit the simulator will write out the state of the virtual CPU. You can see the contents of its program counter (PC) and registers (x1 to x9). x0 is not in use. x0 will always be 0. You will see inputs and outputs as well. Inputs are added as pairs, thus output is the result of 2+3, 8+4 which equals 5, 12. We can also disassemble machine code. When you write assembly code you will use labels and pseudo instructions such as `INP`, `OUT`, `CLR` and `DEC`. When you disassemble you will instead see what these pseudo instructions map to. /disassemble testdata/adder.machine 0: 8190; LD x1, 90 1: 8290; LD x2, 90 2: 1112; ADD x1, x1, x2 3: 9191; ST x1, 91 4: 6000; BRZ x0, 0 Notice how the `INP` instruction maps to a load, `LD`, instruction which loads from memory address 90. The always branch instruction `BRA` actually maps to a conditional instruction, `BRZ`, which checks register `x0`, which is hardwired to always be zero. ## Using Debugger The debugger can be run with the command `debug`. Keep in mind that this debugger is very bare bones. It can only step individual instructions and show content of registers, inputs and outputs. You can launch debugger with an assembled file like this: debugger adder.machine Once in the debugger you can give it single letter commands. You can get an overview of these commands by issuing the help command `h`: debug> h n: next x1 - x9: register value i: input p: print h: help q: quit You use the `i` command to set input numbers and `n` to step one instruction at the time. To check the state of our virtual CPU use the print, `p`, command. Here is an example of a debug sessions: debugger adder.machine debug> i 2 3 4 8 debug> p PC: 0 x0: 0, x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, Inputs: 2, 3, 4, 8, Output: debug> n 0: 8190; LD x1, 90 debug> n 1: 8290; LD x2, 90 debug> p PC: 2 x0: 0, x1: 2, x2: 3, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, Inputs: 4, 8, Output: You can check the value of any register by just writing its name. Thus writing `x3` will print the value of register `x3`. ## Remarks on Difference from Julia Implementation Julia is a high level language and Zig is a low level language, which tend to force a different way of thinking about the problem. In Julia working is text strings is very convenient and easy. In Zig it is often far more verbose to use Zig in a Julia fashion because that involves doing a lot of operations which allocate new memory. E.g. if you want to uppercase a whole string, you need to actually allocate new memory for this new uppercase string. Functional style programming on collections don't work as well in Zig naturally. So collecting some input from a file and storing in an array is much less practical. In Zig you are more likely to use iterators, as they allow you to avoid memory allocations. Thus I am using integer value instead of string to represent the instructions, adding operands is then just a matter of integer operations. Because I am treating every word on a line by iteration it was more practical to introduce a variable to maintain state in my Zig solution. Thus a switch-case statement allows me to do different things depending on what I determined the previous word was. E.g. was it a label, a mnemonic or operand. My Julia solution as almost no error handling, but I notice that my Zig solution naturally gravitates towards more error handling. That is a natural outcome of the design of the language which push you towards handling errors. ## Challenges in Implementation Script language style implementation and thinking don't work well in Zig, you got to think more like a C programmer which take me a bit time getting used to after a long time with Julia. - When an if-statement needs curly braces and not is often not obvious to me. There seems to be some difference between the usage of expressions and statements. - Likewise switch statement can be used as a statement or an expression. When used as an expression you need a semicolon at the end. - Wasted a whole bunch of time trying to figure out if I could iterate over enum values. It does not seem like you can, but then again neither can you in C or C++ either. - Passing around `Reader` objects to functions doesn't work well. The type is specific to the object it belongs to. I reverted to taking an `fs.File` object as argument instead. - Wasted time trying to figure out if there was something akin to `-1` like in Python or `end` like in Julia to refer to the last element in a slice. There isn't. Use `slice.len` if you need it. It is simply a struct member. - Spent a lot of time looking through the Zig source code to understand how `std.StringHashMap` deals with its string keys. Are they automatically deleted when `deinit()` is called? No Zig does not duplicate strings used as keys. It is your responsibility to deallocate the strings keys. In retrospect this makes sense. Zig is staying low level and doing minimal conveniences for you. That is sort of the point. There is no RAII in Zig, so just deleting stuff automatically would not have been a good idea anyway. ## Future Work I don't have an intention of developing this assembler in Zig further. My main motivation was to write a Zig program to explore the capabilities for the language. However, the Calcutron-33 assembler and simulator will most likely evolve further except using a different implementation language. I don't intend to make large changes to the current model. Possibly there will be more pseudo instructions and I ponder dealing with negative numbers in a different way. A CPU doesn't really have negative numbers. Instead one works with complement values. Hence 99 might represent -1, while 98 might represent -2 if we assume 100 is the highest value. Of course I have no problems with anyone else putting in the effort to polish this software more. The debugger could be made much better and the simulator could have more options.

近期下载者

相关文件


收藏者