aboutsummaryrefslogtreecommitdiff
path: root/AT91SAM7S256/Source/c_cmd.h
blob: acd8676b1c996f488b403645acada1a0823511e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
//
// Date init       14.12.2004
//
// Revision date   $Date: 10-07-08 13:22 $
//
// Filename        $Workfile:: c_cmd.h                                       $
//
// Version         $Revision: 8 $
//
// Archive         $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_cmd. $
//
// Platform        C
//

//
// File Description:
// This file contains definitions and prototypes for the VM which runs bytecode
// programs compatible with LEGO MINDSTORMS NXT Software 1.0.
//

#ifndef   C_CMD
#define   C_CMD

//!!! MAX_HANDLES also defined in m_sched.h
#ifndef MAX_HANDLES
#define MAX_HANDLES 16
#endif

#include "c_cmd_bytecodes.h"
#define SYSCALL_COUNT 48

extern    const HEADER cCmd;

//
//Standard interface to other modules
//
void      cCmdInit(void* pHeader);
void      cCmdCtrl(void);
void      cCmdExit(void);

//
//ARM_NXT vs SIM_NXT
//These definitions are set up to allow compiling this code for use in a simulated (non-ARM7) environment.
//If your toolchain doesn't automatically use the __ICCARM__ token, define it to ensure normal compilation.
//
#ifdef __ICCARM__
#define ARM_NXT
#else
#define SIM_NXT
#endif

//
//ENABLE_VM toggles compilation the main body of VM code.
//Define it as 0 to compile alternate implementation for testing (see bottom of c_cmd.c)
//
#define ENABLE_VM 1
#undef ARM_DEBUG
//
//VM_BENCHMARK enables extra instrumentation code to measure VM performance.
//When enabled, a file named "benchmark.txt" is produced every time a program completes.
//
#define VM_BENCHMARK (ENABLE_VM && 0) //<-- Toggle to turn on benchmark calculations

#if VM_BENCHMARK
//Prototype for benchmark recording function
void cCmdWriteBenchmarkFile();
#endif

//
//Run-time assert macros
//Use these to test for unexpected conditions
//If expr evaluates as false while running under a debugger,
// a software breakpoint exception is thrown.
//NXT_BREAK is just a shortcut for unconditional break.
//
//Assert definitions behind WIN_DEBUG only make sense when compiling SIM_NXT
// under an x86 Windows debugger.
//
#if defined WIN_DEBUG
//"int 3" is a break exception on x86
#define NXT_ASSERT(expr) if (expr) {} else { __asm {int 3} }
#define NXT_BREAK NXT_ASSERT(0)
//
//Assert definitions behind ARM_DEBUG aren't quite as handy as WIN_DEBUG,
// but they do record the code line causing the last assert failure.
//
#elif defined(ARM_DEBUG)
#define NXT_ASSERT(expr) if (expr) {}\
                         else\
                         {\
                           VarsCmd.AssertFlag = TRUE;\
                           VarsCmd.AssertLine = __LINE__;\
                         }
#define NXT_BREAK NXT_ASSERT(0);
#else
//Not debugging, so #defined as nothing
//!!! Note that these definitions means all usages of NXT_ASSERT and NXT_BREAK
// get stripped out of an unmodified ARM7 build.
//Unless ARM_DEBUG is enabled, treat them as documentation of expected values.
#define NXT_ASSERT(expr)
#define NXT_BREAK
#endif

//
//Status byte used to return requests for further action or errors
//Valid codes #defined in c_cmd.iom
//!!!JLOFTUS Replace with NXT_STATUS?  Same for ASSERTS? Others? Risk factors?
//
typedef SBYTE NXT_STATUS;

#if ENABLE_VM

//Intial values for clump records are packed into 4 bytes in the file format.
#define VM_FILE_CLUMP_REC_SIZE 4

//
// Definitions for dataspace management, IO Map (IOM) access, and bytecode instruction structure
//

//Type codes for use in the dataspace table-of-contents (DSTOC)
typedef UBYTE TYPE_CODE;

enum
{
  //VOID type for unused DS elements; never valid to address them from bytecode
  TC_VOID,

  //Simple scalar integers, equivalent to matching basic types from stdconst.h
  TC_UBYTE,
  TC_SBYTE,
  TC_UWORD,
  TC_SWORD,
  TC_ULONG,
  TC_SLONG, TC_LAST_INT_SCALAR= TC_SLONG,

  //Aggregate types containing one or more scalar
  TC_ARRAY,
  TC_CLUSTER,

  //Mutex tracks current holder and any waiting clumps
  TC_MUTEX,
  TC_FLOAT, TC_LAST_VALID= TC_FLOAT
};

