1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* paravirt.c */ |
4 | /* */ |
5 | /* Paravirtualization for the sim65 6502 simulator */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2013-2013 Ullrich von Bassewitz */ |
10 | /* Römerstrasse 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 <fcntl.h> |
39 | #include <sys/stat.h> |
40 | #if defined(_WIN32) |
41 | # define O_INITIAL O_BINARY |
42 | #else |
43 | # define O_INITIAL 0 |
44 | #endif |
45 | #if defined(_MSC_VER) |
46 | /* Microsoft compiler */ |
47 | # include <io.h> |
48 | #else |
49 | /* Anyone else */ |
50 | # include <unistd.h> |
51 | #endif |
52 | #ifndef S_IREAD |
53 | # define S_IREAD S_IRUSR |
54 | #endif |
55 | #ifndef S_IWRITE |
56 | # define S_IWRITE S_IWUSR |
57 | #endif |
58 | |
59 | /* common */ |
60 | #include "cmdline.h" |
61 | #include "print.h" |
62 | #include "xmalloc.h" |
63 | |
64 | /* sim65 */ |
65 | #include "6502.h" |
66 | #include "memory.h" |
67 | #include "paravirt.h" |
68 | |
69 | |
70 | |
71 | /*****************************************************************************/ |
72 | /* Data */ |
73 | /*****************************************************************************/ |
74 | |
75 | |
76 | |
77 | typedef void (*PVFunc) (CPURegs* Regs); |
78 | |
79 | static unsigned ArgStart; |
80 | static unsigned char SPAddr; |
81 | |
82 | |
83 | |
84 | /*****************************************************************************/ |
85 | /* Code */ |
86 | /*****************************************************************************/ |
87 | |
88 | |
89 | |
90 | static unsigned GetAX (CPURegs* Regs) |
91 | { |
92 | return Regs->AC + (Regs->XR << 8); |
93 | } |
94 | |
95 | |
96 | |
97 | static void SetAX (CPURegs* Regs, unsigned Val) |
98 | { |
99 | Regs->AC = Val & 0xFF; |
100 | Val >>= 8; |
101 | Regs->XR = Val; |
102 | } |
103 | |
104 | |
105 | |
106 | static unsigned char Pop (CPURegs* Regs) |
107 | { |
108 | return MemReadByte (0x0100 + ++Regs->SP); |
109 | } |
110 | |
111 | |
112 | |
113 | static unsigned PopParam (unsigned char Incr) |
114 | { |
115 | unsigned SP = MemReadZPWord (SPAddr); |
116 | unsigned Val = MemReadWord (SP); |
117 | MemWriteWord (SPAddr, SP + Incr); |
118 | return Val; |
119 | } |
120 | |
121 | |
122 | |
123 | static void PVExit (CPURegs* Regs) |
124 | { |
125 | Print (stderr, 1, "PVExit ($%02X)\n" , Regs->AC); |
126 | if (PrintCycles) { |
127 | Print (stdout, 0, "%lu cycles\n" , GetCycles ()); |
128 | } |
129 | |
130 | exit (Regs->AC); |
131 | } |
132 | |
133 | |
134 | |
135 | static void PVArgs (CPURegs* Regs) |
136 | { |
137 | unsigned ArgC = ArgCount - ArgStart; |
138 | unsigned ArgV = GetAX (Regs); |
139 | unsigned SP = MemReadZPWord (SPAddr); |
140 | unsigned Args = SP - (ArgC + 1) * 2; |
141 | |
142 | Print (stderr, 2, "PVArgs ($%04X)\n" , ArgV); |
143 | |
144 | MemWriteWord (ArgV, Args); |
145 | |
146 | SP = Args; |
147 | while (ArgStart < ArgCount) { |
148 | unsigned I = 0; |
149 | const char* Arg = ArgVec[ArgStart++]; |
150 | SP -= strlen (Arg) + 1; |
151 | do { |
152 | MemWriteByte (SP + I, Arg[I]); |
153 | } |
154 | while (Arg[I++]); |
155 | |
156 | MemWriteWord (Args, SP); |
157 | Args += 2; |
158 | } |
159 | MemWriteWord (Args, SPAddr); |
160 | |
161 | MemWriteWord (SPAddr, SP); |
162 | SetAX (Regs, ArgC); |
163 | } |
164 | |
165 | |
166 | |
167 | static void PVOpen (CPURegs* Regs) |
168 | { |
169 | char Path[1024]; |
170 | int OFlag = O_INITIAL; |
171 | int OMode = 0; |
172 | unsigned RetVal, I = 0; |
173 | |
174 | unsigned Mode = PopParam (Regs->YR - 4); |
175 | unsigned Flags = PopParam (2); |
176 | unsigned Name = PopParam (2); |
177 | |
178 | if (Regs->YR - 4 < 2) { |
179 | /* If the caller didn't supply the mode |
180 | ** argument, use a reasonable default. |
181 | */ |
182 | Mode = 0x01 | 0x02; |
183 | } |
184 | |
185 | do { |
186 | Path[I] = MemReadByte (Name++); |
187 | } |
188 | while (Path[I++]); |
189 | |
190 | Print (stderr, 2, "PVOpen (\"%s\", $%04X)\n" , Path, Flags); |
191 | |
192 | switch (Flags & 0x03) { |
193 | case 0x01: |
194 | OFlag |= O_RDONLY; |
195 | break; |
196 | case 0x02: |
197 | OFlag |= O_WRONLY; |
198 | break; |
199 | case 0x03: |
200 | OFlag |= O_RDWR; |
201 | break; |
202 | } |
203 | if (Flags & 0x10) { |
204 | OFlag |= O_CREAT; |
205 | } |
206 | if (Flags & 0x20) { |
207 | OFlag |= O_TRUNC; |
208 | } |
209 | if (Flags & 0x40) { |
210 | OFlag |= O_APPEND; |
211 | } |
212 | if (Flags & 0x80) { |
213 | OFlag |= O_EXCL; |
214 | } |
215 | |
216 | if (Mode & 0x01) { |
217 | OMode |= S_IREAD; |
218 | } |
219 | if (Mode & 0x02) { |
220 | OMode |= S_IWRITE; |
221 | } |
222 | |
223 | RetVal = open (Path, OFlag, OMode); |
224 | |
225 | SetAX (Regs, RetVal); |
226 | } |
227 | |
228 | |
229 | |
230 | static void PVClose (CPURegs* Regs) |
231 | { |
232 | unsigned RetVal; |
233 | |
234 | unsigned FD = GetAX (Regs); |
235 | |
236 | Print (stderr, 2, "PVClose ($%04X)\n" , FD); |
237 | |
238 | RetVal = close (FD); |
239 | |
240 | SetAX (Regs, RetVal); |
241 | } |
242 | |
243 | |
244 | |
245 | static void PVRead (CPURegs* Regs) |
246 | { |
247 | unsigned char* Data; |
248 | unsigned RetVal, I = 0; |
249 | |
250 | unsigned Count = GetAX (Regs); |
251 | unsigned Buf = PopParam (2); |
252 | unsigned FD = PopParam (2); |
253 | |
254 | Print (stderr, 2, "PVRead ($%04X, $%04X, $%04X)\n" , FD, Buf, Count); |
255 | |
256 | Data = xmalloc (Count); |
257 | |
258 | RetVal = read (FD, Data, Count); |
259 | |
260 | if (RetVal != (unsigned) -1) { |
261 | while (I < RetVal) { |
262 | MemWriteByte (Buf++, Data[I++]); |
263 | } |
264 | } |
265 | xfree (Data); |
266 | |
267 | SetAX (Regs, RetVal); |
268 | } |
269 | |
270 | |
271 | |
272 | static void PVWrite (CPURegs* Regs) |
273 | { |
274 | unsigned char* Data; |
275 | unsigned RetVal, I = 0; |
276 | |
277 | unsigned Count = GetAX (Regs); |
278 | unsigned Buf = PopParam (2); |
279 | unsigned FD = PopParam (2); |
280 | |
281 | Print (stderr, 2, "PVWrite ($%04X, $%04X, $%04X)\n" , FD, Buf, Count); |
282 | |
283 | Data = xmalloc (Count); |
284 | while (I < Count) { |
285 | Data[I++] = MemReadByte (Buf++); |
286 | } |
287 | |
288 | RetVal = write (FD, Data, Count); |
289 | |
290 | xfree (Data); |
291 | |
292 | SetAX (Regs, RetVal); |
293 | } |
294 | |
295 | |
296 | |
297 | static const PVFunc Hooks[] = { |
298 | PVOpen, |
299 | PVClose, |
300 | PVRead, |
301 | PVWrite, |
302 | PVArgs, |
303 | PVExit, |
304 | }; |
305 | |
306 | |
307 | |
308 | void ParaVirtInit (unsigned aArgStart, unsigned char aSPAddr) |
309 | /* Initialize the paravirtualization subsystem */ |
310 | { |
311 | ArgStart = aArgStart; |
312 | SPAddr = aSPAddr; |
313 | }; |
314 | |
315 | |
316 | |
317 | void ParaVirtHooks (CPURegs* Regs) |
318 | /* Potentially execute paravirtualization hooks */ |
319 | { |
320 | /* Check for paravirtualization address range */ |
321 | if (Regs->PC < PARAVIRT_BASE || |
322 | Regs->PC >= PARAVIRT_BASE + sizeof (Hooks) / sizeof (Hooks[0])) { |
323 | return; |
324 | } |
325 | |
326 | /* Call paravirtualization hook */ |
327 | Hooks[Regs->PC - PARAVIRT_BASE] (Regs); |
328 | |
329 | /* Simulate RTS */ |
330 | Regs->PC = Pop(Regs) + (Pop(Regs) << 8) + 1; |
331 | } |
332 | |