Skip to content

Commit edacf5d

Browse files
committed
gec: operator-lamp / sleep(ms) / rand built-ins + examples/blink.c
Add compiler intrinsics handled in gen_builtin_call(), placed before the stdio gate so they work without #include <stdio.h> (they ride on real CPU instructions and runtime scratch only): lon() light the OPER. CALL lamp (LON : 02 80, CI87 sets ALAM) loff() extinguish it (LOFF: 02 40, CI88 resets ALAM) sleep(ms) busy-wait n milliseconds (250 emulator cycles = 1 ms; a calibrated nested loop, inner spin count tuned to ~98%) rand() full-period 16-bit LCG (s = s*25173 + 13849, state __rseed seeded in crt0; no hardware shift, hence an LCG not xorshift) srand(seed) seed rand() gec has no `long`/`bool` type, so counts and returns are int. lol() was dropped: the OPER. CALL lamp FF (ALAM) is write-only to the program, so "read the lamp" is not faithful. examples/blink.c blinks the operator lamp on a ~500 ms (== 125000 instruction clock) cycle, tracking on/off in a variable since ALAM is unreadable. Verified via ./ge --trace cmds: CI87/CI88 fire at regular intervals. cc/test.sh: all end-to-end examples pass.
1 parent a60aeb3 commit edacf5d

2 files changed

Lines changed: 94 additions & 1 deletion

File tree

cc/examples/blink.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* blink.c — blink the operator-call (OPER. CALL) lamp at ~1 Hz.
3+
*
4+
* gec built-ins used (no header needed — they ride on real CPU instructions):
5+
* lon() light the operator-call lamp (LON : CI87 sets ALAM)
6+
* loff() extinguish it (LOFF: CI88 resets ALAM)
7+
* sleep(ms) busy-wait the given milliseconds (250 emulator cycles = 1 ms,
8+
* i.e. ~500 ms == 125000 instruction clocks at 4 us/cycle)
9+
*
10+
* The lamp flip-flop (ALAM) is write-only to the program, so the on/off state
11+
* is kept in a variable and toggled each pass. Runs forever — stop from the
12+
* console.
13+
*/
14+
int main()
15+
{
16+
int on;
17+
18+
on = 0;
19+
while (1) {
20+
sleep(500); /* ~500 ms == 125000 instruction clocks */
21+
if (on) {
22+
loff();
23+
on = 0;
24+
} else {
25+
lon();
26+
on = 1;
27+
}
28+
}
29+
return 0;
30+
}

cc/gec.c

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,67 @@ static void emit_check_io_ok(int Lfail)
700700

