Welcome to Ducky’s documentation!

Ducky is a simple virtual CPU/machine simulator, with modular design and interesting features.

About

ducky

Ducky is a simple virtual CPU/machine simulator, with modular design and interesting features.

https://readthedocs.org/projects/ducky/badge/?version=latest https://api.codacy.com/project/badge/23fdf5e716e64cddadb42e9ae672dbbc Issue Count https://coveralls.io/repos/happz/ducky/badge.svg?branch=master&service=github

Ducky was created for learning purposes, no bigger ambitions. The goal was to experiment with CPU and virtual machine simulation, different instruction sets, and later working FORTH kernel become one of the main goals.

Features

Ducky - as in “Ducky, the CPU” - is a 32-bit RISC CPU. Ducky, “the VM”, is a simulator of Ducky CPU, adding few other modules to create the whole virtual machine, with CPUs, peripherals, storages and other components.

RISC instruction set

Instruction set was inspired by RISC CPUs, and sticks to LOAD/STORE aproach, with fixed-width instructions.

Memory model

Flat, paged, with linear addressing. Main memory consists of memory pages, each page supports simple access control - simple MMU is implemented.

Modular architecture

Virtual machine consists of several modules of different classes, and only few of them are necessary (e.g. CPU). Various peripherals are available, and it’s extremely easy to develop your own and plug them in.

SMP support

Multiple CPUs with multiple cores per each CPU, with shared memory. Each core can be restricted to its own segment of memory.

Persistent storage

Modular persistent storages are available, and accessible by block IO operations, or by mmap-ing storages directly into memory.

Bytecode files

Compiled programs are stored in bytecode files that are inspired by ELF executable format. These files consist of common sections (.text, .data, ...), symbols, and their content. Assembler (ducky-as) translates assembler sources into object files, and these are then processed by a linker (ducky-ld) into the final executable. Both object and executable files use the same format and bytecode for instructions and data.

Snapshots

Virtual machine can be suspended, saved, and later restored. This is also useful for debugging purposes, every bit of memory and CPU registers can be investigated.

Debugging support

Basic support is included - break points, watch points, stack traces, stepping, snapshots, ...

Tools
  • as for translating assembler sources to bytecode files
  • ld for linking bytecode files into the final executable
  • objdump for inspection of bytecode files
  • coredump for inspection of snapshots
  • vm for running virtual machine itself
  • img for converting binaries to images
  • and cc, an experimental C compiler
Planned features
  • FORTH kernel - basic functionality but at least ANS compliant
  • network support - it would be awesome to have a network stack available for running programs
  • functioning C compiler, with simple C library
  • and few others...

Need help?

The whole development is tracked on a GitHub page, including source codes and issue tracker.

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 press Enter 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.

Virtual hardware

CPU

Ducky VM can have multiple CPUs, each with multiple cores. Each core is a 32-bit microprocessor, with 32 32-bit registers, connected to main memory. It is equiped with MMU, and its own instruction cache.

CPU core can work in privileged and unprivileged modes, allowing use of several protected instructions in privileged mode.

Registers
  • 32 32-bit registers - registers r0 to r28 are general purpose registers - r30 is reserved, and used as a stack pointer register, SP - contains address of the last push’ed value on stack - r29 is reserved, and used as a frame pointer register, FP - contains content of SP in time of the last call or int instruction - r31 is reserved, and used as a instruction pointer register, IP - contains address of next instruction to be executed
  • flags register

IP and flags registers are protected, and cannot be modified by standard means (push flags; <modify flags>; pop flags) when CPU is in user mode

Flags register
Mask Flags Usage
0x00 privileged If set, CPU runs in privileged mode, and usage of protected instructions is allowed
0x01 hwint_allowed If set, HW interrupts can be delivered to this core
0x04 e Set if the last two compared registers were equal
0x08 z Set if the last arithmetic operation produced zero
0x10 o Set if the last arithmetic operation overflown
0x20 s Set if the last arithmetic operation produced negative result
Instruction set

CPU supports multiple instruction set. The default one, ducky, is the main workhorse, suited for general coding, but other instruction sets can exist, e.g. coprocessor may use its own instruction set for its operations.

Design principles
  • load and store operations are performed by dedicated instructions
  • all memory-register transfers work with 32-bit operands, 16-bit and 8-bit operands are handled by special instructions when necessary
  • all memory-register transfers work with addresses that are aligned to the size of their operands (1 byte alignment - so no alignment at all - for 8-bit operands)
  • in most cases, destination operand is the first one. Exceptions are instructions that work with IO ports.
  • when content of a register is changed by an instruction, several flags can be modified subsequently. E.g. when new value of register is zero, z flag is set.
Notes on documentation
  • rN refers to generaly any register, from r0 up to r28 - special registers are refered to by their common names (e.g. SP).
  • rA, rB refer to the first and the second instruction operand respectively and stand for any register.
  • <value> means immediate, absolute value. This covers both integers, specified as base 10 or base 16 integers, both positive and negative, and labels and addresses, specified as &label
  • when instruction accepts more than one operand type, it is documented using | character, e.g. (rA|<value>) means either register or immediate value
Ducky instruction set
Design principles
  • basic data unit is a word, 4 bytes, 32 bits. Other units are short and byte. Instructions often have variants for different data units, distinguished by a suffix (w for words, s for shorts, and b for single bytes)
  • load and store operations are performed by dedicated instructions
  • memory-register transfers work with addresses that are aligned to the size of their operands (1 byte alignment - so no alignment at all - for byte operands)
  • in most cases, destination operand is the first one. Exceptions are instructions that work with IO ports.
  • when content of a register is changed by instruction, several flags can be modified subsequently. E.g. when new value of register is zero, z flag is set.
Notes on documentation
  • rN refers to generaly any register, from r0 up to r28 - special registers are refered to by their common names (e.g. SP).
  • rA, rB refer to the first and the second instruction operand respectively and stand for any register.
  • <value> means immediate, absolute value. This covers both integers (specified as base 10 or base 16 integers, both positive and negative), and labels and addresses, specified as &label
  • when instruction accepts more than one operand type, it is documented using | character, e.g. (rA|<value>) means either register or immediate value
Stack frames

Several instructions transfer control to other routines, with possibility of returning back to previous spot. It is done by creating a stack frame. When stack frame is created, CPU performs these steps:

  • IP is pushed onto the stack
  • FP is pushed onto the stack
  • FP is loaded with value of SP

Destroying stack frame - reverting the steps above - effectively transfers control back to the point where the subroutine was called from.

Arithmetic

All arithmetic instructions take at least one operand, a register. In case of binary operations, the second operand can be a register, or an immediate value (15 bits wide, sign-extended to 32 bits). The result is always stored in the first operand.

add rA, (rB|<value>)

dec rA

inc rA

mul rA, (rB|<value>)

sub rA, (rB|<value>)

Bitwise operations

All bitwise operations - with exception of not - take two operands, a register, and either another register or an immediate value (15 bits wide, sign-extended to 32 bits). The result if always stored in the first operand.

and rA, (rB|<value>)

not rA

or rA, (rB|<value>)

shiftl rA, (rB|<value>)

shiftr rA, (rB|<value>)

xor rA, (rB|<values>)

Branching instructions

Branching instructions come in form <inst> (rA|<address>). If certain conditions are met, branching instruction will perform jump by adding value of the operand to the current value of PC (which, when instruction is being executed, points to the next instruction already). If the operand is an immediate address, it is encoded in the instruction as an immediate value (16 bit wide, sign-extended to 32 bits). This limits range of addresses that can be reached using this form of branching instructions.

Branching instructions do not create new stack frame.

Unconditional branching

j (rA|<value>)

Conditional branching
Instruction Jump when ...
be bne bs bns bz bnz bo bno bg bge bl ble e = 1 e = 0 s = 1 s = 0 z = 1 z = 0 o = 1 o = 0 e = 0 and s = 0 e = 1 or s = 0 e = 0 and s = 1 e = 1 or s = 1
Conditional setting

All conditional setting instructions come in form <inst> rA. Depending on relevant flags, rA is set to 1 if condition is evaluated to be true, or to 0 otherwise.

For flags relevant for each instruction, see branching instruction with the same suffix (e.g. setle evaluates the same flags with the same result as ble).

Instruction
sete setne setz setnz seto setno sets setns setg setge setl setle
Comparing

Two instructions are available for comparing of values. Compare their operands and sets corresponding flags. The second operand can be either a register or an immediate value (15 bits wide).

cmp rA, (rB|<value>) - immediate value is sign-extended to 32 bits.

cmpu rA, (rB|<value>) - treat operands as unsigned values, immediate value is zero-extended to 32 bits.

Port IO

All IO instructions take two operands: port number, specified by register or immediate value, and register.

inw (rA|<port>), rB - read word from port and store it in rB

ins (rA|<port>), rB - read short from port and store it in rB

inb (rA|<port>), rB - read byte from port and store it in rB

outw (rA|<port>), rB - write value from rB to port

outs (rA|<port>), rB - write lower short of rB to port

outb (rA|<port>), rB - write lowest byte of rB to port

Interrupts
Delivery

If flag hwint_allowed is unset, no hardware IRQ can be accepted by CPU and stays queued. All queued IRQs will be delivered as soon as flag is set.

cli - clear hwint flag

sti - set hwint flag

In need of waiting for external events it is possible to suspend CPU until the next IRQ is delivered.

idle - wait until next IRQ

Invocation

Any interrupt service routine can be invoked by means of special instruction. When invoked several events take place:

  • SP is saved in temporary space
  • IP and SP are set to values that are stored in IVT in the corresponding entry
  • important registers are pushed onto new stack (in this order): old SP, flags
  • new stack frame is created
  • privileged mode is enabled

When routine ends (via retint), these steps are undone, and content of saved registers is restored.

int (rA|<index>)

retint - return from interrupt routine

Inter-processor interrupts (IPI) can be delivered to other processors, via dedicated instruction, similar to int but specifying CPUID of target core in the first operand.

ipi rA, (rB|<index>)

Routines

When routine is called, new stack frame is created, and CPU continues with instructions pointed to by the first operand. For its meaning (and limitations) see Branching instructions.

call (rA|<address>)

ret

Stack

pop rA

push (rA|<value>)

Miscellaneous

nop - do absolutely nothing

hlt (rA|<value>) - Halt CPU and set its exit code to specified value.

rst - reset CPU state. All flags cleared, privileged = 1, hwint_allowed = 0, all registers set to 0

mov rA, rB - copy value of rB into rA

swp rA, rB - swap content of two registers

sis <value> - switch instruction set to a different one

Memory access

Address operand - {address} - can be specified in different ways:

  • rA - address is stored in register
  • rA[<offset>] - address is computed by addition of rA and offset. offset can be both positive and negative. fp and sp can be also used as rA. <offset> is an immediate value, 15 bits wide, sign-extended to 32 bits.
Read

lw rA, {address} - load word from memory

ls rA, {address} - load short from memory

lb rA, {address} - load byte from memory

Write

stw {address}, rA

stb {addres}, rA - store lower byte of rA

Constants

Instructions for filling registers with values known in compile time.

li rA, <constant> - load constant into register. constant is encoded into instruction as an immediate value (20 bits wide, sign-extended to 32 bits)

liu rA, <constant> - load constant into the upper half of register. constant is encoded into instruction as an immediate value (20 bits wide immediate, only lower 16 bits are used)

la rA, <constant> - load constant into the register. constant is an immediate value (20 bits wide, sign-extended to 32 bits), and is treated as an offset from the current value of PC - register is loaded with the result of PC + constant.

Compare-and-swap

cas rA, rB, rC - read word from address in register rA. Compare it with value in register rB - if both are equal, take content of rC and store it in memory on address rA, else store memory value in rB.

Math coprocessor instruction set

This page describes instruction set of math coprocessor.

Manipulating registers

Instructions for moving values between coprocessor stack and memory or CPU registers.

itol

utol

ltoi

ltoii

iitol

dupl

Arithmetic operations

incl

decl

addl

subl

mull

divl

modl

symdivl

symmodl

Memory

Memory model
  • the full addressable memory is 4 GB, but it is quite unrealistic expectation. I usually stick to 24-bits for addresses, which leads to 16MB of main memory
  • memory is organized into pages of 256 bytes - each page can be restricted for read, write and execute operations
Memory layout
Interrupt Vector table

Interrupt vector table (IVT), located in main memory, is by default located at address 0x00000000. IVT address can be set per CPU core. IVT is 256 bytes long, providing enough space for 64 entries. Typically, lower 32 entries are reserved for hardware interrupts, provided by devices, and upper 32 entries leads to software routines that provide additional functionality for binaries. Each entry has the same layout:

IP - 32 bits SP - 32 bits

When CPU is interrupted - by hardware (device generates interrupt) or software (program executes int instruction) interrupt - corresponding entry is located in IVT, using interrupt ID as an index.

Stack
  • standard LIFO data structure
  • grows from higher addresses to lower
  • there is no pre-allocated stack, every bit of code needs to prepare its own if it intends to use instructions that operate with stack
  • when push’ing value to stack, SP is decreased by 4 (size of general register), and then value is stored on this address
  • each IVT provides its own stack pointer

Software

Calling convention

I use very simple calling convention in my code:

  • all arguments are in registers
  • first argument in r0, second in r1, ... You get the picture.
  • if there’s too many arguments, refactor your code or use a stack...
  • return value is in r0
  • callee is reponsible for save/restore of registers it’s using, with exception of:
    • registers that were used for passing arguments - these are expected to have undefined value when callee returns
    • r0 if callee returns value back to caller

All virtual interrupt routines, assembler code, any pieces of software I’ve written for this virtual machine follows this calling convention - unless stated otherwise...

Software interrupts

Software interrupts provide access to library of common functions, and - in case of virtual interrupts - to internal, complex and otherwise inaccessible resources of virtual machine itself.

For the list for existing interrupts and their numbers, see ducky.irq.IRQList. However, by the nature of invoking a software interrupt, this list is not carved into a stone. You may easily provide your own IVT, with entries leading to your own routines, and use e.g. the 33th entry, HALT, to sing a song.

All values are defined in files in defs/ directory which you can - and should - include into your assembler sources.

BLOCKIO
IVT entry 33
    Read mode Write mode
Parameters r0 device id
r1 bit #0: 0 for read, 1 for write bit #1: 0 for synchronous, 1 for asynchronous operation
r2 block id src memory address
r3 dst memory address block id
r4 number of blocks
Returns r0 0 for success

Perform block IO operation - transfer block between memory and storage device. Use the lowest bit of r1 to specify direction:

  • 0 - read mode, blocks are transfered from storage into memory
  • 1 - write mode, blocks are tranfered from memory to storage

Current data segment is used for addressing memory locations.

If everything went fine, 0 is returned, any other value means error happened.

IO operation is a blocking action, interrupt will return back to the caller once the IO is finished. Non-blocking (DMA-like) mode is planned but not yet implemented.

This operation is implemented as a virtual interrupt, see ducky.blockio.BlockIOInterrupt for details.

VMDEBUG
IVT entry 34
    QUIET mode
Parameters r0 0
r1 0 for quiet mode, anything else for full mode
Returns r0 0 for success

VM interrupt allows control of VM debugging output. Currently, only two levels of verbosity that are available are quiet and full mode. In quiet mode, VM produces no logging output at all.

This interrupt can control level amount of debugging output in case when developer is interested only in debugging only a specific part of his code. Run VM with debugging turned on (-d option), turn the debugging off at the beggining of the code, and turn it on again at the beggining of the interesting part to get detailed output.

Tools

Ducky comes with basic toolchain necessary for development of complex programs. One piece missing is the C cross-compiler with simple C library but this issue will be resolved one day.

Common options

All tools accept few common options:

-q, --quiet

