misc/optime.s
;========================================================================================
;  optime.s
;  Copyright (C) 2003-2023 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/
;========================================================================================

;----------------------------------------------------------------
;
;	optime.x
;		命令列の実行時間を計測します。
;
;	最終更新
;		2023-09-17
;
;	作り方
;		has060 -i include -o optime.o -w optime.s
;		lk -o optime.x optime.o
;
;	使い方
;		-a アセンブラ
;			アセンブラを指定します。
;			指定がなければhas060を使います。
;		-f ファイル名
;			実行時間を計測したい命令列を書いたファイルを指定します。
;			改行で命令を区切ります。
;		-i "命令列"
;			実行時間を計測したい命令列を"~"で囲んで指定します。
;			セミコロンで命令を区切ります。
;			注釈を意味するセミコロンは書けません。
;		-s
;			命令列をSRAMに書き込んで計測します。
;			DRAMのリフレッシュによる誤差が出にくくなります。
;			X68000では1ワードあたり1サイクルのウェイトが追加されます。
;			SRAMが使用中のとき計測するか問い合わせます。
;		-sn
;			-sと同様ですがSRAMが使用中のときは計測しません。
;		-sy
;			-sと同様ですがSRAMが使用中のときも計測します。
;		-t ディレクトリ名
;			テンポラリディレクトリを指定します。
;			指定がなければ環境変数tempを読み出します。
;			環境変数tempもなければカレントディレクトリを使います。
;		-v
;			冗長に出力します。
;		命令列
;			正しく計測できる実行時間の上限は12.799msです。
;			命令列の長さの上限は32766バイトです。
;			セクションの変更はできません。
;			分岐命令も書けますが出口は命令列の末尾になければなりません。
;			d7/a5-a7を変更するときは保存と復元を行ってください。
;			-268(a5)~-1(a5)の268バイトは読み書き自由の空き領域です。
;			命令列はスーパーバイザモードで呼び出されます。
;			命令列は割り込みが禁止された状態で呼び出されます。
;
;	更新履歴
;		2023-01-19
;			公開。
;		2023-01-21
;			-vのときタイトルとバージョンを表示する。
;			エラーメッセージの表示に標準エラー出力を使わない。
;			テンポラリディレクトリ名の末尾が':'のとき'\'を追加しない。
;			アセンブラのパラメータに-wを指定して警告を抑制する。
;			アセンブラのメッセージを隠さない。
;			-vのときアセンブラの終了コードを表示する。
;		2023-09-17
;			"~"または'~'が閉じていないと白窓が出るバグを修正。
;
;----------------------------------------------------------------

	.include	control2.mac
	.include	doscall.mac
	.include	iocscall.mac
	.include	mfp.equ
	.include	misc.mac
	.include	push2.mac
	.include	sram.equ

TITLE_STRING	reg	'optime.x (2023-09-17)'

ERROR_OUTPUT	equ	0		;1=エラーメッセージの表示に標準エラー出力を使う
WARNING_OPTION	equ	1		;1=アセンブラのパラメータに-wを指定して警告を抑制する
HIDE_ASSEMBLER	equ	0		;1=アセンブラのメッセージを隠す

	.text

;----------------------------------------------------------------
;プログラム開始
program_start::

;----------------------------------------------------------------
;スタックエリアを設定する
	lea.l	stack_area_end,sp	;スタックエリアの末尾

	.stack
	.even
		.ds.b	65536		;スタックエリア
stack_area_end::
	.text

;----------------------------------------------------------------
;メモリブロックを短くする
;	子プロセスを起動するため
;	*.r形式は非対応
;<a0.l:メモリ管理ポインタ
;<a1.l:使用部分の末尾
	lea.l	16(a0),a0		;メモリブロックの先頭
	suba.l	a0,a1			;使用部分の長さ
	move.l	a1,-(sp)
	move.l	a0,-(sp)
	DOS	_SETBLOCK
	addq.l	#8,sp

;----------------------------------------------------------------
;オプションを確認する
;<a2.l:コマンドライン。LASCIIZ
	clr.b	assembler_arg		;-a アセンブラ
	clr.b	file_arg		;-f ファイル名
	clr.b	inst_arg		;-i 命令列
	clr.b	sram_flag		;-s SRAMフラグ。1=-s,2=-sn,3=-sy
	clr.b	temp_arg		;-t ディレクトリ名
	clr.b	verbose_flag		;-v 冗長フラグ
	lea.l	1(a2),a1
	dostart
		gotoand	<cmp.b #'-',d0>,ne,<cmp.b #'/',d0>,ne,usage_exit	;-,/以外
		addq.l	#1,a1
		move.b	(a1)+,d0
		goto	eq,usage_exit		;-,/の後に文字がない
		bsr	tolower
		if	<cmp.b #'a',d0>,eq	;-a
			lea.l	assembler_arg,a0	;-a アセンブラ
			bsr	argcpy
			goto	eq,usage_exit		;-aの後に文字がない
		elif	<cmp.b #'f',d0>,eq	;-f
			lea.l	file_arg,a0		;-f ファイル名
			bsr	argcpy
			goto	eq,usage_exit		;-fの後に文字がない
		elif	<cmp.b #'i',d0>,eq	;-i
			lea.l	inst_arg,a0		;-i 命令列
			bsr	argcpy
			goto	eq,usage_exit		;-iの後に文字がない
		elif	<cmp.b #'s',d0>,eq
			move.b	(a1)+,d0
			bsr	tolower
			if	<cmpi.b #'n',d0>,eq	;-sn
				move.b	#2,sram_flag		;-s SRAMフラグ。1=-s,2=-sn,3=-sy
			elif	<cmpi.b #'y',d0>,eq	;-sy
				move.b	#3,sram_flag		;-s SRAMフラグ。1=-s,2=-sn,3=-sy
			else
				subq.l	#1,a1
				move.b	#1,sram_flag		;-s SRAMフラグ。1=-s,2=-sn,3=-sy
			endif
		elif	<cmp.b #'t',d0>,eq	;-t
			lea.l	temp_arg,a0		;-t ディレクトリ名
			bsr	argcpy
			goto	eq,usage_exit		;-tの後に文字がない
		elif	<cmp.b #'v',d0>,eq	;-v
			st.b	verbose_flag		;-v 冗長フラグ
		else				;その他
			goto	usage_exit
		endif
	start
		exg.l	a0,a1
		bsr	nonspace
		exg.l	a0,a1
	while	ne
	gotoand	<tst.b file_arg>,eq,<tst.b inst_arg>,eq,usage_exit	;-fと-iが両方ない
	gotoand	<tst.b file_arg>,ne,<tst.b inst_arg>,ne,usage_exit	;-fと-iが両方ある
	if	<tst.b verbose_flag>,ne
		lea.l	string_buffer,a0	;文字列バッファ
		leastrdata	'> optime.x',a1
		bsr	strcpy
		if	<tst.b assembler_arg>,ne
			leastrdata	' -a ',a1
			bsr	strcpy
			lea.l	assembler_arg,a1	;-a アセンブラ
			bsr	strcpy
		endif
		if	<tst.b file_arg>,ne
			leastrdata	' -f ',a1
			bsr	strcpy
			lea.l	file_arg,a1		;-f ファイル名
			bsr	strcpy
		endif
		if	<tst.b inst_arg>,ne
			leastrdata	' -i ',a1
			bsr	strcpy
			move.b	#'"',(a0)+
			lea.l	inst_arg,a1		;-i 命令列
			bsr	strcpy
			move.b	#'"',(a0)+
		endif
		if	<tst.b sram_flag>,ne
			if	<cmpi.b #2,sram_flag>,lo	;-s SRAMフラグ
				leastrdata	' -s',a1
			elif	eq
				leastrdata	' -sn',a1
			else
				leastrdata	' -sy',a1
			endif
			bsr	strcpy
		endif
		if	<tst.b temp_arg>,ne
			leastrdata	' -t ',a1
			bsr	strcpy
			lea.l	temp_arg,a1		;-t ディレクトリ名
			bsr	strcpy
		endif
	;	if	<tst.b verbose_flag>,ne
			leastrdata	' -v',a1
			bsr	strcpy
	;	endif
		bsr	crlf
		lea.l	string_buffer,a0	;文字列バッファ
		bsr	print
	endif

	.bss
