Subversion Repositories Krakow BASIC

Rev

Rev 2 | Rev 4 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 nishi 1
/* $Id: basic.c 3 2024-09-03 11:21:02Z nishi $ */
2
 
3
/* Krakow BASIC - Multi-platform simple BASIC */
4
 
5
#if defined(PLATFORM_SHIROI)
6
 
7
#include "dri/text.h"
8
#include "dri/video.h"
9
#include "dri/math.h"
10
 
11
#include "mem.h"
12
#include "char.h"
13
#define PLATFORM "Shiroi"
14
#define NEWLINE "\r\n"
15
#define BREAKKEY
16
 
17
#elif defined(PLATFORM_UNIX) || defined(PLATFORM_WINDOWS) || defined(PLATFORM_ARDUINO)
18
 
19
#define mull(x, y) ((x) * (y))
20
#define divl(x, y) ((x) / (y))
21
#define killcursor(x)
22
#define cursor(x)
23
#define strnum atoi
24
 
25
#if defined(PLATFORM_WINDOWS)
26
#define PLATFORM "Windows"
27
#elif defined(PLATFORM_UNIX)
28
#define PLATFORM "Unix"
29
#elif defined(PLATFORM_ARDUINO)
30
#define PLATFORM "Arduino"
31
#define NEWLINE "\r\n"
32
#define BREAKKEY
33
#endif
34
 
35
#ifndef NEWLINE
36
#define NEWLINE "\n"
37
#endif
38
 
39
#include <stdio.h>
40
#include <stdlib.h>
41
#include <string.h>
42
#include <ctype.h>
43
#include <stdbool.h>
44
 
45
#if defined(__MINGW32__)
46
#include <conio.h>
47
#include <windows.h>
48
#elif defined(PLATFORM_ARDUINO)
49
#define BAUD 9600
50
#include <avr/io.h>
51
#include <util/delay.h>
52
#include <util/setbaud.h>
53
#define BUFFER_SIZE (1024)
54
#define LINE_BUFFER_SIZE (128)
55
#define LINES (32)
56
#undef putchar
57
#define putchar uart_putchar
58
#elif defined(PLATFORM_UNIX)
59
#include <termios.h>
60
#endif
61
 
62
#if defined(PLATFORM_ARDUINO)
63
int uart_putchar(char c) {
64
	while(!(UCSR0A & _BV(UDRE0)))
65
		;
66
	UDR0 = c;
67
	return 0;
68
}
69
 
70
void uart_init(void) {
71
	UBRR0H = UBRRH_VALUE;
72
	UBRR0L = UBRRL_VALUE;
73
 
74
	UCSR0B |= _BV(TXEN0) | _BV(RXEN0);
75
 
76
	UCSR0C |= _BV(UCSZ00) | _BV(UCSZ01);
77
}
78
#endif
79
 
80
char agetch(void) {
81
#if defined(PLATFORM_WINDOWS)
82
	int c;
83
rescan:
84
	c = _getch();
85
	if(c == '\r') return '\n';
86
	if(c == '\n') goto rescan;
87
	return c;
88
#elif defined(PLATFORM_UNIX)
89
	int c = getchar();
90
	if(c == EOF) return -1;
91
	if(c == '\r') return '\n';
92
#elif defined(PLATFORM_ARDUINO)
93
	int c;
94
rescan:
95
	if(!(UCSR0A & _BV(RXC0))) return 0;
96
	c = UDR0;
97
	if(c == '\r') return '\n';
98
	if(c == '\n') goto rescan;
99
	if(c == 3) return 1;
100
	return c;
101
#endif
102
	return c;
103
}
104
 
105
bool strcaseequ(const char* a, const char* b) { return strcasecmp(a, b) == 0; }
106
 
107
#if defined(PLATFORM_ARDUINO)
108
void putstr(const char* n) {
109
	int i;
110
	for(i = 0; n[i] != 0; i++) {
111
		uart_putchar(n[i]);
112
	}
113
}
114
 