//Sizes (in bytes) of each scalar type
#define SIZE_UBYTE 1
#define SIZE_SBYTE 1
#define SIZE_UWORD 2
#define SIZE_SWORD 2
#define SIZE_ULONG 4
#define SIZE_SLONG 4
#define SIZE_FLOAT 4

//MUTEX record is a struct containing 3 8-bit CLUMP_IDs, packed into 32-bit word
//See MUTEX_Q typedef
#define SIZE_MUTEX 4

//Module IDs for IO map addressing
enum
{
  MOD_INPUT,
  MOD_OUTPUT
};

//Field IDs for input IOM
enum
{
  IO_IN_TYPE,
  IO_IN_MODE,
  IO_IN_ADRAW,
  IO_IN_NORMRAW,
  IO_IN_SCALEDVAL,
  IO_IN_INVALID_DATA
};

//FPP = Fields Per Port
#define IO_IN_FPP 6
#define IO_IN_FIELD_COUNT (IO_IN_FPP * NO_OF_INPUTS)

//Field IDs for input IOM
enum
{
  IO_OUT_FLAGS,
  IO_OUT_MODE,
  IO_OUT_SPEED, //AKA "Power"
  IO_OUT_ACTUAL_SPEED,
  IO_OUT_TACH_COUNT,
  IO_OUT_TACH_LIMIT,
  IO_OUT_RUN_STATE,
  IO_OUT_TURN_RATIO,
  IO_OUT_REG_MODE,
  IO_OUT_OVERLOAD,
  IO_OUT_REG_P_VAL,
  IO_OUT_REG_I_VAL,
  IO_OUT_REG_D_VAL,
  IO_OUT_BLOCK_TACH_COUNT,
  IO_OUT_ROTATION_COUNT,
  IO_OUT_OPTIONS,
};

#define IO_OUT_FPP 16
#define IO_OUT_FIELD_COUNT (IO_OUT_FPP * NO_OF_OUTPUTS)

//
//DS_TOC_ENTRY is a record in the dataspace table of contents
//The TypeCode describes the data which is stored at Dataspace[DSOffset]
//
typedef struct
{
  TYPE_CODE TypeCode;
  UBYTE Flags;
  SWORD DSOffset;
} DS_TOC_ENTRY;

//DS_TOC_ENTRY Flags
//!!! Yes, there's only one flag defined for an 8-bit field.
//ARM7 alignment rules means those bits would otherwise just be padding, anyway.
#define DS_DEFAULT_DEFAULT  1 //This entry has no default value in file; fill with zero at activation time

//DS_ELEMENT_ID (AKA "DS item ID") indexes DataspaceTOC
typedef UWORD DS_ELEMENT_ID;

//Special flag value used for opcode-specific default behavior when real dataspace argument is not provided
#define NOT_A_DS_ID 0xFFFF

//Macro to bump DS_ELEMENT_IDs +1 with a cast (mostly to quash annoying warnings)
#define INC_ID(X) ((DS_ELEMENT_ID)(X + 1))

//DATA_ARG may contain a DS_ELEMENT_ID or encoded IO map address
typedef UWORD DATA_ARG;

//CODE_WORD is a single indexable element of the codespace
typedef SWORD CODE_WORD;

//CODE_INDEX indexes codespaces for opcodes and args
//!!! UWORD CODE_INDEX currently limits programs to 128KB code
// Yes, this is "plenty", but noted here to make sure we think about it
// when considering code size changes
typedef UWORD CODE_INDEX;

//Typedef and define to hold and check for valid file handles
typedef UBYTE FILE_HANDLE;

#define NOT_A_HANDLE 0xFF

//
// Dynamic Memory Manager
//

typedef UWORD DV_INDEX; //Dope Vector Index: Index into the DopeVectorArray

//DOPE_VECTOR struct: One instance exists in the DopeVectorArray for every array in the dataspace.
typedef struct
{
  UWORD Offset;
  UWORD ElemSize;
  UWORD Count;
  DV_INDEX BackLink; // points to previous DV
  DV_INDEX Link; // points to next DV
} DOPE_VECTOR;

//
//MEM_MGR struct
//Head and Tail keep track of the main linked-list of dope vectors,
// which must be maintained in ascending order according to Offset
//FreeHead is the head DV of the list of allocated but unused DVs
//pDopeVectorArray is initialized at activation-time to point to the master DVA
//
typedef struct
{
  DV_INDEX Head;
  DV_INDEX Tail;
  DV_INDEX FreeHead;
  DOPE_VECTOR * pDopeVectorArray;
} MEM_MGR;

