//--------------------------------------------------------------------------------------
//
// X-BASIC'
//	メイン処理
//
//--------------------------------------------------------------------------------------

#include "fdef.h"
#include <unistd.h>	// unlink()
	// ↑これをxb.hの後でincludeするとなぜかコンパイルエラーが出るので、ここで。
	// どうもsupport.hと競合しているらしい(stdio.h/XBdefines.hは問題ない)
#include "XB.h"
#include "XBerrorJ.h"
#include "XBerrorE.h"
#include "code2code.h"
#import "malloc2.h"
#import "bufferCtrl.h"
#import "libs.h"

//--------------------------------------------------------

INFORMATION_TABLE InfoFuncs[MAX_FUNC_FILES];	// 外部関数インフォメーションテーブル
XB_ERRORNO 	lastErrNo;			// 最新のエラー番号
int			errorCnt;			// エラー発生数
int			errorLimit;			// エラー発生上限（コンパイル時のみ有効）
uwLineNO	startupLines;		// startup.basの行数
char		xbBasicFile[64+1];	// BASICのソースファイル名(長さは適当）;表示用であり、ファイル名のみなので長くなくてもいい

//--------------------------------------------------------

void xb_fileload(char *fname)
{
	nslog(@"%s=%@\n",__FUNCTION__,@(fname));
	// [NSString UTF8string]で来ているので、コピーが必須
	strcpy(xbBasicFile,fname);
}

//--------------------------------------------------------

#ifdef DMP_INTERNALCODE
void saveInternalCode()
{
	// このデバッグ処理の中ではエラーを発生させないこと(setjmp()してしまうため)
	static int cnt=0;
	static int size;
	if (cnt==0) { // 最初にサイズを覚える
		size=(int)calcAdrsOffset(internalCode.bp,internalCodeTop);
	}
	nslog(@"中間コード領域:%p～%p(size=%d/$%04x)",internalCodeTop,internalCodeTop+size-1,size,size);
	
	// 中間コード領域をアドレス付きでダンプ
	FILE *fp;
	char dumpFname[256]={0}; // EOS
	strcat(dumpFname,CTESTDIR);
	strcat(dumpFname,xbBasicFile); // *.bas
	int len=bstrlen(xbBasicFile);
	if (dumpFname[len-4]=='.') dumpFname[len-4]=EOS; // 拡張子以降を消す
	char num[5];
	sprintf(num,"%02d",cnt++);
	strcat((char *)dumpFname,num);
	strcat((char *)dumpFname,".code.txt");
	unlink((char *)dumpFname); // 古いファイルは消してしまう(古いのと間違わないように)
	fp=fopen(dumpFname,"w");
	if (fp==NULL) {
		nslog(@"file not make:%s",dumpFname);
		exit(1);
	}
	INTERNAL_CODE_INFO *internalCodesInfo=(INTERNAL_CODE_INFO *)internalCodeTop;	// 読み替えのため
	uwCODE *siki=internalCodesInfo->codes;
//	for (;siki<internalCode.wp;siki++) {
	for (int i=0;i<size;i+=SZ_WORD) {
		fprintf(fp,"$%08lx/%p : $%04x\n",calcAdrsOffset(siki,internalCodeTop),siki,*siki);
		siki++;
	}
	fputs("// iOS\n",fp);
	fclose(fp);
#if 0
	// 各表示関数の実装がまだ
	nslog(@"引数");						dumpBuffer(&paramBuffCtrl,YES);
	nslog(@"変数");						dumpBuffer(&variableBuffCtrl,YES);
	nslog(@"1行当たりの中間コードバイト数");	dumpBuffer(&internalCodeBytesBy1LineBuffCtrl,YES);
	nslog(@"各行のソース先頭アドレス");		dumpBuffer(&sourceAdrsEachLinesBuffCtrl,YES);
	
	// 名前領域出力
	DBG2_dumpNames();
#endif
}
#endif	// CORE_DEBUG

//--------------------------------------------------------

