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 #ifdef RUNTIME_LOADER
61 struct cpu_interface
62 m6509_interface=
63 CPU0(M6509, m6509, 1, 0,1.00,M6509_INT_NONE, M6509_INT_IRQ, M6509_INT_NMI, 8, 20, 0,20,LE,1, 3 );
64
m6509_runtime_loader_init(void)65 extern void m6509_runtime_loader_init(void)
66 {
67 cpuintf[CPU_M6509]=m6509_interface;
68 }
69 #endif
70
71
72 /* Layout of the registers in the debugger */
73 static UINT8 m6509_reg_layout[] = {
74 M6509_A,M6509_X,M6509_Y,M6509_S,M6509_PC, M6509_P,-1,
75 M6509_PC_BANK, M6509_IND_BANK, M6509_EA, M6509_ZP, -1,
76 M6509_NMI_STATE, M6509_IRQ_STATE, M6509_SO_STATE, 0
77 };
78
79 /* Layout of the debugger windows x,y,w,h */
80 static UINT8 m6509_win_layout[] = {
81 25, 0,55, 3, /* register window (top, right rows) */
82 0, 0,24,22, /* disassembler window (left colums) */
83 25, 4,55, 8, /* memory #1 window (right, upper middle) */
84 25,13,55, 9, /* memory #2 window (right, lower middle) */
85 0,23,80, 1, /* command line window (bottom rows) */
86 };
87
88 typedef struct {
89 UINT8 subtype; /* currently selected cpu sub type */
90 void (**insn)(void); /* pointer to the function pointer table */
91 PAIR ppc; /* previous program counter */
92 /* pc.w.h contains the current page pc_bank.w.h for better speed */
93 PAIR pc; /* program counter */
94 PAIR sp; /* stack pointer (always 100 - 1FF) */
95 PAIR zp; /* zero page address */
96 PAIR ea; /* effective address */
97 UINT8 a; /* Accumulator */
98 UINT8 x; /* X index register */
99 UINT8 y; /* Y index register */
100 PAIR pc_bank; /* 4 bits, addressed over address 0 */
101 PAIR ind_bank; /* 4 bits, addressed over address 1 */
102 UINT8 p; /* Processor status */
103 UINT8 pending_irq; /* nonzero if an IRQ is pending */
104 UINT8 after_cli; /* pending IRQ and last insn cleared I */
105 UINT8 nmi_state;
106 UINT8 irq_state;
107 UINT8 so_state;
108 int (*irq_callback)(int irqline); /* IRQ callback */
109 } m6509_Regs;
110
111 int m6509_ICount = 0;
112
113 static m6509_Regs m6509;
114
115 /***************************************************************
116 * include the opcode macros, functions and tables
117 ***************************************************************/
118
119 #include "t6509.c"
120
READ_HANDLER(m6509_read_00000)121 READ_HANDLER( m6509_read_00000 )
122 {
123 return m6509.pc_bank.b.h2;
124 }
125
READ_HANDLER(m6509_read_00001)126 READ_HANDLER( m6509_read_00001 )
127 {
128 return m6509.ind_bank.b.h2;
129 }
130
WRITE_HANDLER(m6509_write_00000)131 WRITE_HANDLER( m6509_write_00000 )
132 {
133 m6509.pc_bank.b.h2=data&0xf;
134 m6509.pc.w.h=m6509.pc_bank.w.h;
135 change_pc20(PCD);
136 }
137
WRITE_HANDLER(m6509_write_00001)138 WRITE_HANDLER( m6509_write_00001 )
139 {
140 m6509.ind_bank.b.h2=data&0xf;
141 }
142
m6509_reset(void * param)143 void m6509_reset (void *param)
144 {
145 m6509.insn = insn6509;
146
147 m6509.pc_bank.d=m6509.ind_bank.d=0;
148 m6509.pc_bank.b.h2=m6509.ind_bank.b.h2=0xf; /* cbm500 needs this */
149 m6509.pc.w.h=m6509.pc_bank.w.h;
150 /* wipe out the rest of the m6509 structure */
151 /* read the reset vector into PC */
152 PCL = RDMEM(M6509_RST_VEC|PB);
153 PCH = RDMEM((M6509_RST_VEC+1)|PB);
154
155 m6509.sp.d = 0x01ff;
156 m6509.p = F_T|F_B|F_I|F_Z|(P&F_D); /* set T, I and Z flags */
157 m6509.pending_irq = 0; /* nonzero if an IRQ is pending */
158 m6509.after_cli = 0; /* pending IRQ and last insn cleared I */
159 m6509.irq_callback = NULL;
160
161 change_pc20(PCD);
162 }
163
m6509_exit(void)164 void m6509_exit(void)
165 {
166 /* nothing to do yet */
167 }
168
m6509_get_context(void * dst)169 unsigned m6509_get_context (void *dst)
170 {
171 if( dst )
172 *(m6509_Regs*)dst = m6509;
173 return sizeof(m6509_Regs);
174 }
175
m6509_set_context(void * src)176 void m6509_set_context (void *src)
177 {
178 if( src )
179 {
180 m6509 = *(m6509_Regs*)src;
181 change_pc20(PCD);
182 }
183 }
184
m6509_get_reg(int regnum)185 unsigned m6509_get_reg (int regnum)
186 {
187 switch( regnum )
188 {
189 case REG_PC: return PCD;
190 case M6509_PC: return m6509.pc.d;
191 case REG_SP: return S;
192 case M6509_S: return m6509.sp.b.l;
193 case M6509_P: return m6509.p;
194 case M6509_A: return m6509.a;
195 case M6509_X: return m6509.x;
196 case M6509_Y: return m6509.y;
197 case M6509_PC_BANK: return m6509.pc_bank.b.h2;
198 case M6509_IND_BANK: return m6509.ind_bank.b.h2;
199 case M6509_EA: return m6509.ea.d;
200 case M6509_ZP: return m6509.zp.b.l;
201 case M6509_NMI_STATE: return m6509.nmi_state;
202 case M6509_IRQ_STATE: return m6509.irq_state;
203 case M6509_SO_STATE: return m6509.so_state;
204 case REG_PREVIOUSPC: return m6509.ppc.w.l;
205 default:
206 if( regnum <= REG_SP_CONTENTS )
207 {
208 unsigned offset = S + 2 * (REG_SP_CONTENTS - regnum);
209 if( offset < 0x1ff )
210 return RDMEM( offset ) | ( RDMEM( offset + 1 ) << 8 );
211 }
212 }
213 return 0;
214 }
215
m6509_set_reg(int regnum,unsigned val)216 void m6509_set_reg (int regnum, unsigned val)
217 {
218 switch( regnum )
219 {
220 case REG_PC: PCW = val&0xffff; change_pc20(PCD); break;
221 case M6509_PC: m6509.pc.w.l = val; break;
222 case REG_SP: S = val; break;
223 case M6509_S: m6509.sp.b.l = val; break;
224 case M6509_P: m6509.p = val; break;
225 case M6509_A: m6509.a = val; break;
226 case M6509_X: m6509.x = val; break;
227 case M6509_Y: m6509.y = val; break;
228 case M6509_PC_BANK: m6509.pc_bank.b.h2 = val; break;
229 case M6509_IND_BANK: m6509.ind_bank.b.h2 = val; break;
230 case M6509_EA: m6509.ea.d = val; break;
231 case M6509_ZP: m6509.zp.b.l = val; break;
232 case M6509_NMI_STATE: m6509_set_irq_line( IRQ_LINE_NMI, val ); break;
233 case M6509_IRQ_STATE: m6509_set_irq_line( 0, val ); break;
234 case M6509_SO_STATE: m6509_set_irq_line( M6509_SET_OVERFLOW, val ); break;
235 default:
236 if( regnum <= REG_SP_CONTENTS )
237 {
238 unsigned offset = S + 2 * (REG_SP_CONTENTS - regnum);
239 if( offset < 0x1ff )
240 {
241 WRMEM( offset, val & 0xfff );
242 WRMEM( offset + 1, (val >> 8) & 0xff );
243 }
244 }
245 }
246 }
247
m6509_take_irq(void)248 static INLINE void m6509_take_irq(void)
249 {
250 if( !(P & F_I) )
251 {
252 EAD = M6509_IRQ_VEC;
253 EAWH = PBWH;
254 m6509_ICount -= 7;
255 PUSH(PCH);
256 PUSH(PCL);
257 PUSH(P & ~F_B);
258 P |= F_I; /* knock out D and set I flag */
259 PCL = RDMEM(EAD);
260 PCH = RDMEM(EAD+1);
261 log_cb(RETRO_LOG_DEBUG, LOGPRE "M6509#%d takes IRQ ($%04x)\n", cpu_getactivecpu(), PCD);
262 /* call back the cpuintrf to let it clear the line */
263 if (m6509.irq_callback) (*m6509.irq_callback)(0);
264 change_pc20(PCD);
265 }
266 m6509.pending_irq = 0;
267 }
268
m6509_execute(int cycles)269 int m6509_execute(int cycles)
270 {
271 m6509_ICount = cycles;
272
273 change_pc20(PCD);
274
275 do
276 {
277 UINT8 op;
278 PPC = PCD;
279
280 CALL_MAME_DEBUG;
281
282 /* if an irq is pending, take it now */
283 if( m6509.pending_irq )
284 m6509_take_irq();
285
286 op = RDOP();
287 (*m6509.insn[op])();
288
289 /* check if the I flag was just reset (interrupts enabled) */
290 if( m6509.after_cli )
291 {
292 log_cb(RETRO_LOG_DEBUG, LOGPRE "M6509#%d after_cli was >0", cpu_getactivecpu());
293 m6509.after_cli = 0;
294 if (m6509.irq_state != CLEAR_LINE)
295 {
296 log_cb(RETRO_LOG_DEBUG, LOGPRE ": irq line is asserted: set pending IRQ\n");
297 m6509.pending_irq = 1;
298 }
299 else
300 {
301 log_cb(RETRO_LOG_DEBUG, LOGPRE ": irq line is clear\n");
302 }
303 }
304 else
305 if( m6509.pending_irq )
306 m6509_take_irq();
307
308 } while (m6509_ICount > 0);
309
310 return cycles - m6509_ICount;
311 }
312
m6509_set_irq_line(int irqline,int state)313 void m6509_set_irq_line(int irqline, int state)
314 {
315 if (irqline == IRQ_LINE_NMI)
316 {
317 if (m6509.nmi_state == state) return;
318 m6509.nmi_state = state;
319 if( state != CLEAR_LINE )
320 {
321 log_cb(RETRO_LOG_DEBUG, LOGPRE "M6509#%d set_nmi_line(ASSERT)\n", cpu_getactivecpu());
322 EAD = M6509_NMI_VEC;
323 EAWH = PBWH;
324 m6509_ICount -= 7;
325 PUSH(PCH);
326 PUSH(PCL);
327 PUSH(P & ~F_B);
328 P |= F_I; /* knock out D and set I flag */
329 PCL = RDMEM(EAD);
330 PCH = RDMEM(EAD+1);
331 log_cb(RETRO_LOG_DEBUG, LOGPRE "M6509#%d takes NMI ($%04x)\n", cpu_getactivecpu(), PCD);
332 change_pc20(PCD);
333 }
334 }
335 else
336 {
337 if( irqline == M6509_SET_OVERFLOW )
338 {
339 if( m6509.so_state && !state )
340 {
341 log_cb(RETRO_LOG_DEBUG, LOGPRE "M6509#%d set overflow\n", cpu_getactivecpu());
342 P|=F_V;
343 }
344 m6509.so_state=state;
345 return;
346 }
347 m6509.irq_state = state;
348 if( state != CLEAR_LINE )
349 {
350 log_cb(RETRO_LOG_DEBUG, LOGPRE "M6509#%d set_irq_line(ASSERT)\n", cpu_getactivecpu());
351 m6509.pending_irq = 1;
352 }
353 }
354 }
355
m6509_set_irq_callback(int (* callback)(int))356 void m6509_set_irq_callback(int (*callback)(int))
357 {
358 m6509.irq_callback = callback;
359 }
360
361 /****************************************************************************
362 * Return a formatted string for a register
363 ****************************************************************************/
m6509_info(void * context,int regnum)364 const char *m6509_info(void *context, int regnum)
365 {
366 static char buffer[16][47+1];
367 static int which = 0;
368 m6509_Regs *r = context;
369
370 which = (which+1) % 16;
371 buffer[which][0] = '\0';
372 if( !context )
373 r = &m6509;
374
375 switch( regnum )
376 {
377 case CPU_INFO_REG+M6509_PC: sprintf(buffer[which], "PC:%04X", r->pc.w.l); break;
378 case CPU_INFO_REG+M6509_S: sprintf(buffer[which], "S:%02X", r->sp.b.l); break;
379 case CPU_INFO_REG+M6509_P: sprintf(buffer[which], "P:%02X", r->p); break;
380 case CPU_INFO_REG+M6509_A: sprintf(buffer[which], "A:%02X", r->a); break;
381 case CPU_INFO_REG+M6509_X: sprintf(buffer[which], "X:%02X", r->x); break;
382 case CPU_INFO_REG+M6509_Y: sprintf(buffer[which], "Y:%02X", r->y); break;
383 case CPU_INFO_REG+M6509_PC_BANK: sprintf(buffer[which], "0:%01X", r->pc_bank.b.h2); break;
384 case CPU_INFO_REG+M6509_IND_BANK: sprintf(buffer[which], "1:%01X", r->ind_bank.b.h2); break;
385 case CPU_INFO_REG+M6509_EA: sprintf(buffer[which], "EA:%05X", r->ea.d); break;
386 case CPU_INFO_REG+M6509_ZP: sprintf(buffer[which], "ZP:%05X", r->zp.d); break;
387 case CPU_INFO_REG+M6509_NMI_STATE: sprintf(buffer[which], "NMI:%X", r->nmi_state); break;
388 case CPU_INFO_REG+M6509_IRQ_STATE: sprintf(buffer[which], "IRQ:%X", r->irq_state); break;
389 case CPU_INFO_REG+M6509_SO_STATE: sprintf(buffer[which], "SO:%X", r->so_state); break;
390 case CPU_INFO_FLAGS:
391 sprintf(buffer[which], "%c%c%c%c%c%c%c%c",
392 r->p & 0x80 ? 'N':'.',
393 r->p & 0x40 ? 'V':'.',
394 r->p & 0x20 ? 'R':'.',
395 r->p & 0x10 ? 'B':'.',
396 r->p & 0x08 ? 'D':'.',
397 r->p & 0x04 ? 'I':'.',
398 r->p & 0x02 ? 'Z':'.',
399 r->p & 0x01 ? 'C':'.');
400 break;
401 case CPU_INFO_NAME: return "M6509";
402 case CPU_INFO_FAMILY: return "MOS Technology 6509";
403 case CPU_INFO_VERSION: return "1.0beta";
404 case CPU_INFO_CREDITS:
405 return "Copyright (c) 1998 Juergen Buchmueller\n"
406 "Copyright (c) 2000 Peter Trauner\n"
407 "all rights reserved.";
408 case CPU_INFO_FILE: return __FILE__;
409 case CPU_INFO_REG_LAYOUT: return (const char*)m6509_reg_layout;
410 case CPU_INFO_WIN_LAYOUT: return (const char*)m6509_win_layout;
411 }
412 return buffer[which];
413 }
414
m6509_dasm(char * buffer,unsigned pc)415 unsigned m6509_dasm(char *buffer, unsigned pc)
416 {
417 #ifdef MAME_DEBUG
418 return Dasm6509( buffer, pc );
419 #else
420 sprintf( buffer, "$%02X", cpu_readop(pc) );
421 return 1;
422 #endif
423 }
424
425
426
m6509_init(void)427 void m6509_init(void){ return; }
428