misc/hypotrochoid.s
;========================================================================================
;  hypotrochoid.s
;  Copyright (C) 2003-2025 Makoto Kamada
;
;  This file is part of the XEiJ (X68000 Emulator in Java).
;  You can use, modify and redistribute the XEiJ if the conditions are met.
;  Read the XEiJ License for more details.
;  https://stdkmd.net/xeij/
;========================================================================================

;----------------------------------------------------------------
;  hypotrochoid.x
;	XEiJの疑似グラフィック画面(4096個のパターンを敷き詰めたバックグラウンド)にハイポトロコイドを描きます。
;	青(S)と紫(S)はスプライト画面(BG0とBG1)、赤(T)はテキスト画面、緑(G)はグラフィック画面です。
;	改造メニューの4096個のパターンチェックボックスと768x512でスプライトを表示チェックボックスと
;	512x512でBG1を表示チェックボックスをONにしてリセットしてから実行します。
;	BREAK/CTRL+C/ESCキーで終了、動作中はその他のキーで停止。停止中はENTERキーで再開、その他のキーでコマ送りします。
;	X68000実機では動きません。
;----------------------------------------------------------------

;----------------------------------------------------------------
;ハイポトロコイド
;	定円の内側に沿って動円が転がるとき、動円に固定された点の軌跡をハイポトロコイドと呼びます。
;	定円の中心を(0,0)、半径をR、動円の半径をr、動円の中心から点までの距離をdとします。
;	定円と動円の周の長さの比から、動円が左に1周する間に動円は接線に対して右にR/r回転します。
;	同時に接線が左に1回転するので、合わせて動円は右に(R-r)/r回転します。
;	動円の中心の方向をtとすると、点の位置は((R-r)*cos(t)+d*cos((R-r)/r*t),(R-r)*sin(t)-d*sin((R-r)/r*t))となります。
;	動円が左にn周する間に動円は右に(R-r)/r*n回転します。
;	R,rが整数のとき、点が最初の位置に戻るまでの周期n0は(R-r)/r*nが整数になる最小のn、すなわちn0=r/gcd(R-r,r)です。
;	https://en.wikipedia.org/wiki/Hypotrochoid
;----------------------------------------------------------------

	.include	control2.mac
	.include	crtc.equ
	.include	doscall.mac
	.include	fefunc.mac
	.include	iocscall.mac
	.include	mfp.equ
	.include	misc.mac
	.include	push2.mac
	.include	sprc.equ
	.include	vicon.equ

;引数
	lea.l	1(a2),a0
	do
	;R 定円の半径
		bsr	nextword
		break	eq			;指定なし。既定値を使う
		FPACK	__STOL
		goto	cs,error		;整数がない。エラー終了
		goto	<tst.l d0>,le,error	;1より小さい。エラー終了
		goto	<cmp.l #1000000,d0>,gt,error	;1000000より大きい。エラー終了
		move.l	d0,int_R
	;r 動円の半径
		bsr	nextword
		break	eq			;指定なし。既定値を使う
		FPACK	__STOL
		goto	cs,error		;整数がない。エラー終了
		goto	<tst.l d0>,le,error	;1より小さい。エラー終了
		goto	<cmp.l #1000000,d0>,gt,error	;1000000より大きい。エラー終了
		goto	<cmp.l int_R,d0>,ge,error	;rがRと等しいかより大きい。エラー終了
		move.l	d0,int_r
	;d 動円の中心から点までの距離
		bsr	nextword
		break	eq			;指定なし。既定値を使う
		FPACK	__STOD
		goto	cs,error		;数値がない。エラー終了
		FPACK	__DTST
		goto	lt,error		;0より小さい。エラー終了
		move.l	d0,d2
		move.l	d1,d3
		move.l	#1000000,d0
		FPACK	__LTOD
		FPACK	__DCMP
		goto	lt,error		;1000000より大きい。エラー終了
		movem.l	d2-d3,dbl_d
	;f 1周(≠1周期)の点の数
		bsr	nextword
		break	eq			;指定なし。既定値を使う
		FPACK	__STOL
		goto	cs,error		;数値がない。エラー終了
		goto	<tst.l d0>,le,error	;1より小さい。エラー終了
		goto	<cmp.l #1000000,d0>,gt,error	;1000000より大きい。エラー終了
		move.l	d0,int_f
	while	f

