Subversion Repositories SvarDOS

Rev

Rev 350 | Rev 352 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 350 Rev 351
1
/*
1
/*
2
 * SvarCOM is a command-line interpreter.
2
 * SvarCOM is a command-line interpreter.
3
 *
3
 *
4
 * a little memory area is allocated as high as possible. it contains:
4
 * a little memory area is allocated as high as possible. it contains:
5
 *  - a signature (like XMS drivers do)
5
 *  - a signature (like XMS drivers do)
6
 *  - a routine for exec'ing programs
6
 *  - a routine for exec'ing programs
7
 *  - a "last command" buffer for input history
7
 *  - a "last command" buffer for input history
8
 *
8
 *
9
 * when svarcom starts, it tries locating the routine in memory.
9
 * when svarcom starts, it tries locating the routine in memory.
10
 *
10
 *
11
 * if found:
11
 * if found:
12
 *   waits for user input and processes it. if execing something is required, set the "next exec" field in routine's memory and quit.
12
 *   waits for user input and processes it. if execing something is required, set the "next exec" field in routine's memory and quit.
13
 *
13
 *
14
 * if not found:
14
 * if not found:
15
 *   installs it by creating a new PSP, set int 22 vector to the routine, set my "parent PSP" to the routine
15
 *   installs it by creating a new PSP, set int 22 vector to the routine, set my "parent PSP" to the routine
16
 *   and quit.
16
 *   and quit.
17
 *
17
 *
18
 *
18
 *
19
 *
19
 *
20
 * good lecture about PSP and allocating memory
20
 * good lecture about PSP and allocating memory
21
 * https://retrocomputing.stackexchange.com/questions/20001/how-much-of-the-program-segment-prefix-area-can-be-reused-by-programs-with-impun/20006#20006
21
 * https://retrocomputing.stackexchange.com/questions/20001/how-much-of-the-program-segment-prefix-area-can-be-reused-by-programs-with-impun/20006#20006
22
 *
22
 *
23
 * PSP structure
23
 * PSP structure
24
 * http://www.piclist.com/techref/dos/psps.htm
24
 * http://www.piclist.com/techref/dos/psps.htm
25
 *
25
 *
26
 *
26
 *
27
 *
27
 *
28
 * === MCB ===
28
 * === MCB ===
29
 *
29
 *
30
 * each time that DOS allocates memory, it prefixes the allocated memory with
30
 * each time that DOS allocates memory, it prefixes the allocated memory with
31
 * a 16-bytes structure called a "Memory Control Block" (MCB). This control
31
 * a 16-bytes structure called a "Memory Control Block" (MCB). This control
32
 * block has the following structure:
32
 * block has the following structure:
33
 *
33
 *
34
 * Offset  Size     Description
34
 * Offset  Size     Description
35
 *   00h   byte     'M' =  non-last member of the MCB chain
35
 *   00h   byte     'M' =  non-last member of the MCB chain
36
 *                  'Z' = indicates last entry in MCB chain
36
 *                  'Z' = indicates last entry in MCB chain
37
 *                  other values cause "Memory Allocation Failure" on exit
37
 *                  other values cause "Memory Allocation Failure" on exit
38
 *   01h   word     PSP segment address of the owner (Process Id)
38
 *   01h   word     PSP segment address of the owner (Process Id)
39
 *                  possible values:
39
 *                  possible values:
40
 *                    0 = free
40
 *                    0 = free
41
 *                    8 = Allocated by DOS before first user pgm loaded
41
 *                    8 = Allocated by DOS before first user pgm loaded
42
 *                    other = Process Id/PSP segment address of owner
42
 *                    other = Process Id/PSP segment address of owner
43
 *   03h  word      number of paragraphs related to this MCB (excluding MCB)
43
 *   03h  word      number of paragraphs related to this MCB (excluding MCB)
44
 *   05h  11 bytes  reserved
44
 *   05h  11 bytes  reserved
45
 *   10h  ...       start of actual allocated memory block
45
 *   10h  ...       start of actual allocated memory block
46
 */
46
 */
47
 
47
 
48
#include <i86.h>
48
#include <i86.h>
49
#include <dos.h>
49
#include <dos.h>
50
#include <stdio.h>
50
#include <stdio.h>
51
#include <stdlib.h>
51
#include <stdlib.h>
52
#include <string.h>
52
#include <string.h>
53
 
53
 
54
#include <process.h>
54
#include <process.h>
55
 
55
 
56
#include "rmod.h"
56
#include "rmodinit.h"
57
 
57
 
