Subversion Repositories SvarDOS

Rev

Rev 1597 | Rev 1847 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
421 mateuszvis 1
/* This file is part of the SvarCOM project and is published under the terms
2
 * of the MIT license.
3
 *
1730 mateusz.vi 4
 * Copyright (C) 2021-2024 Mateusz Viste
421 mateuszvis 5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a
7
 * copy of this software and associated documentation files (the "Software"),
8
 * to deal in the Software without restriction, including without limitation
9
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
 * and/or sell copies of the Software, and to permit persons to whom the
11
 * Software is furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
 * DEALINGS IN THE SOFTWARE.
23
 */
351 mateuszvis 24
 
25
#include <i86.h>
26
#include <string.h>
27
 
367 mateuszvis 28
#include "env.h"
369 mateuszvis 29
#include "helpers.h"
367 mateuszvis 30
 
351 mateuszvis 31
#include "rmodinit.h"
32
 
33
 
449 mateuszvis 34
/* returns far pointer to rmod's settings block on success */
479 mateuszvis 35
struct rmod_props far *rmod_install(unsigned short envsize, unsigned char *rmodcore, unsigned short rmodcore_len) {
351 mateuszvis 36
  char far *myptr, far *mcb;
37
  unsigned short far *owner;
478 mateuszvis 38
  const unsigned short sizeof_rmodandprops_paras = (0x100 + rmodcore_len + sizeof(struct rmod_props) + 15) / 16;
453 mateuszvis 39
  unsigned short rmodseg = 0xffff;
454 mateuszvis 40
  unsigned short envseg, origenvseg;
465 mateuszvis 41
  struct rmod_props far *res;
351 mateuszvis 42
 
453 mateuszvis 43
  /* read my current env segment from PSP and save it */
454 mateuszvis 44
  envseg = *((unsigned short *)0x2c);
45
  origenvseg = envseg;
351 mateuszvis 46
 
369 mateuszvis 47
  /* printf("original (PSP) env buffer at %04X\r\n", envseg); */
449 mateuszvis 48
 
49
  /* if envseg is zero, then enforce our own one (MSDOS 5 does not provide a default env) */
50
  if ((envseg == 0) && (envsize == 0)) envsize = 256;
51
 
351 mateuszvis 52
  /* if custom envsize requested, convert it to number of paragraphs */
53
  if (envsize != 0) {
54
    envsize += 15;
55
    envsize /= 16;
56
  }
57
 
58
  _asm {
1594 mateusz.vi 59
    push bx
60
    push cx
61
    push dx
62
 
351 mateuszvis 63
    /* link in the UMB memory chain for enabling high-memory allocation (and save initial status on stack) */
64
    mov ax, 0x5802  /* GET UMB LINK STATE */
65
    int 0x21
66
    xor ah, ah
67
    push ax         /* save link state on stack */
68
    mov ax, 0x5803  /* SET UMB LINK STATE */
69
    mov bx, 1
70
    int 0x21
71
    /* get current allocation strategy and save it in DX */
72
    mov ax, 0x5800
73
    int 0x21
74
    push ax
75
    pop dx
76
    /* set strategy to 'last fit, try high then low memory' */
77
    mov ax, 0x5801
78
    mov bx, 0x0082
79
    int 0x21
80
    /* ask for a memory block and save the given segment to rmodseg */
81
    mov ah, 0x48
449 mateuszvis 82
    mov bx, sizeof_rmodandprops_paras
351 mateuszvis 83
    int 0x21
84
    jc ALLOC_FAIL
85
    mov rmodseg, ax
86
    /* ask for a memory block for the environment and save it to envseg (only if custom size requested) */
87
    mov bx, envsize
88
    test bx, bx
89
    jz ALLOC_FAIL
90
    mov ah, 0x48
91
    int 0x21
92
    jc ALLOC_FAIL
93
    mov envseg, ax
94
 
95
    ALLOC_FAIL:
96
    /* restore initial allocation strategy */
97
    mov ax, 0x5801
98
    mov bx, dx
99
    int 0x21
100
    /* restore initial UMB memory link state */
101
    mov ax, 0x5803
102
    pop bx       /* pop initial UMB link state from stack */
103
    int 0x21
1594 mateusz.vi 104
 
105
    pop dx
106
    pop cx
107
    pop bx
351 mateuszvis 108
  }
109
 
110
  if (rmodseg == 0xffff) {
369 mateuszvis 111
    outputnl("malloc error");
449 mateuszvis 112
    return(NULL);
351 mateuszvis 113
  }
114
 
1591 mateusz.vi 115
  /* generate a new PSP where RMOD is about to land */
116
  _asm {
117
    push dx
118
    mov ah, 0x26 /* CREATE NEW PROGRAM SEGMENT PREFIX (DOS 1+) */
119
    mov dx, rmodseg
120
    int 0x21
121
    pop dx
459 mateuszvis 122
  }
1587 mateusz.vi 123
 
1591 mateusz.vi 124
  myptr = MK_FP(rmodseg, 0);
125
 
1587 mateusz.vi 126
  /* patch up RMOD's PSP: Parent's PSP segment @ 0x16-0x17 */
127
  myptr[0x16] = rmodseg & 0xff; /* RMOD is his own parent */
128
  myptr[0x17] = rmodseg >> 8;
129
 
1589 mateusz.vi 130
  /* patch up RMOD's PSP: SS:SP pointer @ 0x2E-0x31  --  I abuse the PSP's
1590 mateusz.vi 131
   * command line tail as stack, but I do NOT set the stack at the end of the
1589 mateusz.vi 132
   * tail. E. C. Masloch kindly explained why this would be a bad idea:
133
   *
134
   * "This is wrong and will potentially overwrite part of your buffers that
135
   * start past the PSP. This is because the dword [PSP:2Eh] is not used merely
136
   * to set SS:SP but rather to find the stack frame created by the int 21h
137
   * call. Therefore the int 21h call that terminates the child process will
138
   * then pop a number of registers off starting from the address stored in the
139
   * PSP." <https://github.com/SvarDOS/bugz/issues/38#issuecomment-1817445740>
140
   */
141
  myptr[0x2e] = 192; /* middle of the command line tail area so I have 64 bytes */
142
  myptr[0x2f] = 0;   /* before and 64 bytes in front of me */
1587 mateusz.vi 143
  myptr[0x30] = rmodseg & 0xff;
144
  myptr[0x31] = rmodseg >> 8;
145
 
146
  /* patch up RMOD's PSP: JFT size @ 0x32-0x33 */
1588 mateusz.vi 147
  myptr[0x32] = 20; /* default JFT size (max that fits without an extra allocation) */
1587 mateusz.vi 148
  myptr[0x33] = 0;
149
 
150
  /* patch up RMOD's PSP: JFT pointer @ 0x34-0x37 */
151
  myptr[0x34] = 0x18; /* the JFT is in the PSP itself */
152
  myptr[0x35] = 0;
153
  myptr[0x36] = rmodseg & 0xff;
154
  myptr[0x37] = rmodseg >> 8;
155
 
156
  /* patch up RMOD's PSP: pointer to previous PSP @ 0x38-0x3B */
157
  myptr[0x38] = 0;
158
  myptr[0x39] = 0;
159
  myptr[0x3A] = rmodseg & 0xff;
160
  myptr[0x3B] = rmodseg >> 8;
161
 
162
  /* copy rmod to its destination (right past the PSP I prepared) */
459 mateuszvis 163
  myptr = MK_FP(rmodseg, 0x100);
478 mateuszvis 164
  _fmemcpy(myptr, rmodcore, rmodcore_len);
351 mateuszvis 165
 
1587 mateusz.vi 166
  /* mark rmod memory (MCB) as "self owned" */
351 mateuszvis 167
  mcb = MK_FP(rmodseg - 1, 0);
168
  owner = (void far *)(mcb + 1);
169
  *owner = rmodseg;
170
  _fmemcpy(mcb + 8, "SVARCOM", 8);
171
 
1587 mateusz.vi 172
  /* mark env memory (MCB) as "owned by rmod" */
359 mateuszvis 173
  mcb = MK_FP(envseg - 1, 0);
174
  owner = (void far *)(mcb + 1);
175
  *owner = rmodseg;
176
  _fmemcpy(mcb + 8, "SVARENV", 8);
351 mateuszvis 177
 
449 mateuszvis 178
  /* if env block is newly allocated, fill it with a few NULLs */
179
  if (envsize != 0) {
180
    owner = MK_FP(envseg, 0);
181
    owner[0] = 0;
182
    owner[1] = 0;
183
  }
184
 
537 mateuszvis 185
  /* set CTRL+BREAK handler to rmod */
186
  _asm {
187
    push ax
188
    push dx
189
    push ds
190
    mov ax, 0x2523
191
    mov ds, rmodseg
192
    mov dx, RMOD_OFFSET_BRKHANDLER
193
    int 0x21
194
    pop ds
195
    pop dx
196
    pop ax
197
  }
198
 
983 mateusz.vi 199
  /* mark the input buffer as empty */
200
  myptr = MK_FP(rmodseg, RMOD_OFFSET_INPUTBUF);
987 mateusz.vi 201
  myptr[0] = 128;  /* max acceptable length */
202
  myptr[1] = 0;    /* len of currently stored history string */
203
  myptr[2] = '\r'; /* string terminator */
204
  myptr[3] = 0xCA; /* signature to detect stack overflow damaging the buffer */
205
  myptr[4] = 0xFE; /* 2nd byte of the signature */
983 mateusz.vi 206
 
449 mateuszvis 207
  /* prepare result (rmod props) */
478 mateuszvis 208
  res = MK_FP(rmodseg, 0x100 + rmodcore_len);
450 mateuszvis 209
  _fmemset(res, 0, sizeof(*res));  /* zero out */
210
  res->rmodseg = rmodseg;          /* rmod segment */
453 mateuszvis 211
  res->origenvseg = origenvseg;    /* original environment segment */
449 mateuszvis 212
 
459 mateuszvis 213
  /* write env segment to rmod's PSP */
351 mateuszvis 214
  owner = MK_FP(rmodseg, RMOD_OFFSET_ENVSEG);
215
  *owner = envseg;
216
 
366 mateuszvis 217
  /* write boot drive to rmod bootdrive field */
218
  _asm {
219
    push ax
220
    push bx
221
    push dx
222
    push ds
223
    mov ax, 0x3305 /* DOS 4.0+ - GET BOOT DRIVE */
224
    int 0x21 /* boot drive is in DL now (1=A:, 2=B:, etc) */
225
    add dl, 'A'-1 /* convert to a proper ASCII letter */
226
    /* set DS to rmodseg */
227
    mov ax, rmodseg
228
    mov ds, ax
229
    /* write boot drive to rmod bootdrive field */
230
    mov bx, RMOD_OFFSET_BOOTDRIVE
231
    mov [bx], dl
232
    pop ds
233
    pop dx
234
    pop bx
235
    pop ax
236
  }
237
 
1597 mateusz.vi 238
  /* save my original int22h handler and parent in rmod's memory */
239
  res->origint22 = *((unsigned long *)0x0a); /* original int22h handler seg:off is at 0x0a of my PSP */
240
  res->origparent = *((unsigned short *)0x16); /* PSP segment of my parent is at 0x16 of my PSP */
450 mateuszvis 241
 
448 mateuszvis 242
  /* set the int22 handler in my PSP to rmod so DOS jumps to rmod after I
243
   * terminate and save the original handler in rmod's memory */
454 mateuszvis 244
  {
245
    unsigned short *ptr = (void *)0x0a; /* int22 handler is at 0x0A of the PSP */
246
    ptr[0] = RMOD_OFFSET_ROUTINE;
247
    ptr[1] = rmodseg;
351 mateuszvis 248
  }
249
 
1595 mateusz.vi 250
  /* set my own parent to RMOD (this is not necessary for MS-DOS nor FreeDOS but
251
   * might be on other DOS implementations) */
252
  {
253
    unsigned short *ptr = (void *)0x16;
254
    *ptr = rmodseg;
255
  }
256
 
449 mateuszvis 257
  return(res);
351 mateuszvis 258
}
259
 