assembler_arg::
	.ds.b	1024			;-a アセンブラ
file_arg::
	.ds.b	1024			;-f ファイル名
inst_arg::
	.ds.b	1024			;-i 命令列
sram_flag::
	.ds.b	1			;-s SRAMフラグ
temp_arg::
	.ds.b	1024			;-t ディレクトリ名
verbose_flag::
	.ds.b	1			;-v 冗長フラグ
string_buffer::
	.ds.b	4096			;文字列バッファ
	.text

;----------------------------------------------------------------
;命令列をソースコードに変換する
	if	<tst.b inst_arg>,ne
		lea.l	inst_arg,a1		;-i 命令列
		lea.l	source_code,a0		;ソースコード
		dostart
			if	<cmpi.b #'@',(a1)>,ne
				move.b	#9,(a0)+
			endif
			docontinue
				move.b	(a1)+,(a0)+
			whileand	<tst.b (a1)>,ne,<cmpi.b #';',(a1)>,ne
			move.b	#13,(a0)+
			move.b	#10,(a0)+
		start
			docontinue
				addq.l	#1,a1
			whileor	<cmpi.b #' ',(a1)>,eq,<cmpi.b #9,(a1)>,eq,<cmpi.b #';',(a1)>,eq
		while	<tst.b (a1)>,ne
		clr.b	(a0)
		if	<tst.b verbose_flag>,ne
			leastrdata	<'source code:',13,10>,a0
			bsr	print
			lea.l	source_code,a0		;ソースコード
			bsr	print
		endif
	endif

	.bss
source_code::
	.ds.b	1024			;ソースコード
	.text

;----------------------------------------------------------------
;テンポラリディレクトリ名を取得する
;	末尾が':'と'/'と'\'のいずれでもないとき'\'を追加する
;	'.'が2バイト文字の2バイト目でないことの確認は省略する
	if	<tst.b temp_arg>,ne
		lea.l	temp_directory,a0	;テンポラリディレクトリ名
		lea.l	temp_arg,a1		;-t ディレクトリ名
		bsr	strcpy
	else
		clr.b	temp_directory		;テンポラリディレクトリ名
		pea.l	temp_directory		;テンポラリディレクトリ名
		clr.l	-(sp)
		peastrdata	'temp'		;環境変数tempを読み出す
		DOS	_GETENV
		lea.l	12(sp),sp
	endif
	lea.l	temp_directory,a0	;テンポラリディレクトリ名
	if	<tst.b (a0)>,ne
		bsr	strchr0
		ifand	<cmpi.b #':',-1(a0)>,ne,<cmpi.b #'/',-1(a0)>,ne,<cmpi.b #'\',-1(a0)>,ne
			move.b	#'\',(a0)+
			clr.b	(a0)
		endif
	endif
	if	<tst.b verbose_flag>,ne
		lea.l	string_buffer,a0	;文字列バッファ
		leastrdata	'temporary directory: ',a1
		bsr	strcpy
		lea.l	temp_directory,a1	;テンポラリディレクトリ名
		bsr	strcpy
		bsr	crlf
		lea.l	string_buffer,a0	;文字列バッファ
		bsr	print
	endif

	.bss
temp_directory::
	.ds.b	1024			;テンポラリディレクトリ名
	.text

;----------------------------------------------------------------
;スーパーバイザモードへ移行する
	supervisormode

;----------------------------------------------------------------
;sspとuspを保存する
	move.l	sp,saved_ssp		;保存されたssp
	move.l	usp,a0
	move.l	a0,saved_usp		;保存されたusp

	.data
	.even
saved_ssp::
	.dc.l	0			;保存されたssp
saved_usp::
	.dc.l	0			;保存されたusp
	.text

;----------------------------------------------------------------
;アボートベクタを保存する
	move.w	#_CTRLVC,-(sp)
	DOS	_INTVCG
	addq.l	#2,sp
	move.l	d0,saved_ctrlvc		;保存された_CTRLVC
	move.w	#_ERRJVC,-(sp)
	DOS	_INTVCG
	addq.l	#2,sp
	move.l	d0,saved_errjvc		;保存された_ERRJVC

	.data
	.even
saved_ctrlvc::
	.dc.l	0			;保存された_CTRLVC
saved_errjvc::
	.dc.l	0			;保存された_ERRJVC
	.text

;----------------------------------------------------------------
;アボートベクタを変更する
	pea.l	abort			;中止
	move.w	#_CTRLVC,-(sp)
	DOS	_INTVCS
	addq.l	#6,sp
	pea.l	abort			;中止
	move.w	#_ERRJVC,-(sp)
	DOS	_INTVCS
	addq.l	#6,sp

;----------------------------------------------------------------
;SRAMの使用状態を確認する
	if	<tst.b sram_flag>,ne
		if	<tst.b SRAM_SRAM_USAGE>,ne
			if	<cmp.b #2,sram_flag>,lo
				leastrdata	'SRAM in use. Continue? (y/n) ',a0
				bsr	yesno
			elif	eq
				moveq.l	#'n',d0
			else
				moveq.l	#'y',d0
			endif
			goto	<cmp.b #'y',d0>,ne,abort	;中止
		endif
	endif

;----------------------------------------------------------------
;ソースファイルを作る
	bsr	create_source		;ソースファイルを作る

;----------------------------------------------------------------
;オブジェクトファイル名を作る
;	ソースファイル名の先頭を除く最後の'.'の位置なければ末尾に'.o'を書く
;	'.'が2バイト文字の2バイト目でないことの確認は省略する
	lea.l	object_file,a0		;オブジェクトファイル名
	lea.l	source_file,a1		;ソースファイル名
	bsr	strcpy
	movea.l	a0,a2			;末尾
	lea.l	object_file,a1		;先頭
	dostart
		if	<cmpi.b #'.',(a0)>,eq
			movea.l	a0,a2			;最後の'.'の位置
			break
		endif
	start
		subq.l	#1,a0
	while	<cmpa.l a1,a0>,hi	;先頭を除く
	move.b	#'.',(a2)+
	move.b	#'o',(a2)+
	clr.b	(a2)
	if	<tst.b verbose_flag>,ne
		lea.l	string_buffer,a0	;文字列バッファ
		leastrdata	'object file: ',a1
		bsr	strcpy
		lea.l	object_file,a1		;オブジェクトファイル名
		bsr	strcpy
		bsr	crlf
		lea.l	string_buffer,a0	;文字列バッファ
		bsr	print
	endif

	.bss
object_file::
	.ds.b	1024			;オブジェクトファイル名
	.text

;----------------------------------------------------------------
;コマンドラインを作る
	lea.l	command_line,a0		;コマンドライン、コマンド
	lea.l	assembler_arg,a1	;アセンブラ
	if	<tst.b (a1)>,eq
		leastrdata	'has060',a1
	endif
	bsr	strcpy
  .if WARNING_OPTION
	leastrdata	' -w -o ',a1
  .else
	leastrdata	' -o ',a1
  .endif
	bsr	strcpy
	lea.l	object_file,a1		;オブジェクトファイル名
	bsr	strcpy
	move.b	#' ',(a0)+
	lea.l	source_file,a1		;ソースファイル名
	bsr	strcpy
	if	<tst.b verbose_flag>,ne
		lea.l	string_buffer,a0	;文字列バッファ
		leastrdata	'command line: ',a1
		bsr	strcpy
		lea.l	command_line,a1		;コマンドライン、コマンド
		bsr	strcpy
		bsr	crlf
		lea.l	string_buffer,a0	;文字列バッファ
		bsr	print
	endif

	.bss