//Macro to shorten common DVA access code
#define DV_ARRAY VarsCmd.MemMgr.pDopeVectorArray
//# of nodes to alloc when the Dope Vector Array is full
#define DV_ARRAY_GROWTH_COUNT 25
//Flag value for invalid Offset fields in DVs
#define NOT_AN_OFFSET 0xFFFF
//Check for legal index into DVA
#define IS_DV_INDEX_SANE(X) (((X) > 0) && ((X) < DV_ARRAY[0].Count))

//
// Message Queuing
//

//
//There are 10 incoming and 10 outgoing message queues, each 5 messages deep
//A "message" is defined as a null-terminated string under MAX_MESSAGE_SIZE
//
#define MESSAGES_PER_QUEUE  5
#define MESSAGE_QUEUE_COUNT 20
#define INCOMING_QUEUE_COUNT ((MESSAGE_QUEUE_COUNT)/2)
#define NOT_A_QUEUE 0xFF

//
//MAX_MESSAGE_SIZE including null-terminator
//!!! Capped at 59 unless USB protocol assumptions are changed!
//
#define MAX_MESSAGE_SIZE    59

//A MESSAGE is a dynamically sized string, so we use a DV_INDEX to get to its information
typedef DV_INDEX MESSAGE;

//
//MESSAGE_QUEUE keeps track of last messages read and written (acts as a circular buffer)
//
typedef struct
{
  UWORD ReadIndex;
  UWORD WriteIndex;
  MESSAGE Messages[MESSAGES_PER_QUEUE];
} MESSAGE_QUEUE;

//Handy macros for accessing MESSAGE_QUEUEs
#define GET_WRITE_MSG(QueueID) (VarsCmd.MessageQueues[(QueueID)].Messages[VarsCmd.MessageQueues[(QueueID)].WriteIndex])
#define GET_READ_MSG(QueueID) (VarsCmd.MessageQueues[(QueueID)].Messages[VarsCmd.MessageQueues[(QueueID)].ReadIndex])
#define SET_WRITE_MSG(QueueID, DVIndex) (VarsCmd.MessageQueues[(QueueID)].Messages[VarsCmd.MessageQueues[(QueueID)].WriteIndex] = (DVIndex))
#define SET_READ_MSG(QueueID, DVIndex) (VarsCmd.MessageQueues[(QueueID)].Messages[VarsCmd.MessageQueues[(QueueID)].ReadIndex] = (DVIndex))

//
// Datalog Queuing
//
// The datalog queue is loosely modeled around the message queue except that there is only one queue, not an array of them.
//

// A datalog has one less byte of 'header' info so different max size
#define MAX_DATALOG_SIZE    60

// The number of datalog messages to buffer
#define DATALOG_QUEUE_DEPTH  30

// A DATALOG_MESSAGE is a dynamically sized string, so we use a DV_INDEX to get to its information
typedef DV_INDEX DATALOG_MESSAGE;

//
// DATALOG_QUEUE keeps track of last messages read and written (acts as a circular buffer)
typedef struct
{
  UWORD ReadIndex;
  UWORD WriteIndex;
  DATALOG_MESSAGE Datalogs[DATALOG_QUEUE_DEPTH];
} DATALOG_QUEUE;

//Handy macros for accessing the DATALOG_QUEUE
#define GET_WRITE_DTLG() (VarsCmd.DatalogBuffer.Datalogs[VarsCmd.DatalogBuffer.WriteIndex])
#define GET_READ_DTLG() (VarsCmd.DatalogBuffer.Datalogs[VarsCmd.DatalogBuffer.ReadIndex])
#define SET_WRITE_DTLG(DVIndex) (VarsCmd.DatalogBuffer.Datalogs[VarsCmd.DatalogBuffer.WriteIndex] = (DVIndex))
#define SET_READ_DTLG(DVIndex) (VarsCmd.DatalogBuffer.Datalogs[VarsCmd.DatalogBuffer.ReadIndex] = (DVIndex))


//
//Definitions related to dataflow scheduling
//

//CLUMP_IDs are used to index list at pAllClumps
typedef UBYTE CLUMP_ID;

//
//The last value in CLUMP_ID's range is reserved as NOT_A_CLUMP
//This is useful as a queue terminator and general placeholder
//
#define NOT_A_CLUMP 0xFF
#define MAX_CLUMPS  255
#define INSTR_MAX_COUNT 20

//CLUMP_Q struct for tracking head and tail of a queue of clumps
typedef struct
{
  CLUMP_ID Head;
  CLUMP_ID Tail;
} CLUMP_Q;

