Getting started¶
Installing¶
The easy way is to use package:
pip install ducky
Or, you can install Ducky by checking out the sources:
git clone https://github.com/happz/ducky.git
cd ducky
python setup.py
After this, you should have the ducky
module on your path:
>>> import ducky
>>> ducky.__version__
'2.0'
Prerequisites¶
Ducky runs with both Python 2 and 3 - supported versions are 2.7, 3.3, 3.4 and 3.5. There are few other dependencies, installation process (or setup.py
) should take care of them autmatically.
“Hello, world!” tutorial¶
Let’s try out the “Hello, world!” example. It’s a simple program that just prints out the well-known message.
Source code¶
Source is located in examples
directory. If you check it out, it’s a plain and simple assembler:
.include "tty.asm"
.data
.type stack, space
.space 64
.type message, string
.string "Hello, world!"
.text
main:
la sp, &stack
add sp, 64
la r0, &message
call &writesn
hlt 0x00
outb:
; > r0: port
; > r1: byte
outb r0, r1
ret
writesn:
; > r0: string address
; ...
; r0: port
; r1: current byte
; r2: string ptr
push r1
push r2
mov r2, r0
li r0, $TTY_PORT_DATA
.__writesn_loop:
lb r1, r2
bz &.__writesn_write_nl
call &outb
inc r2
j &.__writesn_loop
.__writesn_write_nl:
; \n
li r1, 0x0000000A
call &outb
; \r
li r1, 0x0000000D
call &outb
li r0, 0x00000000
pop r2
pop r1
ret
It’s a little bit more structured that necessary, just for educational purposes.
Binary¶
Virtual machine needs binary (or bytecode, as you wish...) code, and there’s a tool for it:
ducky-as -i examples/hello-world/hello-world.asm -o examples/hello-world/hello-world.o
This command will translate source code to object file, containing instructions for VM and other resources. You can inspect the object file using objdump
tool:
ducky-objdump -i examples/hello-world/hello-world.o -a
This should produce output similar to this one:
[INFO] Input file: examples/hello-world.bin
[INFO]
[INFO] === File header ===
[INFO] Magic: 0xDEAD
[INFO] Version: 1
[INFO] Sections: 4
[INFO]
[INFO] === Sections ===
[INFO]
[INFO] Index Name Type Flags Base Items Size Offset
[INFO] ------- -------- ------- ----------- -------- ------- ------ --------
[INFO] 0 .data DATA RW-- (0x03) 0x000000 14 14 104
[INFO] 1 .text TEXT RWX- (0x07) 0x000100 24 96 118
[INFO] 2 .symtab SYMBOLS ---- (0x00) 0x000200 6 120 214
[INFO] 3 .strings STRINGS ---- (0x00) 0x000000 0 122 334
[INFO]
[INFO] === Symbols ===
[INFO]
[INFO] Name Section Address Type Size File Line Content
[INFO] ---------------------- --------- --------- ------------ ------ ------------------------ ------ ---------------
[INFO] message .data 0x000000 string (2) 14 examples/hello-world.asm 1 "Hello, world!"
[INFO] main .text 0x000100 function (3) 0 examples/hello-world.asm 4
[INFO] outb .text 0x000110 function (3) 0 examples/hello-world.asm 10
[INFO] writesn .text 0x000118 function (3) 0 examples/hello-world.asm 16
[INFO] .__fn_writesn_loop .text 0x00012C function (3) 0 examples/hello-world.asm 27
[INFO] .__fn_writesn_write_nl .text 0x000140 function (3) 0 examples/hello-world.asm 33
[INFO]
[INFO] === Disassemble ==
[INFO]
[INFO] Section .text
[INFO] 0x000100 (0x00000004) li r0, 0x0000
[INFO] 0x000104 (0x0000800D) call 0x0010
[INFO] 0x000108 (0x00000004) li r0, 0x0000
[INFO] 0x00010C (0x0000000B) int 0x0000
[INFO] 0x000110 (0x000000E3) outb r0, r1
[INFO] 0x000114 (0x0000000E) ret
[INFO] 0x000118 (0x000000D4) push r1
[INFO] 0x00011C (0x00000154) push r2
[INFO] 0x000120 (0x00000054) push r0
[INFO] 0x000124 (0x00000095) pop r2
[INFO] 0x000128 (0x00040004) li r0, 0x0100
[INFO] 0x00012C (0x00000842) lb r1, r2
[INFO] 0x000130 (0x00006029) bz 0x000C
[INFO] 0x000134 (0x0FFEC00D) call -0x0028
[INFO] 0x000138 (0x00000096) inc r2
[INFO] 0x00013C (0x0FFF6026) j -0x0014
[INFO] 0x000140 (0x00002844) li r1, 0x000A
[INFO] 0x000144 (0x0FFE400D) call -0x0038
[INFO] 0x000148 (0x00003444) li r1, 0x000D
[INFO] 0x00014C (0x0FFE000D) call -0x0040
[INFO] 0x000150 (0x00000004) li r0, 0x0000
[INFO] 0x000154 (0x00000095) pop r2
[INFO] 0x000158 (0x00000055) pop r1
[INFO] 0x00015C (0x0000000E) ret
[INFO]
You can see internal sections in the object file, list of symbols, and disassembled instructions, with labels replaced by dummy offsets. Offsets in jump instructions make no sense yet because object file is not the finalized binary - yet. For that, there’s another tool:
ducky-ld -i examples/hello-world/hello-world.o -o examples/hello-world/hello-world
This command will take object file (or many of them), and produce one binary by merging code, data and sections in object files, and updates addresses used by instructions to retrieve data and to perform jumps. You can inspect the binary file using objdump
tool, too:
ducky-objdump -i examples/hello-world/hello-world -a
This should produce output very similar to the one you’ve already seen - not much had changed, there was only one object files, only offsets used by call
and j
instructions are now non-zero, meaning they are now pointing to the correct locations.
Running¶
Virtual machine configuration can get quite complicated, so I try to avoid too many command line options, and opt for using configuration files. For this example, there’s one already prepared. Go ahead and try it:
ducky-vm --machine-config=examples/hello-world/hello-world.conf -g
There are two other command line options that deserve some explanation:
-g
- by default, VM prepares itself, and waits for user to pressEnter
to actually start running the loaded binaries. This option tells it to skip “press any key” phase and go ahead.
You should get output similar to this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 1441740855.82 [INFO] Ducky VM, version 1.0
1441740855.82 [INFO] mm: 16384.0KiB, 16383.5KiB available
1441740855.82 [INFO] hid: basic keyboard controller on [0x0100] as device-1
1441740855.83 [INFO] hid: basic tty on [0x0200] as device-2
1441740855.83 [INFO] hid: basic terminal (device-1, device-2)
1441740855.83 [INFO] snapshot: storage ready, backed by file ducky-snapshot.bin
1441740855.83 [INFO] RTC: time 21:34:15, date: 08/09/15
1441740855.83 [INFO] irq: loading routines from file interrupts
1441740856.02 [INFO] binary: loading from from file examples/hello-world/hello-world
1441740856.02 [INFO] #0:#0: CPU core is up
1441740856.02 [INFO] #0:#0: check-frames: yes
1441740856.02 [INFO] #0:#0: coprocessor: math
1441740856.02 [INFO] #0: CPU is up
Hello, world!
1441740856.04 [INFO] #0:#0: CPU core halted
1441740856.05 [INFO] #0: CPU halted
1441740856.05 [INFO] snapshot: saved in file ducky-snapshot.bin
1441740856.05 [INFO] Halted.
1441740856.05 [INFO]
1441740856.05 [INFO] Exit codes
1441740856.05 [INFO] Core Exit code
1441740856.06 [INFO] ------ -----------
1441740856.06 [INFO] #0:#0 0
1441740856.06 [INFO]
1441740856.06 [INFO] Instruction caches
1441740856.06 [INFO] Core Reads Inserts Hits Misses Prunes
1441740856.06 [INFO] ------ ------- --------- ------ -------- --------
1441740856.06 [INFO] #0:#0 133 34 99 34 0
1441740856.06 [INFO]
1441740856.06 [INFO] Core Ticks
1441740856.06 [INFO] ------ -------
1441740856.06 [INFO] #0:#0 133
1441740856.06 [INFO]
1441740856.06 [INFO] Executed instructions: 133 0.028670 (4639.0223/sec)
1441740856.06 [INFO]
|
And there, on line 15, between all that funny nonsenses, it is! :) The rest of the output are just various notes about loaded binaries, CPU caches, nothing important right now - as I said, terminal is dedicated to VM itself.