void xb_setStartupLines(uwLineNO startupLines_)
{
	nslog(@"%s=%d",__FUNCTION__,startupLines_);
	startupLines=startupLines_;
}

// コンパイル処理
// ret=ERR_SKIPならgoto endすること
XB_ERRORNO xb_compile(void)
{
	DBG_Func();
	
	// v3.02
	operationStatus.debugMode		=DEBUG_MODE_NORMAL;	// デバッグなし
	operationStatus.debugDumpMode	=DUMP_VAR_ALL|DUMP_DIM_ALL;
	operationStatus.dumpCnt			=0; // 出力回数0
	operationStatus.debugPrintFunc	=NULL;
	
	lastErrNo=ERR_NONE;
	
	// 中間コード領域(初期;不足したらreallocされる)
	nslog(@"中間コード領域");
	internalCodeSize=SZ_INTERNAL_CODE;
	internalCodeTop =(uwCODE *)malloc2(internalCodeSize);
	if (internalCodeTop==NULL) {
		// メモリー不足
#ifdef DOS
		Error(ERR_NO_WORKAREA);
#else
		return (ERR_NO_WORKAREA);
#endif
	}
	nslog(@"中間コードエリア:%p(%dKB)",internalCodeTop,(int)internalCodeSize/K);
	
	// 変数・配列用ワーク(初期;不足したらreallocされる)
	nslog(@"変数・配列用ワーク");
	variableAreaSize=SZ_VARIABLE_AREA;	// 初期は64K
	variableAreaTop =malloc2(variableAreaSize);
	if (variableAreaTop==NULL) {
		// メモリー不足
#ifdef DOS
		Error(ERR_NO_WORKAREA);
#else
		return (ERR_NO_WORKAREA);
#endif
	}
	variableArea=variableAreaTop;
	nslog(@"variableArea=%p(%dKB)",variableAreaTop,(int)variableAreaSize/K);
	
#ifdef CORE_DEBUG
	{
		int i;
		for (i=VAR_INT;i<=VAR_DIM;i++) {
			nslog(@"globalVarCtrl[%d]=%p",i,&globalVarCtrl[i]);
		}
	}
#endif
	
	// 汎用ワーク;初期化で使ってもいいように、一番初めに確保しておく
	// 文字列をSHIFT-JIS->UTF8変換するためにも使われるので、3倍確保しておく
	nslog(@"汎用ワーク");
	int sz=operationStatus.sz_str*3;
	tmp=malloc2(sz*2); // *2=tmp,tmp2の2本あるから
	if (tmp==NULL) {
		// メモリー不足
#ifdef DOS
		Error(ERR_NO_WORKAREA);
#else
		return (ERR_NO_WORKAREA);
#endif
	}
	tmp2=&tmp[sz];
	//
	if (operationStatus.fsjis) {
		nslog(@"SJIS用ワーク");
		tmp3s[0]=malloc2(sz*MAX_TMP3); // 確保は一括で
		if (tmp==NULL) {
			// メモリー不足
#ifdef DOS
			Error(ERR_NO_WORKAREA);
#else
			return (ERR_NO_WORKAREA);
#endif
		}
		int i;
		for (i=1;i<MAX_TMP3;i++) {
			tmp3s[i]=tmp3s[i-1]+sz;
		}
	}
	
	//-----------------------------
	// 中間コードコンパイラ
	//-----------------------------
	
	// 外部関数情報テーブル設定
	int i=0;
	functionsList[i++]=&InfoStandardFuncs;
	functionsList[i++]=&InfoFileFuncs;
	functionsList[i++]=&InfoExtendFuncs;
	functionsList[i++]=&InfoIosAFuncs;
	functionsList[i++]=&InfoIosGFuncs;
	functionsList[i++]=&InfoIosTFuncs;
	functionsList[i++]=&InfoIosKFuncs;
	functionsList[i++]=&InfoIosSFuncs;
	functionsList[i++]=&InfoIosMFuncs;
	functionsList[i++]=&InfoMusicFuncs;	// v3.50
	if (operationStatus.fsprite) {
		// スプライトが有効な時のみ
		functionsList[i++]=&InfoIosSPFuncs;
	}
	if (operationStatus.fX68) {
		// X68互換関数が有効な時のみ
		functionsList[i++]=&InfoX68Funcs;
		functionsList[i++]=&InfoCardFuncs;
	}
	functionsList[i++]=NULL; // end of table
	
	nslog(@"コンパイル開始");
	
	// 設定値をコピーする v3.00〜
	// 関数で変更できるように
	fsjisInRun=operationStatus.fsjis;
	errorCodeFopenInRun=operationStatus.errorCodeFopen;
	nslog(@"errorCodeFopenInRun=%d",errorCodeFopenInRun);
	
	// 初期化
	nslog(@"\n------------------------------------------------------------------------");
	errorCnt=0;
	
	switch (setjmp(jmpEnv)) {
		case JMP_BREAK:	 // 来たらおかしい
		case JMP_IERROR: // 来たらおかしい
		case JMP_ERROR:
			// Error()からの戻り時
			nslog(@"initXBcompiler:Error()からの戻り");
			return (ERR_SKIP);
		//	goto end;
			//	break;
		case JMP_NONE: // 最初の通過時
			initXBcompiler(sourceTop);	// エラーあり
			break;
	}
	
	// メイン
	nslog(@"\n------------------------------------------------------------------------");
	nslog(@"%s",__FUNCTION__);
	if (mainXBcompiler(sourceTop)==ERR_NO_WORKAREA) {
		// 途中でメモリー不足が発生している
#ifdef DOS
		Error(ERR_NO_WORKAREA);
#else
		return (ERR_NO_WORKAREA);
#endif
	}
	
#ifdef DEBUG
	nslog(@"variableBuffCtrl"); 				dumpBuffer(&variableBuffCtrl				,YES);
	nslog(@"sourceAdrsEachLinesBuffCtrl"); 		dumpBuffer(&sourceAdrsEachLinesBuffCtrl		,YES);
	nslog(@"internalCodeBytesBy1LineBuffCtrl"); dumpBuffer(&internalCodeBytesBy1LineBuffCtrl,YES);
	nslog(@"paramBuffCtrl"); 					dumpBuffer(&paramBuffCtrl					,YES);
	nslog(@"labelBuffCtrl"); 					dumpBuffer(&labelBuffCtrl					,YES);
	nslog(@"gotoBuffCtrl"); 					dumpBuffer(&gotoBuffCtrl					,YES);
#endif
	
	// 終了処理
	nslog(@"\n------------------------------------------------------------------------");
	switch (setjmp(jmpEnv)) {
		case JMP_BREAK:	 // 来たらおかしい
		case JMP_IERROR: // 来たらおかしい
		case JMP_ERROR:
			nslog(@"endXBcompiler:Error()からの戻り");
			// Error()からの戻り時
			return (ERR_SKIP);
			//	goto end;
			//	break;
		case JMP_NONE: // 最初の通過時
			endXBcompiler();
			break;
	}
	
	nslog(@"\n------------------------------------------------------------------------");


#ifdef CORE_DEBUG2
	nslog(@"コンパイル時変数");
	VAR_TYPES typeNo;
	// 普通変数
	for (typeNo=VAR_INT;typeNo<=VAR_FLOAT;typeNo++) {
		char *typeName=name_type[typeNo].name;
		nslog(@"globalVarCtrl[%s]=%d",typeName,globalVarCtrl[typeNo].cnt);
		
		VARIABLE1 *var1Array=globalVarCtrl[typeNo].var1Top;
		for (int i=0;i<globalVarCtrl[typeNo].cnt;i++) {
			nslog(@"hash=$%04x/len=%d/token=%s",var1Array[i].hash,var1Array[i].len,var1Array[i].token);
		}
	}
	// 配列
	nslog(@"globalVarCtrl[dim]=%d",globalVarCtrl[typeNo].cnt);
	void *vp=globalVarCtrl[typeNo].var1Top;
	for (int i=0;i<globalVarCtrl[typeNo].cnt;i++) {
		VARIABLE1 *var1=(VARIABLE1 *)vp;
		nslog(@"hash=$%04x/len=%d/token=%s",var1->hash,var1->len,var1->token);
		vp+=sizeof(VARIABLE1);
		//
		DIM1 *dim1=(DIM1 *)vp;
		nslog(@"+mstype=%d/jigen=%d",dim1->msType,dim1->jigen);
		for (int j=0;j<dim1->jigen;j++) {
			nslog(@"+-%d:%d",j+1,dim1->youso[j]);
		}
		vp+=sizeof(DIM1);
	}
#endif
	
	// このデバッグ処理の中ではエラーを発生させないこと
	saveInternalCode();
	//
	return (ERR_NONE);
}