//
//MUTEX_Q is a struct to be stashed in the dataspace to track state of a mutex
//If mutex is free, Owner field is NOT_A_CLUMP and WaitQ is empty.
//The mutex is acquired by stashing a new owner's ID.
//If others attempt to acquire, they will be put on the WaitQ
//
typedef struct
{
  CLUMP_ID Owner;
  CLUMP_Q WaitQ;
} MUTEX_Q;

//
// Clump Record, run-time book-keeping for each clump
//
// CodeStart: Start of this clump's bytecodes, absolute address
// CodeEnd: End of this clump's bytecodes, absolute address
// PC: "program counter" -- current offset into codespace relative to CodeStart
// InitFireCount: Initial count of upstream dependencies
// CurrFireCount: Run-time count of unsatisfied dependencies
// Link: ID of next clump in the queue.  NOT_A_CLUMP denotes end or bad link.
//
// clumpScalarDispatchHints: this clump only uses scalar data args, can be interpretted with faster dispatch tables
//
// pDependents: pointer to list of downstream dependents' ClumpIDs
// awakenTime: If a clump is on rest queue for sleep, this is the time at which it will return to runQueue
// DependentCount: Count of downstream dependents
//
typedef struct
{
  CODE_WORD*  CodeStart;
  CODE_WORD*  CodeEnd;
  CODE_WORD*  PC;
  UBYTE     InitFireCount;
  UBYTE     CurrFireCount; //AKA ShortCount
  CLUMP_ID  Link;

  UBYTE     clumpScalarDispatchHints;

  CLUMP_ID* pDependents;
  ULONG     awakenTime;
  UBYTE     DependentCount;
} CLUMP_REC;

//
//Definitions for memory pool management
//

//First valid pointer into the memory pool
#define POOL_START ((UBYTE*)(VarsCmd.Pool))

//Sentinel points one byte *past* the pool -- i.e. first bad pool pointer
#define POOL_SENTINEL ((UBYTE*)(VarsCmd.Pool + VarsCmd.PoolSize))

//Alignment mod for Pool and all sub-fields of the Pool
#define POOL_ALIGN SIZE_SLONG

#define ALIGN_TO_MOD(val,mod) if ((val) % (mod) != 0) { (val) += (mod) - ((val) % (mod)); } else {}

//
//Internal states of the VM
//VM_IDLE: Just sitting around.  Request to run program will lead to ONE of the VM_RUN* states.
//VM_RUN_FREE: Attempt to run as many instructions as possible within our timeslice
//VM_RUN_SINGLE: Run exactly one instruction per timeslice
//VM_RUN_PAUSE: Program still "active", but someone has asked us to pause
//VM_RESET2: Final clean up and return to IDLE
//VM_RESET1: Initialize state variables and some I/O devices -- executed when programs end
//
typedef enum
{
  VM_IDLE,
  VM_RUN_FREE,
  VM_RUN_SINGLE,
  VM_RUN_PAUSE,
  VM_RESET1,
  VM_RESET2,
} VM_STATE;

