Subversion Repositories SvarDOS

Rev

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

Rev 406 Rev 410
1
/*
1
/*
2
 * a variety of helper functions
2
 * a variety of helper functions
3
 * Copyright (C) 2021 Mateusz Viste
3
 * Copyright (C) 2021 Mateusz Viste
4
 */
4
 */
5
 
5
 
6
#include <i86.h>    /* MK_FP() */
6
#include <i86.h>    /* MK_FP() */
7
 
7
 
8
#include "helpers.h"
8
#include "helpers.h"
9
 
9
 
10
/* case-insensitive comparison of strings, returns non-zero on equality */
10
/* case-insensitive comparison of strings, returns non-zero on equality */
11
int imatch(const char *s1, const char *s2) {
11
int imatch(const char *s1, const char *s2) {
12
  for (;;) {
12
  for (;;) {
13
    char c1, c2;
13
    char c1, c2;
14
    c1 = *s1;
14
    c1 = *s1;
15
    c2 = *s2;
15
    c2 = *s2;
16
    if ((c1 >= 'a') && (c1 <= 'z')) c1 -= ('a' - 'A');
16
    if ((c1 >= 'a') && (c1 <= 'z')) c1 -= ('a' - 'A');
17
    if ((c2 >= 'a') && (c2 <= 'z')) c2 -= ('a' - 'A');
17
    if ((c2 >= 'a') && (c2 <= 'z')) c2 -= ('a' - 'A');
18
    /* */
18
    /* */
19
    if (c1 != c2) return(0);
19
    if (c1 != c2) return(0);
20
    if (c1 == 0) return(1);
20
    if (c1 == 0) return(1);
21
    s1++;
21
    s1++;
22
    s2++;
22
    s2++;
23
  }
23
  }
24
}
24
}
25
 
25
 
26
 
26
 
27
/* returns zero if s1 starts with s2 */
27
/* returns zero if s1 starts with s2 */
28
int strstartswith(const char *s1, const char *s2) {
28
int strstartswith(const char *s1, const char *s2) {
29
  while (*s2 != 0) {
29
  while (*s2 != 0) {
30
    if (*s1 != *s2) return(-1);
30
    if (*s1 != *s2) return(-1);
31
    s1++;
31
    s1++;
32
    s2++;
32
    s2++;
33
  }
33
  }
34
  return(0);
34
  return(0);
35
}
35
}
36
 
36
 
37
 
37
 
38
/* outputs a NULL-terminated string to stdout */
38
/* outputs a NULL-terminated string to stdout */
39
void output_internal(const char *s, unsigned short nl) {
39
void output_internal(const char *s, unsigned short nl) {
40
  _asm {
40
  _asm {
41
    mov ah, 0x02 /* AH=9 - write character in DL to stdout */
41
    mov ah, 0x02 /* AH=9 - write character in DL to stdout */
42
    mov si, s
42
    mov si, s
43
    cld          /* clear DF so lodsb increments SI */
43
    cld          /* clear DF so lodsb increments SI */
44
    NEXTBYTE:
44
    NEXTBYTE:
45
    lodsb /* load byte from DS:SI into AL, SI++ */
45
    lodsb /* load byte from DS:SI into AL, SI++ */
46
    mov dl, al
46
    mov dl, al
47
    or al, 0  /* is al == 0? */
47
    or al, 0  /* is al == 0? */
48
    jz DONE
48
    jz DONE
49
    int 0x21
49
    int 0x21
50
    jmp NEXTBYTE
50
    jmp NEXTBYTE
51
    DONE:
51
    DONE:
52
    or nl, 0
52
    or nl, 0
53
    jz FINITO
53
    jz FINITO
54
    /* print out a CR/LF trailer if nl set */
54
    /* print out a CR/LF trailer if nl set */
55
    mov dl, 0x0D /* CR */
55
    mov dl, 0x0D /* CR */
56
    int 0x21
56
    int 0x21
57
    mov dl, 0x0A /* LF */
57
    mov dl, 0x0A /* LF */
58
    int 0x21
58
    int 0x21
59
    FINITO:
59
    FINITO:
60
  }
60
  }
61
}
61
}
62
 
62
 
63
 
63
 
64
/* find first matching files using a FindFirst DOS call
64
/* find first matching files using a FindFirst DOS call
65
 * returns 0 on success or a DOS err code on failure */