//--------------------------------------------------------

int recursiveCnt;	// 再帰呼び出し段数

// インタープリター
XB_ERRORNO xb_interpreter(void)
{
	DBG_Func2(":実行開始\n");

	// 外部関数の実行時初期化処理
	externalFunctionsRoutine(noFrun);
	
	exitCode=0;
	lineNo  =0;
	operationStatus.ferror=ERR_ON; // デフォルトでは外部関数エラー表示ON(fopenは除く)
	operationStatus.fend=STATE_OK; // 終わってない印
#if 0	// コンパイル中のエラーも取るため、コンパイラ側に移動
	// v3.02
	operationStatus.debugMode		=DEBUG_MODE_NORMAL;	// デバッグなし
	operationStatus.debugDumpMode	=DUMP_VAR_ALL|DUMP_DIM_ALL;
	operationStatus.dumpCnt			=0; // 出力回数0
	operationStatus.debugPrintFunc	=NULL;
#endif
	openFputCode("実行開始\n");
	
	switch (setjmp(jmpEnv)) {
		case JMP_BREAK:
			nslog(@"BREAKで終了");
			break;
		case JMP_ERROR: // 来たらおかしい
		case JMP_IERROR:
			// IError()からの戻り時
			// jongJump()でここに戻ってきた時に、何故かlocalVarCtrlExec[]のローカル変数領域が破壊されているので、ここではローカル変数のダンプはできない
//			if (operationStatus.debugMode&DEBUG_MODE_ERROR) {
//				varDump2(operationStatus.debugDumpMode,NSLocalStr(@"AT_ERROR"),operationStatus.debugPrintFunc);
//			}
			break;
		case JMP_NONE: // 最初の通過時
			{
			// インタプリタ実行
			nslog(@"\n------------------------------------------------------------------------");
			FAC fac[0]; // ルート=グローバルのはダミー(b_argv[]をサポートしないから)
			recursiveCnt=0;
			xbasicMain(internalCodeTop,NO,0,fac); // 返ってくるのはend/stop/エラー/break
			}
			break;
	}

	closeFputCode();

	return (ERR_NONE);
}

