Subversion Repositories SvarDOS

Rev

Rev 352 | Rev 354 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 352 Rev 353
1
/*
1
/*
2
 * SvarCOM is a command-line interpreter.
2
 * SvarCOM is a command-line interpreter.
3
 *
3
 *
4
 * a little memory area is allocated as high as possible. it contains:
4
 * a little memory area is allocated as high as possible. it contains:
5
 *  - a signature (like XMS drivers do)
5
 *  - a signature (like XMS drivers do)
6
 *  - a routine for exec'ing programs
6
 *  - a routine for exec'ing programs
7
 *  - a "last command" buffer for input history
7
 *  - a "last command" buffer for input history
8
 *
8
 *
9
 * when svarcom starts, it tries locating the routine in memory.
9
 * when svarcom starts, it tries locating the routine in memory.
10
 *
10
 *
11
 * if found:
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.
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
 *
13
 *
14
 * if not found:
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
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.
16
 *   and quit.
17
 *
17
 *
18
 *
18
 *
19
 *
19
 *
20
 * good lecture about PSP and allocating memory
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
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
 *
22
 *
23
 * PSP structure
23
 * PSP structure
24
 * http://www.piclist.com/techref/dos/psps.htm
24
 * http://www.piclist.com/techref/dos/psps.htm
25
 *
25
 *
26
 *
26
 *
27
 *
27
 *
28
 * === MCB ===
28
 * === MCB ===
29
 *
29
 *
30
 * each time that DOS allocates memory, it prefixes the allocated memory with
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
31
 * a 16-bytes structure called a "Memory Control Block" (MCB). This control
32
 * block has the following structure:
32
 * block has the following structure:
33
 *
33
 *
34
 * Offset  Size     Description
34
 * Offset  Size     Description
35
 *   00h   byte     'M' =  non-last member of the MCB chain
35
 *   00h   byte     'M' =  non-last member of the MCB chain
36
 *                  'Z' = indicates last entry in MCB chain
36
 *                  'Z' = indicates last entry in MCB chain
37
 *                  other values cause "Memory Allocation Failure" on exit
37
 *                  other values cause "Memory Allocation Failure" on exit
38
 *   01h   word     PSP segment address of the owner (Process Id)
38
 *   01h   word     PSP segment address of the owner (Process Id)
39
 *                  possible values:
39
 *                  possible values:
40
 *                    0 = free
40
 *                    0 = free
41
 *                    8 = Allocated by DOS before first user pgm loaded
41
 *                    8 = Allocated by DOS before first user pgm loaded
42
 *                    other = Process Id/PSP segment address of owner
42
 *                    other = Process Id/PSP segment address of owner
43
 *   03h  word      number of paragraphs related to this MCB (excluding MCB)
43
 *   03h  word      number of paragraphs related to this MCB (excluding MCB)
44
 *   05h  11 bytes  reserved
44
 *   05h  11 bytes  reserved
45
 *   10h  ...       start of actual allocated memory block
45
 *   10h  ...       start of actual allocated memory block
46
 */
46
 */
47
 
47
 
48
#include <i86.h>
48
#include <i86.h>
49
#include <dos.h>
49
#include <dos.h>
50
#include <stdio.h>
50
#include <stdio.h>
51
#include <stdlib.h>
51
#include <stdlib.h>
52
#include <string.h>
52
#include <string.h>
53
 
53
 
54
#include <process.h>
54
#include <process.h>
55
 
55
 
56
#include "cmd.h"
56
#include "cmd.h"
57
#include "helpers.h"
57
#include "helpers.h"
58
#include "rmodinit.h"
58
#include "rmodinit.h"
59
 
59
 
60
struct config {
60
struct config {
61
  int locate;
61
  int locate;
62
  int install;
62
  int install;
63
  int envsiz;
63
  int envsiz;
64
} cfg;
64
} cfg;
65
 
65
 
66
 
66
 
67
static void parse_argv(struct config *cfg, int argc, char **argv) {
67
static void parse_argv(struct config *cfg, int argc, char **argv) {
68
  int i;
68
  int i;
69
  memset(cfg, 0, sizeof(*cfg));
69
  memset(cfg, 0, sizeof(*cfg));
70
 
70
 
71
  for (i = 1; i < argc; i++) {
71
  for (i = 1; i < argc; i++) {
72
    if (strcmp(argv[i], "/locate") == 0) {
72
    if (strcmp(argv[i], "/locate") == 0) {
73
      cfg->locate = 1;
73
      cfg->locate = 1;
74
    }
74
    }
75
    if (strstartswith(argv[i], "/e:") == 0) {
75
    if (strstartswith(argv[i], "/e:") == 0) {
76
      cfg->envsiz = atoi(argv[i]+3);
76
      cfg->envsiz = atoi(argv[i]+3);
77
      if (cfg->envsiz < 64) cfg->envsiz = 0;
77
      if (cfg->envsiz < 64) cfg->envsiz = 0;
78
    }
78
    }
79
  }
79
  }
80
}
80
}
81
 