command_line::
	.ds.b	1024			;コマンドライン、コマンド
	.text

;----------------------------------------------------------------
;コマンドラインをコマンドと引数に分ける
	clr.l	-(sp)
	pea.l	command_args		;引数
	pea.l	command_line		;コマンドライン、コマンド
	move.w	#2,-(sp)
	DOS	_EXEC
	lea.l	14(sp),sp
	if	<tst.l d0>,mi
		lea.l	string_buffer,a0	;文字列バッファ
		leastrdata	'cannot find ',a1
		bsr	strcpy
		lea.l	command_line,a1		;コマンドライン、コマンド
		bsr	strcpy
		bsr	crlf
		lea.l	string_buffer,a0	;文字列バッファ
  .if ERROR_OUTPUT
		bsr	eprint
  .else
		bsr	print
  .endif
		goto	abort			;中止
	endif
	if	<tst.b verbose_flag>,ne
		lea.l	string_buffer,a0	;文字列バッファ
		leastrdata	'command: ',a1
		bsr	strcpy
		lea.l	command_line,a1		;コマンドライン、コマンド
		bsr	strcpy
		bsr	crlf
		leastrdata	'arguments: ',a1
		bsr	strcpy
		lea.l	command_args+1,a1	;引数
		bsr	strcpy
		bsr	crlf
		lea.l	string_buffer,a0	;文字列バッファ
		bsr	print
	endif

	.bss
command_args::
	.ds.b	1024			;引数
	.text

  .if HIDE_ASSEMBLER

;----------------------------------------------------------------
;NULを開く
	bsr	open_nul		;NULを開く

;----------------------------------------------------------------
;標準ハンドルを保存する
	bsr	save_std		;標準ハンドルを保存する

;----------------------------------------------------------------
;標準ハンドルをNULへリダイレクトする
;	アセンブラの表示を隠す
	clr.w	-(sp)			;標準入力ハンドル
	move.w	nul_in_handle,-(sp)	;NUL入力ハンドル
	DOS	_DUP2
	addq.l	#4,sp
	move.w	#1,-(sp)		;標準出力ハンドル
	move.w	nul_out_handle,-(sp)	;NUL出力ハンドル
	DOS	_DUP2
	addq.l	#4,sp
	move.w	#2,-(sp)		;標準エラー出力ハンドル
	move.w	nul_out_handle,-(sp)	;NUL出力ハンドル
	DOS	_DUP2
	addq.l	#4,sp

  .endif ;HIDE_ASSEMBLER

;----------------------------------------------------------------
;コマンドを実行する
	clr.l	-(sp)
	pea.l	command_args		;引数
	pea.l	command_line		;コマンドライン、コマンド
	clr.w	-(sp)
	DOS	_EXEC
	lea.l	14(sp),sp
	if	<tst.b verbose_flag>,ne
		lea.l	string_buffer,a0	;文字列バッファ
		leastrdata	'exit code: ',a1
		bsr	strcpy
		bsr	utos
		bsr	crlf
		lea.l	string_buffer,a0	;文字列バッファ
		bsr	print
	endif
	if	<tst.l d0>,mi
		lea.l	string_buffer,a0	;文字列バッファ
		leastrdata	'cannot execute ',a1
		bsr	strcpy
		lea.l	command_line,a1		;コマンドライン、コマンド
		bsr	strcpy
		bsr	crlf
		lea.l	string_buffer,a0	;文字列バッファ
  .if ERROR_OUTPUT
		bsr	eprint
  .else
		bsr	print
  .endif
		goto	abort			;中止
	endif
	st.b	object_created		;-1=オブジェクトファイル作成済み

	.data
object_created::
	.dc.b	0			;-1=オブジェクトファイル作成済み
	.text

  .if HIDE_ASSEMBLER

;----------------------------------------------------------------
;標準ハンドルを復元する
	bsr	restore_std		;標準ハンドルを復元する

;----------------------------------------------------------------
;NULを閉じる
	bsr	close_nul		;NULを閉じる

  .endif ;HIDE_ASSEMBLER

;----------------------------------------------------------------
;ソースファイルを消す
	bsr	delete_source		;ソースファイルを消す

;----------------------------------------------------------------
;オブジェクトファイルを開く
	bsr	open_object		;オブジェクトファイルを開く

;----------------------------------------------------------------
;SRAMの内容を保存する
	bsr	save_sram		;SRAMの内容を保存する

;----------------------------------------------------------------
;コードの先頭アドレスを決める
	if	<tst.b sram_flag>,ne
		lea.l	SRAM_PROGRAM_START,a0	;SRAM常駐プログラムの先頭
	else
		lea.l	code_buffer,a0		;コードバッファ
	endif
	move.l	a0,d0
	add.l	#256-1,d0		;256の倍数に切り上げる
	and.w	#-256,d0
	movea.l	d0,a0
	move.l	a0,free_start		;空き領域の先頭アドレス
	lea.l	268(a0),a0
	move.l	a0,code_start		;コードの先頭アドレス
	addq.l	#4,a0
	move.l	a0,loop_start		;ループの先頭アドレス
	lea.l	32766(a0),a0
	if	<tst.b sram_flag>,ne
		if	<cmpa.l #SRAM_END-6,a0>,hi
			lea.l	SRAM_END-6,a0		;SRAMの末尾-6
		endif
	endif
	move.l	a0,loop_end_limit	;ループの末尾アドレスの上限
;	addq.l	#6,a0
;	move.l	a0,code_end_limit	;コードの末尾アドレスの上限


	.bss
	.even
free_start::
	.ds.l	1			;空き領域の先頭アドレス
code_start::
	.ds.l	1			;コードの先頭アドレス
loop_start::
	.ds.l	1			;ループの先頭アドレス
loop_end_limit::
	.ds.l	1			;ループの末尾アドレスの上限
;code_end_limit::
;	.ds.l	1			;コードの末尾アドレスの上限
code_buffer::
	.ds.b	256+268+4+32766+6	;コードバッファ
	.text

;----------------------------------------------------------------
;空き領域をゼロクリアする
	if	<tst.b sram_flag>,ne
		unlocksram			;SRAM書き込み許可
	endif
	movea.l	free_start,a0		;空き領域の先頭アドレス
	movea.l	code_start,a1		;コードの先頭アドレス
	moveq.l	#0,d0
	do
		move.l	d0,(a0)+
	while	<cmpa.l a1,a0>,lo
;	if	<tst.b sram_flag>,ne
;		locksram			;SRAM書き込み禁止
;	endif