//--------------------------------------------------------

// コンパイル中に書き換えた、ソース中のMULTI_STATEMENT2/3を元に戻す
void resumeMultiStatement(UByte *source)
{
	DBG_Func2(" start");
	UByte c;
	// 高速化は可能だが、今はソース領域は64KBまでなので何も細工しない
	while ((c=*source++)!=EOS) {
		switch (c) {
			case MULTI_STATEMENT2:
				*(source-1)=' ';
				break;
			case MULTI_STATEMENT3:
				*(source-1)='{';
				break;
		}
	}
	DBG_Func2(" end");
}

//--------------------------------------------------------

// 終了処理
// fskipEend : noFend処理を飛ばすかどうか ; 基本的に、コンパイル中エラーならYES , 実行時ならNO
void xb_end(BOOL fskipEend)
{
	// ここから先はError()/IError()を呼び出してはいけない(無限ループに陥る)
	nslog(@"\n------------------------------------------------------------------------");
	nslog(@"終了処理(XB):fskipEend=%d",fskipEend);

	if (!fskipEend) {
		nslog(@"externalFunctionsRoutine(noFend)");
		externalFunctionsRoutine(noFend);
	}
	nslog(@"externalFunctionsRoutine(noFexit)");
	externalFunctionsRoutine(noFexit);
	
	// malloc()で確保したメモリーを解放する
	nslog(@"確保したメモリーを解放する");
	
	// 汎用ワーク
	free2(tmp); // tmp2も含む
	if (operationStatus.fsjis) {
		free2(tmp3s[0]);
	}
	
	nslog(@"FunctionBuffer");
	free2(FunctionBuffer);
	
#if 1	// v2.5
	// メモリ確保状況表示(本番には自動削除)
	dispAllalloc();

	// すべてのmallocに対する開放が極めて難しい状況にあるので(特にローカル変数)、
	// malloc/freeを登録制にして、一括開放できるようにした
	freeAll();
#else	// ↓freeAll()は以下相当
	nslog(@"変数領域開放");
//	freeVariableDefineAll(NO);	// グローバル変数
//	freeVariableDefineAll(YES);	// ローカル変数

	defineNameFree();
	closeBuffer(&gotoBuffCtrl);
	closeBuffer(&labelBuffCtrl);
	closeBuffer(&internalCodeBytesBy1LineBuffCtrl);
	closeBuffer(&sourceAdrsEachLinesBuffCtrl);
	closeBuffer(&variableBuffCtrl);
	closeBuffer(&paramBuffCtrl);
	//
	nslog(@"variableArea");
	free2(variableAreaTop);
	
	nslog(@"internalCode");
	free2(internalCodeTop);

	// メモリ確保状況表示(本番には自動削除)
	dispAllalloc();
#endif
	
#ifdef DOS
	nslog(@"source");
	free(sourceTop);
#else
	resumeMultiStatement(sourceTop);
	// iOSではソース領域の確保はBasicFilesの中で行われている
#endif
}