;引数で決まる値
;R 定円の半径
	move.l	int_R,d0
	FPACK	__LTOD
	movem.l	d0-d1,dbl_R
;r 動円の半径
	move.l	int_r,d0
	FPACK	__LTOD
	movem.l	d0-d1,dbl_r
;Rr=R-r 定円の中心から動円の中心までの距離
	move.l	int_R,d0
	sub.l	int_r,d0
	move.l	d0,int_Rr
	FPACK	__LTOD
	movem.l	d0-d1,dbl_Rr
;Rrr=(R-r)/r 点の速度
;	movem.l	dbl_Rr,d0-d1
	movem.l	dbl_r,d2-d3
	FPACK	__DDIV
	movem.l	d0-d1,dbl_Rrr
;n0=r/gcd(R-r,r) 周期
	move.l	int_Rr,d0		;d0=前回の余り=R-r。0ではない
	move.l	int_r,d1		;d1=前回の除数=r。0ではない
	do
					;d0=前回の余り,d1=前回の除数
					;前回の除数→被除数,前回の余り→除数
		exg.l	d0,d1		;d0=被除数,d1=除数
		FPACK	__LMOD		;d0=余り,d1=除数
	while	<tst.l d0>,ne		;余りが0になるまで繰り返す
					;余りが0になったときの除数=gcd(R-r,r)
	move.l	int_r,d0		;d0=r
	FPACK	__LDIV			;d0=n0=r/gcd(R-r,r)
	move.l	d0,int_n0
;fn0=f*n0 n0周(=1周期)の点の数
	move.l	int_f,d0
	move.l	int_n0,d1
	FPACK	__LMUL
	move.l	d0,int_fn0
;s=RR/(R-r+d) 倍率
	movem.l	dbl_Rr,d0-d1
	movem.l	dbl_d,d2-d3
	FPACK	__DADD
	move.l	d0,d2
	move.l	d1,d3
	movem.l	dbl_RR,d0-d1
	FPACK	__DDIV
	movem.l	d0-d1,dbl_s
;f 1周(≠1周期)の点の数
	move.l	int_f,d0
	FPACK	__LTOD
	movem.l	d0-d1,dbl_f
;dt=2*pi/f 角度の増分
	moveq.l	#2,d0
	FPACK	__LTOD
	FPACK	__NPI
	movem.l	dbl_f,d2-d3
	FPACK	__DDIV
	movem.l	d0-d1,dbl_dt

;スクロールの軌道を作る
;	中心(128,256)半径(128,256)の円
orbit_length	equ	(orbit_end-orbit)/4
	move.l	#orbit_length,d0
	FPACK	__LTOD
	move.l	d0,d2			;d2d3=n
	move.l	d1,d3
	moveq.l	#2,d0
	FPACK	__LTOD
	FPACK	__NPI			;d0d1=2*pi
	FPACK	__DDIV			;d0d1=2*pi/n=dt
	movea.l	d0,a4			;a4a5=dt
	movea.l	d1,a5
	move.l	#128,d0
	FPACK	__LTOD
	movea.l	d0,a0			;a0a1=128
	movea.l	d1,a1
	move.l	#256,d0
	FPACK	__LTOD
	movea.l	d0,a2			;a2a3=256
	movea.l	d1,a3
	moveq.l	#0,d4			;d4d5=t
	moveq.l	#0,d5
	lea.l	orbit,a6
	move.w	#orbit_length-1,d7
	for	d7
		move.l	d4,d0			;d0d1=t
		move.l	d5,d1
		FPACK	__COS			;d0d1=cos(t)
		move.l	a0,d2			;d2d3=128
		move.l	a1,d3
		FPACK	__DMUL			;d0d1=128*cos(t)
		exg.l	d0,d2			;d0d1=128
		exg.l	d1,d3			;d2d3=128*cos(t)
		FPACK	__DSUB			;d0d1=128-128*cos(t)
		FPACK	__DTOL			;d0=128-128*cos(t)
		move.w	d0,(a6)+		;x
	;
		move.l	d4,d0			;d0d1=t
		move.l	d5,d1
		FPACK	__SIN			;d0d1=sin(t)
		move.l	a2,d2			;d2d3=256
		move.l	a3,d3
		FPACK	__DMUL			;d0d1=256*sin(t)
		FPACK	__DADD			;d0d1=256+256*sin(t)
		FPACK	__DTOL			;d0=256+256*sin(t)
		move.w	d0,(a6)+		;y
	;
		move.l	d4,d0			;d0d1=t
		move.l	d5,d1
		move.l	a4,d2			;d2d3=dt
		move.l	a5,d3
		FPACK	__DADD			;d0d1=t+dt
		move.l	d0,d4			;d4d5=t+dt
		move.l	d1,d5
	next