Lower verbosity level by one. By default, it is set to info, more quiet levels are warnings, `error and critical.

-v, --verbose

Increase verbosity level by one. By default, it is set to info, more verbose levels is debug. debug level is not available for ducky-vm unless -d option is set.

-d, --debug

Set logging level to debug immediately. ducky-vm also requires this option to even provide any debugging output - it is not possible to emit debug output by setting -v enough times if -d is not specified on command-line.

When option takes an address as an argument, address can be specified either using decimal or hexadecimal base. Usually, the absolute address is necessary when option is not binary-aware, yet some options are tied closely to particular binary, such options can also accept name of a symbol. Option handling code will try to find corresponding address in binary’s symbol table. This is valid for both command-line options and configuration files.

as

Assembler. Translates assembler files (.asm) to object files (.o) - files containing bytecode, symbols information, etc.

Options
-i FILE

Take assembly FILE, and create an object file from its content. It can be specified multiple times, each input file will be processed.

-o FILE

Write resulting object data into FILE.

This options is optional. If no -o is specified, ducky-as will then create output file for each input one by replacing its suffix by .o. If it is specified, number of -o options must match the number of -i options.

-f

When output file exists already, ducky-as will refuse to overwrite it, unless -f is set.

-D VAR

Define name, passed to processed assembly sources. User can check for its existence in source by .ifdef/.ifndef directives.

-I DIR

Add DIR to list of directories that are searched for files, when .include directive asks assembler to process additional source file.

-m, --mmapable-sections

Create object file with sections that can be loaded using mmap() syscall. This option can save time during VMstartup, when binaries can be simply mmapped into VM’s memory space, but it also creates larger binary files because of the alignment of sections in file.

-w, --writable-sections

By default, .text and .rodata sections are read-only. This option lowers this restriction, allowing binary to e.g. modify its own code.

ld

Linker. Takes (one or multiple) object files (.o) and merges them into one, binary, which can be executed by VM.

Options
-i FILE

Take object FILE, and create binary file out of it. It can be specified multiple times, all input files will be processed into one binary file.

-o FILE

Output file.

-f

When output file exists already, ducky-ld will refuse to overwrite it, unless -f is set.

--section-base=SECTION=ADDRESS

Linker tries to merge all sections into a binary in a semi-random way - it can be influenced by order of sections in source and object files, and order of input files passed to linker. It is in fact implementation detail and can change in the future. If you need specific section to have its base set to known address, use this option. Be aware that linker may run out of space if you pass conflicting values, or force sections to create too small gaps between each other so other sections would not fit in.

coredump

Prints information stored in a saved VM snapshot.

objdump

Prints information about object and binary files.

profile

Prints information stored in profiling data, created by VM. Used for profiling running binaries.

vm

Stand-alone virtual machine - takes binary, configuration files, and other resources, and executes binaries.

VM Configuration file

Number of available options can easily get quite high, especially when different devices come into play, and setting all of them on command line is not very clear. To ease this part of VM processes, user can create a configuration file. Syntax is based on Python’s ConfigParser (or Windows .ini) configuration files. It consists of sections, setting options for different subsystems (VM, CPUs, ...). You can find few configuration files in examples/ directory.

When option takes an address as an argument, address can be specified either using decimal or hexadecimal base. Usually, the absolute address is necessary when option is not binary-aware, yet some options are tied closely to particular binary, such options can also accept name of a symbol. Option handling code will try to find corresponding address in binary’s symbol table. This is valid for both command-line options and configuration files.

[machine]
cpus

Number of separate CPUs.

int, default 1

cores

Number of cores per CPU.

int, default 1

[memory]
size

Memory size in bytes.

int, default 0x1000000

force-aligned-access

When set, unaligned memory access will lead to exception.

bool, default yes

[cpu]
math-coprocessor

When set, each CPU core will have its own math coprocessor.

bool, default no

control-coprocessor

When set, each CPU core will have its own control coprocessor.

bool, default yes

inst-cache

Number of slots in instruction cache.

int, default 256

check-frame

When set, CPU cores will check if stack frames were cleaned properly when ret or iret is executed.

bool, default yes

ivt-address

Address of interrupt vector table.

int, default 0x000000

[bootloader]
file

Path to bootloader file.

str, required

[device-N]

Each section starting with device- tells VM to create virtual device, and provide it to running binaries. Device sections have few options, common for all kinds of devices, and a set of options, specific for each different device or driver.

klass

Device class - arbitrary string, describing family of devices. E.g. I use input for devices processing user input (e.g. keyboard controllers).

str, required

driver

Python class that is the device driver.

str, required

master

If set, master is superior device, with some responsibilities over its subordinates.

str, optional

Options
--machine-config=PATH

Specify PATH to VM configuration file. For its content, see VM Configuration file.

--set-option=SECTION:OPTION=VALUE
--add-option=SECTION:OPTION=VALUE

These two options allow user to modify the content of configuration file, by adding of new options or by changing the existing ones.

Lets have (incomplete) config file vm.conf:

[machine]
cpu = 1
core = 1

[binary-0]

You can use it to run different binaries without having separate config file for each of them, just by telling ducky-vm to load configuration file, and then change one option:

$ ducky-vm --machine-config=vm.conf --add-option=binary-0:file=<path to binary of your choice>

Similarly, you can modify existing options. Lets have (incomplete) config file vm.conf:

[machine]
cpus = 1
cores = 1

[binary-0]
file = some/terminal/app

[device-1]
klass = input
driver = ducky.devices.keyboard.KeyboardController
master = device-3

[device-2]
klass = output
driver = ducky.devices.tty.TTY
master = device-3

[device-3]
klass = terminal
driver = ducky.devices.terminal.StandardIOTerminal
input = device-1
output = device-2

Your app will run using VM’s standard IO streams for input and out. But you may want to start it with a different kind of terminal, e.g. PTY one, and attach to it using screen:

$ ducky-vm --machine-config=vm.conf --set-option=device-3:driver=ducky.devices.terminal.StandalonePTYTerminal
--enable-device=DEVICE
--disable-device=DEVICE

Shortcuts for --set-option=DEVICE:enabled=yes and --set-option=DEVICE:enabled=no respectively.

--poke=ADDRESS:VALUE:LENGTH

poke option allows modification of VM’s memory after all binaries and resources are loaded, just before the VM starts execution of binaries. It can be used for setting runtime-specific values, e.g. argument for a binary.

Consider a simple binary, running a loop for specified number of iterations:

By default, 10 iterations are hard-coded into binary. If you want to termporarily change number of iterations, it’s not necessary to recompile binary. By default, this binary, being the only one running, would get segment 0x02, it’s .data section was mentioned first, therefore its base address will be 0x0000, leading to loops having absolute address 0x020000. Then:

$ ducky-vm --machine-config=vm.conf --poke=0x020000:100:2

will load binary, then modify its .data section by changing value at address 0x020000 to 100, which is new number of iterations. Meta variable LENGTH specifies number of bytes to overwrite by poke value, and poke will change exactly LENGTH bytes - if VALUE cannot fit into available bits, exceeding bits of VALUE are masked out, and VALUE that can fit is zero-extended to use all LENGTH bytes.

--stdio-console

Enable console terminal with stdin and stdout as its IO streams. User can then enter commands in via the keyboard, while VM and its binaries use e.g. stand-alone pty terminals.

-g, --go-on

By default, ducky-vm creates a VM and boots it, but before handing the constrol to it, ducky-vm will ask user to press any key. -g option tells ducky-vm to skip this part, and immediately start execution of binaries.

img

Converts binaries to binary images that can be loaded by boot loader.

Options
-i FILE

Take binary FILE, and create a binary image from its content.

-o FILE

Write resulting object data into FILE.

-f

When output file exists already, ducky-img will refuse to overwrite it, unless -f is set.

Examples

“Hello, world!”

“Hello, world!” using library

VGA

Clock

Code Documentation

ducky.boot module

This file provides necessary code to allow boot up of a virtual machine with the correct program running. This code can provide slightly different environment when compared to real hardware process, since e.g. external files can be mmap-ed into VM’s memory for writing.

ducky.boot.DEFAULT_BOOTLOADER_ADDRESS = 131072

By default, CPU starts executing instructions at this address after boot.

ducky.boot.DEFAULT_HDT_ADDRESS = 256

By default, Hardware Description Table starts at this address after boot.

class ducky.boot.MMapArea(ptr, address, size, file_path, offset, pages_start, pages_cnt, flags)[source]

Bases: object

Objects of this class represent one mmaped memory area each, to track this information for later use.

Parameters:
  • ptrmmap object, as returned by mmap.mmap function.
  • address (u32_t) – address of the first byte of an area in the memory.
  • size (u32_t) – length of the area, in bytes.
  • file_path – path to a source file.
  • offset (u32_t) – offset of the first byte in the source file.
  • pages_start (int) – first page of the area.
  • pages_cnt (int) – number of pages in the area.
  • flags (mm.binary.SectionFlags) – flags applied to this area.
load_state(state)[source]
save_state(parent)[source]
class ducky.boot.MMapAreaState[source]

Bases: ducky.snapshot.SnapshotNode

class ducky.boot.MMapMemoryPage(area, *args, **kwargs)[source]

Bases: ducky.mm.ExternalMemoryPage

Memory page backed by an external file that is accessible via mmap() call. It’s a part of one of mm.MMapArea instances, and if such area was opened as shared, every change in this page content will affect the content of external file, and vice versa, every change of external file will be reflected in content of this page (if this page lies in affected area).

Parameters:area (MMapArea) – area this page belongs to.
get(offset)[source]

Read one byte from page.

This is an abstract method, __init__ is expected to replace it with a method, tailored for the Python version used.

Parameters:offset (int) – offset of the requested byte.
Return type:int
put(offset, b)[source]

Write one byte to page.

This is an abstract method, __init__ is expected to replace it with a method, tailored for the Python version used.

Parameters:
  • offset (int) – offset of the modified byte.
  • b (int) – new value of the modified byte.
class ducky.boot.ROMLoader(machine)[source]

Bases: ducky.interfaces.IMachineWorker

boot()[source]
halt()[source]
load_data(base, content)[source]
load_text(base, content)[source]
mmap_area(file_path, address, size, offset=0, flags=None, shared=False)[source]

Assign set of memory pages to mirror external file, mapped into memory.

Parameters:
  • file_path (string) – path of external file, whose content new area should reflect.
  • address (u24) – address where new area should start.
  • size (u24) – length of area, in bytes.
  • offset (int) – starting point of the area in mmaped file.
  • flags (ducky.mm.binary.SectionFlags) – specifies required flags for mmaped pages.
  • shared (bool) – if True, content of external file is mmaped as shared, i.e. all changes are visible to all processes, not only to the current ducky virtual machine.
Returns:

newly created mmap area.

Return type:

ducky.mm.MMapArea

Raises:

ducky.errors.InvalidResourceError – when size is not multiply of ducky.mm.PAGE_SIZE, or when address is not multiply of ducky.mm.PAGE_SIZE, or when any of pages in the affected area is already allocated.

poke(address, value, length)[source]
setup_bootloader(filepath, base=None)[source]
setup_debugging()[source]
setup_hdt()[source]

Initialize memory area that contains HDT.

If VM config file specifies HDT image file, it is loaded, otherwise HDT is constructed for the actual configuration. It is then copied into memory.

Parameters:
  • machine.hdt-address (u32_t) – Base address of HDT in memory. If not set, ducky.boot.DEFAULT_HDT_ADDRESS is used.
  • machine.hdt-image – HDT image to load. If not set, HDT is constructed for the actual VM’s configuration.
setup_mmaps()[source]
unmmap_area(mmap_area)[source]

ducky.config module

VM configuration management

class ducky.config.MachineConfig(*args, **kwargs)[source]

Bases: ConfigParser.ConfigParser

Contains configuration of the whole VM, and provides methods for parsing, inspection and extending this configuration.

add_breakpoint(core, address, active=None, flip=None, ephemeral=None, countdown=None)[source]
add_device(klass, driver, **kwargs)[source]

Add another device to the configuration.

Parameters:
  • klass (string) – class of the device (klass option).
  • driver (string) – device driver - dot-separated path to class (driver option).
  • kwargs – all keyword arguments will be added to the section as device options.
add_mmap(filepath, address, size, offset=None, access=None, shared=None)[source]
add_storage(driver, sid, filepath=None)[source]

Add another storage to the configuration.

Parameters:
  • driver (string) – storage’s driver - dot-separated path to class (driver option).
  • sid (int) – storage’s SID (sid options).
  • filepath (string) – path to backend file, if there’s any (filepath option).
create_getters(section)[source]
dumps()[source]
get(section, option, default=None, **kwargs)[source]

Get value for an option.

Parameters:
  • section (string) – config section.
  • option (string) – option name,
  • default – this value will be returned, if no such option exists.
Return type:

string

Returns:

value of config option.

getbool(section, option, default=None)[source]
getfloat(section, option, default=None)[source]
getint(section, option, default=None)[source]
iter_breakpoints()[source]
iter_devices()[source]
iter_mmaps()[source]
iter_storages()[source]
read(*args, **kwargs)[source]
set(section, option, value, *args, **kwargs)[source]
ducky.config.bool2option(b)[source]

Get config-file-usable string representation of boolean value.

Parameters:b (bool) – value to convert.
Return type:string
Returns:yes if input is True, no otherwise.

ducky.console module

class ducky.console.ConsoleConnection(cid, master, stream_in, stream_out)[source]

Bases: object

boot()[source]
die(exc)[source]
execute(cmd)[source]
halt()[source]
log(logger, msg, *args)[source]
prompt()[source]
read_input()[source]
table(table, **kwargs)[source]
write(buff, *args)[source]
writeln(buff, *args)[source]
class ducky.console.ConsoleMaster(machine)[source]

Bases: object

boot()[source]
connect(slave)[source]
console_id = 0
halt()[source]
is_registered_command(name)[source]
register_command(name, callback, *args, **kwargs)[source]
register_commands(commands, *args, **kwargs)[source]
unregister_command(name)[source]
class ducky.console.TerminalConsoleConnection(cid, master)[source]

Bases: ducky.console.ConsoleConnection

halt()[source]
ducky.console.cmd_help(console, cmd)[source]

List all available command and their descriptions

ducky.cc package

Subpackages

ducky.cc.passes package
Submodules
ducky.cc.passes.ast_codegen module
class ducky.cc.passes.ast_codegen.CodegenVisitor(*args, **kwargs)[source]

Bases: ducky.cc.passes.ASTVisitor

block(stage=None, *args, **kwargs)[source]
emit_epilog()[source]
emit_prolog()[source]
emit_string_literals()[source]
emit_trampoline()[source]
generic_visit(node, **kwargs)[source]
get_new_label(name=None)[source]
get_new_literal_label()[source]
get_new_local_storage(size)[source]
make_current(block)[source]
materialize()[source]
pop_scope()[source]
priority = 1000
process_cond(node, iftrue_label=None, iffalse_label=None)[source]
push_scope()[source]
reset_scope()[source]
visit(node, **kwargs)[source]
visit_ArrayRef(node, preferred=None, keep=None)[source]
visit_Assignment(node, preferred=None, keep=None)[source]
visit_BinaryOp(node, preferred=None, keep=None)[source]
visit_Cast(node, **kwargs)[source]
visit_Compound(node, create_scope=True)[source]
visit_Constant(node, preferred=None, keep=None)[source]
visit_Decl(node)[source]
visit_ExprList(node)[source]
visit_FileAST(node)[source]
visit_For(node)[source]
visit_FuncCall(node, preferred=None, keep=None)[source]
visit_FuncDef(node)[source]
visit_ID(node, preferred=None, keep=None)[source]
visit_If(node)[source]
visit_Return(node)[source]
visit_StructRef(node, **kwargs)[source]
visit_TypeDecl(node, **kwargs)[source]
visit_Typedef(node)[source]
visit_Typename(node, **kwargs)[source]
visit_UnaryOp(node, preferred=None, keep=None)[source]
visit_While(node)[source]
visit_constant_value(node)[source]
visit_expr(node, preferred=None, keep=None)[source]
ducky.cc.passes.ast_constprop module
class ducky.cc.passes.ast_constprop.ConstantFoldingVisitor(logger, *args, **kwargs)[source]

Bases: ducky.cc.passes.ASTOptVisitor

priority = 100
visit_BinaryOp(node)[source]
ducky.cc.passes.ast_dce module
class ducky.cc.passes.ast_dce.DSEVisitor(logger, *args, **kwargs)[source]

Bases: ducky.cc.passes.ASTOptVisitor

priority = 500
visit_Compound(node)[source]
ducky.cc.passes.ast_visualise module
class ducky.cc.passes.ast_visualise.ASTVisualiseVisitor(*args, **kwargs)[source]

Bases: ducky.cc.passes.ASTVisitor

generic_visit(node, shape='box')[source]
get_index(node)[source]
visit_BinaryOp(node, shape='box')[source]
visit_Constant(node, shape='box')[source]
visit_FileAST(node)[source]
visit_ID(node, shape='box')[source]
visit_If(node)[source]
visit_UnaryOp(node, shape='box')[source]
visit_While(node)[source]
ducky.cc.passes.bt_peephole module
class ducky.cc.passes.bt_peephole.BTPeepholeVisitor(logger, *args, **kwargs)[source]

Bases: ducky.cc.passes.BlockVisitor

do_visit_block(block)[source]
priority = 100
ducky.cc.passes.bt_simplify module
class ducky.cc.passes.bt_simplify.BlockTreeSimplifyVisitor(logger, *args, **kwargs)[source]

Bases: ducky.cc.passes.BlockVisitor

do_visit_fn(fn)[source]
priority = 500
ducky.cc.passes.bt_visualise module
class ducky.cc.passes.bt_visualise.BlockTreeVisualiseVisitor(*args, **kwargs)[source]

Bases: ducky.cc.passes.BlockVisitor

do_visit_block(block)[source]
priority = 1000
visit(cv)[source]
Module contents
class ducky.cc.passes.ASTOptVisitor(logger, *args, **kwargs)[source]

Bases: ducky.cc.passes.ASTVisitor

replace_child(current_node, new_node)[source]
class ducky.cc.passes.ASTVisitor(logger, *args, **kwargs)[source]

Bases: pycparser.c_ast.NodeVisitor

DOWN()[source]
UP()[source]
generic_visit(node)[source]
priority = 99
visit(node, **kwargs)[source]
class ducky.cc.passes.BlockVisitor(logger, *args, **kwargs)[source]

Bases: object

DOWN()[source]
UP()[source]
do_visit(cv)[source]
do_visit_block(block)[source]
do_visit_fn(fn)[source]
priority = 99
visit(cv)[source]
visit_block(block)[source]
visit_fn(fn)[source]
ducky.cc.passes.load(logger)[source]

Submodules

ducky.cc.types module
class ducky.cc.types.ArrayType(item_type, size=None, *args, **kwargs)[source]

Bases: ducky.cc.types.CType

class ducky.cc.types.CType(visitor, decl=None)[source]

Bases: object

static create_from_decl(visitor, decl)[source]
static get_from_decl(visitor, decl)[source]
static get_from_desc(visitor, desc)[source]
types = {}
class ducky.cc.types.CharType(visitor, decl=None)[source]

Bases: ducky.cc.types.CType

class ducky.cc.types.FunctionType(*args, **kwargs)[source]

Bases: ducky.cc.types.CType

class ducky.cc.types.IntType(visitor, decl=None)[source]

Bases: ducky.cc.types.CType

class ducky.cc.types.PointerType(ptr_to_type, *args, **kwargs)[source]

Bases: ducky.cc.types.CType

class ducky.cc.types.StructType(name, *args, **kwargs)[source]

Bases: ducky.cc.types.CType

field_offset(name)[source]
field_type(name)[source]
class ducky.cc.types.UnsignedCharType(visitor, decl=None)[source]

Bases: ducky.cc.types.CharType

class ducky.cc.types.UnsignedIntType(visitor, decl=None)[source]

Bases: ducky.cc.types.IntType

class ducky.cc.types.VoidType(visitor, decl=None)[source]

Bases: ducky.cc.types.CType

Module contents

class ducky.cc.ADD(*operands)[source]

Bases: ducky.cc.Instruction

class ducky.cc.AND(*operands)[source]

Bases: ducky.cc.Instruction

class ducky.cc.BE(label)[source]

Bases: ducky.cc.Instruction

class ducky.cc.BG(label)[source]

Bases: ducky.cc.Instruction

class ducky.cc.BGE(label)[source]

Bases: ducky.cc.Instruction

class ducky.cc.BL(label)[source]

Bases: ducky.cc.Instruction

class ducky.cc.BLE(label)[source]

Bases: ducky.cc.Instruction

class ducky.cc.BNE(label)[source]

Bases: ducky.cc.Instruction

class ducky.cc.Block(name=None, comment=None)[source]

Bases: object

add_incoming(block)[source]
add_name(name)[source]
add_outgoing(block)[source]
connect(next)[source]
emit(inst)[source]
id = 0
instructions()[source]
materialize(code)[source]
class ducky.cc.CALL(label)[source]

Bases: ducky.cc.Instruction

class ducky.cc.CMP(left, right)[source]

Bases: ducky.cc.Instruction

class ducky.cc.Comment(comment)[source]

Bases: object

materialize()[source]
exception ducky.cc.CompilerError(location, msg)[source]

Bases: exceptions.Exception

class ducky.cc.ConstantValue(value)[source]

Bases: ducky.cc.NamedValue

class ducky.cc.Directive(directive)[source]

Bases: ducky.cc.Instruction

materialize()[source]
class ducky.cc.Expression(value=None, type=None, klass=<ExpressionClass.RVALUE: 2>)[source]

Bases: object

is_lvalue()[source]
is_mlvalue()[source]
is_rvalue()[source]
to_rvalue(visitor, preferred=None, keep=None)[source]
class ducky.cc.ExpressionClass[source]

Bases: enum.Enum

LVALUE = <ExpressionClass.LVALUE: 0>
MLVALUE = <ExpressionClass.MLVALUE: 1>
RVALUE = <ExpressionClass.RVALUE: 2>
class ducky.cc.Function(visitor, decl, ftype, args_types=None)[source]

Bases: object

args_block()[source]
block(*args, **kwargs)[source]
body_block()[source]
epilog_block()[source]
finish()[source]
header_block()[source]
materialize()[source]
prolog_block()[source]
class ducky.cc.HLT(isr)[source]

Bases: ducky.cc.Instruction

class ducky.cc.INC(reg)[source]

Bases: ducky.cc.Instruction

class ducky.cc.INT(isr)[source]

Bases: ducky.cc.Instruction

exception ducky.cc.IncompatibleTypesError(loc, t1, t2)[source]

Bases: ducky.cc.CompilerError

class ducky.cc.InlineAsm(code)[source]

Bases: ducky.cc.Instruction

materialize()[source]
class ducky.cc.Instruction(opcode, *operands)[source]

Bases: object

materialize()[source]
exception ducky.cc.IsAPointerError(loc, t)[source]

Bases: ducky.cc.CompilerError

class ducky.cc.J(label)[source]

Bases: ducky.cc.Instruction

class ducky.cc.LA(reg, value)[source]

Bases: ducky.cc.Instruction

class ducky.cc.LB(reg, addr)[source]

Bases: ducky.cc.Instruction

class ducky.cc.LI(reg, value)[source]

Bases: ducky.cc.Instruction

class ducky.cc.LS(reg, addr)[source]

Bases: ducky.cc.Instruction

class ducky.cc.LValueExpression(*args, **kwargs)[source]

Bases: ducky.cc.Expression

class ducky.cc.LW(reg, addr)[source]

Bases: ducky.cc.Instruction

class ducky.cc.MLValueExpression(*args, **kwargs)[source]

Bases: ducky.cc.Expression

class ducky.cc.MOV(*operands)[source]

Bases: ducky.cc.Instruction

class ducky.cc.MUL(*operands)[source]

Bases: ducky.cc.Instruction

class ducky.cc.MemorySlotStorage(symbol, label)[source]

Bases: ducky.cc.SymbolStorage

addrof(register, emit)[source]
name()[source]
class ducky.cc.MemorySlotValue(storage)[source]

Bases: ducky.cc.NamedValue

class ducky.cc.NOT(*operands)[source]

Bases: ducky.cc.Instruction

class ducky.cc.NamedValue(name)[source]

Bases: object

backing_register()[source]
can_register_backed()[source]
is_register_backed()[source]
exception ducky.cc.NotAPointerError(loc, t)[source]

Bases: ducky.cc.CompilerError

class ducky.cc.OR(*operands)[source]

Bases: ducky.cc.Instruction

class ducky.cc.POP(reg)[source]

Bases: ducky.cc.Instruction

class ducky.cc.PUSH(reg)[source]

Bases: ducky.cc.Instruction

class ducky.cc.RET[source]

Bases: ducky.cc.Instruction

class ducky.cc.RValueExpression(*args, **kwargs)[source]

Bases: ducky.cc.Expression

class ducky.cc.Register(rset, index)[source]

Bases: object

free()[source]
put()[source]
class ducky.cc.RegisterMemorySlotValue(register)[source]

Bases: ducky.cc.NamedValue

class ducky.cc.RegisterSet(fn)[source]

Bases: object

get(preferred=None, keep=None)[source]
restore_callee_saves(block)[source]
save_callee_saves(block)[source]
class ducky.cc.RegisterValue(register)[source]

Bases: ducky.cc.NamedValue

class ducky.cc.SHL(reg, ri)[source]

Bases: ducky.cc.Instruction

class ducky.cc.SHR(reg, ri)[source]

Bases: ducky.cc.Instruction

class ducky.cc.STB(addr, reg)[source]

Bases: ducky.cc.Instruction

class ducky.cc.STS(addr, reg)[source]

Bases: ducky.cc.Instruction

class ducky.cc.STW(addr, reg)[source]

Bases: ducky.cc.Instruction

class ducky.cc.SUB(*operands)[source]

Bases: ducky.cc.Instruction

class ducky.cc.Scope(visitor, parent=None)[source]

Bases: object

add(loc, symbol)[source]
get(name)[source]
scope_id = 0
class ducky.cc.StackSlotStorage(symbol, offset)[source]

Bases: ducky.cc.SymbolStorage

addrof(register, emit)[source]
name()[source]
class ducky.cc.StackSlotValue(storage)[source]

Bases: ducky.cc.NamedValue

class ducky.cc.StringConstantValue(value)[source]

Bases: ducky.cc.ConstantValue

class ducky.cc.Symbol(visitor, name, decl_type, extern=False, defined=False, const=False)[source]

Bases: object

exception ducky.cc.SymbolAlreadyDefinedError(loc, symbol)[source]

Bases: ducky.cc.CompilerError

exception ducky.cc.SymbolConflictError(location, msg)[source]

Bases: ducky.cc.CompilerError

class ducky.cc.SymbolStorage(symbol, register=None)[source]

Bases: object

acquire_register(register)[source]
addrof(reg, emit)[source]
has_register()[source]
name()[source]
release_register()[source]
spill_register(visitor)[source]
unspill_register(visitor, register)[source]
exception ducky.cc.SymbolUndefined(loc, symbol)[source]

Bases: ducky.cc.CompilerError

exception ducky.cc.UnableToImplicitCastError(loc, t1, t2)[source]

Bases: ducky.cc.CompilerError

exception ducky.cc.UndefinedStructMemberError(loc, s, m)[source]

Bases: ducky.cc.CompilerError

ducky.cc.dump_node(node)[source]
ducky.cc.show_node(node)[source]

ducky.cpu package

Subpackages

ducky.cpu.coprocessor package
Submodules
ducky.cpu.coprocessor.control module
class ducky.cpu.coprocessor.control.ControlCoprocessor(core)[source]

Bases: ducky.interfaces.ISnapshotable, ducky.cpu.coprocessor.Coprocessor

read(r)[source]
read_cr0()[source]
read_cr1()[source]
read_cr2()[source]
read_cr3()[source]
write(r, value)[source]
write_cr1(address)[source]
write_cr2(address)[source]
write_cr3(value)[source]
class ducky.cpu.coprocessor.control.ControlRegisters[source]

Bases: enum.IntEnum

CR0 = <ControlRegisters.CR0: 0>
CR1 = <ControlRegisters.CR1: 1>
CR2 = <ControlRegisters.CR2: 2>
CR3 = <ControlRegisters.CR3: 3>
class ducky.cpu.coprocessor.control.CoreFlags[source]

Bases: ducky.util.Flags

exception ducky.cpu.coprocessor.control.ReadOnlyRegisterError(r, *args, **kwargs)[source]

Bases: ducky.cpu.CPUException

exception ducky.cpu.coprocessor.control.WriteOnlyRegisterError(r, *args, **kwargs)[source]

Bases: ducky.cpu.CPUException

ducky.cpu.coprocessor.math_copro module

Stack-based coprocessor, providing several arithmetic operations with “long” numbers.

Coprocessor’s instructions operates on a stack of (by default) 8 slots. Operations to move values between math stack and registers/data stack are also available.

In the following documentation several different data types are used:

  • int - standard word, 32-bit wide integer
  • long - long integer, 64-bit wide

Unless said otherwise, instruction takes its arguments from the stack, removing the values in the process, and pushes the result - if any - back on the stack.

class ducky.cpu.coprocessor.math_copro.ADDL(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

static execute(core, inst)[source]
mnemonic = 'addl'
opcode = <MathCoprocessorOpcodes.ADDL: 32>
class ducky.cpu.coprocessor.math_copro.DECL(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Decrement top of the stack by one.

static execute(core, inst)[source]
mnemonic = 'decl'
opcode = <MathCoprocessorOpcodes.DECL: 31>
class ducky.cpu.coprocessor.math_copro.DIVL(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Divide the value below the top of the math stack by the topmost value.

static execute(core, inst)[source]
mnemonic = 'divl'
opcode = <MathCoprocessorOpcodes.DIVL: 11>
class ducky.cpu.coprocessor.math_copro.DROP(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

static execute(core, inst)[source]
mnemonic = 'drop'
opcode = <MathCoprocessorOpcodes.DROP: 23>
class ducky.cpu.coprocessor.math_copro.DUP(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

static execute(core, inst)[source]
mnemonic = 'dup'
opcode = <MathCoprocessorOpcodes.DUP: 20>
class ducky.cpu.coprocessor.math_copro.DUP2(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

static execute(core, inst)[source]
mnemonic = 'dup2'
opcode = <MathCoprocessorOpcodes.DUP2: 21>
class ducky.cpu.coprocessor.math_copro.Descriptor_MATH(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_R_R

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
operands = ''
exception ducky.cpu.coprocessor.math_copro.EmptyMathStackError(*args, **kwargs)[source]

Bases: ducky.cpu.CPUException

Raised when operation expects at least one value on math stack but stack is empty.

exception ducky.cpu.coprocessor.math_copro.FullMathStackError(*args, **kwargs)[source]

Bases: ducky.cpu.CPUException

Raised when operation tries to put value on math stack but there is no empty spot available.

class ducky.cpu.coprocessor.math_copro.INCL(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Increment top of the stack by one.

static execute(core, inst)[source]
mnemonic = 'incl'
opcode = <MathCoprocessorOpcodes.INCL: 30>
class ducky.cpu.coprocessor.math_copro.LOAD(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Merge two registers together, and make the result new TOS.

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
static execute(core, inst)[source]
mnemonic = 'load'
opcode = <MathCoprocessorOpcodes.LOAD: 9>
operands = 'r,r'
class ducky.cpu.coprocessor.math_copro.LOADUW(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Take a value from register, extend it to long, and make the result TOS.

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
static execute(core, inst)[source]
mnemonic = 'loaduw'
opcode = <MathCoprocessorOpcodes.LOADUW: 5>
operands = 'r'
class ducky.cpu.coprocessor.math_copro.LOADW(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Take a value from register, extend it to long, and make the result TOS.

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
static execute(core, inst)[source]
mnemonic = 'loadw'
opcode = <MathCoprocessorOpcodes.LOADW: 4>
operands = 'r'
class ducky.cpu.coprocessor.math_copro.MODL(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

static execute(core, inst)[source]
mnemonic = 'modl'
opcode = <MathCoprocessorOpcodes.MODL: 12>
class ducky.cpu.coprocessor.math_copro.MULL(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Multiply two top-most numbers on the stack.

static execute(core, inst)[source]
mnemonic = 'mull'
opcode = <MathCoprocessorOpcodes.MULL: 10>
class ducky.cpu.coprocessor.math_copro.MathCoprocessor(core, *args, **kwargs)[source]

Bases: ducky.interfaces.ISnapshotable, ducky.cpu.coprocessor.Coprocessor

Coprocessor itself, includes its register set (“math stack”).

Parameters:core (ducky.cpu.CPUCore) – CPU core coprocessor belongs to.
dump_stack()[source]

Log content of the stack using parent’s DEBUG method.

extend_with_push(u32)[source]
load_state(state)[source]
save_state(parent)[source]
sign_extend_with_push(i32)[source]
class ducky.cpu.coprocessor.math_copro.MathCoprocessorInstructionSet[source]

Bases: ducky.cpu.instructions.InstructionSet

Math coprocessor’s instruction set.

instruction_set_id = 1
instructions = [<ducky.cpu.coprocessor.math_copro.ADDL object at 0x7f923a8e83d0>, <ducky.cpu.coprocessor.math_copro.INCL object at 0x7f923a8e8d90>, <ducky.cpu.coprocessor.math_copro.DECL object at 0x7f923a8e81d0>, <ducky.cpu.coprocessor.math_copro.MULL object at 0x7f923a8e8810>, <ducky.cpu.coprocessor.math_copro.DIVL object at 0x7f923a8e8d10>, <ducky.cpu.coprocessor.math_copro.MODL object at 0x7f923a8e8290>, <ducky.cpu.coprocessor.math_copro.UDIVL object at 0x7f923a8e85d0>, <ducky.cpu.coprocessor.math_copro.UMODL object at 0x7f923a8e8cd0>, <ducky.cpu.coprocessor.math_copro.SYMDIVL object at 0x7f923a8e8b90>, <ducky.cpu.coprocessor.math_copro.SYMMODL object at 0x7f923a8e8b10>, <ducky.cpu.coprocessor.math_copro.DUP object at 0x7f923a8e8f90>, <ducky.cpu.coprocessor.math_copro.DUP2 object at 0x7f923a8e8850>, <ducky.cpu.coprocessor.math_copro.SWP object at 0x7f923a974750>, <ducky.cpu.coprocessor.math_copro.DROP object at 0x7f923a974790>, <ducky.cpu.coprocessor.math_copro.PUSHW object at 0x7f923a974d10>, <ducky.cpu.coprocessor.math_copro.SAVEW object at 0x7f923a974950>, <ducky.cpu.coprocessor.math_copro.POPW object at 0x7f923a974f50>, <ducky.cpu.coprocessor.math_copro.LOADW object at 0x7f923a974b90>, <ducky.cpu.coprocessor.math_copro.POPUW object at 0x7f923a9746d0>, <ducky.cpu.coprocessor.math_copro.LOADUW object at 0x7f923a974bd0>, <ducky.cpu.coprocessor.math_copro.PUSH object at 0x7f923a974b10>, <ducky.cpu.coprocessor.math_copro.SAVE object at 0x7f923a974dd0>, <ducky.cpu.coprocessor.math_copro.POP object at 0x7f923a974990>, <ducky.cpu.coprocessor.math_copro.LOAD object at 0x7f923a974cd0>, <ducky.cpu.instructions.SIS object at 0x7f923a974fd0>]
opcode_desc_map = {<MathCoprocessorOpcodes.POPW: 0>: <ducky.cpu.coprocessor.math_copro.POPW object at 0x7f923a974f50>, <MathCoprocessorOpcodes.POPUW: 1>: <ducky.cpu.coprocessor.math_copro.POPUW object at 0x7f923a9746d0>, <MathCoprocessorOpcodes.PUSHW: 2>: <ducky.cpu.coprocessor.math_copro.PUSHW object at 0x7f923a974d10>, <MathCoprocessorOpcodes.SAVEW: 3>: <ducky.cpu.coprocessor.math_copro.SAVEW object at 0x7f923a974950>, <MathCoprocessorOpcodes.LOADW: 4>: <ducky.cpu.coprocessor.math_copro.LOADW object at 0x7f923a974b90>, <MathCoprocessorOpcodes.LOADUW: 5>: <ducky.cpu.coprocessor.math_copro.LOADUW object at 0x7f923a974bd0>, <MathCoprocessorOpcodes.POP: 6>: <ducky.cpu.coprocessor.math_copro.POP object at 0x7f923a974990>, <MathCoprocessorOpcodes.SAVE: 7>: <ducky.cpu.coprocessor.math_copro.SAVE object at 0x7f923a974dd0>, <MathCoprocessorOpcodes.PUSH: 8>: <ducky.cpu.coprocessor.math_copro.PUSH object at 0x7f923a974b10>, <MathCoprocessorOpcodes.LOAD: 9>: <ducky.cpu.coprocessor.math_copro.LOAD object at 0x7f923a974cd0>, <MathCoprocessorOpcodes.MULL: 10>: <ducky.cpu.coprocessor.math_copro.MULL object at 0x7f923a8e8810>, <MathCoprocessorOpcodes.DIVL: 11>: <ducky.cpu.coprocessor.math_copro.DIVL object at 0x7f923a8e8d10>, <MathCoprocessorOpcodes.MODL: 12>: <ducky.cpu.coprocessor.math_copro.MODL object at 0x7f923a8e8290>, <MathCoprocessorOpcodes.SYMDIVL: 13>: <ducky.cpu.coprocessor.math_copro.SYMDIVL object at 0x7f923a8e8b90>, <MathCoprocessorOpcodes.SYMMODL: 14>: <ducky.cpu.coprocessor.math_copro.SYMMODL object at 0x7f923a8e8b10>, <MathCoprocessorOpcodes.UDIVL: 15>: <ducky.cpu.coprocessor.math_copro.UDIVL object at 0x7f923a8e85d0>, <MathCoprocessorOpcodes.UMODL: 16>: <ducky.cpu.coprocessor.math_copro.UMODL object at 0x7f923a8e8cd0>, <MathCoprocessorOpcodes.DUP: 20>: <ducky.cpu.coprocessor.math_copro.DUP object at 0x7f923a8e8f90>, <MathCoprocessorOpcodes.DUP2: 21>: <ducky.cpu.coprocessor.math_copro.DUP2 object at 0x7f923a8e8850>, <MathCoprocessorOpcodes.SWP: 22>: <ducky.cpu.coprocessor.math_copro.SWP object at 0x7f923a974750>, <MathCoprocessorOpcodes.DROP: 23>: <ducky.cpu.coprocessor.math_copro.DROP object at 0x7f923a974790>, <MathCoprocessorOpcodes.INCL: 30>: <ducky.cpu.coprocessor.math_copro.INCL object at 0x7f923a8e8d90>, <MathCoprocessorOpcodes.DECL: 31>: <ducky.cpu.coprocessor.math_copro.DECL object at 0x7f923a8e81d0>, <MathCoprocessorOpcodes.ADDL: 32>: <ducky.cpu.coprocessor.math_copro.ADDL object at 0x7f923a8e83d0>, <DuckyOpcodes.SIS: 63>: <ducky.cpu.instructions.SIS object at 0x7f923a974fd0>}
opcode_encoding_map = {<MathCoprocessorOpcodes.POPW: 0>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.POPUW: 1>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.PUSHW: 2>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.SAVEW: 3>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.LOADW: 4>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.LOADUW: 5>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.POP: 6>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.SAVE: 7>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.PUSH: 8>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.LOAD: 9>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.MULL: 10>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.DIVL: 11>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.MODL: 12>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.SYMDIVL: 13>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.SYMMODL: 14>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.UDIVL: 15>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.UMODL: 16>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.DUP: 20>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.DUP2: 21>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.SWP: 22>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.DROP: 23>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.INCL: 30>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.DECL: 31>: <class 'ducky.cpu.instructions.EncodingR'>, <MathCoprocessorOpcodes.ADDL: 32>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.SIS: 63>: <class 'ducky.cpu.instructions.EncodingI'>}
opcodes

alias of MathCoprocessorOpcodes

class ducky.cpu.coprocessor.math_copro.MathCoprocessorOpcodes[source]

Bases: enum.IntEnum

Math coprocessor’s instruction opcodes.

ADDL = <MathCoprocessorOpcodes.ADDL: 32>
DECL = <MathCoprocessorOpcodes.DECL: 31>
DIVL = <MathCoprocessorOpcodes.DIVL: 11>
DROP = <MathCoprocessorOpcodes.DROP: 23>
DUP = <MathCoprocessorOpcodes.DUP: 20>
DUP2 = <MathCoprocessorOpcodes.DUP2: 21>
INCL = <MathCoprocessorOpcodes.INCL: 30>
LOAD = <MathCoprocessorOpcodes.LOAD: 9>
LOADUW = <MathCoprocessorOpcodes.LOADUW: 5>
LOADW = <MathCoprocessorOpcodes.LOADW: 4>
MODL = <MathCoprocessorOpcodes.MODL: 12>
MULL = <MathCoprocessorOpcodes.MULL: 10>
POP = <MathCoprocessorOpcodes.POP: 6>
POPUW = <MathCoprocessorOpcodes.POPUW: 1>
POPW = <MathCoprocessorOpcodes.POPW: 0>
PUSH = <MathCoprocessorOpcodes.PUSH: 8>
PUSHW = <MathCoprocessorOpcodes.PUSHW: 2>
SAVE = <MathCoprocessorOpcodes.SAVE: 7>
SAVEW = <MathCoprocessorOpcodes.SAVEW: 3>
SIS = <MathCoprocessorOpcodes.SIS: 63>
SWP = <MathCoprocessorOpcodes.SWP: 22>
SYMDIVL = <MathCoprocessorOpcodes.SYMDIVL: 13>
SYMMODL = <MathCoprocessorOpcodes.SYMMODL: 14>
UDIVL = <MathCoprocessorOpcodes.UDIVL: 15>
UMODL = <MathCoprocessorOpcodes.UMODL: 16>
class ducky.cpu.coprocessor.math_copro.MathCoprocessorState[source]

Bases: ducky.snapshot.SnapshotNode

Snapshot node holding the state of math coprocessor.

class ducky.cpu.coprocessor.math_copro.POP(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Pop the long from data stack, and make it new TOS.

static execute(core, inst)[source]
mnemonic = 'pop'
opcode = <MathCoprocessorOpcodes.POP: 6>
class ducky.cpu.coprocessor.math_copro.POPUW(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Pop the int``from data stack, extend it to ``long, and make the result TOS.

static execute(core, inst)[source]
mnemonic = 'popuw'
opcode = <MathCoprocessorOpcodes.POPUW: 1>
class ducky.cpu.coprocessor.math_copro.POPW(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Pop the int from data stack, extend it to long, and make the result TOS.

static execute(core, inst)[source]
mnemonic = 'popw'
opcode = <MathCoprocessorOpcodes.POPW: 0>
class ducky.cpu.coprocessor.math_copro.PUSH(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Push the TOS on the data stack.

static execute(core, inst)[source]
mnemonic = 'push'
opcode = <MathCoprocessorOpcodes.PUSH: 8>
class ducky.cpu.coprocessor.math_copro.PUSHW(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Downsize TOS to int, and push the result on the data stack.

static execute(core, inst)[source]
mnemonic = 'pushw'
opcode = <MathCoprocessorOpcodes.PUSHW: 2>
class ducky.cpu.coprocessor.math_copro.RegisterSet(core)[source]

Bases: ducky.interfaces.ISnapshotable

Math stack wrapping class. Provides basic push/pop access, and direct access to a top of the stack.

Parameters:core (ducky.cpu.CPUCore) – CPU core registers belong to.
load_state(state)[source]
pop()[source]

Pop the top value from stack and return it.

Raises:ducky.cpu.coprocessor.math_copro.EmptyMathStackError – if there are no values on the stack.
push(v)[source]

Push new value on top of the stack.

Raises:ducky.cpu.coprocessor.math_copro.FullMathStackError – if there is no space available on the stack.
save_state(parent)[source]
tos()[source]

Return the top of the stack, without removing it from a stack.

Raises:ducky.cpu.coprocessor.math_copro.EmptyMathStackError – if there are no values on the stack.
tos1()[source]

Return the item below the top of the stack, without removing it from a stack.

Raises:ducky.cpu.coprocessor.math_copro.EmptyMathStackError – if there are no values on the stack.
class ducky.cpu.coprocessor.math_copro.SAVE(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Store TOS in two registers.

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
static execute(core, inst)[source]
mnemonic = 'save'
opcode = <MathCoprocessorOpcodes.SAVE: 7>
operands = 'r,r'
class ducky.cpu.coprocessor.math_copro.SAVEW(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Downsize TOS to int, and store the result in register.

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
static execute(core, inst)[source]
mnemonic = 'savew'
opcode = <MathCoprocessorOpcodes.SAVEW: 3>
operands = 'r'
ducky.cpu.coprocessor.math_copro.STACK_DEPTH = 8

Number of available spots on the math stack.

class ducky.cpu.coprocessor.math_copro.SWP(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

static execute(core, inst)[source]
mnemonic = 'swp'
opcode = <MathCoprocessorOpcodes.SWP: 22>
class ducky.cpu.coprocessor.math_copro.SYMDIVL(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

The same operation like DIVL but provides symmetric results.

static execute(core, inst)[source]
mnemonic = 'symdivl'
opcode = <MathCoprocessorOpcodes.SYMDIVL: 13>
class ducky.cpu.coprocessor.math_copro.SYMMODL(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

static execute(core, inst)[source]
mnemonic = 'symmodl'
opcode = <MathCoprocessorOpcodes.SYMMODL: 14>
class ducky.cpu.coprocessor.math_copro.UDIVL(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

Divide the value below the top of the math stack by the topmost value.

static execute(core, inst)[source]
mnemonic = 'udivl'
opcode = <MathCoprocessorOpcodes.UDIVL: 15>
class ducky.cpu.coprocessor.math_copro.UMODL(instruction_set)[source]

Bases: ducky.cpu.coprocessor.math_copro.Descriptor_MATH

static execute(core, inst)[source]
mnemonic = 'umodl'
opcode = <MathCoprocessorOpcodes.UMODL: 16>
Module contents

Coprocessors are intended to extend operations of CPUs. They are optional, and can cover wide range of operations, e.g. floating point arithmetic, encryption, or graphics. They are always attached to a CPU core, and may contain and use internal resources, e.g. their very own register sets, machine’s memory, or their parent’s caches.

class ducky.cpu.coprocessor.Coprocessor(core)[source]

Bases: object

Base class for per-core coprocessors.

Submodules

ducky.cpu.assemble module
class ducky.cpu.assemble.AlignSlot(boundary)[source]

Bases: ducky.cpu.assemble.DataSlot

class ducky.cpu.assemble.AsciiSlot[source]

Bases: ducky.cpu.assemble.DataSlot

close()[source]
symbol_type = <SymbolDataTypes.ASCII: 5>
class ducky.cpu.assemble.BssSection(s_name, flags=None, **kwargs)[source]

Bases: ducky.cpu.assemble.Section

class ducky.cpu.assemble.Buffer(logger, filename, buff)[source]

Bases: object

get_error(cls, info, column=None, length=None, **kwargs)[source]
get_line()[source]
has_lines()[source]
put_buffer(buff, filename=None)[source]
put_line(line)[source]
class ducky.cpu.assemble.ByteSlot[source]

Bases: ducky.cpu.assemble.DataSlot

close()[source]
symbol_type = <SymbolDataTypes.CHAR: 2>
class ducky.cpu.assemble.BytesSlot[source]

Bases: ducky.cpu.assemble.DataSlot

close()[source]
symbol_type = <SymbolDataTypes.ASCII: 5>
class ducky.cpu.assemble.CharSlot[source]

Bases: ducky.cpu.assemble.DataSlot

close()[source]
symbol_type = <SymbolDataTypes.CHAR: 2>
class ducky.cpu.assemble.DataSection(s_name, flags=None, **kwargs)[source]

Bases: ducky.cpu.assemble.Section

class ducky.cpu.assemble.DataSlot[source]

Bases: object

close()[source]
class ducky.cpu.assemble.FunctionSlot[source]

Bases: ducky.cpu.assemble.DataSlot

close()[source]
symbol_type = <SymbolDataTypes.FUNCTION: 6>
class ducky.cpu.assemble.IntSlot[source]

Bases: ducky.cpu.assemble.DataSlot

close()[source]
symbol_type = <SymbolDataTypes.INT: 0>
class ducky.cpu.assemble.Label(name, section, location)[source]

Bases: object

ducky.cpu.assemble.PATTERN(pattern)[source]
class ducky.cpu.assemble.RODataSection(s_name, flags=None, **kwargs)[source]

Bases: ducky.cpu.assemble.Section

class ducky.cpu.assemble.Reference(add=None, label=None)[source]

Bases: object

class ducky.cpu.assemble.RelocSection(s_name, flags=None, **kwargs)[source]

Bases: ducky.cpu.assemble.Section

class ducky.cpu.assemble.RelocSlot(name, flags=None, patch_section=None, patch_address=None, patch_offset=None, patch_size=None, patch_add=None)[source]

Bases: object

class ducky.cpu.assemble.Section(s_name, s_type, s_flags)[source]

Bases: object

class ducky.cpu.assemble.ShortSlot[source]

Bases: ducky.cpu.assemble.DataSlot

close()[source]
symbol_type = <SymbolDataTypes.SHORT: 1>
class ducky.cpu.assemble.SourceLocation(filename=None, lineno=None, column=None, length=None)[source]

Bases: object

copy()[source]
class ducky.cpu.assemble.SpaceSlot[source]

Bases: ducky.cpu.assemble.DataSlot

close()[source]
symbol_type = <SymbolDataTypes.ASCII: 5>
class ducky.cpu.assemble.StringSlot[source]

Bases: ducky.cpu.assemble.DataSlot

close()[source]
symbol_type = <SymbolDataTypes.STRING: 4>
class ducky.cpu.assemble.SymbolsSection(s_name, flags=None, **kwargs)[source]

Bases: ducky.cpu.assemble.Section

class ducky.cpu.assemble.TextSection(s_name, flags=None, **kwargs)[source]

Bases: ducky.cpu.assemble.Section

ducky.cpu.assemble.decode_string(s)[source]
ducky.cpu.assemble.sizeof(o)[source]
ducky.cpu.assemble.translate_buffer(logger, buff, base_address=None, mmapable_sections=False, writable_sections=False, filename=None, defines=None, includes=None, verify_disassemble=False)[source]
ducky.cpu.instructions module
class ducky.cpu.instructions.ADD(instruction_set)[source]

Bases: ducky.cpu.instructions._BINOP

static jit(core, inst)[source]
mnemonic = 'add'
opcode = <DuckyOpcodes.ADD: 28>
class ducky.cpu.instructions.AND(instruction_set)[source]

Bases: ducky.cpu.instructions._BITOP

static jit(core, inst)[source]
mnemonic = 'and'
opcode = <DuckyOpcodes.AND: 34>
class ducky.cpu.instructions.BE(instruction_set)[source]

Bases: ducky.cpu.instructions._BRANCH

mnemonic = 'be'
class ducky.cpu.instructions.BG(instruction_set)[source]

Bases: ducky.cpu.instructions._BRANCH

mnemonic = 'bg'
class ducky.cpu.instructions.BGE(instruction_set)[source]

Bases: ducky.cpu.instructions._BRANCH

mnemonic = 'bge'
class ducky.cpu.instructions.BL(instruction_set)[source]

Bases: ducky.cpu.instructions._BRANCH

mnemonic = 'bl'
class ducky.cpu.instructions.BLE(instruction_set)[source]

Bases: ducky.cpu.instructions._BRANCH

mnemonic = 'ble'
class ducky.cpu.instructions.BNE(instruction_set)[source]

Bases: ducky.cpu.instructions._BRANCH

mnemonic = 'bne'
class ducky.cpu.instructions.BNO(instruction_set)[source]

Bases: ducky.cpu.instructions._BRANCH

mnemonic = 'bno'
class ducky.cpu.instructions.BNS(instruction_set)[source]

Bases: ducky.cpu.instructions._BRANCH

mnemonic = 'bns'
class ducky.cpu.instructions.BNZ(instruction_set)[source]

Bases: ducky.cpu.instructions._BRANCH

mnemonic = 'bnz'
class ducky.cpu.instructions.BO(instruction_set)[source]

Bases: ducky.cpu.instructions._BRANCH

mnemonic = 'bo'
class ducky.cpu.instructions.BS(instruction_set)[source]

Bases: ducky.cpu.instructions._BRANCH

mnemonic = 'bs'
class ducky.cpu.instructions.BZ(instruction_set)[source]

Bases: ducky.cpu.instructions._BRANCH

mnemonic = 'bz'
class ducky.cpu.instructions.CALL(instruction_set)[source]

Bases: ducky.cpu.instructions._JUMP

static execute(core, inst)[source]
static jit(core, inst)[source]
mnemonic = 'call'
opcode = <DuckyOpcodes.CALL: 15>
class ducky.cpu.instructions.CAS(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
encoding

alias of EncodingA

static execute(core, inst)[source]
mnemonic = 'cas'
opcode = <DuckyOpcodes.CAS: 7>
operands = 'r,r,r'
class ducky.cpu.instructions.CLI(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

encoding

alias of EncodingI

static execute(core, inst)[source]
mnemonic = 'cli'
opcode = <DuckyOpcodes.CLI: 17>
class ducky.cpu.instructions.CMP(instruction_set)[source]

Bases: ducky.cpu.instructions._CMP

static execute(core, inst)[source]
static jit(core, inst)[source]
mnemonic = 'cmp'
opcode = <DuckyOpcodes.CMP: 47>
class ducky.cpu.instructions.CMPU(instruction_set)[source]

Bases: ducky.cpu.instructions._CMP

static execute(core, inst)[source]
mnemonic = 'cmpu'
opcode = <DuckyOpcodes.CMPU: 48>
class ducky.cpu.instructions.CTR(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_R_R

encoding

alias of EncodingR

static execute(core, inst)[source]
mnemonic = 'ctr'
opcode = <DuckyOpcodes.CTR: 60>
class ducky.cpu.instructions.CTW(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_R_R

encoding

alias of EncodingR

static execute(core, inst)[source]
mnemonic = 'ctw'
opcode = <DuckyOpcodes.CTW: 61>
class ducky.cpu.instructions.DEC(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_R

static execute(core, inst)[source]
static jit(core, inst)[source]
mnemonic = 'dec'
opcode = <DuckyOpcodes.DEC: 27>
class ducky.cpu.instructions.DIV(instruction_set)[source]

Bases: ducky.cpu.instructions._BINOP

mnemonic = 'div'
opcode = <DuckyOpcodes.DIV: 31>
class ducky.cpu.instructions.Descriptor(instruction_set)[source]

Bases: object

static assemble_operands(logger, buffer, inst, operands)[source]
classmethod disassemble_mnemonic(inst)[source]
static disassemble_operands(logger, inst)[source]
emit_instruction(logger, buffer, line)[source]
encoding

alias of EncodingR

static execute(core, inst)[source]
static fill_reloc_slot(logger, inst, slot)[source]
inst_aligned = False
static jit(core, inst)[source]
mnemonic = None
opcode = None
operands = None
pattern = None
relative_address = False
class ducky.cpu.instructions.Descriptor_I(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
operands = 'i'
class ducky.cpu.instructions.Descriptor_R(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
encoding

alias of EncodingR

operands = 'r'
class ducky.cpu.instructions.Descriptor_RI(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
encoding

alias of EncodingI

operands = 'ri'
class ducky.cpu.instructions.Descriptor_RI_R(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
encoding

alias of EncodingR

operands = 'ri,r'
class ducky.cpu.instructions.Descriptor_R_I(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
encoding

alias of EncodingI

operands = 'r,i'
class ducky.cpu.instructions.Descriptor_R_R(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
encoding

alias of EncodingR

operands = 'r,r'
class ducky.cpu.instructions.Descriptor_R_RI(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

static assemble_operands(logger, buffer, inst, operands)[source]
static disassemble_operands(logger, inst)[source]
encoding

alias of EncodingR

operands = 'r,ri'
class ducky.cpu.instructions.DuckyInstructionSet[source]

Bases: ducky.cpu.instructions.InstructionSet

instruction_set_id = 0
instructions = [<ducky.cpu.instructions.NOP object at 0x7f923b2a8810>, <ducky.cpu.instructions.INT object at 0x7f923b2a8910>, <ducky.cpu.instructions.IPI object at 0x7f923b2a8950>, <ducky.cpu.instructions.RETINT object at 0x7f923b2a8990>, <ducky.cpu.instructions.CALL object at 0x7f923b2a8a10>, <ducky.cpu.instructions.RET object at 0x7f923b2a8a50>, <ducky.cpu.instructions.CLI object at 0x7f923b2a8ad0>, <ducky.cpu.instructions.STI object at 0x7f923b2a8b50>, <ducky.cpu.instructions.HLT object at 0x7f923b2a8bd0>, <ducky.cpu.instructions.RST object at 0x7f923b2a8c10>, <ducky.cpu.instructions.IDLE object at 0x7f923b2a8c90>, <ducky.cpu.instructions.PUSH object at 0x7f923b2a8d10>, <ducky.cpu.instructions.POP object at 0x7f923b2a8d50>, <ducky.cpu.instructions.INC object at 0x7f923b2a8d90>, <ducky.cpu.instructions.DEC object at 0x7f923b2a8dd0>, <ducky.cpu.instructions.ADD object at 0x7f923b2a8e10>, <ducky.cpu.instructions.SUB object at 0x7f923b2a8e50>, <ducky.cpu.instructions.CMP object at 0x7f923b2a8e90>, <ducky.cpu.instructions.J object at 0x7f923b2a8ed0>, <ducky.cpu.instructions.INW object at 0x7f923b2a8f10>, <ducky.cpu.instructions.INB object at 0x7f923b2a8f50>, <ducky.cpu.instructions.INS object at 0x7f923b2a8f90>, <ducky.cpu.instructions.OUTS object at 0x7f923b2a8fd0>, <ducky.cpu.instructions.OUTW object at 0x7f923b2bf050>, <ducky.cpu.instructions.OUTB object at 0x7f923b2bf090>, <ducky.cpu.instructions.AND object at 0x7f923b2bf0d0>, <ducky.cpu.instructions.OR object at 0x7f923b2bf110>, <ducky.cpu.instructions.XOR object at 0x7f923b2bf150>, <ducky.cpu.instructions.NOT object at 0x7f923b2bf190>, <ducky.cpu.instructions.SHIFTL object at 0x7f923b2bf1d0>, <ducky.cpu.instructions.SHIFTR object at 0x7f923b2bf210>, <ducky.cpu.instructions.LW object at 0x7f923b2bf250>, <ducky.cpu.instructions.LS object at 0x7f923b2bf290>, <ducky.cpu.instructions.LB object at 0x7f923b2bf2d0>, <ducky.cpu.instructions.LI object at 0x7f923b2bf310>, <ducky.cpu.instructions.LIU object at 0x7f923b2bf350>, <ducky.cpu.instructions.LA object at 0x7f923b2bf390>, <ducky.cpu.instructions.STW object at 0x7f923b2bf3d0>, <ducky.cpu.instructions.STS object at 0x7f923b2bf410>, <ducky.cpu.instructions.STB object at 0x7f923b2bf450>, <ducky.cpu.instructions.MOV object at 0x7f923b2bf490>, <ducky.cpu.instructions.SWP object at 0x7f923b2bf4d0>, <ducky.cpu.instructions.MUL object at 0x7f923b2bf510>, <ducky.cpu.instructions.DIV object at 0x7f923b2bf550>, <ducky.cpu.instructions.UDIV object at 0x7f923b2bf590>, <ducky.cpu.instructions.MOD object at 0x7f923b2bf5d0>, <ducky.cpu.instructions.CMPU object at 0x7f923b2bf610>, <ducky.cpu.instructions.CAS object at 0x7f923b2bf650>, <ducky.cpu.instructions.SIS object at 0x7f923b2bf690>, <ducky.cpu.instructions.BE object at 0x7f923b2bf6d0>, <ducky.cpu.instructions.BNE object at 0x7f923b2bf710>, <ducky.cpu.instructions.BZ object at 0x7f923b2bf750>, <ducky.cpu.instructions.BNZ object at 0x7f923b2bf790>, <ducky.cpu.instructions.BO object at 0x7f923b2bf7d0>, <ducky.cpu.instructions.BNO object at 0x7f923b2bf810>, <ducky.cpu.instructions.BS object at 0x7f923b2bf850>, <ducky.cpu.instructions.BNS object at 0x7f923b2bf890>, <ducky.cpu.instructions.BG object at 0x7f923b2bf8d0>, <ducky.cpu.instructions.BGE object at 0x7f923b2bf910>, <ducky.cpu.instructions.BL object at 0x7f923b2bf950>, <ducky.cpu.instructions.BLE object at 0x7f923b2bf990>, <ducky.cpu.instructions.SETE object at 0x7f923b2bf9d0>, <ducky.cpu.instructions.SETNE object at 0x7f923b2bfa10>, <ducky.cpu.instructions.SETZ object at 0x7f923b2bfa50>, <ducky.cpu.instructions.SETNZ object at 0x7f923b2bfa90>, <ducky.cpu.instructions.SETO object at 0x7f923b2bfad0>, <ducky.cpu.instructions.SETNO object at 0x7f923b2bfb10>, <ducky.cpu.instructions.SETS object at 0x7f923b2bfb50>, <ducky.cpu.instructions.SETNS object at 0x7f923b2bfb90>, <ducky.cpu.instructions.SETG object at 0x7f923b2bfbd0>, <ducky.cpu.instructions.SETGE object at 0x7f923b2bfc10>, <ducky.cpu.instructions.SETL object at 0x7f923b2bfc50>, <ducky.cpu.instructions.SETLE object at 0x7f923b2bfc90>, <ducky.cpu.instructions.LPM object at 0x7f923b2bfcd0>, <ducky.cpu.instructions.CTR object at 0x7f923b2bfd50>, <ducky.cpu.instructions.CTW object at 0x7f923b2bfd90>, <ducky.cpu.instructions.FPTC object at 0x7f923b2bfdd0>]
opcode_desc_map = {<DuckyOpcodes.NOP: 0>: <ducky.cpu.instructions.NOP object at 0x7f923b2a8810>, <DuckyOpcodes.LW: 1>: <ducky.cpu.instructions.LW object at 0x7f923b2bf250>, <DuckyOpcodes.LS: 2>: <ducky.cpu.instructions.LS object at 0x7f923b2bf290>, <DuckyOpcodes.LB: 3>: <ducky.cpu.instructions.LB object at 0x7f923b2bf2d0>, <DuckyOpcodes.STW: 4>: <ducky.cpu.instructions.STW object at 0x7f923b2bf3d0>, <DuckyOpcodes.STS: 5>: <ducky.cpu.instructions.STS object at 0x7f923b2bf410>, <DuckyOpcodes.STB: 6>: <ducky.cpu.instructions.STB object at 0x7f923b2bf450>, <DuckyOpcodes.CAS: 7>: <ducky.cpu.instructions.CAS object at 0x7f923b2bf650>, <DuckyOpcodes.LA: 8>: <ducky.cpu.instructions.LA object at 0x7f923b2bf390>, <DuckyOpcodes.LI: 9>: <ducky.cpu.instructions.LI object at 0x7f923b2bf310>, <DuckyOpcodes.LIU: 10>: <ducky.cpu.instructions.LIU object at 0x7f923b2bf350>, <DuckyOpcodes.MOV: 11>: <ducky.cpu.instructions.MOV object at 0x7f923b2bf490>, <DuckyOpcodes.SWP: 12>: <ducky.cpu.instructions.SWP object at 0x7f923b2bf4d0>, <DuckyOpcodes.INT: 13>: <ducky.cpu.instructions.INT object at 0x7f923b2a8910>, <DuckyOpcodes.RETINT: 14>: <ducky.cpu.instructions.RETINT object at 0x7f923b2a8990>, <DuckyOpcodes.CALL: 15>: <ducky.cpu.instructions.CALL object at 0x7f923b2a8a10>, <DuckyOpcodes.RET: 16>: <ducky.cpu.instructions.RET object at 0x7f923b2a8a50>, <DuckyOpcodes.CLI: 17>: <ducky.cpu.instructions.CLI object at 0x7f923b2a8ad0>, <DuckyOpcodes.STI: 18>: <ducky.cpu.instructions.STI object at 0x7f923b2a8b50>, <DuckyOpcodes.RST: 19>: <ducky.cpu.instructions.RST object at 0x7f923b2a8c10>, <DuckyOpcodes.HLT: 20>: <ducky.cpu.instructions.HLT object at 0x7f923b2a8bd0>, <DuckyOpcodes.IDLE: 21>: <ducky.cpu.instructions.IDLE object at 0x7f923b2a8c90>, <DuckyOpcodes.LPM: 22>: <ducky.cpu.instructions.LPM object at 0x7f923b2bfcd0>, <DuckyOpcodes.IPI: 23>: <ducky.cpu.instructions.IPI object at 0x7f923b2a8950>, <DuckyOpcodes.PUSH: 24>: <ducky.cpu.instructions.PUSH object at 0x7f923b2a8d10>, <DuckyOpcodes.POP: 25>: <ducky.cpu.instructions.POP object at 0x7f923b2a8d50>, <DuckyOpcodes.INC: 26>: <ducky.cpu.instructions.INC object at 0x7f923b2a8d90>, <DuckyOpcodes.DEC: 27>: <ducky.cpu.instructions.DEC object at 0x7f923b2a8dd0>, <DuckyOpcodes.ADD: 28>: <ducky.cpu.instructions.ADD object at 0x7f923b2a8e10>, <DuckyOpcodes.SUB: 29>: <ducky.cpu.instructions.SUB object at 0x7f923b2a8e50>, <DuckyOpcodes.MUL: 30>: <ducky.cpu.instructions.MUL object at 0x7f923b2bf510>, <DuckyOpcodes.DIV: 31>: <ducky.cpu.instructions.DIV object at 0x7f923b2bf550>, <DuckyOpcodes.UDIV: 32>: <ducky.cpu.instructions.UDIV object at 0x7f923b2bf590>, <DuckyOpcodes.MOD: 33>: <ducky.cpu.instructions.MOD object at 0x7f923b2bf5d0>, <DuckyOpcodes.AND: 34>: <ducky.cpu.instructions.AND object at 0x7f923b2bf0d0>, <DuckyOpcodes.OR: 35>: <ducky.cpu.instructions.OR object at 0x7f923b2bf110>, <DuckyOpcodes.XOR: 36>: <ducky.cpu.instructions.XOR object at 0x7f923b2bf150>, <DuckyOpcodes.NOT: 37>: <ducky.cpu.instructions.NOT object at 0x7f923b2bf190>, <DuckyOpcodes.SHIFTL: 38>: <ducky.cpu.instructions.SHIFTL object at 0x7f923b2bf1d0>, <DuckyOpcodes.SHIFTR: 39>: <ducky.cpu.instructions.SHIFTR object at 0x7f923b2bf210>, <DuckyOpcodes.OUTW: 40>: <ducky.cpu.instructions.OUTW object at 0x7f923b2bf050>, <DuckyOpcodes.OUTS: 41>: <ducky.cpu.instructions.OUTS object at 0x7f923b2a8fd0>, <DuckyOpcodes.OUTB: 42>: <ducky.cpu.instructions.OUTB object at 0x7f923b2bf090>, <DuckyOpcodes.INW: 43>: <ducky.cpu.instructions.INW object at 0x7f923b2a8f10>, <DuckyOpcodes.INS: 44>: <ducky.cpu.instructions.INS object at 0x7f923b2a8f90>, <DuckyOpcodes.INB: 45>: <ducky.cpu.instructions.INB object at 0x7f923b2a8f50>, <DuckyOpcodes.J: 46>: <ducky.cpu.instructions.J object at 0x7f923b2a8ed0>, <DuckyOpcodes.CMP: 47>: <ducky.cpu.instructions.CMP object at 0x7f923b2a8e90>, <DuckyOpcodes.CMPU: 48>: <ducky.cpu.instructions.CMPU object at 0x7f923b2bf610>, <DuckyOpcodes.SET: 49>: <ducky.cpu.instructions.SETLE object at 0x7f923b2bfc90>, <DuckyOpcodes.BRANCH: 50>: <ducky.cpu.instructions.BLE object at 0x7f923b2bf990>, <DuckyOpcodes.CTR: 60>: <ducky.cpu.instructions.CTR object at 0x7f923b2bfd50>, <DuckyOpcodes.CTW: 61>: <ducky.cpu.instructions.CTW object at 0x7f923b2bfd90>, <DuckyOpcodes.FPTC: 62>: <ducky.cpu.instructions.FPTC object at 0x7f923b2bfdd0>, <DuckyOpcodes.SIS: 63>: <ducky.cpu.instructions.SIS object at 0x7f923b2bf690>}
opcode_encoding_map = {<DuckyOpcodes.NOP: 0>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.LW: 1>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.LS: 2>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.LB: 3>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.STW: 4>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.STS: 5>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.STB: 6>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.CAS: 7>: <class 'ducky.cpu.instructions.EncodingA'>, <DuckyOpcodes.LA: 8>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.LI: 9>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.LIU: 10>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.MOV: 11>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.SWP: 12>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.INT: 13>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.RETINT: 14>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.CALL: 15>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.RET: 16>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.CLI: 17>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.STI: 18>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.RST: 19>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.HLT: 20>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.IDLE: 21>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.LPM: 22>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.IPI: 23>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.PUSH: 24>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.POP: 25>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.INC: 26>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.DEC: 27>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.ADD: 28>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.SUB: 29>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.MUL: 30>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.DIV: 31>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.UDIV: 32>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.MOD: 33>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.AND: 34>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.OR: 35>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.XOR: 36>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.NOT: 37>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.SHIFTL: 38>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.SHIFTR: 39>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.OUTW: 40>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.OUTS: 41>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.OUTB: 42>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.INW: 43>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.INS: 44>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.INB: 45>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.J: 46>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.CMP: 47>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.CMPU: 48>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.SET: 49>: <class 'ducky.cpu.instructions.EncodingC'>, <DuckyOpcodes.BRANCH: 50>: <class 'ducky.cpu.instructions.EncodingC'>, <DuckyOpcodes.CTR: 60>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.CTW: 61>: <class 'ducky.cpu.instructions.EncodingR'>, <DuckyOpcodes.FPTC: 62>: <class 'ducky.cpu.instructions.EncodingI'>, <DuckyOpcodes.SIS: 63>: <class 'ducky.cpu.instructions.EncodingI'>}
opcodes

alias of DuckyOpcodes

class ducky.cpu.instructions.DuckyOpcodes[source]

Bases: enum.IntEnum

ADD = <DuckyOpcodes.ADD: 28>
AND = <DuckyOpcodes.AND: 34>
BRANCH = <DuckyOpcodes.BRANCH: 50>
CALL = <DuckyOpcodes.CALL: 15>
CAS = <DuckyOpcodes.CAS: 7>
CLI = <DuckyOpcodes.CLI: 17>
CMP = <DuckyOpcodes.CMP: 47>
CMPU = <DuckyOpcodes.CMPU: 48>
CTR = <DuckyOpcodes.CTR: 60>
CTW = <DuckyOpcodes.CTW: 61>
DEC = <DuckyOpcodes.DEC: 27>
DIV = <DuckyOpcodes.DIV: 31>
FPTC = <DuckyOpcodes.FPTC: 62>
HLT = <DuckyOpcodes.HLT: 20>
IDLE = <DuckyOpcodes.IDLE: 21>
INB = <DuckyOpcodes.INB: 45>
INC = <DuckyOpcodes.INC: 26>
INS = <DuckyOpcodes.INS: 44>
INT = <DuckyOpcodes.INT: 13>
INW = <DuckyOpcodes.INW: 43>
IPI = <DuckyOpcodes.IPI: 23>
J = <DuckyOpcodes.J: 46>
LA = <DuckyOpcodes.LA: 8>
LB = <DuckyOpcodes.LB: 3>
LI = <DuckyOpcodes.LI: 9>
LIU = <DuckyOpcodes.LIU: 10>
LPM = <DuckyOpcodes.LPM: 22>
LS = <DuckyOpcodes.LS: 2>
LW = <DuckyOpcodes.LW: 1>
MOD = <DuckyOpcodes.MOD: 33>
MOV = <DuckyOpcodes.MOV: 11>
MUL = <DuckyOpcodes.MUL: 30>
NOP = <DuckyOpcodes.NOP: 0>
NOT = <DuckyOpcodes.NOT: 37>
OR = <DuckyOpcodes.OR: 35>
OUTB = <DuckyOpcodes.OUTB: 42>
OUTS = <DuckyOpcodes.OUTS: 41>
OUTW = <DuckyOpcodes.OUTW: 40>
POP = <DuckyOpcodes.POP: 25>
PUSH = <DuckyOpcodes.PUSH: 24>
RET = <DuckyOpcodes.RET: 16>
RETINT = <DuckyOpcodes.RETINT: 14>
RST = <DuckyOpcodes.RST: 19>
SET = <DuckyOpcodes.SET: 49>
SHIFTL = <DuckyOpcodes.SHIFTL: 38>
SHIFTR = <DuckyOpcodes.SHIFTR: 39>
SIS = <DuckyOpcodes.SIS: 63>
STB = <DuckyOpcodes.STB: 6>
STI = <DuckyOpcodes.STI: 18>
STS = <DuckyOpcodes.STS: 5>
STW = <DuckyOpcodes.STW: 4>
SUB = <DuckyOpcodes.SUB: 29>
SWP = <DuckyOpcodes.SWP: 12>
UDIV = <DuckyOpcodes.UDIV: 32>
XOR = <DuckyOpcodes.XOR: 36>
ducky.cpu.instructions.ENCODE(logger, buffer, inst, field, size, value, raise_on_large_value=False)[source]
class ducky.cpu.instructions.Encoding[source]

Bases: _ctypes.Structure

static sign_extend_immediate(logger, inst, sign_mask, ext_mask)[source]
class ducky.cpu.instructions.EncodingA[source]

Bases: _ctypes.Structure

opcode

Structure/Union member

reg1

Structure/Union member

reg2

Structure/Union member

reg3

Structure/Union member

class ducky.cpu.instructions.EncodingC[source]

Bases: _ctypes.Structure

static fill_reloc_slot(logger, inst, slot)[source]
flag

Structure/Union member

immediate

Structure/Union member

immediate_flag

Structure/Union member

opcode

Structure/Union member

reg

Structure/Union member

static sign_extend_immediate(logger, inst)[source]
value

Structure/Union member

class ducky.cpu.instructions.EncodingI[source]

Bases: _ctypes.Structure

static fill_reloc_slot(logger, inst, slot)[source]
immediate

Structure/Union member

immediate_flag

Structure/Union member

opcode

Structure/Union member

reg

Structure/Union member

static sign_extend_immediate(logger, inst)[source]
class ducky.cpu.instructions.EncodingR[source]

Bases: _ctypes.Structure

static fill_reloc_slot(logger, inst, slot)[source]
immediate

Structure/Union member

immediate_flag

Structure/Union member

opcode

Structure/Union member

reg1

Structure/Union member

reg2

Structure/Union member

static sign_extend_immediate(logger, inst)[source]
class ducky.cpu.instructions.FPTC(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

encoding

alias of EncodingI

static execute(core, inst)[source]
mnemonic = 'fptc'
opcode = <DuckyOpcodes.FPTC: 62>
class ducky.cpu.instructions.HLT(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_RI

static execute(core, inst)[source]
mnemonic = 'hlt'
opcode = <DuckyOpcodes.HLT: 20>
class ducky.cpu.instructions.IDLE(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

encoding

alias of EncodingI

static execute(core, inst)[source]
mnemonic = 'idle'
opcode = <DuckyOpcodes.IDLE: 21>
ducky.cpu.instructions.IE_FLAG(n)[source]
ducky.cpu.instructions.IE_IMM(n, l)[source]
ducky.cpu.instructions.IE_OPCODE()[source]
ducky.cpu.instructions.IE_REG(n)[source]
class ducky.cpu.instructions.INB(instruction_set)[source]

Bases: ducky.cpu.instructions._IN

mnemonic = 'inb'
opcode = <DuckyOpcodes.INB: 45>
class ducky.cpu.instructions.INC(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_R

static execute(core, inst)[source]
static jit(core, inst)[source]
mnemonic = 'inc'
opcode = <DuckyOpcodes.INC: 26>
class ducky.cpu.instructions.INS(instruction_set)[source]

Bases: ducky.cpu.instructions._IN

mnemonic = 'ins'
opcode = <DuckyOpcodes.INS: 44>
class ducky.cpu.instructions.INT(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_RI

static execute(core, inst)[source]
mnemonic = 'int'
opcode = <DuckyOpcodes.INT: 13>
class ducky.cpu.instructions.INW(instruction_set)[source]

Bases: ducky.cpu.instructions._IN

mnemonic = 'inw'
opcode = <DuckyOpcodes.INW: 43>
class ducky.cpu.instructions.IPI(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_R_RI

static execute(core, inst)[source]
mnemonic = 'ipi'
opcode = <DuckyOpcodes.IPI: 23>
class ducky.cpu.instructions.InstructionSet[source]

Bases: object

classmethod decode_instruction(logger, inst, core=None)[source]
classmethod disassemble_instruction(logger, inst)[source]
classmethod init()[source]
instruction_set_id = None
instructions = []
opcodes = None
class ducky.cpu.instructions.InstructionSetMetaclass(name, bases, dict)[source]

Bases: type

class ducky.cpu.instructions.J(instruction_set)[source]

Bases: ducky.cpu.instructions._JUMP

static execute(core, inst)[source]
static jit(core, inst)[source]
mnemonic = 'j'
opcode = <DuckyOpcodes.J: 46>
ducky.cpu.instructions.JUMP(core, inst)[source]
class ducky.cpu.instructions.LA(instruction_set)[source]

Bases: ducky.cpu.instructions._LOAD_IMM

static jit(core, inst)[source]
classmethod load(core, inst)[source]
mnemonic = 'la'
opcode = <DuckyOpcodes.LA: 8>
relative_address = True
class ducky.cpu.instructions.LB(instruction_set)[source]

Bases: ducky.cpu.instructions._LOAD

mnemonic = 'lb'
opcode = <DuckyOpcodes.LB: 3>
class ducky.cpu.instructions.LI(instruction_set)[source]

Bases: ducky.cpu.instructions._LOAD_IMM

static jit(core, inst)[source]
classmethod load(core, inst)[source]
mnemonic = 'li'
opcode = <DuckyOpcodes.LI: 9>
class ducky.cpu.instructions.LIU(instruction_set)[source]

Bases: ducky.cpu.instructions._LOAD_IMM

classmethod load(core, inst)[source]
mnemonic = 'liu'
opcode = <DuckyOpcodes.LIU: 10>
class ducky.cpu.instructions.LPM(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

encoding

alias of EncodingI

static execute(core, inst)[source]
mnemonic = 'lpm'
opcode = <DuckyOpcodes.LPM: 22>
class ducky.cpu.instructions.LS(instruction_set)[source]

Bases: ducky.cpu.instructions._LOAD

mnemonic = 'ls'
opcode = <DuckyOpcodes.LS: 2>
class ducky.cpu.instructions.LW(instruction_set)[source]

Bases: ducky.cpu.instructions._LOAD

mnemonic = 'lw'
opcode = <DuckyOpcodes.LW: 1>
class ducky.cpu.instructions.MOD(instruction_set)[source]

Bases: ducky.cpu.instructions._BINOP

mnemonic = 'mod'
opcode = <DuckyOpcodes.MOD: 33>
class ducky.cpu.instructions.MOV(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_R_R

encoding

alias of EncodingR

static execute(core, inst)[source]
static jit(core, inst)[source]
mnemonic = 'mov'
opcode = <DuckyOpcodes.MOV: 11>
class ducky.cpu.instructions.MUL(instruction_set)[source]

Bases: ducky.cpu.instructions._BINOP

static jit(core, inst)[source]
mnemonic = 'mul'
opcode = <DuckyOpcodes.MUL: 30>
class ducky.cpu.instructions.NOP(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

encoding

alias of EncodingI

static execute(core, inst)[source]
mnemonic = 'nop'
opcode = <DuckyOpcodes.NOP: 0>
class ducky.cpu.instructions.NOT(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_R

encoding

alias of EncodingR

static execute(core, inst)[source]
mnemonic = 'not'
opcode = <DuckyOpcodes.NOT: 37>
class ducky.cpu.instructions.OR(instruction_set)[source]

Bases: ducky.cpu.instructions._BITOP

static jit(core, inst)[source]
mnemonic = 'or'
opcode = <DuckyOpcodes.OR: 35>
class ducky.cpu.instructions.OUTB(instruction_set)[source]

Bases: ducky.cpu.instructions._OUT

mnemonic = 'outb'
opcode = <DuckyOpcodes.OUTB: 42>
class ducky.cpu.instructions.OUTS(instruction_set)[source]

Bases: ducky.cpu.instructions._OUT

mnemonic = 'outs'
opcode = <DuckyOpcodes.OUTS: 41>
class ducky.cpu.instructions.OUTW(instruction_set)[source]

Bases: ducky.cpu.instructions._OUT

mnemonic = 'outw'
opcode = <DuckyOpcodes.OUTW: 40>
class ducky.cpu.instructions.POP(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_R

static execute(core, inst)[source]
static jit(core, inst)[source]
mnemonic = 'pop'
opcode = <DuckyOpcodes.POP: 25>
class ducky.cpu.instructions.PUSH(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_RI

static execute(core, inst)[source]
static jit(core, inst)[source]
mnemonic = 'push'
opcode = <DuckyOpcodes.PUSH: 24>
class ducky.cpu.instructions.RET(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

encoding

alias of EncodingI

static execute(core, inst)[source]
static jit(core, inst)[source]
mnemonic = 'ret'
opcode = <DuckyOpcodes.RET: 16>
class ducky.cpu.instructions.RETINT(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

encoding

alias of EncodingI

static execute(core, inst)[source]
mnemonic = 'retint'
opcode = <DuckyOpcodes.RETINT: 14>
ducky.cpu.instructions.RI_ADDR(core, inst, reg)[source]
ducky.cpu.instructions.RI_VAL(core, inst, reg, sign_extend=True)[source]
class ducky.cpu.instructions.RST(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

encoding

alias of EncodingI

static execute(core, inst)[source]
mnemonic = 'rst'
opcode = <DuckyOpcodes.RST: 19>
class ducky.cpu.instructions.SETE(instruction_set)[source]

Bases: ducky.cpu.instructions._SET

mnemonic = 'sete'
class ducky.cpu.instructions.SETG(instruction_set)[source]

Bases: ducky.cpu.instructions._SET

mnemonic = 'setg'
class ducky.cpu.instructions.SETGE(instruction_set)[source]

Bases: ducky.cpu.instructions._SET

mnemonic = 'setge'
class ducky.cpu.instructions.SETL(instruction_set)[source]

Bases: ducky.cpu.instructions._SET

mnemonic = 'setl'
class ducky.cpu.instructions.SETLE(instruction_set)[source]

Bases: ducky.cpu.instructions._SET

mnemonic = 'setle'
class ducky.cpu.instructions.SETNE(instruction_set)[source]

Bases: ducky.cpu.instructions._SET

mnemonic = 'setne'
class ducky.cpu.instructions.SETNO(instruction_set)[source]

Bases: ducky.cpu.instructions._SET

mnemonic = 'setno'
class ducky.cpu.instructions.SETNS(instruction_set)[source]

Bases: ducky.cpu.instructions._SET

mnemonic = 'setns'
class ducky.cpu.instructions.SETNZ(instruction_set)[source]

Bases: ducky.cpu.instructions._SET

mnemonic = 'setnz'
class ducky.cpu.instructions.SETO(instruction_set)[source]

Bases: ducky.cpu.instructions._SET

mnemonic = 'seto'
class ducky.cpu.instructions.SETS(instruction_set)[source]

Bases: ducky.cpu.instructions._SET

mnemonic = 'sets'
class ducky.cpu.instructions.SETZ(instruction_set)[source]

Bases: ducky.cpu.instructions._SET

mnemonic = 'setz'
class ducky.cpu.instructions.SHIFTL(instruction_set)[source]

Bases: ducky.cpu.instructions._BITOP

static jit(core, inst)[source]
mnemonic = 'shiftl'
opcode = <DuckyOpcodes.SHIFTL: 38>
class ducky.cpu.instructions.SHIFTR(instruction_set)[source]

Bases: ducky.cpu.instructions._BITOP

static jit(core, inst)[source]
mnemonic = 'shiftr'
opcode = <DuckyOpcodes.SHIFTR: 39>
class ducky.cpu.instructions.SIS(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_RI

static execute(core, inst)[source]
mnemonic = 'sis'
opcode = <DuckyOpcodes.SIS: 63>
class ducky.cpu.instructions.STB(instruction_set)[source]

Bases: ducky.cpu.instructions._STORE

mnemonic = 'stb'
opcode = <DuckyOpcodes.STB: 6>
class ducky.cpu.instructions.STI(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor

encoding

alias of EncodingI

static execute(core, inst)[source]
mnemonic = 'sti'
opcode = <DuckyOpcodes.STI: 18>
class ducky.cpu.instructions.STS(instruction_set)[source]

Bases: ducky.cpu.instructions._STORE

mnemonic = 'sts'
opcode = <DuckyOpcodes.STS: 5>
class ducky.cpu.instructions.STW(instruction_set)[source]

Bases: ducky.cpu.instructions._STORE

mnemonic = 'stw'
opcode = <DuckyOpcodes.STW: 4>
class ducky.cpu.instructions.SUB(instruction_set)[source]

Bases: ducky.cpu.instructions._BINOP

static jit(core, inst)[source]
mnemonic = 'sub'
opcode = <DuckyOpcodes.SUB: 29>
class ducky.cpu.instructions.SWP(instruction_set)[source]

Bases: ducky.cpu.instructions.Descriptor_R_R

encoding

alias of EncodingR

static execute(core, inst)[source]
static jit(core, inst)[source]
mnemonic = 'swp'
opcode = <DuckyOpcodes.SWP: 12>
class ducky.cpu.instructions.UDIV(instruction_set)[source]

Bases: ducky.cpu.instructions._BINOP

mnemonic = 'udiv'
opcode = <DuckyOpcodes.UDIV: 32>
ducky.cpu.instructions.UINT20_FMT(i)[source]
class ducky.cpu.instructions.XOR(instruction_set)[source]

Bases: ducky.cpu.instructions._BITOP

static jit(core, inst)[source]
mnemonic = 'xor'
opcode = <DuckyOpcodes.XOR: 36>
ducky.cpu.instructions.encoding_to_u32(inst)[source]
ducky.cpu.instructions.get_instruction_set(i, exc=None)[source]
ducky.cpu.instructions.u32_to_encoding(u, encoding)[source]
ducky.cpu.instructions.update_arith_flags(core, reg)[source]

Set relevant arithmetic flags according to content of registers. Flags are set to zero at the beginning, then content of each register is examined, and S and Z flags are set.

E flag is not touched, O flag is set to zero.

Parameters:reg (u32_t) – register
ducky.cpu.registers module
class ducky.cpu.registers.RegisterSet[source]

Bases: object

class ducky.cpu.registers.Registers[source]

Bases: enum.IntEnum

CNT = <Registers.CNT: 33>
FP = <Registers.FP: 30>
IP = <Registers.IP: 32>
R00 = <Registers.R00: 0>
R01 = <Registers.R01: 1>
R02 = <Registers.R02: 2>
R03 = <Registers.R03: 3>
R04 = <Registers.R04: 4>
R05 = <Registers.R05: 5>
R06 = <Registers.R06: 6>
R07 = <Registers.R07: 7>
R08 = <Registers.R08: 8>
R09 = <Registers.R09: 9>
R10 = <Registers.R10: 10>
R11 = <Registers.R11: 11>
R12 = <Registers.R12: 12>
R13 = <Registers.R13: 13>
R14 = <Registers.R14: 14>
R15 = <Registers.R15: 15>
R16 = <Registers.R16: 16>
R17 = <Registers.R17: 17>
R18 = <Registers.R18: 18>
R19 = <Registers.R19: 19>
R20 = <Registers.R20: 20>
R21 = <Registers.R21: 21>
R22 = <Registers.R22: 22>
R23 = <Registers.R23: 23>
R24 = <Registers.R24: 24>
R25 = <Registers.R25: 25>
R26 = <Registers.R26: 26>
R27 = <Registers.R27: 27>
R28 = <Registers.R28: 28>
R29 = <Registers.R29: 29>
REGISTER_COUNT = <Registers.REGISTER_COUNT: 34>
REGISTER_SPECIAL = <Registers.FP: 30>
SP = <Registers.SP: 31>

Module contents

class ducky.cpu.CPU(machine, cpuid, memory_controller, cores=1)[source]

Bases: ducky.interfaces.ISnapshotable, ducky.interfaces.IMachineWorker

boot()[source]
die(exc)[source]
halt()[source]
load_state(state)[source]
on_core_alive(core)[source]

Triggered when one of cores goes alive.

on_core_halted(core)[source]

Signal CPU that one of cores is no longer alive.

on_core_running(core)[source]

Signal CPU that one of cores is now running.

on_core_suspended(core)[source]

Signal CPU that one of cores is now suspended.

save_state(parent)[source]
suspend()[source]
wake_up()[source]
class ducky.cpu.CPUCore(coreid, cpu, memory_controller)[source]

Bases: ducky.interfaces.ISnapshotable, ducky.interfaces.IMachineWorker

This class represents the main workhorse, one of CPU cores. Reads instructions, executes them, has registers, caches, handles interrupts, ...

Parameters:
  • coreid (int) – id of this core. Usually, it’s its serial number but it has no special meaning.
  • cpu (ducky.cpu.CPU) – CPU that owns this core.
  • memory_controller (ducky.mm.MemoryController) – use this controller to access main memory.
FP()[source]
IP()[source]
REG(reg)[source]
SP()[source]
backtrace()[source]
boot()[source]
change_runnable_state(alive=None, running=None, idle=None)[source]
check_protected_ins()[source]

Raise AccessViolationError if core is not running in privileged mode.

This method should be used by instruction handlers that require privileged mode, e.g. protected instructions.

Raises:AccessViolationError – if the core is not in privileged mode
check_protected_port(port)[source]
create_frame()[source]

Create new call stack frame. Push IP and FP registers and set FP value to SP.

destroy_frame()[source]

Destroy current call frame. Pop FP and IP from stack, by popping FP restores previous frame.

Raises:CPUException – if current frame does not match last created frame.
die(exc)[source]
do_int(index)[source]

Handle software interrupt. Real software interrupts cause CPU state to be saved and new stack and register values are prepared by __enter_interrupt method, virtual interrupts are simply triggered without any prior changes of CPU state.

Parameters:index (int) – interrupt number
exit_interrupt()[source]

Restore CPU state after running a interrupt routine. Call frame is destroyed, registers are restored, stack is returned back to memory pool.

flags
halt()[source]
has_coprocessor(name)[source]
init_debug_set()[source]
irq(index)[source]
load_state(state)[source]
pop(*regs)[source]
push(*regs)[source]
raw_pop()[source]

Pop value from stack. 4 byte number is read from address in SP, then SP is incremented by four.

Returns:popped value
Return type:u32
raw_push(val)[source]

Push value on stack. SP is decremented by four, and value is written at this new address.

Parameters:val (u32) – value to be pushed
reset(new_ip=0)[source]

Reset core’s state. All registers are set to zero, all flags are set to zero, except HWINT flag which is set to one, and IP is set to requested value.

Parameters:new_ip (u32_t) – new IP value, defaults to zero
run()[source]
save_state(parent)[source]
step()[source]

Perform one “step” - fetch next instruction, increment IP, and execute instruction’s code (see inst_* methods)

suspend()[source]
wake_up()[source]
class ducky.cpu.CPUCoreState[source]

Bases: ducky.snapshot.SnapshotNode

exception ducky.cpu.CPUException(msg, core=None, ip=None)[source]

Bases: exceptions.Exception

Base class for CPU-related exceptions.

Parameters:
  • msg (string) – message describing exceptional state.
  • core (ducky.cpu.CPUCore) – CPU core that raised exception, if any.
  • ip (u32_t) – address of an instruction that caused exception, if any.
class ducky.cpu.CPUState(*fields)[source]

Bases: ducky.snapshot.SnapshotNode

get_core_state_by_id(coreid)[source]
get_core_states()[source]
class ducky.cpu.CoreFlags[source]

Bases: ducky.util.Flags

ducky.cpu.DEFAULT_CORE_INST_CACHE_SIZE = 256

Default size of core instruction cache, in instructions.

ducky.cpu.DEFAULT_IVT_ADDRESS = 0

Default IVT address

ducky.cpu.DEFAULT_PT_ADDRESS = 65536

Default PT address

class ducky.cpu.InstructionCache(mmu, size, *args, **kwargs)[source]

Bases: ducky.util.LRUCache

Simple instruction cache class, based on LRU dictionary, with a limited size.

Parameters:
  • core (ducky.cpu.CPUCore) – CPU core that owns this cache.
  • size (int) – maximal number of entries this cache can store.
get_object(addr)[source]

Read instruction from memory. This method is responsible for the real job of fetching instructions and filling the cache.

Parameters:addr (u24) – absolute address to read from
Returns:instruction
Return type:InstBinaryFormat_Master
get_object_jit(addr)[source]

Read instruction from memory. This method is responsible for the real job of fetching instructions and filling the cache.

Parameters:addr (u24) – absolute address to read from
Returns:instruction
Return type:InstBinaryFormat_Master
class ducky.cpu.InterruptVector(ip=0, sp=0)[source]

Bases: object

Interrupt vector table entry.

SIZE = 8
exception ducky.cpu.InvalidInstructionSetError(inst_set, *args, **kwargs)[source]

Bases: ducky.cpu.CPUException

Raised when switch to unknown or invalid instruction set is requested.

Parameters:inst_set (int) – instruction set id.
exception ducky.cpu.InvalidOpcodeError(opcode, *args, **kwargs)[source]

Bases: ducky.cpu.CPUException

Raised when unknown or invalid opcode is found in instruction.

Parameters:opcode (int) – wrong opcode.
class ducky.cpu.MMU(core, memory_controller)[source]

Bases: ducky.interfaces.ISnapshotable

Memory management unit (aka MMU) provides a single point handling all core’s memory operations. All memory reads and writes must go through this unit, which is then responsible for all translations, access control, and caching.

Parameters:
check_access(access, addr, align=None)[source]

Check attempted access against PTE. Be aware that each check can be turned off by configuration file.

Parameters:
  • accessread, write or execute.
  • addr (u24) – memory address.
  • align (int) – if set, operation is expected to be aligned to this boundary.
Raises:

ducky.errors.AccessViolationError – when access is denied.

full_read_u16(addr)[source]
full_read_u32(addr, not_execute=True)[source]
full_read_u8(addr)[source]
full_write_u16(addr, value)[source]
full_write_u32(addr, value)[source]
full_write_u8(addr, value)[source]
get_pte(addr)[source]

Find out PTE for particular physical address. If PTE is not in internal PTE cache, it is fetched from PTE table.

Parameters:addr (u24) – memory address.
halt()[source]
release_ptes()[source]
reset()[source]
set_access_methods()[source]

Set parent core’s memory-access methods to proper shortcuts.

class ducky.cpu.StackFrame(fp)[source]

Bases: object

ducky.cpu.cmd_bt(console, cmd)[source]

Print current backtrace

ducky.cpu.cmd_cont(console, cmd)[source]

Continue execution until next breakpoint is reached: cont

ducky.cpu.cmd_core_state(console, cmd)[source]

Print core state

ducky.cpu.cmd_next(console, cmd)[source]

Proceed to the next instruction in the same stack frame.

ducky.cpu.cmd_set_core(console, cmd)[source]

Set core address of default core used by control commands: sc <coreid>

ducky.cpu.cmd_step(console, cmd)[source]

Step one instruction forward

ducky.cpu.do_log_cpu_core_state(core, logger=None, disassemble=True, inst_set=None)[source]

Log state of a CPU core. Content of its registers, and other interesting or useful internal variables are logged.

Parameters:
  • core (ducky.cpu.CPUCore) – core whose state should be logged.
  • logger – called for each line of output to actualy log it. By default, core’s ducky.cpu.CPUCore.DEBUG() method is used.
ducky.cpu.log_cpu_core_state(*args, **kwargs)[source]

This is a wrapper for ducky.cpu.do_log_cpu_core_state function. Its main purpose is to be removed when debug mode is not set, therefore all debug calls of ducky.cpu.do_log_cpu_core_state will disappear from code, making such code effectively “quiet”.

ducky.debugging module

Virtual machine debugging tools - break points, watch points, etc.

Create “point” that’s triggered when a condition is satisfied (e.g. processor executes instruction on specified address, memory at specified address was modified, etc. Then, create “action” (e.g. suspend core), and bind both pieces together - when point gets triggered, execute list of actions.

class ducky.debugging.Action(logger)[source]

Bases: object

Base class of all debugging actions.

Parameters:logger (logging.Logger) – logger instance used for logging.
act(core, point)[source]

This method is called when “action” is executed. Implement it in child classes to give child actions a functionality.

Parameters:
class ducky.debugging.BreakPoint(debugging_set, ip, *args, **kwargs)[source]

Bases: ducky.debugging.Point

static create_from_config(debugging_set, config, section)[source]
is_triggered(core)[source]
class ducky.debugging.DebuggingSet(core)[source]

Bases: object

add_point(p, chain)[source]
post_memory(address=None, read=None)[source]
post_step()[source]
pre_memory(address=None, read=None)[source]
pre_step()[source]
remove_point(p, chain)[source]
class ducky.debugging.LogMemoryContentAction(logger, address, size)[source]

Bases: ducky.debugging.LogValueAction

When triggered, logs content of a specified location in memory.

Parameters:
  • logger (logging.Logger) – logger instance used for logging.
  • address (u32_t) – memory location.
  • size (int) – size of logged number, in bytes.
static create_from_config(debugging_set, config, section)[source]
get_message(core, point)[source]
get_values(core, point)[source]
class ducky.debugging.LogRegisterContentAction(logger, registers)[source]

Bases: ducky.debugging.LogValueAction

When triggered, logs content of a specified register.

Parameters:
  • logger (logging.Logger) – logger instance used for logging.
  • registers (list) – list of register names.
static create_from_config(debugging_set, config, section)[source]
get_message(core, point)[source]
get_values(core, point)[source]
class ducky.debugging.LogValueAction(logger, size)[source]

Bases: ducky.debugging.Action

This is the base class for actions that log a numerical values.

Parameters:
  • logger (logging.Logger) – logger instance used for logging.
  • size (int) – size of logged number, in bytes.
act(core, point)[source]
get_message(core, point)[source]

Return message that, formatted with output of get_values(), will be shown to user.

Parameters:
Return type:

string

Returns:

information message.

get_values(core, point)[source]

Prepare dictionary with values for message that will be shown to the user.

Parameters:
Return type:

dict

Returns:

dictionary that will be passed to message format() method.

class ducky.debugging.MemoryWatchPoint(debugging_set, address, read, *args, **kwargs)[source]

Bases: ducky.debugging.Point

static create_from_config(debugging_set, config, section)[source]
is_triggered(core, address=None, read=None)[source]
class ducky.debugging.Point(debugging_set, active=True, countdown=0)[source]

Bases: object

Base class of all debugging points.

Parameters:
  • debugging_set (ducky.debugging.DebuggingSet) – debugging set this point belongs to.
  • active (bool) – if not True, point is not active and will not trigger.
  • countdown (int) – if greater than zero, point has to trigger countdown times before its actions are executed for the first time.
is_triggered(core, *args, **kwargs)[source]

Test point’s condition.

Parameters:core (ducky.cpu.CPUCore) – core requesting the test.
Return type:bool
Returns:True if condition is satisfied.
class ducky.debugging.SuspendCoreAction(logger)[source]

Bases: ducky.debugging.Action

If executed, this action will suspend the CPU core that triggered its parent point.

act(core, point)[source]
static create_from_config(debugging_set, config, section)[source]
class ducky.debugging.VMDebugInterrupt(machine)[source]

Bases: ducky.interfaces.IVirtualInterrupt

run(core)[source]
class ducky.debugging.VMDebugOperationList[source]

Bases: enum.Enum

LOGGER_VERBOSITY = <VMDebugOperationList.LOGGER_VERBOSITY: 0>
class ducky.debugging.VMVerbosityLevels[source]

Bases: enum.Enum

DEBUG = <VMVerbosityLevels.DEBUG: 0>
ERROR = <VMVerbosityLevels.ERROR: 3>
INFO = <VMVerbosityLevels.INFO: 1>
WARNING = <VMVerbosityLevels.WARNING: 2>
ducky.debugging.cmd_bp_active(console, cmd)[source]

Toggle “active” flag for a breakpoint: bp-active <id>

ducky.debugging.cmd_bp_add_breakpoint(console, cmd)[source]

Create new breakpoint: bp-break <#cpuid:#coreid> <address> [active] [countdown]

ducky.debugging.cmd_bp_add_memory_watchpoint(console, cmd)[source]

Create new memory watchpoint: bp-mwatch <#cpuid:#coreid> <address> [rw] [active] [countdown]’

ducky.debugging.cmd_bp_list(console, cmd)[source]

List existing breakpoints

ducky.debugging.cmd_bp_remove(console, cmd)[source]

Remove breakpoint: bp-remove <id>

ducky.devices package

Submodules

ducky.devices.keyboard module

Keyboard controller - provides events for pressed and released keys.

class ducky.devices.keyboard.Backend(machine, name, port=None, irq=None)[source]

Bases: ducky.devices.IRQProvider, ducky.devices.IOProvider, ducky.devices.DeviceBackend

boot()[source]
static create_from_config(machine, config, section)[source]
halt()[source]
read_u8(port)[source]
class ducky.devices.keyboard.ControlMessages[source]

Bases: enum.IntEnum

CONTROL_MESSAGE_FIRST = <ControlMessages.CONTROL_MESSAGE_FIRST: 1024>
HALT = <ControlMessages.HALT: 1025>
class ducky.devices.keyboard.Frontend(machine, name)[source]

Bases: ducky.devices.DeviceFrontend

boot()[source]
static create_from_config(machine, config, section)[source]
enqueue_stream(stream)[source]
halt()[source]
ducky.devices.rtc module
class ducky.devices.rtc.RTC(machine, name, frequency=None, port=None, irq=None, *args, **kwargs)[source]

Bases: ducky.devices.IRQProvider, ducky.devices.IOProvider, ducky.devices.Device

boot()[source]
static create_from_config(machine, config, section)[source]
halt()[source]
read_u8(port)[source]
write_u8(port, value)[source]
class ducky.devices.rtc.RTCTask(machine, rtc)[source]

Bases: ducky.reactor.RunInIntervalTask

on_tick(task)[source]
update_tick()[source]
ducky.devices.snapshot module
class ducky.devices.snapshot.DefaultFileSnapshotStorage(machine, name, filepath=None, *args, **kwargs)[source]

Bases: ducky.devices.snapshot.FileSnapshotStorage

static create_from_config(machine, config, section)[source]
class ducky.devices.snapshot.FileSnapshotStorage(machine, name, filepath=None, *args, **kwargs)[source]

Bases: ducky.devices.snapshot.SnapshotStorage

boot()[source]
static create_from_config(machine, config, section)[source]
halt()[source]
save_snapshot(snapshot)[source]
class ducky.devices.snapshot.SnapshotStorage(machine, name, *args, **kwargs)[source]

Bases: ducky.devices.Device

static create_from_config(machine, config, section)[source]
halt()[source]
save_snapshot(snapshot)[source]
ducky.devices.storage module

Persistent storage support.

Several different persistent storages can be attached to a virtual machine, each with its own id. This module provides methods for manipulating their content. By default, storages operate with blocks of constant, standard size, though this is not a mandatory requirement - storage with different block size, or even with variable block size can be implemented.

Each block has its own id. Block IO operations read or write one or more blocks to or from a device. IO is requested by invoking the virtual interrupt, with properly set values in registers.

ducky.devices.storage.BLOCK_SIZE = 1024

Size of block, in bytes.

class ducky.devices.storage.BlockIO(machine, name, port=None, irq=None, *args, **kwargs)[source]

Bases: ducky.devices.IRQProvider, ducky.devices.IOProvider, ducky.devices.Device

boot()[source]
buff_to_memory(addr, buff)[source]
static create_from_config(machine, config, section)[source]
halt()[source]
memory_to_buff(addr, length)[source]
read_u32(port)[source]
reset()[source]
write_u32(port, value)[source]
class ducky.devices.storage.FileBackedStorage(machine, name, filepath=None, *args, **kwargs)[source]

Bases: ducky.devices.storage.Storage

Storage that saves its content into a regular file.

boot()[source]
static create_from_config(machine, config, section)[source]
do_read_blocks(start, cnt)[source]
do_write_blocks(start, cnt, buff)[source]
halt()[source]
class ducky.devices.storage.Storage(machine, name, sid=None, size=None, *args, **kwargs)[source]

Bases: ducky.devices.Device

Base class for all block storages.

Parameters:
  • machine (ducky.machine.Machine) – machine storage is attached to.
  • sid (int) – id of storage.
  • size (int) – size of storage, in bytes.
do_read_blocks(start, cnt)[source]

Read one or more blocks from device to internal buffer.

Child classes are supposed to reimplement this particular method.

Parameters:
  • start (u32_t) – index of the first requested block.
  • cnt (u32_t) – number of blocks to read.
do_write_blocks(start, cnt, buff)[source]

Write one or more blocks from internal buffer to device.

Child classes are supposed to reimplement this particular method.

Parameters:
  • start (u32_t) – index of the first requested block.
  • cnt (u32_t) – number of blocks to write.
read_blocks(start, cnt)[source]

Read one or more blocks from device to internal buffer.

Child classes should not reimplement this method, as it provides checks common for (probably) all child classes.

Parameters:
  • start (u32_t) – index of the first requested block.
  • cnt (u32_t) – number of blocks to read.
write_blocks(start, cnt, buff)[source]

Write one or more blocks from internal buffer to device.

Child classes should not reimplement this method, as it provides checks common for (probably) all child classes.

Parameters:
  • start (u32_t) – index of the first requested block.
  • cnt (u32_t) – number of blocks to write.
exception ducky.devices.storage.StorageAccessError[source]

Bases: exceptions.Exception

Base class for storage-related exceptions.

ducky.devices.terminal module

Terminal is a device that groups together character two input and output devices, thus forming a simple channel for bidirectional communication between VM and user.

Terminal has two slave frontends:
  • input, usually a keyboard
  • output, from simple TTY to more powerful devices

Terminal then manages input and input streams, passing them to its slave devices, which then transports events between streams and VM’s comm channel.

class ducky.devices.terminal.StandalonePTYTerminal(*args, **kwargs)[source]

Bases: ducky.devices.terminal.StreamIOTerminal

boot()[source]
static create_from_config(machine, config, section)[source]
halt()[source]
class ducky.devices.terminal.StandardIOTerminal(machine, name, input_device=None, output_device=None, *args, **kwargs)[source]

Bases: ducky.devices.terminal.StreamIOTerminal

static create_from_config(machine, config, section)[source]
class ducky.devices.terminal.StreamIOTerminal(machine, name, input_device=None, output_device=None, *args, **kwargs)[source]

Bases: ducky.devices.terminal.Terminal

boot()[source]
static create_from_config(machine, config, section)[source]
enqueue_input_stream(stream)[source]
enqueue_streams(streams_in=None, stream_out=None)[source]
halt()[source]
class ducky.devices.terminal.Terminal(machine, name, echo=False, *args, **kwargs)[source]

Bases: ducky.devices.DeviceFrontend

boot()[source]
halt()[source]
ducky.devices.terminal.get_slave_devices(machine, config, section)[source]
ducky.devices.terminal.parse_io_streams(machine, config, section)[source]
ducky.devices.tty module

Very simple character device that just “prints” characters on the screen. It does not care about dimensions of the display, it kknow only how to “print” characters. Suited for the most basic output possible - just “print” chars by writing to this device, and you’ll get this written into a stream attached to the frontend (stdout, file, ...).

class ducky.devices.tty.Backend(machine, name, stream=None, port=None, *args, **kwargs)[source]

Bases: ducky.devices.IOProvider, ducky.devices.DeviceBackend

boot()[source]
static create_from_config(machine, config, section)[source]
halt()[source]
tenh(s, *args)[source]
tenh_close_stream()[source]
tenh_enable()[source]
tenh_flush_stream()[source]
write_u8(port, value)[source]
class ducky.devices.tty.Frontend(machine, name)[source]

Bases: ducky.devices.DeviceFrontend

boot()[source]
close(allow=False)[source]
static create_from_config(machine, config, section)[source]
flush()[source]
halt()[source]
set_output(stream)[source]
tenh_enable()[source]
class ducky.devices.tty.FrontendFlushTask(frontend, queue, stream)[source]

Bases: ducky.interfaces.IReactorTask

run()[source]
set_output(stream)[source]
ducky.devices.svga module

SimpleVGA is very basic implementation of VGA-like device, with text and graphic modes.

class ducky.devices.svga.Char[source]

Bases: _ctypes.Structure

bg

Structure/Union member

Structure/Union member

codepoint

Structure/Union member

fg

Structure/Union member

static from_u16(u)[source]
static from_u8(l, h)[source]
to_u8()[source]
unused

Structure/Union member

ducky.devices.svga.DEFAULT_BOOT_MODE = (t, 80, 25, 1)

Default boot mode

ducky.devices.svga.DEFAULT_MEMORY_BANKS = 8

Default number of memory banks

ducky.devices.svga.DEFAULT_MEMORY_SIZE = 65536

Default memory size, in bytes

ducky.devices.svga.DEFAULT_MODES = [(g, 320, 200, 1), (t, 80, 25, 2), (t, 80, 25, 1)]

Default list of available modes

ducky.devices.svga.DEFAULT_PORT_RANGE = 1008

Default address of command port

class ducky.devices.svga.Display(machine, name, gpu=None, stream_out=None, *args, **kwargs)[source]

Bases: ducky.devices.Device

boot()[source]
static create_from_config(machine, config, section)[source]
static get_slave_gpu(machine, config, section)[source]
halt()[source]
class ducky.devices.svga.DisplayRefreshTask(display)[source]

Bases: ducky.reactor.RunInIntervalTask

on_tick(task)[source]
class ducky.devices.svga.Mode(_type, width, height, depth)[source]

Bases: object

classmethod from_string(s)[source]

Create Mode object from its string representation. It’s a comma-separated list of for items:

  type width height depth
Text mode t chars per line lines on screen bytes per char
Graphic mode g pixels per line pixel lines on screen bites per pixel
to_pretty_string()[source]
to_string()[source]
class ducky.devices.svga.SimpleVGA(machine, name, port=None, memory_size=None, memory_address=None, memory_banks=None, modes=None, boot_mode=None, *args, **kwargs)[source]

Bases: ducky.devices.IOProvider, ducky.devices.Device

SimpleVGA is very basic implementation of VGA-like device, with text and graphic modes.

It has its own graphic memory (“buffer”), split into several banks of the same size. Always only one bank can be directly accessed, by having it mapped into CPU’s address space.

Parameters:
  • machine (ducky.machine.Machine) – machine this device belongs to.
  • name (string) – name of this device.
  • port (u16) – address of the command port.
  • memory_size (int) – size of graphic memory.
  • memory_address (u24) – address of graphic memory - to this address is graphic buffer mapped. Must be specified, there is no default value.
  • memory_banks (int) – number of memory banks.
  • modes (list) – list of ducky.devices.svga.Mode objects, list of supported modes.
  • boot_mode (tuple) – this mode will be set when device boots up.
boot()[source]
static create_from_config(machine, config, section)[source]
halt()[source]
read_u16(port)[source]
reset()[source]
set_mode(mode)[source]
write_u16(port, value)[source]
class ducky.devices.svga.SimpleVGACommands[source]

Bases: enum.IntEnum

COLS = <SimpleVGACommands.COLS: 33>
DEPTH = <SimpleVGACommands.DEPTH: 35>
GRAPHIC = <SimpleVGACommands.GRAPHIC: 32>
MEMORY_BANK_ID = <SimpleVGACommands.MEMORY_BANK_ID: 48>
REFRESH = <SimpleVGACommands.REFRESH: 2>
RESET = <SimpleVGACommands.RESET: 32769>
ROWS = <SimpleVGACommands.ROWS: 34>
class ducky.devices.svga.SimpleVGAMemoryPage(dev, *args, **kwargs)[source]

Bases: ducky.mm.ExternalMemoryPage

Memory page handling MMIO of sVGA device.

Parameters:dev (ducky.devices.svga.SimpleVGA) – sVGA device this page belongs to.
get(offset)[source]
put(offset, b)[source]

Module contents

class ducky.devices.Device(machine, klass, name)[source]

Bases: ducky.interfaces.IMachineWorker

boot()[source]
static create_from_config(machine, config, section)[source]
get_master()[source]
halt()[source]
is_slave()[source]
class ducky.devices.DeviceBackend(machine, klass, name)[source]

Bases: ducky.devices.Device

set_frontend(device)[source]
class ducky.devices.DeviceFrontend(machine, klass, name)[source]

Bases: ducky.devices.Device

set_backend(device)[source]
class ducky.devices.IOPorts[source]

Bases: enum.IntEnum

PORT_COUNT = <IOPorts.PORT_COUNT: 65536>
class ducky.devices.IOProvider[source]

Bases: object

is_port_protected(port, read=True)[source]
read_u16(port)[source]
read_u32(port)[source]
read_u8(port)[source]
write_u16(port, value)[source]
write_u32(port, value)[source]
write_u8(port, value)[source]
class ducky.devices.IRQList[source]

Bases: enum.IntEnum

List of known IRQ sources.

BIO = <IRQList.BIO: 2>
BLOCKIO = <IRQList.BLOCKIO: 17>
HALT = <IRQList.HALT: 16>
IRQ_COUNT = <IRQList.IRQ_COUNT: 32>
KEYBOARD = <IRQList.KEYBOARD: 1>
TIMER = <IRQList.TIMER: 0>
VMDEBUG = <IRQList.VMDEBUG: 18>
class ducky.devices.IRQProvider[source]

Bases: object

ducky.devices.get_driver_creator(driver_class)[source]

ducky.errors module

exception ducky.errors.AccessViolationError(message=None)[source]

Bases: ducky.errors.Error

exception ducky.errors.AssemblerError(location=None, message=None, line=None, info=None)[source]

Bases: ducky.errors.Error

create_text()[source]
log(logger)[source]
exception ducky.errors.DisassembleMismatchError(**kwargs)[source]

Bases: ducky.errors.AssemblerError

exception ducky.errors.EncodingLargeValueError(**kwargs)[source]

Bases: ducky.errors.AssemblerError

exception ducky.errors.Error(message=None)[source]

Bases: exceptions.Exception

exception ducky.errors.IncompatibleLinkerFlagsError(message=None)[source]

Bases: ducky.errors.Error

exception ducky.errors.IncompleteDirectiveError(**kwargs)[source]

Bases: ducky.errors.AssemblerError

exception ducky.errors.InvalidResourceError(message=None)[source]

Bases: ducky.errors.Error

exception ducky.errors.TooManyLabelsError(**kwargs)[source]

Bases: ducky.errors.AssemblerError

exception ducky.errors.UnalignedJumpTargetError(**kwargs)[source]

Bases: ducky.errors.AssemblerError

exception ducky.errors.UnknownFileError(**kwargs)[source]

Bases: ducky.errors.AssemblerError

exception ducky.errors.UnknownPatternError(**kwargs)[source]

Bases: ducky.errors.AssemblerError

exception ducky.errors.UnknownSymbolError(message=None)[source]

Bases: ducky.errors.Error

ducky.hdt module

Hardware Description Table structures.

class ducky.hdt.HDT(logger, config=None)[source]

Bases: object

Root of HDT. Provides methods for creating HDT for a given machine configuration.

Parameters:
  • logger (logging.Logger) – logger instance used for logging.
  • config (ducky.config.MachineConfig) – configuration file HDT should reflect.
create()[source]

Create HDT header and entries from config file.

klasses = [<class 'ducky.hdt.HDTEntry_Memory'>, <class 'ducky.hdt.HDTEntry_CPU'>, <class 'ducky.hdt.HDTEntry_Argument'>]
class ducky.hdt.HDTEntry(entry_type, length)[source]

Bases: ducky.hdt.HDTStructure

Base class of all HDT entries.

Each entry has at least two fields, type and length. Other fields depend on type of entry, and follow immediately after length field.

Parameters:
classmethod create(*args, **kwargs)[source]
class ducky.hdt.HDTEntryTypes[source]

Bases: enum.IntEnum

Types of different HDT entries.

ARGUMENT = <HDTEntryTypes.ARGUMENT: 3>
CPU = <HDTEntryTypes.CPU: 1>
MEMORY = <HDTEntryTypes.MEMORY: 2>
UNDEFINED = <HDTEntryTypes.UNDEFINED: 0>
class ducky.hdt.HDTEntry_Argument(arg_name, arg_type, arg_value)[source]

Bases: ducky.hdt.HDTEntry

MAX_NAME_LENGTH = 13
classmethod create(logger, config)[source]
length

Structure/Union member

name

Structure/Union member

name_length

Structure/Union member

type

Structure/Union member

value

Structure/Union member

value_length

Structure/Union member

class ducky.hdt.HDTEntry_CPU(logger, config)[source]

Bases: ducky.hdt.HDTEntry

HDT entry describing CPU configuration.

Parameters:
  • nr_cpus (u16_t) – number of CPUs.
  • nr_cores (u16_t) – number of cores per CPU.
length

Structure/Union member

nr_cores

Structure/Union member

nr_cpus

Structure/Union member

type

Structure/Union member

class ducky.hdt.HDTEntry_Memory(logger, config)[source]

Bases: ducky.hdt.HDTEntry

HDT entry describing memory configuration.

Parameters:size (u32_t) – size of memory, in bytes.
length

Structure/Union member

size

Structure/Union member

type

Structure/Union member

class ducky.hdt.HDTHeader[source]

Bases: ducky.hdt.HDTStructure

HDT header. Contains magic number, number of HDT entries that immediately follow header.

entries

Structure/Union member

length

Structure/Union member

magic

Structure/Union member

class ducky.hdt.HDTStructure[source]

Bases: _ctypes.Structure

Base class of all HDT structures.

ducky.hdt.HDT_MAGIC = 1298034544

Magic number present in HDT header

ducky.interfaces module

class ducky.interfaces.IMachineWorker[source]

Bases: object

Base class for objects that provide pluggable service to others.

boot(*args)[source]

Prepare for providing the service. After this call, it may be requested by others.

die(exc)[source]

Exceptional state requires immediate termination of service. Probably no object will ever have need to call others’ die method, it’s intended for internal use only.

halt()[source]

Terminate service. It will never be requested again, object can destroy its internal state, and free allocated resources.

run()[source]

Called by reactor’s loop when this object is enqueued as a reactor task.

suspend()[source]

Suspend service. Object should somehow conserve its internal state, its service will not be used until the next call of wake_up method.

wake_up()[source]

Wake up service. In this method, object should restore its internal state, and after this call its service can be requested by others again.

class ducky.interfaces.IReactorTask[source]

Bases: object

Base class for all reactor tasks.

run()[source]

This method is called by reactor to perform task’s actions.

class ducky.interfaces.ISnapshotable[source]

Bases: object

Base class for objects that can be saved into a snapshot.

load_state(state)[source]

Restore state of the object.

Parameters:state (ducky.snapshot.SnapshotNode) – snapshot node containing saved state.
save_state(parent)[source]

Create state of the object, and attach it to a parent snapshot node.

Parameters:parent (ducky.interfaces.ISnapshotable) – parent snapshot node.
class ducky.interfaces.IVirtualInterrupt(machine)[source]

Bases: object

run(core)[source]

ducky.log module

ducky.log.BLUE(s)[source]
ducky.log.GREEN(s)[source]
class ducky.log.LogFormatter(fmt=None, datefmt=None)[source]

Bases: logging.Formatter

format(record)[source]
ducky.log.RED(s)[source]
class ducky.log.StreamHandler(*args, **kwargs)[source]

Bases: logging.StreamHandler

ducky.log.WHITE(s)[source]
ducky.log.create_logger(name=None, handler=None)[source]

ducky.machine module

ducky.machine.Machine is the virtual machine. Each instance represents self-contained virtual machine, with all its devices, memory, CPUs and other necessary properties.

class ducky.machine.CommChannel(machine)[source]

Bases: object

create_queue(name)[source]
get_queue(name)[source]
unregister_queue(name)[source]
class ducky.machine.CommQueue(channel)[source]

Bases: object

is_empty_in()[source]
is_empty_out()[source]
read_in()[source]
read_out()[source]
write_in(o)[source]
write_out(o)[source]
class ducky.machine.EventBus(machine)[source]

Bases: object

add_listener(event, callback, *args, **kwargs)[source]
remove_listener(event, callback)[source]
trigger(event, *args, **kwargs)[source]
class ducky.machine.HaltMachineTask(machine)[source]

Bases: ducky.interfaces.IReactorTask

run()[source]
class ducky.machine.IRQRouterTask(machine)[source]

Bases: ducky.interfaces.IReactorTask

This task is responsible for distributing triggered IRQs between CPU cores. When IRQ is triggered, IRQ source (i.e. device that requires attention) is appended to this tasks queue (ducky.machine.IRQRouterTask.qeueu). As long as this queue is not empty, this task pops IRQ sources, selects free CPU core, and by calling its ducky.cpu.CPUCore.irq() method core takes reponsibility for executing interrupt routine.

Parameters:machine (ducky.machine.Machine) – machine this task belongs to.
run()[source]
class ducky.machine.Machine(logger=None, stdin=None, stdout=None, stderr=None)[source]

Bases: ducky.interfaces.ISnapshotable, ducky.interfaces.IMachineWorker

Virtual machine itself.

boot()[source]
capture_state(suspend=False)[source]

Capture current state of the VM, and store it in it’s last_state attribute.

Parameters:suspend (bool) – if True, suspend VM before taking snapshot.
core(cid)[source]

Find CPU core by its string id.

Parameters:cid (string) – id of searched CPU core, in the form #<cpuid>:#<coreid>.
Return type:ducky.cpu.CPUCore
Returns:found core
Raises:ducky.errors.InvalidResourceError – when no such core exists.
cores

Get list of all cores in the machine.

Return type:list
Returns:list of ducky.cpu.CPUCore instances
die(exc)[source]
exit_code
get_device_by_name(name, klass=None)[source]

Get device by its name and class.

Parameters:
  • name (string) – name of the device.
  • klass (string) – if set, search only devices with this class.
Return type:

ducky.devices.Device

Returns:

found device

Raises:

ducky.errors.InvalidResourceError – when no such device exists

get_storage_by_id(sid)[source]

Get storage by its id.

Parameters:sid (int) – id of storage caller is looking for.
Return type:ducky.devices.Device
Returns:found device.
Raises:ducky.errors.InvalidResourceError – when no such storage exists.
halt()[source]
hw_setup(machine_config)[source]
load_state(state)[source]
on_core_alive(core)[source]

Signal machine that one of CPU cores is now alive.

on_core_halted(core)[source]

Signal machine that one of CPU cores is no longer alive.

register_port(port, handler)[source]
run()[source]
save_state(parent)[source]
setup_devices()[source]
suspend()[source]
tenh(s, *args)[source]
trigger_irq(handler)[source]
unregister_port(port)[source]
wake_up()[source]
class ducky.machine.MachineState[source]

Bases: ducky.snapshot.SnapshotNode

get_cpu_state_by_id(cpuid)[source]
get_cpu_states()[source]
ducky.machine.cmd_boot(console, cmd)[source]

Setup HW, load binaries, init everything

ducky.machine.cmd_halt(console, cmd)[source]

Halt execution

ducky.machine.cmd_run(console, cmd)[source]

Start execution of loaded binaries

ducky.machine.cmd_snapshot(console, cmd)[source]

Create snapshot

ducky.mm package

Submodules

ducky.mm.binary module
class ducky.mm.binary.File(logger, stream)[source]

Bases: ducky.util.BinaryFile

MAGIC = 57005
VERSION = 1
create_header()[source]
create_section()[source]
get_header()[source]
get_section(i)[source]
get_section_by_name(name)[source]
load()[source]
load_symbols()[source]
static open(*args, **kwargs)[source]
save()[source]
sections()[source]
set_content(header, content)[source]
setup()[source]
class ducky.mm.binary.FileFlags[source]

Bases: ducky.util.Flags

field = ('mmapable', <class 'ctypes.c_ushort'>, 1)
class ducky.mm.binary.FileFlagsEncoding[source]

Bases: _ctypes.Structure

mmapable

Structure/Union member

class ducky.mm.binary.FileHeader[source]

Bases: _ctypes.Structure

flags

Structure/Union member

magic

Structure/Union member

sections

Structure/Union member

version

Structure/Union member

class ducky.mm.binary.RelocEntry[source]

Bases: _ctypes.Structure

flags

Structure/Union member

name

Structure/Union member

patch_add

Structure/Union member

patch_address

Structure/Union member

patch_offset

Structure/Union member

patch_section

Structure/Union member

patch_size

Structure/Union member

class ducky.mm.binary.RelocFlags[source]

Bases: ducky.util.Flags

field = ('inst_aligned', <class 'ctypes.c_ushort'>, 1)
class ducky.mm.binary.RelocFlagsEncoding[source]

Bases: _ctypes.Structure

inst_aligned

Structure/Union member

relative

Structure/Union member

class ducky.mm.binary.SectionFlags[source]

Bases: ducky.util.Flags

field = ('globally_visible', <class 'ctypes.c_ubyte'>, 1)
class ducky.mm.binary.SectionFlagsEncoding[source]

Bases: _ctypes.Structure

bss

Structure/Union member

executable

Structure/Union member

globally_visible

Structure/Union member

loadable

Structure/Union member

mmapable

Structure/Union member

readable

Structure/Union member

writable

Structure/Union member

class ducky.mm.binary.SectionHeader[source]

Bases: _ctypes.Structure

base

Structure/Union member

data_size

Structure/Union member

file_size

Structure/Union member

flags

Structure/Union member

index

Structure/Union member

items

Structure/Union member

name

Structure/Union member

offset

Structure/Union member

padding

Structure/Union member

type

Structure/Union member

class ducky.mm.binary.SectionTypes[source]

Bases: enum.IntEnum

DATA = <SectionTypes.DATA: 2>
RELOC = <SectionTypes.RELOC: 5>
STRINGS = <SectionTypes.STRINGS: 4>
SYMBOLS = <SectionTypes.SYMBOLS: 3>
TEXT = <SectionTypes.TEXT: 1>
UNKNOWN = <SectionTypes.UNKNOWN: 0>
class ducky.mm.binary.SymbolDataTypes[source]

Bases: enum.IntEnum

ASCII = <SymbolDataTypes.ASCII: 5>
BYTE = <SymbolDataTypes.BYTE: 3>
CHAR = <SymbolDataTypes.CHAR: 2>
FUNCTION = <SymbolDataTypes.FUNCTION: 6>
INT = <SymbolDataTypes.INT: 0>
SHORT = <SymbolDataTypes.SHORT: 1>
STRING = <SymbolDataTypes.STRING: 4>
UNKNOWN = <SymbolDataTypes.UNKNOWN: 7>
class ducky.mm.binary.SymbolEntry[source]

Bases: _ctypes.Structure

address

Structure/Union member

filename

Structure/Union member

flags

Structure/Union member

lineno

Structure/Union member

name

Structure/Union member

section

Structure/Union member

size

Structure/Union member

type

Structure/Union member

class ducky.mm.binary.SymbolFlags[source]

Bases: ducky.util.Flags

field = ('globally_visible', <class 'ctypes.c_ushort'>, 1)
class ducky.mm.binary.SymbolFlagsEncoding[source]

Bases: _ctypes.Structure

globally_visible

Structure/Union member

Module contents

class ducky.mm.AnonymousMemoryPage(controller, index)[source]

Bases: ducky.mm.MemoryPage

“Anonymous” memory page - this page is just a plain array of bytes, and is not backed by any storage. Its content lives only in the memory.

Page is created with all bytes set to zero.

clear()[source]
read_u16(offset)[source]
read_u32(offset)[source]
read_u8(offset)[source]
write_u16(offset, value)[source]
write_u32(offset, value)[source]
write_u8(offset, value)[source]
class ducky.mm.ExternalMemoryPage(controller, index, data, offset=0)[source]

Bases: ducky.mm.MemoryPage

Memory page backed by an external source. Source is an array of bytes, and can be provided by device driver, mmaped file, or by any other mean.

clear()[source]
get(offset)[source]

Get one byte from page. Override this method in case you need a different offset of requested byte.

Parameters:offset (int) – offset of the requested byte.
Return type:int
Returns:byte at position in page.
put(offset, b)[source]

Put one byte into page. Override this method in case you need a different offset of requested byte.

Parameters:
  • offset (int) – offset of modified byte.
  • b (int) – new value.
read_u16(offset)[source]
read_u32(offset)[source]
read_u8(offset)[source]
save_state(parent)[source]
write_u16(offset, value)[source]
write_u32(offset, value)[source]
write_u8(offset, value)[source]
class ducky.mm.MMOperationList[source]

Bases: enum.IntEnum

ALLOC = <MMOperationList.ALLOC: 3>
FREE = <MMOperationList.FREE: 4>
MMAP = <MMOperationList.MMAP: 6>
UNMMAP = <MMOperationList.UNMMAP: 7>
UNUSED = <MMOperationList.UNUSED: 5>
exception ducky.mm.MalformedBinaryError[source]

Bases: exceptions.Exception

class ducky.mm.MemoryController(machine, size=16777216)[source]

Bases: object

Memory controller handles all operations regarding main memory.

Parameters:
  • machine (ducky.machine.Machine) – virtual machine that owns this controller.
  • size (int) – size of memory, in bytes.
Raises:

ducky.errors.InvalidResourceError – when memory size is not multiple of ducky.mm.PAGE_SIZE.

alloc_page(base=None)[source]

Allocate new anonymous page for usage. The first available index is used.

Parameters:base (int) – if set, start searching pages from this address.
Returns:newly reserved page.
Return type:ducky.mm.AnonymousMemoryPage
Raises:ducky.errors.InvalidResourceError – when there is no available page.
alloc_pages(base=None, count=1)[source]

Allocate continuous sequence of anonymous pages.

Parameters:
  • base (u24) – if set, start searching pages from this address.
  • count (int) – number of requested pages.
Returns:

list of newly allocated pages.

Return type:

list of ducky.mm.AnonymousMemoryPage

Raises:

ducky.errors.InvalidResourceError – when there is no available sequence of pages.

alloc_specific_page(index)[source]

Allocate new anonymous page with specific index for usage.

Parameters:index (int) – allocate page with this particular index.
Returns:newly reserved page.
Return type:ducky.mm.AnonymousMemoryPage
Raises:ducky.errors.AccessViolationError – when page is already allocated.
boot()[source]

Prepare memory controller for immediate usage by other components.

free_page(page)[source]

Free memory page when it’s no longer needed.

Parameters:page (ducky.mm.MemoryPage) – page to be freed.
free_pages(page, count=1)[source]

Free a continuous sequence of pages when they are no longer needed.

Parameters:
get_page(index)[source]

Return memory page, specified by its index from the beginning of memory.

Parameters:index (int) – index of requested page.
Return type:ducky.mm.MemoryPage
Raises:ducky.errors.AccessViolationError – when requested page is not allocated.
get_pages(pages_start=0, pages_cnt=None, ignore_missing=False)[source]

Return list of memory pages.

Parameters:
  • pages_start (int) – index of the first page, 0 by default.
  • pages_cnt (int) – number of pages to get, number of all memory pages by default.
  • ignore_missing (bool) – if True, ignore missing pages, False by default.
Raises:

ducky.errors.AccessViolationError – when ignore_missing == False and there’s a missing page in requested range, this exception is rised.

Returns:

list of pages in area

Return type:

list of ducky.mm.MemoryPage

halt()[source]
load_state(state)[source]
pages_in_area(address=0, size=None, ignore_missing=False)[source]

Return list of memory pages.

Parameters:
  • address (u24) – beggining address of the area, by default 0.
  • size (u24) – size of the area, by default the whole memory size.
  • ignore_missing (bool) – if True, ignore missing pages, False by default.
Raises:

ducky.errors.AccessViolationError – when ignore_missing == False and there’s a missing page in requested range, this exception is rised.

Returns:

list of pages in area

Return type:

list of ducky.mm.MemoryPage

read_u16(addr)[source]
read_u32(addr)[source]
read_u8(addr)[source]
register_page(pg)[source]

Install page object for a specific memory page. This method is intended for external objects, e.g. device drivers to install their memory page objects to handle memory-mapped IO.

Parameters:pg (ducky.mm.MemoryPage) – page to be installed
Returns:installed page
Return type:ducky.mm.AnonymousMemoryPage
Raises:ducky.errors.AccessViolationError – when there is already allocated page
save_state(parent)[source]
unregister_page(pg)[source]

Remove page object for a specific memory page. This method is intende for external objects, e.g. device drivers to remove their memory page objects handling memory-mapped IO.

Parameters:pg (ducky.mm.MemoryPage) – page to be removed
Raises:ducky.errors.AccessViolationError – when there is no allocated page
write_u16(addr, value)[source]
write_u32(addr, value)[source]
write_u8(addr, value)[source]
class ducky.mm.MemoryPage(controller, index)[source]

Bases: object

Base class for all memory pages of any kinds.

Memory page has a set of boolean flags that determine access to and behavior of the page.

Flag Meaning Default
read page is readable by executed instructions False
write page is writable by executed instructions False
execute content of the page can be used as executable instructions False
dirty there have been write access to this page, its content has changed False
Parameters:
clear()[source]

Clear page.

This operation is implemented by child classes.

load_state(state)[source]

Restore page from a snapshot.

read_u16(offset)[source]

Read word.

This operation is implemented by child classes.

Parameters:offset (int) – offset of requested word.
Return type:int
read_u32(offset)[source]

Read longword.

This operation is implemented by child classes.

Parameters:offset (int) – offset of requested longword.
Return type:int
read_u8(offset)[source]

Read byte.

This operation is implemented by child classes.

Parameters:offset (int) – offset of requested byte.
Return type:int
save_state(parent)[source]

Create state of this page, and attach it to snapshot tree.

Parameters:parent (ducky.snapshot.SnapshotNode) – Parent snapshot node.
write_u16(offset, value)[source]

Write word.

This operation is implemented by child classes.

Parameters:
  • offset (int) – offset of requested word.
  • value (int) – value to write into memory.
write_u32(offset, value)[source]

Write longword.

This operation is implemented by child classes.

Parameters:
  • offset (int) – offset of requested longword.
  • value (int) – value to write into memory.
write_u8(offset, value)[source]

Write byte.

This operation is implemented by child classes.

Parameters:
  • offset (int) – offset of requested byte.
  • value (int) – value to write into memory.
class ducky.mm.MemoryPageState(*args, **kwargs)[source]

Bases: ducky.snapshot.SnapshotNode

class ducky.mm.MemoryRegion(mc, name, address, size, flags)[source]

Bases: ducky.interfaces.ISnapshotable, object

load_state(state)[source]
region_id = 0
save_state(parent)[source]
class ducky.mm.MemoryRegionState[source]

Bases: ducky.snapshot.SnapshotNode

class ducky.mm.MemoryState[source]

Bases: ducky.snapshot.SnapshotNode

get_page_states()[source]
ducky.mm.OFFSET_FMT(offset)[source]
ducky.mm.PAGE_SIZE = 256

Size of memory page, in bytes.

class ducky.mm.PageTableEntry[source]

Bases: ducky.util.Flags

DIRTY = 8
EXECUTE = 4
READ = 1
WRITE = 2
ducky.mm.SIZE_FMT(size)[source]
ducky.mm.addr_to_offset(addr)[source]
ducky.mm.addr_to_page(addr)[source]
ducky.mm.area_to_pages(addr, size)[source]

ducky.patch module

class ducky.patch.Importer[source]

Bases: object

find_module(fullname, path=None)[source]
loader_for_path(directory, fullname)[source]
class ducky.patch.ModuleLoader(fullpath)[source]

Bases: object

get_code(fullname)[source]
get_source(path)[source]
load_module(fullname)[source]
class ducky.patch.RemoveLoggingVisitor[source]

Bases: ast.NodeTransformer

visit_Expr(node)[source]
visit_For(node)[source]
visit_If(node)[source]
ducky.patch.debug(s)[source]
ducky.patch.exec_f(object_, globals_=None, locals_=None)[source]

ducky.profiler module

This module provides support for profiling the virtual machine (machine profilers) and running programs (code profilers). Wrappers for python’s deterministic profilers, and simple statistical profiler of running code are available for usage thoughout the ducky sources.

cProfile is the prefered choice of machine profiling backend, with profile is used as a backup option.

Beware, each thread needs its own profiler instance. These instances can be acquired from ProfilerStore class which handles saving their data when virtual machine exits.

To simplify the usage of profilers in ducky sources in case when user does not need profiling, I chose to provide classes with the same API but these classes does not capture any data. These are called dummy profilers, as opposed to the real ones. Both kinds mimic API of profile.Profile - the real machine profiler is profile.Profile object.

class ducky.profiler.DummyCPUCoreProfiler(core, frequency=17)[source]

Bases: object

Dummy code profiler class. Base class for all code profilers.

Parameters:
  • core (ducky.cpu.CPUCore) – core this profiler captures data from.
  • frequency (int) – sampling frequency, given as an instruction count.
create_stats()[source]

Preprocess collected data before they can be printed, searched or saved.

disable()[source]

Disable collection of profiling data.

dump_stats(filename)[source]

Save collected data into file.

Parameters:filename (string) – path to file.
enable()[source]

Enable collection of profiling data.

take_sample()[source]

Take a sample of current state of CPU core, and store any necessary data.

class ducky.profiler.DummyMachineProfiler(*args, **kwargs)[source]

Bases: object

Dummy machine profiler. Does absolutely nothing.

create_stats()[source]

Preprocess collected data before they can be printed, searched or saved.

disable()[source]

Stop collecting profiling data.

dump_stats(filename)[source]

Save collected data into file.

Parameters:filename (string) – path to file.
enable()[source]

Start collecting profiling data.

class ducky.profiler.ProfileRecord[source]

Bases: object

merge(other)[source]
class ducky.profiler.ProfilerStore[source]

Bases: object

This class manages profiler instances. When in need of a profiler (e.g. in a new thread) get one by calling proper method of ProfilerStore object.

enable_cpu()[source]

Each newly created code profiler will be the real one.

enable_machine()[source]

Each newly created virtual machine profiler will be the real one.

get_core_profiler(core)[source]

Create new code profiler.

Return type:DummyCPUCoreProfiler
get_machine_profiler()[source]

Create and return new machine profiler.

Returns:new machine profiler.
is_cpu_enabled()[source]

Returns True when code profiling is enabled.

Return type:bool
is_machine_enabled()[source]

Returns True when virtual machine profiling is enabled.

Return type:bool
save(logger, directory)[source]

Save all captured data to files. Each created profiler stores its data in separate file.

Parameters:directory (string) – directory where all files are stored.
class ducky.profiler.RealCPUCoreProfiler(core)[source]

Bases: ducky.profiler.DummyCPUCoreProfiler

Real code profiler. This class actually does collect data.

dump_stats(filename)[source]
take_sample()[source]
ducky.profiler.STORE = <ducky.profiler.ProfilerStore object>

Main profiler store

ducky.reactor module

This module provides simple reactor core that runs each of registered tasks at least once during one iteration of its internal loop.

There are two different kinds of objects that reactor manages:

  • task - it’s called periodicaly, at least once in each reactor loop iteration
  • event - asynchronous events are queued and executed before running any tasks. If there are no runnable tasks, reactor loop waits for incomming events.
class ducky.reactor.CallInReactorTask(fn, *args, **kwargs)[source]

Bases: ducky.interfaces.IReactorTask

This task request running particular function during the reactor loop. Useful for planning future work, and for running tasks in reactor’s thread.

Parameters:
  • fn – callback to fire.
  • args – arguments for callback.
  • kwargs – keyword arguments for callback.
run()[source]
class ducky.reactor.FDCallbacks(on_read, on_write, on_error)

Bases: tuple

on_error

Alias for field number 2

on_read

Alias for field number 0

on_write

Alias for field number 1

class ducky.reactor.Reactor(machine)[source]

Bases: object

Main reactor class.

add_call(fn, *args, **kwargs)[source]

Enqueue function call. Function will be called in reactor loop.

add_event(event)[source]

Enqueue asynchronous event.

add_fd(fd, on_read=None, on_write=None, on_error=None)[source]

Register file descriptor with reactor. File descriptor will be checked for read/write/error posibilities, and appropriate callbacks will be fired.

No arguments are passed to callbacks.

Parameters:
  • fd (int) – file descriptor.
  • on_read – function that will be called when file descriptor is available for reading.
  • on_write – function that will be called when file descriptor is available for write.
  • on_error – function that will be called when error state raised on file descriptor.
add_task(task)[source]

Register task with reactor’s main loop.

remove_fd(fd)[source]

Unregister file descriptor. It will no longer be checked by its main loop.

Parameters:fd (int) – previously registered file descriptor.
remove_task(task)[source]

Unregister task, it will never be ran again.

run()[source]

Starts reactor loop. Enters endless loop, calling runnable tasks and events, and - in case there are no runnable tasks - waits for new events.

When there are no tasks managed by reactor, loop quits.

task_runnable(task)[source]

If not yet marked as such, task is marked as runnable, and its run() method will be called every iteration of reactor’s main loop.

task_suspended(task)[source]

If runnable, task is marked as suspended, not runnable, and it will no longer be ran by reactor. It’s still registered, so reactor’s main loop will not quit, and task can be later easily re-enabled by calling ducky.reactor.Reactor.task_runnable().

class ducky.reactor.RunInIntervalTask(ticks, fn, *args, **kwargs)[source]

Bases: ducky.interfaces.IReactorTask

This task will run its callback every ticks iterations of reactor’s main loop.

Parameters:
  • ticks (int) – number of main loop iterations between two callback calls.
  • args – arguments for callback.
  • kwargs – keyword arguments for callback.
run()[source]
class ducky.reactor.SelectTask(machine, fds, *args, **kwargs)[source]

Bases: ducky.interfaces.IReactorTask

Private task, serving as a single point where select syscall is being executed. When a subsystem is interested in IO on a file descriptor, such file descriptor should be set as non-blocking, and then registered with reactor - it’s not viable to place select calls everywhere in different drivers. This task takes list of registered file descriptors, checks for possible IO oportunities, and fires callbacks accordingly.

Parameters:
  • machine (ducky.machine.Machine) – VM this task (and reactor) belongs to.
  • fds (dict) – dictionary, where keys are descriptors, and values are lists of their callbacks.
add_fd(fd, on_read=None, on_write=None, on_error=None)[source]

Register file descriptor with reactor. File descriptor will be checked for read/write/error posibilities, and appropriate callbacks will be fired.

No arguments are passed to callbacks.

Parameters:
  • fd (int) – file descriptor.
  • on_read – function that will be called when file descriptor is available for reading.
  • on_write – function that will be called when file descriptor is available for write.
  • on_error – function that will be called when error state raised on file descriptor.
remove_fd(fd)[source]

Unregister file descriptor. It will no longer be checked by its main loop.

Parameters:fd (int) – previously registered file descriptor.
run()[source]

ducky.snapshot module

class ducky.snapshot.CoreDumpFile(logger, stream)[source]

Bases: ducky.util.BinaryFile

load()[source]
static open(*args, **kwargs)[source]
save(state)[source]
class ducky.snapshot.SnapshotNode(*fields)[source]

Bases: object

add_child(name, child)[source]
get_child(name)[source]
get_children()[source]
print_node(level=0)[source]
class ducky.snapshot.VMState(logger)[source]

Bases: ducky.snapshot.SnapshotNode

static capture_vm_state(machine, suspend=True)[source]
static load_vm_state(logger, filename)[source]
save(filename)[source]

ducky.streams module

Streams represent basic IO objects, used by devices for reading or writing (streams) of data.

Stream object encapsulates an actual IO object - file-like stream, raw file descriptor, or even something completely different. Stream classes then provide basic IO methods for moving data to and from stream, shielding user from implementation details, like Python2/Python3 differencies.

class ducky.streams.FDInputStream(machine, fd, **kwargs)[source]

Bases: ducky.streams.InputStream

class ducky.streams.FDOutputStream(machine, fd, **kwargs)[source]

Bases: ducky.streams.OutputStream

class ducky.streams.FileInputStream(machine, f, **kwargs)[source]

Bases: ducky.streams.InputStream

class ducky.streams.FileOutputStream(machine, f, **kwargs)[source]

Bases: ducky.streams.OutputStream

class ducky.streams.InputStream(machine, desc, stream=None, fd=None, close=True, allow_close=True)[source]

Bases: ducky.streams.Stream

static create(machine, desc)[source]
read(size=None)[source]
write(b)[source]
class ducky.streams.MethodInputStream(machine, desc, **kwargs)[source]

Bases: ducky.streams.InputStream

class ducky.streams.MethodOutputStream(machine, desc, **kwargs)[source]

Bases: ducky.streams.OutputStream

class ducky.streams.OutputStream(machine, desc, stream=None, fd=None, close=True, allow_close=True)[source]

Bases: ducky.streams.Stream

static create(machine, desc)[source]
flush()[source]
read(size=None)[source]
write(buff)[source]
class ducky.streams.StderrStream(machine)[source]

Bases: ducky.streams.OutputStream

class ducky.streams.StdinStream(machine, **kwargs)[source]

Bases: ducky.streams.InputStream

close()[source]
get_selectee()[source]
class ducky.streams.StdoutStream(machine)[source]

Bases: ducky.streams.OutputStream

class ducky.streams.Stream(machine, desc, stream=None, fd=None, close=True, allow_close=True)[source]

Bases: object

Abstract base class of all streams.

Parameters:
  • machine – parent :py:class`ducky.machine.Machine` object.
  • desc – description of stream. This is a short, string representation of the stream.
  • streamfile-like stream that provides IO method (read() or write). If it is set, it is preferred IO object.
  • fd (int) – raw file descriptor. stream takes precedence, otherwise this file descriptor is used.
  • close (bool) – if True, and if stream has a close() method, stream will provide close() method that will close the underlaying file-like object. True by default.
  • allow_close (bool) – if not True, stream’s close() method will not close underlying IO resource. True by default.
close()[source]

This method will close the stream. If allow_close flag is not set to True, nothing will happen. If the stream wasn’t created with close set to True, nothing will happen. If the wrapped IO resource does not have close() method, nothing will happen.

has_fd()[source]

Check if stream has raw file descriptor. File descriptors can be checked for IO availability by reactor’s polling task.

Return type:bool
Returns:True when stream has file descriptor.
has_poll_support()[source]

Streams that can polled for data should return True.

Return type:bool
read(size=None)[source]

Read data from stream.

Parameters:size (int) – if set, read at maximum size bytes.
Return type:bytearray (Python2), bytes (Python3)
Returns:read data, of maximum lenght of size, None when there are no available data, or empty string in case of EOF.
register_with_reactor(reactor, **kwargs)[source]

Called by owner to register the stream with reactor’s polling service.

See ducky.reactor.Reactor.add_fd() for keyword arguments.

Parameters:reactor (ducky.reactor.Reactor) – reactor instance to register with.
unregister_with_reactor(reactor)[source]

Called by owner to unregister the stream with reactor’s polling service, e.g. when stream is about to be closed.

Parameters:reactor (ducky.reactor.Reactor) – reactor instance to unregister from.
write(buff)[source]

Write data to stream.

Parameters:buff (bytearray) – data to write. bytearray (Python2), bytes (Python3)
ducky.streams.fd_blocking(fd, block=None)[source]

Query or set blocking mode of file descriptor.

Return type:bool
Returns:if block is None, current setting of blocking mode is returned - True for blocking, False for non-blocking. Othwerwise, function returns nothing.

ducky.tools package

Submodules

ducky.tools.as module
ducky.tools.as.encode_blob(logger, file_in, options)[source]
ducky.tools.as.main()[source]
ducky.tools.as.save_object_file(logger, sections, file_out, options)[source]
ducky.tools.as.translate_buffer(logger, buffer, file_in, options)[source]
ducky.tools.cc module
ducky.tools.cc.compile_file(logger, options, file_in, file_out)[source]
ducky.tools.cc.main()[source]
ducky.tools.coredump module
ducky.tools.coredump.main()[source]
ducky.tools.coredump.show_cores(logger, state)[source]
ducky.tools.coredump.show_forth_dict(logger, state, last)[source]
ducky.tools.coredump.show_forth_trace(logger, state)[source]
ducky.tools.coredump.show_forth_word(logger, state, base_address)[source]
ducky.tools.coredump.show_header(logger, state)[source]
ducky.tools.coredump.show_memory(logger, state)[source]
ducky.tools.coredump.show_pages(logger, state, empty_pages=False)[source]
ducky.tools.defs module
ducky.tools.defs.main()[source]
ducky.tools.defs.parse_template(file_in, file_out)[source]
ducky.tools.img module
ducky.tools.img.align_file(logger, f_out, boundary)[source]
ducky.tools.img.create_binary_image(logger, f_in, f_out, bio=False)[source]
ducky.tools.img.create_hdt_image(logger, file_in, f_out, options)[source]
ducky.tools.img.main()[source]
ducky.tools.ld module
class ducky.tools.ld.LinkerInfo[source]

Bases: object

ducky.tools.ld.align_nop(n)[source]
ducky.tools.ld.fix_section_bases(logger, info, f_out, required_bases)[source]
ducky.tools.ld.main()[source]
ducky.tools.ld.merge_object_into(logger, info, f_dst, f_src)[source]
ducky.tools.ld.process_files(logger, info, files_in, file_out, bases=None)[source]
ducky.tools.ld.resolve_relocations(logger, info, f_out, f_ins)[source]
ducky.tools.ld.resolve_symbols(logger, info, f_out, f_ins)[source]
ducky.tools.objdump module
ducky.tools.objdump.main()[source]
ducky.tools.objdump.show_disassemble(logger, f)[source]
ducky.tools.objdump.show_file_header(logger, f)[source]
ducky.tools.objdump.show_reloc(logger, f)[source]
ducky.tools.objdump.show_sections(logger, f)[source]
ducky.tools.objdump.show_symbols(logger, f)[source]
ducky.tools.profile module
ducky.tools.profile.main()[source]
ducky.tools.profile.read_profiling_data(logger, files_in)[source]
ducky.tools.vm module
class ducky.tools.vm.DuckyProtocol(logger, options, config)[source]

Bases: autobahn.twisted.websocket.WebSocketServerProtocol

Protocol handling communication between VM and remote terminal emulator.

Parameters:
  • logger (logging.Logger) – Logger instanceto use for logging.
  • options – command-line options, as returned by option parser.
  • config (ducky.config.MachineConfig) – VM configuration.
onClose(wasClean, code, reason)[source]

Called when connection ends. Tell VM to halt, and wait for its thread to finish. Then, print some VM stats.

onMessage(payload, isBinary)[source]

Called when new data arrived from client. Feeds the data to VM’s input stream.

See autobahn.twisted.websocket.WebSocketServerProtocol.onMessage().

onOpen(*args, **kwargs)[source]

Called when new client connects to the server.

This callback will setup and start new VM, connecting it to remote terminal by wrapping this protocol instance in two streams (input/output), and spawn a fresh new thread for it.

run_machine()[source]

Wraps VM’s run() method by try/except clause, and call protocols sendClose() method when VM finishes.

This is the target of VM`s thread.