//--------------------------------------------------------

#ifdef DOS
int main(int argc,char *argv[])
{
	prints(	"X-BASIC' Ver."VERSION_CORE"\n"
			" Copyright (C) 2012-15 by AIG-Soft\n");

	xb_fileload(argc,argv);
	//
	XB_ERRORNO ret=xb_compile();
	if (ret==ERR_NONE) {
		if (errorCnt>0) {
			sprintf((char *)tmp,"エラーが発生しているときは実行しない:%d個\n",errorCnt);
			 prints((char *)tmp);
		} else {
			// インタープリター
			ret=xb_interpreter();
			xb_end(NO);
		}
	} else {
		// コンパイルエラー
		xb_end(YES);
	}
	nslog(@"exit(%d)",exitCode);
	exit(exitCode);
}
#endif	// DOS

//--------------------------------------------------------------------------------------
// エラー処理
//--------------------------------------------------------------------------------------

// 外部関数のエラーメッセージを設定する
void setExternalFuncErrmes(char *errmesJ,char *errmesE)
{
	DBG_Func();
	extraFunctionErrmes=selectMessageJE(errmesE,errmesJ);
	nslog(@"外部関数エラー:%@",@(extraFunctionErrmes));
}

// lineNo==LINENO_SPECIAL時の表示するソース行を指定する
void setExternalSourceLine(UByte *src)
{
	DBG_Func();
	externalSourceLine=src;
}

UByte *getExternalSourceLine(void)
{
	DBG_Func();
	return(externalSourceLine);
}

// コンパイル時エラー発生上限設定
// 0=上限なし
// インタープリターでは1つで実行停止するので関係なし
void setErrorMax(int errormax)
{
	DBG_Func();
	errorLimit=errormax;
}

static XBERROR_LINE_INFO *xb_errLine_info;