;画面モードを変更
	move.w	#3,-(sp)		;ファンクションキー表示なし
	move.w	#14,-(sp)
	DOS	_CONCTRL
	move.w	d0,function_mode
	move.w	#1,-(sp)		;768x512グラフィックあり
	move.w	#16,-(sp)
	DOS	_CONCTRL
	move.w	d0,screen_mode
	addq.l	#8,sp

;カーソルOFF
	IOCS	_B_CUROFF

;スーパーバイザモード
	supervisormode

;スプライトコントローラ初期化
	move.w	#28+4,SPRC_H_BACK_END	;水平バックポーチ終了カラム。R02+4
	move.w	#255,SPRC_H_FRONT_END	;水平フロントポーチ終了カラム。255。ウェイトは省略
	move.w	#40,SPRC_V_BACK_END	;垂直バックポーチ終了ラスタ。R06
	move.w	#%10101,SPRC_RESOLUTION	;解像度。--------|---|高解像度|垂直サイズ##|水平サイズ##
	moveq.l	#0,d0
	move.l	d0,SPRC_BG_0_X		;BG0スクロールX座標,BG0スクロールY座標
	move.l	d0,SPRC_BG_1_X		;BG1スクロールX座標,BG1スクロールY座標
	move.w	d0,SPRC_CONTROL		;コントロール。------|スプライト画面ON|---|BG1テキスト##|BG1表示ON|BG0テキスト##|BG0表示ON

;パターン4096枚定義ON
	move.w	#$0005,$00EB0812

;スプライトをクリア
	lea.l	$00EB0000,a0
	moveq.l	#0,d0
	move.w	#256-1,d1
	for	d1
		move.l	d0,(a0)+
		move.l	d0,(a0)+
	next

;パターンをクリア
	moveq.l	#0,d0
	moveq.l	#0,d2			;バンク
	do
		move.w	d2,$00EB0814		;バンク番号
		lea.l	$00EB8000,a0
		move.w	#32768/8-1,d1
		for	d1
			move.l	d0,(a0)+
			move.l	d0,(a0)+
		next
		add.w	#$0100,d2
	while	<cmp.w #$0F00,d2>,ls

;テキストエリアにパターンを敷き詰める
	lea.l	$00EB4000,a0		;開始位置(0,0)
	moveq.l	#0,d4			;パターン番号上位4bit<<12
	move.w	#1<<8,d5		;パレットブロック<<8。パレットブロック1から
	moveq.l	#0,d6			;パターン番号下位8bit
	do
		moveq.l	#16-1,d3		;Y方向ループ上位16回
		for	d3
			moveq.l	#4-1,d2			;Y方向ループ下位4回
			for	d2
				moveq.l	#64-1,d1		;X方向ループ64回
				for	d1
					move.w	d4,d0			;パターン番号上位4bit<<12
					add.w	d5,d0			;パレットブロック<<8
					add.w	d6,d0			;パターン番号下位8bit
					move.w	d0,(a0)+
					addq.b	#1,d6			;パターン番号下位8bit
				next
			next
			add.w	#1<<12,d4		;パターン番号上位4bit<<12
		next
		add.w	#1<<8,d5		;パレットブロック<<8
	while	<cmp.w #2<<8,d5>,ls	;パレットブロック2まで

