Subversion Repositories SvarDOS

Rev

Rev 350 | Rev 352 | 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
 
351 mateuszvis 56
#include "rmodinit.h"
349 mateuszvis 57
 
58
struct config {
59
  int locate;
60
  int install;
350 mateuszvis 61
  int envsiz;
349 mateuszvis 62
} cfg;
63
 
64
 
350 mateuszvis 65
/* returns zero if s1 starts with s2 */
66
static int strstartswith(const char *s1, const char *s2) {
67
  while (*s2 != 0) {
68
    if (*s1 != *s2) return(-1);
69
    s1++;
70
    s2++;
71
  }
72
  return(0);
73
}
74
 
75
 
349 mateuszvis 76
static void parse_argv(struct config *cfg, int argc, char **argv) {
77
  int i;
78
  memset(cfg, 0, sizeof(*cfg));
350 mateuszvis 79
 
349 mateuszvis 80
  for (i = 1; i < argc; i++) {
81
    if (strcmp(argv[i], "/locate") == 0) {
82
      cfg->locate = 1;
83
    }
350 mateuszvis 84
    if (strstartswith(argv[i], "/e:") == 0) {
85
      cfg->envsiz = atoi(argv[i]+3);
86
      if (cfg->envsiz < 64) cfg->envsiz = 0;
87
    }
349 mateuszvis 88
  }
89
}
90
 
91
 
92
static int explode_progparams(char *s, char const **argvlist) {
93
  int si = 0, argc = 0;
94
  for (;;) {
95
    /* skip to next non-space character */
96
    while (s[si] == ' ') si++;
97
    /* end of string? */
98
    if (s[si] == '\r') break;
99
    /* set argv ptr */
100
    argvlist[argc++] = s + si;
101
    /* find next space */
102
    while (s[si] != ' ' && s[si] != '\r') si++;
103
    /* is this end of string? */
104
    if (s[si] == '\r') {
105
      s[si] = 0;
106
      break;
107
    }
108
    /* not end: terminate arg and look for next one */
109
    s[si++] = 0;
110
  }
111
  /* terminate argvlist with a NULL value */
112
  argvlist[argc] = NULL;
113
  return(argc);
114
}
115
 
116
 
117
static void cmd_set(int argc, char const **argv, unsigned short env_seg) {
118
  char far *env = MK_FP(env_seg, 0);
119
  char buff[256];
120
  int i;
121
  while (*env != 0) {
122
    /* copy string to local buff for display */
123
    for (i = 0;; i++) {
124
      buff[i] = *env;
125
      env++;
126
      if (buff[i] == 0) break;
127
    }
128
    puts(buff);
129
  }
130
}
131
 
132
 
133
int main(int argc, char **argv) {
134
  struct config cfg;
350 mateuszvis 135
  unsigned short rmod_seg;
136
  unsigned short far *rmod_envseg;
349 mateuszvis 137
 
138
  parse_argv(&cfg, argc, argv);
139
 
350 mateuszvis 140
  rmod_seg = rmod_find();
349 mateuszvis 141
  if (rmod_seg == 0xffff) {
350 mateuszvis 142
    rmod_seg = rmod_install(cfg.envsiz);
349 mateuszvis 143
    if (rmod_seg == 0xffff) {
350 mateuszvis 144
      puts("ERROR: rmod_install() failed");
349 mateuszvis 145
      return(1);
146
    } else {
147
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
148
    }
149
  } else {
150
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
151
  }
152
 
350 mateuszvis 153
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
154
 
155
  {
156
    unsigned short envsiz;
157
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
158
    envsiz = *sizptr;
159
    envsiz *= 16;
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);
349 mateuszvis 161
  }
162
 
163
  for (;;) {
164
    int i, argcount;
350 mateuszvis 165
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
349 mateuszvis 166
    char path[256] = "C:\\>$";
167
    char const *argvlist[256];
168
    union REGS r;
169
 
170
    /* print shell prompt */
171
    r.h.ah = 0x09;
172
    r.x.dx = FP_OFF(path);
173
    intdos(&r, &r);
174
 
175
    /* wait for user input */
176
    _asm {
177
      push ds
178
 
179
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
180
      mov ax, 0x4800
181
      int 0x2f
182
      mov bl, al /* save doskey status in BL */
183
 
184
      /* set up buffered input */
185
      mov ax, rmod_seg
186
      push ax
187
      pop ds
350 mateuszvis 188
      mov dx, RMOD_OFFSET_INPBUFF
349 mateuszvis 189
 
190
      /* execute either DOS input or DOSKEY */
191
      test bl, bl /* zf set if no DOSKEY present */
192
      jnz DOSKEY
193
 
194
      mov ah, 0x0a
195
      int 0x21
196
      jmp short DONE
197
 
198
      DOSKEY:
199
      mov ax, 0x4810
200
      int 0x2f
201
 
202
      DONE:
203
      pop ds
204
    }
205
    printf("\r\n");
206
 
207
    /* if nothing entered, loop again */
208
    if (cmdline[-1] == 0) continue;
209
 
210
    /* copy buffer to a near var (incl. trailing CR) */
211
    _fmemcpy(path, cmdline, cmdline[-1] + 1);
212
 
213
    argcount = explode_progparams(path, argvlist);
214
 
215
    /* if nothing args found (eg. all-spaces), loop again */
216
    if (argcount == 0) continue;
217
 
218
    printf("got %u bytes of cmdline (%d args)\r\n", cmdline[-1], argcount);
219
    for (i = 0; i < argcount; i++) {
220
      printf("arg #%d = '%s'\r\n", i, argvlist[i]);
221
    }
222
 
223
    /* TODO is it an internal command? */
224
    if (strcmp(argvlist[0], "set") == 0) {
350 mateuszvis 225
      cmd_set(argcount, argvlist, *rmod_envseg);
349 mateuszvis 226
      continue;
227
    }
351 mateuszvis 228
    if (strcmp(argvlist[0], "exit") == 0) break;
349 mateuszvis 229
 
230
    execvp(argvlist[0], argvlist);
231
 
232
    /* execvp() replaces the current process by the new one
233
    if I am still alive then external command failed to execute */
234
    puts("Bad command or file name");
235
 
236
  }
237
 
238
  return(0);
239
}