OrganProject

Check-in [05e07e911c]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Initial checkin - second attempt
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:05e07e911c072ad7f0d51a40e09d621d27a5cda4
User & Date: 200002852 2009-08-11 02:53:46
Context
2009-08-11
03:20
Added (partial) schematic for the display terminal check-in: c9bb81d031 user: kennykb tags: trunk
02:53
Initial checkin - second attempt check-in: 05e07e911c user: 200002852 tags: trunk
02:44
initial empty check-in check-in: 95e3867c00 user: kennykb tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added cardread/Makefile.





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
CC = gcc
CFLAGS = -O2 -I /d/Distributions/EZTWAIN1/VC -Wall
LIBS = /d/Distributions/EZTWAIN1/VC/RELEASE/EZTW32.lib

all: convcard.exe

clean:
	rm convcard.exe convcard.o

convcard.exe: convcard.o
	$(CC) $(CFLAGS) -o convcard.exe convcard.o $(LIBS)

convcard.o: convcard.c
	$(CC) $(CFLAGS) -c convcard.c

Added cardread/convcard.c.































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <limits.h>
#include <math.h>
#include <float.h>
#include <stdio.h>

/* Include the EZTwain Classic files from http://www.dosadi.com/eztwain1.html */

#include <eztwain.h>

/*
 * Geometry of a card
 */

#define CARD_WIDTH 7.375	/* Card width, in inches */
#define HOLE_HPITCH 0.087	/* Hole horizontal spacing, in inches */
#define COLUMNS 80		/* A card has 80 columns */
#define COL_1_X 0.2		/* X co-ordinate of the left edge of column 1 */

#define CARD_HEIGHT 3.25	/* Card height, in inches */
#define HOLE_HEIGHT 0.125	/* Hole height, in inches */
#define HOLE_VPITCH 0.250	/* Hole spacing, center to center, in inches */
#define ROW_12_Y 0.1875		/* Y co-ordinate of the top of row 12 */

/*
 * Representation of a single pixel in memory
 */

typedef struct pixel {
    unsigned char r;
    unsigned char g;
    unsigned char b;
} pixel;

/*
 * Approximate colors for the background and the card
 * The card is buff-colored paper, with light-blue paper behind it in the 
 * scanner.
 */

#define NCLASSES 3
pixel cardcolors[NCLASSES] = {
    { 200, 255, 255 },		/* Background */
    { 0,   0,   0  },		/* Dirt */
    { 255, 255, 200 }		/* Cardstock */
};

/*
 * Conversion table from column binary to ISO8859-1 - assumes an 029 keypunch
 */

const char cardCodes[0x1000] = {
       ' ',  '&',  '-',    0,  '0',    0,    0,    0,
       '1',  'A',  'J',    0,  '/',    0,    0,    0,
       '2',  'B',  'K',    0,  'S',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
       '3',  'C',  'L',    0,  'T',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
       '4',  'D',  'M',    0,  'U',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
       '5',  'E',  'N',    0,  'V',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
       '6',  'F',  'O',    0,  'W',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
       '7',  'G',  'P',    0,  'X',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
       '8',  'H',  'Q',    0,  'Y',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
       ':', 0xa2,  '!',    0, 0x5c,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
       '#',  '.',  '$',    0,  ',',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
       '@',  '<',  '*',    0,  '%',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
      0x27,  '(',  ')',    0,  '_',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
       '=',  '+',  ';',    0,  '>',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
      0x22,  '|', 0xac,    0,  '?',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
       '9',  'I',  'R',    0,  'Z',    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0
};

/*
 * readHeaderLine --
 *
 *	Read a line of a PPM header, discarding comments
 *
 * Results:
 *	Returns 0 if successful, -1 on failure.
 *
 * Side effects:
 *	Stores the line in 'line'. Prints an error message on failure.
 */

static int
readHeaderLine(char* line,	/* Line read from the file */
	       int length,	/* Size of the buffer */
	       FILE* f,		/* File to read */
	       const char* fileName)	/* Name of the file */
{
    int comment = 0;		/* Flag for whether the current line
				 * is a comment */
    int keepscanning = 1;	/* Flag that the scanner needs to read
				 * another line */
    int keeplooking;		/* Flag that the scanner needs to
				 * continue searching the current line
				 * for a comment delimiter */
    int i;

    while (keepscanning) {

	/* Read a line */

	if (fgets(line, length, f) == NULL) {
	    if (ferror(f)) {
		perror(fileName);
	    } else {
		fprintf(stderr, "%s: no image width and height", fileName);
	    }
	    return -1;
	}

	/* Is this the start of a comment? */

	if (comment == 0) {
	    keeplooking = 1;
	    for (i = 0; keeplooking; ++i) {
		switch (line[i]) {
		case '#':
		    comment = 1;
		    keeplooking = 0;
		    break;
		case ' ':
		case '\t':
		    break;
		default:
		    keeplooking = 0;
		    break;
		}
	    }
	}

	/* 
	 * Stop scanning when we get a non-comment line.
	 * If a comment line doesn't fit in the buffer, loop around and
	 * read more of it.
	 */

	if (!comment) {
	    keepscanning = 0;
	} else {
	    if (line[strlen(line) - 1] == '\n') {
		comment = 0;
	    }
	}
    }
    return 0;
}

/*
 * readPPM --
 *
 *	Reads a PPM file from the filesystem
 *
 * Results:
 *	Returns a pointer to the pixels read in, which are in row-major
 *	order, top-to-bottom, left-to-right.
 *	Returns NULL and puts an error message on stderr on failure.
 *
 * Side effects:
 *	Stores image height and width in *height and *width;
 */

static pixel*
readPPM(const char* fileName,	/* Name of the PPM file */
	int* width,		/* Image width */
	int* height)		/* Image height */
{
    FILE* f;			/* Channel to read pixels */
    pixel* pixels;		/* Pixel buffer */
    char line[80];		/* Line from the input file */
    int depth;			/* Bit depth of the image */
    int area;			/* Area of the image */
    int i;

    /* Open the file */

    if ((f = fopen(fileName, "rb")) == NULL) {
	perror(fileName);
	return NULL;
    }

    /* Read magic number */

    if (fgets(line, 80, f) == NULL) {
	if (ferror(f)) {
	    perror(fileName);
	} else {
	    fprintf(stderr, "%s: no magic number\n", fileName);
	}
	return NULL;
    }
    if (strcmp(line, "P6\n")) {
	fprintf(stderr, "%s: not a binary PPM file\n", fileName);
	return NULL;
    }

    /* Read width and height */

    if (readHeaderLine(line, 80, f, fileName)) {
	return NULL;
    }
    if (sscanf(line, "%d %d\n", width, height) < 2) {
	fprintf(stderr, "%s: can't read width and height\n", fileName);
	return NULL;
    }
    area = *width * *height;

    /* Read bit depth */

    if (readHeaderLine(line, 80, f, fileName)) {
	return NULL;
    }
    if (sscanf(line, "%d\n", &depth) < 1) {
	fprintf(stderr, "%s: can't read bit depth\n", fileName);
	return NULL;
    }

    /* Allocate and read pixels */

    pixels = (pixel*) malloc(area * sizeof(pixel));
    if (fread((void*) pixels, sizeof(pixel), area, f) < area) {
	if (ferror(f)) {
	    perror(fileName);
	} else {
	    fprintf(stderr, "%s: premature end of file\n", fileName);
	}
	free(pixels);
	return NULL;
    }
    if (fclose(f) < 0) {
	perror(fileName);
	free(pixels);
	return NULL;
    }

    /* Expand range */

    if (depth != 255) {
	for (i = 0; i < area; ++i) {
	    pixels[i].r = pixels[i].r * 0xff / depth;
	    pixels[i].g = pixels[i].g * 0xff / depth;
	    pixels[i].b = pixels[i].b * 0xff / depth;
	}
    }

    return pixels;
}

/*
 * segment --
 *
 *	Segment the image roughly into cards and holes
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Stores the segmented image (in black and white) in 'seg'
 *
 * The technique used here is K-means segmentation, with K=3.
 * The three classes are card, background and dirt.
 */

static void
segment(pixel* pixels,		/* Pointer to the image buffer */
	unsigned char* seg,	/* Segmentation mask */
	int area)		/* Area of the image */
{
    pixel classes[NCLASSES];	/* Color centroids of pixel classes */
    struct {
	long long r;
	long long g;
	long long b;
    } sums[NCLASSES];		/* Sums of pixels by class */
    int counts[NCLASSES];	/* Counts of pixels by class */
    int changed = 1;		/* Flag that something has changed. */
    int i;			/* Pixel index */
    int j, k;			/* Class index */
    int bestSoFar;		/* Best value obtained for distance */
    int value;			/* Value of current pixel */
    int x;
    
    /* Initialization. Set pixel class centroids to the a priori estimate. */

    memcpy(classes, cardcolors, NCLASSES * sizeof(pixel));

    /* k-means iteration */

    while (changed) {

	fprintf(stderr, "start pass\n"); fflush(stderr);
	/* Initialize */

	for (k = 0; k < NCLASSES; ++k) {
	    sums[k].r = sums[k].g = sums[k].b = 0;
	    counts[k] = 0;
	}
	for (i = 0; i < area; ++i) {

	    /* Classify a pixel */

	    k = -1;
	    bestSoFar = INT_MAX;
	    for (j = 0; j < NCLASSES; ++j) {
		value = 0;
		x = 30 * ((int)pixels[i].r - (int)classes[j].r);
		value += x * x;
		x = 59 * ((int)pixels[i].g - (int)classes[j].g);
		value += x * x;
		x = 11 * ((int)pixels[i].b - (int)classes[j].b);
		value += x * x;
		if (value < bestSoFar) {
		    bestSoFar = value;
		    k = j;
		}
	    }

	    /* Sum the pixel into others of its class */

	    sums[k].r += pixels[i].r;
	    sums[k].g += pixels[i].g;
	    sums[k].b += pixels[i].b;
	    seg[i] = k;
	    ++counts[k];
	}
	fprintf(stderr, "update centroids\n"); fflush(stderr);

	/* Update centroids */

	changed = 0;
	for (k = 0; k < NCLASSES; ++k) {
	    if (counts [k] && classes[k].r != sums[k].r / counts[k]) {
		classes[k].r = sums[k].r / counts[k];
		changed = 1;
	    }
	    if (counts [k] && classes[k].g != sums[k].g / counts[k]) {
		classes[k].g = sums[k].g / counts[k];
		changed = 1;
	    }
	    if (counts[k] && classes[k].b != sums[k].b / counts[k]) {
		classes[k].b = sums[k].b / counts[k];
		changed = 1;
	    }
	}
	fprintf(stderr, "end pass\n"); fflush(stderr);
    }
    fprintf(stderr, "all done segmenting\n"); fflush(stderr);
}

/*
 * edges --
 *
 *	Find edges in the segmented image
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Stores edges as a bit mask in 'edgeMask'
 */

static void
edges(unsigned char* segMask,	/* Segmentation mask */
      unsigned char* edgeMask,	/* Edge mask */
      int width,		/* Width of the image */
      int height)		/* Height of the image */
{
    int i, j;
    for (j = 0; j < width; ++j) {
	edgeMask[j] = 0;
    }
    for (i = 1; i < height; ++i) {
	edgeMask[i * width] = 0;
	for (j = 1; j < width; ++j) {
	    int indx = i * width + j;
	    if (segMask[indx] != segMask[indx-1]
		|| segMask[indx] != segMask[indx-width]) {
		edgeMask[indx] = 255;
	    } else {
		edgeMask[indx] = 0;
	    }
	}
    }
}