260
 
449 mateuszvis 261
/* look up my parent: if it's rmod then return a ptr to its props struct,
975 mateusz.vi 262
 * otherwise return NULL
983 mateusz.vi 263
 * I look at PSP[Ch] to locate RMOD (ie. the "terminate address") */
479 mateuszvis 264
struct rmod_props far *rmod_find(unsigned short rmodcore_len) {
983 mateusz.vi 265
  unsigned short *parent = (void *)0x0C;
448 mateuszvis 266
  unsigned short far *ptr;
267
  const unsigned short sig[] = {0x1983, 0x1985, 0x2017, 0x2019};
465 mateuszvis 268
  unsigned char *cmdtail = (void *)0x80;
448 mateuszvis 269
  unsigned char i;
270
  /* is it rmod? */
459 mateuszvis 271
  ptr = MK_FP(*parent, 0x100);
449 mateuszvis 272
  for (i = 0; i < 4; i++) if (ptr[i] != sig[i]) return(NULL);
465 mateuszvis 273
  /* match successfull (rmod is my parent) - but is it really a respawn?
274
   * command-line tail should contain a single character '\r' */
275
  if ((cmdtail[0] != 1) || (cmdtail[1] != '\n')) return(NULL);
276
  cmdtail[0] = 0;
277
  cmdtail[1] = '\r';
278
  /* */
478 mateuszvis 279
  return(MK_FP(*parent, 0x100 + rmodcore_len));
351 mateuszvis 280
}
367 mateuszvis 281
 