;スプライト表示ON
;	BG0にテキストエリア0、BG1にテキストエリア1を割り当てて表示
	move.w	#SPRC_SPRITE_ON|SPRC_BG_1_TEXT_1|SPRC_BG_1_ON|SPRC_BG_0_TEXT_0|SPRC_BG_0_ON,SPRC_CONTROL
	ori.w	#VICON_SPON_MASK,VICON_VISIBLE
	move.w	#%00_00_01_10_11_10_01_00,VICON_PRIORITY

;パレットを設定する
	movehsv	104,31,31,VICON_TSPALET+2*$11	;青。BG0。スプライトパレット$11
	movehsv	152,31,31,VICON_TSPALET+2*$21	;紫。BG1。スプライトパレット$21
	movehsv	8,31,31,VICON_TSPALET+2*1	;赤。テキスト画面。テキストパレット1
	movehsv	56,31,31,VICON_GPALET+2*1	;緑。グラフィック画面。グラフィックパレット1

;開始位置までスクロールする
	lea.l	orbit,a0
	move.w	(a0),SPRC_BG_0_X	;BG0を動かす
	move.w	2(a0),SPRC_BG_0_Y
	move.w	4*(orbit_length*1/4)(a0),SPRC_BG_1_X	;BG1を動かす
	move.w	4*(orbit_length*1/4)+2(a0),SPRC_BG_1_Y
	move.w	4*(orbit_length*2/4)(a0),CRTC_TEXT_X	;テキスト画面を動かす
	move.w	4*(orbit_length*2/4)+2(a0),CRTC_TEXT_Y
	move.w	4*(orbit_length*3/4)(a0),CRTC_GRAPHIC_X_0	;グラフィック画面を動かす
	move.w	4*(orbit_length*3/4)+2(a0),CRTC_GRAPHIC_Y_0

;文字を描く
	lea.l	moji(pc),a0		;a0=文字
	moveq.l	#2,d7			;2=S,1=T,0=G
	for	d7
	;文字コードを取り出す。SJISに限る
		moveq.l	#0,d0
		moveq.l	#0,d1
		move.b	(a0)+,d0		;sh
		move.b	(a0)+,d1		;sl
	;94区94点の通し番号を求める
	;	((sh - (sh < 0xe0 ? 0x81 : 0xe0 - (0xa0 - 0x81))) * 188 +
	;	 (sl - (sl < 0x80 ? 0x40 : 0x80 - (0x7f - 0x40))))
		moveq.l	#$81,d2
		if	<cmp.b #$E0,d0>,hs
			moveq.l	#$E0-($A0-$81),d2
		endif
		sub.b	d2,d0
		moveq.l	#$40,d2
		if	<cmp.b #$80,d1>,hs
			moveq.l	#$80-($7F-$40),d2
		endif
		sub.b	d2,d1
		mulu.w	#188,d0
		add.w	d1,d0
	;9区から15区までないので詰める
		if	<cmp.w #94*(16-1),d0>,hs	;16区~
			sub.w	#94*(16-9),d0		;16区→9区
		endif
	;フォントアドレスを求める
		mulu.w	#3*24,d0
		add.l	#$00F40000,d0		;24x24
		movea.l	d0,a1
	;点を並べる
		moveq.l	#1,d3			;色
		move.w	#512-12,d2		;Y座標
		moveq.l	#24-1,d6		;24ライン
		for	d6
			move.w	#512-12,d1		;X座標
			moveq.l	#3-1,d5			;3バイト
			for	d5
				move.b	(a1)+,d0
				moveq.l	#8-1,d4			;8ドット
				for	d4
					add.b	d0,d0
					if	cs
						if	<cmp.w #1,d7>,hi	;2=S
							bsr	sp_pset
						elif	eq			;1=T
							bsr	tx_pset
						else				;0=G
							bsr	gr_pset
						endif
					endif
					addq.w	#1,d1			;X座標を進める
				next
			next
			sub.w	#24,d1			;X座標を戻して
			addq.w	#1,d2			;Y座標を進める
		next
	next