//
// VARSCMD: Private state data for active program and VM system
//
//pCodespace: pointer for flat codespace (stored in flash, includes all clumps)
//CodespaceCount: count of code words
//
//pAllClumps: Pointer to list of CLUMP_RECs
//AllClumpsCount: Count of CLUMP_RECs in list
//
//RunQ: Head and tail of run queue (elements in-place in AllClumps list)
//
//pDataspaceTOC: Pointer to DSTOC entries (stored in flash)
//DataspaceCount: Count of entries in DSTOC
//pDataspace: Base pointer of actual dataspace
//DataspaceSize: Size, in bytes, of dataspace
//DSStaticSize: Size, in bytes, of static portion of the dataspace (used as an offset to the dynamic dataspace)
//
//VMState: Internal state of VM's loader/scheduler (cCmdCtrl())
//
//MemMgr: Contains data to manage dynamic arrays
//
//PoolSize: Current size of main memory pool, in bytes.
//Pool: Static pool of bytes for stashing all program run-time data
//
//ActiveProgHandle: Handle of the program that is currently running
//ActiveProgName: Stashed name of currently running program, if any
//
//FileHandleTable: Table of file names opened by program while running.
// First byte of each record is 'r' or 'w' (read or write).
//
//MessageQueues: Message buffer tracking data
//
//CommStat, CommStatReset, CommCurrConnection, DirtyComm: Helper data for interfacing to c_comm module
//
//DirtyDisplay: Boolean reminding us to re-initialize the display if program used it
//
//StartTick: MS tick stashed when program started.  Used for relative time measurements.
//
//Further notes on the memory pool:
// The main memory pool is used for all clump records, dataspace tracking data,
// and the dataspace itself.  In other words, pAllClumps and
// pDataspace must all point to memory within the pool.  Watch for NXT_ASSERTs
// to enforce safe indexing into the pool.
//
typedef struct
{
  CODE_WORD*    pCodespace;
  CLUMP_REC*    pAllClumps;
  DS_TOC_ENTRY* pDataspaceTOC;
  UBYTE*        pDataspace;
  UBYTE*        Pool;

  ULONG     PoolSize;
  UWORD     CodespaceCount;
  CLUMP_ID  AllClumpsCount;
  UWORD     DataspaceCount;
  UWORD     DataspaceSize;
  UWORD     DSStaticSize;

  VM_STATE VMState;

  MEM_MGR   MemMgr;

  CLUMP_Q    RunQ;
  CLUMP_Q    RestQ;

  UBYTE ActiveProgHandle;
  UBYTE ActiveProgName[FILENAME_LENGTH + 1];

  UBYTE FileHandleTable[MAX_HANDLES][FILENAME_LENGTH + 2];

  MESSAGE_QUEUE MessageQueues[MESSAGE_QUEUE_COUNT];

  SWORD CommStat;
  SWORD CommStatReset;
  UBYTE CommCurrConnection;

  UBYTE DirtyComm;
  UBYTE DirtyDisplay;

  ULONG StartTick;

  DATALOG_QUEUE DatalogBuffer;

#if VM_BENCHMARK
  ULONG InstrCount;
  ULONG Average;
  ULONG OverTimeCount;
  ULONG MaxOverTimeLength;
  ULONG CmdCtrlCount;
  ULONG CompactionCount;
  ULONG LastCompactionTick;
  ULONG MaxCompactionTime;
  ULONG OpcodeBenchmarks[OPCODE_COUNT][4];
  ULONG SyscallBenchmarks[SYSCALL_COUNT][4];
  UBYTE Buffer[256];
#endif

#if defined ARM_DEBUG
  UBYTE AssertFlag;
  ULONG AssertLine;
#endif
} VARSCMD;

//
//Activation
//

//Activate new program by filename (open file and inflate run-time data)
NXT_STATUS cCmdActivateProgram(UBYTE * pFileName);

//Deactivate currently active program (re-init run-time data and close file)
void cCmdDeactivateProgram();

//Reset various device state variables
void cCmdResetDevices(void);

//Parse activation record file header information
typedef struct
{
  UWORD DSTOC;
  UWORD DSDefaults;
  UWORD DSDefaultsSize;
  UWORD DynamicDefaults;
  UWORD DynamicDefaultsSize;
  UWORD Clumps;
  UWORD Codespace;
} PROG_FILE_OFFSETS;

NXT_STATUS cCmdReadFileHeader(UBYTE* pData, ULONG DataSize,
            PROG_FILE_OFFSETS* pFileOffsets);

NXT_STATUS cCmdInflateDSDefaults(UBYTE* pDSDefaults, UWORD *pDefaultsOffset, DS_ELEMENT_ID DSElementID);


//
//Clump management
//

//Clump queuing
void cCmdEnQClump(CLUMP_Q * Queue, CLUMP_ID NewClump);
void cCmdDeQClump(CLUMP_Q * Queue, CLUMP_ID Clump);
void cCmdRotateQ();
UBYTE cCmdIsClumpOnQ(CLUMP_Q * Queue, CLUMP_ID Clump);
UBYTE cCmdIsQSane(CLUMP_Q * Queue);

// Rest queue functions
NXT_STATUS cCmdSleepClump(ULONG time);
UBYTE cCmdCheckRestQ(ULONG currTime);

//Mutex queuing
NXT_STATUS cCmdAcquireMutex(MUTEX_Q * Mutex);
NXT_STATUS cCmdReleaseMutex(MUTEX_Q * Mutex);

//Conditionally schedule dependents of given clump (Begin and End specify subset of list)
NXT_STATUS cCmdSchedDependents(CLUMP_ID Clump, SWORD Begin, SWORD End);

//Conditionally schedule TargetClump
NXT_STATUS cCmdSchedDependent(CLUMP_ID Clump, CLUMP_ID TargetClump);

//Test if ClumpID is sane at run-time (valid for indexing AllClumps)
UBYTE cCmdIsClumpIDSane(CLUMP_ID Clump);

//
//Code stream management
//

//Instruction masking macros -- get the interesting bits out of an encoded instruction word
#define COMP_CODE(pInstr)   ((UBYTE)((((pInstr)[0]) & 0x0700) >> 8))
#define INSTR_SIZE(wd)      ((wd) >> 12) & 0x0F;