;----------------------------------------------------------------
;オブジェクトファイルを読み込んで命令列ループを作る
;	if	<tst.b sram_flag>,ne
;		unlocksram			;SRAM書き込み許可
;	endif
	movea.l	code_start,a0		;コードの先頭アドレス
	move.l	#$3E3C0000,(a0)+	;move.w #1-1,d7
	movea.l	a0,a3			;loop_start
	moveq.l	#0,d3			;0=失敗,1=成功
	do
		bsr	object_word
		break	mi
		if	eq			;終了
			move.w	#$51CF,(a0)+		;dbra d7,(loop_start)
			move.w	a3,d0
			sub.w	a0,d0
			move.w	d0,(a0)+
			move.w	#$4E75,(a0)+		;rts
		;	bsr	cache_flush		;キャッシュフラッシュ
			move.l	a0,code_end		;コードの末尾アドレス
			moveq.l	#1,d3			;成功
			break
		elifand	<cmp.w #$1000,d0>,hs,<cmp.w #$10FF,d0>,ls	;.dcまたは命令
			and.w	#$00FF,d0
			addq.w	#1,d0			;長さ
			lea.l	(a0,d0.w),a1		;末尾
			break	<cmpa.l loop_end_limit,a1>,hi	;textセクションが溢れた。失敗
			do
				bsr	object_byte
				break2	mi
				move.b	d0,(a0)+
			while	<cmpa.l a1,a0>,lo
			move.w	a1,d0
			if	<btst.l #0,d0>,ne	;長さが奇数
				bsr	object_byte
				break	mi
			endif
		elif	<cmp.w #$2001,d0>,eq	;textセクション
			bsr	object_long
			break	mi
		elif	<cmp.w #$2002,d0>,eq	;dataセクション
			break	t			;dataセクションがある。失敗
		elif	<cmp.w #$2003,d0>,eq	;bssセクション
			break	t			;bssセクションがある。失敗
		elif	<cmp.w #$2004,d0>,eq	;stackセクション
			break	t			;stackセクションがある。失敗
		elif	<cmp.w #$3000,d0>,eq	;.ds
			bsr	object_long		;長さ
			break	mi
			lea.l	(a0,d0.l),a1		;末尾
			break	<cmpa.l loop_end_limit,a1>,hi	;textセクションが溢れた。失敗
			do
				clr.b	(a0)+
			while	<cmpa.l a1,a0>,lo
		elif	<cmp.w #$4201,d0>,eq	;textセクションの先頭からのオフセット
			bsr	object_long		;オフセット
			break	mi
			lea.l	4(a0),a1		;末尾
			break	<cmpa.l loop_end_limit,a1>,hi	;textセクションが溢れた。失敗
			add.l	a3,d0			;リロケートしたアドレス
			moveq.l	#4-1,d1
			for	d1
				rol.l	#8,d0
				move.b	d0,(a0)+
			next
		elif	<cmp.w #$C001,d0>,eq	;textセクションの情報
			bsr	object_long		;textセクションの長さ
			break	mi
			break	eq			;textセクションがない。失敗
			bsr	object_string
			break	mi
		elif	<cmp.w #$C002,d0>,eq	;.dataの情報
			bsr	object_long		;.dataの長さ
			break	mi
			break	ne			;.dataがある。失敗
			bsr	object_string
			break	mi
		elif	<cmp.w #$C003,d0>,eq	;.bssの情報
			bsr	object_long		;.bssの長さ
			break	mi
			break	ne			;.bssがある。失敗
			bsr	object_string
			break	mi
		elif	<cmp.w #$C004,d0>,eq	;.stackの情報
			bsr	object_long		;.stackの長さ
			break	mi
			break	ne			;.stackがある。失敗
			bsr	object_string
			break	mi
		elif	<cmp.w #$D000,d0>,eq	;ファイル情報
			bsr	object_long
			break	mi
			bsr	object_string
			break	mi
		else				;その他
			break	t			;失敗
		endif
	while	t
	if	<tst.b sram_flag>,ne
		locksram			;SRAM書き込み禁止
	endif
	if	<tst.l d3>,eq		;失敗
		lea.l	string_buffer,a0	;文字列バッファ
		leastrdata	'cannot process ',a1
		bsr	strcpy
	;	lea.l	object_file,a1		;オブジェクトファイル名
		if	<tst.b inst_arg>,ne
			move.b	#'"',(a0)+
			lea.l	inst_arg,a1		;-i 命令列
			bsr	strcpy
			move.b	#'"',(a0)+
		else
			lea.l	file_arg,a1		;-f ファイル名
			bsr	strcpy
		endif
		bsr	crlf
		lea.l	string_buffer,a0	;文字列バッファ
  .if ERROR_OUTPUT
		bsr	eprint
  .else
		bsr	print
  .endif
		goto	abort			;中止
	endif

	.bss
	.even
code_end::
	.ds.l	1			;コードの末尾アドレス
	.text

;----------------------------------------------------------------
;オブジェクトファイルを閉じる
	bsr	close_object		;オブジェクトファイルを閉じる

;----------------------------------------------------------------
;オブジェクトファイルを消す
	bsr	delete_object		;オブジェクトファイルを消す

;----------------------------------------------------------------
;命令列ループを計測する
	move.l	#1,repeat_count		;繰り返し回数
	do
		if	<tst.b verbose_flag>,ne
			lea.l	string_buffer,a0	;文字列バッファ
			leastrdata	'repeat count: ',a1
			bsr	strcpy
			move.l	repeat_count,d0		;繰り返し回数
			bsr	utos
			bsr	crlf
			lea.l	string_buffer,a0	;文字列バッファ
			bsr	print
		endif
		if	<tst.b sram_flag>,ne
			unlocksram			;SRAM書き込み許可
		endif
		move.l	repeat_count,d0		;繰り返し回数
		subq.w	#1,d0			;繰り返し回数-1
		movea.l	code_start,a0		;コードの先頭アドレス
		move.w	d0,2(a0)		;DBRAを書き換える
		bsr	cache_flush		;キャッシュフラッシュ
		if	<tst.b sram_flag>,ne
			locksram			;SRAM書き込み禁止
		endif
		if	<tst.b verbose_flag>,ne
			leastrdata	<'instructions loop:',13,10>,a0
			bsr	print
			bsr	dump_code		;コードをダンプする
		endif
		if	<tst.b sram_flag>,ne
			unlocksram			;SRAM書き込み許可
		endif
		movea.l	code_start,a0		;コードの先頭アドレス
		bsr	optime			;計測
		move.l	d0,inst_loop_time	;命令列ループの所要時間
		if	<tst.b sram_flag>,ne
			locksram			;SRAM書き込み禁止
		endif
		if	<tst.b verbose_flag>,ne
			lea.l	string_buffer,a0	;文字列バッファ
			leastrdata	'instructions loop time: ',a1
			bsr	strcpy
			move.l	inst_loop_time,d0	;命令列ループの所要時間
			moveq.l	#1,d1
			bsr	time_to_string
			bsr	crlf
			lea.l	string_buffer,a0	;文字列バッファ
			bsr	print
		endif
		break	<cmpi.l #1200,inst_loop_time>,hs	;1200us以上ならば終了
		break	<cmpi.l #10000,repeat_count>,eq	;10000回ならば終了
		move.l	repeat_count,d0
		mulu.w	#10,d0			;回数を10倍にする
		move.l	d0,repeat_count
	while	t

	.bss
	.even
repeat_count::
	.ds.l	1			;繰り返し回数
inst_loop_time::
	.ds.l	1			;命令列ループの所要時間
	.text

;----------------------------------------------------------------
;空ループを作る
	if	<tst.b sram_flag>,ne
		unlocksram			;SRAM書き込み許可
	endif
	movea.l	code_start,a0		;コードの先頭アドレス
	move.w	#$3E3C,(a0)+		;move.w #回数-1,d7
	move.l	repeat_count,d0
	subq.w	#1,d0
	move.w	d0,(a0)+
	move.l	#$51CFFFFE,(a0)+	;dbra d7,(loop_start)
	move.w	#$4E75,(a0)+		;rts
	bsr	cache_flush		;キャッシュフラッシュ
	move.l	a0,code_end
	if	<tst.b sram_flag>,ne
		locksram			;SRAM書き込み禁止
	endif
	if	<tst.b verbose_flag>,ne
		leastrdata	<'null loop:',13,10>,a0
		bsr	print
		bsr	dump_code		;コードをダンプする
	endif

;----------------------------------------------------------------
;空ループを計測する
	movea.l	code_start,a0		;コードの先頭アドレス
	bsr	optime			;計測
	move.l	d0,null_loop_time	;空ループの所要時間
	if	<tst.b verbose_flag>,ne
		lea.l	string_buffer,a0	;文字列バッファ
		leastrdata	'null loop time: ',a1
		bsr	strcpy
		move.l	null_loop_time,d0	;空ループの所要時間
		moveq.l	#1,d1
		bsr	time_to_string
		bsr	crlf
		lea.l	string_buffer,a0	;文字列バッファ
		bsr	print
	endif

	.bss
	.even
