2018-07-31 19:02:26 +00:00
|
|
|
#include "platform.h"
|
|
|
|
|
2018-07-28 05:30:44 +00:00
|
|
|
char *title = "Apple IIa";
|
|
|
|
unsigned char title_length = 9;
|
|
|
|
|
2018-07-31 19:48:29 +00:00
|
|
|
#define CURSOR_GLYPH 127
|
|
|
|
#define SCREEN_WIDTH 40
|
|
|
|
#define SCREEN_STRIDE (3*SCREEN_WIDTH + 8)
|
|
|
|
|
|
|
|
// Location of cursor in logical screen space.
|
|
|
|
unsigned int cursor_x = 0;
|
|
|
|
unsigned int cursor_y = 0;
|
|
|
|
// Whether the cursor is being displayed.
|
|
|
|
unsigned int showing_cursor = 0;
|
|
|
|
// Character at the cursor location.
|
|
|
|
unsigned char cursor_ch = 0;
|
2018-07-31 22:03:06 +00:00
|
|
|
unsigned char input_buffer[40];
|
|
|
|
int input_buffer_length = 0;
|
|
|
|
|
|
|
|
// Compiled binary.
|
2018-08-01 03:47:05 +00:00
|
|
|
char binary[128];
|
2018-07-31 22:03:06 +00:00
|
|
|
int binary_length = 0;
|
|
|
|
void (*binary_function)() = (void (*)()) binary;
|
2018-07-31 19:48:29 +00:00
|
|
|
|
2018-07-31 07:05:22 +00:00
|
|
|
/**
|
|
|
|
* Delay for a count of "t". 8000 is about one second.
|
|
|
|
*/
|
|
|
|
static void delay(int t) {
|
|
|
|
while (t >= 0) {
|
|
|
|
t--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-31 19:48:29 +00:00
|
|
|
/**
|
|
|
|
* Return the memory location of the cursor.
|
|
|
|
*/
|
|
|
|
static volatile unsigned char *cursor_pos() {
|
|
|
|
int block = cursor_y >> 3;
|
|
|
|
int line = cursor_y & 0x07;
|
|
|
|
|
|
|
|
return TEXT_PAGE1_BASE + line*SCREEN_STRIDE + block*SCREEN_WIDTH + cursor_x;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Shows the cursor. Safe to call if it's already showing.
|
|
|
|
*/
|
|
|
|
static void show_cursor() {
|
|
|
|
if (!showing_cursor) {
|
|
|
|
volatile unsigned char *pos = cursor_pos();
|
|
|
|
cursor_ch = *pos;
|
|
|
|
*pos = CURSOR_GLYPH | 0x80;
|
|
|
|
showing_cursor = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hides the cursor. Safe to call if it's not already shown.
|
|
|
|
*/
|
|
|
|
static void hide_cursor() {
|
|
|
|
if (showing_cursor) {
|
|
|
|
volatile unsigned char *pos = cursor_pos();
|
|
|
|
*pos = cursor_ch;
|
|
|
|
showing_cursor = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Moves the cursor to the specified location, where X
|
|
|
|
* is 0 to 39 inclusive, Y is 0 to 23 inclusive.
|
|
|
|
*/
|
|
|
|
static void move_cursor(int x, int y) {
|
|
|
|
hide_cursor();
|
|
|
|
cursor_x = x;
|
|
|
|
cursor_y = y;
|
|
|
|
}
|
|
|
|
|
2018-07-31 22:03:06 +00:00
|
|
|
/**
|
|
|
|
* Clear the screen with non-reversed spaces.
|
|
|
|
*/
|
|
|
|
static void home() {
|
|
|
|
volatile unsigned char *p = TEXT_PAGE1_BASE;
|
|
|
|
unsigned char ch = ' ' | 0x80;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// TODO: Could write these as words, not chars.
|
|
|
|
for (i = SCREEN_STRIDE*8; i >= 0; i--) {
|
|
|
|
*p++ = ch;
|
|
|
|
}
|
|
|
|
|
|
|
|
move_cursor(0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Print a string at the cursor.
|
|
|
|
*/
|
|
|
|
static void print(unsigned char *s) {
|
|
|
|
volatile unsigned char *loc = cursor_pos();
|
|
|
|
|
|
|
|
while (*s != '\0') {
|
|
|
|
if (*s == '\n') {
|
|
|
|
// TODO: Scroll.
|
|
|
|
move_cursor(0, cursor_y + 1);
|
|
|
|
} else {
|
|
|
|
*loc = *s | 0x80;
|
|
|
|
move_cursor(cursor_x + 1, cursor_y);
|
|
|
|
}
|
|
|
|
loc = cursor_pos();
|
|
|
|
s += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-01 06:19:15 +00:00
|
|
|
static void print_statement() {
|
|
|
|
print("Hello world!\n");
|
|
|
|
}
|
|
|
|
|
2018-07-31 22:03:06 +00:00
|
|
|
/**
|
2018-08-01 03:47:05 +00:00
|
|
|
* If a starts with string b, returns the position in a after b. Else returns null.
|
2018-07-31 22:03:06 +00:00
|
|
|
*/
|
2018-08-01 03:47:05 +00:00
|
|
|
static unsigned char *skip_over(unsigned char *a, unsigned char *b) {
|
|
|
|
while (*a != '\0' && *b != '\0') {
|
2018-07-31 22:03:06 +00:00
|
|
|
if (*a != *b) {
|
2018-08-01 03:47:05 +00:00
|
|
|
// Doesn't start with b.
|
2018-07-31 22:03:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2018-07-28 05:30:44 +00:00
|
|
|
|
2018-07-31 22:03:06 +00:00
|
|
|
a += 1;
|
|
|
|
b += 1;
|
|
|
|
}
|
2018-07-28 05:30:44 +00:00
|
|
|
|
2018-08-01 03:47:05 +00:00
|
|
|
// See if we're at the end of b.
|
|
|
|
return *b == '\0' ? a : 0;
|
2018-07-31 22:03:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Display a syntax error message.
|
|
|
|
*/
|
|
|
|
static void syntax_error() {
|
2018-08-01 03:47:05 +00:00
|
|
|
print("\n?SYNTAX ERROR");
|
|
|
|
// No linefeed, assume prompt will do it.
|
2018-07-31 22:03:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a function call to the binary buffer.
|
|
|
|
*/
|
|
|
|
static void add_call(void (*function)(void)) {
|
|
|
|
unsigned int addr = (int) function;
|
|
|
|
|
|
|
|
binary[binary_length++] = 0x20; // JSR
|
|
|
|
binary[binary_length++] = addr & 0xFF;
|
|
|
|
binary[binary_length++] = addr >> 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a function return to the binary buffer.
|
|
|
|
*/
|
|
|
|
static void add_return() {
|
|
|
|
binary[binary_length++] = 0x60; // RTS
|
|
|
|
}
|
|
|
|
|
2018-08-01 03:47:05 +00:00
|
|
|
/**
|
|
|
|
* Advance s over whitespace, which is just a space, returning
|
|
|
|
* the new pointer.
|
|
|
|
*/
|
|
|
|
static unsigned char *skip_whitespace(unsigned char *s) {
|
|
|
|
while (*s == ' ') {
|
|
|
|
s += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2018-07-31 22:03:06 +00:00
|
|
|
/**
|
|
|
|
* Process the user's line of input, possibly compiling the code.
|
|
|
|
* and executing it.
|
|
|
|
*/
|
|
|
|
static void process_input_buffer() {
|
2018-08-01 03:47:05 +00:00
|
|
|
unsigned char *s; // Where we are in the buffer.
|
|
|
|
unsigned char *after; // After skipping a token.
|
|
|
|
char done;
|
|
|
|
|
2018-07-31 22:03:06 +00:00
|
|
|
input_buffer[input_buffer_length] = '\0';
|
2018-08-01 06:19:15 +00:00
|
|
|
s = input_buffer;
|
2018-07-31 22:03:06 +00:00
|
|
|
|
|
|
|
// Compile the line of BASIC.
|
|
|
|
binary_length = 0;
|
2018-08-01 03:47:05 +00:00
|
|
|
|
|
|
|
do {
|
|
|
|
char error = 0;
|
|
|
|
|
|
|
|
// Default to being done after one command.
|
|
|
|
done = 1;
|
|
|
|
|
2018-08-01 06:19:15 +00:00
|
|
|
s = skip_whitespace(s);
|
|
|
|
if (*s == '\0' || *s == ':') {
|
|
|
|
// Empty statement.
|
2018-08-01 03:47:05 +00:00
|
|
|
} else if ((after = skip_over(s, "HOME")) != 0) {
|
2018-08-01 06:19:15 +00:00
|
|
|
s = after;
|
2018-08-01 03:47:05 +00:00
|
|
|
add_call(home);
|
2018-08-01 06:19:15 +00:00
|
|
|
} else if ((after = skip_over(s, "PRINT")) != 0) {
|
|
|
|
s = after;
|
|
|
|
|
|
|
|
// TODO: Parse expression.
|
|
|
|
add_call(print_statement);
|
2018-08-01 03:47:05 +00:00
|
|
|
} else {
|
|
|
|
error = 1;
|
|
|
|
}
|
|
|
|
|
2018-08-01 06:19:15 +00:00
|
|
|
// Now we're at the end of our statement.
|
2018-08-01 03:47:05 +00:00
|
|
|
if (!error) {
|
2018-08-01 06:19:15 +00:00
|
|
|
s = skip_whitespace(s);
|
2018-08-01 03:47:05 +00:00
|
|
|
if (*s == ':') {
|
|
|
|
// Skip colon.
|
|
|
|
s += 1;
|
|
|
|
|
2018-08-01 06:19:15 +00:00
|
|
|
// Next statement.
|
2018-08-01 03:47:05 +00:00
|
|
|
done = 0;
|
|
|
|
} else if (*s != '\0') {
|
2018-08-01 06:19:15 +00:00
|
|
|
// Junk at the end of the statement.
|
2018-08-01 03:47:05 +00:00
|
|
|
error = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
add_call(syntax_error);
|
|
|
|
}
|
|
|
|
} while (!done);
|
2018-07-31 22:03:06 +00:00
|
|
|
|
|
|
|
// Return from function.
|
|
|
|
add_return();
|
|
|
|
|
2018-08-01 03:47:05 +00:00
|
|
|
if (binary_length > sizeof(binary)) {
|
|
|
|
// TODO: Check while adding bytes, not at the end.
|
|
|
|
print("\n?Binary length exceeded");
|
|
|
|
} else {
|
|
|
|
// Call it.
|
|
|
|
binary_function();
|
|
|
|
}
|
2018-07-31 22:03:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(void)
|
|
|
|
{
|
|
|
|
volatile unsigned char *loc;
|
2018-07-28 05:30:44 +00:00
|
|
|
int i;
|
|
|
|
|
2018-07-31 07:05:22 +00:00
|
|
|
home();
|
2018-07-28 05:30:44 +00:00
|
|
|
|
2018-07-31 22:03:06 +00:00
|
|
|
/*
|
2018-07-31 20:42:21 +00:00
|
|
|
// Display the character set.
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
|
|
// Fails with: unhandled instruction B2
|
|
|
|
move_cursor(i % 16, i >> 4);
|
|
|
|
// Works.
|
|
|
|
// move_cursor(i & 0x0F, i >> 4);
|
|
|
|
loc = cursor_pos();
|
|
|
|
*loc = i;
|
|
|
|
}
|
|
|
|
while(1);
|
2018-07-31 22:03:06 +00:00
|
|
|
*/
|
2018-07-31 20:42:21 +00:00
|
|
|
|
|
|
|
|
2018-07-31 07:05:22 +00:00
|
|
|
// Title.
|
2018-07-31 22:03:06 +00:00
|
|
|
move_cursor((40 - title_length) / 2, 0);
|
|
|
|
print(title);
|
2018-07-28 05:30:44 +00:00
|
|
|
|
2018-07-31 07:05:22 +00:00
|
|
|
// Prompt.
|
2018-07-31 22:03:06 +00:00
|
|
|
print("\n\n]");
|
2018-07-31 07:05:22 +00:00
|
|
|
|
2018-07-31 19:48:29 +00:00
|
|
|
// Keyboard input.
|
|
|
|
i = 0;
|
2018-07-31 22:03:06 +00:00
|
|
|
input_buffer_length = 0;
|
2018-07-31 19:48:29 +00:00
|
|
|
show_cursor();
|
2018-07-31 07:05:22 +00:00
|
|
|
while(1) {
|
2018-07-31 19:48:29 +00:00
|
|
|
// Blink cursor.
|
|
|
|
i += 1;
|
2018-08-01 03:47:05 +00:00
|
|
|
if (i == 3000) {
|
2018-07-31 19:48:29 +00:00
|
|
|
if (showing_cursor) {
|
|
|
|
hide_cursor();
|
|
|
|
} else {
|
|
|
|
show_cursor();
|
|
|
|
}
|
|
|
|
i = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(keyboard_test()) {
|
|
|
|
hide_cursor();
|
|
|
|
|
|
|
|
while(keyboard_test()) {
|
|
|
|
unsigned char key;
|
2018-07-31 19:02:26 +00:00
|
|
|
|
2018-07-31 19:48:29 +00:00
|
|
|
key = keyboard_get();
|
|
|
|
if (key == 8) {
|
|
|
|
// Backspace.
|
2018-07-31 22:03:06 +00:00
|
|
|
if (input_buffer_length > 0) {
|
2018-07-31 19:48:29 +00:00
|
|
|
move_cursor(cursor_x - 1, cursor_y);
|
2018-07-31 22:03:06 +00:00
|
|
|
input_buffer_length -= 1;
|
2018-07-31 19:48:29 +00:00
|
|
|
}
|
|
|
|
} else if (key == 13) {
|
|
|
|
// Return.
|
|
|
|
move_cursor(0, cursor_y + 1);
|
2018-07-31 22:03:06 +00:00
|
|
|
|
|
|
|
process_input_buffer();
|
|
|
|
|
2018-08-01 03:47:05 +00:00
|
|
|
print("\n]");
|
2018-07-31 22:03:06 +00:00
|
|
|
input_buffer_length = 0;
|
2018-07-31 19:48:29 +00:00
|
|
|
} else {
|
2018-07-31 22:03:06 +00:00
|
|
|
if (input_buffer_length < sizeof(input_buffer) - 1) {
|
|
|
|
volatile unsigned char *loc = cursor_pos();
|
|
|
|
*loc = key | 0x80;
|
|
|
|
move_cursor(cursor_x + 1, cursor_y);
|
|
|
|
|
|
|
|
input_buffer[input_buffer_length++] = key;
|
|
|
|
}
|
2018-07-31 19:48:29 +00:00
|
|
|
}
|
|
|
|
}
|
2018-07-31 19:02:26 +00:00
|
|
|
|
2018-07-31 19:48:29 +00:00
|
|
|
show_cursor();
|
2018-07-31 19:02:26 +00:00
|
|
|
}
|
2018-07-31 07:05:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2018-07-28 05:30:44 +00:00
|
|
|
}
|