class ducky.tools.vm.DuckySocketServerFactory(logger, options, config, *args, **kwargs)[source]

Bases: autobahn.twisted.websocket.WebSocketServerFactory

buildProtocol(*args, **kwargs)[source]
class ducky.tools.vm.WSInputStream(protocol, *args, **kwargs)[source]

Bases: ducky.streams.InputStream

Websocket input stream, receiving bytes from opened websocket, and pushing them to keyboard frontend device.

Stream has an internal LIFO buffer that is being fed by protocol, and consumed by VM frontend device.

Parameters:protocol (DuckyProtocol) – protocol instance with opened websocket.
enqueue(buff)[source]

Called by protocol instance, to add newly received data to stream’s buffer.

Parameters:buff (bytearray) – recerived bytes.
has_poll_support()[source]

See :py:meth:`ducky.streams.Stream.has_poll_support’.

read(size=None)[source]

See :py:meth:`ducky.streams.Stream.read’.

register_with_reactor(reactor, on_read=None, on_error=None)[source]

See :py:meth:`ducky.streams.Stream.register_with_reactor’.

unregister_with_reactor(reactor)[source]

See :py:meth:`ducky.streams.Stream.unregister_with_reactor’.

class ducky.tools.vm.WSOutputStream(protocol, *args, **kwargs)[source]

