Subversion Repositories SvarDOS

Rev

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