/*
 * hough --
 *
 *	Computes the Hough transformation of an image.
 *
 * Results:
 *	Returns an array with [thetasteps] columns and 2*rmax+1 rows, containing
 *	the transformation.
 *
 * Side effects:
 *	Stores the sizes of the array in thetasteps and r. 
 *	Each radial step represents TWO pixels from the center of the image.
 *	The angular steps represent pi/(thetasteps)
 */

int*
hough(unsigned char* edgeMask,	/* Edge-detected image */
      int width,		/* Width of the image */
      int height,		/* Height of the image */
      int* thetasteps,		/* Angular step count in the Hough transform */
      int* rmax)		/* Radial step count in the Hough transform */
{
    int longdim;		/* Longer dimension of the image */
    float thetastep;		/* Angular step size */
    float* cosines;		/* Trig tables */
    float* sines;
    int* accum;			/* Hough transform returned to caller */
    int x, y, t, r;

    /* Compute rmax to be one-half the image diagonal. Compute
     * thetasteps to be pi divided by the angle subtended by a pixel
     * at the edge of the image */

    *rmax = (int) sqrtf(0.25f * (width * width + height * height)) + 10;
    if (width < height) {
	longdim = height;
    } else {
	longdim = width;
    }

    thetastep = 2.0f / longdim;
    *thetasteps = (int) (3.1415926f / thetastep);
    thetastep = 3.1415926f / *thetasteps;

    accum = calloc((2 * *rmax + 1) * *thetasteps, sizeof(int));
    
    cosines = malloc(*thetasteps * sizeof(float));
    sines = malloc(*thetasteps * sizeof(float));
    for (t = 0; t < *thetasteps; ++t) {
	cosines[t] = cosf(t * thetastep);
	sines[t] = sinf(t * thetastep);
    }
    
    for (y = 0; y < height; ++y) {
	for (x = 0; x < width; ++x) {
	    if (edgeMask[width * y + x]) {
		for (t = 0; t < *thetasteps; ++t) {
		    r = (int) ((2 * x - width) / 4.0f * cosines[t]
			       + (-2 * y + height) / 4.0f * sines[t]
			       + 0.5) + *rmax;
		    ++accum[r * *thetasteps + t];
		}
	    }
	}
    }
    
    free(cosines);
    free(sines);
    return accum;
    
}

/*
 * findCard --
 *
 *	Locate the card in the image given the Hough transform.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Stores the co-ordinates of the 12 edge, column 1 of the card
 *	(the cut corner) in x and y.  x2 and y2 give the column-80
 *	end of the card, and x3 and y3 give the 9 edge of the card.
 */

static void
findCard(int* houghXform,	/* Hough transform of the edge-detected
				 * image of the card */
	 int thetasteps,	/* Number of angular steps in the transform */
	 int rmax,		/* Maximum radius in the Hough space */
	 float *h0,
	 float *v0,	/* Co-ordinates of 12 edge column 0 corner  */
	 float *dhx,
	 float *dvx,	 /* One-inch vector in the card X direction */
	 float *dhy,
	 float *dvy)	 /* One-inch vector in the card Y direction */
{
    int bestval = 0;		/* Maximum value in the transform */
    int bestr = 0;		/* Radius for the max value */
    int besttheta = 0;		/* Angle for the max value */
    int longtheta;		/* Angle of the long axis of the card */
    int shorttheta;		/* Angle of the short axis of the card */
    
    int longr[2] = {0,0};	/* R for long edges of the card */
    int shortr[2] = {0,0};	/* R for short edges of the card */

    float h[4];		/* H co-ordinates of the corners of the card */
    float v[4];		/* V co-ordinates of the corners of the card */

    float ulh = 0.f;
    float ulv = 0.f;		/* Co-ordinates of the row 12 column 0 corner */
    float llh = 0.f;
    float llv = 0.f;		/* Co-ordinates of the row 9 column 0 corner */
    float urh = 0.f;
    float urv = 0.f;		/* Co-ordinates of the row 12 column 80
				 * corner */
    float z;
    float ulbest = -1.0e10, llbest = -1.0e10, urbest = -1.0e10;

    int r, theta;
    int dtheta;
    int i, j;

    /* 
     * Find the peak of the Hough transform. The angle of the peak will
     * be parallel to the long axis of the card. It is expected that the
     * peak itself will lie on either the 9 or 12 edge, but we don't
     * assume that.
     */

    bestval = 0;
    for (r = 0; r < 2 * rmax + 1; ++r) {
	for (theta = 0; theta < thetasteps; ++theta) {
	    if (houghXform[r * thetasteps + theta] > bestval) {
		bestval = houghXform[r * thetasteps + theta];
		bestr = r;
		besttheta = theta;
	    }
	}
    }
    longtheta = besttheta;

    /* 
     * Find the lower and upper edges of the card by looking for
     * the first and last local maximum at the given angle having the
     * value at least 30% of the best.  
     */

    for (r = 1; r < 2 * rmax; ++r) {
	if (houghXform[r * thetasteps + longtheta] >= 0.1 * bestval
	    && (houghXform[r * thetasteps + longtheta] >=
		houghXform[(r-1) * thetasteps + longtheta])
	    && (houghXform[r * thetasteps + longtheta] >=
		houghXform[(r+1) * thetasteps + longtheta])) {
	    longr[0] = r;
	    break;
	}
    }
    for (; r < 2 * rmax; ++r) {
	if (houghXform[r * thetasteps + longtheta] >= 0.1 * bestval
	    && (houghXform[r * thetasteps + longtheta] >=
		houghXform[(r-1) * thetasteps + longtheta])
	    && (houghXform[r * thetasteps + longtheta] >=
		houghXform[(r+1) * thetasteps + longtheta])) {
	    longr[1] = r;
	}
    }

    /*
     * Now find the best line that is nearly perpendicular to the two card
     * edges that we just found. That line establishes the direction for
     * the short axis of the card.
     */

    bestval = 0;
    for (r = 0; r < 2 * rmax + 1; ++r) {
	for (dtheta = -10; dtheta <= 10; ++dtheta) {
	    theta = (longtheta + (thetasteps / 2) + dtheta) % thetasteps;
	    if (houghXform[r * thetasteps + theta] > bestval) {
		bestval = houghXform[r * thetasteps + theta];
		bestr = r;
		besttheta = theta;
	    }
	}
    }	    
    shorttheta = besttheta;

    /* 
     * Find the left and right edges of the card by looking for
     * the first and last local maximum at the given angle having the
     * value at least 10% of the best.  
     */

    for (r = 1; r < 2 * rmax; ++r) {
	if (houghXform[r * thetasteps + shorttheta] >= 0.1 * bestval
	    && (houghXform[r * thetasteps + shorttheta] >=
		houghXform[(r-1) * thetasteps + shorttheta])
	    && (houghXform[r * thetasteps + shorttheta] >=
		houghXform[(r+1) * thetasteps + shorttheta])) {
	    shortr[0] = r;
	    break;
	}
    }
    for (; r < 2 * rmax; ++r) {
	if (houghXform[r * thetasteps + shorttheta] >= 0.1 * bestval
	    && (houghXform[r * thetasteps + shorttheta] >=
		houghXform[(r-1) * thetasteps + shorttheta])
	    && (houghXform[r * thetasteps + shorttheta] >=
		houghXform[(r+1) * thetasteps + shorttheta])) {
	    shortr[1] = r;
	}
    }

    /* 
     * Solve for the four corners of the card.
     *
     * A corner of a card is found by solving simultaneous equations
     * for one long and one short edge:
     * x = rho * (-cos(theta) + p * sin(theta))
     * y = rho * (sin(theta) + p * cos(theta))
     * x = sigma * (-cos(phi) + p * sin(phi))
     * y = sigma * (sin(phi) + p * cos(phi))
     */

    {
	int indx = 0;
	float lgtheta = 3.1415927f * longtheta / thetasteps;
	float shtheta = 3.1415927f * shorttheta / thetasteps;
	float denom = sinf(lgtheta - shtheta);
	for (i = 0; i < 2; ++i) {
	    float lgrho = 2 * (longr[i] - rmax);
	    for (j = 0; j < 2; ++j) {
		float shrho = 2 * (shortr[j] - rmax);
		h[indx] = (-lgrho * sinf(shtheta)
			    + shrho * sinf(lgtheta)) / denom;
		v[indx] = (-lgrho * cosf(shtheta)
			    + shrho * cosf(lgtheta)) / denom;
		++indx;
	    }
	}
    }

    /* 
     * The card is loaded into the scanner, face up and oriented so that
     * the legend is readable to the operator. In the resulting image,
     * The row 12 column 0 of the card will have the maximum value
     * of (x+y). The row 12 column 80 corner will have the maximum value
     * of (x-y) and the row 9 column 0 corner will have the maximum value
     * of (y-x). Find all of these.
     */

    for (i = 0; i < 4; ++i) {
	z = h[i] + v[i];
	if (z > ulbest) {
	    ulbest = z; ulh = h[i]; ulv = v[i];
	}
	z = h[i] - v[i];
	if (z > urbest) {
	    urbest = z; urh = h[i]; urv = v[i];
	}
	z = v[i] - h[i];
	if (z > llbest) {
	    llbest = z; llh = h[i]; llv = v[i];
	}
    }
    
    /* 
     * The card is 7.375 inches wide, and 3.25 inches tall. Compute
     * 1-inch vectors in its cardinal directions
     */

    *h0 = ulh; *v0 = ulv;
    *dhx = (urh - ulh) / CARD_WIDTH;	*dvx = (urv - ulv) / CARD_WIDTH;
    *dhy = (llh - ulh) / CARD_HEIGHT;	*dvy = (llv - ulv) / CARD_HEIGHT;
}

/*
 * findPunches --
 *
 *	Locate the 1 and 0 bits on the card.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Stores the punches (column major order, rows 12-9, columns 1-80)
 *	in the 'cardBits' array.
 *
 * When this procedure is entered, we have established a reference frame
 * for the card.  The upper left corner of the card is at h0, v0 in
 * the image.  dhx, dvx is a 1-inch vector pointing in the card +x direction
 * and dhy, dvy is a 1-inch vector pointing in the card +y direction.
 */

