Subversion Repositories SvarDOS

Rev

Rev 349 | Rev 351 | 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
 
56
#include "rmod.h"
57
 
58
struct config {
59
  int locate;
60
  int install;
350 mateuszvis 61
  int envsiz;
349 mateuszvis 62
} cfg;
63
 
64
 
350 mateuszvis 65
#define RMOD_OFFSET_ENVSEG  0x08
66
#define RMOD_OFFSET_INPBUFF 0x0A
67
#define RMOD_OFFSET_ROUTINE 0x8C
68
 
349 mateuszvis 69
/* returns segment where rmod is installed */
350 mateuszvis 70
unsigned short rmod_install(unsigned short envsize) {
71
  char far *myptr, far *mcb;
349 mateuszvis 72
  unsigned short far *owner;
350 mateuszvis 73
  unsigned int rmodseg = 0xffff;
74
  unsigned int envseg = 0;
349 mateuszvis 75
 
350 mateuszvis 76
  /* read my current env segment from PSP */
349 mateuszvis 77
  _asm {
350 mateuszvis 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 {
349 mateuszvis 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
350 mateuszvis 113
    /* ask for a memory block and save the given segment to rmodseg */
349 mateuszvis 114
    mov ah, 0x48
115
    mov bx, (rmod_len + 15) / 16
116
    int 0x21
117
    jc ALLOC_FAIL
350 mateuszvis 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
 
349 mateuszvis 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
 
350 mateuszvis 139
  if (rmodseg == 0xffff) {
349 mateuszvis 140
    puts("malloc error");
141
    return(0xffff);
142
  }
350 mateuszvis 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);
349 mateuszvis 150
  owner = (void far *)(mcb + 1);
350 mateuszvis 151
  *owner = rmodseg;
152
  _fmemcpy(mcb + 8, "SVARCOM", 8);
349 mateuszvis 153
 
350 mateuszvis 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
/*
349 mateuszvis 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");
350 mateuszvis 183
  }*/
349 mateuszvis 184
 
350 mateuszvis 185
  return(rmodseg);
349 mateuszvis 186
}
187
 
188
 
350 mateuszvis 189
/* returns zero if s1 starts with s2 */
190
static int strstartswith(const char *s1, const char *s2) {
191
  while (*s2 != 0) {
192
    if (*s1 != *s2) return(-1);
193
    s1++;
194
    s2++;
195
  }
196
  return(0);
197
}
198
 
199
 
349 mateuszvis 200
static void parse_argv(struct config *cfg, int argc, char **argv) {
201
  int i;
202
  memset(cfg, 0, sizeof(*cfg));
350 mateuszvis 203
 
349 mateuszvis 204
  for (i = 1; i < argc; i++) {
205
    if (strcmp(argv[i], "/locate") == 0) {
206
      cfg->locate = 1;
207
    }
350 mateuszvis 208
    if (strstartswith(argv[i], "/e:") == 0) {
209
      cfg->envsiz = atoi(argv[i]+3);
210
      if (cfg->envsiz < 64) cfg->envsiz = 0;
211
    }
349 mateuszvis 212
  }
213
}
214
 
215
 
216
/* scan memory for my shared buffer, return segment of buffer */
350 mateuszvis 217
static unsigned short rmod_find(void) {
349 mateuszvis 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);
350 mateuszvis 224
    if (pattern[0] != 0x1983) continue;
225
    if (pattern[1] != 0x1985) continue;
226
    if (pattern[2] != 0x2017) continue;
227
    if (pattern[3] != 0x2019) continue;
349 mateuszvis 228
    return(i);
229
  }
230
  return(0xffff);
231
}
232
 
233
 
234
static int explode_progparams(char *s, char const **argvlist) {
235
  int si = 0, argc = 0;
236
  for (;;) {
237
    /* skip to next non-space character */
238
    while (s[si] == ' ') si++;
239
    /* end of string? */
240
    if (s[si] == '\r') break;
241
    /* set argv ptr */
242
    argvlist[argc++] = s + si;
243
    /* find next space */
244
    while (s[si] != ' ' && s[si] != '\r') si++;
245
    /* is this end of string? */
246
    if (s[si] == '\r') {
247
      s[si] = 0;
248
      break;
249
    }
250
    /* not end: terminate arg and look for next one */
251
    s[si++] = 0;
252
  }
253
  /* terminate argvlist with a NULL value */
254
  argvlist[argc] = NULL;
255
  return(argc);
256
}
257
 
