commit 8f291ff

zzo38  ·  2023-06-21 20:01:43 +0000 UTC
parent f88b636
Implement the -m switch.
2 files changed,  +141, -2
M main.c
+39, -0
 1@@ -73,6 +73,10 @@
 2 -j
 3   Enable key repeat for joypad emulation.
 4 
 5+-m #
 6+  Specify memory writes and device writes to do before or after loading the
 7+  file. See the "Memory/device scripts" section below for details.
 8+
 9 -n
10   Disable GUI. Either this switch or -I is required in order to read stdin.
11   If neither -n nor -I is specified, then stdin cannot be read (although
12@@ -323,3 +327,38 @@ expansion commands to activate them).
13   What happens if you then try to change colours using the colour ports
14   after doing this, is not specified.
15 
16+
17+=== Memory/device scripts ===
18+
19+A command with the -m switch has the subcommands which have the format of
20+an optional "." to indicate executed after instead of before loading, and
21+then an optional letter "g" to "z" to specify the specific command (if none
22+is specified, then it is a memory command), and then the address in hex,
23+and then optionally = and the data in hex. The address is treated as a pure
24+number while the data is a sequence of bytes so the number of digits must
25+be an even number.
26+
27+It can also optionally be @ and a file name, in which case the commands are
28+loaded from a file. The file cannot itself have @ commands, and it may have
29+lines starting with # as comments.
30+
31+The subcommands are:
32+
33+Memory (no letter)
34+  Write memory starting at the specified address.
35+
36+Device trigger (p)
37+  Write and trigger devices starting at the specified address. If there are
38+  multiple bytes of data, then multiple consecutive ports are written to
39+  and are triggered.
40+
41+Device set (s)
42+  Same as device trigger but omit the trigger; only writes are done.
43+
44+Execute (x)
45+  Execute uxn code at the specified address, which must be nonzero, and
46+  cannot be in expanded memory. (The data of this command is not used.)
47+
48+During the execution of any of these commands, it behaves as though the -x
49+switch is specified, even if it isn't specified.
50+
M main.c
+102, -2
  1@@ -101,6 +101,13 @@ typedef struct {
  2   Uint8 vol[2];
  3 } UxnAudio;
  4 
  5+typedef struct {
  6+  Uint8*data;
  7+  Uint32 addr;
  8+  Uint16 len;
  9+  Uint8 kind,stage;
 10+} Script;
 11+
 12 static SDL_Color colors[64]={
 13   // 0
 14   {0xFF,0xFF,0xFF,0},
 15@@ -227,6 +234,10 @@ static Uint8 palet=7;
 16 static Uint8 eof_trigger=0;
 17 static Uint8 touch_mode=0;
 18 
 19+static Uint16 scriptc;
 20+static Script*scriptv;
 21+static FILE*scriptf;
 22+
 23 static Sint32*audio_option;
 24 static SDL_AudioSpec audiospec;
 25 
 26@@ -1525,6 +1536,87 @@ static void set_palette(char*s) {
 27   fclose(f);
 28 }
 29 
 30+static void set_script(char*s) {
 31+  static size_t siz;
 32+  Script o={0,0,0,0,0};
 33+  if(!scriptf) {
 34+    scriptf=open_memstream((char**)&scriptv,&siz);
 35+    if(!scriptf) err(1,"Error with open_memstream during parsing command-line arguments");
 36+  }
 37+  if(*s=='@') {
 38+    char*line=0;
 39+    size_t line_size=0;
 40+    FILE*fp=fopen(s+1,"r");
 41+    if(!fp) err(1,"Cannot open file \"%s\"",s+1);
 42+    while(getline(&line,&line_size,fp)>0) {
 43+      s=line+strlen(line);
 44+      while(s>line && (s[-1]=='\r' || s[-1]=='\n' || s[-1]==' ' || s[-1]=='\t')) *--s=0;
 45+      if(*s=='@') errx(1,"Nested file in -m not allowed");
 46+      if(*s && *s!='#') set_script(s);
 47+    }
 48+    fclose(fp);
 49+    free(line);
 50+  } else {
 51+    Uint8 b;
 52+    if(*s=='.') o.stage=1,s++;
 53+    if(*s>'f') o.kind=*s++;
 54+    o.addr=strtoll(s,&s,16);
 55+    if(*s=='=') {
 56+      s++;
 57+      if(strlen(s)>=0x1FFFF) errx(1,"Argument too long");
 58+      o.data=malloc(strlen(s)/2+1);
 59+      if(!o.data) err(1,"Allocation failed");
 60+      while(*s && s[1]) {
 61+        if(!sscanf(s,"%2hhX",&b)) break;
 62+        o.data[o.len++]=b;
 63+        s+=2;
 64+      }
 65+      if(*s) errx(1,"Syntax error in -m");
 66+    } else if(*s) {
 67+      errx(1,"Syntax error in -m");
 68+    }
 69+    if(o.kind || o.len) {
 70+      if(!fwrite(&o,sizeof(Script),1,scriptf)) err(1,"Cannot write to memory stream");
 71+      ++scriptc;
 72+    }
 73+  }
 74+}
 75+
 76+static void do_script(Uint8 stage) {
 77+  Uint8 u=use_extension;
 78+  Uint16 n,i,j;
 79+  Script*o;
 80+  use_extension=1;
 81+  for(n=0;n<scriptc;n++) if(scriptv[n].stage==stage) {
 82+    o=scriptv+n;
 83+    switch(o->kind) {
 84+      case 0: // Memory write
 85+        memcpy(mem+o->addr,o->data,o->len);
 86+        break;
 87+      case 'p': // Device port (including trigger)
 88+        j=o->addr;
 89+        for(i=0;i<o->len;i++) {
 90+          device[(j>>4)&15].d[j&15]=o->data[i];
 91+          device[(j>>4)&15].out(device+((j>>4)&15),j&15);
 92+          j++;
 93+        }
 94+        break;
 95+      case 's': // Device port (set only)
 96+        j=o->addr;
 97+        for(i=0;i<o->len;i++) {
 98+          device[(j>>4)&15].d[j&15]=o->data[i];
 99+          j++;
100+        }
101+        break;
102+      case 'x': // Execute
103+        run(o->addr);
104+        break;
105+      default: errx(1,"Unrecognized command -m%c",o->kind);
106+    }
107+  }
108+  use_extension=u;
109+}
110+
111 int main(int argc,char**argv) {
112   int i,j;
113   for(i=0;i<16;i++) {
114@@ -1538,7 +1630,7 @@ int main(int argc,char**argv) {
115   device[11].aux=&uxnfile1;
116   device[12].in=datetime_in;
117   inf=stdin; outf=stdout;
118-  while((i=getopt(argc,argv,"+ADFIJ:NOQST:YZa:de:h:ijnp:qs:t:w:xyz:"))>0) switch(i) {
119+  while((i=getopt(argc,argv,"+ADFIJ:NOQST:YZa:de:h:ijm:np:qs:t:w:xyz:"))>0) switch(i) {
120     case 'D': scrflags|=SDL_DOUBLEBUF; break;
121     case 'F': scrflags|=SDL_FULLSCREEN; break;
122     case 'I': use_thread=1; break;
123@@ -1556,6 +1648,7 @@ int main(int argc,char**argv) {
124     case 'h': default_height=strtol(optarg,0,10); break;
125     case 'i': hide_cursor=1; break;
126     case 'j': joypad_repeat=1; break;
127+    case 'm': set_script(optarg); break;
128     case 'n': use_screen=0; break;
129     case 'p': palet=strtol(optarg,0,10)&7; if(*optarg && optarg[1]=='=') set_palette(optarg+2); break;
130     case 'q': use_console=0; break;
131@@ -1569,6 +1662,12 @@ int main(int argc,char**argv) {
132   }
133   if(optind==argc) errx(1,"Required argument missing");
134   rom_name=argv[optind];
135+  if(use_console) device[1].out=console_out;
136+  if(scriptf) {
137+    fclose(scriptf);
138+    if(!scriptv) err(1,"Allocation failed");
139+    do_script(0);
140+  }
141   load_rom();
142   if(use_screen) {
143     if(zoom<1) errx(1,"Zoom out of range");
144@@ -1589,13 +1688,13 @@ int main(int argc,char**argv) {
145       SDL_JoystickEventState(SDL_ENABLE);
146     }
147   }
148-  if(use_console) device[1].out=console_out;
149   if(optind+1<argc) strncpy(uxnfile0.name,argv[optind+1],4095);
150   if(optind+2<argc) strncpy(uxnfile1.name,argv[optind+2],4095);
151   restart:
152   size_changed=1;
153   ds.p=rs.p=0;
154   device[1].d[7]=(optind+1==argc)?0:1;
155+  if(scriptc) do_script(1);
156   run(0x0100);
157   if(device[1].d[0] || device[1].d[1]) for(i=optind+1;i<argc;i++) {
158     for(j=0;argv[i][j];j++) {
159@@ -1612,6 +1711,7 @@ int main(int argc,char**argv) {
160     if(use_thread) SDL_CreateThread(stdin_thread_fn,0);
161     while(run_screen()) {
162       for(i=0;i<15;i++) for(j=0;j<16;j++) device[i].d[j]=0;
163+      if(scriptc) do_script(0);
164       load_rom();
165       goto restart;
166     }