1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* main.c */ |
4 | /* */ |
5 | /* sim65 main program */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2002-2009, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | #include <string.h> |
37 | #include <stdlib.h> |
38 | #include <errno.h> |
39 | |
40 | /* common */ |
41 | #include "abend.h" |
42 | #include "cmdline.h" |
43 | #include "print.h" |
44 | #include "version.h" |
45 | |
46 | /* sim65 */ |
47 | #include "6502.h" |
48 | #include "error.h" |
49 | #include "memory.h" |
50 | #include "paravirt.h" |
51 | |
52 | |
53 | |
54 | /*****************************************************************************/ |
55 | /* Data */ |
56 | /*****************************************************************************/ |
57 | |
58 | |
59 | |
60 | /* Name of program file */ |
61 | const char* ProgramFile; |
62 | |
63 | /* exit simulator after MaxCycles Cycles */ |
64 | unsigned long MaxCycles; |
65 | |
66 | /* Header signature 'sim65' */ |
67 | static const unsigned char [] = { |
68 | 0x73, 0x69, 0x6D, 0x36, 0x35 |
69 | }; |
70 | #define (sizeof(HeaderSignature)/sizeof(HeaderSignature[0])) |
71 | |
72 | static const unsigned char = 2; |
73 | |
74 | |
75 | |
76 | /*****************************************************************************/ |
77 | /* Code */ |
78 | /*****************************************************************************/ |
79 | |
80 | |
81 | |
82 | static void Usage (void) |
83 | { |
84 | printf ("Usage: %s [options] file [arguments]\n" |
85 | "Short options:\n" |
86 | " -h\t\t\tHelp (this text)\n" |
87 | " -c\t\t\tPrint amount of executed CPU cycles\n" |
88 | " -v\t\t\tIncrease verbosity\n" |
89 | " -V\t\t\tPrint the simulator version number\n" |
90 | " -x <num>\t\tExit simulator after <num> cycles\n" |
91 | "\n" |
92 | "Long options:\n" |
93 | " --help\t\tHelp (this text)\n" |
94 | " --cycles\t\tPrint amount of executed CPU cycles\n" |
95 | " --verbose\t\tIncrease verbosity\n" |
96 | " --version\t\tPrint the simulator version number\n" , |
97 | ProgName); |
98 | } |
99 | |
100 | |
101 | |
102 | static void OptHelp (const char* Opt attribute ((unused)), |
103 | const char* Arg attribute ((unused))) |
104 | /* Print usage information and exit */ |
105 | { |
106 | Usage (); |
107 | exit (EXIT_SUCCESS); |
108 | } |
109 | |
110 | |
111 | |
112 | static void OptVerbose (const char* Opt attribute ((unused)), |
113 | const char* Arg attribute ((unused))) |
114 | /* Increase verbosity */ |
115 | { |
116 | ++Verbosity; |
117 | } |
118 | |
119 | |
120 | |
121 | static void OptCycles (const char* Opt attribute ((unused)), |
122 | const char* Arg attribute ((unused))) |
123 | /* Set flag to print amount of cycles at the end */ |
124 | { |
125 | PrintCycles = 1; |
126 | } |
127 | |
128 | |
129 | |
130 | static void OptVersion (const char* Opt attribute ((unused)), |
131 | const char* Arg attribute ((unused))) |
132 | /* Print the simulator version */ |
133 | { |
134 | fprintf (stderr, "%s V%s\n" , ProgName, GetVersionAsString ()); |
135 | exit(EXIT_SUCCESS); |
136 | } |
137 | |
138 | static void OptQuitXIns (const char* Opt attribute ((unused)), |
139 | const char* Arg attribute ((unused))) |
140 | /* quit after MaxCycles cycles */ |
141 | { |
142 | MaxCycles = strtoul(Arg, NULL, 0); |
143 | } |
144 | |
145 | static unsigned char ReadProgramFile (void) |
146 | /* Load program into memory */ |
147 | { |
148 | unsigned I; |
149 | int Val, Val2; |
150 | int Version; |
151 | unsigned Addr; |
152 | unsigned Load, Reset; |
153 | unsigned char SPAddr = 0x00; |
154 | |
155 | /* Open the file */ |
156 | FILE* F = fopen (ProgramFile, "rb" ); |
157 | if (F == 0) { |
158 | Error ("Cannot open '%s': %s" , ProgramFile, strerror (errno)); |
159 | } |
160 | |
161 | /* Verify the header signature */ |
162 | for (I = 0; I < HEADER_SIGNATURE_LENGTH; ++I) { |
163 | if ((Val = fgetc(F)) != HeaderSignature[I]) { |
164 | Error ("'%s': Invalid header signature." , ProgramFile); |
165 | } |
166 | } |
167 | |
168 | /* Get header version */ |
169 | if ((Version = fgetc(F)) != HeaderVersion) { |
170 | Error ("'%s': Invalid header version." , ProgramFile); |
171 | } |
172 | |
173 | /* Get the CPU type from the file header */ |
174 | if ((Val = fgetc(F)) != EOF) { |
175 | if (Val != CPU_6502 && Val != CPU_65C02) { |
176 | Error ("'%s': Invalid CPU type" , ProgramFile); |
177 | } |
178 | CPU = Val; |
179 | } |
180 | |
181 | /* Get the address of sp from the file header */ |
182 | if ((Val = fgetc(F)) != EOF) { |
183 | SPAddr = Val; |
184 | } |
185 | |
186 | /* Get load address */ |
187 | if (((Val = fgetc(F)) == EOF) || |
188 | ((Val2 = fgetc(F)) == EOF)) { |
189 | Error ("'%s': Header missing load address" , ProgramFile); |
190 | } |
191 | Load = Val | (Val2 << 8); |
192 | |
193 | /* Get reset address */ |
194 | if (((Val = fgetc(F)) == EOF) || |
195 | ((Val2 = fgetc(F)) == EOF)) { |
196 | Error ("'%s': Header missing reset address" , ProgramFile); |
197 | } |
198 | Reset = Val | (Val2 << 8); |
199 | |
200 | /* Read the file body into memory */ |
201 | Addr = Load; |
202 | while ((Val = fgetc(F)) != EOF) { |
203 | if (Addr >= PARAVIRT_BASE) { |
204 | Error ("'%s': To large to fit into $%04X-$%04X" , ProgramFile, Addr, PARAVIRT_BASE); |
205 | } |
206 | MemWriteByte (Addr++, (unsigned char) Val); |
207 | } |
208 | |
209 | /* Check for errors */ |
210 | if (ferror (F)) { |
211 | Error ("Error reading from '%s': %s" , ProgramFile, strerror (errno)); |
212 | } |
213 | |
214 | /* Close the file */ |
215 | fclose (F); |
216 | |
217 | Print (stderr, 1, "Loaded '%s' at $%04X-$%04X\n" , ProgramFile, Load, Addr - 1); |
218 | Print (stderr, 1, "File version: %d\n" , Version); |
219 | Print (stderr, 1, "Reset: $%04X\n" , Reset); |
220 | |
221 | MemWriteWord(0xFFFC, Reset); |
222 | return SPAddr; |
223 | } |
224 | |
225 | |
226 | |
227 | int main (int argc, char* argv[]) |
228 | { |
229 | /* Program long options */ |
230 | static const LongOpt OptTab[] = { |
231 | { "--help" , 0, OptHelp }, |
232 | { "--cycles" , 0, OptCycles }, |
233 | { "--verbose" , 0, OptVerbose }, |
234 | { "--version" , 0, OptVersion }, |
235 | }; |
236 | |
237 | unsigned I; |
238 | unsigned char SPAddr; |
239 | |
240 | /* Initialize the cmdline module */ |
241 | InitCmdLine (&argc, &argv, "sim65" ); |
242 | |
243 | /* Parse the command line */ |
244 | I = 1; |
245 | while (I < ArgCount) { |
246 | |
247 | /* Get the argument */ |
248 | const char* Arg = ArgVec[I]; |
249 | |
250 | /* Check for an option */ |
251 | if (Arg [0] == '-') { |
252 | |
253 | switch (Arg [1]) { |
254 | |
255 | case '-': |
256 | LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0])); |
257 | break; |
258 | |
259 | case 'h': |
260 | case '?': |
261 | OptHelp (Arg, 0); |
262 | break; |
263 | |
264 | case 'c': |
265 | OptCycles (Arg, 0); |
266 | break; |
267 | |
268 | case 'v': |
269 | OptVerbose (Arg, 0); |
270 | break; |
271 | |
272 | case 'V': |
273 | OptVersion (Arg, 0); |
274 | break; |
275 | |
276 | case 'x': |
277 | OptQuitXIns (Arg, GetArg (&I, 2)); |
278 | break; |
279 | |
280 | default: |
281 | UnknownOption (Arg); |
282 | break; |
283 | } |
284 | } else { |
285 | ProgramFile = Arg; |
286 | break; |
287 | } |
288 | |
289 | /* Next argument */ |
290 | ++I; |
291 | } |
292 | |
293 | /* Do we have a program file? */ |
294 | if (ProgramFile == 0) { |
295 | AbEnd ("No program file" ); |
296 | } |
297 | |
298 | MemInit (); |
299 | |
300 | SPAddr = ReadProgramFile (); |
301 | |
302 | ParaVirtInit (I, SPAddr); |
303 | |
304 | Reset (); |
305 | |
306 | while (1) { |
307 | ExecuteInsn (); |
308 | if (MaxCycles && (GetCycles () >= MaxCycles)) { |
309 | ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles reached." ); |
310 | } |
311 | } |
312 | |
313 | /* Return an apropriate exit code */ |
314 | return EXIT_SUCCESS; |
315 | } |
316 | |