void setErrorInfoBuff(int errormax,XBERROR_LINE_INFO *xb_errLine_info_)
{
	setErrorMax(errormax);
	xb_errLine_info	=xb_errLine_info_;
}

//---------------------------------------------------------
// エラーメッセージ作成処理
//---------------------------------------------------------

char *getErrorMes(XB_ERRORNO eNo)
{
	return ((char *)selectMessageJE(errorMesE[eNo],errorMesJ[eNo]));
}

#ifndef IOS // DOS
char *getMessageError(void)
{
	return (selectMessageJE("Error ","エラー"));
}
#endif

// エラーメッセージ表示(行内容付き)
// lno : 行番号(表示だけ)
// line: ソース(行先頭)
// eNo==ERR_SKIPの時は行のみ表示(テスト用)
void printErrorLine(XB_ERRORNO eNo,uwLineNO lno,UByte *line)
{
	DBG_Func();

	BOOL frecord;
	NSString *errMes;
	slLineNO llno=(slLineNO)lno; // 型変換
	if (errorCnt<errorLimit) {
		if (lno!=LINENO_SPECIAL) {
			nslog(@"startupLines=%d",startupLines);
			if (lno<startupLines) {
				nslog(@"startupの中にエラーがある");
				errMes=make1ErrorMessage(eNo,llno,extraFunctionErrmes,YES,NO,&frecord);
				llno=-(slLineNO)lno-1; // -1〜に変換する
			} else {
				nslog(@"ユーザーソース内にエラーがある");
				llno-=startupLines;
				errMes=make1ErrorMessage(eNo,llno,extraFunctionErrmes,NO ,NO,&frecord);
			}
		}

		if (operationStatus.debugPrintFunc!=NULL) {
			operationStatus.debugPrintFunc(errMes);
		}

		xb_errLine_info[errorCnt].lno=llno; // ここではそのままの
		xb_errLine_info[errorCnt].mes=extraFunctionErrmes; // 外部関数エラーメッセージ
		xb_errLine_info[errorCnt].eno=eNo;
	} // エラー上限を超えるときは記録しない
}

//---------------------------------------------------------

// 行番号が特定できないエラー表示
// コンパイル＆実行時兼用
int printLIneNotSpecifiedError(XB_ERRORNO eNo
#ifdef CORE_DEBUG
									,int line,char *fname
#endif
					 										)
{
	lastErrNo=eNo;	// 最新エラー記録
#ifdef CORE_DEBUG
	nslog(@"%@ : %d",@(fname),line);
#endif
	if (eNo>=ERR_NO_SYSTEM)	return (NO); // 特定できるエラーはここでは処理しない
	
	// ソースコードに関係ないエラー
	if (errorCnt<errorLimit) {
		xb_errLine_info[errorCnt].eno=eNo;
		xb_errLine_info[errorCnt].lno=(slLineNO)LINENO_SPECIAL;
	} // エラー上限を超えるときは記録しない
	return (YES);
}

//---------------------------------------------------------
// エラー処理実体（エラー発生時に呼び出されるのはこれ）
//---------------------------------------------------------

// コンパイル時エラー処理
void
#ifdef CORE_DEBUG
_Error(XB_ERRORNO eNo,int line,char *fname)
#else
Error(XB_ERRORNO eNo)
#endif
{
	// エラー発生行番号ineNoはfirstCheckSource();XBstatSub.m内で設定されている
	
	lastErrNo=eNo;	// 最新エラー記録
	nslog(@"%s(XB):eNo=%d",__FUNCTION__,eNo);
#ifdef CORE_DEBUG
	nslog(@"line=%d/fname=%@",line,@(fname));
#endif
	if (!printLIneNotSpecifiedError(eNo
		#ifdef CORE_DEBUG
							,line,fname
		#endif
	)) {
		switch (lineNo) {
			case LINENO_SPECIAL:
				// アドレス→行番号変換
				lineNo=getLineNo(externalSourceLine);
				break;
		}
		nslog(@"LineNo=%d",lineNo);
		
		// エラーは基本的に現在解釈中の行で発生する(ネストなど後処理が入る場合はその限りではないが)
		SOURCEADRS *adrs=getValueFromBuffer(&sourceAdrsEachLinesBuffCtrl,lineNo); // その行の先頭アドレス
		UByte *bp=*adrs;
		
		// エラー行表示
		printErrorLine(eNo,lineNo,bp);
	}
	
	// エラー発生数
	errorCnt++;
	nslog(@"エラー発生回数:%d",errorCnt);
	
	// 戻り処理
	longjmp(jmpEnv,JMP_ERROR);
}