65
 * returns 0 on success or a DOS err code on failure */
66
unsigned short findfirst(struct DTA *dta, const char *pattern, unsigned short attr) {
66
unsigned short findfirst(struct DTA *dta, const char *pattern, unsigned short attr) {
67
  unsigned short res = 0;
67
  unsigned short res = 0;
68
  _asm {
68
  _asm {
69
    /* set DTA location */
69
    /* set DTA location */
70
    mov ah, 0x1a
70
    mov ah, 0x1a
71
    mov dx, dta
71
    mov dx, dta
72
    int 0x21
72
    int 0x21
73
    /* */
73
    /* */
74
    mov ah, 0x4e    /* FindFirst */
74
    mov ah, 0x4e    /* FindFirst */
75
    mov dx, pattern
75
    mov dx, pattern
76
    mov cx, attr
76
    mov cx, attr
77
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
77
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
78
    jnc DONE
78
    jnc DONE
79
    mov [res], ax
79
    mov [res], ax
80
    DONE:
80
    DONE:
81
  }
81
  }
82
  return(res);
82
  return(res);
83
}
83
}
84
 
84
 
85
 
85
 
86
/* find next matching, ie. continues an action intiated by findfirst() */
86
/* find next matching, ie. continues an action intiated by findfirst() */
87
unsigned short findnext(struct DTA *dta) {
87
unsigned short findnext(struct DTA *dta) {
88
  unsigned short res = 0;
88
  unsigned short res = 0;
89
  _asm {
89
  _asm {
90
    mov ah, 0x4f    /* FindNext */
90
    mov ah, 0x4f    /* FindNext */
91
    mov dx, dta
91
    mov dx, dta
92
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
92
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
93
    jnc DONE
93
    jnc DONE
94
    mov [res], ax
94
    mov [res], ax
95
    DONE:
95
    DONE:
96
  }
96
  }
97
  return(res);
97
  return(res);
98
}
98
}
99
 
99
 
100
 
100
 
101
/* print s string and wait for a single key press from stdin. accepts only
101
/* print s string and wait for a single key press from stdin. accepts only
102
 * key presses defined in the c ASCIIZ string. returns offset of pressed key
102
 * key presses defined in the c ASCIIZ string. returns offset of pressed key
103
 * in string. keys in c MUST BE UPPERCASE! */
103
 * in string. keys in c MUST BE UPPERCASE! */
104
unsigned short askchoice(const char *s, const char *c) {
104
unsigned short askchoice(const char *s, const char *c) {
105
  unsigned short res;
105
  unsigned short res;
106
  char key = 0;
106
  char key = 0;
107
 
107
 
108
  AGAIN:
108
  AGAIN:
109
  output(s);
109
  output(s);
110
  output(" ");
110
  output(" ");
111
 
111
 
112
  _asm {
112
  _asm {
113
    push ax
113
    push ax
114
    push dx
114
    push dx
115
 
115
 
116
    mov ax, 0x0c01 /* clear input buffer and execute getchar (INT 21h,AH=1) */
116
    mov ax, 0x0c01 /* clear input buffer and execute getchar (INT 21h,AH=1) */
117
    int 0x21
117
    int 0x21
118
    /* if AL == 0 then this is an extended character */
118
    /* if AL == 0 then this is an extended character */
119
    test al, al
119
    test al, al
120
    jnz GOTCHAR
120
    jnz GOTCHAR
121
    mov ah, 0x08   /* read again to flush extended char from input buffer */
121
    mov ah, 0x08   /* read again to flush extended char from input buffer */
122
    int 0x21
122
    int 0x21
123
    xor al, al     /* all extended chars are ignored */
123
    xor al, al     /* all extended chars are ignored */
124
    GOTCHAR:       /* received key is in AL now */
124
    GOTCHAR:       /* received key is in AL now */
125
    mov [key], al  /* save key */
125
    mov [key], al  /* save key */
126
 
126
 
127
    /* print a cr/lf */
127
    /* print a cr/lf */
128
    mov ah, 0x02
128
    mov ah, 0x02
129
    mov dl, 0x0D
129
    mov dl, 0x0D
130
    int 0x21
130
    int 0x21
131
    mov dl, 0x0A
131
    mov dl, 0x0A
132
    int 0x21
132
    int 0x21
133
 
133
 
134
    pop dx
134
    pop dx
135
    pop ax
135
    pop ax
136
  }
136
  }
137
 
137
 
138
  /* ucase() result */
138
  /* ucase() result */
139
  if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A');
139
  if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A');
140
 
140
 
141
  /* is there a match? */
141
  /* is there a match? */
142
  for (res = 0; c[res] != 0; res++) if (c[res] == key) return(res);
142
  for (res = 0; c[res] != 0; res++) if (c[res] == key) return(res);
143
 
143
 
144
  goto AGAIN;
144
  goto AGAIN;
145
}
145
}
146
 
