Subversion Repositories SvarDOS

Rev

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

Rev 353 Rev 354
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 "cmd.h"
56
#include "cmd.h"
57
#include "helpers.h"
57
#include "helpers.h"
58
#include "rmodinit.h"
58
#include "rmodinit.h"
59
 
59
 
60
struct config {
60
struct config {
61
  int locate;
61
  int locate;
62
  int install;
62
  int install;
63
  int envsiz;
63
  int envsiz;
64
} cfg;
64
} cfg;
65
 
65
 
66
 
66
 
67
static void parse_argv(struct config *cfg, int argc, char **argv) {
67
static void parse_argv(struct config *cfg, int argc, char **argv) {
68
  int i;
68
  int i;
69
  memset(cfg, 0, sizeof(*cfg));
69
  memset(cfg, 0, sizeof(*cfg));
70
 
70
 
71
  for (i = 1; i < argc; i++) {
71
  for (i = 1; i < argc; i++) {
72
    if (strcmp(argv[i], "/locate") == 0) {
72
    if (strcmp(argv[i], "/locate") == 0) {
73
      cfg->locate = 1;
73
      cfg->locate = 1;
74
    }
74
    }
75
    if (strstartswith(argv[i], "/e:") == 0) {
75
    if (strstartswith(argv[i], "/e:") == 0) {
76
      cfg->envsiz = atoi(argv[i]+3);
76
      cfg->envsiz = atoi(argv[i]+3);
77
      if (cfg->envsiz < 64) cfg->envsiz = 0;
77
      if (cfg->envsiz < 64) cfg->envsiz = 0;
78
    }
78
    }
79
  }
79
  }
80
}
80
}
81
 
81
 
82
 
82
 
83
static int explode_progparams(char *s, char const **argvlist) {
83
static int explode_progparams(char *s, char const **argvlist) {
84
  int si = 0, argc = 0;
84
  int si = 0, argc = 0;
85
  for (;;) {
85
  for (;;) {
86
    /* skip to next non-space character */
86
    /* skip to next non-space character */
87
    while (s[si] == ' ') si++;
87
    while (s[si] == ' ') si++;
88
    /* end of string? */
88
    /* end of string? */
89
    if (s[si] == '\r') break;
89
    if (s[si] == '\r') break;
90
    /* set argv ptr */
90
    /* set argv ptr */
91
    argvlist[argc++] = s + si;
91
    argvlist[argc++] = s + si;
92
    /* find next space */
92
    /* find next space */
93
    while (s[si] != ' ' && s[si] != '\r') si++;
93
    while (s[si] != ' ' && s[si] != '\r') si++;
94
    /* is this end of string? */
94
    /* is this end of string? */
95
    if (s[si] == '\r') {
95
    if (s[si] == '\r') {
96
      s[si] = 0;
96
      s[si] = 0;
97
      break;
97
      break;
98
    }
98
    }
99
    /* not end: terminate arg and look for next one */
99
    /* not end: terminate arg and look for next one */
100
    s[si++] = 0;
100
    s[si++] = 0;
101
  }
101
  }
102
  /* terminate argvlist with a NULL value */
102
  /* terminate argvlist with a NULL value */
103
  argvlist[argc] = NULL;
103
  argvlist[argc] = NULL;
104
  return(argc);
104
  return(argc);
105
}
105
}
106
 
106
 
107
 
107
 
-
 