static void
findPunches(const unsigned char* segMask,
				/* Mask with nonzero values where
				 * there are 'hole' pixels */
	    int width,		/* Width of the image */
	    int height,		/* Height of the image */
	    float h0, float v0,	/* Co-ordinates of the upper left corner */
	    float dhx, float dvx, /* 1-inch vector in the card X direction */
	    float dhy, float dvy, /* 1-inch vector in the card Y direction */
	    unsigned short* cardBits)
				/* Bits of the card, column-binary */
{
    int* count = calloc(12 * 80, sizeof(int));
    int* hole = calloc(12 * 80, sizeof(int));
    int v, h;
    float x, y;
    int row, col;
    float delta;
    float cross = dhx * dvy - dhy * dvx;

    /* Go through the pixels of the card. */

    for (v = 0; v < height; ++v) {
	for (h = 0; h < width; ++h) {

	    /* Find X and Y in card co-ordinates, in inches. */

	    x = (dvy * (h - h0) - dhy * (v - v0)) / cross;
	    y = (dhx * (v - v0) - dvx * (h - h0)) / cross;

	    /* Find card row and column, and offset within the row. */

	    col = (int) floor ((x - COL_1_X) / HOLE_HPITCH);
	    if (col < 0 || col >= 80) continue;
	    row = (int) floor ((y - ROW_12_Y) / HOLE_VPITCH);
	    if (row < 0 || row >= 12) continue;
	    delta = y - ROW_12_Y - HOLE_VPITCH * row;
	    if (delta > HOLE_HEIGHT) continue;
	    ++count[12*col + row];
	    hole[12*col + row] += (!segMask[width*v + h]);
	}
    }

    /* Pack and return the bits of the card in column binary format */

    for (col = 0; col < 80; ++col) {
	unsigned short word = 0;
	for (row = 0; row < 12; ++row) {
	    if (4 * hole[12 * col + row] > count[12 * col + row]) {
		word |= 1 << row;
	    }
	}
	cardBits[col] = word;
    }

#if 0
    {
	/* Write a histogram of hole and not-hole for debugging */

	int histo[101];
	int n;
	for (n = 0; n < 101; ++n) histo[n] = 0;
	for (n = 0; n < 12*80; ++n) {
	    ++histo[(int)(100 * hole[n] / count[n])];
	}
	for (n = 0; n < 101; ++n) {
	    printf("%d,%d\n", n, histo[n]);
	}

    }
#endif

}

int
main(int argc, const char *const argv[]) {

    HANDLE hDIB;
    BYTE* rowBuf;
    pixel* rawImage;
    int width;
    int height;
    int area;
    unsigned char* segMask;
    unsigned char* edgeMask;
    int thetasteps;
    int rmax;
    int* houghXform;
    float h0, v0, dhx, dvx, dhy, dvy;
    unsigned short cardBits[80];
    char title[81];
    const char* sep;
    int row, col;
    int trouble = 0;

    /* Check arguments */

    if (argc != 2) {
	fprintf(stderr, "usage: %s filename.ppm\n", argv[0]);
	return 1;
    }

    /* Test: Acquire a TWAIN image to a file */

#if 1
    TWAIN_SelectImageSource((HWND) NULL);
    hDIB = TWAIN_AcquireNative((HWND) NULL, TWAIN_RGB);
    if (!hDIB) {
	fprintf(stderr, "no image acquired.\n");
	return 1;
    }
    width = TWAIN_DibWidth(hDIB);
    height = TWAIN_DibHeight(hDIB);
    rawImage = malloc(width * height * sizeof(pixel));
    if (DIB_RowBytes(hDIB) < width * sizeof(pixel)) {
	fprintf(stderr, "row bytes is %ld should be at least %d\n",
		DIB_RowBytes(hDIB), width * sizeof(pixel));
	return 1;
    }
    rowBuf = malloc(DIB_RowBytes(hDIB));
    for (row = 0; row < height; ++row) {
	DIB_ReadRow(hDIB, row, rowBuf);
	for (col = 0; col < width; ++col) {
	    rawImage[row * width + col].b = rowBuf[3*col];
	    rawImage[row * width + col].g = rowBuf[3*col+1];
	    rawImage[row * width + col].r = rowBuf[3*col+2];
	}
    }
	
    TWAIN_FreeNative(hDIB);
    free(rowBuf);
#endif

#if 0
    /* Read the image from the PPM file */

    if ((rawImage = readPPM(argv[1], &width, &height)) == NULL) {
	return 1;
    }
#endif

    /* Compute image area */

    fprintf(stderr, "starting\n"); fflush(stderr);
    area = width * height;

    /* Write raw image for debugging */

    {
	FILE* f = fopen("acquired.ppm", "wb");
	fprintf(f, "P6\n%d %d\n255\n", width, height);
	fwrite(rawImage, 3, area, f);
	fclose(f);
    }

    /* Segment card from background */

    fprintf(stderr, "segmenting\n"); fflush(stderr);
    segMask = malloc(area);
    segment(rawImage, segMask, area);
    fprintf(stderr, "done\n"); fflush(stderr);

    /* Write segmented image for debugging */

    {
	FILE* f = fopen("segmask.pgm", "wb");
	int i;
	fprintf(f, "P5\n%d %d\n255\n", width, height);
	for (i = 0; i < area; ++i) {
	    fputc(255 * segMask[i] / (NCLASSES - 1), f);
	}
	fclose(f);
    }

    /* Find edges in the segmented image */

    fprintf(stderr, "finding edges\n"); fflush(stderr);
    edgeMask = malloc(area);
    edges(segMask, edgeMask, width, height);
    fprintf(stderr, "done\n"); fflush(stderr);

    /* Write segmented image for debugging */

    {
	FILE* f = fopen("edges.pgm", "wb");
	fprintf(f, "P5\n%d %d\n255\n", width, height);
	fwrite(edgeMask, 1, area, f);
	fclose(f);
    }

    /* Compute Hough transformation of the edge mask */

    fprintf(stderr, "Hough\n"); fflush(stderr);
    houghXform = hough(edgeMask, width, height, &thetasteps, &rmax);
    fprintf(stderr, "done\n"); fflush(stderr);
    free(edgeMask);

    /* Write the Hough transform for debugging */

    {
	int i;
	int harea = (2 * rmax + 1) * thetasteps;
	int maxx = 0;
	for (i = 0; i < harea; ++i) {
	    if (houghXform[i] > maxx) {
		maxx = houghXform[i];
	    }
	}
	FILE* f = fopen("hough.pgm", "wb");
	fprintf(f, "P5\n%d %d\n255\n", thetasteps, 2 * rmax + 1);
	for (i = 0; i < harea; ++i) {
	    if (houghXform[i] == 0) {
		fputc(0, f);
	    } else {
#if 0
		fputc((int) (255. * log(houghXform[i])/log(maxx)), f);
#else
		fputc((int) (255. * pow((double)houghXform[i]/maxx, 0.45)), f);
#endif
	    }
	}
	fclose(f);
    }

    /* 
     * Locate the card, which is presumed to be face up so that
     * the scanner reads the underside. Recenter the coordinate system
     * back to the corner of the image from the center
     */

    fprintf(stderr, "finding card in Hough xform\n"); fflush(stderr);
    findCard(houghXform, thetasteps, rmax, &h0, &v0, &dhx, &dvx, &dhy, &dvy);
    fprintf(stderr, "found\n"); fflush(stderr);
    free(houghXform);
    h0 += 0.5 * width; v0 += 0.5 * height;


    /* 
     * Save another color picture of the card, with the grid system overlaid
     * in red.
     */

    {
	fprintf(stderr, "gridding punches h0 %g v0 %g dhx %g dvx %g"
		" dhy %g dvy %g\n", h0, v0, dhx, dvx, dhy, dvy);
	fflush(stderr);
	FILE* f = fopen("axes.ppm", "wb");
	int q, r;
	for (q = 0; q <= 400; ++q) {
	    col = h0 + (q * CARD_WIDTH / 400.0) * dhx;
	    row = v0 + (q * CARD_WIDTH / 400.0) * dvx;
	    rawImage[row * width + col].r = 255;
	    rawImage[row * width + col].g = 0;
	    rawImage[row * width + col].b = 0;
	    col += (CARD_HEIGHT * dhy);
	    row += (CARD_HEIGHT * dvy);
	    rawImage[row * width + col].r = 255;
	    rawImage[row * width + col].g = 0;
	    rawImage[row * width + col].b = 0;
	}
	for (q = 0; q <= 200; ++q) {
	    col = h0 + (q * CARD_HEIGHT / 200.0) * dhy;
	    row = v0 + (q * CARD_HEIGHT / 200.0) * dvy;
	    rawImage[row * width + col].r = 255;
	    rawImage[row * width + col].g = 0;
	    rawImage[row * width + col].b = 0;
	    col += (CARD_WIDTH * dhx);
	    row += (CARD_WIDTH * dvx);
	    rawImage[row * width + col].r = 255;
	    rawImage[row * width + col].g = 0;
	    rawImage[row * width + col].b = 0;
	}
	for (q = 0; q < 80; ++q) {
	    for (r = 0; r < 12; ++r) {
		float x = HOLE_HPITCH * (q + 0.5) + COL_1_X;
		float y = HOLE_VPITCH * r + ROW_12_Y + 0.5 * HOLE_HEIGHT;
		int c = h0 + x * dhx + y * dhy;
		int r = v0 + x * dvx + y * dvy;
		for (col = c-1; col <= c+1; ++col) {
		    for (row = r-1; row <= r+1; ++row) {
			rawImage[row * width + col].r = 255;
			rawImage[row * width + col].g = 0;
			rawImage[row * width + col].b = 0;
		    }
		}
	    }
	}
	fprintf(f, "P6\n%d %d\n255\n", width, height);
	fwrite(rawImage, 3, area, f);
	fclose(f);
    }

    /*
     * We now have enough geometric information to find the holes in the
     * card and estimate whether they are punched or not.
     */

    fprintf(stderr, "finding punches h0 %g v0 %g dhx %g dvx %g dhy %g dvy %g\n",
	    h0, v0, dhx, dvx, dhy, dvy);
    fflush(stderr);
    findPunches(segMask, width, height,
		h0, v0, dhx, dvx, dhy, dvy, cardBits);
    fprintf(stderr, "punches found\n");
    fflush(stderr);
    free(segMask);

    /* Draw a picture of the card! */
    
    fprintf(stderr, "         1111111111222222222233333333334"
	    "4444444445555555555666666666677777777778\n");
    fprintf(stderr, "1234567890123456789012345678901234567890"
	    "1234567890123456789012345678901234567890\n");
    fprintf(stderr, "----------------------------------------"
	    "----------------------------------------\n");
    for (col = 0; col < 80; ++col) {
	if (cardCodes[cardBits[col]] == 0) {
	    fputc(' ', stderr);
	} else {
	    fputc(cardCodes[cardBits[col]], stderr);
	}
    }
    for (row = 0; row < 12; ++row) {
	for (col = 0; col < 80; ++col) {
	    fputc((cardBits[col] & (1 << row)) ? ']': ' ', stderr);
	}
	fputc('\n', stderr);
    }
    fprintf(stderr, "----------------------------------------"
	    "----------------------------------------\n");
    free(rawImage);

    /*
     * Validate that the scan is correct at least in that the bits
     * extracted from the card are well formed.
     */

    /* First five columns should be blank */

    for (col = 0; col < 5; ++col) {
	if (cardBits[col] != 0x000) {
	    fprintf(stderr, "card did not scan correctly, column %d should be "
		    "blank", col+1);
	    ++trouble;
	}
    }

    /* 
     * Columns 6-52 have a regular pattern 9, 8, space, ... in rows 7-9.
     * Rows 12-11 should be blank.
     */

    while (col < 52) {
	if ((cardBits[col++] & 0xe03) != 0x800) {
	    fprintf(stderr, "card did not scan correctly, invalid bit pattern "
		    " in column %d\n", col);
	    ++trouble;
	}
	if (cardBits[col++] != 0x400) {
	    if (col != 52 || cardBits[col-1] != 0) {
		fprintf(stderr, "card did not scan correctly, "
			"invalid bit pattern in column %d\n", col);
		++trouble;
	    }
	}
	if (cardBits[col++] != 0x000) {
	    fprintf(stderr, "card did not scan correctly, column %d should be blank\n", col);
	    ++trouble;
	}
    }
    if (trouble) {
	return trouble;
    }

    printf("%s\n", argv[1]);
    title[80] = '\0';
    for (col = 79; col >= 53 && cardCodes[cardBits[col]]; --col) {
	title[col] = cardCodes[cardBits[col]];
    }
    ++col;
    printf("%27s\n", title+col);
    sep = "";
    for (col = 5; col < 53; col += 3) {
	printf("%s%02x", sep, (cardBits[col] & 0x1fc) >> 2);
	sep = " ";
    }
    printf("\n");

    return 0;
}