null_loop_time::
	.ds.l	1			;空ループの所要時間
	.text

;----------------------------------------------------------------
;SRAMの内容を復元する
	bsr	restore_sram		;SRAMの内容を復元する

;----------------------------------------------------------------
;コードの所要時間を表示する
	lea.l	string_buffer,a0	;文字列バッファ
	if	<tst.b inst_arg>,ne
		move.b	#'"',(a0)+
		lea.l	inst_arg,a1		;-i 命令列
		bsr	strcpy
		move.b	#'"',(a0)+
	else
		lea.l	file_arg,a1		;-f ファイル名
		bsr	strcpy
	endif
	move.b	#9,(a0)+
	move.l	inst_loop_time,d0	;命令列ループの所要時間
	sub.l	null_loop_time,d0	;命令列ループの所要時間-空ループの所要時間=命令列の所要時間
	if	lt
		moveq.l	#0,d0
	endif
	move.l	repeat_count,d1
	bsr	time_to_string
	bsr	crlf
	lea.l	string_buffer,a0	;文字列バッファ
	bsr	print

;----------------------------------------------------------------
;正常終了
	clr.w	exit_code		;終了コード

	.data
	.even
exit_code::
	.dc.w	1			;終了コード
	.text

;----------------------------------------------------------------
;中止
abort::

;----------------------------------------------------------------
;sspとuspを復元する
	if	<tst.l saved_ssp>,ne
		movea.l	saved_ssp,sp		;保存されたssp
		clr.l	saved_ssp		;保存されたssp
	endif
	if	<tst.l saved_usp>,ne
		move.l	saved_usp,a0		;保存されたusp
		move.l	a0,usp
		clr.l	saved_usp		;保存されたusp
	endif

;----------------------------------------------------------------
;アボートベクタを復元する
	if	<tst.l saved_ctrlvc>,ne
		move.l	saved_ctrlvc,-(sp)	;保存された_CTRLVC
		move.w	#_CTRLVC,-(sp)
		DOS	_INTVCS
		addq.l	#6,sp
		clr.l	saved_ctrlvc		;保存された_CTRLVC
	endif
	if	<tst.l saved_errjvc>,ne
		move.l	saved_errjvc,-(sp)	;保存された_ERRJVC
		move.w	#_ERRJVC,-(sp)
		DOS	_INTVCS
		addq.l	#6,sp
		clr.l	saved_errjvc		;保存された_ERRJVC
	endif

;----------------------------------------------------------------
;タイマを復元する
	bsr	restore_timer		;タイマを復元する

  .if HIDE_ASSEMBLER

;----------------------------------------------------------------
;標準ハンドルを復元する
	bsr	restore_std		;標準ハンドルを復元する

;----------------------------------------------------------------
;NULを閉じる
	bsr	close_nul		;NULを閉じる

  .endif ;HIDE_ASSEMBLER

;----------------------------------------------------------------
;ソースファイルを消す
	bsr	delete_source		;ソースファイルを消す

;----------------------------------------------------------------
;オブジェクトファイルを閉じる
	bsr	close_object		;オブジェクトファイルを閉じる

;----------------------------------------------------------------
;オブジェクトファイルを消す
	bsr	delete_object		;オブジェクトファイルを消す

;----------------------------------------------------------------
;SRAMの内容を復元する
	bsr	restore_sram		;SRAMの内容を復元する

;----------------------------------------------------------------
;ユーザモードへ復帰する
	usermode

;----------------------------------------------------------------
;終了する
	move.w	exit_code,-(sp)		;終了コード
	DOS	_EXIT2


;----------------------------------------------------------------
;タイトルと使用法を表示して終了する
usage_exit::
	lea.l	title_usage,a0		;タイトルと使用法
  .if ERROR_OUTPUT
	bsr	eprint
  .else
	bsr	print
  .endif
	move.w	#1,-(sp)
	DOS	_EXIT2

	.data
title_usage::
	.dc.b	TITLE_STRING,13,10
	.dc.b	'Measure the execution time of instructions',13,10
	.dc.b	'  -a has060  Assembler',13,10
	.dc.b	'  -f nop.s   File containing instructions',13,10
	.dc.b	'  -i "nop"   Semicolon-delimited instructions',13,10
	.dc.b	'  -s         Measure in SRAM. Ask when SRAM in use',13,10
	.dc.b	'  -sn        Same as -s but not measure when SRAM in use',13,10
	.dc.b	'  -sy        Same as -s but measure even when SRAM in use',13,10
	.dc.b	'  -t %temp%  Temporary directory',13,10
	.dc.b	'  -v         Output verbose messages',13,10
	.dc.b	0			;タイトルと使用法
	.text


;----------------------------------------------------------------
;1回の時間を文字列に変換する
;<d0.l:時間(us)。0~12799
;<d1.w:回数。1,10,100,1000,10000のいずれか
;<a0.l:バッファ
;>a0.l:文字列の末尾の0の位置
time_to_string::
	push	d0-d3/a1-a2
	movea.l	a0,a1
;<a1.l:先頭
	bsr	utos
;<a0.l:末尾
	move.w	a0,d0
	sub.w	a1,d0
;<d0.w:全体の桁数。0usは1桁
	moveq.l	#0,d2
	moveq.l	#1,d3
	docontinue
		addq.w	#1,d2
		mulu.w	#10,d3
	while	<cmp.w d3,d1>,hi
;<d2.w:小数部の桁数。0以上
	move.w	d0,d1
	sub.w	d2,d1
;<d1.w:整数部の桁数
	moveq.l	#'u',d3
	if	<cmp.w #3,d1>,gt	;usの整数部が4桁以上ある。msにする
		addq.w	#3,d2
		subq.w	#3,d1
		moveq.l	#'m',d3
	endif
;<d3.b:単位。'u'または'm'
	if	<tst.w	d1>,le		;整数部がない。(a1)に'0.'と'0'を-d1個挿入する
		neg.w	d1
		lea.l	2(a1,d1.w),a2		;(a1)の移動先
		lea.l	(a2,d0.w),a0		;新しい末尾
		for	d0
			move.b	(a1,d0.w),(a2,d0.w)	;末尾の0と全体をずらす
		next
		move.b	#'0',(a1)+
		move.b	#'.',(a1)+
		forcontinue	d1
			move.b	#'0',(a1)+
		next
	elif	<tst.w	d2>,gt		;小数部がある。(a1,d1.w)に'.'を挿入する
		adda.w	d1,a1			;'.'を挿入する位置
		lea.l	1(a1),a2		;(a1)の移動先
		addq.l	#1,a0			;新しい末尾
		for	d2
			move.b	(a1,d2.w),(a2,d2.w)	;末尾の0と小数部をずらす
		next
		move.b	#'.',(a1)+
	endif
	move.b	#' ',(a0)+
	move.b	d3,(a0)+
	move.b	#'s',(a0)+
	clr.b	(a0)
	pop
	rts


  .if HIDE_ASSEMBLER

;----------------------------------------------------------------
;標準ハンドルを保存する
save_std::
	clr.w	-(sp)			;標準入力ハンドル
	DOS	_DUP
	addq.l	#2,sp
	move.w	d0,saved_stdin		;保存された標準入力ハンドル
	move.w	#1,-(sp)		;標準出力ハンドル
	DOS	_DUP
	addq.l	#2,sp
	move.w	d0,saved_stdout		;保存された標準出力ハンドル
	move.w	#2,-(sp)		;標準エラー出力ハンドル
	DOS	_DUP
	addq.l	#2,sp
	move.w	d0,saved_stderr		;保存された標準エラー出力ハンドル
	rts

	.data
	.even