#define IS_SHORT_OP(pInstr)   ((UBYTE)((((pInstr)[0]) & 0x0800) >> 8) == 8)
#define SHORT_OP_CODE(pInstr) COMP_CODE(pInstr)
#define SHORT_ARG(pInstr)     ((SBYTE) (((pInstr)[0]) & 0x00FF))
//ShortOpMap defined in c_cmd_bytecodes.h
#define OP_CODE(pInstr)       (UBYTE) (((pInstr)[0]) & 0x00FF)

//
//Memory pool management
//

//Initialize entire memory pool with default value
void cCmdInitPool(void);

//Resize dataspace array specified by DSElementID and Offset.
NXT_STATUS cCmdDSArrayAlloc(DS_ELEMENT_ID DSElementID, UWORD Offset, UWORD NewCount);
//Resize dataspace array specified by DVIndex.  In most cases, call higher-level cCmdDSArrayAlloc instead.
NXT_STATUS cCmdDVArrayAlloc(DV_INDEX DVIndex, UWORD NewCount);

NXT_STATUS cCmdAllocSubArrayDopeVectors(DS_ELEMENT_ID DSElementID, UWORD Offset);
NXT_STATUS cCmdFreeSubArrayDopeVectors(DS_ELEMENT_ID DSElementID, UWORD Offset);
NXT_STATUS cCmdAllocDopeVector(DV_INDEX *pIndex, UWORD ElemSize);
NXT_STATUS cCmdFreeDopeVector(DV_INDEX DVIndex);
NXT_STATUS cCmdGrowDopeVectorArray(UWORD NewCount);

UWORD cCmdCalcArrayElemSize(DS_ELEMENT_ID DSElementID);

NXT_STATUS cCmdMemMgrMoveToTail(DV_INDEX DVIndex);
NXT_STATUS cCmdMemMgrInsertAtTail(DV_INDEX DVIndex);

//Utility function to check sanity of MemMgr data structure.  Boolean result.
UBYTE cCmdVerifyMemMgr();

NXT_STATUS cCmdDSCompact(void);

//
// Message Queue management
//

NXT_STATUS cCmdMessageWrite(UWORD QueueID, UBYTE * pData, UWORD Length);
NXT_STATUS cCmdMessageRead(UWORD QueueID, UBYTE * pData, UWORD Length, UBYTE Remove);
NXT_STATUS cCmdMessageGetSize(UWORD QueueID, UWORD * Size);

//
// Datalog Queue management
//

NXT_STATUS cCmdDatalogWrite(UBYTE * pData, UWORD Length);
NXT_STATUS cCmdDatalogRead(UBYTE * pData, UWORD Length, UBYTE Remove);
NXT_STATUS cCmdDatalogGetSize(UWORD * Size);

//
// Color Sensor
//

NXT_STATUS cCmdColorSensorRead (UBYTE Port, SWORD* SensorValue, UWORD* RawArray, UWORD* NormalizedArray,
								SWORD* ScaledArray, UBYTE* InvalidData);

//
//Dataspace management
//

#define IS_AGGREGATE_TYPE(TypeCode) ((TypeCode == TC_ARRAY) || (TypeCode == TC_CLUSTER))
// use carefully, only where tc will be a scalar int
#define QUICK_UNSIGNED_TEST(TypeCode) ((TypeCode) & 0x1)
#define IS_SIGNED_TYPE(TypeCode) (((TypeCode) == TC_SBYTE) || ((TypeCode) == TC_SWORD) || ((TypeCode) == TC_SLONG))
//!!!BDUGGAN add TC_FLOAT?

//Test if DS_ELEMENT_ID is sane at run-time (valid for indexing DS TOC)
UBYTE cCmdIsDSElementIDSane(DS_ELEMENT_ID Index);

DS_ELEMENT_ID cCmdGetDataspaceCount(void);

//Pointer accessors to resolve actual data locations in RAM
void* cCmdDSPtr(DS_ELEMENT_ID DSElementID, UWORD Offset);
void* cCmdDVPtr(DV_INDEX DVIndex);

//Helper to walk the DSTOC to the next entry at the same aggregate nesting level as CurrID
DS_ELEMENT_ID cCmdNextDSElement(DS_ELEMENT_ID CurrID);

//Recursively compare two complete data type descriptors
UBYTE cCmdCompareDSType(DS_ELEMENT_ID DSElementID1, DS_ELEMENT_ID DSElementID2);

//Functions for managing data flattened to byte arrays
UWORD cCmdCalcFlattenedSize(DS_ELEMENT_ID DSElementID, UWORD Offset);
NXT_STATUS cCmdFlattenToByteArray(UBYTE * pByteArray, UWORD * pByteOffset, DS_ELEMENT_ID DSElementID, UWORD Offset);
NXT_STATUS cCmdUnflattenFromByteArray(UBYTE * pByteArray, UWORD * pByteOffset, DS_ELEMENT_ID DSElementID, UWORD Offset);