Added stops.txt.

















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
Krummhorn No. 1 8'
              20.0 S00R1305
66 04 1e 03 6a 04 7f 08 10 5d 13 68 25 10 0e 7b
Clarinet Wood 8'
                   M00R1048
0a 20 30 2c 1e 18 19 1f 1f 19 18 1e 2c 30 20 0a
Flute F 4'
             R45.4 M00F2185
73 5c 4b 41 41 4b 5c 73 0d 24 35 3f 3f 35 24 0d
Voix Humaine 8' A
             R6.2   16R1053
74 7a 0d 02 76 7d 06 06 7e 79 00 01 04 01 00 01
Voix Humaine 8' B
             R10.1  16R1053
6c 76 15 03 70 7b 0a 0a 7d 75 00 02 07 01 00 02
Aeoline 8' A
              R2.1 S02S1020
7f 00 00 7f 7e 7f 00 00 00 00 02 03 05 04 03 01
Aeoline 8' B
             R3.5  S02S1020
7f 00 01 7f 7d 7e 7f 00 00 00 03 05 08 07 05 02
Echo String 8'
              R1.7 S01S1052
7f 00 02 00 7f 00 01 02 01 7f 01 02 04 03 02 01
Flute and Soft String
                   M00F1113
7f 08 11 0f 12 1b 22 28 2b 28 2f 2e 30 26 18 09
Gamba 8'
             R29.4 M00D1076
77 78 00 76 74 01 0d 10 0f 0f 23 31 3f 38 28 10
Gemshorn Harmonic
                   M00D1065
78 75 78 72 73 7f 0c 11 0e 07 0f 1c 32 34 25 0e
Salicet 4'
             R11.5 S05S2036
74 7e 08 70 78 00 15 05 7b 6b 00 08 10 78 02 0c
Salizional 4'
             R5.7   16S2062
7c 7c 7c 77 7d 03 0a 04 7c 76 7d 03 09 04 04 04
Viole d'Orchestre
                   S05S1034
75 01 0f 7c 71 7c 06 06 7c 6a 7a 02 1a 18 10 07
Viole d'Orchestre 8' Seminar
              R18.0 16S1034
70 01 17 7b 6c 7c 0a 08 7c 60 7a 00 24 1e 15 09
Violin 8'
             R28.7 M00S1047
76 08 0f 6d 63 04 26 28 0b 6e 08 12 38 33 27 15
Diapason 8'
             R32.8 M00D1040
7f 7e 7d 7c 7d 03 0a 11 17 1c 25 2e 32 2d 20 0b
Doublette 2'
             R14.7  16D4088
76 68 75 78 08 0b 18 0a 76 68 75 78 08 0b 18 0a
Dulciana 8'
             R3.4   16D1033
7f 7f 7f 7e 7e 7f 00 01 01 02 05 06 07 06 04 02
Dulzian 8'
            R25.1  M00R1104
78 1a 33 11 6f 71 07 09 07 69 7b 07 28 29 22 18
Geigen Diapason 8'
             R26.0 M00D1006
04 0d 15 12 0f 12 13 13 0f 0f 1c 28 32 2f 20 0b
Horn Diapason 8'
             R28.3 M00D1034
00 07 0c 05 00 03 05 04 01 05 19 29 35 30 23 0e
Phonon Diapason 8'
             R32.0 M00D1007
03 0c 16 19 1c 20 24 25 26 26 2a 2c 2d 26 1a 09
Principal 8' A
             R15.0  16D1032
7d 7e 00 7d 7d 02 06 09 0d 0f 17 1a 1e 19 11 06
Principal 8' B
             R10.0  16D1032
7e 7f 00 7e 7e 01 04 06 08 0a 0f 11 14 11 0c 04
Principal Bright
                   M00D1062
79 78 78 71 6e 76 7e 05 0b 13 24 2e 38 31 22 0c
Principal (Harmonic) 8'
             R30.2 M00D1071
77 76 77 6c 69 72 7c 03 0c 12 28 32 3f 37 26 0e
Quinte 2 2/3'
              10.5 S02D3075
03 12 0f 7f 7a 02 06 7c 6d 73 7f 07 14 0a 7c 7b
Blockfloete 8'
                   M00F1058
05 10 19 1f 24 2a 2f 31 30 2e 2e 2f 2d 24 17 07
Blockfloete 8' B
             R14.2 S02F1136
02 07 0b 0c 0f 12 11 13 12 11 12 11 10 0d 08 03
Blockfloete 2'
             R11.2 S01F4071
75 6d 79 7d 03 07 13 0b 75 6d 79 7d 03 07 13 0b
Concert Flute
                   M00F1056
01 02 05 08 0a 0f 15 1c 21 26 2c 30 30 29 1c 0a
Fife 1'
             R33.1 M00F8077
73 53 2d 0d 73 53 2d 0d 73 53 2d 0d 73 53 2d 0d
Fife Harmonic 1'
             R25.8 M00F8096
0e 5e 22 72 0e 5e 22 72 0e 5e 22 72 0e 5e 22 72
Flute 8'
              25.5 S01F1017
03 0b 11 16 1b 1f 22 23 23 22 20 1d 19 13 0c 04
Flute P 4'
             R14.4 S02F2185
7c 75 6f 6c 6c 6f 75 7c 04 0b 11 14 14 11 0b 04
Flute 4'
              13.8 S00F2176
7c 74 6e 6c 6d 72 77 7d 03 09 0e 13 14 12 0c 04
Flute 2 2/3'
              20.0 S00F3186
08 16 1c 19 0d 7d 6e 65 65 6e 7d 0d 19 1c 16 08
Flute 2' F
              48.2 M00F4187
66 41 41 66 1a 3f 3f 1a 66 41 41 66 1a 3f 3f 1a
Flute 2' P
              20.0 S00F4187
75 66 66 75 0b 1a 1a 0b 75 66 66 75 0b 1a 1a 0b
Flute 1 3/5' P
              14.1 S02F5188
09 14 0d 7a 6d 71 02 12 12 02 71 6d 7a 0d 14 09
Flute 1 1/3'
              20.3 S00F6189
10 1c 06 68 68 06 1c 10 70 64 7a 18 18 7a 64 70
Flute 23rd 8/9'
             R44.8 M00F9181
4f 62 3d 06 41 12 38 58 58 38 12 41 06 3d 62 4f
Flute 24th 4/5'
              45.4 M00F9183
4b 73 3f 5c 5c 3f 73 4b 35 0d 41 24 24 41 0d 35
Flute 25th 8/11'
                   S03F9151
74 02 0b 74 04 09 73 07 07 73 09 04 74 0b 02 74
Flute 29th
              21.0 S02F9182
15 6b 15 6b 15 6b 15 6b 15 6b 15 6b 15 6b 15 6b
Flute 8' and Fife 1/2'
             R39.1 M00F1166
17 03 2e 15 3b 1d 3f 1e 3f 1e 3e 1a 36 0d 23 76
Flute Dolce 8' A
             R14.8 S00F1153
03 07 0c 0f 11 12 13 13 13 13 12 11 0f 0c 07 03
Flute Dolce 8' B
              R7.2 S02F1177
01 02 04 05 07 08 09 0a 0a 0a 0a 09 07 06 04 01
Flute Harmonic 8' A
             R36.2 M00F1141
06 16 22 21 1f 25 2f 37 34 2a 25 24 27 23 18 0a
Flute Harmonic 8' B
             R31.7 M00F1109
00 05 0b 0f 14 1d 26 27 25 27 2d 30 2e 26 19 09
Gedeckt No. 2 8'
              17.7 S02F1143
03 0a 0f 12 15 16 15 16 16 15 16 14 13 0f 09 04
Harmonic Flute 8'
             R21.3  16F1246
04 0c 10 13 15 18 1c 1f 1f 1c 18 16 14 12 0d 05
Klein Gedeckt 8'
              12.6 S02F1049
02 06 0a 0d 0e 0f 10 10 10 10 0f 0f 0d 0b 07 02
Koppelfloete 8' A
              20.1 S00F1032
03 0a 0f 13 16 18 1b 1c 1b 19 18 16 14 10 0b 04
Koppelfloete 8' B
             R25.3  16F1228
03 0a 11 14 17 1c 22 25 24 21 1e 1c 19 15 0d 05
Larigot No. 1 1 1/3'
                   S03F6111
04 0f 04 70 78 01 0c 0b 75 74 7f 08 10 7c 71 7c
Larigot No. 2 1 1/3'
                   S03F6110
0a 12 04 71 71 04 12 0a 76 6e 7c 0f 0f 7c 6e 76
Quintade F 8'
              38.9 M01F1172
0d 29 3b 37 29 1a 0e 06 06 09 1c 2d 3f 3e 2f 11
Quintade P 8'
              24.0 M02F1172
08 1a 24 22 19 10 09 04 03 05 11 1c 27 26 1d 0b
Quintaden 8'
                   M00F1130
0a 21 2e 2b 20 14 0a 05 04 08 15 22 2f 2e 22 0d
Quintaden No. 2 8'
              17.7 S02F1130
06 13 1b 1a 13 0c 06 02 03 04 0d 14 1c 1c 15 08
Quintaden No. 3 8'
              17.6 S02F1131
07 14 1b 1b 14 0c 05 03 03 05 0c 14 1b 1b 14 07
Quintadena P 8'
             R24.1 S02F1052
06 18 25 22 1b 13 0a 03 01 03 12 1d 28 25 1a 0a
Quintadena PP 8'
              12.8 S02F1052
03 0d 13 12 0e 0a 05 01 01 02 0a 10 15 14 0e 05
Quintadena 4'
                   M00F2092
07 0b 6d 41 43 68 13 0c 74 6d 18 3d 3f 13 75 79
Quintadena P 4'
              21.4 M02F2092
05 08 75 58 5b 71 0e 08 78 72 0f 25 28 0b 78 7b
Rohrfloete 8'
              44.0 M00F1169
05 1c 34 38 31 2a 2c 30 31 26 2d 33 3f 32 1e 09
Rohrfloete P 8'
              23.8 M02F1173
02 0e 1c 1e 1a 17 18 1a 1a 14 18 1c 23 1c 11 05
Rohrfloete P 4'
             R24.1 S02F2105
72 03 66 59 5d 64 09 74 0c 77 1c 23 27 1a 7d 0e
Septieme 1 1/7'
                   S01F7148
72 6d 07 17 04 6d 78 13 13 78 6d 04 17 07 6d 72
Septieme Dissonant 1 1/7//'
                   M00F7142
