1 /*
2  * ============================================================================
3  *  CP_1600:        CP-1600 Main Core
4  *
5  *  Author:         J. Zbiciak
6  *
7  *
8  * ============================================================================
9  *  CP1600_INIT        --  Initializes the CP-1600 structure to a basic setup
10  *  CP1600_RUN         --  Runs the CP1600 for some number of microcycles.
11  *  CP1600_RD          --  Perform a read from the CP1600   (macro in cp_1600.h)
12  *  CP1600_WR          --  Perform a write from the CP1600  (macro in cp_1600.h)
13  * ============================================================================
14  *
15  *  Notes/To-do:
16  *
17  *   -- The CP1600_RD_xxx, CP1600_WR_xxx functions must not be called
18  *      directly.  Rather, the CP1600_RD, CP1600_WR macros must be used
19  *      to assure proper address decoding and dispatch.
20  *
21  *   -- The CP1600 supports interrupts but doesn't currently have a method for
22  *      receiving them.  Peripherals wishing to interrupt the CP1600 need to
23  *      somehow set cp1600->intrq to '1' to trigger an interrupt.
24  *
25  *   -- The CP1600 supports external branch conditions.  Peripherals need to
26  *      set cp1600->ext to the appropriate state to trigger these.
27  *
28  *   -- SIN, TCI, etc. don't do anything yet.  What do they need to do?
29  *
30  *   -- Functions should be provided for setting up RAM, ROM images, and
31  *      registering peripherals.
32  *
33  * ============================================================================
34  */
35 
36 
37 #include "config.h"
38 #include "periph/periph.h"
39 #include "cp1600.h"
40 #include "op_decode.h"
41 #include "op_exec.h"
42 #include "emu_link.h"
43 #include <limits.h>
44 
45 
46 LOCAL void cp1600_dtor(periph_t *const p);
47 LOCAL void cp1600_rand_regs(cp1600_t *const cp1600);
48 
49 /*
50  * ============================================================================
51  *  CP1600_INIT        -- Initializes a CP1600_T structure
52  *
53  *  This function sets up a basic CP1600 structure.  It allocates a local "flat
54  *  memory" which corresponds to the CP-1600 memory map, and it sets up the
55  *  initial state of the instruction decoder logic.
56  *
57  *  When it's finished, the CP1600 structure is configured to *not* have any
58  *  memory deviced enabled at all.  Calls to "CP1600_ADD_xxx" should be issued
59  *  to configure the memory map as appropriate.
60  * ============================================================================
61  */
62 
cp1600_init(cp1600_t * const cp1600,uint16_t const rst_vec,uint16_t const int_vec,bool const rand_mem)63 int cp1600_init
64 (
65     cp1600_t *const cp1600,
66     uint16_t  const rst_vec,
67     uint16_t  const int_vec,
68     bool      const rand_mem
69 )
70 {
71     /* -------------------------------------------------------------------- */
72     /*  Avoid problems with a garbage cp1600 structure by setting it to     */
73     /*  all-bits-zero.  Note:  This could be a portability problem to       */
74     /*  machines which represent NULL pointers as something other than      */
75     /*  all-bits-zero, but what interesting modern machines do that?        */
76     /* -------------------------------------------------------------------- */
77     memset((void*)cp1600, 0, sizeof(cp1600_t));
78 
79     /* -------------------------------------------------------------------- */
80     /*  Set up our interrupt and reset vectors.                             */
81     /* -------------------------------------------------------------------- */
82     cp1600->r[7]    = rst_vec;
83     cp1600->int_vec = int_vec;
84 
85     /* -------------------------------------------------------------------- */
86     /*  Initially not single-stepping.                                      */
87     /* -------------------------------------------------------------------- */
88     cp1600->step_count = 0;
89     cp1600->steps_remaining = 0;
90 
91     /* -------------------------------------------------------------------- */
92     /*  Nothing hooked to the request queue initially.                      */
93     /* -------------------------------------------------------------------- */
94     req_q_init(&cp1600->req_q);
95 
96     /* -------------------------------------------------------------------- */
97     /*  Set the entire memory map to not-cacheable.                         */
98     /* -------------------------------------------------------------------- */
99     /*  TODO:                                                               */
100     /*  Outside routines will need to configure the memory map to make the  */
101     /*  CP1600 useful.  :-)  It is especially important to set the          */
102     /*  cacheable bits for performance.                                     */
103     /* -------------------------------------------------------------------- */
104     for (unsigned i = 0;
105          i < (1u << (CP1600_MEMSIZE - CP1600_DECODE_PAGE - 5)); i++)
106     {
107         cp1600->cacheable[i] = 0;
108     }
109 
110     /* -------------------------------------------------------------------- */
111     /*  Mark all instructions as needing decode.  The fn_decode function    */
112     /*  will cache the decoded instruction if the "cacheable" bit is set    */
113     /*  for the page containing the instruction.                            */
114     /* -------------------------------------------------------------------- */
115     for (unsigned i = 0; i < (1u << CP1600_MEMSIZE); i++)
116     {
117         cp1600->execute[i] = fn_decode_1st;
118     }
119 
120     /* -------------------------------------------------------------------- */
121     /*  If randomizing, initialize the registers and flags to garbage.      */
122     /* -------------------------------------------------------------------- */
123     cp1600->rand_mem = rand_mem;
124     if (rand_mem)
125         cp1600_rand_regs(cp1600);
126 
127     /* -------------------------------------------------------------------- */
128     /*  Start with a pending reset.                                         */
129     /* -------------------------------------------------------------------- */
130     cp1600->pend_reset = true;
131 
132     /* -------------------------------------------------------------------- */
133     /*  Set up the CP-1600 as a peripheral.                                 */
134     /* -------------------------------------------------------------------- */
135     cp1600->periph.read     = NULL;
136     cp1600->periph.write    = NULL;
137     cp1600->periph.peek     = NULL;
138     cp1600->periph.poke     = NULL;
139     cp1600->periph.reset    = cp1600_reset;
140     cp1600->periph.tick     = cp1600_run;
141     cp1600->periph.min_tick = 1;
142     cp1600->periph.max_tick = 4;
143     cp1600->periph.dtor     = cp1600_dtor;
144 
145     cp1600->snoop.read      = NULL;
146     cp1600->snoop.write     = cp1600_write; /* Bus snoop for cache inval.   */
147     cp1600->snoop.peek      = NULL;
148     cp1600->snoop.poke      = cp1600_write; /* Bus snoop for cache inval.   */
149     cp1600->snoop.addr_base = 0;
150     cp1600->snoop.addr_mask = 0xFFFF;
151     cp1600->snoop.tick      = NULL;
152     cp1600->snoop.min_tick  = 0;
153     cp1600->snoop.max_tick  = ~0U;
154     cp1600->snoop.parent    = (void*)cp1600;
155     cp1600->snoop.dtor      = NULL;
156     return 0;
157 }
158 
159 /*
160  * ============================================================================
161  *  CP1600_RESET       -- Reset the CP1060
162  * ============================================================================
163  */
cp1600_reset(periph_t * const p)164 void cp1600_reset(periph_t *const p)
165 {
166     cp1600_t *const cp1600 = PERIPH_AS(cp1600_t, p);
167     cp1600->pend_reset = true;
168 }
169 
170 /*
171  * ============================================================================
172  *  CP1600_RUN         -- Runs the CP1600 for some number of microcycles
173  *
174  *  This is the main CP1600 loop.  It is responsible for fetching instructions,
175  *  decoding them if necessary (or using predecoded instructions if possible)
176  *  and calling the required execute functions.
177  *
178  *  The cp1600_run function will run as many instructions as are necessary to
179  *  just barely exceed the specified number of microcycles.  eg.  It will
180  *  execute a new instruction if the specified total number of microcycles
181  *  has not yet been exceeded.  The new instruction may exceed the specified
182  *  number of microcycles.  The total number of microcycles exhausted is
183  *  returned as an int.
184  * ============================================================================
185  */
cp1600_run(periph_t * const periph,uint32_t const microcycles)186 uint32_t cp1600_run
187 (
188     periph_t *const periph,
189     uint32_t  const microcycles
190 )
191 {
192     cp1600_t      *const RESTRICT cp1600     = PERIPH_AS(cp1600_t, periph);
193     periph_tick_t *const          instr_tick = cp1600->instr_tick;
194     req_q_t       *const RESTRICT req_q      = &cp1600->req_q;
195     uint64_t const orig_now = cp1600->periph.now;
196     uint64_t       now      = cp1600->periph.now;
197     uint64_t const future   =
198         now + microcycles > req_q->horizon ? req_q->horizon
199                                            : now + microcycles;
200     uint64_t actual_microcycles = 0;
201 //printf("cp1600_run %u\n", microcycles);
202     /* -------------------------------------------------------------------- */
203     /*  Initially, we're not stopped at any sort of breakpoint.             */
204     /* -------------------------------------------------------------------- */
205     cp1600->hit_breakpoint = BK_NONE;
206 
207     /* -------------------------------------------------------------------- */
208     /*  Iterate until we've run out of microcycles.  We can slightly        */
209     /*  exceed our target.                                                  */
210     /* -------------------------------------------------------------------- */
211     while (now < future)
212     {
213         const req_t req       = REQ_Q_FRONT(req_q);
214         const int req_valid   = REQ_Q_SIZE(req_q) != 0 &&
215                                 req.state == REQ_PENDING;
216         const int in_req_span = req_valid && req.start <= now && now < req.end;
217         const int in_busrq_span   = in_req_span && req.type == REQ_BUS;
218         const int in_intrq_span   = in_req_span && req.type == REQ_INT;
219         const int before_req_span = req_valid && now <  req.start;
220         const int after_req_span  = req_valid && now >= req.end;
221         uint32_t pc = cp1600->r[7];
222         uint64_t near_future = future;
223         int cycles = 0;
224         int instrs = 0;
225 
226         /* ---------------------------------------------------------------- */
227         /*  If we have a pending reset, handle it now.                      */
228         /* ---------------------------------------------------------------- */
229         if (cp1600->pend_reset)
230         {
231             /* Registers don't get reset by ~MSYNC, but they can be random. */
232             if (cp1600->rand_mem)
233                 cp1600_rand_regs(cp1600);
234 
235             cp1600->r[7] = 0x1000;
236             cp1600->intr = 0;
237             cp1600->pend_reset = false;
238 
239             /* 5 dead cycles after ~MSYNC deasserts. */
240             /* Ref: Microproc Users Manual, page 27. */
241             /* Do zero cycles on first reset.        */
242             cycles = now ? 5 : 0;
243             now += cycles;
244 
245             if (cp1600->steps_remaining > 0)
246                 cp1600->steps_remaining--;
247 
248             cp1600->tot_instr++;    /* treat reset as a phony instruction */
249 
250             goto do_instr_tick;
251         }
252 
253         assert(!(before_req_span && in_req_span   ));
254         assert(!(in_req_span     && after_req_span));
255         assert(!(before_req_span && after_req_span));
256 
257         /* ---------------------------------------------------------------- */
258         /*  Update our INTRQ/BUSRQ flags, in case debugger is watching.     */
259         /* ---------------------------------------------------------------- */
260         cp1600->req_ack_state = (in_busrq_span ? CP1600_BUSRQ : 0)
261                               | (in_intrq_span ? CP1600_INTRQ : 0);
262 
263         /* ---------------------------------------------------------------- */
264         /*  Determine if we need to respond to a bus request.               */
265         /* ---------------------------------------------------------------- */
266         if (in_busrq_span && CP1600_CAN_BUSAK(cp1600))
267         {
268             cp1600->req_ack_state |= CP1600_BUSAK;
269             cycles = (int)(req.end - now);
270             req_q->ack(req_q, now);
271             now = req.end;
272             REQ_Q_POP(req_q);
273             goto do_instr_tick;
274         }
275 
276         /* ---------------------------------------------------------------- */
277         /*  Determine if we need to respond to an interrupt request.        */
278         /* ---------------------------------------------------------------- */
279         if (in_intrq_span && CP1600_CAN_INTAK(cp1600))
280         {
281             cp1600->req_ack_state |= CP1600_INTAK;
282 
283             /* ------------------------------------------------------------ */
284             /*  The CPU goes 'dead' for 2 cycles before INTAK.              */
285             /* ------------------------------------------------------------ */
286             now += 2;
287             req_q->ack(req_q, now);
288             REQ_Q_POP(req_q);
289 
290             /* ------------------------------------------------------------ */
291             /*  Then the CPU writes out the current PC at the top of stack. */
292             /* ------------------------------------------------------------ */
293             cp1600->periph.now = now;
294             CP1600_WR(cp1600, cp1600->r[6], cp1600->r[7]);
295             cp1600->r[6]++;
296 
297             /* ------------------------------------------------------------ */
298             /*  10 more cycles pass across INTAK, DW, DWS, IAB, until we    */
299             /*  finally get to the first BAR of associated with the ISR.    */
300             /* ------------------------------------------------------------ */
301             now += 10;
302             cp1600->r[7] = pc = cp1600->int_vec;
303 
304             cycles = 12;
305             goto do_instr_tick;
306         }
307 
308         /* ---------------------------------------------------------------- */
309         /*  If we make it to here, either we are not in a request span, or  */
310         /*  we could not acknowledge the request type presented to us.      */
311         /* ---------------------------------------------------------------- */
312         assert(!in_req_span || !CP1600_CAN_INTAK(cp1600)
313                             || !CP1600_CAN_BUSAK(cp1600));
314 
315         /* ---------------------------------------------------------------- */
316         /*  If we've gone past a request span, pop it off and loop.  We     */
317         /*  may have blown through a BUSRQ or INTRQ.  Ooopsie!  :D          */
318         /* ---------------------------------------------------------------- */
319         if (after_req_span)
320         {
321             req_q->drop(req_q, now);
322             REQ_Q_POP(req_q);
323             /* Go back to the top of the loop to look at next req, if any. */
324             continue;
325         }
326 
327         /* ---------------------------------------------------------------- */
328         /*  Handle step requests.                                           */
329         /* ---------------------------------------------------------------- */
330         if (cp1600->steps_remaining == 0)
331             cp1600->steps_remaining = cp1600->step_count;
332 
333         /* ---------------------------------------------------------------- */
334         /*  Attempt to execute as many instructions as possible in a tight  */
335         /*  loop.  We will only execute up until the next request boundary  */
336         /*  or instruction-step boundary.  Also, if an instruction happens  */
337         /*  to be a breakpoint, we'll exit early.                           */
338         /* ---------------------------------------------------------------- */
339         /* If we're before a new request span, run up to the edge of it.    */
340         if (before_req_span && near_future > req.start)
341             near_future = req.start;
342 
343         /* If we're *in* a request span, run for 1 instruction.             */
344         if (in_req_span)
345             near_future = now + 1;
346 
347         while (now < near_future)
348         {
349             /* ------------------------------------------------------------ */
350             /*  Grab our execute function and instruction pointer.          */
351             /* ------------------------------------------------------------ */
352             cp1600_ins_t *const execute = cp1600->execute[pc];
353             instr_t            *instr   = cp1600->instr[pc];
354             cp1600->periph.now          = now;
355             cp1600->oldpc               = pc;
356 
357             /* ------------------------------------------------------------ */
358             /*  The flag cp1600->intr is our interruptibility state. It is  */
359             /*  set equal to our interrupt enable bit, and is cleared by    */
360             /*  non-interruptible instructions, thus making it a "logical-  */
361             /*  AND" of the two conditions.                                 */
362             /* ------------------------------------------------------------ */
363             cp1600->intr = (cp1600->I ? CP1600_INT_ENABLE : 0)
364                          | CP1600_INT_INSTR;
365 
366             /* ------------------------------------------------------------ */
367             /*  Execute the next instruction, and record its cycle count    */
368             /*  and new PC value.  Count-down the DBD state.                */
369             /* ------------------------------------------------------------ */
370             cycles      = execute(instr, cp1600);
371             pc          = cp1600->r[7];
372             cp1600->D >>= 1;
373 
374             /* ------------------------------------------------------------ */
375             /*  Tally up instruction count and microcycle count.            */
376             /* ------------------------------------------------------------ */
377             if (cycles == CYC_MAX)
378             {
379                 /* Re-fetch instr in case execute() changed it on us.       */
380                 instr = cp1600->instr[cp1600->oldpc];
381 
382                 /* Halt and in-the-weeds still advance clock and instr cnt. */
383                 if (instr->opcode.breakpt.cycles)
384                 {
385                     instrs++;
386                     now += instr->opcode.breakpt.cycles;
387                 }
388                 cycles = -1;
389                 break;
390             }
391             now += cycles;
392             instrs++;
393 
394             /* ------------------------------------------------------------ */
395             /*  Handle instruction step counter.                            */
396             /* ------------------------------------------------------------ */
397             if (cp1600->steps_remaining > 0 && !--cp1600->steps_remaining)
398                 break;
399         }
400 
401         /* ---------------------------------------------------------------- */
402         /*  Accumulate instructions.                                        */
403         /* ---------------------------------------------------------------- */
404         cp1600->tot_instr += instrs;
405 
406         /* ---------------------------------------------------------------- */
407         /*  If we have an "instruction tick" function registered,           */
408         /*  go run it.  This is usually the debugger.                       */
409         /* ---------------------------------------------------------------- */
410 do_instr_tick:
411         cp1600->periph.now = now;
412         if (instr_tick &&
413             instr_tick(cp1600->instr_tick_periph, cycles) == CYC_MAX)
414             return CYC_MAX;
415     }
416 
417     /* -------------------------------------------------------------------- */
418     /*  Back out our updates to periph.now and move them to tot_cycles.     */
419     /*  We back them out because periph_tick will add them back in.         */
420     /*  Crappy software architecture is crappy...                           */
421     /* -------------------------------------------------------------------- */
422     actual_microcycles  = now - orig_now;
423     cp1600->tot_cycle  += actual_microcycles;
424     cp1600->periph.now  = orig_now;
425 
426     /* -------------------------------------------------------------------- */
427     /*  Update our max_tick to account for our ever-shifting sim horizon.   */
428     /* -------------------------------------------------------------------- */
429     if (now >= req_q->horizon)
430     {
431         cp1600->periph.min_tick = 1;
432         cp1600->periph.max_tick = 1;
433     } else
434     {
435         cp1600->periph.min_tick = 1;
436         cp1600->periph.max_tick = req_q->horizon - now;
437     }
438 //printf("max_tick=%llu horizon=%llu now=%llu\n", (unsigned long long)cp1600->periph.max_tick, (unsigned long long)req_q->horizon, (unsigned long long)now);
439 
440     /* -------------------------------------------------------------------- */
441     /*  Return how long we ran for.                                         */
442     /* -------------------------------------------------------------------- */
443     return actual_microcycles;
444 }
445 
446 /*
447  * ============================================================================
448  *  CP1600_CACHEABLE     -- Marks a region of instruction space as
449  *                          cacheable in the decoder, so that we can cache
450  *                          the decoded instructions.  It doesn't actually
451  *                          trigger the decode of the instructions though.
452  * ============================================================================
453  */
cp1600_cacheable(cp1600_t * const cp1600,uint32_t const addr_lo,uint32_t const addr_hi,int const need_snoop)454 void cp1600_cacheable
455 (
456     cp1600_t *const cp1600,
457     uint32_t  const addr_lo,
458     uint32_t  const addr_hi,
459     int       const need_snoop
460 )
461 {
462     /* -------------------------------------------------------------------- */
463     /*  First, pull in addresses at either end of range.  If address range  */
464     /*  doesn't span full decode pages at the ends, then don't include the  */
465     /*  pages at the ends of the range.                                     */
466     /* -------------------------------------------------------------------- */
467     const uint32_t page_mask = (1u << CP1600_DECODE_PAGE) - 1;
468     const uint32_t r_addr_lo =  (addr_lo + page_mask)     & ~page_mask;
469     const uint32_t r_addr_hi = ((addr_hi + page_mask + 1) & ~page_mask) - 1;
470 
471     if (r_addr_hi < r_addr_lo)
472         return;
473 
474     /* -------------------------------------------------------------------- */
475     /*  Now, step through all of the addresses and mark the instructions    */
476     /*  cacheable.                                                          */
477     /* -------------------------------------------------------------------- */
478     for (uint32_t addr = r_addr_lo; addr <= r_addr_hi;
479          addr += 1u << CP1600_DECODE_PAGE)
480     {
481         cp1600->cacheable[addr >> (CP1600_DECODE_PAGE + 5)] |=
482                     1u << ((addr >> CP1600_DECODE_PAGE) & 31);
483     }
484 
485     /* -------------------------------------------------------------------- */
486     /*  If this memory range needs snooping (eg. is writable), inform the   */
487     /*  peripheral subsystem that the CP-1610 is interested in seeing       */
488     /*  memory events in these regions, since it now considers these        */
489     /*  locations to be cacheable.                                          */
490     /* -------------------------------------------------------------------- */
491     if (need_snoop)
492         periph_register(cp1600->periph.bus, &cp1600->snoop,
493                         r_addr_lo, r_addr_hi, "CP-1610 Snoop");
494 }
495 
496 /*
497  * ============================================================================
498  *  CP1600_INVALIDATE    -- Invalidates a region of cached instructions.
499  * ============================================================================
500  */
cp1600_invalidate(cp1600_t * const cp1600,uint32_t const addr_lo,uint32_t const addr_hi)501 void cp1600_invalidate
502 (
503     cp1600_t *const cp1600,
504     uint32_t  const addr_lo,
505     uint32_t  const addr_hi
506 )
507 {
508     /* -------------------------------------------------------------------- */
509     /*  Step through all of the addresses and mark the instructions for     */
510     /*  decode.                                                             */
511     /* -------------------------------------------------------------------- */
512     for (uint32_t addr = addr_lo; addr <= addr_hi; addr ++)
513     {
514         if (cp1600->execute[addr] != fn_breakpt)
515             cp1600->execute[addr] = fn_decode;
516         cp1600->disasm[addr] = NULL;
517     }
518 }
519 
520 /*
521  * ============================================================================
522  *  CP1600_WRITE         -- Snoops bus writes and invalidates its cache.
523  * ============================================================================
524  */
cp1600_write(periph_t * const per,periph_t * const req,uint32_t const addr,uint32_t const data)525 void cp1600_write
526 (
527     periph_t *const per,        /*  Peripheral being written to.        */
528     periph_t *const req,        /*  Peripheral requesting the write.    */
529     uint32_t const addr,        /*  Address being written.              */
530     uint32_t const data         /*  Data being written.                 */
531 )
532 {
533     cp1600_t *const cp1600 = PERIPH_PARENT_AS(cp1600_t, per);
534     const uint32_t a0 = 0xFFFF & (addr - 2);
535     const uint32_t a1 = 0xFFFF & (addr - 1);
536     const uint32_t a2 = 0xFFFF & (addr - 0);
537     const uint32_t a3 = 0xFFFF & (addr + 1);
538 
539     UNUSED(req);
540     UNUSED(data);
541 
542     /* -------------------------------------------------------------------- */
543     /*  Step through "addr - 2" to "addr + 1" to invalidate.                */
544     /* -------------------------------------------------------------------- */
545     if (cp1600->execute[a0] != fn_breakpt) cp1600->execute[a0] = fn_decode;
546     if (cp1600->execute[a1] != fn_breakpt) cp1600->execute[a1] = fn_decode;
547     if (cp1600->execute[a2] != fn_breakpt) cp1600->execute[a2] = fn_decode;
548     if (cp1600->execute[a3] != fn_breakpt) cp1600->execute[a3] = fn_decode;
549 
550     cp1600->disasm[a0] = NULL;
551     cp1600->disasm[a1] = NULL;
552     cp1600->disasm[a2] = NULL;
553     cp1600->disasm[a3] = NULL;
554 }
555 
556 /*
557  * ============================================================================
558  *  CP1600_INSTR_TICK    -- Sets/unsets an per-instruction ticker
559  *
560  *  Note:  I may eventually split cp1600_run into two flavors that vary
561  *  depending on whether or not I have an instr_tick function registered.
562  * ============================================================================
563  */
cp1600_instr_tick(cp1600_t * const cp1600,periph_tick_t * const instr_tick,periph_t * const instr_tick_periph)564 void cp1600_instr_tick
565 (
566     cp1600_t      *const cp1600,
567     periph_tick_t *const instr_tick,
568     periph_t      *const instr_tick_periph
569 )
570 {
571     cp1600->instr_tick        = instr_tick;
572     cp1600->instr_tick_periph = instr_tick_periph;
573 }
574 
575 /*
576  * ============================================================================
577  *  CP1600_SET_BREAKPT   -- Sets a breakpoint at a given address.
578  *  CP1600_SET_TRACEPT   -- Like a breakpoint, except it resets itself
579  *
580  *  Note:  Instructions which overlap a breakpoint address but don't start
581  *  at the breakpoint address won't trigger the breakpoint.
582  * ============================================================================
583  */
cp1600_set_breakpt(cp1600_t * const cp1600,uint16_t const addr,uint16_t const flags)584 int cp1600_set_breakpt
585 (
586     cp1600_t *const cp1600,
587     uint16_t  const addr,
588     uint16_t  const flags
589 )
590 {
591     const int was_bkpt = (cp1600->execute[addr] == fn_decode_bkpt ||
592                           cp1600->execute[addr] == fn_breakpt);
593 
594     if (!cp1600->instr[addr])
595         cp1600->instr[addr] = get_instr();
596 
597     cp1600->instr[addr]->opcode.breakpt.flags |= flags;
598 
599     cp1600->execute[addr] = addr == cp1600->r[7] ? fn_decode_bkpt : fn_breakpt;
600 
601     return was_bkpt;
602 }
603 
604 /*
605  * ============================================================================
606  *  CP1600_CLR_BREAKPT   -- Clears a breakpoint at a given address.
607  * ============================================================================
608  */
cp1600_clr_breakpt(cp1600_t * const cp1600,uint16_t const addr,uint16_t const flags)609 void cp1600_clr_breakpt
610 (
611     cp1600_t *const cp1600,
612     uint16_t  const addr,
613     uint16_t  const flags
614 )
615 {
616     if (cp1600->execute[addr] != fn_breakpt &&
617         cp1600->execute[addr] != fn_decode_bkpt)
618         return;
619 
620     if (!cp1600->instr[addr])
621         return;
622 
623     cp1600->instr[addr]->opcode.breakpt.flags &= ~flags;
624 
625     if (!cp1600->instr[addr]->opcode.breakpt.flags)
626         cp1600->execute[addr] = fn_decode;
627 }
628 
629 /*
630  * ============================================================================
631  *  CP1600_LIST_BREAKPTS -- Lists all matching breakpoints via callback.
632  * ============================================================================
633  */
cp1600_list_breakpts(const cp1600_t * const cp1600,uint16_t const flags,cp1600_addr_callback_fn * const callback,void * const opaque)634 void cp1600_list_breakpts
635 (
636     const cp1600_t          *const cp1600,
637     uint16_t                 const flags,
638     cp1600_addr_callback_fn *const callback,
639     void                    *const opaque
640 )
641 {
642     uint32_t addr;
643 
644     for (addr = 0; addr <= 0xFFFF; addr++)
645     {
646         if (!cp1600->instr[addr])
647             continue;
648 
649         const uint32_t instr_flags = cp1600->instr[addr]->opcode.breakpt.flags;
650         if ((instr_flags & flags) == flags)
651         {
652             callback(opaque, addr);
653         }
654     }
655 }
656 
657 /*
658  * ============================================================================
659  *  CP1600_DTOR          -- Destructor for a cp1600_t
660  * ============================================================================
661  */
cp1600_dtor(periph_t * const p)662 LOCAL void cp1600_dtor(periph_t *const p)
663 {
664     cp1600_t *const cp1600 = PERIPH_AS(cp1600_t, p);
665 
666     for (uint32_t addr = 0; addr <= 0xFFFF; addr++)
667     {
668         if (cp1600->instr[addr])
669         {
670             put_instr(cp1600->instr[addr]);
671             cp1600->instr[addr] = NULL;
672         }
673     }
674 
675     emu_link_dtor();
676 }
677 
678 /*
679  * ============================================================================
680  *  CP1600_RAND_REGS     -- Randomize the register file.
681  * ============================================================================
682  */
cp1600_rand_regs(cp1600_t * const cp1600)683 LOCAL void cp1600_rand_regs(cp1600_t *const cp1600)
684 {
685     for (int i = 0; i < 7; i++)
686         cp1600->r[i] = rand_jz();
687 
688     uint32_t flags = rand_jz();
689     cp1600->S = !!(flags &  1);
690     cp1600->C = !!(flags &  2);
691     cp1600->O = !!(flags &  4);
692     cp1600->Z = !!(flags &  8);
693     cp1600->I = !!(flags & 16);
694     cp1600->D = !!(flags & 32);
695 }
696 
697 /* ======================================================================== */
698 /*  This program is free software; you can redistribute it and/or modify    */
699 /*  it under the terms of the GNU General Public License as published by    */
700 /*  the Free Software Foundation; either version 2 of the License, or       */
701 /*  (at your option) any later version.                                     */
702 /*                                                                          */
703 /*  This program is distributed in the hope that it will be useful,         */
704 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
705 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       */
706 /*  General Public License for more details.                                */
707 /*                                                                          */
708 /*  You should have received a copy of the GNU General Public License along */
709 /*  with this program; if not, write to the Free Software Foundation, Inc., */
710 /*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.             */
711 /* ======================================================================== */
712 /*                 Copyright (c) 1998-2018, Joseph Zbiciak                  */
713 /* ======================================================================== */
714