81
 
82
 
82
 
83
static int explode_progparams(char *s, char const **argvlist) {
83
static int explode_progparams(char *s, char const **argvlist) {
84
  int si = 0, argc = 0;
84
  int si = 0, argc = 0;
85
  for (;;) {
85
  for (;;) {
86
    /* skip to next non-space character */
86
    /* skip to next non-space character */
87
    while (s[si] == ' ') si++;
87
    while (s[si] == ' ') si++;
88
    /* end of string? */
88
    /* end of string? */
89
    if (s[si] == '\r') break;
89
    if (s[si] == '\r') break;
90
    /* set argv ptr */
90
    /* set argv ptr */
91
    argvlist[argc++] = s + si;
91
    argvlist[argc++] = s + si;
92
    /* find next space */
92
    /* find next space */
93
    while (s[si] != ' ' && s[si] != '\r') si++;
93
    while (s[si] != ' ' && s[si] != '\r') si++;
94
    /* is this end of string? */
94
    /* is this end of string? */
95
    if (s[si] == '\r') {
95
    if (s[si] == '\r') {
96
      s[si] = 0;
96
      s[si] = 0;
97
      break;
97
      break;
98
    }
98
    }
99
    /* not end: terminate arg and look for next one */
99
    /* not end: terminate arg and look for next one */
100
    s[si++] = 0;
100
    s[si++] = 0;
101
  }
101
  }
102
  /* terminate argvlist with a NULL value */
102
  /* terminate argvlist with a NULL value */
103
  argvlist[argc] = NULL;
103
  argvlist[argc] = NULL;
104
  return(argc);
104
  return(argc);
105
}
105
}
106
 
106
 
107
 
107
 
108
 
108
 
109
 
109
 
110
int main(int argc, char **argv) {
110
int main(int argc, char **argv) {
111
  struct config cfg;
111
  struct config cfg;
112
  unsigned short rmod_seg;
112
  unsigned short rmod_seg;
113
  unsigned short far *rmod_envseg;
113
  unsigned short far *rmod_envseg;
114
  int ecode = 0;
114
  unsigned short far *lastexitcode;
115
 
115
 
116
  parse_argv(&cfg, argc, argv);
116
  parse_argv(&cfg, argc, argv);
117
 
117
 
118
  rmod_seg = rmod_find();
118
  rmod_seg = rmod_find();
119
  if (rmod_seg == 0xffff) {
119
  if (rmod_seg == 0xffff) {
120
    rmod_seg = rmod_install(cfg.envsiz);
120
    rmod_seg = rmod_install(cfg.envsiz);
121
    if (rmod_seg == 0xffff) {
121
    if (rmod_seg == 0xffff) {
122
      puts("ERROR: rmod_install() failed");
122
      puts("ERROR: rmod_install() failed");
123
      return(1);
123
      return(1);
124
    } else {
124
    } else {
125
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
125
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
126
    }
126
    }
127
  } else {
127
  } else {
128
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
128
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
129
  }
129
  }
130
 
130
 
131
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
131
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
-
 
132
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
132
 
133
 
133
  {
134
  {
134
    unsigned short envsiz;
135
    unsigned short envsiz;
135
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
136
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
136
    envsiz = *sizptr;
137
    envsiz = *sizptr;
137
    envsiz *= 16;
138
    envsiz *= 16;
138
    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);
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);
139
  }
140
  }
140
 
141
 