5d 7e 7a 19 06 5f 08 7c 26 63 78 7e 12 13 5c 08
Open Flute 8' Seminar
             R44.9 16SPECYA
05 0f 17 1e 25 2d 33 38 3c 3e 3f 3e 38 2d 1e 0a
Stopped Flute 8' Seminar
             R50.0 16SPECYA
09 1a 28 33 3a 3d 3f 3f 3f 3f 3d 3a 33 28 1a 09
Barpfeife No. 1 8'
                   M00R1053
5a 6c 2a 07 5f 76 15 16 79 69 7e 05 0d 03 00 03
Barpfeife No. 2 8'
              25.9 M00R1349
41 6f 3f 6a 6b 0a 7d 17 78 72 0b 7b 12 06 7c 0d
Bassoon 8' A
            R34.6  M00R1013
0c 21 25 15 03 7b 73 61 53 58 7b 25 3f 3d 2c 10
Bassoon 8' B
                   M00R1141
0a 17 0f 79 6e 7b 0c 01 62 54 6f 1c 3c 3f 2e 11
Bombarde 8'
            R24.4  M00R1116
5b 71 31 03 5d 7d 16 1e 01 6c 05 15 20 1d 0e 06
Chorus Reed
            R 29.0 M00R1300
5b 7c 25 69 75 7b 20 05 1b 51 23 73 3f 06 16 0e
Chrysoglott
                   M00F1163
6f 6d 00 00 18 3a 2e 1a 19 02 74 10 21 18 22 16
Clarinet 8'
                   M00R1183
6e 0a 3b 17 76 05 7a 18 18 7a 05 76 17 3b 0a 6e
Clarinet Metal MF 8'
                   M00R1157
71 0b 21 1b 6e 0f 74 13 13 74 0f 6e 1b 21 0b 71
Clarinet Metal F 8'
                   M00R1046
6a 10 31 23 65 14 70 1b 1c 6d 16 66 27 31 11 6c
Clarinet Harmonic
                   M00R1045
5e 78 18 14 5c 12 68 13 12 66 13 5c 16 17 77 60
Clarion Harmonic 4'
            R23.6  M00R2129
69 78 15 5b 08 6c 25 71 0f 5b 14 78 25 6b 08 17
Classic Reed No. 1
                   M00R1042
62 7e 1f 76 6d 7e 0c 07 0e 5b 11 6c 1e 7b 7e 00
Cornet 8'
            R 27.3 M00R1345
01 09 0d 00 73 7b 0d 10 01 77 05 23 3d 3f 2b 0f
Cornopean 8' A
                   M00D1031
7b 00 07 79 6e 78 08 0c 03 7a 0a 21 38 37 27 0e
Cornopean 8' B
            R24.5  M00R1012
7f 05 0b 01 78 00 12 17 0d 06 0f 22 35 34 23 0d
Cromorne 8'
            R 30.6 M00R1288
65 01 3f 0e 63 11 79 1d 1b 61 17 6e 29 3d 00 09
Echo Horn 8'
              R9.8  16R1422
00 04 06 03 00 7f 7d 7b 7b 7b 05 0c 15 15 11 06
English Horn 8'
                   M00R1179
07 13 08 68 57 6b 0f 22 11 6a 61 79 26 3b 32 13
English Post Horn 8'
                   M00R1196
68 7d 24 7d 64 04 13 21 0a 55 7a 13 3b 24 05 09
Festival Trumpet 8'
            R31.4  M00R1237
54 7d 2a 6d 5e 78 0d 06 10 45 16 6b 3f 15 16 0a
French Horn No. 1 8'
            R23.2  M00R1184
02 06 08 07 05 04 02 01 03 0b 18 26 31 31 25 0e
French Horn No. 2 8'
              30.6 M00R1317
04 0b 0e 0c 07 00 7c 7d 05 13 25 36 3f 3c 2c 10
French Horn No. 3 8'
              25.4 M00R1331
03 07 04 7c 7c 04 0b 0a 7c 6f 75 0d 2d 3f 36 16
Harmonic Trumpet 8'
                   MOOR1124
5f 03 2c 75 5b 7c 17 1b 0a 55 03 00 36 1c 0c 07
Horn 8'
                   M00R1070
7d 7b 7e 03 08 0b 0a 06 01 7f 08 1b 2d 34 29 0f
Kinura No. 1 8'
                   M00R1197
61 01 17 71 7d 06 7f 08 10 6d 25 75 26 0a 08 0c
Kinura No. 2 8'
                   M00R1054
64 03 17 6d 7d 10 06 7c 18 64 21 6f 32 14 1e 0f
Krumet 8'
            R26.4  M00R1032
73 03 1b 5e 56 7f 7d 23 1c 04 1e 00 31 21 13 19
Krummhorn No. 2 8'
              20.0 S00R1288
6e 00 29 09 6d 0b 7c 13 12 6c 0f 74 1b 28 00 06
Krummhorn Harmonic 8'
              45.0 M00R1275
41 79 2b 24 42 27 59 1c 21 47 30 42 38 24 7f 4d
Krummregal 8'
              28.4 S01R1303
68 76 2d 10 4e 66 25 22 7c 6c 65 71 1d 2b 03 74
Musette 8'
            R 14.9 S00R1351
69 7f 20 73 6b 0a 04 05 0a 79 75 00 08 12 16 0a
Oboe Horn 8'
            R34.5  M00R1049
05 16 1a 75 57 6f 1f 2e 0a 5b 58 7d 2e 3f 2f 11
Orchestral Oboe 8'
            R 14.9 S00R1335
72 7d 13 01 76 02 07 03 79 6b 01 12 21 1b 0e 03
Orchestral Schalmei 8'
                   M00R1180
76 09 1d 7b 6e 02 08 0a 7c 5f 61 7c 1d 3d 38 14
Orchestral Tuba 8'
              27.0 M00R1344
01 08 0d 05 7f 08 12 12 05 7b 04 1d 37 3f 31 13
Petit Rankett 8'
              12.7 S02R1304
6f 79 1a 7e 6f 06 7c 0d 06 6f 09 77 11 0f 7b 04
Posaune 8'
              26.3 M00R1347
79 7f 11 09 70 72 13 2b 18 76 7a 24 3f 2c 07 7c
Post Horn 8'
                   M00R1034
7b 00 0e 00 70 7c 19 22 01 57 6a 1f 3f 24 7d 78
Rankett 8' A
            R 25.4 M00R1269
48 6b 3f 74 5a 11 08 05 05 6d 02 0e 7c 0a 06 7a
Rankett 8' B
              31.1 M00R1346
7c 11 2b 0e 5e 69 1b 1b 6e 41 6a 18 37 22 14 06
Reed Chorus 8' & 4'
            R 30.0 M00R1265
66 75 1b 62 73 60 2c 0b 0b 4c 09 0e 3f 0a 1c 1c
Reed Extender
                   M00R1172
61 7c 1d 79 70 00 09 03 0c 5f 15 71 1c 76 77 7d
Reed Harmonics Bright
              34.6 M00R1267
48 78 30 6d 70 07 0f 71 1c 4b 3f 4e 29 69 0e 07
Reed Harmonics Medium
              25.4 M00R1270
47 6a 3f 76 5a 0d 07 07 06 6b 7e 0d 7d 08 02 77
Solo Reed
                   M00R1115
6f 7d 28 0d 58 78 08 14 01 5c 0d 75 29 24 13 1f
Regal 8'
             R28.0 M00R1246
4c 00 3f 6c 69 02 09 1d 72 5c 6c 7c 22 1b 0b 0a
Saxophone 8'
            R 34.2 M00R1342
7b 09 18 06 7f 17 29 2c 23 1d 2a 2c 3f 34 22 11
Singend Regal No. 1 8'
                   M00R1194
60 7b 2c 76 67 06 07 7a 02 69 75 07 20 10 0d 01
Singend Regal No. 2 8'
                   M00R1018
66 7e 2f 03 69 14 14 00 0a 7f 75 15 2b 19 13 0b
Singend Regal No. 3 8'
              19.4  16R1355
5b 7a 3e 02 60 19 1a 7e 0e 7f 72 19 34 1c 15 0c
Spanish Trumpet 8'
            R 38.7 M00R1254
42 01 3c 71 59 0a 11 1f 15 4d 19 7e 3f 3a 7c 31
Stereo Reed Even
            R26.9  M00R1158
64 75 12 5b 02 7a 33 07 11 64 1b 10 35 7a 12 1a
Stereo Reed Odd
            R 25.0 S00R1263
69 0b 20 1b 5b 0d 62 0d 0d 62 0d 5b 1b 20 0b 69
Tenor Saxophone 8'
              32.9 M00R1338
74 09 2b 14 7c 0e 1f 26 21 12 1b 27 3f 39 22 0c
Trompette 8' A
              25.1 S00R1350
67 03 2b 76 5a 78 19 25 00 61 71 14 2a 17 7b 7b
Trompette 8' B
                   S02R1011
77 02 19 02 64 77 1b 1d 7f 68 75 18 34 2d 16 05
Trompette Harmonique No. 1 8'
              31.7 M00R1252
49 7d 3f 68 67 03 0e 28 7e 6b 7e 10 36 2b 14 0e
Trompette Harmonique No. 2 8'
              33.7 M00R1244
42 79 3f 5d 5a 78 03 1f 71 5c 73 07 33 29 13 0e
Trumpet 8'
                   M00R1056
09 14 0c 76 6c 7f 1d 21 06 6d 77 1e 3d 3d 27 0d
Tuba 8'
            R26.2  M00R1062
7b 03 13 06 73 7c 19 22 0c 74 06 22 38 32 24 0e
Waldhorn 8'
            R 29.8 M00R1324
08 10 09 7b 76 03 1a 29 23 0e 7f 08 25 3f 3e 1a
Allen Sound 8'
             R31.4 M00F1140
7f 00 06 0c 14 1f 27 29 29 27 29 2b 2c 26 1a 0a
Cornet III
                   M00FGT30
76 31 3c 7c 56 04 12 01 72 5a 59 73 34 1f 38 16
Cornet IV
             R20.8 M00FSW28
69 01 1e 01 6d 7b 15 01 06 58 04 66 2d 03 0a 0c
Cornet V // (2-1 1/3-1-2/3-1/2)
              18.0 S00F9194
7e 72 11 66 0c 72 1f 0b 75 61 0e 74 1a 6f 0e 02
Dolce Cornet A
             R 2.0  16F9194
00 7e 02 7d 01 7e 03 01 7f 7d 02 7f 03 7e 02 00
Dolce Cornet B
             R4.1   16F9194
00 7d 04 7a 03 7d 07 02 7e 79 03 7d 06 7c 03 00
Mixture III A
             R43.5 M00F9193
77 57 3f 55 2b 41 29 09 77 57 3f 55 2b 41 29 09
Mixture III B
              50.2 M00F9196
14 52 3f 44 3c 41 2e 6c 14 52 3f 44 3c 41 2e 6c
Mixture III C // (2-1 1/3-1)
                   M00FSA24
79 64 18 57 18 6c 2f 12 6e 51 14 68 29 68 1c 07
Mixture II D
                   M00FGA21
71 73 28 7d 63 5e 32 23 5d 4e 22 1d 03 58 0d 0f
Mixture III E
             R31.2  16F9196