58
struct config {
58
struct config {
59
  int locate;
59
  int locate;
60
  int install;
60
  int install;
61
  int envsiz;
61
  int envsiz;
62
} cfg;
62
} cfg;
63
 
63
 
64
 
64
 
65
#define RMOD_OFFSET_ENVSEG  0x08
-
 
66
#define RMOD_OFFSET_INPBUFF 0x0A
-
 
67
#define RMOD_OFFSET_ROUTINE 0x8C
-
 
68
 
-
 
69
/* returns segment where rmod is installed */
-
 
70
unsigned short rmod_install(unsigned short envsize) {
-
 
71
  char far *myptr, far *mcb;
-
 
72
  unsigned short far *owner;
-
 
73
  unsigned int rmodseg = 0xffff;
-
 
74
  unsigned int envseg = 0;
-
 
75
 
-
 
76
  /* read my current env segment from PSP */
-
 
77
  _asm {
-
 
78
    push ax
-
 
79
    push bx
-
 
80
    mov bx, 0x2c
-
 
81
    mov ax, [bx]
-
 
82
    mov envseg, ax
-
 
83
    pop bx
-
 
84
    pop ax
-
 
85
  }
-
 
86
 
-
 
87
  printf("original (PSP) env buffer at %04X\r\n", envseg);
-
 
88
  /* if custom envsize requested, convert it to number of paragraphs */
-
 
89
  if (envsize != 0) {
-
 
90
    envsize += 15;
-
 
91
    envsize /= 16;
-
 
92
  }
-
 
93
 
-
 
94
 
-
 
95
  _asm {
-
 
96
    /* link in the UMB memory chain for enabling high-memory allocation (and save initial status on stack) */
-
 
97
    mov ax, 0x5802  /* GET UMB LINK STATE */
-
 
98
    int 0x21
-
 
99
    xor ah, ah
-
 
100
    push ax         /* save link state on stack */
-
 
101
    mov ax, 0x5803  /* SET UMB LINK STATE */
-
 
102
    mov bx, 1
-
 
103
    int 0x21
-
 
104
    /* get current allocation strategy and save it in DX */
-
 
105
    mov ax, 0x5800
-
 
106
    int 0x21
-
 
107
    push ax
-
 
108
    pop dx
-
 
109
    /* set strategy to 'last fit, try high then low memory' */
-
 
110
    mov ax, 0x5801
-
 
111
    mov bx, 0x0082
-
 
112
    int 0x21
-
 
113
    /* ask for a memory block and save the given segment to rmodseg */
-
 
114
    mov ah, 0x48
-
 
115
    mov bx, (rmod_len + 15) / 16
-
 
116
    int 0x21
-
 
117
    jc ALLOC_FAIL
-
 
118
    mov rmodseg, ax
-
 
119
    /* ask for a memory block for the environment and save it to envseg (only if custom size requested) */
-
 
120
    mov bx, envsize
-
 
121
    test bx, bx
-
 
122
    jz ALLOC_FAIL
-
 
123
    mov ah, 0x48
-
 
124
    int 0x21
-
 
125
    jc ALLOC_FAIL
-
 
126
    mov envseg, ax
-
 
127
 
-
 
128
    ALLOC_FAIL:
-
 
129
    /* restore initial allocation strategy */
-
 
130
    mov ax, 0x5801
-
 
131
    mov bx, dx
-
 
132
    int 0x21
-
 
133
    /* restore initial UMB memory link state */
-
 
134
    mov ax, 0x5803
-
 
135
    pop bx       /* pop initial UMB link state from stack */
-
 
136
    int 0x21
-
 
137
  }
-
 
138
 
-
 
139
  if (rmodseg == 0xffff) {
-
 
140
    puts("malloc error");
-
 
141
    return(0xffff);
-
 
142
  }
-
 
143
 
-
 
144
  /* copy rmod to its destination */
-
 
145
  myptr = MK_FP(rmodseg, 0);
-
 
146
  _fmemcpy(myptr, rmod, rmod_len);
-
 
147
 
-
 
148
  /* mark rmod memory as "self owned" */
-
 
149
  mcb = MK_FP(rmodseg - 1, 0);
-
 
150
  owner = (void far *)(mcb + 1);
-
 
151
  *owner = rmodseg;
-
 
152
  _fmemcpy(mcb + 8, "SVARCOM", 8);
-
 
153
 
-
 
154
  /* mark env memory as "self owned" (only if allocated by me) */
-
 
155
  if (envsize != 0) {
-
 
156
    printf("envseg allocated at %04X:0000 with %u paragraphs\r\n", envseg, envsize);
-
 
157
    mcb = MK_FP(envseg - 1, 0);
-
 
158
    owner = (void far *)(mcb + 1);
-
 
159
    *owner = rmodseg;
-
 
160
    _fmemcpy(mcb + 8, "SVARENV", 8);
-
 
161
  }
-
 
162
 
-
 
163
  /* write env segment to rmod buffer */
-
 
164
  owner = MK_FP(rmodseg, RMOD_OFFSET_ENVSEG);
-
 
165
  *owner = envseg;
-
 
166
 
-
 
167
/*
-
 
168
  printf("MCB sig: %c\r\nMCB owner: 0x%04X\r\n", mcb[0], *owner);
-
 
169
  {
-
 
170
    int i;
-
 
171
    for (i = 0; i < 17; i++) {
-
 
172
      printf("%02x ", mcb[i]);
-
 
173
    }
-
 
174
    printf("\r\n");
-
 
175
    for (i = 0; i < 17; i++) {
-
 
176
      if (mcb[i] > ' ') {
-
 
177
        printf(" %c ", mcb[i]);
-
 
178
      } else {
-
 
179
        printf(" . ", mcb[i]);
-
 
180
      }
-
 
181
    }
-
 
182
    printf("\r\n");
-
 
183
  }*/
-
 
184
 
-
 
185
  return(rmodseg);
-
 
186
}
-
 