108
static void buildprompt(char *s, const char *fmt) {
-
 
109
  for (; *fmt != 0; fmt++) {
-
 
110
    if (*fmt != '$') {
-
 
111
      *s = *fmt;
-
 
112
      s++;
-
 
113
      continue;
-
 
114
    }
-
 
115
    /* escape code ($P, etc) */
-
 
116
    fmt++;
-
 
117
    switch (*fmt) {
-
 
118
      case 'Q':  /* $Q = = (equal sign) */
-
 
119
      case 'q':
-
 
120
        *s = '=';
-
 
121
        s++;
-
 
122
        break;
-
 
123
      case '$':  /* $$ = $ (dollar sign) */
-
 
124
        *s = '$';
-
 
125
        s++;
-
 
126
        break;
-
 
127
      case 'T':  /* $t = current time */
-
 
128
      case 't':
-
 
129
        s += sprintf(s, "00:00"); /* TODO */
-
 
130
        break;
-
 
131
      case 'D':  /* $D = current date */
-
 
132
      case 'd':
-
 
133
        s += sprintf(s, "1985-07-29"); /* TODO */
-
 
134
        break;
-
 
135
      case 'P':  /* $P = current drive and path */
-
 
136
      case 'p':
-
 
137
        _asm {
-
 
138
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
-
 
139
          int 0x21
-
 
140
          mov bx, s
-
 
141
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
-
 
142
        }
-
 
143
        *s += 'A';
-
 
144
        s++;
-
 
145
        *s = ':';
-
 
146
        s++;
-
 
147
        *s = '\\';
-
 
148
        s++;
-
 
149
        _asm {
-
 
150
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
-
 
151
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
-
 
152
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
-
 
153
          int 0x21
-
 
154
        }
-
 
155
        while (*s != 0) s++; /* move ptr forward to end of pathname */
-
 
156
        break;
-
 
157
      case 'V':  /* $V = DOS version number */
-
 
158
      case 'v':
-
 
159
        s += sprintf(s, "VER"); /* TODO */
-
 
160
        break;
-
 
161
      case 'N':  /* $N = current drive */
-
 
162
      case 'n':
-
 
163
        _asm {
-
 
164
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
-
 
165
          int 0x21
-
 
166
          mov bx, s
-
 
167
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
-
 
168
        }
-
 
169
        *s += 'A';
-
 
170
        s++;
-
 
171
        break;
-
 
172
      case 'G':  /* $G = > (greater-than sign) */
-
 
173
      case 'g':
-
 
174
        *s = '>';
-
 
175
        s++;
-
 
176
        break;
-
 
177
      case 'L':  /* $L = < (less-than sign) */
-
 
178
      case 'l':
-
 
179
        *s = '<';
-
 
180
        s++;
-
 
181
        break;
-
 
182
      case 'B':  /* $B = | (pipe) */
-
 
183
      case 'b':
-
 
184
        *s = '|';
-
 
185
        s++;
-
 
186
        break;
-
 
187
      case 'H':  /* $H = backspace (erases previous character) */
-
 
188
      case 'h':
-
 
189
        *s = '\b';
-
 
190
        s++;
-
 
191
        break;
-
 
192
      case 'E':  /* $E = Escape code (ASCII 27) */
-
 
193
      case 'e':
-
 
194
        *s = 27;
-
 
195
        s++;
-
 
196
        break;
-
 
197
      case '_':  /* $_ = CR+LF */
-
 
198
        *s = '\r';
-
 
199
        s++;
-
 
200
        *s = '\n';
-
 
201
        s++;
-
 
202
        break;
-
 
203
    }
-
 
204
  }
-
 
205
  *s = '$';
-
 
206
}
108
 
207
 
109
 
208
 
110
int main(int argc, char **argv) {
209
int main(int argc, char **argv) {
111
  struct config cfg;
210
  struct config cfg;
112
  unsigned short rmod_seg;
211
  unsigned short rmod_seg;
113
  unsigned short far *rmod_envseg;
212
  unsigned short far *rmod_envseg;
114
  unsigned short far *lastexitcode;
213
  unsigned short far *lastexitcode;
115
 
214
 
116
  parse_argv(&cfg, argc, argv);
215
  parse_argv(&cfg, argc, argv);
117
 
216
 
118
  rmod_seg = rmod_find();
217
  rmod_seg = rmod_find();
119
  if (rmod_seg == 0xffff) {
218
  if (rmod_seg == 0xffff) {
120
    rmod_seg = rmod_install(cfg.envsiz);
219
    rmod_seg = rmod_install(cfg.envsiz);
121
    if (rmod_seg == 0xffff) {
220
    if (rmod_seg == 0xffff) {
122
      puts("ERROR: rmod_install() failed");
221
      puts("ERROR: rmod_install() failed");
123
      return(1);
222
      return(1);
124
    } else {
223
    } else {
125
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
224
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
126
    }
225
    }
127
  } else {
226
  } else {
128
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
227
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
129
  }
228
  }
130
 
229
 
131
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
230
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
132
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
231
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
133
 
232
 
134
  {
233
  {
135
    unsigned short envsiz;
234
    unsigned short envsiz;
136
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
235
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
137
    envsiz = *sizptr;
236
    envsiz = *sizptr;
138
    envsiz *= 16;
237
    envsiz *= 16;
139
    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);
238
    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);
140
  }
239
  }
141
 
240
 
