Skip to content

Commit

Permalink
Emulator separation (#30)
Browse files Browse the repository at this point in the history
* Allow console handler with zero args to main

* Add array of devices as a parameter to codegen

* Separate definitions specific to varvara

* Generalize console device hook and argc/argv clash

* Move more varvara definitions from uxn header

* Update uxn header references in README
  • Loading branch information
manifoldslug authored Jul 29, 2023
1 parent 1ff25c4 commit af02db1
Show file tree
Hide file tree
Showing 19 changed files with 187 additions and 174 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ chibicc-uxn is featureful enough to write small [games](examples/danmaku.c) and

Running `make` will build `./chibicc`.

chibicc itself has no preprocessor, but any C compiler's `-E` flag will do. We use `-I.` to include uxn.h and `-P` to eliminate `#` lines from the preprocessor output:
chibicc itself has no preprocessor, but any C compiler's `-E` flag will do. We use `-I.` to include `varvara.h` or just `uxn.h` and `-P` to eliminate `#` lines from the preprocessor output:

```sh
gcc -I. -P -E examples/day3.c -o tmp.c
Expand Down Expand Up @@ -52,7 +52,7 @@ See also `make test`, which runs a test suite.

[Varvara](https://wiki.xxiivv.com/site/varvara.html) is the set of I/O interfaces for Uxn-based tools and games.

[uxn.h](./uxn.h) defines macros for setting the window size, drawing sprites on the screen, playing sounds, etc.
[varvara.h](./varvara.h) defines macros for setting the window size, drawing sprites on the screen, playing sounds, etc.

To set up Varvara event handlers, just define any of the following functions:

Expand Down
2 changes: 1 addition & 1 deletion examples/argc_argv.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include <uxn.h>
#include <varvara.h>

void print_int(unsigned char i) {
putchar('0' + ((i / 10) % 10));
Expand Down
2 changes: 1 addition & 1 deletion examples/danmaku.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "atari8_uf1.c"
#include <uxn.h>
#include <varvara.h>

#define SUBPIXELS 16
#define WIDTH 200
Expand Down
2 changes: 1 addition & 1 deletion examples/day3.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include <uxn.h>
#include <varvara.h>

char square[8] = {0xff, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xff};

Expand Down
2 changes: 1 addition & 1 deletion examples/day6.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include <uxn.h>
#include <varvara.h>

#define PADDLE_WIDTH 16
#define PADDLE_HEIGHT 24
Expand Down
2 changes: 1 addition & 1 deletion examples/echo.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include <uxn.h>
#include <varvara.h>

void on_controller(void) {
putchar(controller_key());
Expand Down
2 changes: 1 addition & 1 deletion examples/fizzbuzz.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// $ uxnasm tmp.tal tmp.rom
// $ uxncli tmp.rom

#include <uxn.h>
#include <varvara.h>

void puts(char *s) {
for (; *s; ++s)
Expand Down
2 changes: 1 addition & 1 deletion examples/mandelbrot.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include <uxn.h>
#include <varvara.h>

// Multiply two 8.8 fixed point numbers.
int mul(unsigned x, unsigned y) {
Expand Down
2 changes: 1 addition & 1 deletion examples/mandelbrot_fast.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// uxnasm tmp.tal tmp.rom
// uxnemu tmp.rom

#include <uxn.h>
#include <varvara.h>

// Multiply two 8.8 fixed point numbers.
// Defined in mandelbrot_mul.tal
Expand Down
2 changes: 1 addition & 1 deletion examples/nqueen.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// $ uxnasm tmp.tal tmp.rom
// $ uxncli tmp.rom

#include <uxn.h>
#include <varvara.h>

int print_board(int (*board)[10]) {
for (int i = 0; i < 10; i++) {
Expand Down
2 changes: 1 addition & 1 deletion examples/printf.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include <uxn.h>
#include <varvara.h>

#define args char *format
#define func_name printf
Expand Down
2 changes: 1 addition & 1 deletion examples/star.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// star demo for chibicc-uxn by hikari_no_yume (2023-06-10 ~ 2023-06-14)

#include <uxn.h>
#include <varvara.h>

// generated with JS: x="";for(i=0;i<64;i++)x+=((Math.sin(i*Math.PI/128)*128)|0)+",";
char SIN_TABLE[64] = {0,3,6,9,12,15,18,21,24,28,31,34,37,40,43,46,48,51,54,57,60,63,65,68,71,73,76,78,81,83,85,88,90,92,94,96,98,100,102,104,106,108,109,111,112,114,115,117,118,119,120,121,122,123,124,124,125,126,126,127,127,127,127,127,};
Expand Down
2 changes: 1 addition & 1 deletion examples/variadic.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include <uxn.h>
#include <varvara.h>

// Writing a variadic function in chibicc-uxn:
// 1. Put ... at the end of the signature.
Expand Down
9 changes: 8 additions & 1 deletion src/chibi.h
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,14 @@ void add_type(Node *node);
// codegen.c
//

void codegen(Program *prog, bool do_opt);
typedef struct {
char *name;
unsigned char port;
} Device;

void varvara_argc_argv_hook(void);

void codegen(Program *prog, bool do_opt, int devices_size, Device* devices, Device* console, void (*argc_argv_hook)(void));

//
// optimize.c
Expand Down
51 changes: 25 additions & 26 deletions src/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -875,17 +875,7 @@ static void emit_text(Program *prog) {
}
}

typedef struct {
char *name;
unsigned char port;
} Device;

static Device devices[] = {
{"console", 0x10}, {"screen", 0x20}, {"audio1", 0x30}, {"audio2", 0x40},
{"audio3", 0x50}, {"audio4", 0x60}, {"controller", 0x80}, {"mouse", 0x90},
};

void do_argc_argv_hook(void) {
void varvara_argc_argv_hook(void) {
// This is routines/argc_argv.tal but without comments or indentation.
// To update this function after editing that file, use update_argc_argv.sh
printf(" LIT2r fffe #0001 STH2kr STA2 LIT2r 0001 SUB2r #17 DEI ?&set_arg_hook !prep_argc_argv_and_call_main\n");
Expand All @@ -900,14 +890,15 @@ void do_argc_argv_hook(void) {
printf(" &strlen_loop INC2k SWP2 LDA ?&strlen_loop DUP2 #fffe LTH2 ?&argv_loop POP2 POP2 STH2kr #fffe LDA2 main_ BRK\n");
}

void codegen(Program *prog, bool do_opt) {
void codegen(Program *prog, bool do_opt, int devices_size, Device* devices, Device* console, void (*argc_argv_hook)(void)) {
int i;
bool need_device_hook[sizeof(devices) / sizeof(Device)] = {false};
bool need_device_hook[devices_size];
for(i = 0; i < devices_size; ++i){ need_device_hook[i] = false; }
bool need_argc_argv = false;

printf("|0100\n");
for (Function *fn = prog->fns; fn; fn = fn->next) {
for (i = 0; i < sizeof(devices) / sizeof(Device); i++) {
for (i = 0; i < devices_size; i++) {
if (*devices[i].name && !strncmp(fn->name, "on_", 3) &&
!strcmp(fn->name + 3, devices[i].name)) {
printf(" ;L.%s.hook #%02x DEO2\n", devices[i].name, devices[i].port);
Expand All @@ -918,29 +909,37 @@ void codegen(Program *prog, bool do_opt) {
if (!fn->params->next || (fn->params->next && fn->params->next->next)) {
error("main() has incorrect parameter count: 0 or 2 expected");
}
if (fn->params->var->ty->kind != TY_INT ||
fn->params->next->var->ty->kind != TY_PTR || // argv[][] == **argv
!fn->params->next->var->ty->base ||
fn->params->next->var->ty->base->kind != TY_PTR ||
fn->params->next->var->ty->base->base->kind != TY_CHAR) {
Type* ty1 = fn->params->var->ty;
Type* ty2 = fn->params->next->var->ty;
if (ty1->kind != TY_INT || ty2->kind != TY_PTR || !ty2->base ||
ty2->base->kind != TY_PTR || ty2->base->base->kind != TY_CHAR) {
error("main() parameters have incorrect types: main(int argc, char *argv[]) expected");
}
need_argc_argv = true;
}
}
if (!need_argc_argv) {
printf(" LIT2r 0000 main_ POP2r BRK\n");
if (need_device_hook[0]) {
} else {
if (!console) {
error("main(int, char*[]), but no console device");
}
if (console < devices || console >= devices + devices_size) {
error("main(int, char*[]), but invalid console device pointer");
}
if (need_device_hook[console - devices]) {
// TODO: passthrough for on_console() for stdin
error("Can't use on_console() and main(int argc, char **argv) at the same time");
error("main(int, char*[]) clashes with console device hook");
}
} else {
do_argc_argv_hook();
if (!argc_argv_hook) {
error("main(int, char*[]), but hook is missing");
}
argc_argv_hook();
}
for (i = 0; i < sizeof(devices) / sizeof(Device); i++) {
for (i = 0; i < devices_size; i++) {
if (need_device_hook[i]) {
printf(" @L.%s.hook LIT2r 0000 on_%s_ POP2 POP2r BRK\n", devices[i].name,
devices[i].name);
printf(" @L.%s.hook LIT2r 0000 on_%s_ POP2 POP2r BRK\n",
devices[i].name, devices[i].name);
}
}
emit_data(prog);
Expand Down
7 changes: 6 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ int main(int argc, char **argv) {
}

// Traverse the AST to emit assembly.
codegen(prog, do_opt);
Device devices[] = {
{"console", 0x10}, {"screen", 0x20}, {"audio1", 0x30}, {"audio2", 0x40},
{"audio3", 0x50}, {"audio4", 0x60}, {"controller", 0x80}, {"mouse", 0x90},
};
codegen(prog, do_opt, sizeof(devices) / sizeof(Device), devices, &devices[0],
varvara_argc_argv_hook);
fprintf(stderr, "codegen(): allocated %d bytes\n", total_alloc);

return 0;
Expand Down
2 changes: 1 addition & 1 deletion tests
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* This is a block comment.
*/

#include <uxn.h>
#include <varvara.h>
void print(char *s) {
for (; *s; *s++) putchar(*s);
}
Expand Down
131 changes: 0 additions & 131 deletions uxn.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,140 +3,9 @@ void deo(char data, char device);
void deo2(int data, char device);
char dei(char device);
int dei2(char device);
void exit(char status);

int __builtin_va_arg();
typedef void *va_list;
#define va_start(ap, va)
#define va_end(ap)
#define va_arg(ap, ty) __builtin_va_arg()

typedef struct {
char operation; // 1 = copy
int length;
int src_page;
void* src_addr;
int dst_page;
void* dst_addr;
} Expansion;

// https://wiki.xxiivv.com/site/varvara.html
#define expansion(ptr) deo2(ptr, 0x02)
#define friend(func) deo2(func, 0x04)
#define set_palette(r, g, b) (deo2(r, 0x08), deo2(g, 0x0a), deo2(b, 0x0c))
#define debug() deo(0x01, 0x0e)

#define console_read() dei(0x12)
#define console_type() dei(0x17)
#define console_write(c) deo(c, 0x18) // stdout
#define console_error(c) deo(c, 0x19) // stderr
#define getchar console_read
#define putchar console_write

#define set_screen_size(width, height) (deo2(width, 0x22), deo2(height, 0x24))
#define screen_width() dei2(0x22)
#define screen_height() dei2(0x24)
#define set_screen_auto(a) deo(a, 0x26)
#define set_screen_x(x) deo2(x, 0x28)
#define set_screen_y(y) deo2(y, 0x2a)
#define set_screen_xy(x,y) (set_screen_x(x), set_screen_y(y))
#define screen_x() dei2(0x28)
#define screen_y() dei2(0x2a)
#define set_screen_addr(a) deo2(a, 0x2c)
#define draw_pixel(a) deo(a, 0x2e)
#define draw_sprite(a) deo(a, 0x2f)

#define audio_position(ch, a) dei2(0x32 + 0x10*ch)
#define audio_output(ch) dei(a, 0x34 + 0x10*ch)
#define set_audio_adsr(ch, adsr) deo2(adsr, 0x38 + 0x10*ch)
#define set_audio_length(ch, length) deo2(length, 0x3a + 0x10*ch)
#define set_audio_addr(ch, addr) deo2(addr, 0x3c + 0x10*ch)
#define set_audio_volume(ch, volume) deo(volume, 0x3e + 0x10*ch)
#define play_audio(ch, pitch) deo(pitch, 0x3f + 0x10*ch)

#define controller_button() dei(0x82)
#define controller_key() dei(0x83)

#define mouse_x() dei2(0x92)
#define mouse_y() dei2(0x94)
#define mouse_state() dei(0x96)
#define mouse_scrollx() dei2(0x9a)
#define mouse_scrolly() dei2(0x9c)

/// Read up to n bytes from file "name" into addr, then return bytes read.
#define file_read(name, n, addr) (deo2(name, 0xa8), deo2(n, 0xaa), deo2(addr, 0xac), dei2(0xa2))
#define _file_write(name, n, addr, append) (deo(append, 0xa7), deo2(name, 0xa8), deo2(n, 0xaa), deo2(addr, 0xae), dei2(0xa2))
/// Write n bytes from addr into file "name", then return bytes written.
#define file_write(name, n, addr) _file_write(name, n, addr, 0)
#define file_append(name, n, addr) _file_write(name, n, addr, 1)
#define file_delete(name) (deo2(name, 0xa8), deo(1, 0xa6))

#define datetime_year() dei2(0xc0)
#define datetime_month() dei(0xc2)
#define datetime_day() dei(0xc3)
#define datetime_hour() dei(0xc4)
#define datetime_minute() dei(0xc5)
#define datetime_second() dei(0xc6)
#define datetime_dotw() dei(0xc7)
#define datetime_doty() dei2(0xc8)
#define datetime_isdst() dei(0xca)

// Pixel values (| with color number)
// (Layer + operation + corner)
#define BgDot 0x00
#define BgFillBR 0x80
#define BgFillBL 0x90
#define BgFillTR 0xa0
#define BgFillTL 0xb0
#define FgDot 0x40
#define FgFillBR 0xc0
#define FgFillBL 0xd0
#define FgFillTR 0xe0
#define FgFillTL 0xf0

// Sprite values (| with blend number)
// (Layer + bit depth + flip axes)
#define Bg1 0x00
#define Bg1X 0x10
#define Bg1Y 0x20
#define Bg1XY 0x30
#define Fg1 0x40
#define Fg1X 0x50
#define Fg1Y 0x60
#define Fg1XY 0x70
#define Bg2 0x80
#define Bg2X 0x90
#define Bg2Y 0xa0
#define Bg2XY 0xb0
#define Fg2 0xc0
#define Fg2X 0xd0
#define Fg2Y 0xe0
#define Fg2XY 0xf0

// Auto values
#define Auto1 0x00
#define Auto1x 0x01
#define Auto1y 0x02
#define Auto1a 0x04
#define Auto1ax 0x05
#define Auto1ay 0x06
#define Auto2 0x10
#define Auto2x 0x11
#define Auto2y 0x12
#define Auto2a 0x14
#define Auto2ax 0x15
#define Auto2ay 0x16

// Button values
#define ButtonCtrl 0x01
#define ButtonA 0x01
#define ButtonAlt 0x02
#define ButtonB 0x02
#define ButtonShift 0x04
#define ButtonSelect 0x04
#define ButtonStart 0x08
#define ButtonHome 0x08
#define ButtonUp 0x10
#define ButtonDown 0x20
#define ButtonLeft 0x40
#define ButtonRight 0x80
Loading

1 comment on commit af02db1

@neauoire
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent idea, it's much cleaner that way <3

Please sign in to comment.