-
Notifications
You must be signed in to change notification settings - Fork 0
/
1V0_20200709_EEPROM_ESP8266_SERIAL.ino
4955 lines (4588 loc) · 180 KB
/
1V0_20200709_EEPROM_ESP8266_SERIAL.ino
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
/* Trying again with EEPROM... */
/* 1V0 */
/* - Pronounced as the name "Ivo", in honour of my beloved father. */
/* This program is an "interpreted high-level quasi-assembler" for Arduino */
/* and similarly "low-powered" devices. It provides a virtual machine which */
/* should look similar on all devices. Depending on the configuration, it */
/* will typically be able to use at most about 64K RAM, but may operate in */
/* full capacity already with about 16KB. It present the user with a */
/* facility to enter programming steps and a pair of two addresses on which */
/* the entered operation is to be performed. While the reference */
/* implementation may use strings, in fact, all I/O should be convertible */
/* to just 12 symbols: numbers 0-9 and two special signs, e.g. * and #, */
/* which allows the use of the system also on old-fashioned mobile phones: */
/* all operations and all data should be possible to enter just in this */
/* simple fashion. The system is conceived to store instructions in EEPROM */
/* (or flash, for ARM) and data in SRAM, allowing non-volatile storage for */
/* the laboriously constructed set of instructions.
/* Every instruction operates on two addresses and is expressed in 32 bit; */
/* two designs thereby appear most sensible: */
/* aaaaaa bbbbbbbbbbbbb ccccccccccccc - 6bit instruction + 2x 13bit data */
/* aaaaaaaa bbbbbbbbbbbb cccccccccccc - 8bit instruction + 2x 12bit data */
/* Instructions 0-62 have been implemented, leaving instruction 63 reserved */
/* should the need arise to give it a final instruction e.g. for GPIO */
/* operations and thelike. */
/* For the ATTINY85, I assume I will have only a handful instructions, <17, */
/* as the poor thing does not have even enough FLASH memory to accomodate */
/* anything fancier! Thus, the instruction set will be reduced - I am */
/* presently considering making it NOOP, JUMP, IADR, SADR, SVAL, IVAS, */
/* PLUS, MINS, MULS, DIVS, POXY, LOXY, COPY, SUMR and OUTP -- where OUTP */
/* may actually be supplanted by a user function, if so desired. 6bit data */
/* addresses will be used - which really gives you 64 floats in 256 bytes */
/* or 64 doubles in 512 bytes, and I do not think its SRAM will allow */
/* anything further. This condenses instructions to 16bit, i.e. 256 in the */
/* available EEPROM. Looks like I will have to cut corners for UNO, too. */
/* The system has two parts: a sort of REPL for interaction with the user */
/* right after startup, and also offers the facility to launch composed */
/* sets of instructions to be interpreted in a sort of "virtual machine". */
/* All operation is geared towards handling floating point values. Nobody */
/* "in real life" really cares all that much about integers. Already it can */
/* be convincingly used as a sort of "scientific calculator", but this is */
/* really just a gross oversimplification of the possibilities it offers, */
/* as entire programs can be composed and run. */
/* All of this really follows the "philosophy of a spreadsheet": */
/* where a user can solve rather complex problems by immediate intervention */
/* or with few commands, each command being quite complex in itself. This */
/* is in contrast to the "high art of programming", where a user solves */
/* arbitrarily complex tasks by chaining simple fragments, each in itself */
/* quite incapable. The difference is "programming" requires the user to */
/* undertake a "leap of faith", whereas users might simply need solutions. */
/* My design idea is that I do not think "variables" are sensible to work */
/* with, but rather, I am attempting to operate on "data fields". Allowing */
/* involved operations with floats or more, I can get much faster practical */
/* results than by fiddling with integers variable by variable. Complex */
/* operations better utilise the chip memory, too, and make "flash" usable */
/* for things for which otherwise "SRAM" would be needed - and flash it is */
/* that is plentiful. The functions should allow it to work like a sort of */
/* "microcontroller spreadsheet" where operations are done on entire fields */
/* of cells. Shortening the needed operations with unfamiliar code should */
/* also be more "pleasant" for the programmer. Using flash vs. SRAM thus */
/* it becomes possible to "offload" some computational complexity from SRAM */
/* to "pre-fixated" memory - compared to ancient computers, this is a sort */
/* of "wiring" rather than "keeping instructions in memory". Personally, */
/* this let me understand why old computers were CISC: nothing you do is */
/* very obvious, so whatever you do - better do it with fewer instructions. */
/* This makes an "assembler" more "intelligible" than a long chain of */
/* cryptic minimal operations, like bit-negations and additions to form a */
/* subtraction or thelike, which make no immediate sense in a listing. */
/* I assume people PARTICULARLY hate "looping in a strange language", */
/* and secondly, "conditions in a strange language". "Range" operations */
/* seek to ameliorate that. Each "range" should be "padded" by an address */
/* "below" it and an address "above" it for all range operations to work, */
/* as these locations can be used to facilitate the range operation. */
/* I had enough of watching "Arduino projects" take a perfect little */
/* computer and turn it into a piece of junk, executing just one or two */
/* "if-then"-s. */
/* Data Address 0 and Command Address 0 are reserved. */
/* A command with address 0 means "immediate execution". */
/* The system does not prefer "immediate failure", but tries to */
/* "tolerate bugs". For complex programs, this is a dangerous design */
/* decision, as they will still "run", but buggily. - The advantage is to */
/* make it easy "to run anything at all", so some tolerance is built-in... */
/* crashes are still possible, though. */
/* A programmed space with jumps to exec0 has a similar effect to a */
/* "file system" with "different programs". The user decides to which */
/* executable section to "jump to", and the "returns" let him have more */
/* than one composition of logical steps on a single computing device. */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// #include <string.h>
#include <EEPROM.h>
/* These DATAMEM and EXECMEM hold data and instructions, respectively. */
#define DATAMEM 1801
/* I.e. x4 using 7204 bytes of RAM */
#define EXECMEM 1001
/* I.e. x4 using 4004 bytes of EEPROM, leaving you with 92 bytes or 23 shifts */
/* If any "location 0" lasts you a year or two, you should be good for some */
/* 20 years or so of usage. Yeah, go ahead, I dare you! */
#define INSTRUCTION_BYTE_LENGTH 4
#define ZERO_OFFSET 4
/* This would serve to handle wear of the EEPROM. Should be divisible by 4. */
/* It is here totally useful that I can start instructions way behind instruction #1, e.g. at 11 or so. */
/* This way, they can "stay where they are", even if I have to re-flash and change position 0. */
/* All jumps would have to be updated, of course. */
/* If I then jump to the first instruction, still everything will be executed as it should be, skipping 0s along the way. */
#define TOLERANCE 0.0001
/* #define PI M_PI */
/* Is defined already. */
/* If M_PI is unavailable, make it 3.14159265358979323846 or 355/113 or */
/* simply 3 "for sufficiently large values of 3" */
float datum[DATAMEM];
// unsigned long instruction[EXECMEM];
unsigned long instr = 0;
/* instruction format: */
/* operator, data address 1 (=result), data address 2 */
/* every operator is at a command address. */
int cmdadr = 0;
unsigned int opr = 0;
unsigned int datadr1 = 0;
unsigned int datadr2 = 0;
unsigned long xx;
unsigned long yy;
unsigned long zz;
int pc = 0;
int h = 0; /* beware: h & k are used for "transcendental" instructions ... */
int i = 0; /* i & j are auxiliary looping variables */
int j = 0;
int k = 0; /* ... so ZERO THEM OUT after non-transcendental usage */
float f = 0.0; /* f & g are auxiliary floating point temporary storage */
double g = 0.0;
char cmnt; /* does NOTHING, serves just as comment facility within terminal */
short tracer = 0;
unsigned int runlimit = 0;
/* If this is 0, run unlimited, else break when it becomes 1. */
/* This is a sort of "safety", to prevent infinite looping. */
/* For scientific float printing. */
double x = 0.0;
char bufr[20];
/* needed for I/O */
long p;
unsigned long pp;
/* double q; */
int readint() {
p = 0;
while (!Serial.available()) { yield(); }
p = Serial.parseInt();
// Serial.print("");
Serial.print(p);
Serial.println();
Serial.flush();
while ((cmnt = Serial.read()) != '\n' && cmnt != EOF ) { yield(); }
return p;
}
unsigned long readunsignedlong() {
pp = 0;
while (!Serial.available()) { yield(); }
pp = Serial.parseInt();
// Serial.print("");
Serial.print(pp);
Serial.println();
Serial.flush();
while ((cmnt = Serial.read()) != '\n' && cmnt != EOF ) { yield(); }
return pp;
}
/*
float q; // THIS TOOK UP MUCH MORE SPACE THAN ACTUALLY COPYING THE STUFF AROUND!
float readfloat() {
q = 0.0;
while (!Serial.available()) {}
q = atof((Serial.readString()).c_str());
Serial.print("");
Serial.printf("%+E", datum[datadr1]);
Serial.println();
Serial.flush();
return q;
}
*/
/* Print the mnemonic - now in a separate function to tak up less space: */
/* NOT IMPLEMENTED INSTRUCTIONS GET AN ASTERISK. */
void printopr() {
if (opr == 0) {
Serial.print(F("NOOP"));
} else if (opr == 1) {
Serial.print(F("JUMP"));
} else if (opr == 2) {
Serial.print(F("IADR"));
} else if (opr == 3) {
Serial.print(F("OUTP"));
} else if (opr == 4) {
Serial.print(F("INPT"));
} else if (opr == 5) {
Serial.print(F("SADR"));
} else if (opr == 6) {
Serial.print(F("SVAL"));
} else if (opr == 7) {
Serial.print(F("IAAS"));
} else if (opr == 8) {
Serial.print(F("IVAS"));
} else if (opr == 9) {
Serial.print(F("PLUS"));
} else if (opr == 10) {
Serial.print(F("MINS"));
} else if (opr == 11) {
Serial.print(F("MULS"));
} else if (opr == 12) {
Serial.print(F("DIVS"));
} else if (opr == 13) {
Serial.print(F("POXY"));
} else if (opr == 14) {
Serial.print(F("LOXY"));
} else if (opr == 15) {
Serial.print(F("IFRA"));
} else if (opr == 16) {
Serial.print(F("REMN"));
} else if (opr == 17) {
Serial.print(F("AMNT"));
} else if (opr == 18) {
Serial.print(F("PERD"));
} else if (opr == 19) {
Serial.print(F("PCNT"));
} else if (opr == 20) {
Serial.print(F("SWAP"));
} else if (opr == 21) {
Serial.print(F("FACT"));
} else if (opr == 22) {
Serial.print(F("COPY"));
} else if (opr == 23) {
Serial.print(F("FRIS"));
} else if (opr == 24) {
Serial.print(F("MNMX"));
} else if (opr == 25) {
Serial.print(F("SORT"));
} else if (opr == 26) {
Serial.print(F("CORS"));
} else if (opr == 27) {
Serial.print(F("TURN"));
} else if (opr == 28) {
Serial.print(F("SUMR"));
} else if (opr == 29) {
Serial.print(F("SUSQ"));
} else if (opr == 30) {
Serial.print(F("IXTH"));
} else if (opr == 31) {
Serial.print(F("ABSR"));
} else if (opr == 32) {
Serial.print(F("SQRT"));
} else if (opr == 33) {
Serial.print(F("SQUA"));
} else if (opr == 34) {
Serial.print(F("CBRT"));
} else if (opr == 35) {
Serial.print(F("CUBE"));
} else if (opr == 36) {
Serial.print(F("LNRN"));
} else if (opr == 37) {
Serial.print(F("EXPR"));
} else if (opr == 38) {
Serial.print(F("RADE"));
} else if (opr == 39) {
Serial.print(F("DERA"));
} else if (opr == 40) {
Serial.print(F("SIND"));
} else if (opr == 41) {
Serial.print(F("COSD"));
} else if (opr == 42) {
Serial.print(F("TAND"));
} else if (opr == 43) {
Serial.print(F("ASND"));
} else if (opr == 44) {
Serial.print(F("ACSD"));
} else if (opr == 45) {
Serial.print(F("ATND"));
} else if (opr == 46) {
Serial.print(F("MSTD"));
} else if (opr == 47) {
Serial.print(F("ZERO"));
} else if (opr == 48) {
Serial.print(F("RAND"));
} else if (opr == 49) {
Serial.print(F("RUND"));
} else if (opr == 50) {
Serial.print(F("CEIL"));
} else if (opr == 51) {
Serial.print(F("TANH"));
} else if (opr == 52) {
Serial.print(F("DTNH"));
} else if (opr == 53) {
Serial.print(F("PLUR"));
} else if (opr == 54) {
Serial.print(F("MINR"));
} else if (opr == 55) {
Serial.print(F("MULR"));
} else if (opr == 56) {
Serial.print(F("DIVR"));
} else if (opr == 57) {
Serial.print(F("PLUN"));
} else if (opr == 58) {
Serial.print(F("MINN"));
} else if (opr == 59) {
Serial.print(F("MULN"));
} else if (opr == 60) {
Serial.print(F("DIVN"));
} else if (opr == 61) {
Serial.print(F("PROB"));
} else if (opr == 62) {
Serial.print(F("STDD"));
} else if (opr == 63) {
Serial.print(F("USER"));
} else {
/* Such an "else" MAY come into existence if not ALL possible */
/* instructions have been implemented, either theoretically, or */
/* practically - simply due to a lack of flash space. */
/* NOKO: NO Known Operation, and is skipped like NOOP. */
Serial.print(F("NOKO"));
}
Serial.print(F(" "));
}
void setup() {
EEPROM.begin(4096);
Serial.begin(9600);
while (!Serial.available()) {
yield();
; // Wait for serial port to connect. Needed for Leonardo only.
; // Yeah, but I HAVE Leonardos, too!
}
while ((cmnt = Serial.read()) != '\n' && cmnt != EOF ) { yield(); } /* Press Enter to start */
// Serial.println(); // Sometimes it prints something BEFORE it is ready -
// let it be at least something irrelevant.
/* Initialise commands to zero, which is no-op */
/*
for (i=0; i<EXECMEM; i++) { yield();
// EEPROM.put((INSTRUCTION_BYTE_LENGTH * (i - 0)) + ZERO_OFFSET, 0); // DEACTIVATED
// EEPROM IS BETTER CLEARED EXPLICITLY.
}
EEPROM.commit();
*/
/* Make data zero: */
for (i=0; i<DATAMEM; i++) { yield();
datum[i] = 0.0;
yield();
}
while (1) {
/* the highest level general loop which oscillates between command mode */
/* and general run mode */
yield();
Serial.println(F("INIT"));
/* HELP TEXT */
/* ------------------------ SETUP AND MAINTENANCE ------------------------ */
/* Read in instruction orders: */
/* - positive addresses: program for later execution; */
/* - zero-command-address: immediate execution according to operators; */
/* - specific negative addresses: specific immediate actions, not covered */
/* by the usual operators. */
/* This is a kind of REPL or user command interface prior to actually */
/* "running" any sort of longer program. Here, the user still has more */
/* immediate influence over the machine. */
runlimit = 5 * EXECMEM; /* set it with -9, 0 means infinite. */
/* It is better to have a "fool-proof" default than 0. */
/* This is particularly useful on systems where you cannot save your */
/* instructions - such as your desktop computer - and do not wish to lose */
/* everything just because the machine went into an infinite loop. */
while (1) {
yield();
pc = 0; /* safety: nuke the program counter and the immediate instruction */
EEPROM.put(ZERO_OFFSET, (long) 0);
EEPROM.commit();
/* Arduino: flush here any read-cache before reading instructions. */
/* First, get the command address - to determine is it +, 0 or - .*/
/* From there it will depend what action is to be undertaken. */
Serial.print(F("CMD ADR : "));
cmdadr = readint();
/* atof and atoi due to lack of scanf */
/* COMMAND SELECTION IN REPL MODE */
/* Positive or zero - give the details of the instruction to be executed. */
if (cmdadr >= 0) {
/* GIVE INSTRUCTION */
Serial.print(F("OPERATION: "));
opr = readint();
Serial.print(F("DATA ADR1: "));
datadr1 = readint();
Serial.print(F("DATA ADR2: "));
datadr2 = readint();
/* COMMENT OUT, IF DESIRED, FROM HERE ...: */
/* "Eat" the return from the last data address. */
cmnt = 0;
// while ((cmnt = Serial.read()) != '\n' && cmnt != EOF ) { yield(); }
// The above is somehow needed for AVR, but not for ESP8266
cmnt = 0;
/* The comment is optional - just press Enter if you do not want it. */
Serial.print(F("CMNT: ")); /* A comment is not saved, merely printed to the */
while (cmnt != '\n') { /* I/O dialogue. This itself may be useful. */
while (!Serial.available()) { yield(); }
cmnt = Serial.read();
Serial.print(cmnt);
if (cmnt == '#') { /* CANCEL */
opr = 0;
datadr1 = 0;
datadr2 = 0;
Serial.print(F("CANCELLED# "));
/* break */ /* no, not immediately, let the user explain why! */
}
}
// Serial.println();
/* ... TO HERE. */
if ((datadr1 >= DATAMEM) || (datadr2 >= DATAMEM) ||
(datadr1 < 0) || (datadr1 < 0) || (cmdadr >= EXECMEM)) {
Serial.print(F("CORR RANGE. DATA 0 ... "));
Serial.print((DATAMEM - 1));
Serial.print(F(", INSTR 0 ... "));
Serial.print((EXECMEM - 1));
Serial.println();
}
/* only positive data addresses - and 0 - are allowed: */
datadr1 = abs(datadr1);
datadr2 = abs(datadr2);
/* force everything to be within range: */
if (cmdadr >= EXECMEM) {
cmdadr = EXECMEM - 1;
}
if (datadr1 >= DATAMEM) {
datadr1 = DATAMEM - 1;
}
if (datadr2 >= DATAMEM) {
datadr2 = DATAMEM - 1;
}
/* Having read the instruction, compose it and save it. */
/* BIT SHIFTING DOES NOT WORK! I.e. it works IN THEORY, but in practice, it */
/* needs larger numbers - and then somehow the sketch explodes in size. */
/* instr = (opr << 26) | (datadr1 << 13) | datadr2; /* 63:26:13,or 255:24:12 */
/* So instead, just multiply: */
/* instr = ((unsigned long) (67108864 * opr)) + ((unsigned long) (8192 * datadr1)) + ((unsigned long) datadr2); */
xx = opr & 63;
yy = datadr1 & 8191;
zz = datadr2 & 8191;
instr = (xx << 26) | (yy << 13) | zz;
EEPROM.put((INSTRUCTION_BYTE_LENGTH * (cmdadr - 0)) + ZERO_OFFSET, instr);
EEPROM.commit();
/* Show again the user what was ordered, remind of possible correction. */
/* First, print the address and the instruction. */
/* Correction will only be possible if it is not for immediate execution. */
if (cmdadr < 1000) { Serial.print(F("0")); } /* "leading zeroes" */
if (cmdadr < 100) { Serial.print(F("0")); }
if (cmdadr < 10) { Serial.print(F("0")); }
Serial.print(cmdadr);
Serial.print(F(" "));
opr = (instr >> 26) & 63; //-----------------------------------------------------------------------------------------------------------------
printopr();
/* Then, print the data addresses and the numbers these point to, and, */
/* finally, the POSSIBLE numbers in turn these point to. */
/* If no data has been entered "batch wise" manually, these will */
/* all be zero. But if data has been entered manually, this may help the */
/* user to keep track whether the program is being setup as is supposed. */
/* Obviously, particularly each third number will often be meaningless, */
/* unless the specific instruction really relates that far */
if (datadr1 < 1000) { Serial.print(F("0")); } /* "leading zeroes" */
if (datadr1 < 100) { Serial.print(F("0")); }
if (datadr1 < 10) { Serial.print(F("0")); }
datadr1 = (instr >> 13) & 8191; //-------------------------------------------------------------------------------------------------------------
Serial.print(datadr1);
Serial.print(F(">"));
Serial.printf("%+E", datum[datadr1]);
/* scientific, big E, sign */
/* 4: E, not e, 1: plus or space, 2: of these plus and not space. */
if ((datum[datadr1] > -1 * DATAMEM) && (datum[datadr1] < DATAMEM)) {
j = abs(round(datum[datadr1]));
Serial.print(F(">"));
Serial.printf("%+E", datum[j]);
/* scientific, big E, sign */
/* 4: E, not e, 1: plus or space, 2: of these plus and not space. */
}
Serial.print(F(" "));
if (datadr2 < 1000) { Serial.print(F("0")); } /* "leading zeroes" */
if (datadr2 < 100) { Serial.print(F("0")); }
if (datadr2 < 10) { Serial.print(F("0")); }
datadr2 = instr & 8191; // -------------------------------------------------------------------------------------------------------------------
Serial.print(datadr2);
Serial.print(F(">"));
Serial.printf("%+E", datum[datadr2]);
/* scientific, big E, sign */
/* 4: E, not e, 1: plus or space, 2: of these plus and not space. */
if ((datum[datadr2] > -1 * DATAMEM) && (datum[datadr2] < DATAMEM)) {
j = abs(round(datum[datadr2]));
Serial.print(F(">"));
Serial.printf("%+E", datum[j]);
/* scientific, big E, sign */
/* 4: E, not e, 1: plus or space, 2: of these plus and not space. */
}
Serial.println();
if (cmdadr == 0) {
break; /* Go execute the command and return, unless it is a jump: */
/* a jump does not necessarily return, unless the execution */
/* either reaches the end of the instruction space - */
/* which it totally may without encountering any error, */
/* given that 0 is "no operation" and the memory is filled */
/* with it at the beginning - or the execution encounters */
/* a jump back to command address 0. Such jumps may indeed */
/* be used to "partition" the execution space into */
/* "different programs" if desired, which are separated from */
/* each other by "forced returns". In the immediate interpreter, */
/* the user may then decide which "entry point" to jump to */
/* in order to trigger execution at the desired section. */
}
} else if (cmdadr == -1) {
/* LIST INSTRUCTION RANGE */
/* Give an output of the entered instructions between two addresses, */
/* these addresses considered inclusive. Like "LIST" in BASIC, */
/* to "let you see what you did" or "what you are about to run". */
Serial.println(F("LIST INSTRUCTIONS"));
Serial.print(F("1ST CMD ADR: "));
datadr1 = readint();
/* atof and atoi due to lack of scanf */
Serial.print(F("2ND CMD ADR: "));
datadr2 = readint();
/* atof and atoi due to lack of scanf */
/* To save to time, if the listing is undesired, the user may */
/* terminate this by giving any negative address. */
if ((datadr1 < 0) || (datadr2 < 0)) { break; } /* fast eject */
/* If both addresses are 0, this is a shorthand for "print everything" */
if ((datadr1 == 0) && (datadr2 == 0)) {
datadr1 = 1;
datadr2 = EXECMEM - 1;
} else if (datadr2 == 0) {
/* A zero in the second address means "simply print one instruction" */
datadr2 = datadr1;
}
/* Normalise ranges: always print low to high */
if (datadr2 < datadr1) {
k = datadr2;
datadr2 = datadr1;
datadr1 = k;
}
h = datadr1;
k = datadr2;
if (h < 1) { h = 1;}
/* Don't print the "immediate execution" address. */
/* This should remind the user that this address is special, */
/* and that it should not be relied on for program execution. */
/* Moreover, help the user if an out-of-range-address has been given. */
if (k >= EXECMEM) { k = EXECMEM - 1; }
Serial.println(F("INSTRUCTIONS:"));
for (i = h; i <= k; i++) { yield();
EEPROM.get((INSTRUCTION_BYTE_LENGTH * (i - 0)) + ZERO_OFFSET, instr);
xx = (instr >> 26) & 63; /* 4095:255:24:12 or 8191:63:26:13 */
yy = (instr >> 13) & 8191;
zz = instr & 8191;
opr = xx;
datadr1 = yy;
datadr2 = zz;
if (i < 1000) { Serial.print(F("0")); } /* "leading zeroes" */
if (i < 100) { Serial.print(F("0")); }
if (i < 10) { Serial.print(F("0")); }
Serial.print(i);
Serial.print(F(" "));
if (opr < 10) { Serial.print(F("0")); }
Serial.print(opr);
Serial.print(F("="));
printopr();
if (datadr1 < 1000) { Serial.print(F("0")); } /* "leading zeroes". */
if (datadr1 < 100) { Serial.print(F("0")); }
if (datadr1 < 10) { Serial.print(F("0")); }
Serial.print(datadr1);
Serial.print(F(">"));
Serial.printf("%+E", datum[datadr1]);
/* scientific, big E, sign */
/* 4: E, not e, 1: plus or space, 2: of these plus and not space. */
if ((datum[datadr1] > -1 * DATAMEM) && (datum[datadr1] < DATAMEM)) {
j = abs(round(datum[datadr1]));
Serial.print(F(">"));
Serial.printf("%+E", datum[j]);
/* scientific, big E, sign */
/* 4: E, not e, 1: plus or space, 2: of these plus and not space. */
}
Serial.print(F(" "));
if (datadr2 < 1000) { Serial.print(F("0")); } /* "leading zeroes" */
if (datadr2 < 100) { Serial.print(F("0")); }
if (datadr2 < 10) { Serial.print(F("0")); }
Serial.print(datadr2);
Serial.print(F(">"));
Serial.printf("%+E", datum[datadr2]);
/* scientific, big E, sign */
/* 4: E, not e, 1: plus or space, 2: of these plus and not space. */
if ((datum[datadr2] > -1 * DATAMEM) && (datum[datadr2] < DATAMEM)) {
j = abs(round(datum[datadr2]));
Serial.print(F(">"));
Serial.printf("%+E", datum[j]);
/* scientific, big E, sign */
/* 4: E, not e, 1: plus or space, 2: of these plus and not space. */
}
Serial.println();
Serial.print(F("DECIMAL REPRESENTATION: "));
Serial.println(instr);
} /* End of the instruction print. */
/* Always zero out h & k, they have a special meaning otherwise: */
/* they signal that datadr1 and datadr2 should be replaced by them */
/* in the next instruction. */
h = 0;
k = 0;
j = 0;
i = 0;
} else if (cmdadr == -2) {
/* LIST DATA RANGE */
/* Same as above, just this time, for data, not instructions. */
/* Listing data is simpler, just the numbers are given */
/* - without the pointer interpretation attempted above. */
Serial.println(F("LIST DATA"));
Serial.print(F("1ST ADR: "));
datadr1 = readint();
Serial.print(F("2ND ADR: "));
datadr2 = readint();
if ((datadr1 < 0) || (datadr2 < 0)) { break; } /* fast eject */
if ((datadr1 == 0) && (datadr2 == 0)) {
/* print EVERYTHING */
datadr1 = 1;
datadr2 = DATAMEM - 1;
} else if (datadr2 == 0) {
datadr2 = datadr1;
}
/* Normalise ranges: low to high */
if (datadr2 < datadr1) {
k = datadr2;
datadr2 = datadr1;
datadr1 = k;
}
h = datadr1;
k = datadr2;
if (h < 1) { h = 1;} /* Do not print datum[0], it is reserved. */
if (h >= DATAMEM - 5) { h = DATAMEM - 6; } /* correct maximum range */
if (k >= DATAMEM - 5) { k = DATAMEM - 6; } /* correct maximum range */
Serial.println(F("DATA:"));
for (i = h; i <= k; i = i + 5) { /* Print as many as the terminal allows. */
if (i < 1000) { Serial.print(F("0")); } /* "leading zeroes" */
if (i < 100) { Serial.print(F("0")); }
if (i < 10) { Serial.print(F("0")); }
Serial.print(i);
Serial.print(F(" "));
Serial.printf("%+E", datum[i]);
/* scientific, big E, sign */
/* 4: E, not e, 1: plus or space, 2: of these plus and not space. */
Serial.print(F(" "));
Serial.printf("%+E", datum[i+1]);
Serial.print(F(" "));
Serial.printf("%+E", datum[i+2]);
Serial.print(F(" "));
Serial.printf("%+E", datum[i+3]);
Serial.print(F(" "));
Serial.printf("%+E", datum[i+4]);
Serial.println();
}
/* As always, zero out h & k, to not trigger data address replacement */
/* in the next instruction. */
h = 0;
k = 0;
i = 0;
} else if (cmdadr == -3) {
/* ENTER INSTRUCTIONS AS NUMBERS, UNTIL YOU SUBMIT A NEGATIVE INSTRUCTION */
/* It may happen, e.g. due to a print-out, that you already KNOW what */
/* decimal numbers the entire instructions will have, and that you */
/* want to enter them quickly. This is possible by this interaction. */
/* A negative instruction is impossible and terminates this interaction. */
/* In other words, this range is "flexible" - enter as many as you wish. */
/* The flexibility may be needed in case you want to enter a few 0s */
/* - i.e. "no operation". */
Serial.println(F("ENTER FULL INSTRUCTIONS AS DECIMALS, 67108863 TO END"));
Serial.print(F("START ADR: "));
cmdadr = readint(); /* From here upwards you enter instructions. */
if (cmdadr < 0) { break; } /* fast eject */
while (cmdadr < EXECMEM) {
yield();
if (pc < 1000) { Serial.print(F("0")); } /* "Leading zeroes. */
if (pc < 100) { Serial.print(F("0")); }
if (pc < 10) { Serial.print(F("0")); }
Serial.print(cmdadr);
Serial.print(": INSTRUCTION: ");
instr = readunsignedlong();
if (instr == 67108863) { /* eject */
break;
} else {
EEPROM.put((INSTRUCTION_BYTE_LENGTH * (cmdadr - 0)) + ZERO_OFFSET, instr);
EEPROM.commit();
}
cmdadr++;
}
} else if (cmdadr == -4) {
/* ENTER DATA AS FLOATS WITHIN A PRE-SPECIFIED RANGE */
/* Same as above, just this time, you enter data. */
/* What is different: obviously, negative numbers are totally allowed. */
/* So this time, the range is not flexible, but determined by two limits. */
Serial.println(F("ENTER DATA FLOATS"));
Serial.print(F("1ST ADR: "));
datadr1 = readint();
Serial.print(F("2ND ADR: "));
datadr2 = readint();
if ((datadr1 < 0) || (datadr2 < 0) || (datadr2 < datadr1)) { break; }
/* fast eject - and ONLY here; below, you are expected to give data */
while (datadr1 <= datadr2) {
yield();
if (datadr1 < 1000) { Serial.print(F("0")); } /* "Formatting with leading zeroes". */
if (datadr1 < 100) { Serial.print(F("0")); }
if (datadr1 < 10) { Serial.print(F("0")); }
Serial.print(datadr1);
Serial.print(F(": DATUM: "));
while (!Serial.available()) { yield(); }
f = atof((Serial.readString()).c_str());
Serial.print("");
Serial.printf("%+E", f);
Serial.println();
Serial.flush();
datum[datadr1] = f;
datadr1++;
}
} else if (cmdadr == -5) {
/* CLEAR INSTRUCTION RANGE */
/* Simply erase to zero an entire section of program space, i.e. to */
/* "no operation". */
Serial.println(F("CLEAR INSTRUCTION RANGE"));
Serial.print(F("1ST CMD ADR: "));
datadr1 = readint();
Serial.print(F("2ND CMD ADR: "));
datadr2 = readint();
if ((datadr1 < 0) || (datadr2 < 0)) { break; } /* fast eject */
if ((datadr1 == 0) && (datadr2 == 0)) {
/* clear EVERYTHING */
datadr1 = 1;
datadr2 = DATAMEM - 1;
} else if (datadr2 == 0) {
datadr2 = datadr1; /* clear one single instruction */
}
/* Normalise ranges: low to high */
if (datadr2 < datadr1) {
k = datadr2;
datadr2 = datadr1;
datadr1 = k;
}
h = datadr1;
k = datadr2;
if (h < 1) { h = 1;} /* Do not clear address 0, it is reserved. */
if (k >= EXECMEM) { k = EXECMEM - 1; } /* out-of-range-correction */
instr = 0; /* Just to make sure it "understands" the size as 32bit. */
for (i = h; i <= k; i++) { yield();
EEPROM.put((INSTRUCTION_BYTE_LENGTH * (i - 0)) + ZERO_OFFSET, instr);
EEPROM.commit();
}
Serial.println(F("INSTRUCTIONS CLEARED"));
h = 0;
k = 0;
i = 0;
} else if (cmdadr == -6) {
/* CLEAR DATA RANGE */
/* Same idea as above, just clear data (set to 0.0), not instructions. */
Serial.println(F("CLEAR DATA RANGE"));
Serial.print(F("1ST ADR: "));
datadr1 = readint();
Serial.print(F("2ND ADR: "));
datadr2 = readint();
if ((datadr1 < 0) || (datadr2 < 0)) { break; } /* fast eject */
if ((datadr1 == 0) && (datadr2 == 0)) {
/* clear EVERYTHING */
datadr1 = 1;
datadr2 = DATAMEM - 1;
} else if (datadr2 == 0) {
datadr2 = datadr1; /* i.e. clear only one datum */
}
/* Normalise ranges: low to high */
if (datadr2 < datadr1) {
k = datadr2;
datadr2 = datadr1;
datadr1 = k;
}
h = datadr1;
k = datadr2;
if (h < 1) { h = 1;}
if (k >= DATAMEM) { k = DATAMEM - 1; }
for (i = h; i <= k; i++) { yield();
datum[i] = 0.0;
}
Serial.println(F("DATA CLEARED"));
h = 0;
k = 0;
i = 0;
} else if (cmdadr == -7) {
/* TRACING: show - or do not - which command is executed, */
/* as the program runs. */
Serial.println(F("TRACE ON"));
tracer = 1;
} else if (cmdadr == -8) {
Serial.println(F("TRACE OFF"));
tracer = 0;
} else if (cmdadr == -9) {
Serial.print(F("SET RUNLIMIT, UP TO 32767, 0=INFINITE, TERMINATING WHEN 1: "));
runlimit = readint();
} else if (cmdadr == -10) {
/* OMITTED, NOT SENSIBLE - YOU CANNOT "EXIT" TO ANYWHERE. */
} /* end of command selection */
} /* end of setup and maintenance */
/* DEBUG */
/*
xx = (instr >> 26) & 63;
yy = (instr >> 13) & 8191;
zz = instr & 8191;
Serial.print("Mid-air: instr=");
Serial.print(instr);
Serial.print(" EEPROMLONG[0]=");
EEPROM.get(ZERO_OFFSET, p);
Serial.print(p);
Serial.print(" xx=");
Serial.print(xx);
Serial.print(" yy=");
Serial.print(yy);
Serial.print(" zz=");
Serial.print(zz);
Serial.print(" oprBefore=");
Serial.print(opr);
Serial.print(" d1Before=");
Serial.print(datadr1);
Serial.print(" d2Before=");
Serial.print(datadr2);
Serial.println();
opr = xx;
datadr1 = yy;
datadr2 = zz;
*/
/* EEPROM committed in mid-air - balance between "wear" and "safety" */
EEPROM.commit();
/* ----------------------- COMMAND EXECUTION PHASE ----------------------- */
/* All sails are set and you are now in the hands of Fate. The REPL has */
/* been exited, and whatever the Program Counter pc encounters is what */
/* shall be executed. If you enter into an infinite loop - tough luck! */
/* Actually, due to runlimit it should be not so bad now, either... */
/* As described, all commands are defined by an opcode operating on two */
/* addresses, whereby the first one also, generally, bears the result and */
/* is, roughly speaking, "more important". Often, the second address may be */
/* set to 0 as a short hand to signify that it does not matter. */
while (pc < EXECMEM) {
yield();
if (runlimit == 1) {
Serial.print(F("RUN EXHAUSTED PC="));
Serial.print(pc);
Serial.print(F("STOPPED"));
Serial.println();
break; /* ejection if the runlimit is over, pc printed for re-entry */
} else if (runlimit > 1) {
runlimit--;
if (((runlimit - 1) % 100) == 0) {
Serial.print(F("RUNLIMIT = "));
Serial.print(runlimit);
Serial.println();
/* Give an indication of progress without annoying too often. */
}
} /* and do nothing if runlimit is 0. */
/* Arduino: some sort of interruption due to reading the serial port */
/* here would be desirable, for instance when reading "***".*/
/* Either *** or ### are desirable, as they are similar to +++ */
/* in modems, and moreover, are likely available on a mobile phone - */
/* should this system ever be ported to a Java midlet. */
/* .... Not necessary any more, since I implemented the run limit. */
/* safety: erase the reserved datum[0]. */
/* Do NOT erase here instruction[0], or you will prevent all execution: */
/* whatever was there to be executed, would be annihilated! */