;ハイポトロコイドを描く
;	t=0
;	for i=1 to fn0
;		pset(floor(ox+s*(Rr*cos(t)+d*cos(Rrr*t))),
;		     floor(oy-s*(Rr*sin(t)-d*sin(Rrr*t))))
;		t=t+dt
;	next
	moveq.l	#0,d4			;d4d5=t
	moveq.l	#0,d5
	move.l	int_fn0,d7
	forlong	d7
		movem.l	dbl_Rrr,d0-d1		;d0d1=Rrr
		move.l	d4,d2			;d2d3=t
		move.l	d5,d3
		FPACK	__DMUL			;d0d1=Rrr*t
		movea.l	d0,a4			;a4a5=Rrr*t
		movea.l	d1,a5
		FPACK	__COS			;d0d1=cos(Rrr*t)
		movem.l	dbl_d,d2-d3		;d2d3=d
		FPACK	__DMUL			;d0d1=d*cos(Rrr*t)
		movea.l	d0,a2			;a2a3=d*cos(Rrr*t)
		movea.l	d1,a3
		move.l	d4,d0			;d0d1=t
		move.l	d5,d1
		FPACK	__COS			;d0d1=cos(t)
		movem.l	dbl_Rr,d2-d3		;d2d3=Rr
		FPACK	__DMUL			;d0d1=Rr*cos(t)
		move.l	a2,d2			;d2d3=d*cos(Rrr*t)
		move.l	a3,d3
		FPACK	__DADD			;d0d1=Rr*cos(t)+d*cos(Rrr*t)
		movem.l	dbl_s,d2-d3		;d2d3=s
		FPACK	__DMUL			;d0d1=s*(Rr*cos(t)+d*cos(Rrr*t))
		movem.l	dbl_ox,d2-d3	;d2d3=ox
		FPACK	__DADD			;d0d1=ox+s*(Rr*cos(t)+d*cos(Rrr*t))
		FPACK	__DFLOOR		;d0d1=floor(ox+s*(Rr*cos(t)+d*cos(Rrr*t)))
		FPACK	__DTOL			;d0=floor(ox+s*(Rr*cos(t)+d*cos(Rrr*t)))
		movea.l	d0,a0			;a0=floor(ox+s*(Rr*cos(t)+d*cos(Rrr*t))) X座標
	;
		move.l	a4,d0
		move.l	a5,d1			;d0d1=Rrr*t
		FPACK	__SIN			;d0d1=sin(Rrr*t)
		movem.l	dbl_d,d2-d3		;d2d3=d
		FPACK	__DMUL			;d0d1=d*sin(Rrr*t)
		movea.l	d0,a2			;a2a3=d*sin(Rrr*t)
		movea.l	d1,a3
		move.l	d4,d0			;d0d1=t
		move.l	d5,d1
		FPACK	__SIN			;d0d1=sin(t)
		movem.l	dbl_Rr,d2-d3		;d2d3=Rr
		FPACK	__DMUL			;d0d1=Rr*sin(t)
		move.l	a2,d2			;d2d3=d*sin(Rrr*t)
		move.l	a3,d3
		FPACK	__DSUB			;d0d1=Rr*sin(t)-d*sin(Rrr*t)
		movem.l	dbl_s,d2-d3		;d2d3=s
		FPACK	__DMUL			;d0d1=s*(Rr*sin(t)-d*sin(Rrr*t))
		move.l	d0,d2			;d2d3=s*(Rr*sin(t)-d*sin(Rrr*t))
		move.l	d1,d3
		movem.l	dbl_oy,d0-d1	;d0d1=oy
		FPACK	__DSUB			;d0d1=oy-s*(Rr*sin(t)-d*sin(Rrr*t))
		FPACK	__DFLOOR		;d0d1=floor(oy-s*(Rr*sin(t)-d*sin(Rrr*t)))
		FPACK	__DTOL			;d0=floor(oy-s*(Rr*sin(t)-d*sin(Rrr*t)))
		movea.l	d0,a1			;a1=floor(oy-s*(Rr*sin(t)-d*sin(Rrr*t))) Y座標
	;
		move.l	a0,d1
		move.l	a1,d2
		moveq.l	#1,d3
		bsr	sp_pset
		bsr	tx_pset
		bsr	gr_pset
	;
		move.l	d4,d0			;d0d1=t
		move.l	d5,d1
		movem.l	dbl_dt,d2-d3		;d2d3=dt
		FPACK	__DADD			;d0d1=t+dt
		move.l	d0,d4			;d4d5=t+dt
		move.l	d1,d5
	next

