aboutsummaryrefslogtreecommitdiff
path: root/int21dos.h
blob: 790bd8899e1cbc58dce91ebf2ed96065216ee1e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
/*
 * VBMouse - Interface to some DOS int 21h services
 * Copyright (C) 2022 Javier S. Pedro
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#ifndef INT21DOS_H
#define INT21DOS_H

#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <dos.h>
#include "utils.h"

#define DOS_PSP_SIZE 256

enum dos_error {
	DOS_ERROR_SUCCESS = 0,
	DOS_ERROR_INVALID_FUNCTION = 1,
	DOS_ERROR_FILE_NOT_FOUND = 2,
	DOS_ERROR_PATH_NOT_FOUND = 3,
	DOS_ERROR_TOO_MANY_OPEN_FILES = 4,
	DOS_ERROR_ACCESS_DENIED = 5,
	DOS_ERROR_INVALID_HANDLE = 6,
	DOS_ERROR_ARENA_TRASHED = 7,
	DOS_ERROR_NOT_ENOUGH_MEMORY = 8,
	DOS_ERROR_INVALID_BLOCK = 9,
	DOS_ERROR_BAD_ENVIRONMENT = 10,
	DOS_ERROR_BAD_FORMAT = 11,
	DOS_ERROR_INVALID_ACCESS = 12,
	DOS_ERROR_INVALID_DATA = 13,
	DOS_ERROR_OUT_OF_MEMORY = 14,
	DOS_ERROR_INVALID_DRIVE = 15,
	DOS_ERROR_CURRENT_DIRECTORY = 16,
	DOS_ERROR_NOT_SAME_DEVICE = 17,
	DOS_ERROR_NO_MORE_FILES = 18,
	DOS_ERROR_WRITE_PROTECT = 19,
	DOS_ERROR_BAD_UNIT = 20,
	DOS_ERROR_NOT_READY = 21,
	DOS_ERROR_BAD_COMMAND = 22,
	DOS_ERROR_CRC = 23,
	DOS_ERROR_BAD_LENGTH = 24,
	DOS_ERROR_SEEK = 25,
	DOS_ERROR_NOT_DOS_DISK = 26,
	DOS_ERROR_SECTOR_NOT_FOUND = 27,

	DOS_ERROR_GEN_FAILURE = 31,

	DOS_ERROR_HANDLE_EOF = 38,
	DOS_ERROR_HANDLE_DISK_FULL = 39,

	DOS_ERROR_FILE_EXISTS = 80,
	DOS_ERROR_CANNOT_MAKE = 82,
};

// General functions
static inline void dos_print_string(const char *s);
#pragma aux dos_print_string = \
	"mov ah, 0x09" \
	"int 0x21" \
	__parm [dx] \
	__modify [ah]

static inline uint8_t dos_read_character(void);
#pragma aux dos_read_character = \
	"mov ah, 0x08" \
	"int 0x21" \
	__value [al] \
	__modify [ax]

// Memory manipulation functions

typedef __segment segment_t;

enum dos_allocation_strategy {
	DOS_FIT_FIRST    = 0,
	DOS_FIT_BEST     = 1,
	DOS_FIT_LAST     = 2,

	DOS_FIT_HIGH     = 0x80,
	DOS_FIT_HIGHONLY = 0x40,
};

/** Converts bytes to paragraphs (16 bytes), rounding up. */
static inline unsigned get_paragraphs(unsigned bytes)
{
	return (bytes + 15) / 16;
}

static unsigned dos_query_allocation_strategy(void);
#pragma aux dos_query_allocation_strategy = \
	"mov ax, 0x5800" \
	"int 0x21" \
	__value [ax]

static int dos_set_allocation_strategy(unsigned strategy);
#pragma aux dos_set_allocation_strategy = \
	"clc" \
	"mov ax, 0x5801" \
	"int 0x21" \
	"jc end" \
	"mov ax, 0" \
	"end:" \
	__parm [bx] \
	__value [ax]

static bool dos_query_umb_link_state(void);
#pragma aux dos_query_umb_link_state = \
	"mov ax, 0x5802" \
	"int 0x21" \
	__value [al]

static int dos_set_umb_link_state(bool state);
#pragma aux dos_set_umb_link_state = \
	"clc" \
	"mov ax, 0x5803" \
	"int 0x21" \
	"jc end" \
	"mov ax, 0" \
	"end:" \
	__parm [bx] \
	__value [ax]

/** Allocates a new DOS segment.
 *  @returns either the allocated segment or 0 if any error. */
static __segment dos_alloc(unsigned paragraphs);
#pragma aux dos_alloc = \
	"clc" \
	"mov ah, 0x48" \
	"int 0x21" \
	"jnc end" \
	"mov ax, 0xB" \
	"end:" \
	__parm [bx] \
	__value [ax]

/** Frees DOS segment. */
static void dos_free(segment_t segment);
#pragma aux dos_free = \
	"mov ah, 0x49" \
	"int 0x21" \
	__parm [es] \
	__modify [ax]

