aboutsummaryrefslogtreecommitdiff
path: root/AT91SAM7S256/Source/c_cmd.h
blob: 04f10a3274b0ea25866275e4f794132d3d562059 (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
//
// Date init       14.12.2004
//
// Revision date   $Date:: 24-07-06 8:52                                     $
//
// Filename        $Workfile:: c_cmd.h                                       $
//
// Version         $Revision:: 43                                            $
//
// Archive         $Archive:: /LMS2006/Sys01/Main/Firmware/Source/c_cmd.h    $
//
// 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 34

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

//
//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;

//dTimerRead() constantly returns latest system MS tick, so empty loop is convenient macro
#define BUSY_WAIT_NEXT_MS while (IOMapCmd.Tick == dTimerRead())

#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,

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

  //Mutex tracks current holder and any waiting clumps
  TC_MUTEX
};

//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

//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
};

#define IO_OUT_FPP 15
#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.
//!!! BackPtr is an unused field.  Intended to enable compaction of DopeVectorArray.
typedef struct
{
  UWORD Offset;
  UWORD ElemSize;
  UWORD Count;
  UWORD BackPtr;
  DV_INDEX Link;
} 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 5
//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))


//
//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

//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
// CodeEnd: End of this clump's bytecodes
// 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.
//
// Priority: number of instructions to run per pass on this clump
//
// pDependents: pointer to list of downstream dependents' ClumpIDs
// DependentCount: Count of downstream dependents
//
typedef struct
{
  CODE_INDEX  CodeStart;
  CODE_INDEX  CodeEnd;
  CODE_INDEX PC;
  UBYTE     InitFireCount;
  UBYTE     CurrFireCount; //AKA ShortCount
  CLUMP_ID  Link;

  UBYTE     Priority;

  CLUMP_ID* pDependents;
  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)
//ScratchPC: Temp PC value for control flow instructions
//
//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;
  CODE_INDEX ScratchPC;
  CLUMP_ID   CallerClump;

  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;

#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(CLUMP_Q * Queue);
UBYTE cCmdIsClumpOnQ(CLUMP_Q * Queue, CLUMP_ID Clump);
UBYTE cCmdIsQSane(CLUMP_Q * Queue);

//Mutex queuing
NXT_STATUS cCmdAcquireMutex(MUTEX_Q * Mutex, CLUMP_ID Clump);
NXT_STATUS cCmdReleaseMutex(MUTEX_Q * Mutex, CLUMP_ID Clump);

//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(pInstr)  ((UBYTE)((((pInstr)[0]) & 0xF000) >> 12))

#ifdef USE_SHORT_OPS
//!!! IS_SHORT_OP and SHORT_OP_CODE do not check for insane (out of bounds) data. Accessor function would be safer.
#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)       (IS_SHORT_OP(pInstr) ? ShortOpMap[SHORT_OP_CODE(pInstr)] : (UBYTE) (((pInstr)[0]) & 0x00FF))
#else
#define OP_CODE(pInstr)       ((UBYTE) (((pInstr)[0]) & 0x00FF))
#endif

//Access count of codewords belonging to Clump. If no clump specified, return count of all codewords in program.
CODE_INDEX cCmdGetCodespaceCount(CLUMP_ID Clump);

//
//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, UWORD BackPtr);
NXT_STATUS cCmdFreeDopeVector(DV_INDEX DVIndex);
NXT_STATUS cCmdGrowDopeVectorArray(UWORD NewCount);
NXT_STATUS cCmdCompactDopeVectorArray(void);

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);

//
//Dataspace management
//

#define IS_AGGREGATE_TYPE(TypeCode) ((TypeCode == TC_ARRAY) || (TypeCode == TC_CLUSTER))
#define IS_SIGNED_TYPE(TypeCode) (((TypeCode) == TC_SBYTE) || ((TypeCode) == TC_SWORD) || ((TypeCode) == TC_SLONG))

//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);
TYPE_CODE cCmdDSType(DS_ELEMENT_ID DSElementID);

//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);
//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);
ULONG cCmdGetVal(void * pVal, TYPE_CODE TypeCode);
void cCmdSetVal(void * pVal, TYPE_CODE TypeCode, ULONG NewVal);
UWORD cCmdSizeOf(TYPE_CODE TypeCode);

//
//Interpreter functions
//

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

//Function pointer typedef for sub-interpreters
typedef NXT_STATUS (*pInterp)(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 cCmdInterpBinop(CODE_WORD * const pCode);
NXT_STATUS cCmdInterpOther(CODE_WORD * const pCode);

#define INTERP_COUNT 5

//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);

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);

//
//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 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[]);

//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