1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "sci/sci.h"
24 #include "sci/engine/state.h"
25 #include "sci/engine/seg_manager.h"
26 #include "sci/engine/vm_types.h"
27 #include "sci/engine/workarounds.h"
28 
29 namespace Sci {
30 
getSegment() const31 SegmentId reg_t::getSegment() const {
32 	if (getSciVersion() < SCI_VERSION_3) {
33 		return _segment;
34 	} else {
35 		// Return the lower 14 bits of the segment
36 		return (_segment & 0x3FFF);
37 	}
38 }
39 
setSegment(SegmentId segment)40 void reg_t::setSegment(SegmentId segment) {
41 	if (getSciVersion() < SCI_VERSION_3) {
42 		_segment = segment;
43 	} else {
44 		// Set the lower 14 bits of the segment, and preserve the upper 2 ones for the offset
45 		_segment = (_segment & 0xC000) | (segment & 0x3FFF);
46 	}
47 }
48 
getOffset() const49 uint32 reg_t::getOffset() const {
50 	if (getSciVersion() < SCI_VERSION_3) {
51 		return _offset;
52 	} else {
53 		// Return the lower 16 bits from the offset, and the 17th and 18th bits from the segment
54 		return ((_segment & 0xC000) << 2) | _offset;
55 	}
56 }
57 
setOffset(uint32 offset)58 void reg_t::setOffset(uint32 offset) {
59 	if (getSciVersion() < SCI_VERSION_3) {
60 		_offset = offset;
61 	} else {
62 		// Store the lower 16 bits in the offset, and the 17th and 18th bits in the segment
63 		_offset = offset & 0xFFFF;
64 		_segment = ((offset & 0x30000) >> 2) | (_segment & 0x3FFF);
65 	}
66 }
67 
lookForWorkaround(const reg_t right,const char * operation) const68 reg_t reg_t::lookForWorkaround(const reg_t right, const char *operation) const {
69 	SciCallOrigin originReply;
70 	SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, arithmeticWorkarounds, &originReply);
71 	if (solution.type == WORKAROUND_NONE)
72 		error("Invalid arithmetic operation (%s - params: %04x:%04x and %04x:%04x) from %s", operation, PRINT_REG(*this), PRINT_REG(right), originReply.toString().c_str());
73 	assert(solution.type == WORKAROUND_FAKE);
74 	return make_reg(0, solution.value);
75 }
76 
operator +(const reg_t right) const77 reg_t reg_t::operator+(const reg_t right) const {
78 	if (isPointer() && right.isNumber()) {
79 		// Pointer arithmetics. Only some pointer types make sense here
80 		SegmentObj *mobj = g_sci->getEngineState()->_segMan->getSegmentObj(getSegment());
81 
82 		if (!mobj)
83 			error("[VM]: Attempt to add %d to invalid pointer %04x:%04x", right.getOffset(), PRINT_REG(*this));
84 
85 		switch (mobj->getType()) {
86 		case SEG_TYPE_LOCALS:
87 		case SEG_TYPE_SCRIPT:
88 		case SEG_TYPE_STACK:
89 		case SEG_TYPE_DYNMEM:
90 			return make_reg(getSegment(), getOffset() + right.toSint16());
91 		default:
92 			return lookForWorkaround(right, "addition");
93 		}
94 	} else if (isNumber() && right.isPointer()) {
95 		// Adding a pointer to a number, flip the order
96 		return right + *this;
97 	} else if (isNumber() && right.isNumber()) {
98 		// Normal arithmetics
99 		return make_reg(0, toSint16() + right.toSint16());
100 	} else {
101 		return lookForWorkaround(right, "addition");
102 	}
103 }
104 
operator -(const reg_t right) const105 reg_t reg_t::operator-(const reg_t right) const {
106 	if (getSegment() == right.getSegment()) {
107 		// We can subtract numbers, or pointers with the same segment,
108 		// an operation which will yield a number like in C
109 		return make_reg(0, toSint16() - right.toSint16());
110 	} else {
111 		return *this + make_reg(right.getSegment(), -right.toSint16());
112 	}
113 }
114 
operator *(const reg_t right) const115 reg_t reg_t::operator*(const reg_t right) const {
116 	if (isNumber() && right.isNumber())
117 		return make_reg(0, toSint16() * right.toSint16());
118 	else
119 		return lookForWorkaround(right, "multiplication");
120 }
121 
operator /(const reg_t right) const122 reg_t reg_t::operator/(const reg_t right) const {
123 	if (isNumber() && right.isNumber() && !right.isNull())
124 		return make_reg(0, toSint16() / right.toSint16());
125 	else
126 		return lookForWorkaround(right, "division");
127 }
128 
operator %(const reg_t right) const129 reg_t reg_t::operator%(const reg_t right) const {
130 	if (isNumber() && right.isNumber() && !right.isNull()) {
131 		// Support for negative numbers was added in Iceman, and perhaps in
132 		// SCI0 0.000.685 and later. Theoretically, this wasn't really used
133 		// in SCI0, so the result is probably unpredictable. Such a case
134 		// would indicate either a script bug, or a modulo on an unsigned
135 		// integer larger than 32767. In any case, such a case should be
136 		// investigated, instead of being silently accepted.
137 		if (getSciVersion() <= SCI_VERSION_0_LATE && (toSint16() < 0 || right.toSint16() < 0))
138 			warning("Modulo of a negative number has been requested for SCI0. This *could* lead to issues");
139 		int16 value = toSint16();
140 		int16 modulo = ABS(right.toSint16());
141 		int16 result = value % modulo;
142 		if (result < 0)
143 			result += modulo;
144 		return make_reg(0, result);
145 	} else
146 		return lookForWorkaround(right, "modulo");
147 }
148 
operator >>(const reg_t right) const149 reg_t reg_t::operator>>(const reg_t right) const {
150 	if (isNumber() && right.isNumber())
151 		return make_reg(0, toUint16() >> right.toUint16());
152 	else
153 		return lookForWorkaround(right, "shift right");
154 }
155 
operator <<(const reg_t right) const156 reg_t reg_t::operator<<(const reg_t right) const {
157 	if (isNumber() && right.isNumber())
158 		return make_reg(0, toUint16() << right.toUint16());
159 	else
160 		return lookForWorkaround(right, "shift left");
161 }
162 
operator +(int16 right) const163 reg_t reg_t::operator+(int16 right) const {
164 	return *this + make_reg(0, right);
165 }
166 
operator -(int16 right) const167 reg_t reg_t::operator-(int16 right) const {
168 	return *this - make_reg(0, right);
169 }
170 
requireUint16() const171 uint16 reg_t::requireUint16() const {
172 	if (isNumber())
173 		return toUint16();
174 	else
175 		// The right parameter is NULL_REG because
176 		// we're not comparing *this with anything here.
177 		return lookForWorkaround(NULL_REG, "require unsigned number").toUint16();
178 }
179 
requireSint16() const180 int16 reg_t::requireSint16() const {
181 	if (isNumber())
182 		return toSint16();
183 	else
184 		// The right parameter is NULL_REG because
185 		// we're not comparing *this with anything here.
186 		return lookForWorkaround(NULL_REG, "require signed number").toSint16();
187 }
188 
operator &(const reg_t right) const189 reg_t reg_t::operator&(const reg_t right) const {
190 	if (isNumber() && right.isNumber())
191 		return make_reg(0, toUint16() & right.toUint16());
192 	else
193 		return lookForWorkaround(right, "bitwise AND");
194 }
195 
operator |(const reg_t right) const196 reg_t reg_t::operator|(const reg_t right) const {
197 	if (isNumber() && right.isNumber())
198 		return make_reg(0, toUint16() | right.toUint16());
199 	else
200 		return lookForWorkaround(right, "bitwise OR");
201 }
202 
operator ^(const reg_t right) const203 reg_t reg_t::operator^(const reg_t right) const {
204 	if (isNumber() && right.isNumber())
205 		return make_reg(0, toUint16() ^ right.toUint16());
206 	else
207 		return lookForWorkaround(right, "bitwise XOR");
208 }
209 
210 #ifdef ENABLE_SCI32
operator &(int16 right) const211 reg_t reg_t::operator&(int16 right) const {
212 	return *this & make_reg(0, right);
213 }
214 
operator |(int16 right) const215 reg_t reg_t::operator|(int16 right) const {
216 	return *this | make_reg(0, right);
217 }
218 
operator ^(int16 right) const219 reg_t reg_t::operator^(int16 right) const {
220 	return *this ^ make_reg(0, right);
221 }
222 #endif
223 
cmp(const reg_t right,bool treatAsUnsigned) const224 int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const {
225 	if (getSegment() == right.getSegment()) { // can compare things in the same segment
226 		if (treatAsUnsigned || !isNumber())
227 			return toUint16() - right.toUint16();
228 		else
229 			return toSint16() - right.toSint16();
230 #ifdef ENABLE_SCI32
231 	} else if (getSciVersion() >= SCI_VERSION_2) {
232 		return sci32Comparison(right);
233 #endif
234 	} else if (pointerComparisonWithInteger(right)) {
235 		return 1;
236 	} else if (right.pointerComparisonWithInteger(*this)) {
237 		return -1;
238 	} else
239 		return lookForWorkaround(right, "comparison").toSint16();
240 }
241 
242 #ifdef ENABLE_SCI32
sci32Comparison(const reg_t right) const243 int reg_t::sci32Comparison(const reg_t right) const {
244 	// In SCI32, MemIDs are normally indexes into the memory manager's handle
245 	// list, but the engine reserves indexes at and above 20000 for objects
246 	// that were created inside the engine (as opposed to inside the VM). The
247 	// engine compares these as a tiebreaker for graphics objects that are at
248 	// the same priority, and it is necessary to at least minimally handle
249 	// this situation.
250 	// This is obviously a bogus comparison, but then, this entire thing is
251 	// bogus. For the moment, it just needs to be deterministic.
252 	if (isNumber() && !right.isNumber()) {
253 		return 1;
254 	} else if (right.isNumber() && !isNumber()) {
255 		return -1;
256 	}
257 
258 	return getOffset() - right.getOffset();
259 }
260 #endif
261 
pointerComparisonWithInteger(const reg_t right) const262 bool reg_t::pointerComparisonWithInteger(const reg_t right) const {
263 	// This function handles the case where a script tries to compare a pointer
264 	// to a number. Normally, we would not want to allow that. However, SCI0 -
265 	// SCI1.1 scripts do this in order to distinguish references to
266 	// external resources (which are numbers) from pointers. In
267 	// our SCI implementation, such a check may seem pointless, as
268 	// one can simply use the segment value to achieve this goal.
269 	// But Sierra's SCI did not have the notion of segment IDs, so
270 	// both pointer and numbers were simple integers.
271 	//
272 	// But for some things, scripts had (and have) to distinguish between
273 	// numbers and pointers. Lacking the segment information, Sierra's
274 	// developers resorted to a hack: If an integer is smaller than a certain
275 	// bound, it can be assumed to be a number, otherwise it is assumed to be a
276 	// pointer. This allowed them to implement polymorphic functions, such as
277 	// the Print function, which can be called in two different ways, with a
278 	// pointer or a far text reference:
279 	//
280 	// (Print "foo") // Pointer to a string
281 	// (Print 420 5) // Reference to the fifth message in text resource 420
282 	// It works because in those games, the maximum resource number is 999,
283 	// so any parameter value above that threshold must be a pointer.
284 	// PQ2 japanese compares pointers to 2000 to find out if its a pointer
285 	// or a resource ID. Thus, we check for all integers <= 2000.
286 	//
287 	// Some examples where game scripts check for arbitrary numbers against
288 	// pointers:
289 	// Hoyle 3, Pachisi, when any opponent is about to talk
290 	// SQ1, room 28, when throwing water at the Orat
291 	// SQ1, room 58, when giving the ID card to the robot
292 	// SQ4 CD, at the first game screen, when the narrator is about to speak
293 	return (isPointer() && right.isNumber() && right.getOffset() <= 2000 && getSciVersion() <= SCI_VERSION_1_1);
294 }
295 
296 } // End of namespace Sci
297