//Comparison evaluation.  Comparison codes defined in c_cmd_bytecodes.h.
//cCmdCompare operates on scalars passed as ULONGs -- type-specific comparisons done inside function.
UBYTE cCmdCompare(UBYTE CompCode, ULONG Val1, ULONG Val2, TYPE_CODE TypeCode1, TYPE_CODE TypeCode2);
UBYTE cCmdCompareFlt(UBYTE CompCode, float Val1, float Val2, TYPE_CODE TypeCode1, TYPE_CODE TypeCode2);
//cCmdCompareAggregates does polymorphic comparisons (with recursive helper function).
NXT_STATUS cCmdCompareAggregates(UBYTE CompCode, UBYTE *ReturnBool, DATA_ARG Arg2, UWORD Offset2, DATA_ARG Arg3, UWORD Offset3);
NXT_STATUS cCmdRecursiveCompareAggregates(UBYTE CompCode, UBYTE *ReturnBool, UBYTE *Finished, DATA_ARG Arg2, UWORD Offset2, DATA_ARG Arg3, UWORD Offset3);

//Cluster functions
UWORD cCmdClusterCount(DS_ELEMENT_ID DSElementID);

//Array functions
#define ARRAY_ELEM_OFFSET(DVIndex, Index) ((UWORD)(DV_ARRAY[(DVIndex)].Offset + DV_ARRAY[(DVIndex)].ElemSize * (Index)))
UWORD cCmdGetDVIndex(DS_ELEMENT_ID DSElementID, UWORD Offset);
UWORD cCmdArrayCount(DS_ELEMENT_ID DSElementID, UWORD Offset);
TYPE_CODE cCmdArrayType(DS_ELEMENT_ID DSElementID);

//!!! DATA_ARG masks are for internal use only! (Bytecode programs should never contain them)
//    See cCmdResolveDataArg() calls in the interpreter code for OP_GETOUT, OP_SETIN, and OP_GETIN.
#define DATA_ARG_ADDR_MASK 0x3FFF
#define DATA_ARG_IMM_MASK 0x7FFF

//General data accessors (DS and IO Map)
void * cCmdResolveDataArg(DATA_ARG DataArg, UWORD Offset, TYPE_CODE * TypeCode);
void * cCmdResolveIODataArg(DATA_ARG DataArg, ULONG Offset, TYPE_CODE * TypeCode);
ULONG cCmdGetVal(void * pVal, TYPE_CODE TypeCode);
void cCmdSetVal(void * pVal, TYPE_CODE TypeCode, ULONG NewVal);

// Calibration routines
void cCmdLoadCalibrationFiles(void);
NXT_STATUS cCmdComputeCalibratedValue(UBYTE *nm, SWORD *raw);
void cCmdUpdateCalibrationCache(UBYTE *nm, SWORD min, SWORD max);

//
//Interpreter functions
//

//Clump-based "master" interpreter
NXT_STATUS cCmdInterpFromClump();

//Function pointer typedef for sub-interpreters
typedef NXT_STATUS (*pInterp)(CODE_WORD * const);
typedef NXT_STATUS (*pInterpShort)(CODE_WORD * const);

//Sub-interpreter dispatch functions
NXT_STATUS cCmdInterpNoArg(CODE_WORD * const pCode);
NXT_STATUS cCmdInterpUnop1(CODE_WORD * const pCode);
NXT_STATUS cCmdInterpUnop2(CODE_WORD * const pCode);
NXT_STATUS cCmdInterpScalarUnop2(CODE_WORD * const pCode);
NXT_STATUS cCmdInterpBinop(CODE_WORD * const pCode);
NXT_STATUS cCmdInterpScalarBinop(CODE_WORD * const pCode);
NXT_STATUS cCmdInterpOther(CODE_WORD * const pCode);

NXT_STATUS cCmdInterpShortError(CODE_WORD * const pCode);
NXT_STATUS cCmdInterpShortSubCall(CODE_WORD * const pCode);
NXT_STATUS cCmdInterpShortMove(CODE_WORD * const pCode);
NXT_STATUS cCmdInterpShortAcquire(CODE_WORD * const pCode);
NXT_STATUS cCmdInterpShortRelease(CODE_WORD * const pCode);

NXT_STATUS cCmdMove(DATA_ARG Arg1, DATA_ARG Arg2);