0d 63 27 5b 25 59 1d 73 0d 63 27 5b 25 59 1d 73
Mixture III F
              R27.2 16F9193
7b 66 27 65 1b 59 1a 05 7b 66 27 65 1b 59 1a 05
Mixture III G
              R20.1 16F9193
7c 6d 1d 6c 14 63 13 04 7c 6d 1d 6c 14 63 13 04
Mixture III H
             R20.1  16FSA24
00 6d 14 63 14 6f 1e 06 7a 62 11 6c 1d 6c 13 00
Mixture III J
              R16.8 16FSA24
00 70 10 68 11 71 19 05 7b 67 0f 6f 18 70 10 00
Sesquialtera
             R31.4 M00FSW29
00 36 3a 6d 5d 06 04 7e 7e 69 5c 09 2f 07 13 16
Sine Chorus A
                   M00R1230
5d 04 02 38 08 38 29 3f 67 7c 6a 15 60 10 07 26
Sine Chorus B
             R24.6 M00R1057
76 6c 75 7c 01 0c 1e 1e 14 12 20 25 24 22 23 0f
Bell 8' 2 2/3' 1'
             R34.8 M00R1294
75 08 3e 3f 0d 00 1e 16 6b 73 2b 37 14 13 33 20
Carillon No. 1 //
                   M00R2224
6f 76 07 6f 6f 0c 22 20 69 4c 00 16 00 08 0c 01
Carillon No. 2 // 1' 4/5' 2/3' 1/2'
                   M00FPO32
5a 76 22 72 7c 7d 04 0a 76 7c 03 04 0e 5e 0a 26
Chimes
             R23.4 M00R1284
6d 03 1b 76 7a 0e 0c 13 63 41 04 1d 03 1a 16 7d
Crystal Bell
             R34.2 M00R1293
18 74 30 6f 03 5f 2f 7a 06 5a 39 23 3f 7c 2c 74
Dolce Bell
             R35.4 M00R1298
0b 1f 2a 2a 20 12 05 7f 03 11 24 35 3f 3c 2b 10
Jeu de Clochette
             R41.5 M00F9195
09 67 2f 41 3f 51 19 77 09 67 2f 41 3f 51 19 77
Multi-Bell
             R34.2 M00R1297
5f 5d 07 7b 06 7b 27 2a 6f 76 29 25 33 22 3f 2b
Octa-Bell
             R35.8 M00R1299
61 4f 73 01 71 05 34 30 00 7b 26 35 1e 24 3f 24
Tierce Bell
              34.8 M00R2292
63 2f 24 23 6a 4b 1e 72 0b 4b 0e 00 76 24 1f 3f
Tubular Chimes
                   M00R2156
69 7a 12 6b 77 0d 0a 1b 77 51 0b 14 72 0c 0a 77
Quadra-Bell
              31.0 M00R1295
68 0c 15 7f 39 19 10 20 61 7d 07 7a 3f 27 1f 28
Cymbal V // 8'
             R28.5 M00FSW31
6c 79 12 69 70 70 32 24 0d 47 5e 70 24 0d 18 16

Added terminal/GDM2004D.pdf.

cannot compute difference between binary files

Added terminal/HD44780.pdf.

cannot compute difference between binary files

Added terminal/Keypad1-L.jpg.

cannot compute difference between binary files

Added terminal/Makefile.























































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
# Arduino 0015 Makefile
# Arduino adaptation by mellis, eighthave, oli.keller
#
# This makefile allows you to build sketches from the command line
# without the Arduino environment (or Java).
#
# Detailed instructions for using the makefile:
#
#  1. Copy this file into the folder with your sketch. There should be a
#     file with the same name as the folder and with the extension .pde
#     (e.g. foo.pde in the foo/ folder).
#
#  2. Modify the line containg "INSTALL_DIR" to point to the directory that
#     contains the Arduino installation (for example, under Mac OS X, this
#     might be /Applications/arduino-0012).
#
#  3. Modify the line containing "PORT" to refer to the filename
#     representing the USB or serial connection to your Arduino board
#     (e.g. PORT = /dev/tty.USB0).  If the exact name of this file
#     changes, you can use * as a wildcard (e.g. PORT = /dev/tty.usb*).
#
#  4. Set the line containing "MCU" to match your board's processor. 
#     Older one's are atmega8 based, newer ones like Arduino Mini, Bluetooth
#     or Diecimila have the atmega168.  If you're using a LilyPad Arduino,
#     change F_CPU to 8000000.
#
#  5. At the command line, change to the directory containing your
#     program's file and the makefile.
#
#  6. Type "make" and press enter to compile/verify your program.
#
#  7. Type "make upload", reset your Arduino board, and press enter to
#     upload your program to the Arduino board.
#
# $Id$

TARGET = $(notdir $(CURDIR))
INSTALL_DIR = /d/Distributions/arduino-0016
PORT = COM5:
UPLOAD_RATE = 19200
AVRDUDE_PROGRAMMER = stk500v1
MCU = atmega168
F_CPU = 16000000

############################################################################
# Below here nothing should be changed...

ARDUINO = $(INSTALL_DIR)/hardware/cores/arduino
AVR_TOOLS_PATH = $(INSTALL_DIR)/hardware/tools/avr/bin
# kbk owns both pieces
#SRC =  $(ARDUINO)/pins_arduino.c $(ARDUINO)/wiring.c \
#$(ARDUINO)/wiring_analog.c $(ARDUINO)/wiring_digital.c \
#$(ARDUINO)/wiring_pulse.c $(ARDUINO)/wiring_serial.c \
#$(ARDUINO)/wiring_shift.c $(ARDUINO)/WInterrupts.c
# kbk owns both pieces
SRC =  $(ARDUINO)/pins_arduino.c $(ARDUINO)/wiring.c \
$(ARDUINO)/wiring_analog.c $(ARDUINO)/wiring_digital.c \
$(ARDUINO)/wiring_pulse.c \
$(ARDUINO)/wiring_shift.c $(ARDUINO)/WInterrupts.c
CXXSRC = $(ARDUINO)/HardwareSerial.cpp $(ARDUINO)/WMath.cpp \
$(ARDUINO)/Print.cpp
FORMAT = ihex


# Name of this Makefile (used for "make depend").
MAKEFILE = Makefile

# Debugging format.
# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2.
# AVR (extended) COFF requires stabs, plus an avr-objcopy run.
DEBUG = stabs

OPT = s

# Place -D or -U options here
CDEFS = -DF_CPU=$(F_CPU)
CXXDEFS = -DF_CPU=$(F_CPU)

# Place -I options here
CINCS = -I$(ARDUINO)
CXXINCS = -I$(ARDUINO)

# Compiler flag to set the C Standard level.
# c89   - "ANSI" C
# gnu89 - c89 plus GCC extensions
# c99   - ISO C99 standard (not yet fully implemented)
# gnu99 - c99 plus GCC extensions
CSTANDARD = -std=gnu99
CDEBUG = -g$(DEBUG)
CWARN = -Wall -Wstrict-prototypes
CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
#CEXTRA = -Wa,-adhlns=$(<:.c=.lst)

CFLAGS = $(CDEBUG) $(CDEFS) $(CINCS) -O$(OPT) $(CWARN) $(CSTANDARD) $(CEXTRA)
CXXFLAGS = $(CDEFS) $(CINCS) -O$(OPT)
#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs 
LDFLAGS = -lm


# Programming support using avrdude. Settings and variables.
AVRDUDE_PORT = $(PORT)
AVRDUDE_WRITE_FLASH = -U flash:w:applet/$(TARGET).hex
AVRDUDE_FLAGS = -V -F -C $(INSTALL_DIR)/hardware/tools/avr/etc/avrdude.conf \
-p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) \
-b $(UPLOAD_RATE)

# Program settings
CC = $(AVR_TOOLS_PATH)/avr-gcc
CXX = $(AVR_TOOLS_PATH)/avr-g++
OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy
OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump
AR  = $(AVR_TOOLS_PATH)/avr-ar
SIZE = $(AVR_TOOLS_PATH)/avr-size
NM = $(AVR_TOOLS_PATH)/avr-nm
AVRDUDE = $(AVR_TOOLS_PATH)/avrdude
REMOVE = rm -f
MV = mv -f

# Define all object files.
OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) 

# Define all listing files.
LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst)

# Combine all necessary flags and optional flags.
# Add target processor to flags.
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS)
ALL_CXXFLAGS = -mmcu=$(MCU) -I. $(CXXFLAGS)
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)


# Default target.
all: applet_files build sizeafter

build: elf hex 

applet_files: $(TARGET).pde
	# Here is the "preprocessing".
	# It creates a .cpp file based with the same name as the .pde file.
	# On top of the new .cpp file comes the WProgram.h header.
	# At the end there is a generic main() function attached.
	# Then the .cpp file will be compiled. Errors during compile will
	# refer to this new, automatically generated, file. 
	# Not the original .pde file you actually edit...
	test -d applet || mkdir applet
	echo '#include "WProgram.h"' > applet/$(TARGET).cpp
	cat $(TARGET).pde >> applet/$(TARGET).cpp
	cat $(ARDUINO)/main.cxx >> applet/$(TARGET).cpp

elf: applet/$(TARGET).elf
hex: applet/$(TARGET).hex
eep: applet/$(TARGET).eep
lss: applet/$(TARGET).lss 
sym: applet/$(TARGET).sym

# Program the device.  
upload: applet/$(TARGET).hex
	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)


	# Display size of file.
HEXSIZE = $(SIZE) --target=$(FORMAT) applet/$(TARGET).hex
ELFSIZE = $(SIZE)  applet/$(TARGET).elf
sizebefore:
	@if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(HEXSIZE); echo; fi

sizeafter:
	@if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(HEXSIZE); echo; fi


# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
COFFCONVERT=$(OBJCOPY) --debugging \
--change-section-address .data-0x800000 \
--change-section-address .bss-0x800000 \
--change-section-address .noinit-0x800000 \
--change-section-address .eeprom-0x810000 


coff: applet/$(TARGET).elf
	$(COFFCONVERT) -O coff-avr applet/$(TARGET).elf $(TARGET).cof


extcoff: $(TARGET).elf
	$(COFFCONVERT) -O coff-ext-avr applet/$(TARGET).elf $(TARGET).cof


.SUFFIXES: .elf .hex .eep .lss .sym

.elf.hex:
	$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@

.elf.eep:
	-$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
	--change-section-lma .eeprom=0 -O $(FORMAT) $< $@

# Create extended listing file from ELF output file.
.elf.lss:
	$(OBJDUMP) -h -S $< > $@

# Create a symbol table from ELF output file.
.elf.sym:
	$(NM) -n $< > $@

	# Link: create ELF output file from library.
applet/$(TARGET).elf: $(TARGET).pde applet/core.a 
	$(CC) $(ALL_CFLAGS) -o $@ applet/$(TARGET).cpp -L. applet/core.a $(LDFLAGS)

applet/core.a: $(OBJ)
	@for i in $(OBJ); do echo $(AR) rcs applet/core.a $$i; $(AR) rcs applet/core.a $$i; done



# Compile: create object files from C++ source files.
.cpp.o:
	$(CXX) -c $(ALL_CXXFLAGS) $< -o $@ 

# Compile: create object files from C source files.
.c.o:
	$(CC) -c $(ALL_CFLAGS) $< -o $@ 


