1 /*****************************************************************************
2 *
3 * m6509.c
4 * Portable 6509 emulator V1.0beta1
5 *
6 * Copyright (c) 2000 Peter Trauner, all rights reserved.
7 * documentation by vice emulator team
8 *
9 * - This source code is released as freeware for non-commercial purposes.
10 * - You are free to use and redistribute this code in modified or
11 * unmodified form, provided you list me in the credits.
12 * - If you modify this source code, you must add a notice to each modified
13 * source file that it has been changed. If you're a nice person, you
14 * will clearly mark each change too. :)
15 * - If you wish to use this for commercial purposes, please contact me at
16 * pullmoll@t-online.de
17 * - The author of this copywritten work reserves the right to change the
18 * terms of its usage and license at any time, including retroactively
19 * - This entire notice must remain in the source code.
20 *
21 *****************************************************************************/
22 /*
23 2000 March 10 PeT added SO input line
24
25 The basic difference is the amount of RAM these machines have been supplied with. The B128 and the CBM *10
26 models had 128k RAM, the others 256k. This implies some banking scheme, as the 6502 can only address 64k. And
27 indeed those machines use a 6509, that can address 1 MByte of RAM. It has 2 registers at addresses 0 and 1. The
28 indirect bank register at address 1 determines the bank (0-15) where the opcodes LDA (zp),Y and STA (zp),Y
29 take the data from. The exec bank register at address 0 determines the bank where all other read and write
30 addresses take place.
31
32 vice writes to bank register only with zeropage operand
33 0, 1 are bank register in all banks
34
35 lda (zp),y
36 sta (zp),y
37
38 */
39
40 #include <stdio.h>
41 #include "driver.h"
42 #include "state.h"
43 #include "mamedbg.h"
44 #include "m6509.h"
45 #include "m6502.h"
46
47 #include "ops02.h"
48 #include "ill02.h"
49 #include "ops09.h"
50
51 #define CORE_M6509
52
53 #define M6502_NMI_VEC 0xfffa
54 #define M6502_RST_VEC 0xfffc
55 #define M6502_IRQ_VEC 0xfffe
56 #define M6509_RST_VEC M6502_RST_VEC
57 #define M6509_IRQ_VEC M6502_IRQ_VEC
58 #define M6509_NMI_VEC M6502_NMI_VEC
59
60 #define VERBOSE 0
61
62 #if VERBOSE
63 #define LOG(x) logerror x
64 #else
65 #define LOG(x)
66 #endif
67
68 #ifdef RUNTIME_LOADER
69 struct cpu_interface
70 m6509_interface=
71 CPU0(M6509, m6509, 1, 0,1.00,M6509_INT_NONE, M6509_INT_IRQ, M6509_INT_NMI, 8, 20, 0,20,LE,1, 3 );
72
m6509_runtime_loader_init(void)73 extern void m6509_runtime_loader_init(void)
74 {
75 cpuintf[CPU_M6509]=m6509_interface;
76 }
77 #endif
78
79
80 /* Layout of the registers in the debugger */
81 static UINT8 m6509_reg_layout[] = {
82 M6509_A,M6509_X,M6509_Y,M6509_S,M6509_PC, M6509_P,-1,
83 M6509_PC_BANK, M6509_IND_BANK, M6509_EA, M6509_ZP, -1,
84 M6509_NMI_STATE, M6509_IRQ_STATE, M6509_SO_STATE, 0
85 };
86
87 /* Layout of the debugger windows x,y,w,h */
88 static UINT8 m6509_win_layout[] = {
89 25, 0,55, 3, /* register window (top, right rows) */
90 0, 0,24,22, /* disassembler window (left colums) */
91 25, 4,55, 8, /* memory #1 window (right, upper middle) */
92 25,13,55, 9, /* memory #2 window (right, lower middle) */
93 0,23,80, 1, /* command line window (bottom rows) */
94 };
95
96 typedef struct {
97 UINT8 subtype; /* currently selected cpu sub type */
98 void (**insn)(void); /* pointer to the function pointer table */
99 PAIR ppc; /* previous program counter */
100 /* pc.w.h contains the current page pc_bank.w.h for better speed */
101 PAIR pc; /* program counter */
102 PAIR sp; /* stack pointer (always 100 - 1FF) */
103 PAIR zp; /* zero page address */
104 PAIR ea; /* effective address */
105 UINT8 a; /* Accumulator */
106 UINT8 x; /* X index register */
107 UINT8 y; /* Y index register */
108 PAIR pc_bank; /* 4 bits, addressed over address 0 */
109 PAIR ind_bank; /* 4 bits, addressed over address 1 */
110 UINT8 p; /* Processor status */
111 UINT8 pending_irq; /* nonzero if an IRQ is pending */
112 UINT8 after_cli; /* pending IRQ and last insn cleared I */
113 UINT8 nmi_state;
114 UINT8 irq_state;
115 UINT8 so_state;
116 int (*irq_callback)(int irqline); /* IRQ callback */
117 } m6509_Regs;
118
119 int m6509_ICount = 0;
120
121 static m6509_Regs m6509;
122
123 /***************************************************************
124 * include the opcode macros, functions and tables
125 ***************************************************************/
126
127 #include "t6509.c"
128
READ_HANDLER(m6509_read_00000)129 READ_HANDLER( m6509_read_00000 )
130 {
131 return m6509.pc_bank.b.h2;
132 }
133
READ_HANDLER(m6509_read_00001)134 READ_HANDLER( m6509_read_00001 )
135 {
136 return m6509.ind_bank.b.h2;
137 }
138
WRITE_HANDLER(m6509_write_00000)139 WRITE_HANDLER( m6509_write_00000 )
140 {
141 m6509.pc_bank.b.h2=data&0xf;
142 m6509.pc.w.h=m6509.pc_bank.w.h;
143 change_pc20(PCD);
144 }
145
WRITE_HANDLER(m6509_write_00001)146 WRITE_HANDLER( m6509_write_00001 )
147 {
148 m6509.ind_bank.b.h2=data&0xf;
149 }
150
m6509_reset(void * param)151 void m6509_reset (void *param)
152 {
153 m6509.insn = insn6509;
154
155 m6509.pc_bank.d=m6509.ind_bank.d=0;
156 m6509.pc_bank.b.h2=m6509.ind_bank.b.h2=0xf; /* cbm500 needs this */
157 m6509.pc.w.h=m6509.pc_bank.w.h;
158 /* wipe out the rest of the m6509 structure */
159 /* read the reset vector into PC */
160 PCL = RDMEM(M6509_RST_VEC|PB);
161 PCH = RDMEM((M6509_RST_VEC+1)|PB);
162
163 m6509.sp.d = 0x01ff;
164 m6509.p = F_T|F_B|F_I|F_Z|(P&F_D); /* set T, I and Z flags */
165 m6509.pending_irq = 0; /* nonzero if an IRQ is pending */
166 m6509.after_cli = 0; /* pending IRQ and last insn cleared I */
167 m6509.irq_callback = NULL;
168
169 change_pc20(PCD);
170 }
171
m6509_exit(void)172 void m6509_exit(void)
173 {
174 /* nothing to do yet */
175 }
176
m6509_get_context(void * dst)177 unsigned m6509_get_context (void *dst)
178 {
179 if( dst )
180 *(m6509_Regs*)dst = m6509;
181 return sizeof(m6509_Regs);
182 }
183
m6509_set_context(void * src)184 void m6509_set_context (void *src)
185 {
186 if( src )
187 {
188 m6509 = *(m6509_Regs*)src;
189 change_pc20(PCD);
190 }
191 }
192
m6509_get_reg(int regnum)193 unsigned m6509_get_reg (int regnum)
194 {
195 switch( regnum )
196 {
197 case REG_PC: return PCD;
198 case M6509_PC: return m6509.pc.d;
199 case REG_SP: return S;
200 case M6509_S: return m6509.sp.b.l;
201 case M6509_P: return m6509.p;
202 case M6509_A: return m6509.a;
203 case M6509_X: return m6509.x;
204 case M6509_Y: return m6509.y;
205 case M6509_PC_BANK: return m6509.pc_bank.b.h2;
206 case M6509_IND_BANK: return m6509.ind_bank.b.h2;
207 case M6509_EA: return m6509.ea.d;
208 case M6509_ZP: return m6509.zp.b.l;
209 case M6509_NMI_STATE: return m6509.nmi_state;
210 case M6509_IRQ_STATE: return m6509.irq_state;
211 case M6509_SO_STATE: return m6509.so_state;
212 case REG_PREVIOUSPC: return m6509.ppc.w.l;
213 default:
214 if( regnum <= REG_SP_CONTENTS )
215 {
216 unsigned offset = S + 2 * (REG_SP_CONTENTS - regnum);
217 if( offset < 0x1ff )
218 return RDMEM( offset ) | ( RDMEM( offset + 1 ) << 8 );
219 }
220 }
221 return 0;
222 }
223
m6509_set_reg(int regnum,unsigned val)224 void m6509_set_reg (int regnum, unsigned val)
225 {
226 switch( regnum )
227 {
228 case REG_PC: PCW = val&0xffff; change_pc20(PCD); break;
229 case M6509_PC: m6509.pc.w.l = val; break;
230 case REG_SP: S = val; break;
231 case M6509_S: m6509.sp.b.l = val; break;
232 case M6509_P: m6509.p = val; break;
233 case M6509_A: m6509.a = val; break;
234 case M6509_X: m6509.x = val; break;
235 case M6509_Y: m6509.y = val; break;
236 case M6509_PC_BANK: m6509.pc_bank.b.h2 = val; break;
237 case M6509_IND_BANK: m6509.ind_bank.b.h2 = val; break;
238 case M6509_EA: m6509.ea.d = val; break;
239 case M6509_ZP: m6509.zp.b.l = val; break;
240 case M6509_NMI_STATE: m6509_set_irq_line( IRQ_LINE_NMI, val ); break;
241 case M6509_IRQ_STATE: m6509_set_irq_line( 0, val ); break;
242 case M6509_SO_STATE: m6509_set_irq_line( M6509_SET_OVERFLOW, val ); break;
243 default:
244 if( regnum <= REG_SP_CONTENTS )
245 {
246 unsigned offset = S + 2 * (REG_SP_CONTENTS - regnum);
247 if( offset < 0x1ff )
248 {
249 WRMEM( offset, val & 0xfff );
250 WRMEM( offset + 1, (val >> 8) & 0xff );
251 }
252 }
253 }
254 }
255
m6509_take_irq(void)256 static INLINE void m6509_take_irq(void)
257 {
258 if( !(P & F_I) )
259 {
260 EAD = M6509_IRQ_VEC;
261 EAWH = PBWH;
262 m6509_ICount -= 7;
263 PUSH(PCH);
264 PUSH(PCL);
265 PUSH(P & ~F_B);
266 P |= F_I; /* knock out D and set I flag */
267 PCL = RDMEM(EAD);
268 PCH = RDMEM(EAD+1);
269 LOG(("M6509#%d takes IRQ ($%04x)\n", cpu_getactivecpu(), PCD));
270 /* call back the cpuintrf to let it clear the line */
271 if (m6509.irq_callback) (*m6509.irq_callback)(0);
272 change_pc20(PCD);
273 }
274 m6509.pending_irq = 0;
275 }
276
m6509_execute(int cycles)277 int m6509_execute(int cycles)
278 {
279 m6509_ICount = cycles;
280
281 change_pc20(PCD);
282
283 do
284 {
285 UINT8 op;
286 PPC = PCD;
287
288 CALL_MAME_DEBUG;
289
290 /* if an irq is pending, take it now */
291 if( m6509.pending_irq )
292 m6509_take_irq();
293
294 op = RDOP();
295 (*m6509.insn[op])();
296
297 /* check if the I flag was just reset (interrupts enabled) */
298 if( m6509.after_cli )
299 {
300 LOG(("M6509#%d after_cli was >0", cpu_getactivecpu()));
301 m6509.after_cli = 0;
302 if (m6509.irq_state != CLEAR_LINE)
303 {
304 LOG((": irq line is asserted: set pending IRQ\n"));
305 m6509.pending_irq = 1;
306 }
307 else
308 {
309 LOG((": irq line is clear\n"));
310 }
311 }
312 else
313 if( m6509.pending_irq )
314 m6509_take_irq();
315
316 } while (m6509_ICount > 0);
317
318 return cycles - m6509_ICount;
319 }
320
m6509_set_irq_line(int irqline,int state)321 void m6509_set_irq_line(int irqline, int state)
322 {
323 if (irqline == IRQ_LINE_NMI)
324 {
325 if (m6509.nmi_state == state) return;
326 m6509.nmi_state = state;
327 if( state != CLEAR_LINE )
328 {
329 LOG(( "M6509#%d set_nmi_line(ASSERT)\n", cpu_getactivecpu()));
330 EAD = M6509_NMI_VEC;
331 EAWH = PBWH;
332 m6509_ICount -= 7;
333 PUSH(PCH);
334 PUSH(PCL);
335 PUSH(P & ~F_B);
336 P |= F_I; /* knock out D and set I flag */
337 PCL = RDMEM(EAD);
338 PCH = RDMEM(EAD+1);
339 LOG(("M6509#%d takes NMI ($%04x)\n", cpu_getactivecpu(), PCD));
340 change_pc20(PCD);
341 }
342 }
343 else
344 {
345 if( irqline == M6509_SET_OVERFLOW )
346 {
347 if( m6509.so_state && !state )
348 {
349 LOG(( "M6509#%d set overflow\n", cpu_getactivecpu()));
350 P|=F_V;
351 }
352 m6509.so_state=state;
353 return;
354 }
355 m6509.irq_state = state;
356 if( state != CLEAR_LINE )
357 {
358 LOG(( "M6509#%d set_irq_line(ASSERT)\n", cpu_getactivecpu()));
359 m6509.pending_irq = 1;
360 }
361 }
362 }
363
m6509_set_irq_callback(int (* callback)(int))364 void m6509_set_irq_callback(int (*callback)(int))
365 {
366 m6509.irq_callback = callback;
367 }
368
369 /****************************************************************************
370 * Return a formatted string for a register
371 ****************************************************************************/
m6509_info(void * context,int regnum)372 const char *m6509_info(void *context, int regnum)
373 {
374 static char buffer[16][47+1];
375 static int which = 0;
376 m6509_Regs *r = context;
377
378 which = (which+1) % 16;
379 buffer[which][0] = '\0';
380 if( !context )
381 r = &m6509;
382
383 switch( regnum )
384 {
385 case CPU_INFO_REG+M6509_PC: sprintf(buffer[which], "PC:%04X", r->pc.w.l); break;
386 case CPU_INFO_REG+M6509_S: sprintf(buffer[which], "S:%02X", r->sp.b.l); break;
387 case CPU_INFO_REG+M6509_P: sprintf(buffer[which], "P:%02X", r->p); break;
388 case CPU_INFO_REG+M6509_A: sprintf(buffer[which], "A:%02X", r->a); break;
389 case CPU_INFO_REG+M6509_X: sprintf(buffer[which], "X:%02X", r->x); break;
390 case CPU_INFO_REG+M6509_Y: sprintf(buffer[which], "Y:%02X", r->y); break;
391 case CPU_INFO_REG+M6509_PC_BANK: sprintf(buffer[which], "0:%01X", r->pc_bank.b.h2); break;
392 case CPU_INFO_REG+M6509_IND_BANK: sprintf(buffer[which], "1:%01X", r->ind_bank.b.h2); break;
393 case CPU_INFO_REG+M6509_EA: sprintf(buffer[which], "EA:%05X", r->ea.d); break;
394 case CPU_INFO_REG+M6509_ZP: sprintf(buffer[which], "ZP:%05X", r->zp.d); break;
395 case CPU_INFO_REG+M6509_NMI_STATE: sprintf(buffer[which], "NMI:%X", r->nmi_state); break;
396 case CPU_INFO_REG+M6509_IRQ_STATE: sprintf(buffer[which], "IRQ:%X", r->irq_state); break;
397 case CPU_INFO_REG+M6509_SO_STATE: sprintf(buffer[which], "SO:%X", r->so_state); break;
398 case CPU_INFO_FLAGS:
399 sprintf(buffer[which], "%c%c%c%c%c%c%c%c",
400 r->p & 0x80 ? 'N':'.',
401 r->p & 0x40 ? 'V':'.',
402 r->p & 0x20 ? 'R':'.',
403 r->p & 0x10 ? 'B':'.',
404 r->p & 0x08 ? 'D':'.',
405 r->p & 0x04 ? 'I':'.',
406 r->p & 0x02 ? 'Z':'.',
407 r->p & 0x01 ? 'C':'.');
408 break;
409 case CPU_INFO_NAME: return "M6509";
410 case CPU_INFO_FAMILY: return "MOS Technology 6509";
411 case CPU_INFO_VERSION: return "1.0beta";
412 case CPU_INFO_CREDITS:
413 return "Copyright (c) 1998 Juergen Buchmueller\n"
414 "Copyright (c) 2000 Peter Trauner\n"
415 "all rights reserved.";
416 case CPU_INFO_FILE: return __FILE__;
417 case CPU_INFO_REG_LAYOUT: return (const char*)m6509_reg_layout;
418 case CPU_INFO_WIN_LAYOUT: return (const char*)m6509_win_layout;
419 }
420 return buffer[which];
421 }
422
m6509_dasm(char * buffer,unsigned pc)423 unsigned m6509_dasm(char *buffer, unsigned pc)
424 {
425 #ifdef MAME_DEBUG
426 return Dasm6509( buffer, pc );
427 #else
428 sprintf( buffer, "$%02X", cpu_readop(pc) );
429 return 1;
430 #endif
431 }
432
433
434
m6509_init(void)435 void m6509_init(void){ return; }
436
437