//---------------------------------------------------------

// 中間コードアドレスから行を特定する
UByte *pinpointExecuteLine(uwCODE *wp,uwLineNO *lineNo)
{
	int cnt=internalCodeBytesBy1LineBuffCtrl.cnt;
	nslog(@"全行数=%d",cnt);
	
	// internalCodeTopからには、まずINTERNAL_CODE_INFOが格納されているので、実中間コードはINTERNAL_CODE_INFO.codeの位置から
	uwCODE *wp2=((INTERNAL_CODE_INFO *)internalCodeTop)->codes; // v2.3修正
	nslog(@"エラー発生中間コードアドレス=%p/internalCodeTop=%p",wp,wp2);
	nslog(@"相対位置=%d",(int)wp-(int)wp2);
	
	int ofst=0;
	int j;
	for (j=0;j<cnt;j++) { // 念のためリミッター付き
		CODEBYTES *codeBytes=(CODEBYTES *)getValueFromBuffer(&internalCodeBytesBy1LineBuffCtrl, j);
		int byte=*codeBytes; // これはバイト単位
		UWord size1line=withWord(byte); // .w単位に変換
#ifdef DEBUG
		if (byte==0) {
			nslog(@"行%d=%d.b(%d.w),ofst:%d"		,j,byte,size1line,ofst);
		} else {
			nslog(@"行%d=%d.b(%d.w),ofst:%d〜%d"	,j,byte,size1line,ofst,ofst+byte-1);
		}
#endif
		ofst+=byte;
		if (wp2+size1line>=wp) {
			break;
		}
		wp2+=size1line;
	}
	if (j==cnt) j--; // 最後まで行ったときは最終行にしておく（念のため）
//	if (eNo==ERR_IN_EXTERNAL_FUNCTION) j--; // 外部関数内でエラーが発生したときは、関数呼び出しまでは正常にできている＝internalCode.wpは次の行を指しているので1つ前に戻す・・・とダメみたい
	*lineNo=j; // 0〜
	if (j<startupLines) {
		nslog(@"lineNo=%d",j+1);
	} else {
		nslog(@"lineNo=%d(startup %d行を含む)/%d",j,startupLines,j-startupLines+1);
	}
	
	// 行数から行アドレスを得る
	SOURCEADRS *adrs=getValueFromBuffer(&sourceAdrsEachLinesBuffCtrl,j); // その行の先頭アドレス
	return (*adrs);
}

// 実行時エラー(実行時エラーは同時1つだけ)
// コンパイラーとはソースコードとの対応付けが異なる
// ERR_IN_EXTERNAL_FUNCTION=外部関数エラーの時は extraFunctionErrmes に実エラーメッセージが設定されている
void
#ifdef CORE_DEBUG
_IError(XB_ERRORNO eNo,int line,char *fname)
#else
IError(XB_ERRORNO eNo)
#endif
{
	nslog(@"%s(XB):eNo=%d/internalCode=%p",__FUNCTION__,eNo,internalCode.wp-1);
	saveInternalCode(); // その時の中間コードを出力；破壊されていないかどうかを調べるため
	
	if (!printLIneNotSpecifiedError(eNo
		#ifdef CORE_DEBUG
							,line,fname
		#endif
	)) {
		// 現在の中間コードアドレスから行を特定する
		// internalCode.wpは、エラー発生コードの次をさしているので
		UByte *bp=pinpointExecuteLine(internalCode.wp-1,&lineNo);
		
		// エラー行表示
		printErrorLine(eNo,lineNo,bp);
	}
	
	// エラー発生数
	errorCnt++;

	// longJump()するとローカル変数領域が何故か破壊されるので、ここでダンプする
	// 実行時エラーは1つしか発生しないのでこれでも良い
	if (operationStatus.debugMode&DEBUG_MODE_ERROR) {
		varDump2(operationStatus.debugDumpMode,NSLocalStr(@"AT_ERROR"),operationStatus.debugPrintFunc);
	}
	
	// 戻り処理
	longjmp(jmpEnv,JMP_IERROR);
}

