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