;動かす
	lea.l	orbit,a0
	movea.l	a0,a1
	lea.l	4*(orbit_length*1/4)(a0),a2
	lea.l	4*(orbit_length*2/4)(a0),a3
	lea.l	4*(orbit_length*3/4)(a0),a4
	lea.l	orbit_end-orbit(a0),a5
	moveq.l	#0,d1			;-1=停止中,0=動作中,1=コマ送り
	dostart
		ifand	<tst.l d1>,mi,<cmp.b #13,d0>,eq	;停止中にENTERキーが押されたとき
			moveq.l	#0,d1			;動作中にする
		elif	<tst.b d0>,ne		;停止中にENTER以外のキーが押されたか、動作中に何かキーが押されたとき
			moveq.l	#1,d1			;コマ送りにする
		endif
	start
		do
		while	<btst.b #MFP_G_VDISP_BIT,MFP_GPDR>,eq	;垂直表示期間を待つ
		do
		while	<btst.b #MFP_G_VDISP_BIT,MFP_GPDR>,ne	;垂直帰線期間を待つ
		if	<tst.l d1>,pl		;動作中またはコマ送りのとき
			if	ne			;コマ送りのとき
				moveq.l	#-1,d1			;停止中にする
			endif
			move.w	(a1)+,SPRC_BG_0_X	;BG0を動かす
			move.w	(a1)+,SPRC_BG_0_Y
			if	<cmpa.l a5,a1>,eq
				movea.l	a0,a1
			endif
			move.w	(a2)+,SPRC_BG_1_X	;BG1を動かす
			move.w	(a2)+,SPRC_BG_1_Y
			if	<cmpa.l a5,a2>,eq
				movea.l	a0,a2
			endif
			move.w	(a3)+,CRTC_TEXT_X	;テキスト画面を動かす
			move.w	(a3)+,CRTC_TEXT_Y
			if	<cmpa.l a5,a3>,eq
				movea.l	a0,a3
			endif
			move.w	(a4)+,CRTC_GRAPHIC_X_0	;グラフィック画面を動かす
			move.w	(a4)+,CRTC_GRAPHIC_Y_0
			if	<cmpa.l a5,a4>,eq
				movea.l	a0,a4
			endif
		endif
		bsr	inkey0
	whileand	<cmp.b #27,d0>,ne,<cmp.b #3,d0>,ne	;ESCまたはCTRL+Cが押されるまで繰り返す

;スプライト表示OFF
	move.w	#$0000,SPRC_CONTROL
	andi.w	#.notw.VICON_SPON_MASK,VICON_VISIBLE

;パターン4096枚定義OFF
	move.w	#$0000,$00EB0812
	move.w	#$0000,$00EB0814

;ユーザモード
	usermode

;カーソルON
	IOCS	_B_CURON

;画面モードを復元
	move.w	screen_mode,-(sp)
	move.w	#16,-(sp)
	DOS	_CONCTRL
	move.w	function_mode,-(sp)
	move.w	#14,-(sp)
	DOS	_CONCTRL
	addq.l	#8,sp

;終了
exit:
	DOS	_EXIT

moji:		.dc.w	'S','T','G'

;エラー終了
error:
	pea.l	usage(pc)
	DOS	_PRINT
	addq.l	#4,sp
	move.w	#1,-(sp)
	DOS	_EXIT2

