aboutsummaryrefslogtreecommitdiff
path: root/dostsr.h
blob: 34e5a5718cbe46d07fdbc5c41c338d66c0e85ead (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
/*
 * VBMouse - utility functions for DOS TSRs
 * 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 DOSTSR_H
#define DOSTSR_H

#include <stdint.h>

#include "int21dos.h"

/** Deallocates the environment block from the passed PSP segment. */
static void deallocate_environment(segment_t psp)
{
	// TODO : Too lazy to make PSP struct;
	// 0x2C is offsetof the environment block field on the PSP
	uint16_t __far *envblockP = (uint16_t __far *) MK_FP(psp, 0x2C);
	dos_free(*envblockP);
	*envblockP = 0;
}

/** Copies a program to another location.
 *  @param new_seg PSP segment for the new location
 *  @param old_seg PSP segment for the old location
 *  @param size size of the program to copy including PSP size. */
static void copy_program(segment_t new_seg, segment_t old_seg, unsigned size)
{
	// The MCB is always 1 segment before.
	uint8_t __far *new_mcb = MK_FP(new_seg - 1, 0);
	uint8_t __far *old_mcb = MK_FP(old_seg - 1, 0);
	uint16_t __far *new_mcb_owner = (uint16_t __far *) &new_mcb[1];
	char __far *new_mcb_owner_name = &new_mcb[8];
	char __far *old_mcb_owner_name = &old_mcb[8];

	// Copy entire resident segment including PSP
	_fmemcpy(MK_FP(new_seg, 0), MK_FP(old_seg, 0), size);

	// Make the new MCB point to itself as owner
	*new_mcb_owner = new_seg;

	// Copy the program name, too.
	_fmemcpy(new_mcb_owner_name, old_mcb_owner_name, 8);
}

/** Allocates a UMB of the given size.
 *  If no UMBs are available, this may still return a block in conventional memory. */
static __segment allocate_umb(unsigned size)
{
	bool old_umb_link = dos_query_umb_link_state();
	unsigned int old_strategy = dos_query_allocation_strategy();
	segment_t new_segment;

	dos_set_umb_link_state(true);
	dos_set_allocation_strategy(DOS_FIT_BEST | DOS_FIT_HIGHONLY);

	new_segment = dos_alloc(get_paragraphs(size));

	dos_set_umb_link_state(old_umb_link);
	dos_set_allocation_strategy(old_strategy);

	return new_segment;
}

static __segment reallocate_to_umb(segment_t cur_seg, unsigned segment_size)
{
	segment_t old_segment_psp = cur_seg - (DOS_PSP_SIZE/16);
	segment_t new_segment_psp;

	deallocate_environment(_psp);

	// If we are already in UMA, don't bother
	if (old_segment_psp >= 0xA000) {
		return 0;
	}

	new_segment_psp = allocate_umb(segment_size);

	if (new_segment_psp && new_segment_psp >= 0xA000) {
		segment_t new_segment = new_segment_psp + (DOS_PSP_SIZE/16);

		// Create a new program instance including PSP at the new_segment
		copy_program(new_segment_psp, old_segment_psp, segment_size);

		// Tell DOS to "switch" to the new program
		dos_set_psp(new_segment_psp);

		// Return the new segment
		return new_segment;
	} else {
		if (new_segment_psp) {
			// In case we got another low-memory segment...
			dos_free(new_segment_psp);
		}

		return 0;
	}
}

#endif // DOSTSR_H