saved_stdin::
	.dc.w	-1			;保存された標準入力ハンドル
saved_stdout::
	.dc.w	-1			;保存された標準出力ハンドル
saved_stderr::
	.dc.w	-1			;保存された標準エラー出力ハンドル
	.text

;----------------------------------------------------------------
;標準ハンドルを復元する
restore_std::
	if	<cmpi.w #-1,saved_stdin>,ne
		clr.w	-(sp)			;標準入力ハンドル
		move.w	saved_stdin,-(sp)	;保存された標準入力ハンドル
		DOS	_DUP2
		DOS	_CLOSE
		addq.l	#4,sp
		move.w	#-1,saved_stdin		;保存された標準入力ハンドル
	endif
	if	<cmpi.w #-1,saved_stdout>,ne
		move.w	#1,-(sp)		;標準出力ハンドル
		move.w	saved_stdout,-(sp)	;保存された標準出力ハンドル
		DOS	_DUP2
		DOS	_CLOSE
		addq.l	#4,sp
		move.w	#-1,saved_stdout	;保存された標準出力ハンドル
	endif
	if	<cmpi.w #-1,saved_stderr>,ne
		move.w	#2,-(sp)		;標準エラー出力ハンドル
		move.w	saved_stderr,-(sp)	;保存された標準エラー出力ハンドル
		DOS	_DUP2
		DOS	_CLOSE
		addq.l	#4,sp
		move.w	#-1,saved_stderr	;保存された標準エラー出力ハンドル
	endif
	rts

;----------------------------------------------------------------
;NULを開く
open_nul::
	clr.w	-(sp)			;入力
	peastrdata	'nul'
	DOS	_OPEN
	addq.l	#6,sp
	move.w	d0,nul_in_handle	;NUL入力ハンドル
	move.w	#1,-(sp)		;出力
	peastrdata	'nul'
	DOS	_OPEN
	addq.l	#6,sp
	move.w	d0,nul_out_handle	;NUL出力ハンドル
	rts

	.data
	.even
nul_in_handle::
	.dc.w	-1			;NUL入力ハンドル
nul_out_handle::
	.dc.w	-1			;NUL出力ハンドル
	.text

;----------------------------------------------------------------
;NULを閉じる
close_nul::
	if	<cmpi.w #-1,nul_in_handle>,ne
		move.w	nul_in_handle,-(sp)		;NUL入力ハンドル
		DOS	_CLOSE
		addq.l	#2,sp
		move.w	#-1,nul_in_handle		;NUL入力ハンドル
	endif
	if	<cmpi.w #-1,nul_out_handle>,ne
		move.w	nul_out_handle,-(sp)		;NUL出力ハンドル
		DOS	_CLOSE
		addq.l	#2,sp
		move.w	#-1,nul_out_handle		;NUL出力ハンドル
	endif
	rts

  .endif ;HIDE_ASSEMBLER

;----------------------------------------------------------------
;ソースファイルを作る
create_source::
	if	<tst.b inst_arg>,ne
		lea.l	source_file,a0		;ソースファイル名
		lea.l	temp_directory,a1	;テンポラリディレクトリ名
		bsr	strcpy
		leastrdata	'opti????.s',a1
		bsr	strcpy
		move.w	#$0020,-(sp)
		pea.l	source_file		;ソースファイル名
		DOS	_MAKETMP
		addq.l	#6,sp
		move.l	d0,d1
		if	mi
			lea.l	string_buffer,a0	;文字列バッファ
			leastrdata	'cannot create ',a1
			bsr	strcpy
			lea.l	source_file,a1		;ソースファイル名
			bsr	strcpy
			bsr	crlf
			lea.l	string_buffer,a0	;文字列バッファ
  .if ERROR_OUTPUT
			bsr	eprint
  .else
			bsr	print
  .endif
			goto	abort			;中止
		endif
		st.b	source_created		;ソースファイル作成済みフラグ
		move.w	d1,-(sp)
		pea.l	source_code		;ソースコード
		DOS	_FPUTS
		addq.l	#6,sp
		move.w	d1,-(sp)
		DOS	_CLOSE
		addq.l	#2,sp
	else
		lea.l	source_file,a0		;ソースファイル名
		lea.l	file_arg,a1		;-f ファイル名
		bsr	strcpy
	endif
	if	<tst.b verbose_flag>,ne
		lea.l	string_buffer,a0	;文字列バッファ
		leastrdata	'source file: ',a1
		bsr	strcpy
		lea.l	source_file,a1		;ソースファイル名
		bsr	strcpy
		bsr	crlf
		lea.l	string_buffer,a0	;文字列バッファ
		bsr	print
	endif
	rts

	.data
source_created::
	.dc.b	0			;ソースファイル作成済みフラグ
	.bss
source_file::
	.ds.b	1024			;ソースファイル名
	.text

;----------------------------------------------------------------
;ソースファイルを消す
delete_source::
	if	<tst.b source_created>,ne
		pea.l	source_file		;ソースファイル名
		DOS	_DELETE
		addq.l	#4,sp
		sf.b	source_created		;ソースファイル作成済みフラグ
	endif
	rts

;----------------------------------------------------------------
;オブジェクトファイルを開く
open_object::
	clr.w	-(sp)
	pea.l	object_file		;オブジェクトファイル名
	DOS	_OPEN
	addq.l	#6,sp
	if	<tst.l d0>,mi
		lea.l	string_buffer,a0	;文字列バッファ
		leastrdata	'cannot open ',a1
		bsr	strcpy
		lea.l	object_file,a1		;オブジェクトファイル名
		bsr	strcpy
		bsr	crlf
		lea.l	string_buffer,a0	;文字列バッファ
  .if ERROR_OUTPUT
		bsr	eprint
  .else
		bsr	print
  .endif
		goto	abort			;中止
	endif
	move.w	d0,object_handle
	rts

	.data
	.even
object_handle::
	.dc.w	-1			;オブジェクトファイルハンドル
	.text

;----------------------------------------------------------------
;オブジェクトファイルを閉じる
close_object::
	if	<cmpi.w #-1,object_handle>,ne
		move.w	object_handle,-(sp)	;オブジェクトファイルハンドル
		DOS	_CLOSE
		addq.l	#2,sp
		move.w	#-1,object_handle	;オブジェクトファイルハンドル
	endif
	rts

;----------------------------------------------------------------
;オブジェクトファイルを消す
delete_object::
	if	<tst.b object_created>,ne
		pea.l	object_file		;オブジェクトファイル名
		DOS	_DELETE
		addq.l	#4,sp
		sf.b	object_created		;-1=オブジェクトファイル作成済み
	endif
	rts

;----------------------------------------------------------------
;SRAMの内容を保存する
save_sram::
	if	<tst.b sram_flag>,ne
		lea.l	sram_data,a0		;保存されたSRAMデータ
		lea.l	$00ED0100,a1
		move.w	#$3F00/4-1,d0
		for	d0
			move.l	(a1)+,(a0)+
		next
		st.b	sram_saved		;SRAM保存済みフラグ
	endif
	rts

	.data
sram_saved::
	.dc.b	0			;SRAM保存済みフラグ
	.bss
	.even
sram_data::
	.ds.l	$3F00/4			;保存されたSRAMデータ
	.text

;----------------------------------------------------------------
;SRAMの内容を復元する
restore_sram::
	if	<tst.b sram_saved>,ne
		unlocksram			;SRAM書き込み許可
		lea.l	$00ED0100,a0
		lea.l	sram_data,a1		;保存されたSRAMデータ
		move.w	#$3F00/4-1,d0
		for	d0
			move.l	(a1)+,(a0)+
		next
		locksram			;SRAM書き込み禁止
		sf.b	sram_saved		;SRAM保存済みフラグ
	endif
	rts


