Introduction[]
Canons firmware includes a scripting language, which appears to be a flavor of basic. Thanks to recent work by Alfredo Ortega and Oren Isacson of core labs, it is now known how to execute scripts in this language. They have made available their defcon presentation and some preliminary documentation, read more in this →forum thread. Additional information about "autotest.m" and using Canon Basic to shoot are discussed in this thread.
- This script system is available on both VxWorks and DryOS cameras, although the functions available vary by OS and camera model. It does not require CHDK to be loaded and is not associated in any way with CHDK.
Executing Scripts[]
Based on Alfredo and Orens documentation.
This section contains information about the format and contents of the SD cards and basic script required for execution.
- Tools for automating the process of preparing the card can be found at Canon Basic/Card Setup.
Format of the SD card[]
- The SD card may be in FAT16 or FAT32 format.
- The SD card must contain the following items:
- The string "SCRIPT" must be at offset 0x1F0 of the first sector (Boot sector).
- The file "script.req" must exist on the card's root directory, and must only contain the string "for DC_scriptdisk\n" (where the \n represents a newline character)
- The file "extend.m" or "autotest.m" must exist on the root directory. This file must contain the Canon BASIC script to execute.
Script file[]
The script must be in a file named "extend.m" or "autotest.m" placed in the root directory of the SD card. There are two callback subroutines called by the camera to initialize the execution:
private sub Initialize( ) ' Init code end sub private sub terminate( ) ' Ending code end sub
The two subroutines will be called in order: First Initialize(), then terminate()
On some cameras (older, vxworks ?) terminate is not called.
Starting the script[]
The method used to start the script depends on the script file name.
extend.m[]
Once an SD with this format is inserted on the Camera, "extend.m" is called when the camera is in playback mode, by pressing FUNC./SET.
On newer touch screen cameras, without a 'Func/Set' button, the script can be started by holding the zoom lever to the left (zoom out) while starting the camera in playback mode. After the camera has started release the zoom lever, then press it to the left again. Tested on Ixus 310 HS (Elph 500 HS).
autotest.m[]
With the card prepared as described above "autotest.m" is called by holding FUNC./SET and pressing RIGHT while in record mode.
Language documentation[]
Much of this is based on Alfredo and Orens original documentation.
General Information[]
The language consists of a structured BASIC-like core, together with a large number of functions exported from the firmware. These functions are also known as Event Procedures, and in many cases are the same functions used by CHDK. The same event procedures may also be called from CHDK Lua using the Lua/Lua_Reference/Native_Function_Calls. For historical reasons, the name used by CHDK may not match the event procedure name, or a function referred to in CHDK by an event procedure name may actually call a different firmware function.
It is unknown whether the language is entirely a Canon proprietary development or if it is derived from some other flavor of basic. Figuring this out would be useful.
The interpreter is very sensitive to syntax errors. Incorrect syntax usually triggers an ASSERT in the camera interpreter (which causes the camera to power down, use the Canon ROMLOG to diagnose) or cause it to display FAILED on the screen and then shut down.
note This documentation is based on experimental results from a few cameras. Different models or Canon OS versions may not behave identically.
Keywords[]
The following keywords are known.
- sub public while else private if until dim then function extern do step loop for exit next to
Keywords are case insensitive.
In addition, there are at least two special function names, Initialize and terminate, described above.
Comments[]
a ' causes the remainder of the line to be treated as a comment
Operators[]
Relational: <, >, <=, >=
Equality: =, <>
Assignment: =
Arithmetic: +, -, *, /
Address/Pointer: &, * (like C, but see note)
Bitwise: &, |, <<, >>, ^ (like C, but see note)
notes
- Space and context are used to decide the meaning of some operatators. & and * are taken as pointer operators if there is no space between the operator and operand. If there is space, they are treated as multiplication and bitwise and.
- The = operator is treated as assignment or equality test depending on whether it appears in a conditional or not.
- Precedence is unknown
Control structures[]
In the condition below = is treated as an equality operator. Variables, functions, event procedures and literal values may also be used.
For-next[]
For counter = start To end [ statements ] Next
Do-While/Do-Until[]
Do { While | Until } condition [ statements ] [ Exit Do ] [ statements ] Loop
If-Then-Else[]
If condition Then [ statements ] [else] [ statements ] End If
Subroutines[]
Arguments to subroutines may be literal values, variables, or function return values. Unlike some flavors of BASIC, a subroutine may return a value. The function keyword also exists, but appears to be redundant and possibly broken.
Arguments are passed by value (but may contain pointers).
Calling a non-existent subroutine triggers an assert on some cameras (DryOS ?) and is ignored on others.
Public subroutines are available as Event Procedures.
The meaning of the Private and Extern keywords are not known.
Subroutine declaration
[ Public ] | [ Private ] Sub name (Arg1 , Arg2 ) [ statements ] [ name = return_value ] [ Exit Sub ] [ statements ] End Sub
Subroutine invocation
[var =] name(Arg1, Arg2)
Subroutines must be declared before the first invocation in the file. No forward declaration syntax is known.
Event Procedures appear to be treated very similarly to user written subroutines. You may use ExecuteEventProcedure() to safely attempt to call an event procedure which might not be registered. If the event procedure is not registered, the return value will be -1. On some cameras ExecuteEventProcedure itself must be registered by calling SystemEventInit.
The above syntax can be used substituting function for sub, but only if lower case. exit function is not recognized.
Identifiers[]
Identifiers include subroutines, functions and variables defined by the script, as well as event procedures exported by the firmware.
All of these identifiers are case sensitive. The variables A and a are completely independent. Note: The special initialize subroutine may be named Initialize() or initialize(), but only because the canon code checks for both. initializE() is not recognized, and terminate() is only recognized in lower case.
The exact rules for identifiers have not been determined. Some event procedures have a . in the name (e.g System.Create), but this does not appear to be significant to the language.
Variables[]
Scope[]
Variables may be local, global or subroutine parameters.
Global variables are declared and optionally assigned using the dim keyword outside of any subroutine. Using dim inside a subroutine triggers an ASSERT.
Variables used in a subroutine without being declared are local to that subroutine.
Subroutine parameters appear to act like local variables, unless the parameter name is the same as a global. In that case, the global is set when the subroutine is called.
Values[]
Variables may be assigned numbers or strings, including values returned by event procedures.
Numeric literals may be given in decimal or C hex notation.
String literals are delimited by double quotes. Some C escape sequences can be used. The following are verified on D10 and a540:
\t \n \" \\
Hex \xHH and octal \NNN escapes do not appear to work, although they may be handled by some functions.
The value of an unassigned variable appears to be zero.
Variables or literal values are treated as pointers when the underlying function expects a pointer or string.
Array and structure types are not known to exist. The typical basic DIM var(10) syntax for arrays does not appear to work.
Shooting and simulating key presses from Canon Basic[]
The normal "extend.m" and "autotest.m" methods do not support useful shooting scripts, because key presses sent using the PostLogicalEvent* functions are not executed until after the script terminates, and only a very limited number of events are queued. This appears to be due to the script running in the same task that handles events.
Running script from another task using the LoadScript eventproc allows avoids this limitation. See this post and this post for further development.
Useful scripts[]
CBasic dumper[]
Dump camera firmware to file. See Canon Basic/Scripts/Dumper
CBasic loader[]
Load and run an ARM binary. See Canon Basic/Scripts/Loader
Hello World[]
- sample from Alfredo & Orens documentation; the title says it all...
- note: capture an image with light colors first (e.g. a "white" image), the text color may be black or very dark ;)
- may work on newer cameras (2008 - 2010): sucessfully tested on:
- does not work on cameras without "LCDMsg_*" support:
- SD400 (fe50)
- work with A1100 IS 100c build CHDK 0.9.9 Jun 23 2010 14:25:15 a1100 100c GCC 3.4.6
- with script line added " LCDMsg_ChangeColor(a ,6) " after SetStr
private sub sayHello()
a=LCDMsg_Create()
LCDMsg_SetStr(a,"Hello World!")
end sub
private sub Initialize()
UI.CreatePublic()
sayHello()
end sub
- Hello world version that works on A710, from [Dryos Basic Scripting forum thread]
private sub Initialize()
SystemEventInit()
UI_RegistDebugEventProc()
ControlViewEvent(0x18E, "Hello, world!")
Wait(1000)
ControlViewEvent(0x68, 0) ' Hide the message
end sub
RomLog[]
Get the canon crash log from onboard flash to SD card. See Canon Basic/Scripts/Romlog
LED Test[]
- script does show the value of the active LED, maybe usefull for CHDK function ubasic_set_led.
- You might need to change the value for the display color (cl), see also Canon Basic/Reference.
- sucessfully tested on:
- A720IS (msl), SD1400IS / IXUS 130 (emlyn), SD940 (with change color : dim cl=2)
dim a,b,c
dim cl=1
private sub Initialize()
System.Create()
Driver.Create()
UI.CreatePublic()
a=LCDMsg_Create()
LCDMsg_ChangeColor(a,cl)
LCDMsg_SetStr(a,"LED Test")
Wait(2000)
b=LCDMsg_Create()
LCDMsg_ChangeColor(b,cl)
LCDMsg_Move(b,300,50)
for c=0 to 15
LCDMsg_SetNum(b,c)
BeepDrive(3)
Wait(500)
LEDDrive(c,0)
Wait(2000)
LEDDrive(c,1)
next
Wait(2000)
LCDMsg_SetStr(a,"Test End")
end sub
private sub Terminate()
end sub