//---------------------------------------------------------

// 警告(実行時エラーの一種)
// wNo=WARN_*のみ
void Warn(XB_WARNNO wNo)
{
	nslog(@"%s=%d",__FUNCTION__,wNo);

	sprintf((char *)tmp,"%s%2d = %s\n",nsStringTocString(NSLocalStr(@"ERROR")),wNo,selectMessageJE(warnMesE[wNo],warnMesJ[wNo]));
#ifdef DOS
	prints(tmp);
#else	// iOS
#endif
}

//---------------------------------------------------------

// エラーメッセージ文字列を作成する
NSString *make1ErrorMessage(XB_ERRORNO	eno,
							uwLineNO	lno,
							char	*exmes,
							BOOL	finStartup,
							BOOL	fhtml,
							BOOL	*frecord)
{
	nslog(@"eno=%d,lno=%d,exmes=%s",eno,lno,exmes);

	// 処理上作れるけど、EditorView用；fhtml=YESでfinStartup=YESでは呼び出さないこと
	// (エラージャンプできないから)
	
	*frecord=NO;
	NSString *errorTitle=NSLocalStr(@"ERROR");
	NSString *mesStartup=(finStartup? @"startup.bas : ":@"");
	NSString *errMes;
	if (eno>=ERR_NO_SYSTEM) {
		nslog(@"普通エラー");
		if (eno!=ERR_SKIP) {
			char *emes=getErrorMes(eno);
			lno++; // 表示上の行番号は1〜
			if (eno==ERR_IN_EXTERNAL_FUNCTION) {
				// %sでは漢字が文字化けするのでCstringToNSString()する必要がある
				if (fhtml) {
					errMes=[NSString stringWithFormat:@"<a href='%5d'>%@%5d : %@%2d = %@ (%@)</a><br>\n"	,lno,mesStartup,lno,errorTitle,eno,@(emes),@(exmes)];
				} else {
					errMes=[NSString stringWithFormat:@"%@%5d : %@%2d = %@ (%@)\n"					 		,    mesStartup,lno,errorTitle,eno,@(emes),@(exmes)];
				}
			} else { // 他エラー
				if (fhtml) {
					errMes=[NSString stringWithFormat:@"<a href='%5d'>%@%5d : %@%2d = %@</a><br>\n" 		,lno,mesStartup,lno,errorTitle,eno,@(emes)];
				} else {
					errMes=[NSString stringWithFormat:@"%@%5d : %@%2d = %@\n"								,    mesStartup,lno,errorTitle,eno,@(emes)];
				}
			}
			// エラー行番号を記録させる印
			*frecord=YES;
		}
	} else { // システムエラー：ソースコードに関係ない
		// エラー番号 エラーメッセージ
		nslog(@"システムエラー");
		if (fhtml) {
			errMes=[NSString stringWithFormat:@"%@%@%2d = %@<br>\n"	,mesStartup,errorTitle,eno,@(getErrorMes(eno))];
		} else {
			errMes=[NSString stringWithFormat:@"%@%@%2d = %@\n"		,mesStartup,errorTitle,eno,@(getErrorMes(eno))];
		}
	}
	nslog(@"%@",errMes);
	return (errMes);
}

//--------------------------------------------------------------------------------------
