Subversion Repositories SvarDOS

Rev

Rev 362 | Rev 366 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
349 mateuszvis 1
/*
2
 * SvarCOM is a command-line interpreter.
3
 *
4
 * a little memory area is allocated as high as possible. it contains:
5
 *  - a signature (like XMS drivers do)
6
 *  - a routine for exec'ing programs
7
 *  - a "last command" buffer for input history
8
 *
9
 * when svarcom starts, it tries locating the routine in memory.
10
 *
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.
13
 *
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
16
 *   and quit.
17
 *
18
 *
19
 *
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
22
 *
23
 * PSP structure
24
 * http://www.piclist.com/techref/dos/psps.htm
25
 *
26
 *
27
 *
28
 * === MCB ===
29
 *
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
32
 * block has the following structure:
33
 *
34
 * Offset  Size     Description
35
 *   00h   byte     'M' =  non-last member of the MCB chain
36
 *                  'Z' = indicates last entry in MCB chain
37
 *                  other values cause "Memory Allocation Failure" on exit
38
 *   01h   word     PSP segment address of the owner (Process Id)
39
 *                  possible values:
40
 *                    0 = free
41
 *                    8 = Allocated by DOS before first user pgm loaded
42
 *                    other = Process Id/PSP segment address of owner
43
 *   03h  word      number of paragraphs related to this MCB (excluding MCB)
44
 *   05h  11 bytes  reserved
45
 *   10h  ...       start of actual allocated memory block
46
 */
47
 
48
#include <i86.h>
49
#include <dos.h>
50
#include <stdio.h>
350 mateuszvis 51
#include <stdlib.h>
349 mateuszvis 52
#include <string.h>
53
 
54
#include <process.h>
55
 
352 mateuszvis 56
#include "cmd.h"
57
#include "helpers.h"
351 mateuszvis 58
#include "rmodinit.h"
349 mateuszvis 59
 
60
struct config {
61
  int locate;
62
  int install;
350 mateuszvis 63
  int envsiz;
349 mateuszvis 64
} cfg;
65
 
66
 
67
static void parse_argv(struct config *cfg, int argc, char **argv) {
68
  int i;
69
  memset(cfg, 0, sizeof(*cfg));
350 mateuszvis 70
 
349 mateuszvis 71
  for (i = 1; i < argc; i++) {
72
    if (strcmp(argv[i], "/locate") == 0) {
73
      cfg->locate = 1;
74
    }
350 mateuszvis 75
    if (strstartswith(argv[i], "/e:") == 0) {
76
      cfg->envsiz = atoi(argv[i]+3);
77
      if (cfg->envsiz < 64) cfg->envsiz = 0;
78
    }
349 mateuszvis 79
  }
80
}
81
 
82
 
354 mateuszvis 83
static void buildprompt(char *s, const char *fmt) {
84
  for (; *fmt != 0; fmt++) {
85
    if (*fmt != '$') {
86
      *s = *fmt;
87
      s++;
88
      continue;
89
    }
90
    /* escape code ($P, etc) */
91
    fmt++;
92
    switch (*fmt) {
93
      case 'Q':  /* $Q = = (equal sign) */
94
      case 'q':
95
        *s = '=';
96
        s++;
97
        break;
98
      case '$':  /* $$ = $ (dollar sign) */
99
        *s = '$';
100
        s++;
101
        break;
102
      case 'T':  /* $t = current time */
103
      case 't':
104
        s += sprintf(s, "00:00"); /* TODO */
105
        break;
106
      case 'D':  /* $D = current date */
107
      case 'd':
108
        s += sprintf(s, "1985-07-29"); /* TODO */
109
        break;
110
      case 'P':  /* $P = current drive and path */
111
      case 'p':
112
        _asm {
113
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
114
          int 0x21
115
          mov bx, s
116
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
117
        }
118
        *s += 'A';
119
        s++;
120
        *s = ':';
121
        s++;
122
        *s = '\\';
123
        s++;
124
        _asm {
125
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
126
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
127
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
128
          int 0x21
129
        }
130
        while (*s != 0) s++; /* move ptr forward to end of pathname */
131
        break;
132
      case 'V':  /* $V = DOS version number */
133
      case 'v':
134
        s += sprintf(s, "VER"); /* TODO */
135
        break;
136
      case 'N':  /* $N = current drive */
137
      case 'n':
138
        _asm {
139
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
140
          int 0x21
141
          mov bx, s
142
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
143
        }
144
        *s += 'A';
145
        s++;
146
        break;
147
      case 'G':  /* $G = > (greater-than sign) */
148
      case 'g':
149
        *s = '>';
150
        s++;
151
        break;
152
      case 'L':  /* $L = < (less-than sign) */
153
      case 'l':
154
        *s = '<';
155
        s++;
156
        break;
157
      case 'B':  /* $B = | (pipe) */
158
      case 'b':
159
        *s = '|';
160
        s++;
161
        break;
162
      case 'H':  /* $H = backspace (erases previous character) */
163
      case 'h':
164
        *s = '\b';
165
        s++;
166
        break;
167
      case 'E':  /* $E = Escape code (ASCII 27) */
168
      case 'e':
169
        *s = 27;
170
        s++;
171
        break;
172
      case '_':  /* $_ = CR+LF */
173
        *s = '\r';
174
        s++;
175
        *s = '\n';
176
        s++;
177
        break;
178
    }
179
  }
180
  *s = '$';
181
}
349 mateuszvis 182
 