;----------------------------------------------------------------
;コードをダンプする
dump_code::
	push	d0/a0-a4
	movea.l	code_start,a3		;コードの先頭アドレス
	move.l	a3,d0
	and.w	#-16,d0			;16の倍数に切り捨てる
	movea.l	d0,a1			;表示開始アドレス
	movea.l	code_end,a4		;コードの末尾アドレス
	move.l	a4,d0
	add.l	#16-1,d0		;16の倍数に切り上げる
	and.w	#-16,d0
	movea.l	d0,a2			;表示終了アドレス
	docontinue
		move.l	a1,d0
		and.w	#16-1,d0
		if	eq
			lea.l	string_buffer,a0	;文字列バッファ
			move.l	a1,d0
			bsr	h8tos
			move.b	#' ',(a0)+
		endif
		move.b	#' ',(a0)+
		ifand	<cmpa.l a3,a1>,hs,<cmpa.l a4,a1>,lo	;コードの範囲内
			move.w	(a1),d0
			bsr	h4tos
		else				;コードの範囲外
			move.b	#'.',(a0)+
			move.b	#'.',(a0)+
			move.b	#'.',(a0)+
			move.b	#'.',(a0)+
		endif
		addq.l	#2,a1
		move.l	a1,d0
		and.w	#16-1,d0
		if	eq
			bsr	crlf
			lea.l	string_buffer,a0	;文字列バッファ
			bsr	print
		endif
	while	<cmpa.l a2,a1>,lo
	pop
	rts


;----------------------------------------------------------------
;計測サブルーチン
;	スーパーバイザモードで呼び出すこと
;<a0.l:計測するサブルーチン。d0-d7/a0-a4を自由に使える
;>d0.l:所要時間(us)。0~12799
optime::
	push	d1-d7/a0-a6
	movea.l	a0,a5
aTCDCR	reg	a6
	lea.l	MFP_TCDCR,aTCDCR
;割り込み禁止
	di
;タイマ保存
	lea.l	timer_ierb(pc),a0
	move.b	MFP_IERB-MFP_TCDCR(aTCDCR),(a0)	;timer_ierb
	move.b	MFP_IMRB-MFP_TCDCR(aTCDCR),-(a0)	;timer_imrb
	move.b	MFP_TCDCR-MFP_TCDCR(aTCDCR),-(a0)	;timer_tcdcr
	st.b	-(a0)			;timer_saved
;タイマ設定
	andi.b	#.notb.(MFP_B_TIMERC_MASK|MFP_B_TIMERD_MASK),MFP_IERB-MFP_TCDCR(aTCDCR)	;Timer-C/D割り込み停止
	andi.b	#.notb.(MFP_B_TIMERC_MASK|MFP_B_TIMERD_MASK),MFP_IMRB-MFP_TCDCR(aTCDCR)	;Timer-C/D割り込み禁止
	move.b	#0,(aTCDCR)		;Timer-C/Dカウント停止
	do
	while	<tst.b (aTCDCR)>,ne	;完全に停止するまで待つ
;リハーサルと本番
	clr.b	-(sp)			;$00=リハーサル,$FF=本番
	do
	;カウント開始
		move.b	#0,MFP_TCDR-MFP_TCDCR(aTCDCR)	;Timer-Cカウンタクリア
		move.b	#0,MFP_TDDR-MFP_TCDCR(aTCDCR)	;Timer-Dカウンタクリア
		move.b	#MFP_50US<<4|MFP_1US,(aTCDCR)	;Timer-C/Dカウント開始
						;Timer-Cは1/200プリスケール(50us)
						;Timer-Dは1/4プリスケール(1us)
	;計測
		jsr	(a5)
	;カウント停止
		move.b	#0,(aTCDCR)		;Timer-C/Dカウント停止
		do
		while	<tst.b (aTCDCR)>,ne	;完全に停止するまで待つ
		not.b	(sp)
	while	ne
	tst.b	(sp)+
;タイマ取得
	moveq.l	#0,d0
	moveq.l	#0,d1
	sub.b	MFP_TCDR-MFP_TCDCR(aTCDCR),d0	;Timer-Cカウント数
	sub.b	MFP_TDDR-MFP_TCDCR(aTCDCR),d1	;Timer-Dカウント数(オーバーフローあり)
;タイマ復元
	lea.l	timer_saved(pc),a0
	move.b	#200,MFP_TCDR-MFP_TCDCR(aTCDCR)	;Timer-Cカウンタ復元
	move.b	#0,MFP_TDDR-MFP_TCDCR(aTCDCR)	;Timer-Dカウンタクリア
	sf.b	(a0)+				;timer_saved
	move.b	(a0)+,(aTCDCR)			;timer_tcdcr
	move.b	(a0)+,MFP_IMRB-MFP_TCDCR(aTCDCR)	;timer_imrb
	move.b	(a0),MFP_IERB-MFP_TCDCR(aTCDCR)	;timer_ierb
;割り込み許可
	ei
;カウンタ合成
	mulu.w	#50,d0
	if	<cmp.b d1,d0>,hi
		add.w	#256,d0
	endif
	move.b	d1,d0
	move.l	d0,d1
;終了
	move.l	d1,d0
	pop
	rts

;タイマを復元する
restore_timer::
	push	a0/a6
	lea.l	MFP_TCDCR,aTCDCR
	lea.l	timer_saved(pc),a0
	if	<tst.b (a0)>,ne
		di
		move.b	#200,MFP_TCDR-MFP_TCDCR(aTCDCR)	;Timer-Cカウンタ復元
		move.b	#0,MFP_TDDR-MFP_TCDCR(aTCDCR)	;Timer-Dカウンタクリア
		sf.b	(a0)+				;timer_saved
		move.b	(a0)+,(aTCDCR)			;timer_tcdcr
		move.b	(a0)+,MFP_IMRB-MFP_TCDCR(aTCDCR)	;timer_imrb
		move.b	(a0),MFP_IERB-MFP_TCDCR(aTCDCR)	;timer_ierb
		ei
	endif
	pop
	rts

	.data
timer_saved::
	.dc.b	0			;-1=以下保存済み
timer_tcdcr::
	.dc.b	0			;保存されたTCDCR
timer_imrb::
	.dc.b	0			;保存されたIMRB
timer_ierb::
	.dc.b	0			;保存されたIERB
	.text


;----------------------------------------------------------------
;オブジェクトファイルの文字列を読み飛ばす
;>d0.l:読み飛ばしたバイト数。0を含む。偶数
object_string::
	push	d1
	moveq.l	#0,d1
	do
		do
			addq.l	#1,d1
			bsr	object_byte
			break2	mi
		while	ne
		if	<btst.l #0,d1>,ne
			addq.l	#1,d1
			bsr	object_byte
			break	mi
		endif
		move.l	d1,d0
	while	f
	pop
	tst.l	d0
	rts

;----------------------------------------------------------------
;オブジェクトファイルから1ロングワード読み出す
;>d0.l:読み出したデータ
object_long::
	clr.l	-(sp)
	do
		bsr	object_byte
		break	mi
		move.b	d0,(sp)
		bsr	object_byte
		break	mi
		move.b	d0,1(sp)
		bsr	object_byte
		break	mi
		move.b	d0,2(sp)
		bsr	object_byte
		break	mi
		move.b	d0,3(sp)
		move.l	(sp),d0
	while	f
	addq.l	#4,sp
	rts

;----------------------------------------------------------------
;オブジェクトファイルから1ワード読み出す
;>d0.l:読み出したデータ
object_word::
	clr.l	-(sp)
	do
		bsr	object_byte
		break	mi
		move.b	d0,2(sp)
		bsr	object_byte
		break	mi
		move.b	d0,3(sp)
		move.l	(sp),d0
	while	f
	addq.l	#4,sp
	rts

