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