701701
static int gen_builtin_call(Node *n, int dst)
702702
{
703+
/* Console / lamp / timing / RNG built-ins. These need no peripheral channel
704+
* (they ride on real CPU instructions only), so they are handled before the
705+
* stdio gate below and work without #include <stdio.h>. gec has no `long`
706+
* or `bool` type, so counts and returns are plain `int`. */
707+
if (!strcmp(n->name, "lon")) { /* light the OPER. CALL lamp */
708+
EM("LON\n"); /* 02 80: CI87 sets ALAM */
709+
emit_word_imm(dst, 0);
710+
return 1;
711+
}
712+
if (!strcmp(n->name, "loff")) { /* extinguish it */
713+
EM("LOFF\n"); /* 02 40: CI88 resets ALAM */
714+
emit_word_imm(dst, 0);
715+
return 1;
716+
}
717+
if (!strcmp(n->name, "sleep")) { /* busy-wait n milliseconds */
718+
/* The emulator clock is 4 us/cycle (250 cycles = 1 ms). Spend ~250
719+
* cycles per ms with an outer (ms) loop around a fixed inner spin; the
720+
* inner count below is calibrated so each outer pass ~= 1 ms. */
721+
if (n->nargs != 1)
722+
die("sleep() expects 1 argument");
723+
int t = push2();
724+
gen_expr(n->args[0], t);
725+
int Lo = lbl(), Ld = lbl(), Li = lbl(), Lie = lbl();
726+
fprintf(out, "L%d:\tCMC 2, %d(5), __zero\n", Lo, t);
727+
EM("JC 0x20, L%d\n", Ld); /* ms == 0 -> done */
728+
EM("MVI 0, __acc\n");
729+
EM("MVI 9, __acc+1\n"); /* inner spins per ms (calibrated)*/
730+
fprintf(out, "L%d:\tCMC 2, __acc, __zero\n", Li);
731+
EM("JC 0x20, L%d\n", Lie);
732+
EM("SB 2,2, __acc+1, __one+1\n");
733+
EM("JU L%d\n", Li);
734+
fprintf(out, "L%d:\tSB 2,2, %d(5), __one+1\n", Lie, t + 1); /* ms -= 1 */
735+
EM("JU L%d\n", Lo);
736+
fprintf(out, "L%d:\n", Ld);
737+
emit_word_imm(dst, 0);
738+
return 1;
739+
}
740+
if (!strcmp(n->name, "rand")) { /* full-period 16-bit LCG */
741+
/* s = (s * 25173 + 13849) mod 2^16. No hardware shift, so a Lehmer LCG
742+
* (a-1 mult. of 4, c odd) gives a full 65536 period via __mul + AB. */
743+
EM("MVC 2, __a, __rseed\n");
744+
EM("MVI 0x62, __b\n");
745+
EM("MVI 0x55, __b+1\n"); /* 25173 = 0x6255 */
746+
EM("JRT 0xF0, __mul\n"); /* __rv = (s * 25173) mod 2^16 */
747+
EM("MVI 0x36, __acc\n");
748+
EM("MVI 0x19, __acc+1\n"); /* 13849 = 0x3619 */
749+
EM("MVC 2, __rseed, __rv\n");
750+
EM("AB 2,2, __rseed+1, __acc+1\n"); /* s += 13849 */
751+
EM("MVC 2, %d(5), __rseed\n", dst);
752+
return 1;
753+
}
754+
if (!strcmp(n->name, "srand")) { /* seed rand() */
755+
if (n->nargs != 1)
756+
die("srand() expects 1 argument");
757+
int t = push2();
758+
gen_expr(n->args[0], t);
759+
EM("MVC 2, __rseed, %d(5)\n", t);
760+
emit_word_imm(dst, 0);
761+
return 1;
762+
}
763+
703764
int pct = ge100('%');
704765
int spc = ge100(' ');
705766
int ch_c = ge100('c');
@@ -998,6 +1059,7 @@ static void emit_runtime(FILE *o) {
9981059
"__lr4 EQU 0x0046\n"
9991060
"__io_len EQU 0x0048\n"
10001061
"__io_ptr EQU 0x004A\n" /* saved pointer across putchar calls */
1062+
"__rseed EQU 0x004C\n" /* rand() LCG state (16-bit, seeded crt0) */
10011063
/* Code + globals live above 0x1000. With bit-15 absolute/modified
10021064
* addressing honored (gemu indexing micro-cycle), absolute code/data
10031065
* references are used verbatim and no longer alias the reloaded base
@@ -1007,7 +1069,8 @@ static void emit_runtime(FILE *o) {
10071069
"__start:\n"
10081070
"\tLA 5, 0x000(6)\n" /* FP = SP (R6 = 0x6000 by reset identity) */
10091071
"\tMVI 0, __one\n\tMVI 1, __one+1\n"
1010-
"\tMVI 0, __zero\n\tMVI 0, __zero+1\n");
1072+
"\tMVI 0, __zero\n\tMVI 0, __zero+1\n"
1073+
"\tMVI 0xAC, __rseed\n\tMVI 0xE1, __rseed+1\n"); /* nonzero rand() seed */
10111074
/* Seed the channel-2 output PER order block's buffer address. Only emit
10121075
* this when stdio.h is in use — the __io_* order block and addrmask only
10131076
* exist in that case (otherwise these are undefined symbols). */

0 commit comments

Comments
 (0)