-
Notifications
You must be signed in to change notification settings - Fork 44
Expand file tree
/
Copy pathm68kinterface.c
More file actions
683 lines (577 loc) · 17.5 KB
/
m68kinterface.c
File metadata and controls
683 lines (577 loc) · 17.5 KB
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
//
// m68kinterface.c: Code interface to the UAE 68000 core and support code
//
// by James Hammons
// (C) 2011 Underground Software
//
// JLH = James Hammons <[email protected]>
//
// Who When What
// --- ---------- -------------------------------------------------------------
// JLH 10/28/2011 Created this file ;-)
//
#include "m68kinterface.h"
#include "cpudefs.h"
#include "inlines.h"
#include "cpuextra.h"
#include "readcpu.h"
#include "../core/state.h"
#include "../core/vjag_memory.h"
// Exception Vectors handled by emulation
#define EXCEPTION_BUS_ERROR 2 /* This one is not emulated! */
#define EXCEPTION_ADDRESS_ERROR 3 /* This one is partially emulated (doesn't stack a proper frame yet) */
#define EXCEPTION_ILLEGAL_INSTRUCTION 4
#define EXCEPTION_ZERO_DIVIDE 5
#define EXCEPTION_CHK 6
#define EXCEPTION_TRAPV 7
#define EXCEPTION_PRIVILEGE_VIOLATION 8
#define EXCEPTION_TRACE 9
#define EXCEPTION_1010 10
#define EXCEPTION_1111 11
#define EXCEPTION_FORMAT_ERROR 14
#define EXCEPTION_UNINITIALIZED_INTERRUPT 15
#define EXCEPTION_SPURIOUS_INTERRUPT 24
#define EXCEPTION_INTERRUPT_AUTOVECTOR 24
#define EXCEPTION_TRAP_BASE 32
// These are found in obj/cpustbl.c (generated by gencpu)
//extern const struct cputbl op_smalltbl_0_ff[]; /* 68040 */
//extern const struct cputbl op_smalltbl_1_ff[]; /* 68020 + 68881 */
//extern const struct cputbl op_smalltbl_2_ff[]; /* 68020 */
//extern const struct cputbl op_smalltbl_3_ff[]; /* 68010 */
extern const struct cputbl op_smalltbl_4_ff[]; /* 68000 */
extern const struct cputbl op_smalltbl_5_ff[]; /* 68000 slow but compatible. */
// Externs, supplied by the user...
//extern int irq_ack_handler(int);
// Function prototypes...
static INLINE void m68ki_check_interrupts(void);
void m68ki_exception_interrupt(uint32_t intLevel);
static INLINE uint32_t m68ki_init_exception(void);
static INLINE void m68ki_stack_frame_3word(uint32_t pc, uint32_t sr);
unsigned long IllegalOpcode(uint32_t opcode);
void BuildCPUFunctionTable(void);
void m68k_set_irq2(unsigned int intLevel);
// Local "Global" vars
static int32_t initialCycles;
cpuop_func * cpuFunctionTable[65536];
// By virtue of the fact that m68k_set_irq() can be called asychronously by
// another thread, we need something along the lines of this:
static int checkForIRQToHandle = 0;
static int IRQLevelToHandle = 0;
void M68KDebugHalt(void)
{
regs.spcflags |= SPCFLAG_DEBUGGER;
}
void M68KDebugResume(void)
{
regs.spcflags &= ~SPCFLAG_DEBUGGER;
}
// File-scope so m68k_done() below can reset it after freeing table68k.
static uint32_t emulation_initialized = 0;
// Free process-lifetime allocations made by read_table68k(). Called
// from JaguarDone() so ASAN runs see a clean shutdown.
extern struct instr * table68k;
void m68k_done(void)
{
if (table68k)
{
free(table68k);
table68k = NULL;
}
emulation_initialized = 0;
}
// Pulse the RESET line on the CPU
void m68k_pulse_reset(void)
{
// The first call to this function initializes the opcode handler jump table
if (!emulation_initialized)
{
// Build opcode handler table here...
read_table68k();
do_merges();
BuildCPUFunctionTable();
emulation_initialized = 1;
}
regs.spcflags = 0;
regs.stopped = 0;
regs.remainingCycles = 0;
regs.intmask = 0x07;
regs.s = 1; // Supervisor mode ON
// Read initial SP and PC
m68k_areg(regs, 7) = m68k_read_memory_32(0);
m68k_setpc(m68k_read_memory_32(4));
refill_prefetch(m68k_getpc(), 0);
}
int m68k_execute(int num_cycles)
{
if (regs.stopped)
{
regs.remainingCycles = 0; // int32_t
regs.interruptCycles = 0; // uint32_t
return num_cycles;
}
regs.remainingCycles = num_cycles;
/*int32_t*/ initialCycles = num_cycles;
regs.remainingCycles -= regs.interruptCycles;
regs.interruptCycles = 0;
/* Main loop. Keep going until we run out of clock cycles */
do
{
uint32_t opcode;
int32_t cycles;
// This is so our debugging code can break in on a dime.
// Otherwise, this is just extra slow down :-P
if (regs.spcflags & SPCFLAG_DEBUGGER)
{
// Not sure this is correct... :-P
num_cycles = initialCycles - regs.remainingCycles;
regs.remainingCycles = 0; // int32_t
regs.interruptCycles = 0; // uint32_t
return num_cycles;
}
if (checkForIRQToHandle)
{
checkForIRQToHandle = 0;
m68k_set_irq2(IRQLevelToHandle);
}
#ifdef M68K_HOOK_FUNCTION
M68KInstructionHook();
#endif
opcode = get_iword(0);
cycles = (int32_t)(*cpuFunctionTable[opcode])(opcode);
regs.remainingCycles -= cycles;
}
while (regs.remainingCycles > 0);
regs.remainingCycles -= regs.interruptCycles;
regs.interruptCycles = 0;
// Return # of clock cycles used
return initialCycles - regs.remainingCycles;
}
void m68k_set_irq(unsigned int intLevel)
{
// We need to check for stopped state as well...
if (regs.stopped)
{
m68k_set_irq2(intLevel);
return;
}
// Since this can be called asynchronously, we need to fix it so that it
// doesn't fuck up the main execution loop.
IRQLevelToHandle = intLevel;
checkForIRQToHandle = 1;
}
/* ASG: rewrote so that the int_level is a mask of the IPL0/IPL1/IPL2 bits */
void m68k_set_irq2(unsigned int intLevel)
{
int oldLevel = regs.intLevel;
regs.intLevel = intLevel;
// A transition from < 7 to 7 always interrupts (NMI)
// Note: Level 7 can also level trigger like a normal IRQ
if (oldLevel != 0x07 && regs.intLevel == 0x07)
m68ki_exception_interrupt(7); // Edge triggered level 7 (NMI)
else
m68ki_check_interrupts(); // Level triggered (IRQ)
}
// Check for interrupts
static INLINE void m68ki_check_interrupts(void)
{
if (regs.intLevel > regs.intmask)
m68ki_exception_interrupt(regs.intLevel);
}
// Service an interrupt request and start exception processing
void m68ki_exception_interrupt(uint32_t intLevel)
{
uint32_t vector, sr, newPC;
// Turn off the stopped state (N.B.: normal 68K behavior!)
regs.stopped = 0;
//JLH: need to add halt state?
// prolly, for debugging/alpine mode... :-/
// but then again, this should be handled already by the main execution loop :-P
// If we are halted, don't do anything
// if (regs.stopped)
// return;
// Acknowledge the interrupt (NOTE: This is a user supplied function!)
vector = irq_ack_handler(intLevel);
// Get the interrupt vector
if (vector == M68K_INT_ACK_AUTOVECTOR)
// Use the autovectors. This is the most commonly used implementation
vector = EXCEPTION_INTERRUPT_AUTOVECTOR + intLevel;
else if (vector == M68K_INT_ACK_SPURIOUS)
// Called if no devices respond to the interrupt acknowledge
vector = EXCEPTION_SPURIOUS_INTERRUPT;
else if (vector > 255)
{
// M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: Interrupt acknowledge returned invalid vector $%x\n",
// m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC), vector));
return;
}
// Start exception processing
sr = m68ki_init_exception();
// Set the interrupt mask to the level of the one being serviced
regs.intmask = intLevel;
// Get the new PC
newPC = m68k_read_memory_32(vector << 2);
// If vector is uninitialized, call the uninitialized interrupt vector
if (newPC == 0)
newPC = m68k_read_memory_32(EXCEPTION_UNINITIALIZED_INTERRUPT << 2);
// Generate a stack frame
m68ki_stack_frame_3word(regs.pc, sr);
m68k_setpc(newPC);
// Defer cycle counting until later
regs.interruptCycles += 56; // NOT ACCURATE-- !!! FIX !!!
// CPU_INT_CYCLES += CYC_EXCEPTION[vector];
}
// Initiate exception processing
static INLINE uint32_t m68ki_init_exception(void)
{
uint32_t sr;
MakeSR();
sr = regs.sr; // Save old status register
regs.s = 1; // Set supervisor mode
return sr;
}
// 3 word stack frame (68000 only)
static INLINE void m68ki_stack_frame_3word(uint32_t pc, uint32_t sr)
{
// Push PC on stack:
m68k_areg(regs, 7) -= 4;
m68k_write_memory_32(m68k_areg(regs, 7), pc);
// Push SR on stack:
m68k_areg(regs, 7) -= 2;
m68k_write_memory_16(m68k_areg(regs, 7), sr);
}
unsigned int m68k_get_reg(void * context, m68k_register_t reg)
{
if (reg <= M68K_REG_A7)
return regs.regs[reg];
else if (reg == M68K_REG_PC)
return regs.pc;
else if (reg == M68K_REG_SR)
{
MakeSR();
return regs.sr;
}
else if (reg == M68K_REG_SP)
return regs.regs[15];
return 0;
}
void m68k_set_reg(m68k_register_t reg, unsigned int value)
{
if (reg <= M68K_REG_A7)
regs.regs[reg] = value;
else if (reg == M68K_REG_PC)
regs.pc = value;
else if (reg == M68K_REG_SR)
{
regs.sr = value;
MakeFromSR();
}
else if (reg == M68K_REG_SP)
regs.regs[15] = value;
}
//
// Check if the instruction is a valid one
//
unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type)
{
instruction &= 0xFFFF;
if (cpuFunctionTable[instruction] == IllegalOpcode)
return 0;
return 1;
}
// Dummy functions, for now, until we prove the concept here. :-)
int m68k_cycles_run(void) { return 0; } /* Number of cycles run so far */
int m68k_cycles_remaining(void) { return 0; } /* Number of cycles left */
void m68k_modify_timeslice(int cycles)
{
regs.remainingCycles = cycles;
}
void m68k_end_timeslice(void)
{
initialCycles = regs.remainingCycles;
regs.remainingCycles = 0;
}
/* Read a 32-bit operand from any addressable EA the wrapper code uses.
* The Removers/aln-built Jaguar binaries we care about always reach MULL/DIVL
* with the EA = data-register-direct (mode 0). Other modes (immediate,
* abs.W/L, (An), etc.) are emulated for completeness. */
static int read_long_ea(uint32_t opcode, uint32_t *out)
{
uint32_t mode = (opcode >> 3) & 0x7;
uint32_t reg = opcode & 0x7;
uint32_t ea;
switch (mode)
{
case 0: /* Dn */
*out = m68k_dreg(regs, reg);
return 0;
case 1: /* An */
*out = m68k_areg(regs, reg);
return 0;
case 2: /* (An) */
*out = m68k_read_memory_32(m68k_areg(regs, reg));
return 0;
case 5: /* (d16,An) */
{
int16_t d = (int16_t)get_iword(4);
*out = m68k_read_memory_32(m68k_areg(regs, reg) + d);
return 2;
}
case 7:
switch (reg)
{
case 0: /* (xxx).W */
ea = (int32_t)(int16_t)get_iword(4);
*out = m68k_read_memory_32(ea);
return 2;
case 1: /* (xxx).L */
ea = (uint32_t)get_ilong(4);
*out = m68k_read_memory_32(ea);
return 4;
case 4: /* #imm */
*out = (uint32_t)get_ilong(4);
return 4;
}
break;
}
return -1;
}
/* Emulate the 68020+ MULL / DIVL instructions on a 68000-only core.
* The Removers Library + m68k-atari-mint-gcc toolchain emits these for
* 32x32 multiply and divide; without them, our binaries hard-hang inside
* libgcc helpers. Returns 1 if handled, 0 to fall through to a true illegal
* exception. */
static int handle_68020_mull_divl(uint32_t opcode)
{
uint32_t base = opcode & 0xFFC0;
uint16_t ext;
uint32_t src;
int extra;
if (base != 0x4C00 && base != 0x4C40)
return 0;
ext = (uint16_t)get_iword(2);
if (ext & 0x83F8)
return 0; /* reserved bits set — not a clean MULL/DIVL */
extra = read_long_ea(opcode, &src);
if (extra < 0)
return 0;
{
uint32_t Dl = (ext >> 12) & 0x7;
uint32_t Dh = ext & 0x7;
int sz = (ext >> 10) & 0x1; /* 0=32-bit, 1=64-bit */
int sg = (ext >> 11) & 0x1; /* 0=unsigned, 1=signed */
if (base == 0x4C00) /* MULL */
{
uint32_t a = m68k_dreg(regs, Dl);
uint32_t b = src;
if (sz == 0)
{
uint32_t r;
if (sg)
{
int64_t sr = (int64_t)(int32_t)a * (int64_t)(int32_t)b;
r = (uint32_t)sr;
SET_VFLG(sr != (int64_t)(int32_t)r);
}
else
{
uint64_t ur = (uint64_t)a * (uint64_t)b;
r = (uint32_t)ur;
SET_VFLG((ur >> 32) != 0);
}
m68k_dreg(regs, Dl) = r;
SET_NFLG(r >> 31);
SET_ZFLG(r == 0);
SET_CFLG(0);
}
else
{
uint64_t prod;
if (sg)
prod = (uint64_t)((int64_t)(int32_t)a * (int64_t)(int32_t)b);
else
prod = (uint64_t)a * (uint64_t)b;
m68k_dreg(regs, Dl) = (uint32_t)prod;
m68k_dreg(regs, Dh) = (uint32_t)(prod >> 32);
SET_NFLG((prod >> 63) & 1);
SET_ZFLG(prod == 0);
SET_VFLG(0); SET_CFLG(0);
}
}
else /* DIVL */
{
uint32_t divisor = src;
if (divisor == 0)
{
m68k_incpc(4 + extra);
Exception(0x05, 0, M68000_EXC_SRC_CPU);
return 1;
}
if (sz == 0)
{
uint32_t a = m68k_dreg(regs, Dl);
if (sg)
{
int32_t q = (int32_t)a / (int32_t)divisor;
int32_t r = (int32_t)a % (int32_t)divisor;
m68k_dreg(regs, Dl) = (uint32_t)q;
if (Dh != Dl) m68k_dreg(regs, Dh) = (uint32_t)r;
SET_NFLG(q < 0); SET_ZFLG(q == 0);
}
else
{
uint32_t q = a / divisor;
uint32_t r = a % divisor;
m68k_dreg(regs, Dl) = q;
if (Dh != Dl) m68k_dreg(regs, Dh) = r;
SET_NFLG(q >> 31); SET_ZFLG(q == 0);
}
}
else /* 64-bit dividend in Dh:Dl */
{
uint64_t dividend = ((uint64_t)m68k_dreg(regs, Dh) << 32)
| (uint64_t)m68k_dreg(regs, Dl);
if (sg)
{
int64_t q = (int64_t)dividend / (int32_t)divisor;
int64_t r = (int64_t)dividend % (int32_t)divisor;
int32_t q32 = (int32_t)(uint32_t)q;
m68k_dreg(regs, Dl) = (uint32_t)q;
m68k_dreg(regs, Dh) = (uint32_t)r;
SET_NFLG(q32 < 0); SET_ZFLG(q32 == 0);
}
else
{
uint64_t q = dividend / divisor;
uint64_t r = dividend % divisor;
uint32_t q32 = (uint32_t)q;
m68k_dreg(regs, Dl) = q32;
m68k_dreg(regs, Dh) = (uint32_t)r;
SET_NFLG((q32 >> 31) & 1); SET_ZFLG(q32 == 0);
}
}
SET_VFLG(0); SET_CFLG(0);
}
}
m68k_incpc(4 + extra);
return 1;
}
unsigned long IllegalOpcode(uint32_t opcode)
{
if ((opcode & 0xFF80) == 0x4C00)
{
if (handle_68020_mull_divl(opcode))
return 40;
}
if ((opcode & 0xF000) == 0xF000)
{
Exception(0x0B, 0, M68000_EXC_SRC_CPU); // LineF exception...
return 4;
}
else if ((opcode & 0xF000) == 0xA000)
{
Exception(0x0A, 0, M68000_EXC_SRC_CPU); // LineA exception...
return 4;
}
Exception(0x04, 0, M68000_EXC_SRC_CPU); // Illegal opcode exception...
return 4;
}
void BuildCPUFunctionTable(void)
{
int i;
unsigned long opcode;
// We're only using the "fast" 68000 emulation here, not the "compatible"
// ("fast" doesn't throw exceptions, so we're using "compatible" now :-P)
//let's try "compatible" and see what happens here...
const struct cputbl * tbl = op_smalltbl_5_ff;
// Set all instructions to Illegal...
for(opcode=0; opcode<65536; opcode++)
cpuFunctionTable[opcode] = IllegalOpcode;
// Move functions from compact table into our full function table...
for(i=0; tbl[i].handler!=NULL; i++)
cpuFunctionTable[tbl[i].opcode] = tbl[i].handler;
//JLH: According to readcpu.c, handler is set to -1 and never changes.
// Actually, it does read this crap in readcpu.c, do_merges() does it... :-P
// Again, seems like a build time thing could be done here...
for(opcode=0; opcode<65536; opcode++)
{
if (table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > 0)
continue;
if (table68k[opcode].handler != -1)
{
cpuop_func * f = cpuFunctionTable[table68k[opcode].handler];
if (f == IllegalOpcode)
abort();
cpuFunctionTable[opcode] = f;
}
}
}
/* Save state serialization for 68K CPU */
size_t M68KStateSave(uint8_t *buf)
{
uint8_t *start = buf;
uint32_t pc_p_offset, pc_oldp_offset;
/* Save register struct fields individually (not the raw struct,
* because pc_p/pc_oldp are pointers that differ per session). */
STATE_SAVE_BUF(buf, regs.regs, sizeof(regs.regs));
STATE_SAVE_VAR(buf, regs.usp);
STATE_SAVE_VAR(buf, regs.isp);
STATE_SAVE_VAR(buf, regs.sr);
STATE_SAVE_VAR(buf, regs.s);
STATE_SAVE_VAR(buf, regs.stopped);
STATE_SAVE_VAR(buf, regs.intmask);
STATE_SAVE_VAR(buf, regs.intLevel);
STATE_SAVE_VAR(buf, regs.c);
STATE_SAVE_VAR(buf, regs.z);
STATE_SAVE_VAR(buf, regs.n);
STATE_SAVE_VAR(buf, regs.v);
STATE_SAVE_VAR(buf, regs.x);
STATE_SAVE_VAR(buf, regs.pc);
STATE_SAVE_VAR(buf, regs.spcflags);
STATE_SAVE_VAR(buf, regs.prefetch_pc);
STATE_SAVE_VAR(buf, regs.prefetch);
STATE_SAVE_VAR(buf, regs.remainingCycles);
STATE_SAVE_VAR(buf, regs.interruptCycles);
/* Save pc_p and pc_oldp as offsets from jagMemSpace */
pc_p_offset = (uint32_t)(regs.pc_p ? (regs.pc_p - jagMemSpace) : 0xFFFFFFFF);
pc_oldp_offset = (uint32_t)(regs.pc_oldp ? (regs.pc_oldp - jagMemSpace) : 0xFFFFFFFF);
STATE_SAVE_VAR(buf, pc_p_offset);
STATE_SAVE_VAR(buf, pc_oldp_offset);
/* Local statics */
STATE_SAVE_VAR(buf, initialCycles);
STATE_SAVE_VAR(buf, checkForIRQToHandle);
STATE_SAVE_VAR(buf, IRQLevelToHandle);
return (size_t)(buf - start);
}
size_t M68KStateLoad(const uint8_t *buf)
{
const uint8_t *start = buf;
uint32_t pc_p_offset, pc_oldp_offset;
STATE_LOAD_BUF(buf, regs.regs, sizeof(regs.regs));
STATE_LOAD_VAR(buf, regs.usp);
STATE_LOAD_VAR(buf, regs.isp);
STATE_LOAD_VAR(buf, regs.sr);
STATE_LOAD_VAR(buf, regs.s);
STATE_LOAD_VAR(buf, regs.stopped);
STATE_LOAD_VAR(buf, regs.intmask);
STATE_LOAD_VAR(buf, regs.intLevel);
STATE_LOAD_VAR(buf, regs.c);
STATE_LOAD_VAR(buf, regs.z);
STATE_LOAD_VAR(buf, regs.n);
STATE_LOAD_VAR(buf, regs.v);
STATE_LOAD_VAR(buf, regs.x);
STATE_LOAD_VAR(buf, regs.pc);
STATE_LOAD_VAR(buf, regs.spcflags);
STATE_LOAD_VAR(buf, regs.prefetch_pc);
STATE_LOAD_VAR(buf, regs.prefetch);
STATE_LOAD_VAR(buf, regs.remainingCycles);
STATE_LOAD_VAR(buf, regs.interruptCycles);
/* Reconstruct pc_p and pc_oldp from offsets */
STATE_LOAD_VAR(buf, pc_p_offset);
STATE_LOAD_VAR(buf, pc_oldp_offset);
regs.pc_p = (pc_p_offset != 0xFFFFFFFF) ? (jagMemSpace + pc_p_offset) : NULL;
regs.pc_oldp = (pc_oldp_offset != 0xFFFFFFFF) ? (jagMemSpace + pc_oldp_offset) : NULL;
STATE_LOAD_VAR(buf, initialCycles);
STATE_LOAD_VAR(buf, checkForIRQToHandle);
STATE_LOAD_VAR(buf, IRQLevelToHandle);
return (size_t)(buf - start);
}