;----------------------------------------------------------------
;オブジェクトファイルから1バイト読み出す
;>d0.l:読み出したデータ
object_byte::
	move.w	object_handle,-(sp)
	DOS	_FGETC
	addq.l	#2,sp
	if	<tst.l d0>,pl
		and.l	#$000000FF,d0
	endif
	rts


;----------------------------------------------------------------
;引数をコピーする
;	空白を読み飛ばしてから次の空白の手前までコピーする
;	"~"または'~'で囲むと引数に空白を含めることができる
;	""または''と書くと長さが0の引数を与えることができる
;<a0.l:コピー先のバッファの先頭
;<a1.l:コピー元の文字列の先頭
;>d0.l:0=引数がない,1=引数がある
;>a0.l:コピー先の文字列の末尾の0の位置
;>a1.l:コピー元の引数の直後。なければコピー元の文字列の末尾の0の位置
;>eq=引数がない,ne=引数がある
argcpy::
	exg.l	a0,a1
	bsr	nonspace		;空白を読み飛ばす
	exg.l	a0,a1
	if	eq			;引数がない
		clr.b	(a0)
		moveq.l	#0,d0
		rts
	endif
	dostart
		if	<cmp.b #'"',d0>,eq	;"~"
			dostart
				move.b	d0,(a0)+		;書き込む
			start
				move.b	(a1)+,d0		;次の文字
				break2	eq			;引数が終わった
			while	<cmp.b #'"',d0>,ne
		elif	<cmp.b #39,d0>,eq	;'~'
			dostart
				move.b	d0,(a0)+		;書き込む
			start
				move.b	(a1)+,d0		;次の文字
				break2	eq			;引数が終わった
			while	<cmp.b #$39,d0>,ne
		else
			move.b	d0,(a0)+		;書き込む
		endif
	start
		move.b	(a1)+,d0		;次の文字
		break	eq			;引数が終わった
		breakand	<cmp.b #9,d0>,hs,<cmp.b #13,d0>,ls	;\t\n\v\f\rならば終了
	while	<cmp.b #' ',d0>,ne	;空白でなければ繰り返す
	subq.l	#1,a1			;進み過ぎた分戻る
	clr.b	(a0)
	moveq.l	#1,d0
	rts

;----------------------------------------------------------------
;キャッシュフラッシュ
cache_flush::
	push	d0-d1
	if	<is68000 d0>,ne
		moveq.l	#3,d1
		IOCS	_SYS_STAT
	endif
	pop
	rts

;----------------------------------------------------------------
;改行をコピーする
;<a0.l:コピー先
;>a0.l:コピー先の0の位置
crlf::
	move.b	#13,(a0)+
	move.b	#10,(a0)+
	clr.b	(a0)
	rts

  .if ERROR_OUTPUT
;----------------------------------------------------------------
;文字列をエラー表示する
;<a0.l:文字列の先頭
;>a0.l:文字列の先頭
eprint::
	push	d0
	bsr	strlen
	move.l	d0,-(sp)
	move.l	a0,-(sp)
	move.w	#2,-(sp)
	DOS	_WRITE
	lea.l	(10,sp),sp
	pop
	rts
  .endif

;----------------------------------------------------------------
;符号なし整数を16進数4桁の文字列に変換する
;<d0.w:符号なし整数
;<a0.l:バッファ
;>a0.l:バッファの0の位置
h4tos::
	push	d1-d2
	moveq.l	#4-1,d1
	for	d1
		rol.w	#4,d0
		moveq.l	#15,d2
		and.w	d0,d2
		move.b	h8tos_hex(pc,d2.w),(a0)+
	next
	clr.b	(a0)
	pop
	rts

;----------------------------------------------------------------
;符号なし整数を16進数8桁の文字列に変換する
;<d0.l:符号なし整数
;<a0.l:バッファ
;>a0.l:バッファの0の位置
h8tos::
	push	d1-d2
	moveq.l	#8-1,d1
	for	d1
		rol.l	#4,d0
		moveq.l	#15,d2
		and.w	d0,d2
		move.b	h8tos_hex(pc,d2.w),(a0)+
	next
	clr.b	(a0)
	pop
	rts

h8tos_hex:
	.dc.b	'0123456789ABCDEF'
	.even

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

;----------------------------------------------------------------
;文字列を表示する
;<a0.l:文字列の先頭
;>a0.l:文字列の先頭
print::
	push	d0
	bsr	strlen
	move.l	d0,-(sp)
	move.l	a0,-(sp)
	move.w	#1,-(sp)
	DOS	_WRITE
	lea.l	(10,sp),sp
	pop
	rts

;----------------------------------------------------------------
;文字列を末尾まで読み飛ばす
;<a0.l:文字列の先頭
;>a0.l:文字列の末尾の0の位置
strchr0::
	do
		tst.b	(a0)+
	while	ne
	subq.l	#1,a0
	rts

;----------------------------------------------------------------
;文字列をコピーする
;<a0.l:コピー先
;<a1.l:コピー元
;>a0.l:コピー先の0の位置
;>a1.l:コピー元の0の次の位置
strcpy::
	do
		move.b	(a1)+,(a0)+
	while	ne			;0でなければ繰り返す
	subq.l	#1,a0			;進み過ぎた分戻る
	rts

;----------------------------------------------------------------
;文字列の長さを数える
;<a0.l:文字列
;>d0.l:長さ
strlen::
	move.l	a0,d0			;d0=先頭
	do
		tst.b (a0)+
	while	ne			;0でなければ繰り返す
	subq.l	#1,a0			;進み過ぎた分戻る。a0=末尾
	exg.l	d0,a0			;d0=末尾,a0=先頭
	sub.l	a0,d0			;d0=末尾-先頭=長さ
	rts

;----------------------------------------------------------------
;小文字にする
;<d0.b:文字
;>d0.b:文字
tolower::
	ifand	<cmp.b #'A',d0>,hs,<cmp.b #'Z',d0>,ls	;大文字
		add.b	#'a'-'A',d0		;小文字にする
	endif
	rts

;----------------------------------------------------------------
;符号なし整数を10進数の文字列に変換する
;<d0.l:符号なし整数
;<a0.l:バッファ
;>a0.l:バッファの0の位置
utos::
	push	d0-d2/a1
	if	<tst.l d0>,eq
		move.b	#'0',(a0)+
	else
		lea.l	utos_table(pc),a1
		do
			move.l	(a1)+,d1
		while	<cmp.l d1,d0>,lo	;引けるところまで進む
		do
			moveq.l	#'0'-1,d2
			do
				addq.b	#1,d2
				sub.l	d1,d0
			while	hs			;引ける回数を数える
			move.b	d2,(a0)+
			add.l	d1,d0			;引きすぎた分を加え戻す
			move.l	(a1)+,d1
		while	ne
	endif
	clr.b	(a0)
	pop
	rts

utos_table::
	.dc.l	1000000000
	.dc.l	100000000
	.dc.l	10000000
	.dc.l	1000000
	.dc.l	100000
	.dc.l	10000
	.dc.l	1000
	.dc.l	100
	.dc.l	10
	.dc.l	1
	.dc.l	0

;----------------------------------------------------------------
;y/n入力待ち
;	メッセージを表示してyまたはnが入力されるまで待って改行する
;	^Cで中断できる
;<a0.l:メッセージ
;>d0.l:'y'=yes,'n'=no
yesno::
	push	a0
	bsr	print
	do
		DOS	_GETCHAR		;標準入力から1バイト入力(標準出力にエコーバックする)
		bsr	tolower
	whileand	<cmp.b #'y',d0>,ne,<cmp.b #'n',d0>,ne
	leastrdata	<13,10>,a0
	bsr	print
	pop
	rts

;----------------------------------------------------------------
;プログラム終了
	.end	program_start