282
 
283
/* update rmod's pointer to comspec */
284
void rmod_updatecomspecptr(unsigned short rmod_seg, unsigned short env_seg) {
285
  unsigned short far *comspecptr = MK_FP(rmod_seg, RMOD_OFFSET_COMSPECPTR);
439 mateuszvis 286
  char far *comspecfp = env_lookup_val(env_seg, "COMSPEC");
367 mateuszvis 287
  if (comspecfp != NULL) {
439 mateuszvis 288
    *comspecptr = FP_OFF(comspecfp);
367 mateuszvis 289
  } else {
290
    *comspecptr = 0;
291
  }
292
}
949 mateusz.vi 293
 
294
 
295
/* allocates bytes of far memory, flags it as belonging to rmod
957 mateusz.vi 296
 * the new block can be optionally flagged as 'ident' (if not null) and zero
297
 * out the newly allocated memory.
949 mateusz.vi 298
 * returns a far ptr to the allocated block, or NULL on error */
957 mateusz.vi 299
void far *rmod_fcalloc(unsigned short bytes, unsigned short rmod_seg, char *ident) {
949 mateusz.vi 300
  unsigned short far *owner;
301
  unsigned short newseg = 0;
302
 
303
  /* ask DOS for a memory block (as high as possible) */
304
  _asm {
305
    push bx /* save initial value in BX so I can restore it later */
306
 
307
    /* get current allocation strategy and save it on stack */
308
    mov ax, 0x5800
309
    int 0x21
310
    push ax
311
 
312
    /* set strategy to 'last fit, try high then low memory' */
313
    mov ax, 0x5801
314
    mov bx, 0x0082
315
    int 0x21
316
 
317
    /* ask for a memory block and save the given segment to rmodseg */
318
    mov ah, 0x48  /* Allocate Memory */
319
    mov bx, bytes
320
    add bx, 15    /* convert bytes to paragraphs */
321
    shr bx, 1     /* bx /= 16 */
322
    shr bx, 1
323
    shr bx, 1
324
    shr bx, 1
325
    int 0x21
326
 
327
    /* error handling */
328
    jc FAIL
329
 
330
    /* save newly allocated segment to newseg */
331
    mov newseg, ax
332
 
333
    FAIL:
334
    /* restore initial allocation strategy */
335
    mov ax, 0x5801
336
    pop bx
337
    int 0x21
338
 
339
    pop bx /* restore BX to its initial value */
340
  }
341
 
342
  if (newseg == 0) return(NULL);
343
 
344
  /* mark memory as "owned by rmod" */
345
  owner = (void far *)(MK_FP(newseg - 1, 1));
346
  *owner = rmod_seg;
347
 
348
  /* set the MCB description to ident, if provided */
349
  if (ident) {
350
    char far *mcbdesc = MK_FP(newseg - 1, 8);
351
    int i;
352
    _fmemset(mcbdesc, 0, 8);
353
    for (i = 0; (i < 8) && (ident[i] != 0); i++) { /* field's length is limited to 8 bytes max */
354
      mcbdesc[i] = ident[i];
355
    }
356
  }
357
 
957 mateusz.vi 358
  /* zero out the memory before handing it out */
359
  _fmemset(MK_FP(newseg, 0), 0, bytes);
360
 
949 mateusz.vi 361
  return(MK_FP(newseg, 0));
362
}
363
 