/** Sets a given PSP as the current active process. */
static void dos_set_psp(segment_t psp);
#pragma aux dos_set_psp = \
	"mov ah, 0x50" \
	"int 0x21" \
	__parm [bx] \
	__modify [ax]

// Internal DOS structures

// For documentation of these, better grab "Undocumented DOS" or similar book
// These are probably valid only on DOS >= 4

typedef _Packed struct dos_current_directory_structure {
	char curr_path[67];
	uint16_t flags;
	uint32_t disk_blk;
	void __far * info;
	char pad1[11];
} DOSCDS;
STATIC_ASSERT(sizeof(DOSCDS) == 88);

enum dos_cds_flags {
	DOS_CDS_FLAG_PHYSICAL = 0x4000,
	DOS_CDS_FLAG_NETWORK = 0x8000
};

typedef _Packed struct dos_system_file_table_entry {
	uint16_t num_handles;
	uint16_t open_mode;
	uint8_t attr;
	uint16_t dev_info;
	void __far *dpb;
	uint16_t start_cluster;
	uint16_t f_time;
	uint16_t f_date;
	uint32_t f_size;
	uint32_t f_pos;
	uint16_t last_rel_cluster;
	uint16_t last_abs_cluster;
	uint16_t dir_sector;
	uint8_t dir_entry;
	char filename[11];
} DOSSFT;
STATIC_ASSERT(sizeof(DOSSFT) == 43);

enum dos_sft_dev_flags {
	DOS_SFT_FLAG_NETWORK  = 0x8000,
	DOS_SFT_FLAG_TIME_SET = 0x4000,
	DOS_SFT_FLAG_CLEAN    = 0x0040,
	DOS_SFT_DRIVE_MASK    = 0x001F
};

typedef _Packed struct dos_search_data_block {
	uint8_t drive;
	char search_templ[11];
	uint8_t search_attr;
	uint16_t dir_entry;
	uint16_t par_clstr;
	char pad1[4];
} DOSSDB;
STATIC_ASSERT(sizeof(DOSSDB) == 21);

enum dos_sdb_drive_flags {
	DOS_SDB_DRIVE_FLAG_NETWORK = 0x80,
	DOS_SDB_DRIVE_MASK         = 0x1F
};

typedef _Packed struct dos_lock_params {
	uint32_t start_offset;
	uint32_t region_size;
} DOSLOCK;

typedef _Packed struct dos_direntry {
	char filename[11];
	uint8_t attr;
	char pad1[10];
	uint16_t f_time;
	uint16_t f_date;
	uint16_t start_cluster;
	uint32_t f_size;
} DOSDIR;
STATIC_ASSERT(sizeof(DOSDIR) == 32);

typedef _Packed struct dos_swappable_area {
	uint8_t criterr;
	uint8_t indos;
	uint8_t drive_num;
	uint8_t lasterr_dum[9];
	uint8_t __far *cur_dta;
	uint16_t cur_psp;
	uint8_t pad1[4];
	uint8_t cur_drive;
	uint8_t pad2[135];
	char fn1[128];
	char fn2[128];
	DOSSDB sdb;
	DOSDIR found_file;
	DOSCDS drive_cdscopy;
	uint8_t fcb_fn1[11];
	uint8_t pad3[1];
	uint8_t fcb_fn2[11];
	uint8_t pad4[11];
	uint8_t search_attr;
	uint8_t open_mode;
	uint8_t pad5[51];
	DOSCDS __far *drive_cds;
	uint8_t pad6[12];
	uint16_t fn1_csoffset;
	uint16_t fn2_csoffset;
	uint8_t pad7[71];
	uint16_t openex_act;
	uint16_t openex_attr;
	uint16_t openex_mode;
	uint8_t pad8[29];
	DOSSDB rename_srcfile;
	DOSDIR rename_file;
} DOSSDA;
STATIC_ASSERT(offsetof(DOSSDA, cur_dta) == 0xC);
STATIC_ASSERT(offsetof(DOSSDA, cur_drive) == 0x16);
STATIC_ASSERT(offsetof(DOSSDA, fn1) == 0x9E);
STATIC_ASSERT(offsetof(DOSSDA, fn2) == 0x11E);
STATIC_ASSERT(offsetof(DOSSDA, sdb) == 0x19E);
STATIC_ASSERT(offsetof(DOSSDA, found_file) == 0x1B3);
STATIC_ASSERT(offsetof(DOSSDA, search_attr) == 0x24D);
STATIC_ASSERT(offsetof(DOSSDA, open_mode) == 0x24E);
STATIC_ASSERT(offsetof(DOSSDA, drive_cds) == 0x282);
STATIC_ASSERT(offsetof(DOSSDA, openex_act) == 0x2DD);
STATIC_ASSERT(offsetof(DOSSDA, openex_mode) == 0x2E1);

typedef _Packed struct dos_list_of_lists {
	char pad1[22];
	DOSCDS __far *cds;
	char pad2[7];
	uint8_t last_drive;
} DOSLOL;

