Subversion Repositories SvarDOS

Rev

Rev 350 | Go to most recent revision | Details | 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>
51
#include <string.h>
52
 
53
#include <process.h>
54
 
55
#include "rmod.h"
56
 
57
struct config {
58
  int locate;
59
  int install;
60
} cfg;
61
 
62
 
63
/* returns segment where rmod is installed */
64
unsigned short install_routine(void) {
65
  char far *ptr, far *mcb;
66
  unsigned short far *owner;
67
  unsigned int memseg = 0xffff;
68
 
69
  _asm {
70
    /* link in the UMB memory chain for enabling high-memory allocation (and save initial status on stack) */
71
    mov ax, 0x5802  /* GET UMB LINK STATE */
72
    int 0x21
73
    xor ah, ah
74
    push ax         /* save link state on stack */
75
    mov ax, 0x5803  /* SET UMB LINK STATE */
76
    mov bx, 1
77
    int 0x21
78
    /* get current allocation strategy and save it in DX */
79
    mov ax, 0x5800
80
    int 0x21
81
    push ax
82
    pop dx
83
    /* set strategy to 'last fit, try high then low memory' */
84
    mov ax, 0x5801
85
    mov bx, 0x0082
86
    int 0x21
87
    /* ask for a memory block and save the given segment to memseg */
88
    mov ah, 0x48
89
    mov bx, (rmod_len + 15) / 16
90
    int 0x21
91
    jc ALLOC_FAIL
92
    mov memseg, ax
93
    ALLOC_FAIL:
94
    /* restore initial allocation strategy */
95
    mov ax, 0x5801
96
    mov bx, dx
97
    int 0x21
98
    /* restore initial UMB memory link state */
99
    mov ax, 0x5803
100
    pop bx       /* pop initial UMB link state from stack */
101
    int 0x21
102
  }
103
 
104
  if (memseg == 0xffff) {
105
    puts("malloc error");
106
    return(0xffff);
107
  }
108
  ptr = MK_FP(memseg, 0);
109
  mcb = MK_FP(memseg - 1, 0);
110
  owner = (void far *)(mcb + 1);
111
  _fmemcpy(ptr, rmod, rmod_len);
112
 
113
  printf("MCB sig: %c\r\nMCB owner: 0x%04X\r\n", mcb[0], *owner);
114
  {
115
    int i;
116
    for (i = 0; i < 17; i++) {
117
      printf("%02x ", mcb[i]);
118
    }
119
    printf("\r\n");
120
    for (i = 0; i < 17; i++) {
121
      if (mcb[i] > ' ') {
122
        printf(" %c ", mcb[i]);
123
      } else {
124
        printf(" . ", mcb[i]);
125
      }
126
    }
127
    printf("\r\n");
128
  }
129
 
130
  /* mark memory as "self owned" */
131
  *owner = memseg;
132
  _fmemcpy(mcb + 8, "COMMAND", 8);
133
  return(memseg);
134
}
135
 
136
 
137
static void parse_argv(struct config *cfg, int argc, char **argv) {
138
  int i;
139
  memset(cfg, 0, sizeof(*cfg));
140
  for (i = 1; i < argc; i++) {
141
    if (strcmp(argv[i], "/locate") == 0) {
142
      cfg->locate = 1;
143
    }
144
  }
145
}
146
 
147
 
148
/* scan memory for my shared buffer, return segment of buffer */
149
static unsigned short find_shm(void) {
150
  unsigned short i;
151
  unsigned short far *pattern;
152
 
153
  /* iterate over all paragraphs, looking for my signature */
154
  for (i = 0; i != 65535; i++) {
155
    pattern = MK_FP(i, 0);
156
    if (pattern[1] != 0x1983) continue;
157
    if (pattern[2] != 0x1985) continue;
158
    if (pattern[3] != 0x2017) continue;
159
    if (pattern[4] != 0x2019) continue;
160
    return(i);
161
  }
162
  return(0xffff);
163
}
164
 
165
 
166
static int explode_progparams(char *s, char const **argvlist) {
167
  int si = 0, argc = 0;
168
  for (;;) {
169
    /* skip to next non-space character */
170
    while (s[si] == ' ') si++;
171
    /* end of string? */
172
    if (s[si] == '\r') break;
173
    /* set argv ptr */
174
    argvlist[argc++] = s + si;
175
    /* find next space */
176
    while (s[si] != ' ' && s[si] != '\r') si++;
177
    /* is this end of string? */
178
    if (s[si] == '\r') {
179
      s[si] = 0;
180
      break;
181
    }
182
    /* not end: terminate arg and look for next one */
183
    s[si++] = 0;
184
  }
185
  /* terminate argvlist with a NULL value */
186
  argvlist[argc] = NULL;
187
  return(argc);
188
}
189
 