183
 
364 mateuszvis 184
static void run_as_external(const char far *cmdline) {
185
  char buff[256];
186
  char const *argvlist[256];
187
  int i, n;
188
  /* copy buffer to a near var (incl. trailing CR), insert a space before
189
     every slash to make sure arguments are well separated */
190
  n = 0;
191
  i = 0;
192
  for (;;) {
193
    if (cmdline[i] == '/') buff[n++] = ' ';
194
    buff[n++] = cmdline[i++];
195
    if (buff[n] == 0) break;
196
  }
197
 
198
  cmd_explode(buff, cmdline, argvlist);
199
 
200
  /* for (i = 0; argvlist[i] != NULL; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */
201
 
202
  /* must be an external command then. this call should never return, unless
203
   * the other program failed to be executed. */
204
  execvp(argvlist[0], argvlist);
205
}
206
 
207
 
349 mateuszvis 208
int main(int argc, char **argv) {
209
  struct config cfg;
350 mateuszvis 210
  unsigned short rmod_seg;
211
  unsigned short far *rmod_envseg;
353 mateuszvis 212
  unsigned short far *lastexitcode;
349 mateuszvis 213
 
214
  parse_argv(&cfg, argc, argv);
215
 
350 mateuszvis 216
  rmod_seg = rmod_find();
349 mateuszvis 217
  if (rmod_seg == 0xffff) {
350 mateuszvis 218
    rmod_seg = rmod_install(cfg.envsiz);
349 mateuszvis 219
    if (rmod_seg == 0xffff) {
350 mateuszvis 220
      puts("ERROR: rmod_install() failed");
349 mateuszvis 221
      return(1);
222
    } else {
223
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
224
    }
225
  } else {
226
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
227
  }
228
 
350 mateuszvis 229
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
353 mateuszvis 230
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
350 mateuszvis 231
 
232
  {
233
    unsigned short envsiz;
234
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
235
    envsiz = *sizptr;
236
    envsiz *= 16;
237
    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);
349 mateuszvis 238
  }
239
 
240
  for (;;) {
350 mateuszvis 241
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
349 mateuszvis 242
 
364 mateuszvis 243
    /* revert input history terminator to \r */
244
    if (cmdline[-1] != 0) {
245
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
246
    }
247
 
354 mateuszvis 248
    {
249
      /* print shell prompt */
364 mateuszvis 250
      char buff[256];
354 mateuszvis 251
      char *promptptr = buff;
252
      buildprompt(promptptr, "$p$g"); /* TODO: prompt should be configurable via environment */
253
      _asm {
364 mateuszvis 254
        push ax
354 mateuszvis 255
        push dx
256
        mov ah, 0x09
257
        mov dx, promptptr
258
        int 0x21
259
        pop dx
364 mateuszvis 260
        pop ax
354 mateuszvis 261
      }
262
    }
349 mateuszvis 263
 
264
    /* wait for user input */
265
    _asm {
364 mateuszvis 266
      push ax
267
      push bx
268
      push cx
269
      push dx
349 mateuszvis 270
      push ds
271
 
272
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
273
      mov ax, 0x4800
274
      int 0x2f
275
      mov bl, al /* save doskey status in BL */
276
 
277
      /* set up buffered input */
278
      mov ax, rmod_seg
279
      push ax
280
      pop ds
350 mateuszvis 281
      mov dx, RMOD_OFFSET_INPBUFF
349 mateuszvis 282
 
283
      /* execute either DOS input or DOSKEY */
284
      test bl, bl /* zf set if no DOSKEY present */
285
      jnz DOSKEY
286
 
287
      mov ah, 0x0a
288
      int 0x21
289
      jmp short DONE
290
 
291
      DOSKEY:
292
      mov ax, 0x4810
293
      int 0x2f
294
 
295
      DONE:
296
      pop ds
364 mateuszvis 297
      pop dx
298
      pop cx
299
      pop bx
300
      pop ax
349 mateuszvis 301
    }
302
    printf("\r\n");
303
 
304
    /* if nothing entered, loop again */
305
    if (cmdline[-1] == 0) continue;
306
 
364 mateuszvis 307
    /* replace \r by a zero terminator */
308
    cmdline[(unsigned char)(cmdline[-1])] = 0;
349 mateuszvis 309
 
364 mateuszvis 310
    /* move pointer forward to skip over any leading spaces */
311
    while (*cmdline == ' ') cmdline++;
349 mateuszvis 312
 
364 mateuszvis 313
    /* try matching (and executing) an internal command */
353 mateuszvis 314
    {
364 mateuszvis 315
      int ecode = cmd_process(*rmod_envseg, cmdline);
353 mateuszvis 316
      if (ecode >= 0) *lastexitcode = ecode;
364 mateuszvis 317
      if (ecode >= -1) continue; /* internal command executed */
353 mateuszvis 318
    }
352 mateuszvis 319
 
364 mateuszvis 320
    /* if here, then this was not an internal command */
321
    run_as_external(cmdline);
349 mateuszvis 322
 
323
    /* execvp() replaces the current process by the new one
324
    if I am still alive then external command failed to execute */
325
    puts("Bad command or file name");
326
 
327
  }
328
 
329
  return(0);
330
}