typedef _Packed struct dos_nls_table {
	uint8_t table_id;
	void __far *table_data;
} NLSTABLE;

typedef _Packed struct file_char_table {
	uint16_t	size;		// table size (not counting this word)
	uint8_t		unk1;		// ??? (01h for MS-DOS 3.30-6.00)
	uint8_t		lowest;		// lowest permissible character value for filename
	uint8_t		highest;	// highest permissible character value for filename
	uint8_t		unk2;		// ??? (00h for MS-DOS 3.30-6.00)
	uint8_t		first_x;	// first excluded character in range \ all characters in this
	uint8_t		last_x;		// last excluded character in range  / range are illegal
	uint8_t		unk3;		// ??? (02h for MS-DOS 3.30-6.00)
	uint8_t		n_illegal;	// number of illegal (terminator) characters
	uint8_t		illegal[1];	// characters which terminate a filename:       ."/\[]:|<>+=;,
} FCHAR;

static inline int drive_letter_to_index(char letter)
{
	if (letter >= 'A' && letter <= 'Z') return letter - 'A';
	else if (letter >= 'a' && letter <= 'z') return letter - 'a';
	else return -1;
}

static inline char drive_index_to_letter(int index)
{
	return 'A' + index;
}

static inline DOSSDA __far * dos_get_swappable_dos_area(void);
#pragma aux dos_get_swappable_dos_area = \
	"push ds" \
	"mov ax, 0x5D06" \
	"int 0x21" \
	"jnc success" \
	"xor si, si" \
	"mov es, si" \
	"jmp end" \
	"success: mov ax, ds" \
	"mov es, ax" \
	"end: pop ds" \
	__value [es si] \
	__modify [ax cx dx]

static inline DOSLOL __far * dos_get_list_of_lists(void);
#pragma aux dos_get_list_of_lists = \
	"mov ax, 0x5200" \
	"int 0x21" \
	__value [es bx] \
	__modify [ax]

static inline uint16_t dos_sft_decref(DOSSFT __far *sft);
#pragma aux dos_sft_decref = \
	"mov ax, 0x1208" \
	"int 0x2F" \
	__parm [es di] \
	__value [ax]

// Network redirector interface

enum DOS_REDIR_SUBFUNCTION {
	DOS_FN_RMDIR     = 0x01,
	DOS_FN_MKDIR     = 0x03,
	DOS_FN_CHDIR     = 0x05,
	DOS_FN_CLOSE     = 0x06,
	DOS_FN_COMMIT    = 0x07,
	DOS_FN_READ      = 0x08,
	DOS_FN_WRITE     = 0x09,
	DOS_FN_LOCK      = 0x0A,
	DOS_FN_UNLOCK    = 0x0B,
	DOS_FN_GET_DISK_FREE = 0x0C,
	DOS_FN_SET_FILE_ATTR = 0x0E,
	DOS_FN_GET_FILE_ATTR = 0x0F,
	DOS_FN_RENAME    = 0x11,
	DOS_FN_DELETE    = 0x13,
	DOS_FN_OPEN      = 0x16,
	DOS_FN_CREATE    = 0x17,
	DOS_FN_FIND_FIRST = 0x1B,
	DOS_FN_FIND_NEXT  = 0x1C,
	DOS_FN_CLOSE_ALL  = 0x1D,
	DOS_FN_DO_REDIR   = 0x1E,
	DOS_FN_PRINTSETUP = 0x1F,
	DOS_FN_FLUSH      = 0x20,
	DOS_FN_SEEK_END   = 0x21,
	DOS_FN_ATEXIT     = 0x22,
	DOS_FN_QUALIFY    = 0x23,
	DOS_FN_OPEN_EX    = 0x2E
};

enum OPENEX_ACTIONS {
	OPENEX_FAIL_IF_EXISTS     = 0x00,
	OPENEX_OPEN_IF_EXISTS     = 0x01,
	OPENEX_REPLACE_IF_EXISTS  = 0x02,
	OPENEX_FAIL_IF_NEW        = 0x00,
	OPENEX_CREATE_IF_NEW      = 0x10,
};

enum OPENEX_MODE {
	OPENEX_MODE_READ       = 0,
	OPENEX_MODE_WRITE      = 1,
	OPENEX_MODE_RDWR       = 2,
	OPENEX_MODE_MASK       = 3,
	OPENEX_SHARE_COMPAT    = 0x00,
	OPENEX_SHARE_DENYALL   = 0x10,
	OPENEX_SHARE_DENYWRITE = 0x20,
	OPENEX_SHARE_DENYREAD  = 0x30,
	OPENEX_SHARE_DENYNONE  = 0x40,
	OPENEX_SHARE_MASK      = 0x70,
};

enum OPENEX_RESULT {
	OPENEX_FILE_OPENED        = 1,
	OPENEX_FILE_CREATED       = 2,
	OPENEX_FILE_REPLACED      = 3,
};

#endif // INT21DOS_H