source: sandbox/JamPlayerUSB/jbistub.c@ 109

Last change on this file since 109 was 109, checked in by demin, 14 years ago

First working version

File size: 20.5 KB
Line 
1/****************************************************************************/
2/* */
3/* Module: jbistub.c */
4/* */
5/* Copyright (C) Altera Corporation 1997-2001 */
6/* */
7/* Description: Jam STAPL ByteCode Player main source file */
8/* */
9/* Supports Altera ByteBlaster hardware download cable */
10/* on Windows 95 and Windows NT operating systems. */
11/* (A device driver is required for Windows NT.) */
12/* */
13/* Also supports BitBlaster hardware download cable on */
14/* Windows 95, Windows NT, and UNIX platforms. */
15/* */
16/* Revisions: 1.1 fixed control port initialization for ByteBlaster */
17/* 2.0 added support for STAPL bytecode format, added code */
18/* to get printer port address from Windows registry */
19/* 2.1 improved messages, fixed delay-calibration bug in */
20/* 16-bit DOS port, added support for "alternative */
21/* cable X", added option to control whether to reset */
22/* the TAP after execution, moved porting macros into */
23/* jbiport.h */
24/* 2.2 added support for static memory */
25/* fixed /W4 warnings */
26/* */
27/****************************************************************************/
28
29#include <windows.h>
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <io.h>
35#include <fcntl.h>
36#include <process.h>
37#include <malloc.h>
38#define POINTER_ALIGNMENT sizeof(BYTE)
39#include <time.h>
40#include <conio.h>
41#include <ctype.h>
42#include <sys/types.h>
43#include <sys/stat.h>
44
45#include "jbiexprt.h"
46
47/************************************************************************
48*
49* Global variables
50*/
51
52unsigned char *file_buffer = NULL;
53long file_pointer = 0L;
54long file_length = 0L;
55
56/* delay count for one millisecond delay */
57long one_ms_delay = 0L;
58
59BOOL jtag_hardware_initialized = FALSE;
60
61/* function prototypes to allow forward reference */
62extern void delay_loop(long count);
63
64BOOL verbose = FALSE;
65
66/******************************************************************/
67
68#include "ftd2xx.h"
69
70static FT_HANDLE ftdih = NULL;
71
72BYTE usb_buf[64];
73int usb_buf_pos;
74
75/******************************************************************/
76
77int usb_blaster_buf_write(BYTE *buf, int size, DWORD* bytes_written)
78{
79 FT_STATUS status;
80 DWORD dw_bytes_written;
81 if((size == 1) && (buf[0]&0x01)) printf("(tms tdi) -> (%d %d)\n", (buf[0]&0x02) >> 1, (buf[0]&0x10) >> 4);
82 if ((status = FT_Write(ftdih, buf, size, &dw_bytes_written)) != FT_OK)
83 {
84 *bytes_written = dw_bytes_written;
85 printf("FT_Write returned: %ld\n", status);
86 return 0;
87 }
88 else
89 {
90 *bytes_written = dw_bytes_written;
91 return 1;
92 }
93}
94
95/******************************************************************/
96
97int usb_blaster_buf_read(BYTE* buf, int size, DWORD* bytes_read)
98{
99 DWORD dw_bytes_read;
100 FT_STATUS status;
101 if ((status = FT_Read(ftdih, buf, size, &dw_bytes_read)) != FT_OK)
102 {
103 *bytes_read = dw_bytes_read;
104 printf("FT_Read returned: %ld", status);
105 return 0;
106 }
107 *bytes_read = dw_bytes_read;
108 return 1;
109}
110
111/******************************************************************/
112
113int usb_blaster_init()
114{
115 BYTE latency_timer;
116
117 FT_STATUS status;
118
119 usb_buf_pos = 0;
120
121 if ((status = FT_OpenEx("USB-Blaster", FT_OPEN_BY_DESCRIPTION, &ftdih)) != FT_OK)
122 {
123 printf("unable to open ftdi device: %ld\n", status);
124 return 0;
125 }
126
127 if ((status = FT_SetLatencyTimer(ftdih, 2)) != FT_OK)
128 {
129 printf("unable to set latency timer: %ld\n", status);
130 return 0;
131 }
132
133 if ((status = FT_GetLatencyTimer(ftdih, &latency_timer)) != FT_OK)
134 {
135 printf("unable to get latency timer: %ld\n", status);
136 return 0;
137 }
138 else
139 {
140 printf("current latency timer: %d\n", latency_timer);
141 }
142
143 if ((status = FT_SetBitMode(ftdih, 0x00, 0)) != FT_OK)
144 {
145 printf("unable to disable bit i/o mode: %ld\n", status);
146 return 0;
147 }
148
149 return 1;
150}
151
152/******************************************************************/
153
154int usb_blaster_quit()
155{
156 FT_STATUS status;
157
158 status = FT_Close(ftdih);
159
160 return 1;
161}
162
163/******************************************************************/
164
165void usb_blaster_buf_flush()
166{
167 DWORD cnt;
168 int len;
169
170 if(usb_buf_pos > 0)
171 {
172 len = usb_buf_pos >> 3;
173 usb_buf[0] = len | 0x80;
174
175 usb_blaster_buf_write(usb_buf, len + 1, &cnt);
176 usb_buf_pos = 0;
177 }
178}
179
180/******************************************************************/
181
182void usb_blaster_buf_fill(int tdi)
183{
184 int index, shift;
185
186 if (usb_buf_pos >= 504) usb_blaster_buf_flush();
187
188 index = (usb_buf_pos >> 3) + 1;
189 shift = (usb_buf_pos & 7);
190
191 if (shift == 0) usb_buf[index] = 0;
192
193 usb_buf[index] |= ((tdi&0x01) << shift);
194
195 ++usb_buf_pos;
196}
197
198/************************************************************************
199*
200* Customized interface functions for Jam STAPL ByteCode Player I/O:
201*
202* jbi_jtag_io()
203* jbi_message()
204* jbi_delay()
205*/
206
207int jbi_jtag_io(int tms, int tdi, int read_tdo, int flag)
208{
209 BYTE buf[2];
210 DWORD count;
211
212 int data = 0;
213 int tdo = 0;
214
215 if (!jtag_hardware_initialized)
216 {
217 usb_blaster_init();
218 jtag_hardware_initialized = TRUE;
219 }
220
221 if (flag == 0 || read_tdo)
222 {
223/*
224 printf("(tms tdi read flag) -> (%d %d %d %d)\n", tms, tdi, read_tdo, flag);
225 fflush(stdout);
226*/
227 usb_blaster_buf_flush();
228
229 data = (tdi ? 0x10 : 0) | (tms ? 0x02 : 0);
230
231 buf[0] = data | 0x0C;
232 buf[1] = data | 0x01 | 0x0C | (read_tdo ? 0x40 : 0);
233 usb_blaster_buf_write(buf, 2, &count);
234
235 if (read_tdo)
236 {
237 usb_blaster_buf_read(buf, 1, &count);
238 tdo = buf[0]&0x01;
239/*
240 printf("(tdo) -> (%d)\n", tdo);
241 fflush(stdout);
242*/
243 }
244 }
245 else
246 {
247 usb_blaster_buf_fill(tdi ? 1 : 0);
248 }
249
250 return (tdo);
251}
252
253void jbi_message(char *message_text)
254{
255 puts(message_text);
256 fflush(stdout);
257}
258
259void jbi_export_integer(char *key, long value)
260{
261 if (verbose)
262 {
263 printf("Export: key = \"%s\", value = %ld\n", key, value);
264 fflush(stdout);
265 }
266}
267
268#define HEX_LINE_CHARS 72
269#define HEX_LINE_BITS (HEX_LINE_CHARS * 4)
270
271char conv_to_hex(unsigned long value)
272{
273 char c;
274
275 if (value > 9)
276 {
277 c = (char) (value + ('A' - 10));
278 }
279 else
280 {
281 c = (char) (value + '0');
282 }
283
284 return (c);
285}
286
287void jbi_export_boolean_array(char *key, unsigned char *data, long count)
288{
289 char string[HEX_LINE_CHARS + 1];
290 long i, offset;
291 unsigned long size, line, lines, linebits, value, j, k;
292
293 if (verbose)
294 {
295 if (count > HEX_LINE_BITS)
296 {
297 printf("Export: key = \"%s\", %ld bits, value = HEX\n", key, count);
298 lines = (count + (HEX_LINE_BITS - 1)) / HEX_LINE_BITS;
299
300 for (line = 0; line < lines; ++line)
301 {
302 if (line < (lines - 1))
303 {
304 linebits = HEX_LINE_BITS;
305 size = HEX_LINE_CHARS;
306 offset = count - ((line + 1) * HEX_LINE_BITS);
307 }
308 else
309 {
310 linebits = count - ((lines - 1) * HEX_LINE_BITS);
311 size = (linebits + 3) / 4;
312 offset = 0L;
313 }
314
315 string[size] = '\0';
316 j = size - 1;
317 value = 0;
318
319 for (k = 0; k < linebits; ++k)
320 {
321 i = k + offset;
322 if (data[i >> 3] & (1 << (i & 7))) value |= (1 << (i & 3));
323 if ((i & 3) == 3)
324 {
325 string[j] = conv_to_hex(value);
326 value = 0;
327 --j;
328 }
329 }
330 if ((k & 3) > 0) string[j] = conv_to_hex(value);
331
332 printf("%s\n", string);
333 }
334
335 fflush(stdout);
336 }
337 else
338 {
339 size = (count + 3) / 4;
340 string[size] = '\0';
341 j = size - 1;
342 value = 0;
343
344 for (i = 0; i < count; ++i)
345 {
346 if (data[i >> 3] & (1 << (i & 7))) value |= (1 << (i & 3));
347 if ((i & 3) == 3)
348 {
349 string[j] = conv_to_hex(value);
350 value = 0;
351 --j;
352 }
353 }
354 if ((i & 3) > 0) string[j] = conv_to_hex(value);
355
356 printf("Export: key = \"%s\", %ld bits, value = HEX %s\n",
357 key, count, string);
358 fflush(stdout);
359 }
360 }
361}
362
363void jbi_delay(long microseconds)
364{
365 delay_loop(microseconds *
366 ((one_ms_delay / 1000L) + ((one_ms_delay % 1000L) ? 1 : 0)));
367}
368
369void *jbi_malloc(unsigned int size)
370{
371 unsigned int n_bytes_to_allocate =
372 (POINTER_ALIGNMENT * ((size + POINTER_ALIGNMENT - 1) / POINTER_ALIGNMENT));
373
374 unsigned char *ptr = 0;
375
376 ptr = (unsigned char *) malloc(n_bytes_to_allocate);
377
378 return ptr;
379}
380
381void jbi_free(void *ptr)
382{
383 if (ptr != 0)
384 {
385 free(ptr);
386 }
387}
388
389void calibrate_delay(void)
390{
391 one_ms_delay = 0L;
392}
393
394char *error_text[] =
395{
396/* JBIC_SUCCESS 0 */ "success",
397/* JBIC_OUT_OF_MEMORY 1 */ "out of memory",
398/* JBIC_IO_ERROR 2 */ "file access error",
399/* JAMC_SYNTAX_ERROR 3 */ "syntax error",
400/* JBIC_UNEXPECTED_END 4 */ "unexpected end of file",
401/* JBIC_UNDEFINED_SYMBOL 5 */ "undefined symbol",
402/* JAMC_REDEFINED_SYMBOL 6 */ "redefined symbol",
403/* JBIC_INTEGER_OVERFLOW 7 */ "integer overflow",
404/* JBIC_DIVIDE_BY_ZERO 8 */ "divide by zero",
405/* JBIC_CRC_ERROR 9 */ "CRC mismatch",
406/* JBIC_INTERNAL_ERROR 10 */ "internal error",
407/* JBIC_BOUNDS_ERROR 11 */ "bounds error",
408/* JAMC_TYPE_MISMATCH 12 */ "type mismatch",
409/* JAMC_ASSIGN_TO_CONST 13 */ "assignment to constant",
410/* JAMC_NEXT_UNEXPECTED 14 */ "NEXT unexpected",
411/* JAMC_POP_UNEXPECTED 15 */ "POP unexpected",
412/* JAMC_RETURN_UNEXPECTED 16 */ "RETURN unexpected",
413/* JAMC_ILLEGAL_SYMBOL 17 */ "illegal symbol name",
414/* JBIC_VECTOR_MAP_FAILED 18 */ "vector signal name not found",
415/* JBIC_USER_ABORT 19 */ "execution cancelled",
416/* JBIC_STACK_OVERFLOW 20 */ "stack overflow",
417/* JBIC_ILLEGAL_OPCODE 21 */ "illegal instruction code",
418/* JAMC_PHASE_ERROR 22 */ "phase error",
419/* JAMC_SCOPE_ERROR 23 */ "scope error",
420/* JBIC_ACTION_NOT_FOUND 24 */ "action not found",
421};
422
423#define MAX_ERROR_CODE (int)((sizeof(error_text)/sizeof(error_text[0]))+1)
424
425/************************************************************************/
426
427int main(int argc, char **argv)
428{
429 BOOL help = FALSE;
430 BOOL error = FALSE;
431 char *filename = NULL;
432 long offset = 0L;
433 long error_address = 0L;
434 JBI_RETURN_TYPE crc_result = JBIC_SUCCESS;
435 JBI_RETURN_TYPE exec_result = JBIC_SUCCESS;
436 unsigned short expected_crc = 0;
437 unsigned short actual_crc = 0;
438 char key[33] = {0};
439 char value[257] = {0};
440 int exit_status = 0;
441 int arg = 0;
442 int exit_code = 0;
443 int format_version = 0;
444 time_t start_time = 0;
445 time_t end_time = 0;
446 int time_delta = 0;
447 char *workspace = NULL;
448 char *action = NULL;
449 char *init_list[10];
450 int init_count = 0;
451 FILE *fp = NULL;
452 struct stat sbuf;
453 long workspace_size = 0;
454 char *exit_string = NULL;
455 int reset_jtag = 1;
456 int execute_program = 1;
457 int action_count = 0;
458 int procedure_count = 0;
459 int index = 0;
460 char *action_name = NULL;
461 char *description = NULL;
462 JBI_PROCINFO *procedure_list = NULL;
463 JBI_PROCINFO *procptr = NULL;
464
465 verbose = FALSE;
466
467 init_list[0] = NULL;
468
469 /* print out the version string and copyright message */
470 fprintf(stderr, "Jam STAPL ByteCode Player Version 2.2\nCopyright (C) 1998-2001 Altera Corporation\n\n");
471
472 for (arg = 1; arg < argc; arg++)
473 {
474 if (argv[arg][0] == '-')
475 {
476 switch(toupper(argv[arg][1]))
477 {
478 case 'A': /* set action name */
479 if (action == NULL)
480 {
481 action = &argv[arg][2];
482 }
483 else
484 {
485 error = TRUE;
486 }
487 break;
488
489 case 'D': /* initialization list */
490 if (argv[arg][2] == '"')
491 {
492 init_list[init_count] = &argv[arg][3];
493 }
494 else
495 {
496 init_list[init_count] = &argv[arg][2];
497 }
498 init_list[++init_count] = NULL;
499 break;
500
501 case 'R': /* don't reset the JTAG chain after use */
502 reset_jtag = 0;
503 break;
504
505 case 'M': /* set memory size */
506 if (sscanf(&argv[arg][2], "%ld", &workspace_size) != 1)
507 error = TRUE;
508 if (workspace_size == 0) error = TRUE;
509 break;
510
511 case 'H': /* help */
512 help = TRUE;
513 break;
514
515 case 'V': /* verbose */
516 verbose = TRUE;
517 break;
518
519 case 'I': /* show info only, do not execute */
520 verbose = TRUE;
521 execute_program = 0;
522 break;
523
524 default:
525 error = TRUE;
526 break;
527 }
528 }
529 else
530 {
531 /* it's a filename */
532 if (filename == NULL)
533 {
534 filename = argv[arg];
535 }
536 else
537 {
538 /* error -- we already found a filename */
539 error = TRUE;
540 }
541 }
542
543 if (error)
544 {
545 fprintf(stderr, "Illegal argument: \"%s\"\n", argv[arg]);
546 help = TRUE;
547 error = FALSE;
548 }
549 }
550
551 if (help || (filename == NULL))
552 {
553 fprintf(stderr, "Usage: jbi [options] <filename>\n");
554 fprintf(stderr, "\nAvailable options:\n");
555 fprintf(stderr, " -h : show help message\n");
556 fprintf(stderr, " -v : show verbose messages\n");
557 fprintf(stderr, " -i : show file info only - does not execute any action\n");
558 fprintf(stderr, " -a<action> : specify an action name (Jam STAPL)\n");
559 fprintf(stderr, " -d<var=val> : initialize variable to specified value (Jam 1.1)\n");
560 fprintf(stderr, " -d<proc=1> : enable optional procedure (Jam STAPL)\n");
561 fprintf(stderr, " -d<proc=0> : disable recommended procedure (Jam STAPL)\n");
562 fprintf(stderr, " -r : don't reset JTAG TAP after use\n");
563 exit_status = 1;
564 }
565 else if ((workspace_size > 0) &&
566 ((workspace = (char *) jbi_malloc((size_t) workspace_size)) == NULL))
567 {
568 fprintf(stderr, "Error: can't allocate memory (%d Kbytes)\n",
569 (int) (workspace_size / 1024L));
570 exit_status = 1;
571 }
572 else if (access(filename, 0) != 0)
573 {
574 fprintf(stderr, "Error: can't access file \"%s\"\n", filename);
575 exit_status = 1;
576 }
577 else
578 {
579 /* get length of file */
580 if (stat(filename, &sbuf) == 0) file_length = sbuf.st_size;
581
582 if ((fp = fopen(filename, "rb")) == NULL)
583 {
584 fprintf(stderr, "Error: can't open file \"%s\"\n", filename);
585 exit_status = 1;
586 }
587 else
588 {
589 /*
590 * Read entire file into a buffer
591 */
592 file_buffer = (unsigned char *) jbi_malloc((size_t) file_length);
593
594 if (file_buffer == NULL)
595 {
596 fprintf(stderr, "Error: can't allocate memory (%d Kbytes)\n",
597 (int) (file_length / 1024L));
598 exit_status = 1;
599 }
600 else
601 {
602 if (fread(file_buffer, 1, (size_t) file_length, fp) !=
603 (size_t) file_length)
604 {
605 fprintf(stderr, "Error reading file \"%s\"\n", filename);
606 exit_status = 1;
607 }
608 }
609
610 fclose(fp);
611 }
612
613 if (exit_status == 0)
614 {
615 /*
616 * Calibrate the delay loop function
617 */
618 calibrate_delay();
619
620 /*
621 * Check CRC
622 */
623 crc_result = jbi_check_crc(file_buffer, file_length,
624 &expected_crc, &actual_crc);
625
626 if (verbose || (crc_result == JBIC_CRC_ERROR))
627 {
628 switch (crc_result)
629 {
630 case JBIC_SUCCESS:
631 printf("CRC matched: CRC value = %04X\n", actual_crc);
632 break;
633
634 case JBIC_CRC_ERROR:
635 printf("CRC mismatch: expected %04X, actual %04X\n",
636 expected_crc, actual_crc);
637 break;
638
639 case JBIC_UNEXPECTED_END:
640 printf("Expected CRC not found, actual CRC value = %04X\n",
641 actual_crc);
642 break;
643
644 case JBIC_IO_ERROR:
645 printf("Error: File format is not recognized.\n");
646 exit(1);
647 break;
648
649 default:
650 printf("CRC function returned error code %d\n", crc_result);
651 break;
652 }
653 }
654
655 if (verbose)
656 {
657 /*
658 * Display file format version
659 */
660 jbi_get_file_info(file_buffer, file_length,
661 &format_version, &action_count, &procedure_count);
662
663 printf("File format is %s ByteCode format\n",
664 (format_version == 2) ? "Jam STAPL" : "pre-standardized Jam 1.1");
665
666 /*
667 * Dump out NOTE fields
668 */
669 while (jbi_get_note(file_buffer, file_length,
670 &offset, key, value, 256) == 0)
671 {
672 printf("NOTE \"%s\" = \"%s\"\n", key, value);
673 }
674
675 /*
676 * Dump the action table
677 */
678 if ((format_version == 2) && (action_count > 0))
679 {
680 printf("\nActions available in this file:\n");
681
682 for (index = 0; index < action_count; ++index)
683 {
684 jbi_get_action_info(file_buffer, file_length,
685 index, &action_name, &description, &procedure_list);
686
687 if (description == NULL)
688 {
689 printf("%s\n", action_name);
690 }
691 else
692 {
693 printf("%s \"%s\"\n", action_name, description);
694 }
695
696 procptr = procedure_list;
697 while (procptr != NULL)
698 {
699 if (procptr->attributes != 0)
700 {
701 printf(" %s (%s)\n", procptr->name,
702 (procptr->attributes == 1) ?
703 "optional" : "recommended");
704 }
705
706 procedure_list = procptr->next;
707 jbi_free(procptr);
708 procptr = procedure_list;
709 }
710 }
711
712 /* add a blank line before execution messages */
713 if (execute_program) printf("\n");
714 }
715 }
716
717 if (execute_program)
718 {
719 /*
720 * Execute the Jam STAPL ByteCode program
721 */
722 time(&start_time);
723 exec_result = jbi_execute(file_buffer, file_length, workspace,
724 workspace_size, action, init_list, reset_jtag,
725 &error_address, &exit_code, &format_version);
726 time(&end_time);
727
728 if (exec_result == JBIC_SUCCESS)
729 {
730 if (format_version == 2)
731 {
732 switch (exit_code)
733 {
734 case 0: exit_string = "Success"; break;
735 case 1: exit_string = "Checking chain failure"; break;
736 case 2: exit_string = "Reading IDCODE failure"; break;
737 case 3: exit_string = "Reading USERCODE failure"; break;
738 case 4: exit_string = "Reading UESCODE failure"; break;
739 case 5: exit_string = "Entering ISP failure"; break;
740 case 6: exit_string = "Unrecognized device"; break;
741 case 7: exit_string = "Device revision is not supported"; break;
742 case 8: exit_string = "Erase failure"; break;
743 case 9: exit_string = "Device is not blank"; break;
744 case 10: exit_string = "Device programming failure"; break;
745 case 11: exit_string = "Device verify failure"; break;
746 case 12: exit_string = "Read failure"; break;
747 case 13: exit_string = "Calculating checksum failure"; break;
748 case 14: exit_string = "Setting security bit failure"; break;
749 case 15: exit_string = "Querying security bit failure"; break;
750 case 16: exit_string = "Exiting ISP failure"; break;
751 case 17: exit_string = "Performing system test failure"; break;
752 default: exit_string = "Unknown exit code"; break;
753 }
754 }
755 else
756 {
757 switch (exit_code)
758 {
759 case 0: exit_string = "Success"; break;
760 case 1: exit_string = "Illegal initialization values"; break;
761 case 2: exit_string = "Unrecognized device"; break;
762 case 3: exit_string = "Device revision is not supported"; break;
763 case 4: exit_string = "Device programming failure"; break;
764 case 5: exit_string = "Device is not blank"; break;
765 case 6: exit_string = "Device verify failure"; break;
766 case 7: exit_string = "SRAM configuration failure"; break;
767 default: exit_string = "Unknown exit code"; break;
768 }
769 }
770
771 printf("Exit code = %d... %s\n", exit_code, exit_string);
772 }
773 else if ((format_version == 2) &&
774 (exec_result == JBIC_ACTION_NOT_FOUND))
775 {
776 if ((action == NULL) || (*action == '\0'))
777 {
778 printf("Error: no action specified for Jam STAPL file.\nProgram terminated.\n");
779 }
780 else
781 {
782 printf("Error: action \"%s\" is not supported for this Jam STAPL file.\nProgram terminated.\n", action);
783 }
784 }
785 else if (exec_result < MAX_ERROR_CODE)
786 {
787 printf("Error at address %ld: %s.\nProgram terminated.\n",
788 error_address, error_text[exec_result]);
789 }
790 else
791 {
792 printf("Unknown error code %d\n", exec_result);
793 }
794
795 /*
796 * Print out elapsed time
797 */
798 if (verbose)
799 {
800 time_delta = (int) (end_time - start_time);
801 printf("Elapsed time = %02u:%02u:%02u\n",
802 time_delta / 3600, /* hours */
803 (time_delta % 3600) / 60, /* minutes */
804 time_delta % 60); /* seconds */
805 }
806 }
807 }
808 }
809
810 if (jtag_hardware_initialized) usb_blaster_quit();
811
812 if (workspace != NULL) jbi_free(workspace);
813 if (file_buffer != NULL) jbi_free(file_buffer);
814
815 return (exit_status);
816}
817
818void delay_loop(long count)
819{
820 while (count != 0L) count--;
821}
Note: See TracBrowser for help on using the repository browser.