# Compile: create assembler files from C source files.
.c.s:
	$(CC) -S $(ALL_CFLAGS) $< -o $@
.cpp.s:
	$(CC) -S $(ALL_CFLAGS) $< -o $@


# Assemble: create object files from assembler source files.
.S.o:
	$(CC) -c $(ALL_ASFLAGS) $< -o $@


# Automatic dependencies
%.d: %.c
	$(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@

%.d: %.cpp
	$(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@


# Target: clean project.
clean:
	$(REMOVE) applet/$(TARGET).hex applet/$(TARGET).eep applet/$(TARGET).cof applet/$(TARGET).elf \
	applet/$(TARGET).map applet/$(TARGET).sym applet/$(TARGET).lss applet/core.a \
	$(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d)

.PHONY:	all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter

include $(SRC:.c=.d)
include $(CXXSRC:.cpp=.d)

Added terminal/bom.txt.

















>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
Electronics in the display terminal

Arduino Duemilanove (the earlier version with the 168 chip).
ladyada.net prototyping shield.
sparkfun.com keypad COM-08653
sparkfun.com display LCD-00256
    (Actually a Xiamen Ocular GDM2004D, using a HD44180 chip)

Added terminal/pins.txt.



























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
Pin usage for Arduino.

0	Reserved for USB Serial communications
1    

2	These pins are capable of interrupts; not needed for keypad
3	or display, so left available

4	Keypad column 1   (keypad pin 3)
5	Keypad row 0      (keypad pin 2)
6	Keypad column 2   (keypad pin 1)

7	Display data bit 7 (display pin 14)
8	Display data bit 6 (display pin 13)
9	Display data bit 5 (display pin 12)
10	Display data bit 4 (display pin 11)

11	Display backlight PWM control (indirectly, display pins 15-16)

12	Display ENABLE	   (display pin 6)
13	Display RS	   (display pin 4)

14
15

16	Keypad row 1	  (keypad pin 7)
17	Keypad row 2      (keypad pin 6)
18	Keypad column 0   (keypad pin 5)
19	Keypad row 3      (keypad pin 4)

Added terminal/protoshield/v5schematic.png.

cannot compute difference between binary files

Added terminal/terminal.pde.





























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#include <avr/pgmspace.h>
#include <avr/eeprom.h>

extern "C" void __cxa_pure_virtual() {
  while (1) {}
}
 
/*---------------------------------------------------------------------------*
 *                                                                           *
 *			     	EEPROM LAYOUT				     *
 *									     *
 *---------------------------------------------------------------------------*/

enum EEPROM_address {
  EEPROM_DISP_BRIGHTNESS,	/* Last display brightness setting */
};
 
/*---------------------------------------------------------------------------*
 *                                                                           *
 *			     KEYPAD DEFINITIONS				     *
 *                                                                           *
 *---------------------------------------------------------------------------*/

/* CONSTANTS */

/* Definitions of symbols on the keypad */

#define KP_ASTERISK  ((byte) 10) 	/* Keycode returned for * */
#define KP_OCTOTHORP ((byte) 11)	/* Keycode returned for # */
#define KP_ROLLOVER  ((byte) 0xfe)	/* Keycode used for rollover */
#define KP_NOKEY     ((byte) 0xff)	/* Keycode for "no key" */

/* Size of the keypad */

#define KP_ROWS 4		/* Number of rows in the keypad */
#define KP_COLS 3		/* Number of columns in the keypad */

/* Timings for polling the keypad */

#define KP_POLL_DELAY_MICROS	10	/* Delay, in microsecods, between 
					 * asserting a keypad row and reading 
					 * the columns */

#define KP_DEBOUNCE_MILLIS	20 	/* Time, in milliseconds, that
					 * keypad input must be stable to be
					 * considered "debounced" */

/* PROGRAM MEMORY */

/* Pin assignments for the keypad rows. */

static const prog_uchar kpRowPinsPGM[4] PROGMEM = { 5, 16, 17, 19 };

/* Pin assignments for the keypad columns. */

static const prog_uchar kpColPinsPGM[3] PROGMEM = { 4, 6, 18 };

/* Keypad layout */

static const prog_uchar kpKeymapPGM[KP_ROWS * KP_COLS] PROGMEM = {
  1, 2, 3, 
  4, 5, 6, 
  7, 8, 9, 
  KP_ASTERISK, 0, KP_OCTOTHORP
};

/* RAM */

static byte kpLastState; 		/* Last state read from the keypad. */

static byte kpLastStableState;		/* Last debounced keypad state */

static unsigned long kpLastStateChangeTime;
				        /* Time at which the keypad state 
					 * last changed */
 
/* KEYPAD FUNCTIONS */

/*
 *-----------------------------------------------------------------------------
 *
 * kpInit --
 *
 *	Initialize the keypad.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *
 * 	This procedure sets the keypad pins to all be inputs (so that
 * 	columns will be held at a high impedance state when not
 * 	scanning). It sets the keypad scanner to an initial idle
 * 	state.
 *
 * This function should be called from the setup() function at the time
 * that the keypad needs to be initialized.
 *
 *-----------------------------------------------------------------------------
 */

void
kpInit(void) {

  byte pin;			/* Number of the row or column being accessed */
  byte p;			/* Actual pin number that the row or column
				 * is wired to. */

  /*
   * Initialize the keypad. Set both rows and columns to be input lines.
   * Put pullups in the real inputs (the columns) and leave the real outputs
   * (the rows) in the high-impedance state.
   */
  
  for (pin = 0; pin < KP_ROWS; ++pin) {
    p = pgm_read_byte(kpRowPinsPGM + pin);
    pinMode(p, INPUT);
    digitalWrite(p, LOW);
  }
  for (pin = 0; pin < KP_COLS; ++pin) {
    p = pgm_read_byte(kpColPinsPGM + pin);
    pinMode(p, INPUT);
    digitalWrite(p, HIGH);
  }

  /*
   * Set the keypad state to KP_NOKEY to indicate that no key is yet
   * pressed. (If the sketch initializes with a button pressed, then
   * that state will be detected as a keypress on the first round of
   * polling.)
   */

  kpLastState = kpLastStableState = KP_NOKEY;
  kpLastStateChangeTime = micros();
  
}
 
/*
 *-----------------------------------------------------------------------------
 *
 * kpState --
 *
 *	Read the current state of the keypad.
 *
 * Return value:
 *
 *	Returns one of the values in kpKeymapPGM if a key is
 *	depressed.  Returns KP_NOKEY if no key is depressed or
 *	KP_ROLLOVER if multiple keys are depressed.
 *
 * This procedure works by enabling each row output in turn and
 * setting it LOW.  It then reads column inputs, looking for a HIGH
 * input that indicates a key is pressed.  If it finds a single HIGH
 * input, it returns it. If it finds none, it returns KP_NOKEY, and if
 * it finds multiple ones, it returns KP_ROLLOVER.  In any case, it
 * restores all rows to the high-impedance state before returning.
 *
 * This procedure makes no attempt to debounce the input.
 *
 *-----------------------------------------------------------------------------
 */

byte
kpState(void) {

  byte row, col;		/* Current key co-ordinates */
  byte k;		        /* Current key's position in kpKeymapPGM */
  byte key;			/* Current key's code */
  byte rowPin;			/* Pin number of the current row */
  
  /* Start with no key found */

  key = KP_NOKEY;

  /* Iterate through the rows */

  k = 0;
  for (row = 0; row < KP_ROWS; ++row) {

    /* Turn on a row and wait for the input to settle */

    rowPin = pgm_read_byte(kpRowPinsPGM + row);
    pinMode(rowPin, OUTPUT);
    delayMicroseconds(KP_POLL_DELAY_MICROS);

    /* Read the columns */

    for (col = 0; col < KP_COLS; ++col) {
      if (digitalRead(pgm_read_byte(kpColPinsPGM + col)) == LOW) {
	if (key == KP_NOKEY) {

	  /* Found a key pressed - set the return value */

	  key = pgm_read_byte(kpKeymapPGM+k);

	} else {

	  /* Found multiple keys pressed - set KP_ROLLOVER */

	  key = KP_ROLLOVER;
	}
      }
      ++k;
    }

    /* Restore the row output to the high impedance state */

    pinMode(rowPin, INPUT);
  }

  /* Return whatever key was found. */

  return key;
}
 
/*
 *-----------------------------------------------------------------------------
 *
 * kpPoll --
 *
 *	Polls the keyboard for a keypress, debouncing the input.
 *
 * Return value:
 *	Returns the key code for the key that was pressed, or KP_NOKEY
 *	if no keypress has occurred since the last poll.  
 *
 * This function requires that any keyboard state change be stable for
 * kpDebounceMillis before being accepted.
 *
 *-----------------------------------------------------------------------------
 */

byte
kpPoll() {

  byte thisState = kpState();	/* Current keyboard state */

  unsigned long now = millis();	/* Current time */

  if (thisState != kpLastState) {

    /*  
     * The keyboard state changed since the last poll.  Wind the debounce
     * timer and report KP_NOKEY
     */

    kpLastState = thisState;
    kpLastStateChangeTime = now;

  } else if (((now - kpLastStateChangeTime) >= KP_DEBOUNCE_MILLIS)
	     && (thisState != kpLastStableState)) {
    
    /*
     * The keyboard state has settled, and is different from the
     * last stable state. Remember the current stable state.
     */
    
    kpLastStableState = thisState;
    
    /* If the current state is not ROLLOVER or NOKEY, return it */
    
    if (thisState < KP_ROLLOVER) {
      return thisState;
    }
  }

  /*
   * If the code gets here, either
   *	- no key is pressed
   *    - multiple keys are pressed simultaneously (key rollover)
   *	- the keyboard state is not stable (key bounce)
   * In all these cases, indicate to the caller that there is no new
   * input from the keypad.
   */

  return KP_NOKEY;
}
 
/*---------------------------------------------------------------------------*
 *                                                                           *
 *				DISPLAY DEFINITIONS			     *
 *                                                                           *
 *---------------------------------------------------------------------------*/

/* CONSTANTS */

/* Pin assignments for control pins */

#define DISP_REG_SELECT_PIN	13
#define DISP_ENABLE_PIN		12
#define DISP_BRIGHTNESS_PIN	11   /* must be PWM-capable */

/* TIME INTERVALS */

#define DISP_SETUP_TIME_MICROS		2
  				/* Amount of time to allow outputs to settle
				 * before pulsing ENABLE */
#define DISP_ENABLE_DURATION_MICROS     1
				/* Duration of the 'enable' pulse */
#define DISP_MIN_ENABLE_INTERVAL_MICROS	42 /* 42 */
				/* Minimum wait time between 'enable' pulses */
#define DISP_CLEAR_DELAY_MICROS		1530 /* 1530 */
				/* Minimum wait time after clearing display */

/* PROGRAM MEMORY */

/* Pin assignments for the data nybble, LSB first */

static const prog_uchar dispDataPinsPGM[4] PROGMEM = { 10, 9, 8, 7 };

/* LED duty cycles corresponding to the 12 display brightness settings */

static const prog_uchar dispBrightness[12] PROGMEM = {
  0,    1,   6,   15,  28,  45,
  67,  94,  127, 164, 207, 255
};

/* CGRAM content. */

static const prog_uchar dispCGRAM[] PROGMEM = {
  0x01, 0x06, 0x0c, 0x06, 0x01, 0x0c, 0x06, 0x01,
				/* 0x08 - less than or equal */
  0x08, 0x08, 0x08, 0x08, 0x09, 0x02, 0x04, 0x00,
				/* 0x01 - vulgar fraction numerator 1 */
  0x18, 0x04, 0x18, 0x10, 0x1D, 0x02, 0x04, 0x00,
				/* 0x02 - vulgar fraction numerator 2 */
  0x18, 0x04, 0x18, 0x04, 0x19, 0x02, 0x04, 0x00,
				/* 0x03 - vulgar fraction numerator 3 */
  0x04, 0x08, 0x16, 0x01, 0x06, 0x01, 0x06, 0x00,
				/* 0x04 - vulgar fraction denominator 3 */
  0x04, 0x08, 0x17, 0x04, 0x06, 0x01, 0x06, 0x00,
				/* 0x05 - vulgar fraction denominator 5 */
  0x00, 0x00, 0x00, 0x11, 0x0a, 0x04, 0x00, 0x00,
				/* 0x06 - inverted caret */
  0x04, 0x08, 0x17, 0x01, 0x02, 0x02, 0x04, 0x00
				/* 0x07 - vulgar fraction denominator 7 */
};

/* DDRAM addresses of the starts of the lines on the display */

static const prog_uchar dispLineAddr[4] PROGMEM = {
  0x00, 0x40, 0x14, 0x54
};


/* RAM */

byte dispLastEnablePulseRecent;
				/* TRUE if the last enable pulse was
				 * close to the current time */
unsigned long dispLastEnablePulseMicros;
				/* Time in microseconds of the last
				 * 'enable' pulse */
 
/*
 *-----------------------------------------------------------------------------
 *
 * dispPulseEnable --
 *
 *	Pulses the 'enable' pin to clock data into the display
 *
 * Results:
 *	None.
 *
 * Side effects: 
 *	If less than DISP_MIN_ENABLE_INTERVAL_MICROS has elapsed since
 *	the last pulse, the program delays until
 *	DISP_MIN_ENABLE_INTERVAL_MICROS has elapsed.  The 'enable' pin
 *	is raised for DISP_ENABLE_DURATION_MICROS, and then lowered
 *	again.
 *
 *-----------------------------------------------------------------------------
 */

void
dispPulseEnable(void)
{
  unsigned long howlong;

  /* Wait until it's safe to issue the next pulse */

  delayMicroseconds(DISP_SETUP_TIME_MICROS);
  if (dispLastEnablePulseRecent) {
    while ((howlong = micros() - dispLastEnablePulseMicros)
	   < DISP_MIN_ENABLE_INTERVAL_MICROS) {
      delayMicroseconds(DISP_MIN_ENABLE_INTERVAL_MICROS - howlong);
    }
  }
  digitalWrite(DISP_ENABLE_PIN, HIGH);
  delayMicroseconds(DISP_ENABLE_DURATION_MICROS);
  digitalWrite(DISP_ENABLE_PIN, LOW);
  dispLastEnablePulseRecent = 1;
  dispLastEnablePulseMicros = micros();
}
 
/*
 *-----------------------------------------------------------------------------
 *
 * dispWriteNybble --
 *
 *	Write one nybble to the display.
 *
 * Parameters:
 *	nybble - Nybble to write.
 *
 * Side effects:
 *	Nybble is written to the display, and the enable line is pulsed.
 *
 *-----------------------------------------------------------------------------
 */

void
dispWriteNybble(byte nybble)
{
  int i;
  for (i = 0; i < 4; ++i) {
    digitalWrite(pgm_read_byte(dispDataPinsPGM+i), (nybble & 0x01));
    nybble >>= 1;
  }
  dispPulseEnable();
}
 
/*
 *-----------------------------------------------------------------------------
 *
 * dispWriteByte --
 *
 *	Write one byte to the display.
 *
 * Parameters:
 *	b - Byte to write.
 *
 * Side effects:
 *	Byte is written to the display, and the enable line is pulsed twice.
 *
 * Note that the byte must be written with the most significant nybble
 * first.  Since writing a nybble ignores the more significant bits,
 * there's no need to mask the less significant nybble.
 *
 *-----------------------------------------------------------------------------
 */

void dispWriteByte(byte b) {
  dispWriteNybble(b >> 4);
  dispWriteNybble(b /* & 0x0f */);
}
 
/*
 *-----------------------------------------------------------------------------
 *
 * dispWriteStringPGM --
 *
 *	Write a program-memory string to the display
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	String is written to the display starting from the current
 *	cursor position.
 *
 *-----------------------------------------------------------------------------
 */

void
dispWriteStringPGM(
    const char* str)	     /* Address of string in program memory */
{
  byte c;
  while ((c = pgm_read_byte(str++)) != '\0') {
    dispWriteByte(c);
  }
}
 
/*
 *-----------------------------------------------------------------------------
 *
 * dispInit --
 *
 *	Initialize the display
 *
 * Side effects:
 *	The display's data and control pins are set to 'output'. The
 *	display brightness is set to any saved setting.
 *  
 *-----------------------------------------------------------------------------
 */

void
dispInit() {
  unsigned i;

  /* Set the pins to 'output' */
  
  pinMode(DISP_ENABLE_PIN, OUTPUT);
  digitalWrite(DISP_ENABLE_PIN, LOW);
  pinMode(DISP_REG_SELECT_PIN, OUTPUT);
  digitalWrite(DISP_REG_SELECT_PIN, HIGH);
  for (i = 0; i < 4; ++i) {
    byte pin = pgm_read_byte(dispDataPinsPGM+i); 
    pinMode(pin, OUTPUT);
    digitalWrite(pin, LOW);
  }
  pinMode(DISP_BRIGHTNESS_PIN, OUTPUT);
  
  /* Restore display brightness */

  analogWrite(DISP_BRIGHTNESS_PIN,
	      eeprom_read_byte((unsigned char*) EEPROM_DISP_BRIGHTNESS));

  /* Wind the 'enable' clock */

  dispLastEnablePulseRecent = 0;
  dispLastEnablePulseMicros = 0;

  /* 
   * Put the display into 8-bit bus mode. The four least significant bits
   * will be hardwired 0.  If the display is in 4-bit bus mode, the
   * two 'dispWriteNybble' calls here will set it to 8-bit, and set 
   * it to single-line mode and a 5x8 font.  If the display is in 8-bit
   * bus mode, the same settings will simply be executed twice.
   */

  digitalWrite(DISP_REG_SELECT_PIN, LOW); /* Command mode */

  /*
   * Next command (set 8-bit mode and delay 5 milliseconds)
   * is a bit of 'cargo cult': nobody is quite sure why it's needed,
   * but the display doesn't quite initialize without it.
   */
   
  dispWriteNybble(0x3); delay(5);
  
  dispWriteNybble(0x3);		  /* 8-bit bus, 1 line, 5x8 */
  dispWriteNybble(0x3);		  /* Either an ignored nybble,
				   * or a repeat of the above. */

  /*
   * Now that the display is in 8-bit bus mode, it can be commanded into
   * 4-bit bus mode predictably.  The '1 line' and '5x8 font' attributes
   * are wired in the hardware in the least significant bits, so this command
   * also sets them up.
   */

  dispWriteNybble(0x02);	/* 4-bit bus, 1 line, 5x8 */

  /*
   * Finish the initialization, a byte at a time.
   */

  dispWriteByte(0x06);		/* Autoincrement cursor, no display shift */
  dispWriteByte(0x0C);		/* Display enable, invisible cursor, no blink */
  dispWriteByte(0x01);		/* Clear the display and home the cursor */
  delayMicroseconds(DISP_CLEAR_DELAY_MICROS);

  /* Program the CGRAM */

  dispWriteByte(0x40);		/* Select start of CGRAM */
  digitalWrite(DISP_REG_SELECT_PIN, HIGH);
				/* Back to data mode */
  for (i = 0; i < sizeof(dispCGRAM); ++i) {
    dispWriteByte(pgm_read_byte(dispCGRAM+i));
  }
  digitalWrite(DISP_REG_SELECT_PIN, LOW);
  dispWriteByte(0x80);		/* Home cursor once more */
  digitalWrite(DISP_REG_SELECT_PIN, HIGH);
				/* Back to data mode */

}
 
/*
 *-----------------------------------------------------------------------------
 *
 * dispSetBrightness --
 *
 *	Sets the brightness of the display.
 *
 * Parameters:
 *	bright - Number from 0 to 11 of how bright the display should be
 *
 * Results:
 *	Sets the display brightness and stores the setting in EEPROM so
 *	that it can be set at the next startup.
 *
 *-----------------------------------------------------------------------------
 */

void
dispSetBrightness(int bright)
{
  byte duty;			/* Duty cycle for the PWM */
  duty = pgm_read_byte(dispBrightness+bright);
  analogWrite(DISP_BRIGHTNESS_PIN, duty);
  eeprom_write_byte((unsigned char*) EEPROM_DISP_BRIGHTNESS, duty);
}
 
/*
 *-----------------------------------------------------------------------------
 *
 * dispMoveTo --
 *
 *	Moves the display cursor
 *
 * Side effects:
 *	Moves the display cursor
 *
 *-----------------------------------------------------------------------------
 */

void
dispMoveTo(
    int row,			/* Row number (0-3) */
    int col)			/* Column number (0-19) */
{
  
  digitalWrite(DISP_REG_SELECT_PIN, LOW);
  dispWriteByte(0x80 + pgm_read_byte(dispLineAddr + row) + col);
  digitalWrite(DISP_REG_SELECT_PIN, HIGH);

}
 
/*
 *-----------------------------------------------------------------------------
 *
 * dispClear --
 *
 *	Clear the display
 *
 *-----------------------------------------------------------------------------
 */

void
dispClear() {
  digitalWrite(DISP_REG_SELECT_PIN, LOW);
  dispWriteByte(0x01);		/* Clear display */
  digitalWrite(DISP_REG_SELECT_PIN, HIGH);
  delayMicroseconds(DISP_CLEAR_DELAY_MICROS);
};
 
void 
setup() {

  kpInit();			/* Initialize the keypad */

  dispInit();			/* Initialize the display */

#if 0
  Serial.begin(9600);		/* Initialize serial comms with the 
				 * programmer host */
  Serial.println("starting");
#endif
  dispMoveTo(0, 0); dispWriteStringPGM(PSTR("1\x01\x07\x08" "1\x03\x05\x08" "2\x02\x04 up^ dn\x06"));
  dispMoveTo(1, 7); dispWriteStringPGM(PSTR("Hello,"));
  dispMoveTo(2, 7); dispWriteStringPGM(PSTR("World!"));
  dispMoveTo(3, 0);
  
}

void
loop() {
  
  byte state;
  
  state = kpPoll();
  if (state != KP_NOKEY) {
#if 0
    Serial.print("Button: <");
    Serial.print((int)state, HEX);
    Serial.print(">");
    Serial.print((int) pgm_read_byte(brightness+state));
    Serial.println("!");
#endif
    dispMoveTo(3,0);
    dispWriteStringPGM(PSTR("Brightness = "));
    dispWriteByte(state/10 ? '1' : ' ');
    dispWriteByte(state%10 + '0');
    dispSetBrightness(state);
  }
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 2
 * End:
 */