aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/frotz/processor_buffer.cpp
blob: 734a5309fc0790aac943160c7817ca1f476dd49a (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
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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.
 *
 */

#include "glk/frotz/processor.h"
#include "common/algorithm.h"
#include "common/textconsole.h"

namespace Glk {
namespace Frotz {

const char *const Processor::ERR_MESSAGES[ERR_NUM_ERRORS] = {
	"Text buffer overflow",
	"Store out of dynamic memory",
	"Division by zero",
	"Illegal object",
	"Illegal attribute",
	"No such property",
	"Stack overflow",
	"Call to illegal address",
	"Call to non-routine",
	"Stack underflow",
	"Illegal opcode",
	"Bad stack frame",
	"Jump to illegal address",
	"Can't save while in interrupt",
	"Nesting stream #3 too deep",
	"Illegal window",
	"Illegal window property",
	"Print at illegal address",
	"Illegal dictionary word length",
	"@jin called with object 0",
	"@get_child called with object 0",
	"@get_parent called with object 0",
	"@get_sibling called with object 0",
	"@get_prop_addr called with object 0",
	"@get_prop called with object 0",
	"@put_prop called with object 0",
	"@clear_attr called with object 0",
	"@set_attr called with object 0",
	"@test_attr called with object 0",
	"@move_object called moving object 0",
	"@move_object called moving into object 0",
	"@remove_object called with object 0",
	"@get_next_prop called with object 0"
};

void Processor::flush_buffer() {
	/* Make sure we stop when flush_buffer is called from flush_buffer.
	 * Note that this is difficult to avoid as we might print a newline
	 * during flush_buffer, which might cause a newline interrupt, that
	 * might execute any arbitrary opcode, which might flush the buffer.
	 */
	if (_locked || bufferEmpty())
		return;

	// Send the buffer to the output streams
	_buffer[_bufPos] = '\0';

	_locked = true;
	stream_word(_buffer);
	_locked = false;

	// Reset the buffer
	_bufPos = 0;
	_prevC = '\0';
}

void Processor::print_char(zchar c) {
	static bool flag = false;

	if (message || ostream_memory || enable_buffering) {
		if (!flag) {
			// Characters 0 and ZC_RETURN are special cases
			if (c == ZC_RETURN) {
				new_line();
				return;
			}
			if (c == 0)
				return;

			// Flush the buffer before a whitespace or after a hyphen
			if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || (_prevC == '-' && c != '-'))
				flush_buffer();

			// Set the flag if this is part one of a style or font change
			if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE)
				flag = true;

			// Remember the current character code
			_prevC = c;
		} else {
			flag = false;
		}

		// Insert the character into the buffer
		_buffer[_bufPos++] = c;

		if (_bufPos == TEXT_BUFFER_SIZE)
			error("Text buffer overflow");
	} else {
		stream_char(c);
	}
}

void Processor::print_string(const char *s) {
	char c;

	while ((c = *s++) != 0) {
		if (c == '\n')
			new_line();
		else
			print_char(c);
	}
}

void Processor::print_long(uint value, int base) {
	unsigned long i;
	char c;

	for (i = (base == 10 ? 1000000000 : 0x10000000); i != 0; i /= base) {
		if (value >= i || i == 1) {
			c = (value / i) % base;
			print_char(c + (c <= 9 ? '0' : 'a' - 10));
		}
	}
}

void Processor::new_line()  {
	flush_buffer();
	stream_new_line();
}

void Processor::runtimeError(ErrorCode errNum) {
	int wasfirst;

	if (errNum <= 0 || errNum > ERR_NUM_ERRORS)
		return;

	if (_err_report_mode == ERR_REPORT_FATAL
		|| (!_ignore_errors && errNum <= ERR_MAX_FATAL)) {
		flush_buffer();
		error("%s", ERR_MESSAGES[errNum - 1]);
		return;
	}

	wasfirst = (_errorCount[errNum - 1] == 0);
	_errorCount[errNum - 1]++;

	if ((_err_report_mode == ERR_REPORT_ALWAYS)
		|| (_err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
		offset_t pc;
		GET_PC(pc);
		print_string("Warning: ");
		print_string(ERR_MESSAGES[errNum - 1]);
		print_string(" (PC = ");
		print_long(pc, 16);
		print_char(')');

		if (_err_report_mode == ERR_REPORT_ONCE) {
			print_string(" (will ignore further occurrences)");
		} else {
			print_string(" (occurence ");
			print_long(_errorCount[errNum - 1], 10);
			print_char(')');
		}

		new_line();
	}
}

} // End of namespace Frotz
} // End of namespace Glk