Initial working version.
Currently, mp3.rom will start playing a hardcoded file. On Linux, when the uxn11 process ends the mpg123 process will also be killed. The code to do this isn't portable, so on BSD/etc. there will be an orphan process left around if uxn11 is killed. (In both cases an emulator-driven exit will end the subprocess.) The mp3 ROM will currently interpret each line of `mpg123 -R` output, and dispatch to the appropriate subroutine. None of these do anything yet.
This commit is contained in:
parent
436e579df4
commit
75c62e88fb
|
@ -0,0 +1,131 @@
|
|||
( mp3.tal )
|
||||
|
||||
( commands are ended by newlines: )
|
||||
( )
|
||||
( help show all these commands )
|
||||
( load FILE load FILE and start playing )
|
||||
( loadpaused FILE load FILE without playing )
|
||||
( pause pause, or unpause, playback )
|
||||
( stop stops playback, unloads file )
|
||||
( seek # seeks to sample # )
|
||||
( seek +N | seek -N seeks forward, or back, N samples )
|
||||
( seek +Ns | seek -Ns seeks forward, or back, N seconds )
|
||||
( jump # jumps to MPEG frame # )
|
||||
( jump +N | jump -N jumps forward, or back, N frames )
|
||||
( jump +Ns | jump -Ns jumps forward, or back, N seconds )
|
||||
( volume P set volume to P percent, 0-100 )
|
||||
( mute mute playback )
|
||||
( unmute unmute playback )
|
||||
( tag print all ID3 data; seeking back may remove tag data )
|
||||
|
||||
@Console [
|
||||
|10 &vector $2
|
||||
|12 &read $1
|
||||
( 13 - 14 padding )
|
||||
|15 &live $1
|
||||
|15 &exit $1
|
||||
|17 &type $1
|
||||
|18 &write $1
|
||||
|19 &error $1
|
||||
( 1a - 1b padding )
|
||||
|1c &addr $2
|
||||
|1e &mode $1
|
||||
|1f &exec $1
|
||||
]
|
||||
|
||||
|0100
|
||||
( initialize buffer )
|
||||
;buffer ;buffer/pos STA2
|
||||
|
||||
( run mpg123 )
|
||||
;on-console .Console/vector DEO2
|
||||
;program .Console/addr DEO2 ( cmd addr )
|
||||
#03 .Console/mode DEO ( cmd mode )
|
||||
#01 .Console/exec DEO ( exec )
|
||||
|
||||
;cmd1 print
|
||||
;cmd2 print
|
||||
|
||||
BRK
|
||||
|
||||
@printerr ( s* -> )
|
||||
LDAk ?{ #0a .Console/error DEO POP2 JMP2r }
|
||||
LDAk .Console/error DEO INC2 !printerr
|
||||
|
||||
@print ( s* -> )
|
||||
LDAk ?{ POP2 JMP2r }
|
||||
LDAk .Console/write DEO INC2 !print
|
||||
|
||||
@on-console ( -> brk )
|
||||
.Console/type DEI #01 EQU ?{ BRK } ( )
|
||||
.Console/read DEI #0a EQU ?&newline ( )
|
||||
;buffer/pos LDA2k STH2k ( pos* buf* [buf*] )
|
||||
.Console/read DEI STH2r STA ( pos* buf ; buf<-c )
|
||||
INC2 SWP2 STA2 BRK ( ; pos<-buf+1 )
|
||||
&newline ( )
|
||||
#00 ;buffer/pos LDA2 STA on-line ( ; buf<-0, run on-line )
|
||||
;buffer ;buffer/pos STA2 BRK ( )
|
||||
|
||||
|
||||
( called when a newline is reached )
|
||||
( buffer is guaranteed to be null-terminated )
|
||||
( newline is implied but not included )
|
||||
@on-line ( -> )
|
||||
;buffer LDAk LIT "@ EQU ?{ POP2 JMP2r }
|
||||
INC2k LDA LIT "F EQU ?on-frame
|
||||
( INC2k LDA LIT "H EQU ?on-help )
|
||||
( INC2k LDA LIT "I EQU ?on-id3 )
|
||||
( INC2k LDA LIT "P EQU ?on-paused )
|
||||
( INC2k LDA LIT "R EQU ?on-revision )
|
||||
( INC2k LDA LIT "S EQU ?on-status )
|
||||
( INC2k LDA LIT "T EQU ?on-tag )
|
||||
!printerr
|
||||
|
||||
( e.g. "@F 184 8713 4.81 227.60" )
|
||||
( seen when playing )
|
||||
( the fields here are: )
|
||||
( @F <curr-frame> <total-frames> <curr-secs> <total-secs> )
|
||||
@on-frame ( buf* -> )
|
||||
POP2 JMP2r
|
||||
|
||||
( e.g. "@H HELP/H: command listing (LONG/SHORT forms), command case insensitve" )
|
||||
( seen in response to the "help" command )
|
||||
@on-help ( buf* -> )
|
||||
POP2r JMP2r
|
||||
|
||||
( e.g. "@I ID3v2.artist:Chipzel" )
|
||||
( seen when loading a track )
|
||||
@on-id3 ( buf* -> )
|
||||
POP2 JMP2r
|
||||
|
||||
( e.g. "@P 0" or "@P 1" )
|
||||
( seen when pausing or unpausing )
|
||||
@on-paused ( buf* -> )
|
||||
POP2 JMP2r
|
||||
|
||||
( e.g. "@R MPG123 (ThOr) v10" )
|
||||
( seen on start up )
|
||||
@on-revision ( buf* -> )
|
||||
POP2 JMP2r
|
||||
|
||||
( e.g. "@S 1.0 3 44100 Joint-Stereo 0 1044 2 0 0 0 320 0 1" )
|
||||
( seen when playback starts )
|
||||
@on-status ( buf* -> )
|
||||
POP2 JMP2r
|
||||
|
||||
( e.g. "@T ID3v2.TPE1:" )
|
||||
( seen in response to the "tag" command for extended tag info )
|
||||
@on-tag ( buf* -> )
|
||||
POP2 JMP2r
|
||||
|
||||
@program
|
||||
"mpg123 20 "-R 00
|
||||
|
||||
@cmd1
|
||||
"loadpaused 20 "tokyo-skies.mp3 0a 00
|
||||
|
||||
@cmd2
|
||||
"pause 0a 00
|
||||
|
||||
|
||||
@buffer $200 &pos $2 ( input buffer )
|
|
@ -1,5 +1,22 @@
|
|||
#undef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __linux
|
||||
#include <pty.h>
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
#ifdef __NetBSD__
|
||||
#include <sys/ioctl.h>
|
||||
#include <util.h>
|
||||
#endif
|
||||
|
||||
#include "../uxn.h"
|
||||
#include "console.h"
|
||||
|
@ -15,6 +32,164 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|||
WITH REGARD TO THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* subprocess support */
|
||||
static char *fork_args[4] = {"/bin/sh", "-c", "", NULL};
|
||||
static int child_mode;
|
||||
static int to_child_fd[2];
|
||||
static int from_child_fd[2];
|
||||
static int saved_in;
|
||||
static int saved_out;
|
||||
static pid_t child_pid;
|
||||
|
||||
/* child_mode:
|
||||
* 0x01: writes to child's stdin
|
||||
* 0x02: reads from child's stdout
|
||||
* 0x04: reads from child's stderr
|
||||
* 0x08: kill previous process (if any) but do not start
|
||||
* (other bits ignored for now )
|
||||
*/
|
||||
|
||||
#define CMD_LIVE 0x15 // 0x00 not started, 0x01 running, 0xff dead
|
||||
#define CMD_EXIT 0x16 // if dead, exit code of process
|
||||
#define CMD_ADDR 0x1c // address to read command args from
|
||||
#define CMD_MODE 0x1e // mode to execute, 0x00 to 0x07
|
||||
#define CMD_EXEC 0x1f // write to execute programs, etc
|
||||
|
||||
/* call after we're sure the process has exited */
|
||||
static void
|
||||
clean_after_child(void)
|
||||
{
|
||||
child_pid = 0;
|
||||
if(child_mode & 0x01) {
|
||||
close(to_child_fd[1]);
|
||||
dup2(saved_out, 1);
|
||||
}
|
||||
if(child_mode & (0x04 | 0x02)) {
|
||||
close(from_child_fd[0]);
|
||||
dup2(saved_in, 0);
|
||||
}
|
||||
child_mode = 0;
|
||||
saved_in = -1;
|
||||
saved_out = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
start_fork_pipe(void)
|
||||
{
|
||||
fflush(stdout);
|
||||
pid_t pid;
|
||||
pid_t parent_pid = getpid();
|
||||
int addr = PEEK2(&uxn.dev[CMD_ADDR]);
|
||||
if(child_mode & 0x08) {
|
||||
uxn.dev[CMD_EXIT] = uxn.dev[CMD_LIVE] = 0x00;
|
||||
return;
|
||||
}
|
||||
if(child_mode & 0x01) {
|
||||
/* parent writes to child's stdin */
|
||||
if(pipe(to_child_fd) == -1) {
|
||||
uxn.dev[CMD_EXIT] = uxn.dev[CMD_LIVE] = 0xff;
|
||||
fprintf(stderr, "pipe error: to child\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(child_mode & (0x04 | 0x02)) {
|
||||
/* parent reads from child's stdout and/or stderr */
|
||||
if(pipe(from_child_fd) == -1) {
|
||||
uxn.dev[CMD_EXIT] = uxn.dev[CMD_LIVE] = 0xff;
|
||||
fprintf(stderr, "pipe error: from child\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fork_args[2] = (char *)&uxn.ram[addr];
|
||||
pid = fork();
|
||||
if(pid < 0) { /* failure */
|
||||
uxn.dev[CMD_EXIT] = uxn.dev[CMD_LIVE] = 0xff;
|
||||
fprintf(stderr, "fork failure\n");
|
||||
} else if(pid == 0) { /* child */
|
||||
|
||||
#ifdef __linux__
|
||||
int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||||
if (r == -1) { perror(0); exit(6); }
|
||||
// test in case the original parent exited just
|
||||
// before the prctl() call
|
||||
if (getppid() != parent_pid) exit(13);
|
||||
#endif
|
||||
|
||||
if(child_mode & 0x01) {
|
||||
dup2(to_child_fd[0], 0);
|
||||
close(to_child_fd[1]);
|
||||
}
|
||||
if(child_mode & (0x04 | 0x02)) {
|
||||
if(child_mode & 0x02) dup2(from_child_fd[1], 1);
|
||||
if(child_mode & 0x04) dup2(from_child_fd[1], 2);
|
||||
close(from_child_fd[0]);
|
||||
}
|
||||
fflush(stdout);
|
||||
execvp(fork_args[0], fork_args);
|
||||
exit(1);
|
||||
} else { /*parent*/
|
||||
child_pid = pid;
|
||||
uxn.dev[CMD_LIVE] = 0x01;
|
||||
uxn.dev[CMD_EXIT] = 0x00;
|
||||
if(child_mode & 0x01) {
|
||||
saved_out = dup(1);
|
||||
dup2(to_child_fd[1], 1);
|
||||
close(to_child_fd[0]);
|
||||
}
|
||||
if(child_mode & (0x04 | 0x02)) {
|
||||
saved_in = dup(0);
|
||||
dup2(from_child_fd[0], 0);
|
||||
close(from_child_fd[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_child(void)
|
||||
{
|
||||
int wstatus;
|
||||
if(child_pid) {
|
||||
if(waitpid(child_pid, &wstatus, WNOHANG)) {
|
||||
uxn.dev[CMD_LIVE] = 0xff;
|
||||
uxn.dev[CMD_EXIT] = WEXITSTATUS(wstatus);
|
||||
clean_after_child();
|
||||
} else {
|
||||
uxn.dev[CMD_LIVE] = 0x01;
|
||||
uxn.dev[CMD_EXIT] = 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
kill_child(void)
|
||||
{
|
||||
int wstatus;
|
||||
if(child_pid) {
|
||||
kill(child_pid, 9);
|
||||
if(waitpid(child_pid, &wstatus, WNOHANG)) {
|
||||
uxn.dev[CMD_LIVE] = 0xff;
|
||||
uxn.dev[CMD_EXIT] = WEXITSTATUS(wstatus);
|
||||
clean_after_child();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
start_fork(void)
|
||||
{
|
||||
fflush(stderr);
|
||||
kill_child();
|
||||
child_mode = uxn.dev[CMD_MODE];
|
||||
start_fork_pipe();
|
||||
}
|
||||
|
||||
void
|
||||
close_console(void)
|
||||
{
|
||||
kill_child();
|
||||
}
|
||||
|
||||
int
|
||||
console_input(Uint8 c, int type)
|
||||
{
|
||||
|
@ -33,6 +208,16 @@ console_listen(int i, int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
Uint8
|
||||
console_dei(Uint8 addr)
|
||||
{
|
||||
switch(addr) {
|
||||
case CMD_LIVE:
|
||||
case CMD_EXIT: check_child(); break;
|
||||
}
|
||||
return uxn.dev[addr];
|
||||
}
|
||||
|
||||
void
|
||||
console_deo(Uint8 addr)
|
||||
{
|
||||
|
@ -40,5 +225,6 @@ console_deo(Uint8 addr)
|
|||
switch(addr) {
|
||||
case 0x18: fd = stdout, fputc(uxn.dev[0x18], fd), fflush(fd); break;
|
||||
case 0x19: fd = stderr, fputc(uxn.dev[0x19], fd), fflush(fd); break;
|
||||
case CMD_EXEC: start_fork(); break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,3 +18,4 @@ int console_input(Uint8 c, int type);
|
|||
void console_listen(int i, int argc, char **argv);
|
||||
Uint8 console_dei(Uint8 addr);
|
||||
void console_deo(Uint8 addr);
|
||||
void close_console(void);
|
||||
|
|
|
@ -49,6 +49,7 @@ emu_dei(Uint8 addr)
|
|||
{
|
||||
switch(addr & 0xf0) {
|
||||
case 0x00: return system_dei(addr);
|
||||
case 0x10: return console_dei(addr);
|
||||
case 0x20: return screen_dei(addr);
|
||||
case 0xc0: return datetime_dei(addr);
|
||||
}
|
||||
|
@ -89,6 +90,7 @@ emu_resize(int w, int h)
|
|||
static void
|
||||
emu_restart(char *rom, int soft)
|
||||
{
|
||||
close_console();
|
||||
screen_resize(WIDTH, HEIGHT, uxn_screen.scale);
|
||||
screen_rect(uxn_screen.bg, 0, 0, uxn_screen.width, uxn_screen.height, 0);
|
||||
screen_rect(uxn_screen.fg, 0, 0, uxn_screen.width, uxn_screen.height, 0);
|
||||
|
@ -98,6 +100,7 @@ emu_restart(char *rom, int soft)
|
|||
static int
|
||||
emu_end(void)
|
||||
{
|
||||
close_console();
|
||||
free(uxn.ram);
|
||||
XDestroyImage(ximage);
|
||||
XDestroyWindow(display, window);
|
||||
|
|
|
@ -25,6 +25,7 @@ emu_dei(Uint8 addr)
|
|||
{
|
||||
switch(addr & 0xf0) {
|
||||
case 0x00: return system_dei(addr);
|
||||
case 0x10: return console_dei(addr);
|
||||
case 0xc0: return datetime_dei(addr);
|
||||
}
|
||||
return uxn.dev[addr];
|
||||
|
|
Loading…
Reference in New Issue