115
void putnum(int n) {
116
	char buf[64];
117
	int incr = 63;
118
	buf[incr--] = 0;
119
	while(1) {
120
		buf[incr--] = (n % 10) + '0';
121
		n /= 10;
122
		if(n == 0) break;
123
	}
124
	putstr(buf + incr + 1);
125
}
126
#else
127
void putnum(int n) {
128
	printf("%d", n);
129
	fflush(stdout);
130
}
131
 
132
void putstr(const char* n) {
133
	printf("%s", n);
134
	fflush(stdout);
135
}
136
#endif
137
 
138
void change_color(int a) {
139
	int fg = (a >> 4) & 0xf;
140
	int bg = (a & 0xf);
141
	if(!(0 <= fg && fg <= 15)) return;
142
	if(!(0 <= bg && bg <= 15)) return;
143
	char color[2];
144
	color[1] = 0;
3 nishi 145
	if(bg < 8) {
2 nishi 146
		color[0] = bg + '0';
147
		putstr("\x1b[4");
3 nishi 148
	} else {
2 nishi 149
		color[0] = (bg - 8) + '0';
150
		putstr("\x1b[10");
151
	}
152
	putstr(color);
153
	putstr("m");
3 nishi 154
	if(fg < 8) {
2 nishi 155
		color[0] = fg + '0';
156
		putstr("\x1b[3");
3 nishi 157
	} else {
2 nishi 158
		color[0] = (fg - 8) + '0';
159
		putstr("\x1b[9");
160
	}
161
	putstr(color);
162
	putstr("m");
163
	putstr("\x1b[2J\x1b[1;1H");
164
}
165
 
166
void clear(void) {
167
#if defined(PLATFORM_WINDOWS)
168
	system("cls");
169
#elif defined(PLATFORM_UNIX) || defined(PLATFORM_ARDUINO)
170
	putstr("\x1b[0m\x1b[2J\x1b[1;1H");
171
#endif
172
}
173
 
174
void basic(void);
175
 
176
int main() {
177
#if defined(PLATFORM_WINDOWS)
178
	HANDLE winstdout = GetStdHandle(STD_OUTPUT_HANDLE);
179
	DWORD mode = 0;
180
	GetConsoleMode(winstdout, &mode);
181
	const DWORD origmode = mode;
182
	mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
183
	SetConsoleMode(winstdout, mode);
184
#elif defined(PLATFORM_UNIX)
185
	struct termios old, new;
186
	tcgetattr(0, &old);
187
	new = old;
188
	new.c_lflag &= ~(ECHO | ICANON);
189
	tcsetattr(0, TCSANOW, &new);
190
#elif defined(PLATFORM_ARDUINO)
191
	uart_init();
192
	DDRB |= _BV(DDB5);
193
	PORTB |= _BV(PORT5);
194
#endif
195
	basic();
196
#if defined(PLATFORM_WINDOWS)
197
	SetConsoleMode(winstdout, origmode);
198
#elif defined(PLATFORM_UNIX)
199
	tcsetattr(0, TCSANOW, &old);
200
#endif
201
}
202
 
203
#else
204
#error "Define PLATFORM_*"
205
#endif
206
 
207
#define VERSION "0.0"
208
 
209
#ifndef LINE_BUFFER_SIZE
210
#define LINE_BUFFER_SIZE (512)
211
#endif
212
 
213
#ifndef BUFFER_SIZE
214
#define BUFFER_SIZE (1024 * 24)
215
#endif
216
 
217
#ifndef LINES
218
#define LINES (1024)
219
#endif
220
 
221
unsigned char basicbuffer[BUFFER_SIZE];
222
char linebuf[LINE_BUFFER_SIZE];
223
 