187
 
-
 
188
 
-
 
189
/* returns zero if s1 starts with s2 */
65
/* returns zero if s1 starts with s2 */
190
static int strstartswith(const char *s1, const char *s2) {
66
static int strstartswith(const char *s1, const char *s2) {
191
  while (*s2 != 0) {
67
  while (*s2 != 0) {
192
    if (*s1 != *s2) return(-1);
68
    if (*s1 != *s2) return(-1);
193
    s1++;
69
    s1++;
194
    s2++;
70
    s2++;
195
  }
71
  }
196
  return(0);
72
  return(0);
197
}
73
}
198
 
74
 
199
 
75
 
200
static void parse_argv(struct config *cfg, int argc, char **argv) {
76
static void parse_argv(struct config *cfg, int argc, char **argv) {
201
  int i;
77
  int i;
202
  memset(cfg, 0, sizeof(*cfg));
78
  memset(cfg, 0, sizeof(*cfg));
203
 
79
 
204
  for (i = 1; i < argc; i++) {
80
  for (i = 1; i < argc; i++) {
205
    if (strcmp(argv[i], "/locate") == 0) {
81
    if (strcmp(argv[i], "/locate") == 0) {
206
      cfg->locate = 1;
82
      cfg->locate = 1;
207
    }
83
    }
208
    if (strstartswith(argv[i], "/e:") == 0) {
84
    if (strstartswith(argv[i], "/e:") == 0) {
209
      cfg->envsiz = atoi(argv[i]+3);
85
      cfg->envsiz = atoi(argv[i]+3);
210
      if (cfg->envsiz < 64) cfg->envsiz = 0;
86
      if (cfg->envsiz < 64) cfg->envsiz = 0;
211
    }
87
    }
212
  }
88
  }
213
}
89
}
214
 
90
 
215
 
91
 
216
/* scan memory for my shared buffer, return segment of buffer */
-
 
217
static unsigned short rmod_find(void) {
-
 
218
  unsigned short i;
-
 
219
  unsigned short far *pattern;
-
 
220
 
-
 
221
  /* iterate over all paragraphs, looking for my signature */
-
 
222
  for (i = 0; i != 65535; i++) {
-
 
223
    pattern = MK_FP(i, 0);
-
 
224
    if (pattern[0] != 0x1983) continue;
-
 
225
    if (pattern[1] != 0x1985) continue;
-
 
226
    if (pattern[2] != 0x2017) continue;
-
 
227
    if (pattern[3] != 0x2019) continue;
-
 
228
    return(i);
-
 
229
  }
-
 
230
  return(0xffff);
-
 
231
}
-
 
232
 
-
 
233
 
-
 