//Polymorphic interpreter functions
NXT_STATUS cCmdInterpPolyUnop2(CODE_WORD const Code, DATA_ARG Arg1, UWORD Offset1, DATA_ARG Arg2, UWORD Offset2);
ULONG cCmdUnop2(CODE_WORD const Code, ULONG Operand, TYPE_CODE TypeCode);
float cCmdUnop2Flt(CODE_WORD const Code, float Operand, TYPE_CODE TypeCode);

NXT_STATUS cCmdInterpPolyBinop(CODE_WORD const Code, DATA_ARG Arg1, UWORD Offset1, DATA_ARG Arg2, UWORD Offset2, DATA_ARG Arg3, UWORD Offset3);
ULONG cCmdBinop(CODE_WORD const Code, ULONG LeftOp, ULONG RightOp, TYPE_CODE LeftType, TYPE_CODE RightType);
float cCmdBinopFlt(CODE_WORD const Code, float LeftOp, float RightOp, TYPE_CODE LeftType, TYPE_CODE RightType);
void cCmdSetValFlt(void * pVal, TYPE_CODE TypeCode, float NewVal);
float cCmdGetValFlt(void * pVal, TYPE_CODE TypeCode);
//
//Support functions for lowspeed (I2C devices, i.e. ultrasonic sensor) communications
//

NXT_STATUS cCmdLSCheckStatus(UBYTE Port);
UBYTE cCmdLSCalcBytesReady(UBYTE Port);
NXT_STATUS cCmdLSWrite(UBYTE Port, UBYTE BufLength, UBYTE *pBuf, UBYTE ResponseLength);
NXT_STATUS cCmdLSRead(UBYTE Port, UBYTE BufLength, UBYTE * pBuf);

//
//Support for OP_SYSCALL
//

//
//Each cCmdWrap<SysCallName> funtion below implements one system call.
//The OP_SYSCALL interpreter wrangles the argument vector, ArgV,
// then calls the appropriate wrapper function according to the SysCallID.
//Wrapper functions write directly back into the dataspace via ArgV.
//
#define MAX_CALL_ARGS 16

typedef NXT_STATUS (*pSysCall)(UBYTE * ArgV[]);

NXT_STATUS cCmdWrapFileOpenRead(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapFileOpenWrite(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapFileOpenAppend(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapFileRead(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapFileWrite(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapFileClose(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapFileResolveHandle (UBYTE * ArgV[]);
NXT_STATUS cCmdWrapFileRename (UBYTE * ArgV[]);
NXT_STATUS cCmdWrapFileDelete (UBYTE * ArgV[]);
NXT_STATUS cCmdWrapSoundPlayFile(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapSoundPlayTone(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapSoundGetState(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapSoundSetState(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapDrawText(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapDrawPoint(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapDrawLine(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapDrawCircle(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapDrawRect(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapDrawPicture(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapSetScreenMode(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapReadButton(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapCommLSWrite(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapCommLSRead(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapCommLSCheckStatus(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapRandomNumber(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapGetStartTick(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapMessageWrite(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapMessageRead(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapDatalogWrite(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapCommBTCheckStatus(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapCommBTWrite(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapCommBTRead(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapKeepAlive(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapIOMapRead(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapIOMapWrite(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapColorSensorRead (UBYTE * ArgV[]);
NXT_STATUS cCmdWrapDatalogGetTimes(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapSetSleepTimeout(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapListFiles(UBYTE * ArgV[]);

// Handlers for dynamically added syscalls
NXT_STATUS cCmdWrapCommHSWrite(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapCommHSRead(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapCommHSCheckStatus(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapCommBTOnOff(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapCommBTConnection(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapReadSemData(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapWriteSemData(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapUpdateCalibCacheInfo(UBYTE * ArgV[]);
NXT_STATUS cCmdWrapComputeCalibValue(UBYTE * ArgV[]);


//Handler for remote control protocol packets -- called from comm module via IO map function pointer
UWORD cCmdHandleRemoteCommands(UBYTE * pInBuf, UBYTE * pOutBuf, UBYTE * pLen);

#ifdef SIM_NXT
//
// Helper functions to provide simulator library access to VM internals
//
SWORD cCmdGetCodeWord(CLUMP_ID Clump, CODE_INDEX Index);
UBYTE * cCmdGetDataspace(UWORD *DataspaceSize);
DOPE_VECTOR * cCmdGetDopeVectorPtr(void);
ULONG cCmdGetPoolSize(void);
MEM_MGR cCmdGetMemMgr(void);
#endif

#else //!ENABLE_VM

//Placeholder VARSCMD for alternate implementation (see bottom of c_cmd.c for usage notes)
typedef struct
{
  UBYTE         Tmp;
} VARSCMD;

#endif //ENABLE_VM

#endif //C_CMD