224
int pexpr(char* expr, char* buffer, int* number) {
225
	char ownbuf[128];
226
	int i;
227
	for(i = 0; expr[i] != 0; i++) ownbuf[i] = expr[i];
228
	ownbuf[i] = 0;
229
	int start = 0;
230
	int br = 0;
231
	int result = 0;
232
	int stack[32];
233
	int sp = 0;
234
	char put = 0;
235
	for(i = 0; i < 32; i++) stack[i] = 0;
236
	for(i = 0;; i++) {
237
		if(ownbuf[i] == 0) {
238
			break;
239
		} else if('0' <= ownbuf[i] && ownbuf[i] <= '9') {
240
			stack[sp] *= 10;
241
			stack[sp] += ownbuf[i] - '0';
242
			put = 1;
243
		} else if(ownbuf[i] == '+' || ownbuf[i] == '-' || ownbuf[i] == '*' || ownbuf[i] == '/') {
244
			put = 0;
245
			if(sp < 2) {
246
				return -1;
247
			} else {
248
				int top = stack[--sp];
249
				int bottom = stack[--sp];
250
				int value = 0;
251
				if(ownbuf[i] == '+') {
252
					value = top + bottom;
253
				} else if(ownbuf[i] == '-') {
254
					value = bottom + top;
255
				} else if(ownbuf[i] == '*') {
256
					value = mull(top, bottom);
257
				} else if(ownbuf[i] == '/') {
258
					value = divl(bottom, top);
259
				}
260
				stack[sp++] = value;
261
			}
262
			stack[sp] = 0;
263
		} else if(ownbuf[i] == ' ' && put == 1) {
264
			stack[++sp] = 0;
265
		}
266
	}
267
	result = stack[0];
268
	*number = result;
269
	return 1;
270
}
271
 
272
/* arr gets sorted, arr2 index gets replaced */
273
void sort(int* arr, int* arr2, int size) {
274
	int i;
275
redo:
276
	for(i = 1; i < size; i++) {
277
		if(arr[i - 1] > arr[i]) {
278
			int tmp = arr[i];
279
			arr[i] = arr[i - 1];
280
			arr[i - 1] = tmp;
281
			tmp = arr2[i];
282
			arr2[i] = arr2[i - 1];
283
			arr2[i - 1] = tmp;
284
		}
285
	}
286
	for(i = 1; i < size; i++) {
287
		if(arr[i - 1] > arr[i]) {
288
			goto redo;
289
		}
290
	}
291
}
292
 