234
static int explode_progparams(char *s, char const **argvlist) {
92
static int explode_progparams(char *s, char const **argvlist) {
235
  int si = 0, argc = 0;
93
  int si = 0, argc = 0;
236
  for (;;) {
94
  for (;;) {
237
    /* skip to next non-space character */
95
    /* skip to next non-space character */
238
    while (s[si] == ' ') si++;
96
    while (s[si] == ' ') si++;
239
    /* end of string? */
97
    /* end of string? */
240
    if (s[si] == '\r') break;
98
    if (s[si] == '\r') break;
241
    /* set argv ptr */
99
    /* set argv ptr */
242
    argvlist[argc++] = s + si;
100
    argvlist[argc++] = s + si;
243
    /* find next space */
101
    /* find next space */
244
    while (s[si] != ' ' && s[si] != '\r') si++;
102
    while (s[si] != ' ' && s[si] != '\r') si++;
245
    /* is this end of string? */
103
    /* is this end of string? */
246
    if (s[si] == '\r') {
104
    if (s[si] == '\r') {
247
      s[si] = 0;
105
      s[si] = 0;
248
      break;
106
      break;
249
    }
107
    }
250
    /* not end: terminate arg and look for next one */
108
    /* not end: terminate arg and look for next one */
251
    s[si++] = 0;
109
    s[si++] = 0;
252
  }
110
  }
253
  /* terminate argvlist with a NULL value */
111
  /* terminate argvlist with a NULL value */
254
  argvlist[argc] = NULL;
112
  argvlist[argc] = NULL;
255
  return(argc);
113
  return(argc);
256
}
114
}
257
 
115
 
258
 
116
 
259
static void cmd_set(int argc, char const **argv, unsigned short env_seg) {
117
static void cmd_set(int argc, char const **argv, unsigned short env_seg) {
260
  char far *env = MK_FP(env_seg, 0);
118
  char far *env = MK_FP(env_seg, 0);
261
  char buff[256];
119
  char buff[256];
262
  int i;
120
  int i;
263
  while (*env != 0) {
121
  while (*env != 0) {
264
    /* copy string to local buff for display */
122
    /* copy string to local buff for display */
265
    for (i = 0;; i++) {
123
    for (i = 0;; i++) {
266
      buff[i] = *env;
124
      buff[i] = *env;
267
      env++;
125
      env++;
268
      if (buff[i] == 0) break;
126
      if (buff[i] == 0) break;
269
    }
127
    }
270
    puts(buff);
128
    puts(buff);
271
  }
129
  }
272
}
130
}
273
 
131
 
274
 
132
 
