Get duckyPad Pro | Official Discord | Getting Started | Table of Contents |
Details of duckyPad duckyScript 3 (DPDS3) instruction set, compiler, and binary format.
This is NOT the official implementation, which appears to be close-source.
I designed everything here myself from scratch.
In duckyScript 1, it’s sufficient to simply parse line-by-line, as each line is independent.
duckyScript 3 introduced variables, IF
statements, WHILE
loops, and function calls.
Being much more complex, it makes sense to compile the script into bytecode and execute it on a virtual machine instead.
DPDS3 VM is a simple stack machine with two stacks and a Program Counter (PC).
PC points to the current instruction being executed.
Arithmetic Stack: Used to perform calculations and store arguments.
Call Stack: Keeps track of function calls.
Compiled binary is loaded into memory, and PC starts executing at the beginning of the program.
Each DPDS3 instruction has a fixed-length of three bytes.
First byte (Byte 0) is Opcode, specifying the action this instruction perform.
Second and third byte (Byte 1 and 2) provides optional data.
They are treated as one uint16_t or two uint8_t depending on the instruction.
The DPDS3 compiler takes a text file and outputs a executable binary file.
The binary file should have extension .dsb
(duckyScript binary).
Normally this is taken care of in the configurator. But you can also try it out on its own:
Unzip, and run with Python 3: python3 make_bytecode.py input output
Let’s say you have a simple script in sample_script.txt
:
DEFINE TEN 10
VAR $spam = TEN*7+3
DELAY $spam
Compile with python3 make_bytecode.py sample_script.txt output.dsb
Compiled binary is written to output.dsb
, along with some extra info:
--------- Program Listing After Preprocessing: ---------
1 VAR $spam = 10*7+3
2 DELAY $spam
--------- Assembly Listing, Unresolved ---------
PUSHC 10 0xa ;VAR $spam = 10*7+3
PUSHC 7 0x7 ;VAR $spam = 10*7+3
MULT ;VAR $spam = 10*7+3
PUSHC 3 0x3 ;VAR $spam = 10*7+3
ADD ;VAR $spam = 10*7+3
POP spam ;VAR $spam = 10*7+3
PUSHI spam ;DELAY $spam
DELAY ;DELAY $spam
HALT
--------- Assembly Listing, Resolved ---------
0 PUSHC 10 0xa ;VAR $spam = 10*7+3
3 PUSHC 7 0x7 ;VAR $spam = 10*7+3
6 MULT ;VAR $spam = 10*7+3
9 PUSHC 3 0x3 ;VAR $spam = 10*7+3
12 ADD ;VAR $spam = 10*7+3
15 POP 0 0x0 ;VAR $spam = 10*7+3
18 PUSHI 0 0x0 ;DELAY $spam
21 DELAY ;DELAY $spam
24 HALT
--------- Bytecode ---------
0x01 0x0a 0x00 0x01 0x07 0x00 0x11 0x00 0x00
0x01 0x03 0x00 0x0f 0x00 0x00 0x03 0x00 0x00
0x02 0x00 0x00 0x1b 0x00 0x00 0x08 0x00 0x00
Binary Size: 27 Bytes
When a key is pressed, the corresponding .dsb
file is loaded into bin_buf
in ds3_vm.c. Execution occurs inside run_dsb()
with execute_instruction()
.
Zero-terminated strings are stored at the end of the binary file.
Variables are stored in var_buf
. Up to 64 variables can be declared.
All reference to “stack” refers to Arithmetic Stack. Unless noted otherwise.
Opcode Name |
Byte 0 Decimal |
Byte 0 Hex |
Comment | Byte 1 | Byte 2 |
---|---|---|---|---|---|
NOP | 0 | 0x0 | Do nothing | ||
PUSHC | 1 | 0x1 | Push a constant on stack | CONST_LSB | CONST_MSB |
PUSHI | 2 | 0x2 | Push value at ADDR on stack | ADDR_LSB | ADDR_MSB |
POP | 3 | 0x3 | Pop one item off top of stack Write it to ADDR |
ADDR_LSB | ADDR_MSB |
BRZ | 4 | 0x4 | Pop one item off top of stack If value is zero, jump to ADDR |
ADDR_LSB | ADDR_MSB |
JMP | 5 | 0x5 | Unconditional Jump | ADDR_LSB | ADDR_MSB |
CALL | 6 | 0x6 | Jump to Subroutine Push PC+1 to call stack Jump to ADDR |
ADDR_LSB | ADDR_MSB |
RET | 7 | 0x7 | Return from Subroutine Pop one item off call stack Set PC to its value |
||
HALT | 8 | 0x8 | Stop execution | ||
VMVER | 255 | 0xFF | VM Version | VM VER |
Binary as in involving two operands.
All instructions here:
Pop TWO items off arithmetic stack
First item is left-hand-side, second item is right-hand-side.
Perform operation
Push result back on stack.
Opcode Name |
Byte 0 Decimal |
Byte 0 Hex |
Comment |
---|---|---|---|
EQ | 9 | 0x9 | Equal |
NOTEQ | 10 | 0xa | Not equal |
LT | 11 | 0xb | Less than |
LTE | 12 | 0xc | Less than or equal |
GT | 13 | 0xd | Greater than |
GTE | 14 | 0xe | Greater than or equal |
ADD | 15 | 0xf | Add |
SUB | 16 | 0x10 | Subtract |
MULT | 17 | 0x11 | Multiply |
DIV | 18 | 0x12 | Integer division |
MOD | 19 | 0x13 | Modulus |
POW | 20 | 0x14 | Power of |
LSHIFT | 21 | 0x15 | Logical left shift |
RSHIFT | 22 | 0x16 | Logical right shift |
BITOR | 23 | 0x17 | Bitwise OR |
BITAND | 24 | 0x18 | Bitwise AND |
LOGIAND | 25 | 0x19 | Logical AND |
LOGIOR | 26 | 0x1a | Logical OR |
All reference to “stack” refers to Arithmetic Stack. Unless noted otherwise.
Opcode Name |
Byte 0 Decimal |
Byte 0 Hex |
Comment | Byte 1 | Byte 2 |
---|---|---|---|---|---|
DELAY | 27 | 0x1b | Pop one item off top of stack Delay the amount in milliseconds |
||
KUP | 28 | 0x1c | Release Key Pop ONE item Upper byte: KEYTYPE Lower byte: KEYCODE |
||
KDOWN | 29 | 0x1d | Press Key Pop ONE item Upper byte: KEYTYPE Lower byte: KEYCODE |
||
MSCL | 30 | 0x1e | Mouse Scroll Pop ONE item Scroll number of lines |
||
MMOV | 31 | 0x1f | Mouse Move Pop TWO items Move X and Y |
||
SWCF | 32 | 0x20 | Switch Color Fill Pop THREE items Red, Green, Blue Set ALL LED color to the RGB value |
||
SWCC | 33 | 0x21 | Switch Color Change Pop FOUR items N, Red, Green, Blue Set N-th switch to the RGB value If N is 0, set current switch. |
||
SWCR | 34 | 0x22 | Switch Color Reset Pop one item off top of stack If value is 0, reset color of current key If value is between 1 and 15, reset color of that key If value is 99, reset color of all keys |
||
STR | 35 | 0x23 | Print zero-terminated string at ADDR | ADDR_LSB | ADDR_MSB |
STRLN | 36 | 0x24 | Same as above, presses ENTER at end | ||
—- | 37 | 0x25 | Deprecated Do not use |
||
OLC | 38 | 0x26 | OLED_CURSOR Pop TWO items X and Y |
||
OLP | 39 | 0x27 | Print zero-terminated string at ADDR to OLED | ADDR_LSB | ADDR_MSB |
OLU | 40 | 0x28 | OLED_UPDATE | ||
OLB | 41 | 0x29 | OLED_CLEAR | ||
OLR | 42 | 0x2a | OLED_RESTORE | ||
BCLR | 43 | 0x2b | Clear switch event queue | ||
PREVP | 44 | 0x2c | Previous profile | ||
NEXTP | 45 | 0x2d | Next profile | ||
GOTOP | 46 | 0x2e | Pop one item Go to profile of its value |
||
SLEEP | 47 | 0x2f | Put duckyPad to sleep Terminates execution |
Please feel free to open an issue, ask in the official duckyPad discord, or email dekuNukem
@gmail
.com
!
MOUSE_WHEEL argument on stack
MOUSE_MOVE argument on stack
removed MOUSE_MOVE preprocessor expansion, to be done at instruction level
evaluate_expr handles unary operations such as -1
SWC_SET SWC_FILL SWC_RESET arguments now on stack
OLED_CURSOR arguments now on stack
KEYUP KEYDOWN and key combos argument on stack
MOUSE_WHEEL FW implementation
MOUSE_MOVE FW implementation
MOUSE_MOVE FW level expansion
KEYUP KEYDOWN FW level implementation