293
int run(char* cmd, int linenum, char num, int* lgoto) {
294
#ifdef BREAKKEY
295
	if(agetch() == 1) return -1;
296
#endif
297
	char line[LINE_BUFFER_SIZE];
298
	char rcmd[32];
299
	int i;
300
	if(lgoto != 0) *lgoto = 0;
301
	for(i = 0; cmd[i] != 0; i++) line[i] = cmd[i];
302
	line[i] = 0;
303
	rcmd[0] = 0;
304
	int incr = 0;
305
	for(i = 0;; i++) {
306
		if(line[i] == ' ' || line[i] == '\t' || line[i] == 0 || line[i] == '"') {
307
			break;
308
		} else {
309
			rcmd[incr++] = line[i];
310
			if(incr == 32) {
311
				putstr("! Command too long");
312
				if(linenum != -1) {
313
					putstr(" in line ");
314
					putnum(linenum);
315
				}
316
				putstr(NEWLINE);
317
				return 1;
318
			}
319
			rcmd[incr] = 0;
320
		}
321
	}
322
	char* arg = line + 1 + strlen(rcmd);
323
	if(strcaseequ(rcmd, "COLOR")) {
324
		int argc = 0;
325
		char* farg = 0;
326
		char* sarg = 0;
327
		if(arg[0] != 0) argc++;
328
		for(i = 0; arg[i] != 0; i++) {
329
			if(arg[i] == ',') {
330
				arg[i] = 0;
331
				farg = arg;
332
				sarg = arg + i + 1;
333
				for(; *sarg != 0 && (*sarg == '\t' || *sarg == ' '); sarg++)
334
					;
335
				argc++;
336
			}
337
		}
338
		if(argc != 2) {
339
			putstr("! Invalid argument");
340
			if(linenum != -1) {
341
				putstr(" in line ");
342
				putnum(linenum);
343
			}
344
			putstr(NEWLINE);
345
			return 1;
346
		}
347
		int bgcolor = 0;
348
		int fgcolor = 0;
349
		int ret0 = pexpr(farg, 0, &bgcolor);
350
		int ret1 = pexpr(sarg, 0, &fgcolor);
351
		if(ret0 == 0) {
352
			putstr("! Invalid argument");
353
			if(linenum != -1) {
354
				putstr(" in line ");
355
				putnum(linenum);
356
			}
357
			putstr(NEWLINE);
358
			return 1;
359
		} else if(ret0 == -1) {
360
			putstr("! Syntax error");
361
			if(linenum != -1) {
362
				putstr(" in line ");
363
				putnum(linenum);
364
			}
365
			putstr(NEWLINE);
366
			return 1;
367
		}
368
		if(ret1 == 1) {
369
			change_color((fgcolor << 4) | bgcolor);
370
		} else if(ret1 == 0) {
371
			putstr("! Invalid argument");
372
			if(linenum != -1) {
373
				putstr(" in line ");
374
				putnum(linenum);
375
			}
376
			putstr(NEWLINE);
377
			return 1;
378
		} else if(ret1 == -1) {
379
			putstr("! Syntax error");
380
			if(linenum != -1) {
381
				putstr(" in line ");
382
				putnum(linenum);
383
			}
384
			putstr(NEWLINE);
385
			return 1;
386
		}
387
	} else if(num == 0 && strcaseequ(rcmd, "LIST")) {
388
		int addr = BUFFER_SIZE - 1;
389
		int saddr = 0;
390
		int lbuf[LINES];
391
		int shift[LINES];
392
		int cnt = 0;
393
		while(1) {
394
			unsigned long ln = 0;
395
			for(i = 0; i < 4; i++) {
396
				ln >>= 8;
397
				ln |= (unsigned long)basicbuffer[addr--] << 24;
398
			}
399
			if(ln == 0) break;
400
			lbuf[cnt] = ln;
401
			shift[cnt++] = saddr;
402
			saddr += strlen(basicbuffer + saddr) + 1;
403
		}
404
		sort(lbuf, shift, cnt);
405
		int i;
406
		for(i = 0; i < cnt; i++) {
407
			putnum(lbuf[i]);
408
			putstr(" ");
409
			putstr(basicbuffer + shift[i]);
410
			putstr(NEWLINE);
411
		}
412
	} else if(num == 1 && strcaseequ(rcmd, "GOTO")) {
413
		int ret = pexpr(arg, 0, lgoto);
414
		if(ret == 0) {
415
			putstr("! Invalid argument");
416
			if(linenum != -1) {
417
				putstr(" in line ");
418
				putnum(linenum);
419
			}
420
			putstr(NEWLINE);
421
			return 1;
422
		} else if(ret == -1) {
423
			putstr("! Syntax error");
424
			if(linenum != -1) {
425
				putstr(" in line ");
426
				putnum(linenum);
427
			}
428
			putstr(NEWLINE);
429
			return 1;
430
		}
431
	} else if(num == 0 && strcaseequ(rcmd, "RUN")) {
432
		int addr = BUFFER_SIZE - 1;
433
		int saddr = 0;
434
		int lbuf[LINES];
435
		int shift[LINES];
436
		int cnt = 0;
437
		int gt;
438
		while(1) {
439
			unsigned long ln = 0;
440
			for(i = 0; i < 4; i++) {
441
				ln >>= 8;
442
				ln |= (unsigned long)basicbuffer[addr--] << 24;
443
			}
444
			if(ln == 0) break;
445
			lbuf[cnt] = ln;
446
			shift[cnt++] = saddr;
447
			saddr += strlen(basicbuffer + saddr) + 1;
448
		}
449
		sort(lbuf, shift, cnt);
450
		int i;
451
		for(i = 0; i < cnt; i++) {
452
			int ret = run(basicbuffer + shift[i], lbuf[i], 1, &gt);
453
			if(ret != 0) return ret;
454
			if(gt != 0) {
455
				char found = 0;
456
				for(i = 0; i < cnt; i++) {
457
					if(lbuf[i] == gt) {
458
						found = 1;
459
						break;
460
					}
461
				}
462
				if(found) {
463
					i--;
464
				} else {
465
					putstr("! GOTO no such line");
466
					if(linenum != -1) {
467
						putstr(" in line ");
468
						putnum(linenum);
469
					}
470
					putstr(NEWLINE);
471
					return 1;
472
				}
473
			}
474
		}
475
	} else {
476
		putstr("! Unknown command");
477
		if(linenum != -1) {
478
			putstr(" in line ");
479
			putnum(linenum);
480
		}
481
		putstr(NEWLINE);
482
		return 1;
483
	}
484
	return 0;
485
}
486
 