275
int main(int argc, char **argv) {
133
int main(int argc, char **argv) {
276
  struct config cfg;
134
  struct config cfg;
277
  unsigned short rmod_seg;
135
  unsigned short rmod_seg;
278
  unsigned short far *rmod_envseg;
136
  unsigned short far *rmod_envseg;
279
 
137
 
280
  parse_argv(&cfg, argc, argv);
138
  parse_argv(&cfg, argc, argv);
281
 
139
 
282
  rmod_seg = rmod_find();
140
  rmod_seg = rmod_find();
283
  if (rmod_seg == 0xffff) {
141
  if (rmod_seg == 0xffff) {
284
    rmod_seg = rmod_install(cfg.envsiz);
142
    rmod_seg = rmod_install(cfg.envsiz);
285
    if (rmod_seg == 0xffff) {
143
    if (rmod_seg == 0xffff) {
286
      puts("ERROR: rmod_install() failed");
144
      puts("ERROR: rmod_install() failed");
287
      return(1);
145
      return(1);
288
    } else {
146
    } else {
289
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
147
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
290
    }
148
    }
291
  } else {
149
  } else {
292
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
150
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
293
  }
151
  }
294
 
152
 
295
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
153
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
296
 
154
 
297
  {
155
  {
298
    unsigned short envsiz;
156
    unsigned short envsiz;
299
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
157
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
300
    envsiz = *sizptr;
158
    envsiz = *sizptr;
301
    envsiz *= 16;
159
    envsiz *= 16;
302
    printf("rmod_inpbuff at %04X:%04X, env_seg at %04X:0000 (env_size = %u bytes)\r\n", rmod_seg, RMOD_OFFSET_INPBUFF, *rmod_envseg, envsiz);
160
    printf("rmod_inpbuff at %04X:%04X, env_seg at %04X:0000 (env_size = %u bytes)\r\n", rmod_seg, RMOD_OFFSET_INPBUFF, *rmod_envseg, envsiz);
303
  }
161
  }
304
 
162
 
305
  _asm {
-
 
306
    /* set the int22 handler in my PSP to rmod so DOS jumps to rmod after I terminate */
-
 
307
    mov bx, 0x0a
-
 
308
    xor ax, ax
-
 
309
    mov [bx], ax
-
 
310
    mov ax, rmod_seg
-
 
311
    mov [bx+2], ax
-
 
312
  }
-
 
313
 
-
 
314
  for (;;) {
163
  for (;;) {
315
    int i, argcount;
164
    int i, argcount;
316
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
165
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
317
    char path[256] = "C:\\>$";
166
    char path[256] = "C:\\>$";
318
    char const *argvlist[256];
167
    char const *argvlist[256];
319
    union REGS r;
168
    union REGS r;
320
 
169
 
321
    /* print shell prompt */
170
    /* print shell prompt */
322
    r.h.ah = 0x09;
171
    r.h.ah = 0x09;
323
    r.x.dx = FP_OFF(path);
172
    r.x.dx = FP_OFF(path);
324
    intdos(&r, &r);
173
    intdos(&r, &r);
325
 
174
 
326
    /* wait for user input */
175
    /* wait for user input */
327
    _asm {
176
    _asm {
328
      push ds
177
      push ds
329
 
178
 
330
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
179
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
331
      mov ax, 0x4800
180
      mov ax, 0x4800
332
      int 0x2f
181
      int 0x2f
333
      mov bl, al /* save doskey status in BL */
182
      mov bl, al /* save doskey status in BL */
334
 
183
 
335
      /* set up buffered input */
184
      /* set up buffered input */
336
      mov ax, rmod_seg
185
      mov ax, rmod_seg
337
      push ax
186
      push ax
338
      pop ds
187
      pop ds
339
      mov dx, RMOD_OFFSET_INPBUFF
188
      mov dx, RMOD_OFFSET_INPBUFF
340
 
189
 
341
      /* execute either DOS input or DOSKEY */
190
      /* execute either DOS input or DOSKEY */
342
      test bl, bl /* zf set if no DOSKEY present */
191
      test bl, bl /* zf set if no DOSKEY present */
343
      jnz DOSKEY
192
      jnz DOSKEY
344
 
193
 
345
      mov ah, 0x0a
194
      mov ah, 0x0a
346
      int 0x21
195
      int 0x21
347
      jmp short DONE
196
      jmp short DONE
348
 
197
 
349
      DOSKEY:
198
      DOSKEY:
350
      mov ax, 0x4810
199
      mov ax, 0x4810
351
      int 0x2f
200
      int 0x2f
352
 
201
 
353
      DONE:
202
      DONE:
354
      pop ds
203
      pop ds
355
    }
204
    }
356
    printf("\r\n");
205
    printf("\r\n");
357
 
206
 
358
    /* if nothing entered, loop again */
207
    /* if nothing entered, loop again */
359
    if (cmdline[-1] == 0) continue;
208
    if (cmdline[-1] == 0) continue;
360
 
209
 
361
    /* copy buffer to a near var (incl. trailing CR) */
210
    /* copy buffer to a near var (incl. trailing CR) */
362
    _fmemcpy(path, cmdline, cmdline[-1] + 1);
211
    _fmemcpy(path, cmdline, cmdline[-1] + 1);
363
 
212
 
364
    argcount = explode_progparams(path, argvlist);
213
    argcount = explode_progparams(path, argvlist);
365
 
214
 
366
    /* if nothing args found (eg. all-spaces), loop again */
215
    /* if nothing args found (eg. all-spaces), loop again */
367
    if (argcount == 0) continue;
216
    if (argcount == 0) continue;
368
 
217
 
369
    printf("got %u bytes of cmdline (%d args)\r\n", cmdline[-1], argcount);
218
    printf("got %u bytes of cmdline (%d args)\r\n", cmdline[-1], argcount);
370
    for (i = 0; i < argcount; i++) {
219
    for (i = 0; i < argcount; i++) {
371
      printf("arg #%d = '%s'\r\n", i, argvlist[i]);
220
      printf("arg #%d = '%s'\r\n", i, argvlist[i]);
372
    }
221
    }
373
 
222
 
374
    /* TODO is it an internal command? */
223
    /* TODO is it an internal command? */
375
    if (strcmp(argvlist[0], "set") == 0) {
224
    if (strcmp(argvlist[0], "set") == 0) {
376
      cmd_set(argcount, argvlist, *rmod_envseg);
225
      cmd_set(argcount, argvlist, *rmod_envseg);
377
      continue;
226
      continue;
378
    }
227
    }
-
 
228
    if (strcmp(argvlist[0], "exit") == 0) break;
379
 
229
 
380
    execvp(argvlist[0], argvlist);
230
    execvp(argvlist[0], argvlist);
381
 
231
 
382
    /* execvp() replaces the current process by the new one
232
    /* execvp() replaces the current process by the new one
383
    if I am still alive then external command failed to execute */
233
    if I am still alive then external command failed to execute */
384
    puts("Bad command or file name");
234
    puts("Bad command or file name");
385
 
235
 
386
  }
236
  }
387
 
237
 
388
  return(0);
238
  return(0);
389
}
239
}
390
 
240