258
 
259
static void cmd_set(int argc, char const **argv, unsigned short env_seg) {
260
  char far *env = MK_FP(env_seg, 0);
261
  char buff[256];
262
  int i;
263
  while (*env != 0) {
264
    /* copy string to local buff for display */
265
    for (i = 0;; i++) {
266
      buff[i] = *env;
267
      env++;
268
      if (buff[i] == 0) break;
269
    }
270
    puts(buff);
271
  }
272
}
273
 
274
 
275
int main(int argc, char **argv) {
276
  struct config cfg;
350 mateuszvis 277
  unsigned short rmod_seg;
278
  unsigned short far *rmod_envseg;
349 mateuszvis 279
 
280
  parse_argv(&cfg, argc, argv);
281
 
350 mateuszvis 282
  rmod_seg = rmod_find();
349 mateuszvis 283
  if (rmod_seg == 0xffff) {
350 mateuszvis 284
    rmod_seg = rmod_install(cfg.envsiz);
349 mateuszvis 285
    if (rmod_seg == 0xffff) {
350 mateuszvis 286
      puts("ERROR: rmod_install() failed");
349 mateuszvis 287
      return(1);
288
    } else {
289
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
290
    }
291
  } else {
292
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
293
  }
294
 
350 mateuszvis 295
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
296
 
297
  {
298
    unsigned short envsiz;
299
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
300
    envsiz = *sizptr;
301
    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);
349 mateuszvis 303
  }
304
 
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 (;;) {
315
    int i, argcount;
350 mateuszvis 316
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
349 mateuszvis 317
    char path[256] = "C:\\>$";
318
    char const *argvlist[256];
319
    union REGS r;
320
 
321
    /* print shell prompt */
322
    r.h.ah = 0x09;
323
    r.x.dx = FP_OFF(path);
324
    intdos(&r, &r);
325
 
326
    /* wait for user input */
327
    _asm {
328
      push ds
329
 
330
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
331
      mov ax, 0x4800
332
      int 0x2f
333
      mov bl, al /* save doskey status in BL */
334
 
335
      /* set up buffered input */
336
      mov ax, rmod_seg
337
      push ax
338
      pop ds
350 mateuszvis 339
      mov dx, RMOD_OFFSET_INPBUFF
349 mateuszvis 340
 
341
      /* execute either DOS input or DOSKEY */
342
      test bl, bl /* zf set if no DOSKEY present */
343
      jnz DOSKEY
344
 
345
      mov ah, 0x0a
346
      int 0x21
347
      jmp short DONE
348
 
349
      DOSKEY:
350
      mov ax, 0x4810
351
      int 0x2f
352
 
353
      DONE:
354
      pop ds
355
    }
356
    printf("\r\n");
357
 
358
    /* if nothing entered, loop again */
359
    if (cmdline[-1] == 0) continue;
360
 
361
    /* copy buffer to a near var (incl. trailing CR) */
362
    _fmemcpy(path, cmdline, cmdline[-1] + 1);
363
 
364
    argcount = explode_progparams(path, argvlist);
365
 
366
    /* if nothing args found (eg. all-spaces), loop again */
367
    if (argcount == 0) continue;
368
 
369
    printf("got %u bytes of cmdline (%d args)\r\n", cmdline[-1], argcount);
370
    for (i = 0; i < argcount; i++) {
371
      printf("arg #%d = '%s'\r\n", i, argvlist[i]);
372
    }
373
 
374
    /* TODO is it an internal command? */
375
    if (strcmp(argvlist[0], "set") == 0) {
350 mateuszvis 376
      cmd_set(argcount, argvlist, *rmod_envseg);
349 mateuszvis 377
      continue;
378
    }
379
 
380
    execvp(argvlist[0], argvlist);
381
 
382
    /* execvp() replaces the current process by the new one
383
    if I am still alive then external command failed to execute */
384
    puts("Bad command or file name");
385
 
386
  }
387
 
388
  return(0);
389
}