uxn

Varvara Ordinator, written in ANSI C(SDL2)
git clone https://git.eamoncaddigan.net/uxn.git
Log | Files | Refs | README | LICENSE

commit b2891da133caa5df40f4fe0062afbf5e07a27324
parent 25fc9ca5fdfea7d5b2270f7d9865ca2a6c062f31
Author: neauoire <aliceffekt@gmail.com>
Date:   Fri, 18 Mar 2022 11:03:07 -0700

Merge branch 'main' of git.sr.ht:~rabbits/uxn

Diffstat:
Msrc/devices/audio.c | 40+++++++++++++++++++++++++++++++++++-----
Msrc/devices/audio.h | 19+++++--------------
Msrc/devices/file.c | 173++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/devices/file.h | 5++++-
Msrc/uxncli.c | 16+++++++++++++++-
Msrc/uxnemu.c | 46++++++++++++++++++++++++----------------------
6 files changed, 183 insertions(+), 116 deletions(-)

diff --git a/src/devices/audio.c b/src/devices/audio.c @@ -16,6 +16,14 @@ WITH REGARD TO THIS SOFTWARE. #define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025) #define ADSR_STEP (SAMPLE_FREQUENCY / 0xf) +typedef struct { + Uint8 *addr; + Uint32 count, advance, period, age, a, d, s, r; + Uint16 i, len; + Sint8 volume[2]; + Uint8 pitch, repeat; +} UxnAudio; + /* clang-format off */ static Uint32 advances[12] = { @@ -23,7 +31,7 @@ static Uint32 advances[12] = { 0xb504f, 0xbfc88, 0xcb2ff, 0xd7450, 0xe411f, 0xf1a1c }; -UxnAudio uxn_audio[POLYPHONY]; +static UxnAudio uxn_audio[POLYPHONY]; /* clang-format on */ @@ -40,8 +48,9 @@ envelope(UxnAudio *c, Uint32 age) } int -audio_render(UxnAudio *c, Sint16 *sample, Sint16 *end) +audio_render(int instance, Sint16 *sample, Sint16 *end) { + UxnAudio *c = &uxn_audio[instance]; Sint32 s; if(!c->advance || !c->period) return 0; while(sample < end) { @@ -59,13 +68,26 @@ audio_render(UxnAudio *c, Sint16 *sample, Sint16 *end) *sample++ += s * c->volume[0] / 0x180; *sample++ += s * c->volume[1] / 0x180; } - if(!c->advance) audio_finished_handler(c); + if(!c->advance) audio_finished_handler(instance); return 1; } void -audio_start(UxnAudio *c, Uint16 adsr, Uint8 pitch) +audio_start(int instance, Device *d) { + UxnAudio *c = &uxn_audio[instance]; + Uint16 addr, adsr; + Uint8 pitch; + DEVPEEK16(adsr, 0x8); + DEVPEEK16(c->len, 0xa); + DEVPEEK16(addr, 0xc); + if(c->len > 0x10000 - addr) + c->len = 0x10000 - addr; + c->addr = &d->u->ram[addr]; + c->volume[0] = d->dat[0xe] >> 4; + c->volume[1] = d->dat[0xe] & 0xf; + c->repeat = !(d->dat[0xf] & 0x80); + pitch = d->dat[0xf] & 0x7f; if(pitch < 108 && c->len) c->advance = advances[pitch % 12] >> (8 - pitch / 12); else { @@ -85,8 +107,9 @@ audio_start(UxnAudio *c, Uint16 adsr, Uint8 pitch) } Uint8 -audio_get_vu(UxnAudio *c) +audio_get_vu(int instance) { + UxnAudio *c = &uxn_audio[instance]; int i; Sint32 sum[2] = {0, 0}; if(!c->advance || !c->period) return 0; @@ -97,3 +120,10 @@ audio_get_vu(UxnAudio *c) } return (sum[0] << 4) | sum[1]; } + +Uint16 +audio_get_position(int instance) +{ + UxnAudio *c = &uxn_audio[instance]; + return c->i; +} diff --git a/src/devices/audio.h b/src/devices/audio.h @@ -15,17 +15,8 @@ typedef signed int Sint32; #define SAMPLE_FREQUENCY 44100 #define POLYPHONY 4 -typedef struct { - Uint8 *addr; - Uint32 count, advance, period, age, a, d, s, r; - Uint16 i, len; - Sint8 volume[2]; - Uint8 pitch, repeat; -} UxnAudio; - -extern UxnAudio uxn_audio[POLYPHONY]; - -Uint8 audio_get_vu(UxnAudio *c); -int audio_render(UxnAudio *c, Sint16 *sample, Sint16 *end); -void audio_start(UxnAudio *c, Uint16 adsr, Uint8 pitch); -void audio_finished_handler(UxnAudio *c); +Uint8 audio_get_vu(int instance); +Uint16 audio_get_position(int instance); +int audio_render(int instance, Sint16 *sample, Sint16 *end); +void audio_start(int instance, Device *d); +void audio_finished_handler(int instance); diff --git a/src/devices/file.c b/src/devices/file.c @@ -19,29 +19,32 @@ WITH REGARD TO THIS SOFTWARE. #include <sys/stat.h> #include <unistd.h> -static FILE *f; -static DIR *dir; -static char *current_filename = ""; -static struct dirent *de; - -static enum { IDLE, - FILE_READ, - FILE_WRITE, - DIR_READ } state; +typedef struct { + FILE *f; + DIR *dir; + char current_filename[4096]; + struct dirent *de; + enum { IDLE, + FILE_READ, + FILE_WRITE, + DIR_READ } state; +} UxnFile; + +static UxnFile uxn_file[POLYFILEY]; static void -reset(void) +reset(UxnFile *c) { - if(f != NULL) { - fclose(f); - f = NULL; + if(c->f != NULL) { + fclose(c->f); + c->f = NULL; } - if(dir != NULL) { - closedir(dir); - dir = NULL; + if(c->dir != NULL) { + closedir(c->dir); + c->dir = NULL; } - de = NULL; - state = IDLE; + c->de = NULL; + c->state = IDLE; } static Uint16 @@ -61,20 +64,20 @@ get_entry(char *p, Uint16 len, const char *pathname, const char *basename, int f } static Uint16 -file_read_dir(char *dest, Uint16 len) +file_read_dir(UxnFile *c, char *dest, Uint16 len) { - static char pathname[4096]; + static char pathname[4352]; char *p = dest; - if(de == NULL) de = readdir(dir); - for(; de != NULL; de = readdir(dir)) { + if(c->de == NULL) c->de = readdir(c->dir); + for(; c->de != NULL; c->de = readdir(c->dir)) { Uint16 n; - if(de->d_name[0] == '.' && de->d_name[1] == '\0') + if(c->de->d_name[0] == '.' && c->de->d_name[1] == '\0') continue; - if(strlen(current_filename) + 1 + strlen(de->d_name) < sizeof(pathname)) - sprintf(pathname, "%s/%s", current_filename, de->d_name); + if(strlen(c->current_filename) + 1 + strlen(c->de->d_name) < sizeof(pathname)) + sprintf(pathname, "%s/%s", c->current_filename, c->de->d_name); else pathname[0] = '\0'; - n = get_entry(p, len, pathname, de->d_name, 1); + n = get_entry(p, len, pathname, c->de->d_name, 1); if(!n) break; p += n; len -= n; @@ -83,102 +86,126 @@ file_read_dir(char *dest, Uint16 len) } static Uint16 -file_init(void *filename) +file_init(UxnFile *c, char *filename, size_t max_len) { - reset(); - current_filename = filename; + char *p = c->current_filename; + size_t len = sizeof(c->current_filename); + reset(c); + if(len > max_len) len = max_len; + while(len) { + if((*p++ = *filename++) == '\0') + return 0; + len--; + } + c->current_filename[0] = '\0'; return 0; } static Uint16 -file_read(void *dest, Uint16 len) +file_read(UxnFile *c, void *dest, Uint16 len) { - if(state != FILE_READ && state != DIR_READ) { - reset(); - if((dir = opendir(current_filename)) != NULL) - state = DIR_READ; - else if((f = fopen(current_filename, "rb")) != NULL) - state = FILE_READ; + if(c->state != FILE_READ && c->state != DIR_READ) { + reset(c); + if((c->dir = opendir(c->current_filename)) != NULL) + c->state = DIR_READ; + else if((c->f = fopen(c->current_filename, "rb")) != NULL) + c->state = FILE_READ; } - if(state == FILE_READ) - return fread(dest, 1, len, f); - if(state == DIR_READ) - return file_read_dir(dest, len); + if(c->state == FILE_READ) + return fread(dest, 1, len, c->f); + if(c->state == DIR_READ) + return file_read_dir(c, dest, len); return 0; } static Uint16 -file_write(void *src, Uint16 len, Uint8 flags) +file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags) { Uint16 ret = 0; - if(state != FILE_WRITE) { - reset(); - if((f = fopen(current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL) - state = FILE_WRITE; + if(c->state != FILE_WRITE) { + reset(c); + if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL) + c->state = FILE_WRITE; } - if(state == FILE_WRITE) { - if((ret = fwrite(src, 1, len, f)) > 0 && fflush(f) != 0) + if(c->state == FILE_WRITE) { + if((ret = fwrite(src, 1, len, c->f)) > 0 && fflush(c->f) != 0) ret = 0; } return ret; } static Uint16 -file_stat(void *dest, Uint16 len) +file_stat(UxnFile *c, void *dest, Uint16 len) { - char *basename = strrchr(current_filename, '/'); + char *basename = strrchr(c->current_filename, '/'); if(basename != NULL) basename++; else - basename = current_filename; - return get_entry(dest, len, current_filename, basename, 0); + basename = c->current_filename; + return get_entry(dest, len, c->current_filename, basename, 0); } static Uint16 -file_delete(void) +file_delete(UxnFile *c) { - return unlink(current_filename); + return unlink(c->current_filename); } /* IO */ void -file_deo(Device *d, Uint8 port) +file_i_deo(int instance, Device *d, Uint8 port) { - Uint16 a, b, res; + UxnFile *c = &uxn_file[instance]; + Uint16 addr, len, res; switch(port) { case 0x5: - DEVPEEK16(a, 0x4); - DEVPEEK16(b, 0xa); - if(b > 0x10000 - a) - b = 0x10000 - a; - res = file_stat(&d->u->ram[a], b); + DEVPEEK16(addr, 0x4); + DEVPEEK16(len, 0xa); + if(len > 0x10000 - addr) + len = 0x10000 - addr; + res = file_stat(c, &d->u->ram[addr], len); DEVPOKE16(0x2, res); break; case 0x6: - res = file_delete(); + res = file_delete(c); DEVPOKE16(0x2, res); break; case 0x9: - DEVPEEK16(a, 0x8); - res = file_init(&d->u->ram[a]); + DEVPEEK16(addr, 0x8); + res = file_init(c, (char *)&d->u->ram[addr], 0x10000 - addr); DEVPOKE16(0x2, res); break; case 0xd: - DEVPEEK16(a, 0xc); - DEVPEEK16(b, 0xa); - if(b > 0x10000 - a) - b = 0x10000 - a; - res = file_read(&d->u->ram[a], b); + DEVPEEK16(addr, 0xc); + DEVPEEK16(len, 0xa); + if(len > 0x10000 - addr) + len = 0x10000 - addr; + res = file_read(c, &d->u->ram[addr], len); DEVPOKE16(0x2, res); break; case 0xf: - DEVPEEK16(a, 0xe); - DEVPEEK16(b, 0xa); - if(b > 0x10000 - a) - b = 0x10000 - a; - res = file_write(&d->u->ram[a], b, d->dat[0x7]); + DEVPEEK16(addr, 0xe); + DEVPEEK16(len, 0xa); + if(len > 0x10000 - addr) + len = 0x10000 - addr; + res = file_write(c, &d->u->ram[addr], len, d->dat[0x7]); + DEVPOKE16(0x2, res); + break; + } +} + +Uint8 +file_i_dei(int instance, Device *d, Uint8 port) +{ + UxnFile *c = &uxn_file[instance]; + Uint16 res; + switch(port) { + case 0xc: + case 0xd: + res = file_read(c, &d->dat[port], 1); DEVPOKE16(0x2, res); break; } + return d->dat[port]; } diff --git a/src/devices/file.h b/src/devices/file.h @@ -10,4 +10,7 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ -void file_deo(Device *d, Uint8 port); +#define POLYFILEY 1 + +void file_i_deo(int instance, Device *d, Uint8 port); +Uint8 file_i_dei(int instance, Device *d, Uint8 port); diff --git a/src/uxncli.c b/src/uxncli.c @@ -17,6 +17,8 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ +static Device *devfile0; + static int error(char *msg, const char *err) { @@ -42,6 +44,18 @@ console_deo(Device *d, Uint8 port) } } +static void +file_deo(Device *d, Uint8 port) +{ + file_i_deo(d - devfile0, d, port); +} + +static Uint8 +file_dei(Device *d, Uint8 port) +{ + return file_i_dei(d - devfile0, d, port); +} + static Uint8 nil_dei(Device *d, Uint8 port) { @@ -102,7 +116,7 @@ start(Uxn *u) /* empty */ uxn_port(u, 0x7, nil_dei, nil_deo); /* empty */ uxn_port(u, 0x8, nil_dei, nil_deo); /* empty */ uxn_port(u, 0x9, nil_dei, nil_deo); - /* file */ uxn_port(u, 0xa, nil_dei, file_deo); + /* file */ devfile0 = uxn_port(u, 0xa, file_dei, file_deo); /* datetime */ uxn_port(u, 0xb, datetime_dei, nil_deo); /* empty */ uxn_port(u, 0xc, nil_dei, nil_deo); /* empty */ uxn_port(u, 0xd, nil_dei, nil_deo); diff --git a/src/uxnemu.c b/src/uxnemu.c @@ -42,7 +42,7 @@ static SDL_Rect gRect; /* devices */ -static Device *devscreen, *devmouse, *devctrl, *devaudio0; +static Device *devscreen, *devmouse, *devctrl, *devaudio0, *devfile0; static Uint8 zoom = 1; static Uint32 stdin_event, audio0_event; @@ -58,21 +58,21 @@ error(char *msg, const char *err) static void audio_callback(void *u, Uint8 *stream, int len) { - int i, running = 0; + int instance, running = 0; Sint16 *samples = (Sint16 *)stream; SDL_memset(stream, 0, len); - for(i = 0; i < POLYPHONY; i++) - running += audio_render(&uxn_audio[i], samples, samples + len / 2); + for(instance = 0; instance < POLYPHONY; instance++) + running += audio_render(instance, samples, samples + len / 2); if(!running) SDL_PauseAudioDevice(audio_id, 1); (void)u; } void -audio_finished_handler(UxnAudio *c) +audio_finished_handler(int instance) { SDL_Event event; - event.type = audio0_event + (c - uxn_audio); + event.type = audio0_event + instance; SDL_PushEvent(&event); } @@ -185,11 +185,11 @@ console_deo(Device *d, Uint8 port) static Uint8 audio_dei(Device *d, Uint8 port) { - UxnAudio *c = &uxn_audio[d - devaudio0]; + int instance = d - devaudio0; if(!audio_id) return d->dat[port]; switch(port) { - case 0x4: return audio_get_vu(c); - case 0x2: DEVPOKE16(0x2, c->i); /* fall through */ + case 0x4: return audio_get_vu(instance); + case 0x2: DEVPOKE16(0x2, audio_get_position(instance)); /* fall through */ default: return d->dat[port]; } } @@ -197,26 +197,28 @@ audio_dei(Device *d, Uint8 port) static void audio_deo(Device *d, Uint8 port) { - UxnAudio *c = &uxn_audio[d - devaudio0]; + int instance = d - devaudio0; if(!audio_id) return; if(port == 0xf) { - Uint16 addr, adsr; SDL_LockAudioDevice(audio_id); - DEVPEEK16(adsr, 0x8); - DEVPEEK16(c->len, 0xa); - DEVPEEK16(addr, 0xc); - if(c->len > 0x10000 - addr) - c->len = 0x10000 - addr; - c->addr = &d->u->ram[addr]; - c->volume[0] = d->dat[0xe] >> 4; - c->volume[1] = d->dat[0xe] & 0xf; - c->repeat = !(d->dat[0xf] & 0x80); - audio_start(c, adsr, d->dat[0xf] & 0x7f); + audio_start(instance, d); SDL_UnlockAudioDevice(audio_id); SDL_PauseAudioDevice(audio_id, 0); } } +static void +file_deo(Device *d, Uint8 port) +{ + file_i_deo(d - devfile0, d, port); +} + +static Uint8 +file_dei(Device *d, Uint8 port) +{ + return file_i_dei(d - devfile0, d, port); +} + static Uint8 nil_dei(Device *d, Uint8 port) { @@ -263,7 +265,7 @@ start(Uxn *u, char *rom) /* unused */ uxn_port(u, 0x7, nil_dei, nil_deo); /* control */ devctrl = uxn_port(u, 0x8, nil_dei, nil_deo); /* mouse */ devmouse = uxn_port(u, 0x9, nil_dei, nil_deo); - /* file */ uxn_port(u, 0xa, nil_dei, file_deo); + /* file */ devfile0 = uxn_port(u, 0xa, file_dei, file_deo); /* datetime */ uxn_port(u, 0xb, datetime_dei, nil_deo); /* unused */ uxn_port(u, 0xc, nil_dei, nil_deo); /* unused */ uxn_port(u, 0xd, nil_dei, nil_deo);