141
  for (;;) {
142
  for (;;) {
142
    int i, argcount;
143
    int i, argcount;
143
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
144
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
144
    char path[256] = "C:\\>$";
145
    char path[256] = "C:\\>$";
145
    char const *argvlist[256];
146
    char const *argvlist[256];
146
    union REGS r;
147
    union REGS r;
147
 
148
 
148
    /* print shell prompt */
149
    /* print shell prompt */
149
    r.h.ah = 0x09;
150
    r.h.ah = 0x09;
150
    r.x.dx = FP_OFF(path);
151
    r.x.dx = FP_OFF(path);
151
    intdos(&r, &r);
152
    intdos(&r, &r);
152
 
153
 
153
    /* wait for user input */
154
    /* wait for user input */
154
    _asm {
155
    _asm {
155
      push ds
156
      push ds
156
 
157
 
157
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
158
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
158
      mov ax, 0x4800
159
      mov ax, 0x4800
159
      int 0x2f
160
      int 0x2f
160
      mov bl, al /* save doskey status in BL */
161
      mov bl, al /* save doskey status in BL */
161
 
162
 
162
      /* set up buffered input */
163
      /* set up buffered input */
163
      mov ax, rmod_seg
164
      mov ax, rmod_seg
164
      push ax
165
      push ax
165
      pop ds
166
      pop ds
166
      mov dx, RMOD_OFFSET_INPBUFF
167
      mov dx, RMOD_OFFSET_INPBUFF
167
 
168
 
168
      /* execute either DOS input or DOSKEY */
169
      /* execute either DOS input or DOSKEY */
169
      test bl, bl /* zf set if no DOSKEY present */
170
      test bl, bl /* zf set if no DOSKEY present */
170
      jnz DOSKEY
171
      jnz DOSKEY
171
 
172
 
172
      mov ah, 0x0a
173
      mov ah, 0x0a
173
      int 0x21
174
      int 0x21
174
      jmp short DONE
175
      jmp short DONE
175
 
176
 
176
      DOSKEY:
177
      DOSKEY:
177
      mov ax, 0x4810
178
      mov ax, 0x4810
178
      int 0x2f
179
      int 0x2f
179
 
180
 
180
      DONE:
181
      DONE:
181
      pop ds
182
      pop ds
182
    }
183
    }
183
    printf("\r\n");
184
    printf("\r\n");
184
 
185
 
185
    /* if nothing entered, loop again */
186
    /* if nothing entered, loop again */
186
    if (cmdline[-1] == 0) continue;
187
    if (cmdline[-1] == 0) continue;
187
 
188
 
188
    /* copy buffer to a near var (incl. trailing CR) */
189
    /* copy buffer to a near var (incl. trailing CR) */
189
    _fmemcpy(path, cmdline, cmdline[-1] + 1);
190
    _fmemcpy(path, cmdline, cmdline[-1] + 1);
190
 
191
 
191
    argcount = explode_progparams(path, argvlist);
192
    argcount = explode_progparams(path, argvlist);
192
 
193
 
193
    /* if nothing args found (eg. all-spaces), loop again */
194
    /* if nothing args found (eg. all-spaces), loop again */
194
    if (argcount == 0) continue;
195
    if (argcount == 0) continue;
195
 
196
 
196
    printf("got %u bytes of cmdline (%d args)\r\n", cmdline[-1], argcount);
197
    printf("got %u bytes of cmdline (%d args)\r\n", cmdline[-1], argcount);
197
    for (i = 0; i < argcount; i++) {
198
    for (i = 0; i < argcount; i++) {
198
      printf("arg #%d = '%s'\r\n", i, argvlist[i]);
199
      printf("arg #%d = '%s'\r\n", i, argvlist[i]);
199
    }
200
    }
200
 
201
 
201
    /* is it about quitting? */
202
    /* is it about quitting? */
202
    if (imatch(argvlist[0], "exit")) break;
203
    if (imatch(argvlist[0], "exit")) break;
203
 
204
 
204
    /* try running it as an internal command */
205
    /* try running it as an internal command */
-
 
206
    {
205
    ecode = cmd_process(argcount, argvlist, *rmod_envseg);
207
      int ecode = cmd_process(argcount, argvlist, *rmod_envseg);
206
    if (ecode >= 0) continue;
208
      if (ecode >= 0) *lastexitcode = ecode;
-
 
209
      if (ecode >= -1) continue; /* command is internal but did not set an exit code */
-
 
210
    }
207
 
211
 
208
    /* must be an external command then */
212
    /* must be an external command then */
209
    execvp(argvlist[0], argvlist);
213
    execvp(argvlist[0], argvlist);
210
 
214
 
211
    /* execvp() replaces the current process by the new one
215
    /* execvp() replaces the current process by the new one
212
    if I am still alive then external command failed to execute */
216
    if I am still alive then external command failed to execute */
213
    puts("Bad command or file name");
217
    puts("Bad command or file name");
214
 
218
 
215
  }
219
  }
216
 
220
 
217
  return(0);
221
  return(0);
218
}
222
}
219
 
223