146
 
147
 
147
 
148
/* converts a path to its canonic representation, returns 0 on success
148
/* converts a path to its canonic representation, returns 0 on success
149
 * or DOS err on failure (invalid drive) */
149
 * or DOS err on failure (invalid drive) */
150
unsigned short file_truename(const char *src, char *dst) {
150
unsigned short file_truename(const char *src, char *dst) {
151
  unsigned short res = 0;
151
  unsigned short res = 0;
152
  _asm {
152
  _asm {
153
    push es
153
    push es
154
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
154
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
155
    push ds
155
    push ds
156
    pop es
156
    pop es
157
    mov si, src
157
    mov si, src
158
    mov di, dst
158
    mov di, dst
159
    int 0x21
159
    int 0x21
160
    jnc DONE
160
    jnc DONE
161
    mov [res], ax
161
    mov [res], ax
162
    DONE:
162
    DONE:
163
    pop es
163
    pop es
164
  }
164
  }
165
  return(res);
165
  return(res);
166
}
166
}
167
 
167
 
168
 
168
 
169
/* returns DOS attributes of file, or -1 on error */
169
/* returns DOS attributes of file, or -1 on error */
170
int file_getattr(const char *fname) {
170
int file_getattr(const char *fname) {
171
  int res = -1;
171
  int res = -1;
172
  _asm {
172
  _asm {
173
    mov ax, 0x4300  /* query file attributes, fname at DS:DX */
173
    mov ax, 0x4300  /* query file attributes, fname at DS:DX */
174
    mov dx, fname
174
    mov dx, fname
175
    int 0x21        /* CX=attributes if CF=0, otherwise AX=errno */
175
    int 0x21        /* CX=attributes if CF=0, otherwise AX=errno */
176
    jc DONE
176
    jc DONE
177
    mov [res], cx
177
    mov [res], cx
178
    DONE:
178
    DONE:
179
  }
179
  }
180
  return(res);
180
  return(res);
181
}
181
}
182
 
182
 
183
 
183
 
184
/* returns screen's width (in columns) */
184
/* returns screen's width (in columns) */
185
unsigned short screen_getwidth(void) {
185
unsigned short screen_getwidth(void) {
186
  /* BIOS 0040:004A = word containing screen width in text columns */
186
  /* BIOS 0040:004A = word containing screen width in text columns */
187
  unsigned short far *scrw = MK_FP(0x40, 0x4a);
187
  unsigned short far *scrw = MK_FP(0x40, 0x4a);
188
  return(*scrw);
188
  return(*scrw);
189
}
189
}
190
 
190
 
191
 
191
 
192
/* returns screen's height (in rows) */
192
/* returns screen's height (in rows) */
193
unsigned short screen_getheight(void) {
193
unsigned short screen_getheight(void) {
194
  /* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */
194
  /* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */
195
  unsigned char far *scrh = MK_FP(0x40, 0x84);
195
  unsigned char far *scrh = MK_FP(0x40, 0x84);
196
  if (*scrh == 0) return(25);  /* pre-EGA adapter */
196
  if (*scrh == 0) return(25);  /* pre-EGA adapter */
197
  return(*scrh + 1);
197
  return(*scrh + 1);
198
}
198
}
199
 
199
 
200
 
200
 