Bases: ducky.streams.OutputStream

Websocket output stream, receiving bytes from TTY frontend, and pushing them to protocol’s socket.

Parameters:protocol (DuckyProtocol) – protocol instance with opened websocket.
write(buff)[source]

Write buffer into the socket.

Called by device from machine thread, therefore this method hands buffer over to the reactor thread.

Parameters:buff (bytearray) – bytes to send to client.
ducky.tools.vm.main()[source]
ducky.tools.vm.print_machine_stats(logger, M)[source]
ducky.tools.vm.process_config_options(logger, options, config_file=None, set_options=None, add_options=None, enable_devices=None, disable_devices=None)[source]

Load VM config file, and apply all necessary changes, as requested by command-line options.

Parameters:
  • logger (logging.Logger) – Logger instance to use for logging.
  • options – command-line options, as returned by option parser.
  • config_file (string) – path to configuration file.
Return type:

ducky.config.MachineConfig

Returns:

VM configuration.

Module contents

ducky.tools.add_common_options(parser)[source]
ducky.tools.parse_options(parser, default_loglevel=20, stream=None)[source]
ducky.tools.setup_logger(stream=None, debug=False, quiet=None, verbose=None, default_loglevel=20)[source]

ducky.util module

class ducky.util.BinaryFile(logger, stream)[source]

