home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / nqc_10b1.zip / nqcc.txt < prev    next >
Text File  |  1998-11-15  |  16KB  |  484 lines

  1. Not Quite C Compiler
  2. version 1.0 b1
  3.  
  4. Introduction
  5.  
  6. NQC is a simple language for programming the LEGO RCX.  The preprocessor and 
  7. control structures of NQC are very similar to C.  NQC is not a general purpose language - 
  8. there are many restrictions that originate from actual limitations and lack of detailed 
  9. information about the RCX itself.
  10.  
  11. A NQC program consists of tasks and subroutines.  The first task is started automatically 
  12. when the program is run, the remaining tasks can be started by the program.  Subroutines 
  13. can be shared across all of the tasks.
  14.  
  15. The compiler itself understands very little of the RCX.  Most of the RCX functionality 
  16. (such as playing a sound, reading an input sensor, etc) is added by the rcx.nqh include.  
  17. This file is included automatically by the compiler (unless -s is specified on the command 
  18. line).  The file itself defines many high level macros (such as PlaySound()) that are 
  19. implemented in terms of low level primitives within the compiler itself.  This approach 
  20. means that new "system" features can be easily added to rcx.nqh without modifying the 
  21. compiler or introducing new keywords.
  22.  
  23. Usage
  24.  
  25. The nqcc command line useage looks like this:
  26.  
  27. nqcc [-s] [-d] [-l] [-e] [-o output_file] [-Dsym=[def]]
  28.      [-Usym] source_file
  29.  
  30. The default behavior is to compile the specified source_file to an output file that is named 
  31. by replacing the ".nqc" extension of the source filename with ".rcx".  If the source 
  32. filename does not end with ".nqc", the default output filename is the source filename with 
  33. ".rcx" appended to it.  The "-o" option may be used to specify an alternate name for the 
  34. output file.
  35.  
  36. The source file name "-" causes nqcc to read stdin as the source file.  In this case, the -o 
  37. option must be used to explicitly name an output file if one is desired.
  38.  
  39. The "-s" option prevents the compiler for automatically including "rcx.nqh" before 
  40. compiling the source file.
  41.  
  42. The "-d" option causes nqcc to atttempt downloading the program to an RCX after 
  43. compiling it.  If an output file was not explicitly named ("-o" option), then no output file 
  44. is generated.
  45.  
  46. The "-l" option writes a bytecode listing to stdout.
  47.  
  48. The "-e" options sends compiler error messages to stdout instead of stderr.  This option is 
  49. only needed when running nqcc under Win95, which does not support redirection of 
  50. stderr from the standard shell (command.com).  The following examples show how to 
  51. redirect stderr (to a file called errors) under other platforms:
  52.  
  53. MPW:      nqcc test.nqc │errors
  54. WinNT:  nqcc test.nqc 2>errors    
  55.  
  56. The -Dsym[=def] option defines preprocessor symbol sym to the value def.
  57.  
  58. The -U option undefines preprocessor symbol def.
  59.  
  60. Two environment variables are used by nqcc:
  61.  
  62. NQCC_INCLUDE  - this environment variable can be used to specifiy a directory to 
  63. search for include files in addition to the current working directory.  When building 
  64. filenames, nqcc simply concatentates the value of NQCC_INCLUDE with the target 
  65. filename, so NQCC_INCLUDE must end in the appropriate directory separator ('\' for 
  66. PC, ':' for Mac, '/' for Unix).
  67.  
  68. RCX_PORT - this environment variable can be used to specify a serial port other than the 
  69. default (COM1 for PC, Modem for Mac).  The value of RCX_PORT should be the name 
  70. of the port (for example "COM2" for the PC, or "b" for the Mac's printer port).
  71.  
  72.  
  73. Preprocessor
  74.  
  75. The preprocessor implements the following directives: #include, #define, #ifdef, #ifndef, 
  76. #if, #elif, #else, #endif, #undef.
  77.  
  78. The #include command works as expected, with the caveat that the filename must be 
  79. enclosed in double quotes:
  80.  
  81. #include "foo.nqh"
  82.  
  83. The #define command is used for simple macro substitution.  Redefinition of a macro is 
  84. an error (unlike in C where it is a warning).  Macros are normally terminated by the end 
  85. of the line, but the newline may be escaped to allow multi-line macros:
  86.  
  87. #define foo(x)  do { bar(x); \
  88.                      baz(x); } while(false)
  89.  
  90. Conditional compilation works similar to the C preprocessor.  Expressions in #if 
  91. directives use the same operators and precedence as in C.
  92.  
  93. C and C++ style comments are supported
  94.  
  95. /* this is
  96.    a comment */
  97.  
  98. // another comment
  99.  
  100.  
  101. Tasks and Subroutines
  102.  
  103. Each program is made up of tasks and subroutines, each of which is composed of 
  104. statements.  Each program must have at least one task (named "main"), which serves as 
  105. the entry point of the program.
  106.  
  107. A task looks like this:
  108.  
  109. task task_name
  110. {
  111.    statements
  112. }
  113.  
  114.  
  115. Tasks can be started or stopped with the following statements
  116.  
  117. start task_name;
  118. stop task_name;
  119.  
  120.  
  121. Subroutines look very similar to tasks, but use the sub keyword.  Note that there are no 
  122. parameters or return values for subroutines.  The only variables available are global 
  123. variables.
  124.  
  125. sub sub_name
  126. {
  127.     statements
  128. }
  129.  
  130. Subroutines are called just like in C, with the caveat that there are not parameters or 
  131. return value:
  132.  
  133. sub_name();
  134.  
  135. Due to a limitation in the RCX firmware, subroutines are not allowed to make subroutine 
  136. calls (either to themselves or another subroutine).
  137.  
  138. Statements are contained within tasks and subroutines.  Like C, the semicolon (';') is used 
  139. as a statement terminator.  Groups of statements can be combined into a a block using 
  140. braces ('{' and '}').
  141.  
  142.  
  143. Values
  144.  
  145. Many statements (such as variable assignment, or conditional testing) make use of 
  146. "values".  A value can be many things: a constant, the contents of a global variable, a 
  147. random number, the reading from an input sensor, etc.  Each of these is covered in more 
  148. detail below.  In addition, some of the high level commands may take constants and/or 
  149. values as arguments.  In the description of a command its arguments will be noted as 
  150. either "const" or "value".  "const" arguments must be a numeric constant.  "value" 
  151. arguments may be either a numeric constant or another sort of value (such as a variable).  
  152. Note that not all value types are legal for all commands that use values.
  153.  
  154. Numeric constants can be written as either decimal (e.g. 123) or hexidecimal (e.g. 
  155. 0xABC).  Presently, there is very little range checking on constants, so using a value 
  156. larger than expected can have unpredictable results.  Constants can be combined using the 
  157. standard arithmetic operators (+, -, *, /, %, <<, >>, &, |, ^, ~).  Precedence and 
  158. associativity are the same as C.  Parentheses can be used to group lower precedence 
  159. operations together.
  160.  
  161. The RCX provides 32 global variables.  In order to use variables in your program, you 
  162. must first declare them:
  163.  
  164. int foo;
  165.  
  166. All of the variables are global (even across multiple programs), hence they must be 
  167. decalred outside the scope of any tasks and subroutines.  Variables can be assigned values 
  168. using the following assigment operators:
  169.  
  170. var = value;
  171. var += value;
  172. var -= value;
  173. var *= value;
  174. var /= value;
  175.  
  176. Note that general expressions (e.g. a = b + c) are only supported for compile time 
  177. constants and not for runtime evaluation:
  178.  
  179. int foo,bar;
  180.  
  181. task main
  182. {
  183.     foo = 2 + 3;  // legal
  184.     foo = bar + 2; // illegal
  185. }
  186.  
  187. Other value types inclue:
  188.  
  189.     Input(n)    - current value of sensor 'n', where n=0-2
  190.     Timer(n)    - current value of timer 'n', where n=0-2
  191.     Random(n)    - random number between 0 and n-1? (not sure about the range)
  192.     Message()    - value of the last received IR message
  193.     Watch()    - value of the system clock 
  194.  
  195. Note: the rxc.nqh file defines macros IN_1, IN_2, and IN_3 as Input(0), Input(1), and 
  196. Input(2), respectively. 
  197.  
  198.  
  199. Control Structures
  200.  
  201. Conditions can be either a comparison beteen two values, or one of the predefined 
  202. constants "true" and "false".  There are six relations (<, >, <=, >=, ==, !=).  Conditions 
  203. may be combined using && and ||, or negated with '!".
  204.  
  205. Here are the control structures:
  206.  
  207. if (condition) statement
  208. if (condition) statement else statement
  209. while(condition) statement
  210. do statement while(condition)
  211. repeat(value) statement
  212. wait (condition) statement
  213.  
  214. The first four structures are identical to their counterparts in C. 
  215.  
  216. The "repeat" structure causes a statement to be executed a number of times.  This number 
  217. does not have to be a constant, for example it could be the value of a global variable, 
  218. however the number is only evaluated prior to starting the loop (as opposed to the "for" 
  219. structure in C which evaluates the condition before each iteration).  The RCX allows 
  220. "repeat" loops to be nested (maintaining its own "stack" of look counters).  The "break" 
  221. statement is also legal within repeat loops, although it has a rather unexpected result 
  222. when used within a nested loop.  Although the control flow will be broken, there is no 
  223. way to pop the old counter off the internal stack, so the enclosing loop will be using the 
  224. wrong counter when it starts repeating.  This cannot be reliably checked at compile time 
  225. since the internal repeat stack is shared across subroutine calls.
  226.  
  227. The "wait" structure executes the body until the condition becomes true.  It is actually 
  228. implemented as a macro (so be careful of side effects).  It it handy when waiting for 
  229. something to happen:
  230.  
  231. wait(IN_3 == 1); // wait for sensor 3 to have value 1
  232.  
  233.  
  234. Several other control flow statements are supported:
  235.  
  236. break;
  237. continue;
  238. return;
  239.  
  240. "break" and "continue" can be used within "do" and "while" loops.
  241. "return" can be used to exit a subroutine before getting to its end.
  242.  
  243.  
  244. Sensors
  245.  
  246. The constants IN_1, IN_2, and IN_3 can be used to either read an input sensor, or to 
  247. specify a sensor.  Sensor types are defined by IN_SWITCH, IN_LIGHT, IN_ANGLE, 
  248. IN_PULSE, and IN_EDGE.  The Sensor() function is used to initialize the sensor:
  249.  
  250. Sensor(value sensor, const type);
  251.  
  252. Some sensor types (such as the IN_ANGLE type) allow you to "reset" the sensors 
  253. internal counter with the following command:
  254.  
  255. ClearSensor(value sensor);
  256.  
  257. The only supported value types for "sensor" in the above two examples are numeric 
  258. constants and Input(n) - all other types have undefined behavior.
  259.  
  260. Here's a brief example that configures sensor 1 as a switch, then waits for it to be pressed 
  261. before playing a sound.
  262.  
  263. Sensor(IN_1, IN_SWITCH);
  264. wait (IN_1 == 1);
  265. PlaySound(0);
  266.  
  267. The macros IN_1, IN_2, and IN_3 are equivalent to Input(0), Input(1), and Input(2) and 
  268. can be used anywhere that a runtime value is legal (such as setting a variable, checking a 
  269. condition, etc). 
  270.  
  271.  
  272. Motors
  273.  
  274. The constants OUT_A, OUT_B, and OUT_C refer to the three outputs.  They can be 
  275. combined (using + or |) in order to specify multiple outputs.  OUT_FULL is a constants 
  276. that represents full speed for a motor.  Here's how to turn on all three motors for 1 
  277. second:
  278.  
  279.     Fwd(OUT_A + OUT_B + OUT_C, OUT_FULL);
  280.     Sleep(100);
  281.     Off(OUT_A + OUT_B + OUT_C);
  282.  
  283. These commands control motors:
  284.  
  285. Fwd(const outs, value speed) // turn motor(s) on forwards
  286. Rev(const outs, value speed) // turn motor(s) on backwards
  287. Off(const outs) // stop motor(s)
  288.  
  289.  
  290. Other Commands
  291.  
  292. PlaySound(const n); // play system sound #n
  293. PlayNote(const freq, const duration); // play a note
  294.  
  295. Sleep(value v) // sleep for v ticks (100 ticks per second)
  296.  
  297. Display(value v) // set the dispaly mode as follows:
  298.  
  299. Note: display modes for "Display" are as follows: 0 = system clock, 1-3 = input channel, 
  300. 4-6 = output channel.  To show the value of the third input, use "Display(3)", not 
  301. "Display(IN_3)".  The later command will read the value of sensor 3, and use that value 
  302. as the argument to the display command (e.g. if the sensor's value is 0, then the system 
  303. clock will be displayed).  Yeah, this is pretty stupid, but its how the RCX works.
  304.  
  305. SendMessage(value m);    // send low 8 bits of m as an IR message
  306. ClearMessage();    // clear the last received IR message
  307.  
  308. ClearTimer(const n);    // clear timer #n
  309.  
  310.  
  311. Data Logging
  312.  
  313. The RCX supports a Datalog feature.  Using this feature requires three steps:
  314.  
  315. 1) Set the datalog size
  316. 2) Add data points to the datalog
  317. 3) Upload the datalog to a host
  318.  
  319. You can set the datalog size with the following command
  320.  
  321. SetDatalog(const n); // create datalog for n points
  322.  
  323. Point can be added with
  324.  
  325. Datalog(value v); // add v to the datalog
  326.  
  327. Only certain types of values may be added (variables, timers, sensor readings, the system 
  328. clock).
  329.  
  330. Some (or all) of the datalog may be uploaded at any time as follows:
  331.  
  332. UploadDatalog(const index, count count);
  333.  
  334. "index" is the first data point to upload, and "count" is the number of points to upload.
  335.  
  336. Each entry in the datalog uses three bytes - the first byte is the type of entry,
  337. the next two are a 16 bit value in little endian format.  If you set the size of
  338. the datalog to N, then there are actually N+1 entries - the first one (at index 0)
  339. is the number of data points collected so far (type = 0xff), while the remaining
  340. ones (index 1 - N) are the points themselves.  Here are the type codes for normal
  341. data points:
  342.  
  343.     0x00 - value of a variable
  344.     0x20 - value of a timer
  345.     0x40 - value of a sensor
  346.     0x80 - value of the clock
  347.  
  348.  
  349. Keywords
  350.  
  351. The following keywords are used in nqcc:
  352.  
  353. asm        if        stop
  354. break        int        sub
  355. continue    repeat    task
  356. do        return    true
  357. else        start        while
  358. false
  359.  
  360.  
  361. Grammar
  362.  
  363. program : unit_list
  364.     ;
  365.  
  366. unit_list: unit_list unit
  367.     |
  368.     ;
  369.  
  370. unit     : int id_list ;
  371.     | task id block
  372.     | sub id block
  373.     ;
  374.  
  375. id_list    : id_list , id
  376.     | id
  377.     ;
  378.  
  379. block    : { stmt_list }
  380.     ;
  381.  
  382. stmt_list : stmt_list stmt
  383.     |
  384.     ;
  385.  
  386. stmt     : ;
  387.     | block
  388.     | asm  { bytes } ;
  389.     | while ( condition ) stmt
  390.     | do stmt while ( condition ) ;
  391.     | repeat ( value ) stmt
  392.     | break ;
  393.     | continue ;
  394.     | if ( condition ) stmt
  395.     | if ( condition) stmt else stmt
  396.     | id assign value ;
  397.     | start id ;
  398.     | stop id ;
  399.     | id ( ) ;
  400.     | return ;
  401.  
  402. bytes    : bytes , expr
  403.     | expr
  404.     ;
  405.  
  406. value    : expr
  407.     | id
  408.     | @ expr
  409.     ;
  410.  
  411. assign    : = | += | -= | *= | /= 
  412.     ;
  413.  
  414. expr    : number
  415.     | expr binop expr
  416.     | - expr
  417.     | ~ expr
  418.     | ( expr )
  419.     | [ value ]
  420.     ;
  421.  
  422. binop    : + | - | * | / | % | & | | | ^ | << | >> 
  423.     ;
  424.  
  425. condition : value relop value
  426.     | ! condition
  427.     | condition && condition
  428.     | condition || condition
  429.     | ( condition )
  430.     | true
  431.     | false
  432.     ;
  433.  
  434. relop    : == | != | < | > | <= | >=
  435.     ;
  436.  
  437.  
  438. For Hackers Only
  439.  
  440. NQC contains very little in the way of RCX specific functions.  Support for controlling 
  441. motors, reading sensors, etc. is provided in the rcx.nqh file.  This section explains some 
  442. of the low level constructs used within these definitions.
  443.  
  444. Internally, many of the RCX operations use "values", which are essentially a form of 
  445. typed data.  Each value has a "type" and a "index", which together specify a sort of 
  446. effective address for the runtime value.   Of course, one of the types is "constant", in 
  447. which case the effective value is equal to the index itself.  Other types include reading an 
  448. input sensor or generating a random number.
  449.  
  450. The compiler can perform arithmetic operations only on compile time constants.  Certain 
  451. statement (such as conditional clauses) require values, and constants are automatically 
  452. promoted to values in these situations.
  453.  
  454. However, sometimes it is desireable to specify the type/index pair directly.  This can be 
  455. done by using the @ operator, which converts a 24 bit number directly to a value.  The 
  456. lower 16 bits are the index, the next 8 are the type code.  For example, since the type code 
  457. for "constants" is 2, the following value is equal to 0x123:
  458.  
  459. @0x20123
  460.  
  461. The type code for variables is 0, so the following refers to variable 7:
  462.  
  463. @0x00007
  464.  
  465. This is very different from 0x00007, which equals the number 7.  It would've been much 
  466. nicer if LEGO had made the type code for constants equal to 0, but we have to live with 
  467. this hack.
  468.  
  469. Sometimes it is necessary to convert a value back into its "effective address".  This is 
  470. done by enclosing the value in brackets:
  471.  
  472. [ Random(7) ]
  473.  
  474. The effective address is constant, thus can be manipulated with the compiler's arithmetic 
  475. operators.
  476.  
  477. Finally, the "asm" statement allows most primitives to be implemented:
  478.  
  479. asm { data };
  480.  
  481. "data" is one or more numbers, separated by commas.  The asm statement emits the literal 
  482. data contained within its braces.  The data bytes for an asm statement must be compile 
  483. time contsatnts, not runtime values.
  484.