OSDN Git Service

[General] Tracking to upstream, rev 2015-01-14.
[csp-qt/common_source_project-fm7.git] / source / src / vm / mame / emu / cpu / h6280 / h6280.c
1 /*****************************************************************************\r
2 \r
3     h6280.c - Portable HuC6280 emulator\r
4 \r
5     Copyright Bryan McPhail, mish@tendril.co.uk\r
6 \r
7     This source code is based (with permission!) on the 6502 emulator by\r
8     Juergen Buchmueller.  It is released as part of the Mame emulator project.\r
9     Let me know if you intend to use this code in any other project.\r
10 \r
11 \r
12     NOTICE:\r
13 \r
14     This code is around 99% complete!  Several things are unimplemented,\r
15     some due to lack of time, some due to lack of documentation, mainly\r
16     due to lack of programs using these features.\r
17 \r
18     csh, csl opcodes are not supported.\r
19 \r
20     I am unsure if flag B is set upon execution of rti.\r
21 \r
22     Cycle counts should be quite accurate.\r
23 \r
24 \r
25     Changelog, version 1.02:\r
26         JMP + indirect X (0x7c) opcode fixed.\r
27         SMB + RMB opcodes fixed in disassembler.\r
28         change_pc function calls removed.\r
29         TSB & TRB now set flags properly.\r
30         BIT opcode altered.\r
31 \r
32     Changelog, version 1.03:\r
33         Swapped IRQ mask for IRQ1 & IRQ2 (thanks Yasuhiro)\r
34 \r
35     Changelog, version 1.04, 28/9/99-22/10/99:\r
36         Adjusted RTI (thanks Karl)\r
37         TST opcodes fixed in disassembler (missing break statements in a case!).\r
38         TST behaviour fixed.\r
39         SMB/RMB/BBS/BBR fixed in disassembler.\r
40 \r
41     Changelog, version 1.05, 8/12/99-16/12/99:\r
42         Added CAB's timer implementation (note: irq ack & timer reload are changed).\r
43         Fixed STA IDX.\r
44         Fixed B flag setting on BRK.\r
45         Assumed CSH & CSL to take 2 cycles each.\r
46 \r
47         Todo:  Performance could be improved by precalculating timer fire position.\r
48 \r
49     Changelog, version 1.06, 4/5/00 - last opcode bug found?\r
50         JMP indirect was doing a EAL++; instead of EAD++; - Obviously causing\r
51         a corrupt read when L = 0xff!  This fixes Bloody Wolf and Trio The Punch!\r
52 \r
53     Changelog, version 1.07, 3/9/00:\r
54         Changed timer to be single shot - fixes Crude Buster music in level 1.\r
55 \r
56     Changelog, version 1.08, 8/11/05: (Charles MacDonald)\r
57 \r
58         Changed timer implementation, no longer single shot and reading the\r
59         timer registers returns the count only. Fixes the following:\r
60         - Mesopotamia: Music tempo & in-game timer\r
61         - Dragon Saber: DDA effects\r
62         - Magical Chase: Music tempo and speed regulation\r
63         - Cadash: Allows the first level to start\r
64         - Turrican: Allows the game to start\r
65 \r
66         Changed PLX and PLY to set NZ flags. Fixes:\r
67         - Afterburner: Graphics unpacking\r
68         - Aoi Blink: Collision detection with background\r
69 \r
70         Fixed the decimal version of ADC/SBC to *not* update the V flag,\r
71         only the binary ones do.\r
72 \r
73         Fixed B flag handling so it is always set outside of an interrupt;\r
74         even after being set by PLP and RTI.\r
75 \r
76         Fixed P state after reset to set I and B, leaving T, D cleared and\r
77         NVZC randomized (cleared in this case).\r
78 \r
79         Fixed interrupt processing order (Timer has highest priority followed\r
80         by IRQ1 and finally IRQ2).\r
81 \r
82     Changelog, version 1.09, 1/07/06: (Rob Bohms)\r
83 \r
84         Added emulation of the T flag, fixes PCE Ankuku Densetsu title screen\r
85 \r
86     Changelog, version 1.10, 5/09/07: (Wilbert Pol)\r
87 \r
88         - Taking of interrupts is delayed to respect a pending instruction already\r
89           in the instruction pipeline; fixes After Burner.\r
90         - Added 1 cycle for decimal mode ADC and SBC instructions.\r
91         - Changed cycle counts for CSH and CSL instructions to 3.\r
92         - Added T flag support to the SBC instruction.\r
93         - Fixed ADC T flag to set the Z flag based on the value read.\r
94         - Added 3 cycle penalty to ADC, AND, EOR, ORA, and SBC instructions\r
95           when the T flag is set.\r
96         - Fixed cycle count and support for 65536 byte blocks for the TAI, TDD,\r
97           TIA, TII, and TIN instructions.\r
98         - Fixed RDWORD macro in the disassembler.\r
99         - Fixed setting of N and V flags in the TST instructions.\r
100         - Removed unneeded debug_mmr code.\r
101         - Fixed TSB and TRB instructions.\r
102         - Added 1 delay when accessing the VDC or VCE areas.\r
103         - Implemented low and high speed cpu modes.\r
104 \r
105     Changelog, version 1.11, 18/09/07: (Wilbert Pol)\r
106 \r
107         - Improvements to the handling of taking of delayed interrupts.\r
108 \r
109 ******************************************************************************/\r
110 \r
111 //#include "emu.h"\r
112 //#include "debugger.h"\r
113 #include "h6280.h"\r
114 \r
115 static void set_irq_line(h6280_Regs* cpustate, int irqline, int state);\r
116 \r
117 /* include the macros */\r
118 #include "h6280ops.h"\r
119 \r
120 /* include the opcode macros, functions and function pointer tables */\r
121 #include "tblh6280.c"\r
122 \r
123 /*****************************************************************************/\r
124 static CPU_INIT( h6280 )\r
125 {\r
126         h6280_Regs *cpustate = (h6280_Regs *)calloc(1, sizeof(h6280_Regs));\r
127 \r
128         return cpustate;\r
129 }\r
130 \r
131 static CPU_RESET( h6280 )\r
132 {\r
133         int i;\r
134 \r
135         /* wipe out the h6280 structure */\r
136         DEVICE *save_program = cpustate->program;\r
137         DEVICE *save_io = cpustate->io;\r
138         memset(cpustate, 0, sizeof(h6280_Regs));\r
139         cpustate->program = save_program;\r
140         cpustate->io = save_io;\r
141 \r
142         /* set I and B flags */\r
143         P = _fI | _fB;\r
144 \r
145     /* stack starts at 0x01ff */\r
146         cpustate->sp.d = 0x1ff;\r
147 \r
148     /* read the reset vector into PC */\r
149         PCL = RDMEM(cpustate, H6280_RESET_VEC);\r
150         PCH = RDMEM(cpustate, (H6280_RESET_VEC+1));\r
151 \r
152         /* CPU starts in low speed mode */\r
153     cpustate->clocks_per_cycle = 4;\r
154 \r
155         /* timer off by default */\r
156         cpustate->timer_status=0;\r
157         cpustate->timer_load = 128 * 1024;\r
158 \r
159     /* clear pending interrupts */\r
160         for (i = 0; i < 3; i++)\r
161                 cpustate->irq_state[i] = CLEAR_LINE;\r
162         cpustate->nmi_state = CLEAR_LINE;\r
163 \r
164         cpustate->irq_pending = 0;\r
165 }\r
166 \r
167 static CPU_EXECUTE( h6280 )\r
168 {\r
169         int in;\r
170 \r
171         if (ICount == -1) {\r
172                 cpustate->ICount = 1;\r
173         } else {\r
174                 cpustate->ICount += ICount;\r
175         }\r
176         int Base_ICount = cpustate->ICount;\r
177 \r
178         if ( cpustate->irq_pending == 2 ) {\r
179                 cpustate->irq_pending--;\r
180         }\r
181 \r
182         /* Execute instructions */\r
183         do\r
184     {\r
185                 cpustate->ppc = cpustate->pc;\r
186 \r
187                 /* Execute 1 instruction */\r
188                 in=RDOP();\r
189                 PCW++;\r
190                 insnh6280[in](cpustate);\r
191 \r
192                 if ( cpustate->irq_pending ) {\r
193                         if ( cpustate->irq_pending == 1 ) {\r
194                                 if ( !(P & _fI) ) {\r
195                                         cpustate->irq_pending--;\r
196                                         CHECK_AND_TAKE_IRQ_LINES;\r
197                                 }\r
198                         } else {\r
199                                 cpustate->irq_pending--;\r
200                         }\r
201                 }\r
202 \r
203                 /* Check internal timer */\r
204                 if(cpustate->timer_status)\r
205                 {\r
206                         if(cpustate->timer_value<=0)\r
207                         {\r
208                                 if ( ! cpustate->irq_pending )\r
209                                         cpustate->irq_pending = 1;\r
210                                 while( cpustate->timer_value <= 0 )\r
211                                         cpustate->timer_value += cpustate->timer_load;\r
212                                 set_irq_line(cpustate, 2,ASSERT_LINE);\r
213                         }\r
214                 }\r
215         } while (cpustate->ICount > 0);\r
216 \r
217         return Base_ICount - cpustate->ICount;\r
218 }\r
219 \r
220 /*****************************************************************************/\r
221 \r
222 static void set_irq_line(h6280_Regs* cpustate, int irqline, int state)\r
223 {\r
224         if (irqline == INPUT_LINE_NMI)\r
225         {\r
226                 if ( state != ASSERT_LINE ) return;\r
227                 cpustate->nmi_state = state;\r
228                 CHECK_IRQ_LINES;\r
229         }\r
230         else if (irqline < 3)\r
231         {\r
232                 /* If the state has not changed, just return */\r
233                 if ( cpustate->irq_state[irqline] == state )\r
234                         return;\r
235 \r
236             cpustate->irq_state[irqline] = state;\r
237 \r
238                 CHECK_IRQ_LINES;\r
239         }\r
240 }\r
241 \r
242 \r
243 \r
244 /*****************************************************************************/\r
245 \r
246 READ8_HANDLER( h6280_irq_status_r )\r
247 {\r
248         int status;\r
249 \r
250         switch (offset&3)\r
251         {\r
252         default: return 0;\r
253         case 3:\r
254                 {\r
255                         status=0;\r
256                         if(cpustate->irq_state[1]!=CLEAR_LINE) status|=1; /* IRQ 2 */\r
257                         if(cpustate->irq_state[0]!=CLEAR_LINE) status|=2; /* IRQ 1 */\r
258                         if(cpustate->irq_state[2]!=CLEAR_LINE) status|=4; /* TIMER */\r
259                         return status;\r
260                 }\r
261         case 2: return cpustate->irq_mask;\r
262         }\r
263 }\r
264 \r
265 WRITE8_HANDLER( h6280_irq_status_w )\r
266 {\r
267         switch (offset&3)\r
268         {\r
269                 case 2: /* Write irq mask */\r
270                         cpustate->irq_mask=data&0x7;\r
271                         CHECK_IRQ_LINES;\r
272                         break;\r
273 \r
274                 case 3: /* Timer irq ack */\r
275                         set_irq_line(cpustate, 2, CLEAR_LINE);\r
276                         break;\r
277         }\r
278 }\r
279 \r
280 READ8_HANDLER( h6280_timer_r )\r
281 {\r
282         /* only returns countdown */\r
283         return ((cpustate->timer_value >> 10)&0x7F);\r
284 }\r
285 \r
286 WRITE8_HANDLER( h6280_timer_w )\r
287 {\r
288         switch (offset & 1) {\r
289                 case 0: /* Counter preload */\r
290                         cpustate->timer_load=cpustate->timer_value=((data&127)+1)*1024;\r
291                         return;\r
292 \r
293                 case 1: /* Counter enable */\r
294                         if(data&1)\r
295                         {       /* stop -> start causes reload */\r
296                                 if(cpustate->timer_status==0) cpustate->timer_value=cpustate->timer_load;\r
297                         }\r
298                         cpustate->timer_status=data&1;\r
299                         return;\r
300         }\r
301 }\r
302 \r
303 /*****************************************************************************/\r
304 \r