;
;	clip65ca.s
;
;	P/ECE MOS6502 Emulator (高速版)
;
;	CLiP - Common Library for P/ECE
;	Copyright (C) 2001-2005 Naoyuki Sawa
;
;	* Fri Jan 28 03:55:00 JST 2005 Naoyuki Sawa
;	- 作成開始。
;
;#include "clip65ca.h"
;
;	clip65ca.h
;
;	P/ECE MOS6502 Emulator (高速版)
;
;	CLiP - Common Library for P/ECE
;	Copyright (C) 2001-2005 Naoyuki Sawa
;
;	* Fri Jan 28 03:55:00 JST 2005 Naoyuki Sawa
;	- 作成開始。
;	* Sat Apr 09 16:38:00 JST 2005 Naoyuki Sawa
;	- レジスタ割り当てを変更しました。
;

; clip65ca.s、fram65ca.sにインクルードして使います。

;****************************************************************************
;	M6502CPU構造体
;****************************************************************************

				; typedef struct _M6502CPU {
;#define CA		 0	; unsigned short ca;	/* + 0,2 -------C AAAAAAAA */
;#define _NZ_		 2	; unsigned short nz;	/* + 2,2 N------- NZZZZZZZ */
;#define BDI		 4	; unsigned char bdi;	/* + 4,1          001BDI00 */
;#define V		 5	; unsigned char v;	/* + 5,1          V------- */
;#define _X_		 6	; unsigned char x;	/* + 6,1          XXXXXXXX */
;#define _Y_		 7	; unsigned char y;	/* + 7,1          YYYYYYYY */
;#define _PC_		 8	; unsigned char* pc;	/* + 8,4 &mem[PC]          */
;#define _SP_		12	; unsigned char sp;	/* +12,1          SPSPSPSP */
				; //			/* +13,3 (パディング)      */
;#define FLAGS		16	; int flags;		/* +16,4          ------IW */
;#define _CYCLE_		20	; int cycle;		/* +20,4 残りCycle         */
;#define READ		24	; void* read;		/* +24,4 メモリ読み出しルーチン */
;#define WRITE		28	; void* write;		/* +28,4 メモリ書き込みルーチン */
;#define SIZEOF_M6502CPU	32	; } M6502CPU;		/* =32   */

;****************************************************************************
;	レジスタ割り当て
;****************************************************************************

;#define addr	r0	; 現在の命令のアドレスモードを解決したアドレスを保持します。
			; AddressModeルーチンが初期化し、Actionルーチンが参照します。
			; Operationルーチン、その他のサブルーチンからは使用不可です。
;#define data	r1	; 現在の命令へ渡すデータ、命令からの戻りデータを保持します。
			; メモリ読み込みを行う命令の場合、Actionルーチンが初期化し、Operationルーチンが使用します。
			; メモリ書き込みを行う命令の場合、Operationルーチンが初期化し、Actionルーチンが使用します。
			; その他の場合、汎用ワークレジスタとして使用可能です。
;#define ptbl	r2	; 現在の命令に対応するm6502tblエントリ内の走査ポインタです。
			; m6502cpu_runが初期化し、m6502cpu_runとActionルーチンが使用します。
			; AddressModeルーチン、Operationルーチン、その他のサブルーチンからは使用不可です。
;#define TBL	r3	; m6502tblの先頭アドレスを保持します。
			; m6502cpu_enter〜m6502cpu_leaveの間、不変です。
;#define A	r4	; Aレジスタの値を保持します。D31:8は常に0とします。
			; Operationルーチンが使用します。
			; m6502cpu_run、Actionルーチン、AddressModeルーチン、その他のサブルーチンからは使用不可です。
;#define X	r5	; Xレジスタの値を保持します。D31:8は常に0とします。
			; Operationルーチンが使用します。
			; m6502cpu_run、Actionルーチン、AddressModeルーチン、その他のサブルーチンからは使用不可です。
;#define Y	r6	; Yレジスタの値を保持します。D31:8は常に0とします。
			; Operationルーチンが使用します。
			; m6502cpu_run、Actionルーチン、AddressModeルーチン、その他のサブルーチンからは使用不可です。
;#define NZ	r7	; PSR(N,Z)を表す2バイト値を保持します。詳しくは、M6502CPU構造体のコメントを参照してください。
			; Operationルーチンが使用します。
			; m6502cpu_run、Actionルーチン、AddressModeルーチン、その他のサブルーチンからは使用不可です。
;		r8	; 
;		r9	; 
;#define PC	r10	; &m6502mem[PC]を保持します。
			; 任意のルーチンが、Jump、Callなどの処理にて、適切に設定します。
;#define SP	r11	; SPレジスタの値を保持します。D31:8は常に0とします。
			; 任意のルーチンが、Push、Pull、Callなどの処理にて、適切に設定します。
;#define t0	r12	; 汎用ワークレジスタです。
;#define t1	r13	; 汎用ワークレジスタです。
;#define CPU	r14	; m6502cpuの先頭アドレスを保持します。
			; m6502cpu_enter〜m6502cpu_leaveの間、不変です。
;#define CYCLE	r15	; 残り実行サイクル数を保持します。
			; 任意のルーチンが、適切に残りサイクル数を減らします。

;****************************************************************************
;	
;****************************************************************************

	.code
	.align 1

;****************************************************************************
;	
;****************************************************************************

; 割り込みを実行します。
;
; [in]
;		%data	%addr
;		------	------
;	IRQ	0	0xfffe
;	NMI	0	0xfffa
;	BRK	1	0xfffe
;
; [note]
;	* 実行コンテキスト内から利用します。
;	  実行コンテキスト外から直接呼び出すことはできません。
;
	.global	m6502cpu_interrupt
m6502cpu_interrupt:
	add	%r10, %r1			; if BRK then PC++
	;
	ext	m6502cpu_pack@rm	; 	xcall.d	m6502cpu_pack			; %t0 = PSR
	call.d	m6502cpu_pack@rl
	ld.w	%r12, %r1			; *delay*
	and	%r1,0x2f	; 	xand	%r1, %r1, -17		; if BRK then B=1 else B=0
	sll	%r12,0x4	; 	xsll	%r12, 4
	or	%r12, %r1
	;
	ext	m6502mem@ah			; %data = PC
	ext	m6502mem@al
	sub	%r1, %r10
	ext	m6502cpu_push@rm	; 	xcall.d	m6502cpu_push			; Push HI(PC)
	call.d	m6502cpu_push@rl
	rr	%r1, 8			; *delay*
	ext	m6502cpu_push@rm	; 	xcall.d	m6502cpu_push			; Push Lo(PC)
	call.d	m6502cpu_push@rl
	rl	%r1, 8			; *delay*
	ext	m6502cpu_push@rm	; 	xcall.d	m6502cpu_push			; Push PSR
	call.d	m6502cpu_push@rl
	ld.w	%r1, %r12			; *delay*
	;
	ext	m6502mem@ah			; %PC = &mem[mem[addr]]
	ext	m6502mem@al
	ld.uh	%r10, [%r0]
	ext	m6502mem@ah
	ext	m6502mem@al
	add	%r10, %r10
	;
	ext	0x4		; 	xbset	[%r14+0x4], 0x2			; IRQをマスクします。
	bset	[%r14],0x2
	ext	0x10		; 	xbclr	[%r14+0x10], 0x0			; 割り込み発生により、WAIを解除します。
	bclr	[%r14],0x0
	ret

; 保留されているIRQ要求を調べ、実行可能ならば実行します。
;
; [note]
;	* 実行コンテキスト内から利用します。
;	  実行コンテキスト外から直接呼び出すことはできません。
;
	.global	m6502cpu_check_pending
m6502cpu_check_pending:
	ext	0x4		; 	xbtst	[%r14+0x4], 0x2			; IRQがマスクされていないか?
	btst	[%r14],0x2
	jrne	m6502cpu_check_pending_EXIT	; 	xjrne	m6502cpu_check_pending_EXIT
	ext	0x10		; 	xbtst	[%r14+0x10], 0x1			; IRQ要求が保留されているか?
	btst	[%r14],0x1
	jreq	m6502cpu_check_pending_EXIT	; 	xjreq	m6502cpu_check_pending_EXIT
	ext	0x10		; 	xbclr	[%r14+0x10], 0x1			; IRQ要求フラグをクリアします。
	bclr	[%r14],0x1
	ext	0x3ff		; 	xld.w	%r0, 0xfffe			; IRQを実行します。
	ld.w	%r0,0x3e
	call.d	m6502cpu_interrupt	; 	xcall.d	m6502cpu_interrupt
	ld.w	%r1, 0			; *delay*
m6502cpu_check_pending_EXIT:
	ret

;****************************************************************************
;	
;****************************************************************************

; IRQを要求します。
;
; [note]
;	* 実行コンテキスト外から呼ばれます。
;	  実行コンテキスト内から呼び出してはいけません。
;
	.global	m6502cpu_irq
m6502cpu_irq:
	pushn	%r3
	ext	m6502cpu_enter@rm	; 	xcall	m6502cpu_enter			; 実行コンテキストを取り出します。
	call	m6502cpu_enter@rl
	ext	0x10		; 	xbset	[%r14+0x10], 0x1			; IRQ要求フラグをセットします。
	bset	[%r14],0x1
	call	m6502cpu_check_pending	; 	xcall	m6502cpu_check_pending		; 直ちにIRQ実行可能か調べます。
	ext	m6502cpu_leave@rm	; 	xcall	m6502cpu_leave			; 実行コンテキストを書き戻します。
	call	m6502cpu_leave@rl
	popn	%r3
	ret

; NMIを実行します。
;
; [note]
;	* 実行コンテキスト外から呼ばれます。
;	  実行コンテキスト内から呼び出してはいけません。
;
	.global	m6502cpu_nmi
m6502cpu_nmi:
	pushn	%r3
	ext	m6502cpu_enter@rm	; 	xcall	m6502cpu_enter			; 実行コンテキストを取り出します。
	call	m6502cpu_enter@rl
	ext	0x3ff		; 	xld.w	%r0, 0xfffa			; NMIを実行します。
	ld.w	%r0,0x3a
	call.d	m6502cpu_interrupt	; 	xcall.d	m6502cpu_interrupt
	ld.w	%r1, 0			; *delay*
	ext	m6502cpu_leave@rm	; 	xcall	m6502cpu_leave			; 実行コンテキストを書き戻します。
	call	m6502cpu_leave@rl
	popn	%r3
	ret

;****************************************************************************
;	
;****************************************************************************

	.global	m6502cpu_ERR
m6502cpu_ERR:
	ext	m6502cpu_leave@rm	; 	xcall	m6502cpu_leave		; 実行コンテキストを抜けます。
	call	m6502cpu_leave@rl
	ext	m6502cpu_err@rm	; 	xjp	m6502cpu_err		; C言語版のエラールーチンへ。
	jp	m6502cpu_err@rl

;****************************************************************************
;	
;****************************************************************************