201
/* displays the "Press any key to continue" msg and waits for a keypress */
201
/* displays the "Press any key to continue" msg and waits for a keypress */
202
void press_any_key(void) {
202
void press_any_key(void) {
203
  output("Press any key to continue...");
203
  output("Press any key to continue...");
204
  _asm {
204
  _asm {
205
    mov ah, 0x08  /* no echo console input */
205
    mov ah, 0x08  /* no echo console input */
206
    int 0x21      /* pressed key in AL now (0 for extended keys) */
206
    int 0x21      /* pressed key in AL now (0 for extended keys) */
207
    test al, al
207
    test al, al
208
    jnz DONE
208
    jnz DONE
209
    int 0x21      /* executed ah=8 again to read the rest of extended key */
209
    int 0x21      /* executed ah=8 again to read the rest of extended key */
210
    DONE:
210
    DONE:
211
    /* output CR/LF */
211
    /* output CR/LF */
212
    mov ah, 0x02
212
    mov ah, 0x02
213
    mov dl, 0x0D
213
    mov dl, 0x0D
214
    int 0x21
214
    int 0x21
215
    mov dl, 0x0A
215
    mov dl, 0x0A
216
    int 0x21
216
    int 0x21
217
  }
217
  }
218
}
218
}
219
 
219
 
220
 
220
 
221
/* validate a drive (A=0, B=1, etc). returns 1 if valid, 0 otherwise */
221
/* validate a drive (A=0, B=1, etc). returns 1 if valid, 0 otherwise */
222
int isdrivevalid(unsigned char drv) {
222
int isdrivevalid(unsigned char drv) {
223
  _asm {
223
  _asm {
224
    mov ah, 0x19  /* query default (current) disk */
224
    mov ah, 0x19  /* query default (current) disk */
225
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
225
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
226
    mov ch, al    /* save current drive to ch */
226
    mov ch, al    /* save current drive to ch */
227
    /* try setting up the drive as current */
227
    /* try setting up the drive as current */
228
    mov ah, 0x0E   /* select default drive */
228
    mov ah, 0x0E   /* select default drive */
229
    mov dl, [drv]  /* 0=A, 1=B, etc */
229
    mov dl, [drv]  /* 0=A, 1=B, etc */
230
    int 0x21
230
    int 0x21
231
    /* this call does not set CF on error, I must check cur drive to look for success */
231
    /* this call does not set CF on error, I must check cur drive to look for success */
232
    mov ah, 0x19  /* query default (current) disk */
232
    mov ah, 0x19  /* query default (current) disk */
233
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
233
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
234
    mov [drv], 1  /* preset result as success */
234
    mov [drv], 1  /* preset result as success */
235
    cmp al, dl    /* is eq? */
235
    cmp al, dl    /* is eq? */
236
    je DONE
236
    je DONE
237
    mov [drv], 0  /* fail */
237
    mov [drv], 0  /* fail */
238
    jmp FAILED
238
    jmp FAILED
239
    DONE:
239
    DONE:
240
    /* set current drive back to what it was initially */
240
    /* set current drive back to what it was initially */
241
    mov ah, 0x0E
241
    mov ah, 0x0E
242
    mov dl, ch
242
    mov dl, ch
243
    int 0x21
243
    int 0x21
244
    FAILED:
244
    FAILED:
245
  }
245
  }
246
  return(drv);
246
  return(drv);
247
}
247
}
248
 
248
 
249
 
249
 
250
/* converts a 8+3 filename into 11-bytes FCB format (MYFILE  EXT) */
250
/* converts a 8+3 filename into 11-bytes FCB format (MYFILE  EXT) */
251
void file_fname2fcb(char *dst, const char *src) {
251
void file_fname2fcb(char *dst, const char *src) {
252
  unsigned short i;
252
  unsigned short i;
253
 
253
 
254
  /* fill dst with 11 spaces and a NULL terminator */
254
  /* fill dst with 11 spaces and a NULL terminator */
255
  for (i = 0; i < 12; i++) dst[i] = ' ';
255
  for (i = 0; i < 12; i++) dst[i] = ' ';
256
  dst[12] = 0;
256
  dst[12] = 0;
257
 
257
 
258
  /* copy fname until dot (.) or 8 characters */
258
  /* copy fname until dot (.) or 8 characters */
259
  for (i = 0; i < 8; i++) {
259
  for (i = 0; i < 8; i++) {
260
    if ((src[i] == '.') || (src[i] == 0)) break;
260
    if ((src[i] == '.') || (src[i] == 0)) break;
261
    dst[i] = src[i];
261
    dst[i] = src[i];
262
  }
262
  }
263
 
263
 
264
  /* advance src until extension or end of string */
264
  /* advance src until extension or end of string */
265
  src += i;
265
  src += i;
266
  for (;;) {
266
  for (;;) {
267
    if (*src == '.') {
267
    if (*src == '.') {
268
      src++; /* next character is extension */
268
      src++; /* next character is extension */
269
      break;
269
      break;
270
    }
270
    }
271
    if (*src == 0) break;
271
    if (*src == 0) break;
272
  }
272
  }
273
 
273
 
274
  /* copy extension to dst (3 chars max) */
274
  /* copy extension to dst (3 chars max) */
275
  dst += 8;
275
  dst += 8;
276
  for (i = 0; i < 3; i++) {
276
  for (i = 0; i < 3; i++) {
277
    if (src[i] == 0) break;
277
    if (src[i] == 0) break;
278
    dst[i] = src[i];
278
    dst[i] = src[i];
279
  }
279
  }
280
}
280
}
281
 