190
 
191
static void cmd_set(int argc, char const **argv, unsigned short env_seg) {
192
  char far *env = MK_FP(env_seg, 0);
193
  char buff[256];
194
  int i;
195
  while (*env != 0) {
196
    /* copy string to local buff for display */
197
    for (i = 0;; i++) {
198
      buff[i] = *env;
199
      env++;
200
      if (buff[i] == 0) break;
201
    }
202
    puts(buff);
203
  }
204
}
205
 
206
 
207
int main(int argc, char **argv) {
208
  struct config cfg;
209
  unsigned short env_seg = 0, rmod_seg, rmod_buff = 0;
210
  void far *rmod_func;
211
 
212
  parse_argv(&cfg, argc, argv);
213
 
214
  rmod_seg = find_shm();
215
  if (rmod_seg == 0xffff) {
216
    rmod_seg = install_routine();
217
    if (rmod_seg == 0xffff) {
218
      puts("ERROR: install_rmod() failed");
219
      return(1);
220
    } else {
221
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
222
    }
223
  } else {
224
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
225
  }
226
 
227
  rmod_func = MK_FP(rmod_seg, 0x0A);
228
  /* fetch offset of buffer (result in AX) */
229
  _asm {
230
    call dword ptr [rmod_func]
231
    mov rmod_buff, ax
232
  }
233
 
234
  printf("rmod_buff at %04X:%04X\r\n", rmod_seg, rmod_buff);
235
 
236
  _asm {
237
    /* set the int22 handler in my PSP to rmod so DOS jumps to rmod after I terminate */
238
    mov bx, 0x0a
239
    xor ax, ax
240
    mov [bx], ax
241
    mov ax, rmod_seg
242
    mov [bx+2], ax
243
    /* get the segment of my environment */
244
    mov bx, 0x2c
245
    mov ax, [bx]
246
    mov env_seg, ax
247
  }
248
 
249
  printf("env_seg at %04X\r\n", env_seg);
250
 
251
  for (;;) {
252
    int i, argcount;
253
    char far *cmdline = MK_FP(rmod_seg, rmod_buff + 2);
254
    char path[256] = "C:\\>$";
255
    char const *argvlist[256];
256
    union REGS r;
257
 
258
    /* print shell prompt */
259
    r.h.ah = 0x09;
260
    r.x.dx = FP_OFF(path);
261
    intdos(&r, &r);
262
 
263
    /* wait for user input */
264
    _asm {
265
      push ds
266
 
267
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
268
      mov ax, 0x4800
269
      int 0x2f
270
      mov bl, al /* save doskey status in BL */
271
 
272
      /* set up buffered input */
273
      mov ax, rmod_seg
274
      push ax
275
      pop ds
276
      mov dx, rmod_buff
277
 
278
      /* execute either DOS input or DOSKEY */
279
      test bl, bl /* zf set if no DOSKEY present */
280
      jnz DOSKEY
281
 
282
      mov ah, 0x0a
283
      int 0x21
284
      jmp short DONE
285
 
286
      DOSKEY:
287
      mov ax, 0x4810
288
      int 0x2f
289
 
290
      DONE:
291
      pop ds
292
    }
293
    printf("\r\n");
294
 
295
    /* if nothing entered, loop again */
296
    if (cmdline[-1] == 0) continue;
297
 
298
    /* copy buffer to a near var (incl. trailing CR) */
299
    _fmemcpy(path, cmdline, cmdline[-1] + 1);
300
 
301
    argcount = explode_progparams(path, argvlist);
302
 
303
    /* if nothing args found (eg. all-spaces), loop again */
304
    if (argcount == 0) continue;
305
 
306
    printf("got %u bytes of cmdline (%d args)\r\n", cmdline[-1], argcount);
307
    for (i = 0; i < argcount; i++) {
308
      printf("arg #%d = '%s'\r\n", i, argvlist[i]);
309
    }
310
 
311
    /* TODO is it an internal command? */
312
    if (strcmp(argvlist[0], "set") == 0) {
313
      cmd_set(argcount, argvlist, env_seg);
314
      continue;
315
    }
316
 
317
    execvp(argvlist[0], argvlist);
318
 
319
    /* execvp() replaces the current process by the new one
320
    if I am still alive then external command failed to execute */
321
    puts("Bad command or file name");
322
 
323
  }
324
 
325
  return(0);
326
}