usage:		.dc.b	'>hypotrochoid.x R r d f',13,10
		.dc.b	'                               既定値  最小値   最大値',13,10
		.dc.b	'  R  定円の半径(整数)              97     r+1  1000000',13,10
		.dc.b	'  r  動円の半径(整数)              47       1      R-1',13,10
		.dc.b	'  d  動円の中心から点までの距離    37       1  1000000',13,10
		.dc.b	'  f  1周(≠1周期)の点の数(整数)   720       1  1000000',13,10
		.dc.b	0
	.even

;sp_pset
;	バックグラウンド画面に点を描く
;	1024x1024ドットの画面が64x64個の16x16ドット(128バイト)のパターンに分割されている
;	画面座標(%LLLLLLSTXX,%BBBBHHYYYY)
;	パターン座標(%LLLLLL,%BBBBHH) パターン内座標(%STXX,%YYYY)
;	パターン番号%BBBBHHLLLLLL バンク番号%BBBB バンク内パターン番号%HHLLLLLL
;	ワードアドレス$00EB8000+%HHLLLLLLSYYYYT0 ワード内ビット番号%XX00^%1100
;<d1.w:X座標。0~1023。範囲外のとき何もしない
;<d2.w:Y座標。0~1023。範囲外のとき何もしない
;<d3.w:パレットコード。0~15。下位4bitを使う
sp_pset:
	ifand	<cmp.w #1023,d1>,ls,<cmp.w #1023,d2>,ls
		push	d1-d5/a0
		moveq.l	#%1111,d4		;d1.w=%LLLLLLSTXX
		moveq.l	#%1111,d5		;d2.w=%BBBBHHYYYY
		and.w	d4,d3			;d3.w=%PPPP
		and.w	d1,d4			;d4.l=%STXX
		and.w	d2,d5			;d5.l=%YYYY
		lsr.w	#4,d1			;d1.w=%LLLLLL
		lsr.w	#4,d2			;d2.w=%BBBBHH
		lsl.w	#6,d2			;d2.w=%BBBBHH000000
		or.w	d2,d1			;d1.w=%BBBBHHLLLLLL パターン番号
		move.w	d1,$00EB0814		;%BBBB バンク番号
		moveq.l	#0,d2
		move.b	d1,d2			;d2.l=%HHLLLLLL
		lsl.b	#5,d4			;d4.b=%TXX00000,x=S
		addx.w	d2,d2			;d2.l=%HHLLLLLLS
		lsl.w	#4,d2			;d2.l=%HHLLLLLLS0000
		or.w	d5,d2			;d2.l=%HHLLLLLLSYYYY
		add.b	d4,d4			;d4.b=%XX000000,x=T
		addx.w	d2,d2			;d2.l=%HHLLLLLLSYYYYT
		add.w	d2,d2			;d2.l=%HHLLLLLLSYYYYT0
		lea.l	$00EB8000,a0
		adda.l	d2,a0			;a0=$00EB8000+%HHLLLLLLSYYYYT0 ワードアドレス
		lsr.b	#4,d4			;d4.b=%XX00
		eor.b	#%1100,d4		;d4.b=%XX00^%1100 ワード内ビット番号
		move.w	(a0),d2			;ワードを読み出す
		ror.w	d4,d2			;目的の位置を下位4bitへ移動する
		and.w	#%1111111111110000,d2	;下位4bitを消す
		or.w	d3,d2			;下位4bitにパレットコードを入れる
		rol.w	d4,d2			;元の位置に戻す
		move.w	d2,(a0)			;ワードを書き戻す
		pop
	endif
	rts