281
 
282
 
282
 
283
/* converts a 11-bytes FCB filename (MYFILE  EXT) into 8+3 format (MYFILE.EXT) */
283
/* converts a 11-bytes FCB filename (MYFILE  EXT) into 8+3 format (MYFILE.EXT) */
284
void file_fcb2fname(char *dst, const char *src) {
284
void file_fcb2fname(char *dst, const char *src) {
285
  unsigned short i, end = 0;
285
  unsigned short i, end = 0;
286
 
286
 
287
  for (i = 0; i < 8; i++) {
287
  for (i = 0; i < 8; i++) {
288
    dst[i] = src[i];
288
    dst[i] = src[i];
289
    if (dst[i] != ' ') end = i + 1;
289
    if (dst[i] != ' ') end = i + 1;
290
  }
290
  }
291
 
291
 
292
  /* is there an extension? */
292
  /* is there an extension? */
293
  if (src[8] == ' ') {
293
  if (src[8] == ' ') {
294
    dst[end] = 0;
294
    dst[end] = 0;
295
  } else { /* found extension: copy it until first space */
295
  } else { /* found extension: copy it until first space */
296
    dst[end++] = '.';
296
    dst[end++] = '.';
297
    for (i = 8; i < 11; i++) {
297
    for (i = 8; i < 11; i++) {
298
      if (src[i] == ' ') break;
298
      if (src[i] == ' ') break;
299
      dst[end++] = src[i];
299
      dst[end++] = src[i];
300
    }
300
    }
301
    dst[end] = 0;
301
    dst[end] = 0;
302
  }
302
  }
303
}
303
}
-
 
304
 
-
 
305
 
-
 
306
/* converts an ASCIIZ string into an unsigned short. returns 0 on success. */
-
 
307
int atouns(unsigned short *r, const char *s) {
-
 
308
  int err = 0;
-
 
309
 
-
 
310
  _asm {
-
 
311
    mov si, s
-
 
312
    xor ax, ax  /* general purpose register */
-
 
313
    xor cx, cx  /* contains the result */
-
 
314
    mov bx, 10  /* used as a multiplicative step */
-
 
315
 
-
 
316
    NEXTBYTE:
-
 
317
    xchg cx, ax /* move result into cx temporarily */
-
 
318
    lodsb  /* AL = DS:[SI++] */
-
 
319
    /* is AL 0? if so we're done */
-
 
320
    test al, al
-
 
321
    jz DONE
-
 
322
    /* validate that AL is in range '0'-'9' */
-
 
323
    sub al, '0'
-
 
324
    jc FAIL   /* neg result */
-
 
325
    cmp al, 9
-
 
326
    jg FAIL
-
 
327
    /* restore result into AX (CX contains the new digit) */
-
 
328
    xchg cx, ax
-
 
329
    /* multiply result by 10 and add cl */
-
 
330
    mul bx    /* DX AX = AX * BX(10) */
-
 
331
    jc FAIL   /* overflow */
-
 
332
    add ax, cx
-
 
333
    /* if CF then overflow occured (overflow part lands in DX) */
-
 
334
    jnc NEXTBYTE
-
 
335
 
-
 
336
    FAIL:
-
 
337
    inc [err]
-
 
338
 
-
 
339
    DONE: /* save result (CX) into indirect memory address r */
-
 
340
    mov bx, [r]
-
 
341
    mov [bx], cx
-
 
342
  }
-
 
343
  return(err);
-
 
344
}
304
 
345