# Writing duckyScript
[Get duckyPad Pro](https://www.tindie.com/products/37399/) | [Official Discord](https://discord.gg/4sJCBx5) | [Getting Started](./getting_started.md) | [Table of Contents](#table-of-contents)
------
## Overview
duckyScript is a simple language for **automating keyboard/mouse inputs**.
It was originally developed for [USB Rubber Ducky](https://shop.hak5.org/products/usb-rubber-ducky-deluxe).
## Quick Examples
In the **simplest form**, you just tell it what key to press!
Ideal for key combos:
* New Tab: `CONTROL t`
* Task Manager: `CONTROL SHIFT ESC`
--------
Once familiar, you can write longer **multi-line scripts** for more complex actions.
**Open Webpage:**
```
WINDOWS r
DELAY 500
STRING https://youtu.be/dQw4w9WgXcQ
ENTER
```
---------
At **full potential**, duckyScript is much closer to a **general-purpose language**.
You can:
* Use Variables, Conditions, and Loops.
* Read Buttons
* Write to OLED screen
* Change RGB colors
* And more!
This allows highly customized macros for your exact needs.
## First Time?
There are quite a few commands, and it can be a bit daunting.
The **first few sections** (from `Comments` to `Mouse`) is more than enough to get started.
You can skim the rest once familiar with the basics.
Also **playing with sample profiles** is a good way to get a feel of duckyScript.
Of course, people at the [Official Discord](https://discord.gg/4sJCBx5) are always happy to help!
## 👉👉 duckyScript Cheatsheet 👈👈
[Click me to download a PDF](https://github.com/dekuNukem/duckyPad-Pro/blob/master/doc/duckyscript_cheatsheet.pdf?raw=1) of the **quick reference guide**!
Much easier to lookup than going through this whole page.
## 🤖 Doc for AI 🤖
You can also ask AI to write / explain duckyScript for you.
* [Download this file](https://raw.githubusercontent.com/dekuNukem/duckyPad-Pro/refs/heads/master/doc/duckyscript_for_ai.md) (Right click -> Save As)
* Include as attachment (or paste content) in chat
* AI can make mistakes. Always double check!
## Syntax Highlighter
[Available for VS Code and Sublime Text](https://github.com/duckyPad/duckyScript-Syntax-Highlighter/blob/master/README.md)
Recommended for longer and more complex scripts, write there and copy it back.
## List of Commands
- [Comments](#comments)
- [`//`](#)
- [`REM_BLOCK`](#rem_block-and-end_rem)
- [Typing](#typing)
- [`STRING` and `STRINGLN`](#string-and-stringln)
- [`STRINGLN_BLOCK`](#stringln_block-and-end_stringln)
- [`STRING_BLOCK`](#string_block-and-end_string)
- [Pressing Keys](#pressing-keys)
- [Special Keys](#special-keys)
- [`KEYDOWN` / `KEYUP`](#keydown-keyup)
- [`REPEAT`](#repeat)
- [Timing](#timing)
- [`DELAY`](#delay-n)
- [`DEFAULTDELAY`](#defaultdelay-n)
- [`DEFAULTCHARDELAY`](#defaultchardelay-n)
- [`CHARJITTER`](#charjitter-n)
- [Mouse](#mouse)
- [Mouse Buttons](#mouse-buttons)
- [`MOUSE_MOVE`](#mouse_move-x-y)
- [`MOUSE_SCROLL`](#mouse_scroll-h-v)
- [Multiple Actions](#multiple-actions)
- [Profile Switching](#profile-switching)
- [`PREV_PROFILE` / `NEXT_PROFILE`](#prev_profile-next_profile)
- [`GOTO_PROFILE`](#goto_profile)
- [Autoswitcher](#autoswitcher)
- [OLED](#oled)
- [`OLED_CURSOR`](#oled_cursor-x-y)
- [`OLED_PRINT`](#oled_print)
- [`OLED_CPRINT`](#oled_cprint)
- [`OLED_CLEAR`](#oled_clear)
- [`OLED_CIRCLE`](#oled_circle-x-y-radius-options)
- [`OLED_LINE`](#oled_line-x1-y1-x2-y2)
- [`OLED_RECT`](#oled_rect-x1-y1-x2-y2-options)
- [`OLED_UPDATE`](#oled_update)
- [`OLED_RESTORE`](#oled_restore)
- [Per-Key RGB](#per-key-rgb)
- [`SWC_SET`](#swc_set-n-r-g-b)
- [`SWC_FILL`](#swc_fill-r-g-b)
- [`SWC_RESET`](#swc_reset-n)
- [Constants](#constants)
- [Variables](#variables)
- [Persistent Global Variables](#persistent-global-variables)
- [Reserved Variables](#reserved-variables)
- [Operators](#operators)
- [Mathematics](#mathematics)
- [Comparison](#comparison)
- [Logical](#logical)
- [Bitwise](#bitwise)
- [Augmented Assignments](#augmented-assignments)
- [UNSIGNED Operators](#unsigned-operators)
- [Advanced Printing](#advanced-printing)
- [Format Specifiers](#format-specifiers)
- [Numerical Padding](#numerical-padding)
- [Real-time Clock (RTC)](#real-time-clock-rtc)
- [Setting RTC](#setting-rtc)
- [Reading RTC](#reading-rtc)
- [Conditional Statements](#conditional-statements)
- [`WHILE` Loops](#loops)
- [Functions](#functions)
- [Plain Functions](#plain-functions)
- [Arguments and Returns](#arguments-and-returns)
- [Variable Scoping](#variable-scoping)
- [Nested / Recursive Calls](#nested-recursive-calls)
- [duckyPad Standard Library](#duckypad-standard-library)
- [User Headers](#user-headers)
- [Built-in Functions](#built-in-functions)
- [Reading Inputs](#reading-inputs)
- [Randomization](#randomization)
- [Random Number](#random-number)
- [Random Character](#random-character)
- [Miscellaneous](#miscellaneous)
- [Reserved Variables List](#reserved-variables-list)
## Comments
### `//`
C-style comment. Anything after `//` is ignored.
```
// This is a comment
```
### `REM_BLOCK` and `END_REM`
Comment block. Everything in-between is ignored.
```
REM_BLOCK
Put as much comment here
as you want!
END_REM
```
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Typing
### `STRING` and `STRINGLN`
`STRING` types out whatever after it **`AS-IS`**.
```
STRING Hello world!
// types out "Hello world!"
```
`STRINGLN` also presses **enter key** at the end.
### `STRINGLN_BLOCK` and `END_STRINGLN`
Type out everything inside as-is.
Also presses **enter key** at the **end of each line**.
```
STRINGLN_BLOCK
According to all known laws of aviation,
there is no way a bee should be able to fly.
END_STRINGLN
```
### `STRING_BLOCK` and `END_STRING`
Similar to above, but does **NOT** press enter on new lines.
### Printing Variables / Print Formatting
See [Advanced Printing](#advanced-printing)
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Pressing Keys
### Special Keys
duckyScript supports many special keys.
They can be used on their own:
```
WINDOWS
```
...or combined with a character to form shortcuts:
```
WINDOWS s
```
...or chained even longer:
```
WINDOWS SHIFT s
```
------
* Type the key name **as-is** in **`ALL CAPS`**.
* Keys are pressed in sequence from **left-to-right**, then released **right-to-left**.
------
List of Special Keys:
```
CTRL / RCTRL | (media keys)
SHIFT / RSHIFT | MK_VOLUP
ALT / RALT | MK_VOLDOWN
WINDOWS / RWINDOWS | MK_MUTE
GUI | MK_PREV
COMMAND / RCOMMAND | MK_NEXT
OPTION / ROPTION | MK_PP (play/pause)
ESC | MK_STOP
ENTER |
UP/DOWN/LEFT/RIGHT |
SPACE | (numpad keys)
BACKSPACE | NUMLOCK
TAB | KP_SLASH
CAPSLOCK | KP_ASTERISK
PRINTSCREEN | KP_MINUS
SCROLLLOCK | KP_PLUS
PAUSE | KP_ENTER
BREAK | KP_0 to KP_9
INSERT | KP_DOT
HOME | KP_EQUAL
PAGEUP / PAGEDOWN |
DELETE | (Japanese input method)
END | ZENKAKUHANKAKU
MENU | HENKAN
POWER | MUHENKAN
F1 to F24 | KATAKANAHIRAGANA
```
### `KEYDOWN` / `KEYUP`
Hold/release a key.
Allows more fine-grained control.
Can be used to input [Alt Codes](https://en.wikipedia.org/wiki/Alt_code) for special characters:
```
// types out ¼
KEYDOWN ALT
KP_1
KP_7
KP_2
KEYUP ALT
```
### `REPEAT`
Repeats the **last line** **`n`** times.
```
STRING Hello world
REPEAT 10
// types out "Hello world" 11 times (1 original + 10 repeats)
```
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Timing
### `DELAY n`
Pause execution for `n` **milliseconds**.
Useful for **waiting for UI to catch up**.
* 1 second = 1000 ms
```
WINDOWS r
DELAY 1000
STRING cmd
```
### `DEFAULTDELAY n`
How long to wait between each **`NON-LETTER input actions`**.
* Default: 20ms
* Applies to:
* `KEYDOWN` and `KEYUP` (including combo keys)
* `MOUSE_MOVE` and `MOUSE_SCROLL`
* Pressing ENTER at end of `STRINGLN`
```
DEFAULTDELAY 50
CTRL ALT DELETE // Waits 50ms between pressing each key
```
### `DEFAULTCHARDELAY n`
How long to wait between **`each letter`** when **`typing text`**.
* Default: 20ms
* To type faster, set to around 10.
* Applies to:
* `STRING` and `STRINGLN`
* `RANDCHR()`
* `PUTS()`
```
DEFAULTCHARDELAY 10
STRING Hello World! // Waits 10ms between each letter
```
### `CHARJITTER n`
Adds an **`additional`** random delay between 0 and `n` milliseconds after **`each letter`** when **`typing text`**.
* Can make typing more human-like
* Set to 0 to disable
* Applies to:
* `STRING` and `STRINGLN`
* `RANDCHR()`
* `PUTS()`
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Mouse
### Mouse Buttons
* `LMOUSE`: Click `LEFT` mouse button
* `RMOUSE`: Click `RIGHT` mouse button
* `MMOUSE`: Click `MIDDLE` mouse button
* `FMOUSE`: Click `FORWARD` mouse side-button
* `BMOUSE`: Click `BACKWARD` mouse side-button
* Can be used with `KEYDOWN` / `KEYUP` commands.
### `MOUSE_MOVE x y`
Move mouse cursor `x` pixels horizontally, and `y` pixels vertically.
* `x`: Positive moves RIGHT, negative moves LEFT.
* `y`: Positive moves UP, negative moves DOWN.
* Set to 0 if no movement needed
* **Disable mouse acceleration** for **pixel-accurate** results
### `MOUSE_SCROLL h v`
Scroll mouse wheel **Horizontal** `h` lines, and **Vertical** `v` lines.
* `h`: Positive scrolls RIGHT, negative scrolls LEFT.
* `v`: Positive scrolls UP, negative scrolls DOWN.
* Set to 0 for no scroll
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Multiple Actions
`LOOP` command lets you to **assign multiple actions to one key**.
You can use it to toggle / cycle through several actions like this:
```
LOOP0:
STRINGLN first action
LOOP1:
STRINGLN second action
LOOP2:
STRINGLN third action
```
* Start from `LOOP0`
* When pressed, a counter increments, and the script at the corresponding loop is executed.
* Keep the code inside simple!
* For more complex needs, see [`WHILE` Loops](#loops) section below.
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Profile Switching
### `PREV_PROFILE` / `NEXT_PROFILE`
Switch to the previous / next profile.
### `GOTO_PROFILE`
Jump to a profile by name. **Case sensitive!**
This ends the current script execution.
Works with [Advanced Printing](#advanced-printing).
```
GOTO_PROFILE NumPad
```
### Autoswitcher
Also try the [Autoswitcher](https://github.com/dekuNukem/duckyPad-profile-autoswitcher) for **switching profile automatically** based on **active window**!
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## OLED
### `OLED_CURSOR x y`
Set where to print on screen.
`x y`: Pixel coordinates between `0` and `127`.
Characters are **7 pixels wide, 10 pixels tall.**
Max **18 Characters Per Line**.
Characters print from **top-left** corner.
### `OLED_PRINT`
`OLED_PRINT hello world!`
Print the message into display buffer at **current cursor location**.
Works with [Advanced Printing](#advanced-printing).
### `OLED_CPRINT`
Same as `OLED_PRINT`, but prints message **center-aligned**.
### `OLED_CLEAR`
Clear the display buffer.
### `OLED_CIRCLE x y radius options`
* `x y`: Origin
* `radius`: In Pixels
* `options`:
* `0`: White, outline.
* `1`: White, filled.
* `2`: Black, outline.
* `3`: Black, filled.
### `OLED_LINE x1 y1 x2 y2`
* `x1, y1`: Start Point
* `X2, y2`: End Point
### `OLED_RECT x1 y1 x2 y2 options`
* `x1, y1`: Start Corner
* `X2, y2`: End Corner
* `options`:
* `0`: White, outline.
* `1`: White, filled.
* `2`: Black, outline.
* `3`: Black, filled.
### `OLED_UPDATE`
Actually update the OLED.
You should use the other commands to set up the buffer, then call `OLED_UPDATE` to write to display.
This is **much faster** than updating the whole screen for every change.
### `OLED_RESTORE`
Restore the default profile/key name display.
* `OLED_UPDATE` **NOT NEEDED**.
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Per-Key RGB
### `SWC_SET n r g b`
Change LED color of a switch
Set `n` to 0 for current key.
Set `n` between 1 to 20 for a particular key.
`r, g, b` must be between 0 and 255.
### `SWC_FILL r g b`
Change color of **ALL** LEDs.
`r, g, b` must be between 0 and 255.
### `SWC_RESET n`
Reset the key back to default color.
Set `n` to 0 for current key.
Set `n` from 1 to 20 for a particular key.
Set `n` to 99 for all keys.
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Constants
You can use `DEFINE` to, well, define a constant.
The content is **replaced AS-IS** during preprocessing, similar to `#define` in C.
```
DEFINE MY_EMAIL example@gmail.com
STRING My email is MY_EMAIL!
```
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Variables
You can declare a variable using `VAR` command, and assign values to it.
Assignment can be:
* Decimal: Normal numbers
* Hexadecimal: Starts with `0x`
* Character: **A single character** inside **single or double quote**
* Will be converted to its 8-bit ASCII value
```
// Declaration
VAR spam = 42
VAR eggs = 0xff
VAR foo = 'a'
// Assignment
spam = 20
eggs = spam*2
```
* Variables are **SIGNED 32-bit Integers**
* Can hold values between **−2,147,483,648 and 2,147,483,647**
* Variables declared at top level have **global scope** and can be accessed **anywhere**.
* Variables declared **inside a function** have **local scope** and is only accessible **within that function**.
### Persistent Global Variables
There are 32 pre-defined global variables that provides **non-volatile** data storage.
* `_GV0` to `_GV31`
* Available across **all profiles**
* Persists over reboots
### Reserved Variables
Some variables are **always available**. They all start with an underscore `_`.
You can read them to obtain information, or write to adjust settings.
```
VAR status = _IS_NUMLOCK_ON
_CHARJITTER = 10
```
[Click me for full list](#reserved-variables-list)
## Operators
You can perform operations on constants and variables.
All ops are **Signed** by default. Use explicit [**unsigned calls**](#unsigned-operators) to treat variables as unsigned.
### Mathematics
```
= Assignment
+ Add
- Subtract
* Multiply
/ SIGNED Integer Division
% SIGNED Modulus
** Exponent
```
Example:
```
spam = 2+3
spam = eggs * 10
```
### Comparison
All comparisons evaluate to **either 0 or 1**.
```
== Equal
!= Not equal
> SIGNED Greater than
< SIGNED Less than
>= SIGNED Greater than or equal
<= SIGNED Less than or equal
```
### Logical
| Operator | Name | Comment |
|:--------:|:---------------------:|--------------------------------------------------------|
| `&&` | Logical AND | Evaluates to 1 if BOTH side are non-zero, otherwise 0. |
| `\|\|` | Logical OR | Evaluates to 1 if ANY side is non-zero, otherwise 0. |
| `!` | Logical NOT | **Single (Unary) Operand**
Evaluates to 1 if expression is 0
Evaluates to 0 if expression is **Non-Zero**|
### Bitwise
```
& Bitwise AND
| Bitwise OR
^ Bitwise XOR
~ Bitwise NOT
<< Left Shift
>> Arithmetic Right Shift (sign-extend)
```
### Augmented Assignments
* E.g. `+=`, `-=`, `*=`, etc.
* Available for all **2-operand operators**
* `x += 1` same as `x = x + 1`, etc.
### UNSIGNED Operators
Call built-in functions below to perform **unsigned** operations on variables.
```
ULT(lhs, rhs) Unsigned Less Than
ULTE(lhs, rhs) Unsigned Less Than or Equal
UGT(lhs, rhs) Unsigned Greater Than
UGTE(lhs, rhs) Unsigned Greater Than or Equal
UDIV(val, n) Unsigned Division (val / n)
UMOD(val, n) Unsigned Modulo (val % n)
LSR(val, n) Logical Right Shift (Zero-Extend)
```
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Advanced Printing
You can print the **value of a variable** by adding a **dollar symbol ($) before its name**.
```
VAR foo = -10
STRING Value is $foo
```
```
Value is -10
```
* Works with `STRING`, `STRINGLN`, `OLED_PRINT`, `OLED_CPRINT` and `GOTO_PROFILE`.
### Format Specifiers
You can use **optional C-Style Format Specifiers** to adjust **print format** and **padding**.
To add a specifier: **Immediately after the variable name**, type `%`, then a **data-type indicator letter**.
* `%d` to print variable as **Signed Decimal**
* **DEFAULT**, same as no specifier.
* `%u` to print variable as **Unsigned Decimal**
* `%x` to print variable as **Lowercase Hexadecimal**
* `%X` to print variable as **Uppercase Hexadecimal**
```
VAR foo = -10
STRING Value is: $foo%d
STRING Value is: $foo%u
STRING Value is: $foo%x
STRING Value is: $foo%X
```
```
Value is: -10
Value is: 4294967286
Value is: fffffff6
Value is: FFFFFFF6
```
### Numerical Padding
* To pad with **SPACE**
* Add a **width number** just **after `%`** and **before the letter**
* The output will be **at least that** wide
* Any extra space are padded with **space characters**
```
VAR foo = 5
STRING I have $foo%10d apples!
```
```
I have 5 apples!
```
* To pad with **LEADING-ZERO**
* Right **after `%`**, add a `0`, then **width number**, then the letter.
* The output will be **at least that** wide
* Any extra space are padded with `0`
* Useful for printing **dates** and **hex numbers**
```
VAR foo = 5
STRING I have $foo%010d apples!
```
```
I have 0000000005 apples!
```
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Real-time Clock (RTC)
duckyPad can keep track of **current date and time** for use in scripts.
### Setting RTC
On cold-boot, duckyPad doesn't know what time it is.
It must be set once, after which it will keep time **as long as it is powered-on**.
* RTC is **automatically set** when using the [Autoswitcher](https://github.com/duckyPad/duckyPad-Profile-Autoswitcher)
* A **clock icon** appears when RTC is valid

* You can also set it manually
* [HID Commands](https://github.com/duckyPad/duckyPad-Profile-Autoswitcher/blob/master/HID_details.md)
* [Sample Script](https://github.com/duckyPad/duckyPad-Profile-Autoswitcher/blob/master/hid_example/ex3_set_rtc.py)
### Reading RTC
#### Validity Check
**ALWAYS check** `_RTC_IS_VALID` **first**!
* **Do not proceed** if value is 0.
```
IF _RTC_IS_VALID == 0
// RTC is uninitialised, do not proceed.
HALT
END_IF
```
#### UTC Offset
The RTC always runs in **UTC**.
Local time is obtained by adding an **UTC Offset in `MINUTES`**
* It is **set automatically** to your **local timezone** when using the [Autoswitcher](https://github.com/duckyPad/duckyPad-Profile-Autoswitcher).
* You can check (and manually adjust) the offset by reading/writing `_RTC_UTC_OFFSET` variable
* Can be positive, 0, or negative.
#### Time and Date
With **valid RTC** and **correct UTC offset**, you can now read from the variables below:
| Name | Comment | Range |
| ------------- | -------------------------- | --------------- |
| `_RTC_YEAR` | **4-digit** Year | e.g. `2025` |
| `_RTC_MONTH` | Month | `1–12` |
| `_RTC_DAY` | Day | `1–31` |
| `_RTC_HOUR` | Hour | `0–23` |
| `_RTC_MINUTE` | Minute | `0–59` |
| `_RTC_SECOND` | Second | `0–60` |
| `_RTC_WDAY` | Day of Week (`0 = Sunday`) | `0–6` |
| `_RTC_YDAY` | Day of Year (`0 = Jan 1`) | `0–365` |
#### Example Usage
```
STRING $_RTC_YEAR%04d-$_RTC_MONTH%02d-$_RTC_DAY%02d $_RTC_HOUR%02d:$_RTC_MINUTE%02d:$_RTC_SECOND%02d
```
```
2025-09-18 09:07:23
```
See [Advanced Printing](#advanced-printing) for formatting tips.
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Conditional Statements
`IF` statements can be used to **conditionally execute code**.
At simplest, it involves `IF` and `END_IF`:
```
IF expression
code to execute
END_IF
```
The code inside is executed if the **expression evaluates to non-zero**.
Indent doesn't matter, feel free to add them for a cleaner look.
----
You can use `ELSE IF` and `ELSE` for additional checks.
If the first `IF` evaluate to 0, `ELSE IF`s are checked.
If none of the conditions are met, code inside `ELSE` is executed.
```
VAR temp = 25
IF temp > 30
STRING It's very hot!
ELSE IF temp > 18
STRING It's a pleasant day.
ELSE
STRING It's quite chilly!
END_IF
```
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Loops
You can use `WHILE` statement to **repeat actions** until a **certain condition is met**.
```
WHILE expression
code to repeat
END_WHILE
```
* If `expression` evaluates to **non-zero**, code inside is repeated. Otherwise, the code is skipped.
This simple example loops 3 times.
```
VAR i = 0
WHILE i < 3
STRINGLN Counter is $i!
i = i + 1
END_WHILE
```
```
Counter is 0!
Counter is 1!
Counter is 2!
```
### `LBREAK`
Use `LBREAK` to **exit a loop** immediately.
```
VAR i = 0
WHILE 1
STRINGLN Counter is $i!
i = i + 1
IF i == 3
LBREAK
END_IF
END_WHILE
```
```
Counter is 0!
Counter is 1!
Counter is 2!
```
### `CONTINUE`
Use `CONTINUE` to **jump to the start of loop** immediately.
```
VAR i = 0
WHILE i < 5
i = i + 1
IF i == 3
CONTINUE
END_IF
STRINGLN Counter is $i!
END_WHILE
```
Here when `i` is 3, it skips printing and starts from the top instead.
```
Counter is 1!
Counter is 2!
Counter is 4!
Counter is 5!
```
### Infinite Loop
To exit an infinite loop, you can [check button status](#reading-inputs), or turn on `Allow Abort` in configurator settings.
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Functions
A function is a **block of organized code** that you can call to **perform a task**.
It makes your script **more modular** and **easier to maintain** compared to copy-pasting same code multiple times.
### Plain Functions
* Declare a function with `FUN name()` and `END_FUN`
* Put the code you want to execute inside
* Call it with `name()`
* Code inside are executed
```
FUN print_addr()
STRINGLN 123 Ducky Lane
STRINGLN Pond City, QU 12345
END_FUN
print_addr() // call it
```
### Arguments and Returns
You can also **pass arguments** into a function and specify a **return value**.
* Ideal for performing calculations
```
FUN add_number(a, b)
RETURN a + b
END_FUN
VAR total = add_number(10, 20)
```
### Variable Scoping
Variables declared **outside functions** have **global scope**, they can be **accessed anywhere**.
Variables declared **inside functions** have **local scope**, they are only accessible **within that function**.
* If a local variable has the **same name** as a global variable, the **local var takes priority** within that function.
```
// Both global scope
VAR x = 10
VAR y = 20
FUN scope_demo()
VAR x = 5 // This x is local, will shadow the global x.
x = x + y
STRINGLN Local x is: $x
END_FUN
```
```
Local x is: 25
```
### Nested / Recursive Calls
You can also:
* Call other functions from inside a function
* Including **calling itself**!
```
FUN factorial(n)
IF n <= 1
RETURN 1
END_IF
RETURN n * factorial(n - 1)
END_FUN
VAR fact = factorial(5)
```
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## duckyPad Standard Library
The **DPDS StdLib** provides handy helper functions to simplify duckyScript coding.
To use them, add `USE_STDLIB` in your code.
[More Info / Contribute](https://github.com/duckyPad/DPDS-Standard-Library/blob/master/README.md)
```
USE_STDLIB
STRINGLN Press Key 3 to continue...
WAITKEY(3)
VAR high_score = MAX(100, 500)
STRINGLN The high score is: $high_score
```
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## User Headers
You can also **create your own header** for custom helper functions and more.
* Click `Edit Headers` Button
* Write code
* Add `USE_UH` to your script to include them
* The header is added to your source code **AS-IS** during preprocessing
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Built-in Functions
A few built-in functions are available. They are intended for **low-level tinkering**.
You might want to get familiar with [VM's memory map](https://duckypad.github.io/DuckStack/)
All multi-byte values are **little-endian**
### `PEEK8(addr)` / `PEEK16(addr)` / `PEEK32(addr)`
Read **SIGNED** value at memory address.
* High bits are **SIGN-extended**
### `PEEKU8(addr)` / `PEEKU16(addr)`
Read **UNSIGNED** value at memory address.
* High bits are **ZERO-extended**
### `POKE8(addr, val)` / `POKE16(addr, val)` / `POKE32(addr, val)`
Write value at memory address.
* `val` can be numbers or characters
* `POKE8(0xf400, 'c')`
* `POKE32(0xf410, 0xabcd)`
### `RANDCHR(value)`
Generate a **random character**.
* `value` is checked as a bitfield:
* `Bit 0`: Letter Lowercase (A-Z)
* `Bit 1`: Letter Uppercase (a-z)
* `Bit 2`: Digits (0-9)
* `Bit 3`: Symbols (\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\})
* `Bit 8`: Type via Keyboard
* `Bit 9`: Print to OLED at current cursor position
* For `Bit 0-3`, if any bit is `1`, its pool of characters will be included for random selection.
* If `Bit 8` is 1, it will type the character via keyboard.
* If `Bit 9` is 1, it will print the character to screen buffer
* Don't forget to use `OLED_UPDATE` to actually refresh the screen.
### `RANDINT(lower, upper)` / `RANDUINT(lower, upper)`
Returns a **Signed/Unsigned** random number between `lower` and `upper` **INCLUSIVE**.
```
VAR value = RANDINT(-100, 100)
VAR value = RANDUINT(3000000000, 4000000000)
```
### `PUTS(value)`
**Print string** at memory address.
* `value` contains:
* `Bit 0-15`: Address
* `Bit 16-23`: `n`
* `Bit 29`: Print to OLED at current cursor position
* `Bit 30`: Print to OLED center-aligned
* `Bit 31`: Type via Keyboard
* If `n = 0`, print until zero-termination.
* Else, print max `n` characters.
### `HIDTX(addr)`
Send a **raw HID message**
* Pick an address `addr` in **scratch memory area**
* Use `POKE8()` to write **9 bytes** starting from `addr`
* Follow the format below
* Call `HIDTX(addr)` to send the HID message
* Include a short delay (10-20ms) to allow computer to register the input
* Don't forget to **release the key** after pressing it
* Set `Byte 1-8` to 0 to release
#### Keyboard
|Byte|Value|Description|
|:-------:|:----------:|:---------:|
|`addr`|1|Usage ID|
|`addr+1`|Modifier
Bitfield|`Bit 0`: Left Control
`Bit 1`: Left Shift
`Bit 2`: Left Alt
`Bit 3`: Left GUI (Win/Cmd)
`Bit 4`: Right Control
`Bit 5`: Right Shift
`Bit 6`: Right Alt (AltGr)
`Bit 7`: Right GUI (Win/Cmd)|
|`addr+2`|0|Reserved|
|`addr+3`
-
`addr+8`|HID Keyboard
Scan Code|[See list](https://gist.github.com/MightyPork/6da26e382a7ad91b5496ee55fdc73db2)
Max 6 keys at once (6KRO)
Write `0` for released / unused|
#### Media Keys
|Byte|Value|Description|
|:-------:|:----------:|:---------:|
|`addr`|2|Usage ID|
|`addr+1`|Key Status
Bitfield|`Bit 0`: Next Track
`Bit 1`: Previous Track
`Bit 2`: Stop
`Bit 3`: Eject
`Bit 4`: Play / Pause
`Bit 5`: Mute
`Bit 6`: Volume Up
`Bit 7`: Volume Down|
|`addr+2`
-
`addr+8`|0|0|
#### Mouse
|Byte|Value|Description|
|:-------:|:----------:|:---------:|
|`addr`|3|Usage ID|
|`addr+1`|Buttons
Status
Bitfield|`Bit 0`: Left
`Bit 1`: Right
`Bit 2`: Middle
`Bit 3`: Backward
`Bit 4`: Forward|
|`addr+2`|X Movement|-127 - 127|
|`addr+3`|Y Movement|-127 - 127|
|`addr+4`|Vertical
Scroll|-127 - 127|
|`addr+5`|Horizontal
Scroll|-127 - 127|
|`addr+6`
-
`addr+8`|0|0|
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Reading Inputs
You can **read the status of switches / encoders** to perform actions.
### Blocking Read
Simplest method.
Just read `_BLOCKING_READKEY` reserved variable.
It will block until a key is pressed.
```
VAR this_key = _BLOCKING_READKEY
// Blocks here until a key is pressed
IF this_key == 1
// do something here
ELSE IF this_key == 2
// do something else
END_IF
```
### Non-Blocking Read
Read `_READKEY`, **returns immediately**.
Returns 0 if no key is pressed. `Key ID` otherwise.
Check this **in a loop** to perform work even when no key is pressed.
```
WHILE TRUE
VAR this_key = _READKEY
IF this_key == 1
// handling button press
END_IF
// otherwise do work here
END_WHILE
```
### Switch Status Bitfield
Read `_SW_BITFIELD`, **returns immediately**.
Each **bit position** stores the status of a key.
|Bit #|Key ID|
|:-:|:-:|
|0|1|
|1|2|
|...|...|
|31|32|
If that bit is 1, the key is currently pressed.
You can use bitmasks to **check multiple keys at once**.
### Key ID
This is the number returned by methods above.
```
duckyPad Pro (2024):
1-20:
* Built-in keys
* Top left is 1
* Bottom right is 20
21: Upper Rotary Encoder Clockwise
22: Upper Rotary Encoder Counterclockwise
23: Upper Rotary Encoder Push-down
24: Lower Rotary Encoder Clockwise
25: Lower Rotary Encoder Counterclockwise
26: Lower Rotary Encoder Push-down
27: Plus Button
28: Minus Button
37+: External Switches
```

```
duckyPad (2020):
1-15:
* Top left is 1
* Bottom right is 15
* Plus button 16, Minus button 17.
```

-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Randomization
### Random Number
Call `RANDINT(lower, upper)` for a random number between `lower` and `upper` **INCLUSIVE**.
```
VAR value = RANDINT(0, 1000)
```
### Random Character
Use one of below to **type a random character**:
```
RANDOM_LOWERCASE_LETTER RANDOM_NUMBER
RANDOM_UPPERCASE_LETTER RANDOM_SPECIAL
RANDOM_LETTER RANDOM_CHAR
```
```
RANDOM_NUMBER
REPEAT 7
// types 8 random numbers
```
For more granular control, see `RANDCHR()` in [Built-in Functions](#built-in-functions).
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Miscellaneous
### `DP_SLEEP`
Make duckyPad go to sleep. Terminates execution.
Backlight and screen are turned off.
Press any key to wake up.
### `HALT`
Stop execution immediately
### `BCLR`
Clears the internal keypress event queue. Can be used:
* **At end of a long script**: Prevents "buffered" presses from triggering it again.
* **Before reading button status**: Ensures **new presses** are returned instead of old ones in the queue.
### `PASS`
Does nothing. Can be used as **placeholders** or empty statements.
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Reserved Variables List
There are some **reserved variables** that are always available.
You can read or write (RW) to adjust settings. Some are read-only (RO).
| Name | Access | Description |
| --------------------------------------------------------------------------- | :-----: |:----:|
| **`_TIME_S`**
**`_TIME_MS`** | RO | Elapsed time since power-on|
| **`_READKEY`**
**`_BLOCKING_READKEY`**
**`_SW_BITFIELD`** | RO | See [Reading Inputs](#reading-inputs)|
| **`_KBLED_BITFIELD`** | RO |Keyboard LED Status
`Bit 0`: Num Lock
`Bit 1`: Caps Lock
`Bit 2`: Scroll Lock
Bit is set if LED is on.
Certain OS may not have all LEDs|
| **`_IS_NUMLOCK_ON`**
**`_IS_CAPSLOCK_ON`**
**`_IS_SCROLLLOCK_ON`** | RO | Aliases|
| **`_DEFAULTDELAY`**
**`_DEFAULTCHARDELAY`**
**`_CHARJITTER`** | RW | Aliases|
| **`_ALLOW_ABORT`**
**`_DONT_REPEAT`** | RW | Write `1` to enable
`0` to disable. |
| **`_THIS_KEYID`** | RO | Returns the [Key ID](#key-id) for the **current script** |
| **`_DP_MODEL`** | RO | Device model. Returns:
`1` for duckyPad (2020)
`2` for duckyPad Pro (2024) |
| **`_KEYPRESS_COUNT`** | RW | How many times **current key**
has been pressed in the **current profile**
Assign **0 to reset** |
| **`_LOOP_SIZE`** | RO | Used by `LOOP` command.
Do not modify |
| **`_NEEDS_EPILOGUE`** | RO | Internal use only
Do not modify |
|**`_RTC_IS_VALID`**
**`_RTC_YEAR`**
**`_RTC_MONTH`**
**`_RTC_DAY`**
**`_RTC_HOUR`**
**`_RTC_MINUTE`**
**`_RTC_SECOND`**
**`_RTC_WDAY`**
**`_RTC_YDAY`**|RO|See [Real-time Clock](#real-time-clock-rtc)|
|**`_RTC_UTC_OFFSET`**|RW|See [Real-time Clock](#real-time-clock-rtc)|
-------
[⬆️⬆️⬆️⬆️⬆️⬆️ Back to Top ⬆️⬆️⬆️⬆️⬆️⬆️](#list-of-commands)
## Table of Contents
[Main page](../README.md)
[User Manual / Getting Started](getting_started.md)
[Kit Assembly Guide](kit_assembly.md)
[Writing duckyScript](duckyscript_info.md)
[duckStack VM](https://duckypad.github.io/DuckStack/)
[Firmware Update](fw_update.md)
[Tinkering Guide](tinkering_guide.md)
[Troubleshooting](troubleshooting.md)
## Questions or Comments?
Please feel free to [open an issue](https://github.com/dekuNukem/duckypad-pro/issues), ask in the [official duckyPad discord](https://discord.gg/4sJCBx5), or email `dekuNukem`@`gmail`.`com`!