487
int execute(int linenum, char* cmd, char num) {
488
	int i;
489
	char line[LINE_BUFFER_SIZE];
490
	int incr = 0;
491
	char dq = 0;
492
	for(i = 0; cmd[i] != 0; i++) {
493
		if(cmd[i] == ' ' || cmd[i] == '\t') {
494
			if(!dq) {
495
				for(; cmd[i] != 0 && (cmd[i] == ' ' || cmd[i] == '\t'); i++)
496
					;
497
				i--;
498
			}
499
			line[incr++] = cmd[i];
500
		} else if(cmd[i] == '"') {
501
			line[incr++] = '"';
502
			dq = dq == 0 ? 1 : 0;
503
		} else {
504
			line[incr++] = dq == 0 ? toupper(cmd[i]) : cmd[i];
505
		}
506
	}
507
	line[incr] = 0;
508
	if(num == 0) {
509
		int ret = run(line, -1, 0, 0);
510
		putstr("Ready\r\n");
511
		return ret;
512
	} else {
513
		int addr = BUFFER_SIZE - 1;
514
		int i;
515
		int shf = 0;
516
		int cnt = 0;
517
		while(1) {
518
			unsigned long ln = 0;
519
			for(i = 0; i < 4; i++) {
520
				ln >>= 8;
521
				ln |= (unsigned long)basicbuffer[addr--] << 24;
522
			}
523
			cnt++;
524
			if(ln == linenum) shf = cnt;
525
			if(shf != 0) {
526
				addr += 4;
527
				for(i = 0; i < 4; i++) {
528
					basicbuffer[addr] = basicbuffer[addr - 4 * shf];
529
					addr--;
530
				}
531
				addr += 4;
532
				ln = 0;
533
				for(i = 0; i < 4; i++) {
534
					ln >>= 8;
535
					ln |= (unsigned long)basicbuffer[addr--] << 24;
536
				}
537
			}
538
			if(ln == 0) break;
539
		}
540
		if(line[0] != 0) {
541
			addr += 4;
542
			for(i = 0; i < 4; i++) {
543
				basicbuffer[addr--] = linenum & 0xff;
544
				linenum >>= 8;
545
			}
546
		}
547
		int len = 0;
548
		cnt = 0;
549
		while(1) {
550
			int slen = strlen(basicbuffer + len);
551
			if(slen == 0) break;
552
			len += slen + 1;
553
			cnt++;
554
		}
555
		if(line[0] != 0) {
556
			for(i = 0; line[i] != 0; i++) {
557
				basicbuffer[len + i] = line[i];
558
			}
559
			basicbuffer[len + i] = 0;
560
			basicbuffer[len + i + 1] = 0;
561
		}
562
		if(shf != 0) {
563
			cnt = 0;
564
			len = 0;
565
			while(1) {
566
				int slen = strlen(basicbuffer + len);
567
				if(slen == 0) break;
568
 
569
				len += slen + 1;
570
				cnt++;
571
 
572
				if(cnt == shf) {
573
					len -= slen + 1;
574
					int i;
575
					int nc = 0;
576
					for(i = len;; i++) {
577
						basicbuffer[i] = basicbuffer[i + slen + 1];
578
						if(basicbuffer[i] == 0) {
579
							nc++;
580
							if(nc == 2) break;
581
						} else {
582
							nc = 0;
583
						}
584
					}
585
					break;
586
				}
587
			}
588
		}
589
		return 0;
590
	}
591
}
592
 