Bases: object

Base class of all classes that represent “binary” files - binaries, core dumps. It provides basic methods for reading and writing structures.

static do_open(logger, path, mode='rb', klass=None)[source]
static open(*args, **kwargs)[source]
read_struct(st_class)[source]

Read structure from current position in file.

Returns:instance of class st_class with content read from file
Return type:st_class
setup()[source]
write_struct(st)[source]

Write structure into file at the current position.

Parameters:st (class) – ctype-based structure
class ducky.util.Flags[source]

Bases: object

classmethod create(**kwargs)[source]
classmethod encoding()[source]
classmethod from_encoding(encoding)[source]
classmethod from_int(u)[source]
classmethod from_string(s)[source]
load_encoding(encoding)[source]
load_int(u)[source]
load_string(s)[source]
save_encoding(encoding)[source]
to_encoding()[source]
to_int()[source]
to_string()[source]
class ducky.util.Formatter[source]

Bases: string.Formatter

format_field(value, format_spec)[source]
format_int(format_spec, value)[source]
class ducky.util.LRUCache(logger, size, *args, **kwargs)[source]

Bases: collections.OrderedDict

Simple LRU cache, based on OrderedDict, with limited size. When limit is reached, the least recently inserted item is removed.

Parameters:
  • logger (logging.Logger) – logger object instance should use for logging.
  • size (int) – maximal number of entries allowed.
get_object(key)[source]

The real workhorse - responsible for getting requested item from outside when it’s not present in cache. Called by __missing__ method. This method itself makes no changes to cache at all.

make_space()[source]

This method is called when there is no free space in cache. It’s responsible for freeing at least one slot, upper limit of removed entries is not enforced.

class ducky.util.StringTable[source]

Bases: object

Simple string table, used by many classes operating with files (core, binaries, ...). String can be inserted into table and read, each has its starting offset and its end is marked with null byte ().

Thsi is a helper class - it makes working with string, e.g. section and symbol names, much easier.

get_string(offset)[source]

Read string from table.

Parameters:offset (int) – offset of the first character from the beginning of the table
Returns:string
Return type:string
put_string(s)[source]

Insert new string into table. String is appended at the end of internal buffer, and terminating zero byte () is appended to mark end of string.

Returns:offset of inserted string
Return type:int
class ducky.util.SymbolTable(binary)[source]

Bases: dict

get_symbol(name)[source]
ducky.util.align(boundary, n)[source]
ducky.util.isfile(o)[source]
ducky.util.sizeof_fmt(n, suffix='B', max_unit='Zi')[source]
ducky.util.str2int(s)[source]

Indices and tables