tx_pset:
	ifand	<cmp.w #1023,d1>,ls,<cmp.w #1023,d2>,ls
		push	d0-d3/a0
		move.w	CRTC_ACCESS,-(sp)
		move.w	#$0200,CRTC_ACCESS	;マスクON,同時OFF
		move.w	CRTC_MASK,-(sp)
		lea.l	$00E60000,a0		;プレーン3から
		ext.l	d2
		lsl.l	#7,d2			;y*128
		adda.l	d2,a0
		moveq.l	#15,d0
		and.w	d1,d0			;xの下位4bit
		move.w	#$8000,d2
		lsr.w	d0,d2			;書き込むビットだけ1
		not.w	d2			;書き込むビットだけ0
		move.w	d2,CRTC_MASK
		lsr.w	#4,d1
		add.w	d1,d1			;x/16*2
		adda.w	d1,a0
		ror.w	#4,d3			;bit3を最上位に
		moveq.l	#4-1,d2
		for	d2
			add.w	d3,d3			;押し出す
			subx.w	d0,d0			;全ビットに展開
			move.w	d0,(a0)			;書き込む
			suba.l	#$00020000,a0
		next
		move.w	(sp)+,CRTC_MASK
		move.w	(sp)+,CRTC_ACCESS
		pop
	endif
	rts

gr_pset:
	ifand	<cmp.w #1023,d1>,ls,<cmp.w #1023,d2>,ls
		push	d1-d2/a0
		lea.l	$00C00000,a0
		swap.w	d2
		clr.w	d2
		lsr.l	#5,d2			;y*2048
		adda.l	d2,a0
		add.w	d1,d1			;x*2
		move.w	d3,(a0,d1.w)
		pop
	endif
	rts

	.data
	.even
;定数
dbl_ox:		.dc.d	512.0		;ox 中心のX座標
dbl_oy:		.dc.d	512.0		;oy 中心のY座標
dbl_RR:		.dc.d	510.0		;RR 表示半径
;引数
int_R:		.dc.l	97		;R 定円の半径
int_r:		.dc.l	47		;r 動円の半径
dbl_d:		.dc.d	37.0		;d 動円の中心から点までの距離
int_f:		.dc.l	720		;f 1周(≠1周期)の点の数

	.bss
	.even
;引数で決まる値
dbl_R:		.ds.d	1		;R 定円の半径
dbl_r:		.ds.d	1		;r 動円の半径
int_Rr:		.ds.l	1		;Rr=R-r 定円の中心から動円の中心までの距離
dbl_Rr:		.ds.d	1		;Rr=R-r 定円の中心から動円の中心までの距離
dbl_Rrr:	.ds.d	1		;Rrr=(R-r)/r 点の速度
int_n0:		.ds.l	1		;n0=r/gcd(R-r,r) 周期
int_fn0:	.ds.l	1		;fn0=f*n0 n0周(=1周期)の点の数
dbl_s:		.ds.d	1		;s=RR/(R-r+d) 倍率
dbl_f:		.ds.d	1		;f 1周(≠1周期)の点の数
dbl_dt:		.ds.d	1		;dt=2*pi/f 角度の増分

;保存した画面モード
function_mode:	.ds.w	1
screen_mode:	.ds.w	1

;スクロールの軌道
orbit:		.ds.w	2*720
orbit_end:

	.text

;----------------------------------------------------------------
;文字コードが0でないキーを入力する。押されていなくても待たない
;>d0.l:文字コード。0=押されていない
inkey0::
	dostart
		IOCS	_B_KEYINP		;キーバッファから取り除く
		break	<tst.b d0>,ne		;文字コードが0でないキーが押されたとき終了
	start
		IOCS	_B_KEYSNS		;キーバッファを先読みする
	while	<tst.l d0>,ne		;何か押されているとき繰り返す
	and.l	#$000000FF,d0		;文字コード
	rts

;----------------------------------------------------------------
;空白を読み飛ばす
;<a0.l:文字列
;>d0.l:最初の空白以外の文字または0
;>a0.l:最初の空白以外の文字または0の位置
;>z:ne=空白以外の文字がある,eq=空白以外の文字がない
nextword::
	moveq.l	#0,d0
	do
		move.b	(a0)+,d0		;次の文字
		break	eq			;0ならば終了
		redo	<cmp.b #' ',d0>,eq	;' 'ならば繰り返す
	whileand	<cmp.b #9,d0>,hs,<cmp.b #13,d0>,ls	;\t\n\v\f\rならば繰り返す
	subq.l	#1,a0			;進み過ぎた分戻る
	tst.l	d0
	rts

	.end