Atypical Language Reference
- Introduction
- Instruction set in Atypical notation
- Combining instructions
- Variables
- Constants
- Control flow
- Pseudo-ops
- Reserved words
Introduction
Atypical is an experimental assembly language for the PICmicro family of microcontrollers (14-bit core only: 16f84, 16f628, 16f877, etc). It uses a vaguely pseudo-C-like syntax instead of opcode mnemonics, so rather than writing, for example,
MOVF a, 0 ANDWF b, W XORLW 0xf0 MOVWF c
you can write
w = a w = w & b w ^= 0xf0 c = w
or even
w += a, & b, ^ 0xf0, => c
The most important thing to remember about Atypical is that it is an assembly language, not a high-level language. There is a one-to-one correspondence between Atypical statements and PICmicro instructions. For example, this Atypical assignment,
w = 42is exactly equivalent to
movlw 42This next assignment, however,
PORTA = 42is not allowed because there is no "move immediate to file register" instruction. In regular assembly language, you would have to use two instructions:
movlw 42 movwf PORTAIn Atypical, you would use the same two instructions, just notated a little differently:
w = 42, => PORTA
Familiarity with the PICmicro instruction set is a requirement for using Atypical.
Atypical is case-sensitive. Whitespace between symbols is generally unimportant, but line-breaks are significant. Comments start with // and continue to the end of the line.
w= PORTA ;call Process // weird spacing doesn't matter.
call process // Process and process are different subroutines.
Instruction set in Atypical notation
The following tables list the PIC instructions and their Atypical equivalents. In the tables, k refers to a constant expression, f refers to a variable, and b is a bit number (a constant expression in the range 0 to 7 inclusive).
The columns labeled Focus and Implicit form contain information needed when comma-splicing multiple instructions on one line.
Literal and control instructions
| PICmicro instruction | Atypical equivalent | Focus | Implicit form |
|
addlw k |
w += k w = w + k w = k + w |
w |
+ k |
|
k +=> w |
k |
+=> w |
|
|
w -= k1 w = w - k |
w |
- k |
|
|
andlw k |
w &= k w = w & k w = k & w |
w |
& k |
|
k &=> w |
k |
&=> w |
|
|
call label |
call label |
||
|
clrwdt |
clrwdt |
||
|
goto label |
goto label goto { label1, label2, .. labeln }2 |
||
|
iorlw k |
w |= k w = w | k w = k | w |
w |
| k |
|
k |=> w |
k |
|=> w |
|
|
movlw k |
w = k |
w |
|
|
k => w |
k |
=> w |
|
|
retfie |
retfie |
||
|
retlw k |
retlw k retlw { k1, k2, .. kn }2 |
||
|
return |
return |
||
|
sleep |
sleep |
||
|
sublw k |
w = k - w |
w |
|
|
k -=> w |
k |
-=> w |
|
|
xorlw k |
w ^= k w = w ^ k w = k ^ w |
w |
^ k |
|
k ^=> w |
k |
^=> w |
w -= 1is really
addlw 255
Byte-oriented instructions
| PICmicro instruction | Atypical equivalent | Focus | Implicit form | |
|
addwf f,0 |
w += f w = w + f w = f + w |
w |
+ f |
|
|
f +=> w |
f |
+=> w |
||
|
addwf f,1 |
f += w f = f + w f = w + f |
f |
+ w |
|
|
w +=> f |
w |
+=> f |
||
|
andwf f,0 |
w &= f w = w & f w = f & w |
w |
& f |
|
|
f &=> w |
f |
&=> w |
||
|
andwf f,1 |
f &= w f = f & w f = w & f |
f |
& w |
|
|
w &=> f |
w |
&=> f |
||
|
clrf f |
clr f |
f |
clr |
|
|
clrw w |
clr w |
w |
clr |
|
|
comf f,0 |
w = ~ f |
w |
||
|
comf f,1 |
f = ~ f ~ f |
f |
~ |
|
|
decf f,0 |
w = --f |
w |
||
|
decf f,1 |
f = --f --f |
f |
-- |
|
|
decfsz f,0 |
if w = --f == 0 then skip if !w = --f then skip |
|||
|
decfsz f,1 |
if --f == 0 then skip if !--f then skip |
|||
|
incf f,0 |
w = ++f |
w |
||
|
incf f,1 |
f = ++f ++f |
f |
++ |
|
|
incfsz f,0 |
if w = ++f == 0 then skip if !w = ++f then skip |
|||
|
incfsz f,1 |
if ++f == 0 then skip if !++f then skip |
|||
|
iorwf f,0 |
w |= f w = w | f w = f | w |
w |
| f |
|
|
f |=> w |
f |
|=> w |
||
|
iorwf f,1 |
f |= w f = f | w f = w | f |
f |
| w |
|
|
w |=> f |
w |
|=> f |
||
|
movf f,0 |
w = f |
w |
||
|
f => w |
f |
=> w |
||
|
movf f,1 |
f = f f => f |
f |
=> f | |
|
movwf f |
f = w |
f |
= w |
|
|
w => f |
w |
=> f |
||
|
nop |
nop |
|||
|
rlf f,0 |
w = rol f |
w |
||
|
rlf f,1 |
f = rol f rol f |
f |
rol |
|
|
rrf f,0 |
w = ror f |
w |
||
|
rrf f,1 |
f = ror f ror f |
f |
ror |
|
|
subwf f,0 |
w = f - w |
w |
||
|
f -=> w |
f |
-=> w |
||
|
subwf f,1 |
f = f - w f -= w |
f |
- w |
|
|
swapf f,0 |
w = swap f |
w |
||
|
swapf f,1 |
f = swap f swap f |
f |
swap |
|
|
xorwf f,0 |
w ^= f w = w ^ f w = f ^ w |
w |
^ f |
|
|
f ^=> w |
f |
^=> w |
||
|
xorwf f,1 |
f ^= w f = f ^ w f = w ^ f |
f |
^ w |
|
|
w ^=> f |
w |
^=> f |
Bit-oriented instructions
| PICmicro instruction | Atypical equivalent | Focus | Implicit form |
|
bcf f,b |
f<b> = 0 |
f |
<b> = 0 |
|
bsf f,b |
f<b> = 0 |
f |
<b> = 1 |
|
btfsc f,b |
if f<b> == 0 then skip if f<b> != 1 then skip if !f<b> then skip |
||
|
btfss f,b |
if f<b> == 1 then skip if f<b> != 0 then skip if f<b> then skip |
Combining instructions
When a series of Atypical instructions share a common operand (called the focus for lack of a better term), they can be "comma-spliced" together on one line. The focus is typically the left-hand side of assignment-operator instructions. Example:w = var1 // focus is w w += 3 // focus is w w = w & 0x0f // focus is wAll three instructions have w as their focus, so they can be written
w = var1, + 3, & 0x0f
Note that instructions after the comma are written in implicit form, as listed in the instruction set tables.
Sometimes you have to reverse left- and right-hand sides before comma-splicing:w = var1 // focus is w w = w & 0x0f // focus is w PORTA |= w // focus is PORTAbecomes
w = var1 w = w & 0x0f w |=> PORTA // same as previous code, but now the focus is wwhich can be rewritten like this:
w = var1, & 0x0f, |=> PORTASome instructions are notated as unary operators in Atypical; their focus is their operand:
--var1 // focus is var1 com var1 // focus is var1 clr var1 // focus is var1 --var1, com, clr // the preceding instructions comma-splicedThe focus of the bit set and clear instructions is the file register:
PORTA<0> = 1 // focus is PORTA PORTA<7> = 0 // focus is PORTA PORTA<0> = 1, <7> = 0 // comma-spliced equivalentSome instructions have no operand to be the focus; those instructions can be combined on one line with semicolons in between:
nop; nop; nop;You can mix and match:
w = var1, com; call DoSomething; w &= 0x7f, => var2
Variables
Variables in Atypical correspond to file registers. They are defined with the byte keyword and are allocated consecutively starting with the register specified in the varorg pseudo-op.varorg 0x20 byte x // x is file register 0x20 byte a, b, c // a is 0x21, b 0x22, c 0x23You can also specify file registers explicitly:
varorg 0x20 byte PORTA : 0x05 // PORTA is file register 0x05 byte PORTB : 0x06 // guess what byte x // x is 0x20You can define arrays:
varorg 0x20 byte a[3] // a[0] is 0x20, a[1] is 0x21, a[2] is 0x22 byte b // b is 0x23Indirect addressing is done either the traditional way, through INDF,
w = INDF // movf 0, wor with the special notation @FSR, which assembles to exactly the same thing:
w = @fsr // movf 0, w
The @ is supposed to be reminiscent of C's * dereferencing operator, and thus is supposed to indicate indirection through the FSR file register. @ can only be followed by "fsr" (case-insensitive).
Note: Forward references to variables and constants are not allowed.
Constants
Constants are either literals,w = 15 // decimal w = 0x0f // hex w = 0b1111 // binary w = 'A' // ASCIIor named constants defined with the const keyword:
const max_len = 100You can form constant expressions with +, -, *, /, &, |, ^, <<, and >>. These all have equal precedence, so use parentheses to change the order of evaluation.
const recsize = 3, numrecs = 10 const buffsize = 1 + (recsize * numrecs)Note that if you use a constant expression in an instruction, you must enclose the expression in parentheses:
w = w + recsize // plain constant doesn't need parentheses w = w + (recsize * 2) // constant expression needs parenthesesYou can use the # operator to make a constant equal to the address of a label or variable:
varorg 0x20 byte a w = (#a), => fsr // put address of a into FSR (note parentheses around constant expression) w = a, => fsr // put contents of a into FSRThe symbol $ is the address in program memory where the next assembled instruction will go. It's not really a constant, but it can be used as one.
Control flow
Labels
A label can be the target of a goto or call instruction. A label may be placed on any line that isn't a pseudo-op. The label can start in any column but it must be the first non-blank sequence on the line (in other words, any instructions on that line must come after the label).
infinite_loop: // here's a label (infinite_loop)
call do_something
goto infinite_loop
do_something: w = PORTA // another label (do_something)
// etc etc
Local labels are labels that begin with the . (dot) character. Local labels need be unique only within their scope; local labels may be redefined in other scopes. A non-local (global) label starts a new scope. Within its scope, a local label can be referred to as itself; outside its scope, a local label can be referred to by prepending the global label to the local label.
Blah: goto Foo.0 // forward reference to local label in Foo Foo: // global label Foo starts new scope ... .0: ... // local label (Foo.0) goto .0 Bar: ... // start new scope (Bar) .0: ... // this .0 doesn't conflict with previous one goto .0 .zzz: ... // local labels can be alphanumeric
Variables and constants may also be local to a scope. Note that global variables and constants do not start new scopes.
Foo: byte .i // local variable Foo.i byte Total // global variable doesn't start new scope const .max=10 // local variable Foo.max byte .0 // ERROR: only local labels may be numeric
Basic control flow
The basic Atypical notation for the conditional instructions is of this general form:
if condition then skipwhere condition is a bit test or a decrement (or increment) and test against zero (see the instruction set tables).
However, the basic form is rarely used; far more common is the one-liner if, in which the condition is negated and the instruction that would have followed the if is instead incorporated into the same line. For example,
if STATUSis more clearly expressed as this one-liner:then skip goto nonzero
if !STATUSorthen goto nonzero // exactly the same machine code as above
if STATUSThe one-liner if is not restricted to goto instructions:==0 then goto nonzero
if !STATUSAny single instruction can follow the then.then ++Count
Structured control flow
One can use the basic Atypical conditionals (or the one-liner if) along with the goto instruction and explicit labels to emulate the unstructured control flow typical of assembly language programming; however, Atypical also provides structured if/then/else and looping constructs. These reduce spaghetti code and make program flow more understandable, but their translation into machine code is less transparent than the standard PICmicro instructions.| Atypical | Assembly language |
if condition then
...
endif
|
btfsc/btfss/decfsz/incfsz (condition) goto .endif_label ... .endif_label: |
if condition then
...
else
...
endif
|
btfsc/btfss/decfsz/incfsz (condition) goto .else_label ... goto .endif_label .else_label: ... .endif_label: |
if condition1 then
...
elseif condition2 then
...
// there can be multiple elseif clauses
else
...
endif
|
btfsc/btfss/decfsz/incfsz (condition1) goto .elseif_label ... goto .endif_label .elseif_label: btfsc/btfss/decfsz/incfsz (condition2) goto .else_label ... goto .endif_label .else_label: ... .endif_label: |
// infinite loop
do
...
loop
|
.do_label: ... goto do_label |
// loop (test at top)
while condition do
...
loop
|
goto .test .do_label: ... .test: btfsc/btfss/decfsz/incfsz (!condition) goto .do_label |
// loop (test at bottom)
do
...
loop while condition
|
.do_label: ... .test: btfsc/btfss/decfsz/incfsz (!condition) goto .do_label |
You can also use break to exit a loop, and continue to jump directly to the next iteration of a loop.
Control-flow structures can, of course, be nested.
Atypical does not perform any optimizations; the various program-flow constructs always generate the code described above.
Note that, because decfsz and incfsz do not have "skip on non-zero" counterparts, you must choose your conditions carefully when decrementing or incrementing. Some constructs require that you test for equality to 0; others for inequality.
| Atypical construct | Examples of suitable conditions |
|
explicit skip form; if/then (not one-liner if) |
--f == 0 (test for 0) !w = ++f |
|
one-liner if; while loops |
++f != 0 (test for non-0) w = --f |
Jump tables and lookup tables
Jump tables are formed by following a goto with a list of labels surrounded by braces: goto { .0, .1, ErrorHandler }
// equivalent to goto .0; goto.1; goto ErrorHandler
Similarly, lookup tables are formed by following a retlw
with a list of constants surrounded by braces:
retlw { 10, 0xff, 'y' }
// equivalent to retlw 10; retlw 0xff; retlw 'y'
Lookup tables can also contain string constants:
retlw { "Hello, world!", CR, LF, 0 }
The assembler does not check for page boundary crossings.
You can implement such a check with the assert pseudo-op:
.table: retlw { "0123456789abcdef" }
assert ($-1>>8) == (#.table>>8)
Pseudo-ops
| Pseudo-op | Description |
|
assert const_expr |
Generates a warning if const_expr is false (zero). Example: assert ($-1>>8) == (#.table2>>8) |
|
bank const_expr |
Specifies the bank that the program is supposed to be accessing. Example: byte TRISA : 0x85 // TRISA is in bank 1 ... STATUS |
|
byte var_list |
Defines variables. Example: byte a, TRISA:0x85, buff[10] |
|
config const_expr |
Specifies the configuration byte. Example: config _WDT_OFF & _INTRC_OSC_CLKOUT |
|
const const_list |
Defines named constants. Example: const height=50, width=20 |
|
include "filename" |
Includes the named file. Typically used for processor-specific header files. Example: include "p16f877.aty" |
|
list |
Enables output to the listing file. |
|
nolist |
Disables output to the listing file. |
|
org const_expr |
Specifies the location in program memory where the next instruction will be assembled. Example: org 0x0100 |
|
varorg const_expr |
Specifies the starting file register for variables. Example: varorg 0x20 |
Reserved words
The following words are reserved and cannot be used for variables, constants, or labels.
| Atypical | else | retlw |
| assert | elseif | return |
| bank | endif | rol |
| break | goto | ror |
| byte | if | skip |
| call | include | sleep |
| clr | list | swap |
| clrwdt | loop | then |
| config | nolist | varorg |
| const | nop | w |
| continue | org | W |
| do | retfie | while |