364
 
957 mateusz.vi 365
/* free memory previously allocated by rmod_fcalloc() */
949 mateusz.vi 366
void rmod_ffree(void far *ptr) {
367
  unsigned short ptrseg;
368
  unsigned short myseg = 0;
369
  unsigned short far *owner;
370
  if (ptr == NULL) return;
371
  ptrseg = FP_SEG(ptr);
372
 
373
  /* get my own segment */
374
  _asm {
375
    mov myseg, cs
376
  }
377
 
378
  /* mark memory in MCB as my own, otherwise DOS might refuse to free it */
379
  owner = MK_FP(ptrseg - 1, 1);
380
  *owner = myseg;
381
 
382
  /* free the memory block */
383
  _asm {
384
    push es
385
    mov ah, 0x49  /* Free Memory Block */
386
    mov es, ptrseg
387
    int 0x21
388
    pop es
389
  }
390
}
963 mateusz.vi 391
 
392
 
393
/* free the entire linked list of bat ctx nodes (and set its rmod ptr to NULL) */
394
void rmod_free_bat_llist(struct rmod_props far *rmod) {
395
  while (rmod->bat != NULL) {
396
    struct batctx far *victim = rmod->bat;
397
    rmod->bat = rmod->bat->parent;
398
    rmod_ffree(victim);
399
  }
400
}