Turn VBL break point list into a structure to make game stop at various events
[mercenary-reloaded.git] / src / libcpu / execute.c
1 /* CPU emulation loop and memory access
2  *
3  * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
4  * All Rights Reserved
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <endian.h>
24 #include "execute.h"
25 #include "m68k.h"
26 #include "m68kcpu.h"
27
28 //#define DEBUG_CPU
29
30 #define IOMASK  0xfffff000
31 #define IOBASE  0x00dff000
32
33 static uint8_t *memory = NULL;
34 static uint32_t memory_size = 0;
35 static uint16_t *chipreg = NULL;
36
37 static uint16_t read_io(unsigned int address)
38 {
39 #ifdef DEBUG_CPU
40         printf("Chip read from 0x%08x\n", address);
41 #endif
42         return emulate_io_read(address);
43 }
44
45 static void write_io(unsigned int address, uint16_t value)
46 {
47 #ifdef DEBUG_CPU
48         printf("Chip write to 0x%08x\n", address);
49 #endif
50         if ((address & IOMASK) == IOBASE)
51                 chipreg[address & ~IOMASK] = value;
52         emulate_io_write(address, value);
53 }
54
55 unsigned int m68k_read_memory_8(unsigned int address)
56 {
57         if (address > memory_size - 1) {
58                 if ((address & 1)) {
59                         /* read lower (right) byte */
60                         return read_io(address & ~1);
61                 } else {
62                         /* read upper (left) byte */
63                         return read_io(address) >> 8;
64                 }
65         }
66         return *((uint8_t *)(memory + address));
67 }
68
69 unsigned int m68k_read_memory_16(unsigned int address)
70 {
71         if (address > memory_size - 2) {
72                 return read_io(address);
73         }
74         return be16toh(*((uint16_t *)(memory + address)));
75 }
76
77 unsigned int m68k_read_memory_32(unsigned int address)
78 {
79         if (address > memory_size - 4) {
80                 int32_t value;
81                 value = read_io(address) << 16;
82                 value |= read_io(address + 2);
83                 return value;
84         }
85         return be32toh(*((uint32_t *)(memory + address)));
86 }
87
88 void m68k_write_memory_8(unsigned int address, unsigned int value)
89 {
90         if (address > memory_size - 1) {
91                 if ((address & 1)) {
92                         /* write lower (right) byte */
93                         write_io(address & ~1, 0xff00 | value);
94                 } else {
95                         /* write upper (left) byte */
96                         write_io(address, 0x00ff | (value << 8));
97                 }
98                 return;
99         }
100         *((uint8_t *)(memory + address)) = value;
101 }
102
103 void m68k_write_memory_16(unsigned int address, unsigned int value)
104 {
105         if (address > memory_size - 2) {
106                 write_io(address, value);
107                 return;
108         }
109         *((uint16_t *)(memory + address)) = htobe16(value);
110 }
111
112 void m68k_write_memory_32(unsigned int address, unsigned int value)
113 {
114         if (address > memory_size - 4) {
115                 write_io(address, value >> 16);
116                 write_io(address + 2, value);
117                 return;
118         }
119         *((uint32_t *)(memory + address)) = htobe32(value);
120 }
121
122 void execute_init(int32_t _memory_size, uint8_t *_memory, uint16_t *_chipreg)
123 {
124         memory_size = _memory_size;
125         memory = _memory;
126         chipreg = _chipreg;
127
128         /* init CPU */
129         m68k_init();
130         m68k_set_cpu_type(M68K_CPU_TYPE_68000);
131 }
132
133 void reset_cpu(void)
134 {
135         m68k_pulse_reset();
136 }
137
138 int execute_cpu(int irq, const struct cpu_stop stop_at[], int *event)
139 {
140         int cycle_count = 0;
141         int instruction_count = 0;
142         int i;
143         uint32_t save_pc;
144
145         if (irq) {
146                 save_pc = REG_PC;
147                 m68k_set_irq(irq);
148                 /* loop until we returned from interrupt */
149                 do {
150                         /* execute one opcode */
151                         cycle_count += m68k_execute(0);
152                         instruction_count++;
153                 } while (REG_PC != save_pc);
154 #ifdef DEBUG_CPU
155                 printf("interrupt %d took %d opcodes\n", irq, instruction_count);
156 #endif
157         } else {
158                 do {
159                         /* execute one opcode */
160                         cycle_count += m68k_execute(0);
161                         instruction_count++;
162
163                         /* checking if game does not hit any 'stop_at' break point, give error output */
164                         if (instruction_count >= 10000000) {
165                                 if (instruction_count == 10000000)
166                                         fprintf(stderr, "!!! games seems to got stuck in an endless loop, please fix !!!\n");
167                                 fprintf(stderr, "program counter at: %06x\n", REG_PC);
168                                 if (instruction_count == 10000020)
169                                         break;
170                         }
171
172                         for (i = 0; stop_at[i].event; i++) {
173                                 if (REG_PC == stop_at[i].pc) {
174 #ifdef DEBUG_CPU
175                                         printf("execution to address 0x%06x took %d opcodes\n", REG_PC, instruction_count);
176 #endif
177                                         *event = stop_at[i].event;
178                                         return cycle_count;
179                                 }
180                         }
181                 } while (42);
182         }
183
184         return cycle_count;
185 }
186