142
  for (;;) {
241
  for (;;) {
143
    int i, argcount;
242
    int i, argcount;
144
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
243
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
145
    char path[256] = "C:\\>$";
244
    char buff[256];
146
    char const *argvlist[256];
245
    char const *argvlist[256];
147
    union REGS r;
-
 
148
 
246
 
-
 
247
    {
149
    /* print shell prompt */
248
      /* print shell prompt */
-
 
249
      char *promptptr = buff;
-
 
250
      buildprompt(promptptr, "$p$g"); /* TODO: prompt should be configurable via environment */
-
 
251
      _asm {
-
 
252
        push dx
150
    r.h.ah = 0x09;
253
        mov ah, 0x09
151
    r.x.dx = FP_OFF(path);
254
        mov dx, promptptr
152
    intdos(&r, &r);
255
        int 0x21
-
 
256
        pop dx
-
 
257
      }
-
 
258
    }
153
 
259
 
154
    /* wait for user input */
260
    /* wait for user input */
155
    _asm {
261
    _asm {
156
      push ds
262
      push ds
157
 
263
 
158
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
264
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
159
      mov ax, 0x4800
265
      mov ax, 0x4800
160
      int 0x2f
266
      int 0x2f
161
      mov bl, al /* save doskey status in BL */
267
      mov bl, al /* save doskey status in BL */
162
 
268
 
163
      /* set up buffered input */
269
      /* set up buffered input */
164
      mov ax, rmod_seg
270
      mov ax, rmod_seg
165
      push ax
271
      push ax
166
      pop ds
272
      pop ds
167
      mov dx, RMOD_OFFSET_INPBUFF
273
      mov dx, RMOD_OFFSET_INPBUFF
168
 
274
 
169
      /* execute either DOS input or DOSKEY */
275
      /* execute either DOS input or DOSKEY */
170
      test bl, bl /* zf set if no DOSKEY present */
276
      test bl, bl /* zf set if no DOSKEY present */
171
      jnz DOSKEY
277
      jnz DOSKEY
172
 
278
 
173
      mov ah, 0x0a
279
      mov ah, 0x0a
174
      int 0x21
280
      int 0x21
175
      jmp short DONE
281
      jmp short DONE
176
 
282
 
177
      DOSKEY:
283
      DOSKEY:
178
      mov ax, 0x4810
284
      mov ax, 0x4810
179
      int 0x2f
285
      int 0x2f
180
 
286
 
181
      DONE:
287
      DONE:
182
      pop ds
288
      pop ds
183
    }
289
    }
184
    printf("\r\n");
290
    printf("\r\n");
185
 
291
 
186
    /* if nothing entered, loop again */
292
    /* if nothing entered, loop again */
187
    if (cmdline[-1] == 0) continue;
293
    if (cmdline[-1] == 0) continue;
188
 
294
 
189
    /* copy buffer to a near var (incl. trailing CR) */
295
    /* copy buffer to a near var (incl. trailing CR) */
190
    _fmemcpy(path, cmdline, cmdline[-1] + 1);
296
    _fmemcpy(buff, cmdline, cmdline[-1] + 1);
191
 
297
 
192
    argcount = explode_progparams(path, argvlist);
298
    argcount = explode_progparams(buff, argvlist);
193
 
299
 
194
    /* if nothing args found (eg. all-spaces), loop again */
300
    /* if nothing args found (eg. all-spaces), loop again */
195
    if (argcount == 0) continue;
301
    if (argcount == 0) continue;
196
 
302
 
197
    printf("got %u bytes of cmdline (%d args)\r\n", cmdline[-1], argcount);
303
    printf("got %u bytes of cmdline (%d args)\r\n", cmdline[-1], argcount);
198
    for (i = 0; i < argcount; i++) {
304
    for (i = 0; i < argcount; i++) {
199
      printf("arg #%d = '%s'\r\n", i, argvlist[i]);
305
      printf("arg #%d = '%s'\r\n", i, argvlist[i]);
200
    }
306
    }
201
 
307
 
202
    /* is it about quitting? */
308
    /* is it about quitting? */
203
    if (imatch(argvlist[0], "exit")) break;
309
    if (imatch(argvlist[0], "exit")) break;
204
 
310
 
205
    /* try running it as an internal command */
311
    /* try running it as an internal command */
206
    {
312
    {
207
      int ecode = cmd_process(argcount, argvlist, *rmod_envseg);
313
      int ecode = cmd_process(argcount, argvlist, *rmod_envseg);
208
      if (ecode >= 0) *lastexitcode = ecode;
314
      if (ecode >= 0) *lastexitcode = ecode;
209
      if (ecode >= -1) continue; /* command is internal but did not set an exit code */
315
      if (ecode >= -1) continue; /* command is internal but did not set an exit code */
210
    }
316
    }
211
 
317
 
212
    /* must be an external command then */
318
    /* must be an external command then */
213
    execvp(argvlist[0], argvlist);
319
    execvp(argvlist[0], argvlist);
214
 
320
 
215
    /* execvp() replaces the current process by the new one
321
    /* execvp() replaces the current process by the new one
216
    if I am still alive then external command failed to execute */
322
    if I am still alive then external command failed to execute */
217
    puts("Bad command or file name");
323
    puts("Bad command or file name");
218
 
324
 
219
  }
325
  }
220
 
326
 
221
  return(0);
327
  return(0);
222
}
328
}
223
 
329