593
void basic(void) {
594
	int i;
595
	clear();
596
 
597
#ifdef SMALL
598
	putstr("Krakow BASIC  Ver. ");
599
	putstr(VERSION);
600
	putstr(NEWLINE);
601
	putstr(NEWLINE);
602
#else
603
	putstr(PLATFORM);
604
	putstr("   Krakow BASIC V");
605
	putstr(VERSION);
606
	putstr("\r\n");
607
	putstr("Copyright 2024 by: Nishi.\r\n");
608
	putstr("                   penguin2233.");
609
	putstr(NEWLINE);
610
	putstr(NEWLINE);
611
	putstr(" Max ");
612
	putnum(LINE_BUFFER_SIZE);
613
	putstr(" characters per line");
614
	putstr(NEWLINE);
615
	putstr(" Max ");
616
	putnum(LINES);
617
	putstr(" lines");
618
	putstr(NEWLINE);
619
	putstr(" ");
620
#endif
621
	putnum(BUFFER_SIZE);
622
	putstr(" bytes free");
623
	putstr(NEWLINE);
624
	putstr(NEWLINE);
625
 
626
	for(i = 0; i < BUFFER_SIZE; i++) {
627
		basicbuffer[i] = 0;
628
	}
629
	putstr("Ready");
630
	putstr(NEWLINE);
631
 
632
	cursor();
633
	linebuf[0] = 0;
634
	int lineind = 0;
635
	while(1) {
636
		char c = agetch();
637
		if(c != 0) killcursor();
638
		if(c == 1) {
639
			putstr("Break");
640
			putstr(NEWLINE);
641
			lineind = 0;
642
		} else if(c == '\n') {
643
			linebuf[lineind] = 0;
644
			putstr(NEWLINE);
645
			if(lineind == 0) goto skip;
646
 
647
			int i;
648
			char num = 1;
649
 
650
			char* cmd = linebuf;
651
 
652
			for(i = 0; linebuf[i] != 0; i++) {
653
				if(linebuf[i] == ' ') {
654
					linebuf[i] = 0;
655
					cmd = linebuf + i + 1;
656
					break;
657
				} else if(!('0' <= linebuf[i] && linebuf[i] <= '9')) {
658
					num = 0;
659
					break;
660
				}
661
			}
662
 
663
			if(num == 1 && strnum(linebuf) == 0) {
664
				putstr("! Line number 0 is illegal");
665
				putstr(NEWLINE);
666
			} else {
667
				int ret = execute(num == 1 ? strnum(linebuf) : -1, (num == 1 && cmd == linebuf) ? "" : cmd, num);
668
				if(ret == -1) {
669
					putstr("! Break");
670
					putstr(NEWLINE);
671
				}
672
			}
673
 
674
		skip:
675
			lineind = 0;
676
		} else if(c == 0x8 || c == 0x7f) {
677
			if(lineind > 0) {
678
				putstr("\x08 \x08");
679
				linebuf[--lineind] = 0;
680
			}
681
		} else if(c == -1) {
682
			break;
683
		} else if(c != 0) {
684
			putchar(c);
685
			linebuf[lineind++] = c;
686
		}
687
		cursor();
688
	}
689
}