Remove circular dependencies between libs, use callback function pointers instead
[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 "execute.h"
24 #include "m68k.h"
25 #include "m68kcpu.h"
26
27 //#define DEBUG_CPU
28
29 #define IOMASK  0xfffff000
30 #define IOBASE  0x00dff000
31
32 static uint8_t *memory = NULL;
33 static uint32_t memory_size = 0;
34 static uint16_t *chipreg = NULL;
35 static uint16_t (*io_read)(uint32_t address) = NULL;
36 static void (*io_write)(uint32_t address, uint16_t value) = NULL;
37
38
39 static uint16_t read_io_16(unsigned int address)
40 {
41 #ifdef DEBUG_CPU
42         printf("Chip read from 0x%08x\n", address);
43 #endif
44         return io_read(address);
45 }
46
47 static void write_io_16(unsigned int address, uint16_t value)
48 {
49 #ifdef DEBUG_CPU
50         printf("Chip write to 0x%08x\n", address);
51 #endif
52         if ((address & IOMASK) == IOBASE)
53                 chipreg[address & ~IOMASK] = value;
54         io_write(address, value);
55 }
56
57 unsigned int m68k_read_memory_8(unsigned int address)
58 {
59         if (address > memory_size - 1) {
60                 if ((address & 1)) {
61                         /* read lower (right) byte */
62                         return read_io_16(address & ~1);
63                 } else {
64                         /* read upper (left) byte */
65                         return read_io_16(address) >> 8;
66                 }
67         }
68         return *((uint8_t *)(memory + address));
69 }
70
71 unsigned int m68k_read_memory_16(unsigned int address)
72 {
73         if (address > memory_size - 2) {
74                 return read_io_16(address);
75         }
76         return  (memory[address] << 8) |
77                  memory[address + 1];
78 }
79
80 unsigned int m68k_read_memory_32(unsigned int address)
81 {
82         if (address > memory_size - 4) {
83                 int32_t value;
84                 value = read_io_16(address) << 16;
85                 value |= read_io_16(address + 2);
86                 return value;
87         }
88         return  (memory[address] << 24)|
89                 (memory[address + 1] << 16) |
90                 (memory[address + 2] << 8) |
91                  memory[address + 3];
92 }
93
94 void m68k_write_memory_8(unsigned int address, unsigned int value)
95 {
96         if (address > memory_size - 1) {
97                 if ((address & 1)) {
98                         /* write lower (right) byte */
99                         write_io_16(address & ~1, 0xff00 | value);
100                 } else {
101                         /* write upper (left) byte */
102                         write_io_16(address, 0x00ff | (value << 8));
103                 }
104                 return;
105         }
106         *((uint8_t *)(memory + address)) = value;
107 }
108
109 void m68k_write_memory_16(unsigned int address, unsigned int value)
110 {
111         if (address > memory_size - 2) {
112                 write_io_16(address, value);
113                 return;
114         }
115         memory[address] = value >> 8;
116         memory[address + 1] = value;
117 }
118
119 void m68k_write_memory_32(unsigned int address, unsigned int value)
120 {
121         if (address > memory_size - 4) {
122                 write_io_16(address, value >> 16);
123                 write_io_16(address + 2, value);
124                 return;
125         }
126         memory[address] = value >> 24;
127         memory[address + 1] = value >> 16;
128         memory[address + 2] = value >> 8;
129         memory[address + 3] = value;
130 }
131
132 void execute_init(int32_t _memory_size, uint8_t *_memory, uint16_t *_chipreg, uint16_t (*_io_read)(uint32_t address), void (*_io_write)(uint32_t address, uint16_t value))
133 {
134         memory_size = _memory_size;
135         memory = _memory;
136         chipreg = _chipreg;
137         io_read = _io_read;
138         io_write = _io_write;
139
140         /* init CPU */
141         m68k_init();
142         m68k_set_cpu_type(M68K_CPU_TYPE_68000);
143 }
144
145 void reset_cpu(void)
146 {
147         m68k_pulse_reset();
148 }
149
150 int execute_cpu(int irq, const struct cpu_stop stop_at[], int *event)
151 {
152         int cycle_count = 0;
153         int instruction_count = 0;
154         int i;
155         uint32_t save_pc;
156
157         if (irq) {
158                 save_pc = REG_PC;
159                 m68k_set_irq(irq);
160                 /* loop until we returned from interrupt */
161                 do {
162                         /* execute one opcode */
163                         cycle_count += m68k_execute(0);
164                         instruction_count++;
165                 } while (REG_PC != save_pc);
166 #ifdef DEBUG_CPU
167                 printf("interrupt %d took %d opcodes\n", irq, instruction_count);
168 #endif
169         } else {
170                 do {
171                         /* execute one opcode */
172                         cycle_count += m68k_execute(0);
173                         instruction_count++;
174
175                         /* checking if game does not hit any 'stop_at' break point, give error output */
176                         if (instruction_count >= 10000000) {
177                                 if (instruction_count == 10000000)
178                                         fprintf(stderr, "!!! games seems to got stuck in an endless loop, please fix !!!\n");
179                                 fprintf(stderr, "program counter at: %06x\n", REG_PC);
180                                 if (instruction_count == 10000020)
181                                         break;
182                         }
183
184                         for (i = 0; stop_at[i].event; i++) {
185                                 if (REG_PC == stop_at[i].pc) {
186 #ifdef DEBUG_CPU
187                                         printf("execution to address 0x%06x took %d opcodes\n", REG_PC, instruction_count);
188 #endif
189                                         *event = stop_at[i].event;
190                                         return cycle_count;
191                                 }
192                         }
193                 } while (42);
194         }
195
196         return cycle_count;
197 }
198