Subversion Repositories SvarDOS

Rev

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