Subversion Repositories Krakow BASIC

Rev

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