UMEHOSHI ITA TOP PAGE

アセンブリ言語で作る「 RAM上で動作するLチカ プログラム」(リセットスイッチでスタートさせる
  (基板入手直後で実験できます)

このページは、パワーONでないリセット操作で実行するRAM上で起動するプログラムの作り方です。(参考:EEPROM版の紹介
この手法は、このページでも紹介しています。
リセットに関係なく、"R00〜"の実行アドレスを指定した起動用のコマンド文字列で実行させる方法のリンクはこちらです

D1のLEDのチカチカ点滅を永遠に行う例を示します。

D1のLEDは、下記回路図の赤の接続線で示しています。赤マルのRB15に繋がっていますが、 これは制御チップで[PORTB REGISTER]のBit15をを意味しています(資料:DS60001168K TABLE 11-4: PORTB REGISTER MAP)。
このRB15の出力端子がLowで消え、HiにすればLEDに電流が流れて点灯します。

 UMEHOSHI ITAの起動時の初期化の概要を下記に示します。
if(  パワーオンリセットか? ){
      マクロ用の関数へのポインタなど各種変数の初期化や_HANDLES配列の初期化を行う。
      「ウメ・エディットプログラム」で使われるマクロ関数が登録される。そして、
        _HANDLES[_IDX_HANDLE_USER_SET_FUNC] = dummy_init_function;  の関数が登録される。
         (上記関数へのポインタは、_handle_user_set_func()のマクロの関数で実行できる)
        _HANDLES[_IDX_INIT_SUB_FUNC] = dummy_init_function;  の関数が登録される。
         (上記関数へのポインタは、_init_sub_func()のマクロの関数で実行できる)
        power_on_flag = 1;
}
_handle_user_set_func(); // 起動時のマクロ関数呼び出し(ここで、_HANDLESの要素の変更などを行う)

SYS_Initialize ( NULL );// ここでUSBや指定IOの初期化が行われる

_handle_user_set_func(); // 起動時のマクロ関数呼び出し
この後で、『USB受信文字列処理用のループ』に進みます。

上記より、パワーオン時にマクロ関数用配列の_HANDLES[機能識別添え字] 群に関数を記憶しています。
ここで_HANDLES[_IDX_HANDLE_USER_SET_FUNC] や、 _HANDLES[_IDX_INIT_SUB_FUNC]に何もしないdummy_init_func関数が記憶しています。
それがリセット時に_handle_user_set_func()_handle_user_set_func()のマクロ呼び出しで実行されます。
リセットで実行プログラムは、このどちらかまたは両方を使います。

USBや指定IOの初期化前に行わせる関数であれば、_HANDLES[_IDX_HANDLE_USER_SET_FUNC]に登録し、
 初期化後に行わせる関数であれば、_HANDLES[_IDX_HANDLE_USER_SET_FUNC]に登録します。
この登録を「希望する起動関数」に設定し直せば、 電源を切るまでリセットボタンで「希望する起動関数」を実行させることができます。

以下ではこの仕組みを利用して、D1のLEDでの「 Lチカ 」無限ループの関数を、 _HANDLES[_IDX_INIT_SUB_FUNC] に登録しています。
これでこの登録関数は、電源投入後、リセットボタンの操作で動作することになります。

下記C言語ソースは、「umehoshiEdit」ツールで、ビルド実行できます。
右リストは逆アセンブルしたコードで、これを参考に、後述のアセンブリリストを作っています。

#include <xc.h>	// test.c
#include "common.h"
// RAMで動かす場合の絶対アドレス範囲は、(0x80005000〜0x80008000)です
__attribute__((address( 0x80005000 ))) void start_boot (void);
void start_boot(){
	extern void start_main();
	_HANDLES[_IDX_INIT_SUB_FUNC] = start_main;//起動時に動作する関数に、下記start_mainを登録
}

// __attribute__((address( 0x80005000 ))) void start_main (void);
void wait(int n){// nカウント待つ
	while(n--) ;
}
void start_main(){
	__asm__ ("NOP");
	for(;;){
		PORTBSET = 0x8000;	// D1 LED 点灯(RB15を1):これは、0xbf886128番地への設定です.
		__asm__ ("NOP");	// チョット待つ 
		wait(0x003f0000);
		PORTBCLR = 0x8000;	// D1 LED 消灯(RB15を0):これは、0xbf886124番地への設定です.
		__asm__ ("NOP");	// チョット待つ
		wait(0x003f0000);
	}
}


/*

C言語用のcommon.hには、たくさんのAPI用マクロが定義されています。
_handle_user_set_func()関数は、_HANDLES[_IDX_INIT_SUB_FUNC] に登録される関数を記憶しています。
パワーオン時に、何もしないダミー関数が登録されるので、それが起動時に実行されるので何も実行しません。
そして上記では、_HANDLES[_IDX_INIT_SUB_FUNC]start_mainを登録することで、
それが起動時に実行されます。(リセットSWでプログラムや_HANDLES配列内容が初期化されないためです)
(パワーオンしてしまうと、元の何もしないダミー関数に戻ってしまいます。)

さて、_HANDLES[_IDX_INIT_SUB_FUNC]の要素のアドレスは、
  _HANDLESが0xA0004000で、_IDX_INIT_SUB_FUNCの値が50になっています。(情報源)
  それよりこの要素のアドレスは、hex(0xA0004000+50*4)='0xa00040c8'と計算できます。
  右では、このアドレスをv0に記憶して、『*v0= start_mainのアドレスが記憶されるv1』で、関数を登録しています。

UMEHOSHI ITAで使っている制御チップは、PIC32MX270F256Bで、
そのメモリには特殊機能レジスタ(SFRs:Special Function Registers)が割り当てられています。
このレジスタの操作で、様々な周辺回路がアクセスできます。
上記のORTBSETが、PORTBの指定ビットを1にすることで対応端子をHiにする機能のレジスタです。
また、PORTBCLR が、PORTBの指定ビットを1にすることで対応端子をLowにする機能のレジスタです。
ORTBSETとPORTBCLRのレジスタは、それぞれのメモリ上のアドレスは、0xbf8861280xbf886124です。

よって、これらレジスタにLEDに繋がるRB15のビットを表現するx8000を代入すれば、点灯や消灯ができます。

なお、LED点滅間隔のwait関数引数は、80005098番地で、0x3fを16bit右シフトでa0に設定しています。

*/
80005000 <start_boot>:
80005000:	27bdfff8 	addiu	sp,sp,-8
80005004:	afbe0004 	sw	s8,4(sp)
80005008:	03a0f021 	move	s8,sp
8000500c:	3c02a000 	lui	v0,0xa000
80005010:	344240c8 	ori	v0,v0,0x40c8
80005014:	3c038000 	lui	v1,0x8000
80005018:	24635070 	addiu	v1,v1,0x5070
8000501c:	ac430000 	sw	v1,0(v0)
80005020:	03c0e821 	move	sp,s8
80005024:	8fbe0004 	lw	s8,4(sp)
80005028:	27bd0008 	addiu	sp,sp,8
8000502c:	03e00008 	jr	ra
80005030:	00000000 	nop

80005034 <wait>:
80005034:	27bdfff8 	addiu	sp,sp,-8
80005038:	afbe0004 	sw	s8,4(sp)
8000503c:	03a0f021 	move	s8,sp
80005040:	afc40008 	sw	a0,8(s8)
80005044:	00000000 	nop
80005048:	8fc20008 	lw	v0,8(s8)
8000504c:	2443ffff 	addiu	v1,v0,-1
80005050:	afc30008 	sw	v1,8(s8)
80005054:	1440fffc 	bnez	v0,80005048 <wait+0x14>
80005058:	00000000 	nop
8000505c:	03c0e821 	move	sp,s8
80005060:	8fbe0004 	lw	s8,4(sp)
80005064:	27bd0008 	addiu	sp,sp,8
80005068:	03e00008 	jr	ra
8000506c:	00000000 	nop

80005070 <start_main>:
80005070:	27bdffe8 	addiu	sp,sp,-24
80005074:	afbf0014 	sw	ra,20(sp)
80005078:	afbe0010 	sw	s8,16(sp)
8000507c:	03a0f021 	move	s8,sp
80005080:	00000000 	nop
80005084:	3c02bf88 	lui	v0,0xbf88
80005088:	34038000 	li	v1,0x8000
8000508c:	ac436128 	sw	v1,0x6128(v0)
80005090:	00000000 	nop
80005094:	0c00140d 	jal	80005034 <wait>
80005098:	3c04003f 	lui	a0,0x3f
8000509c:	3c02bf88 	lui	v0,0xbf88
800050a0:	34038000 	li	v1,0x8000
800050a4:	ac436124 	sw	v1,0x6124(v0)
800050a8:	00000000 	nop
800050ac:	0c00140d 	jal	80005034 <wait>
800050b0:	3c04003f 	lui	a0,0x3f
800050b4:	08001421 	j	80005084 <start_main+0x14>
800050b8:	00000000 	nop

  以下に、上記の実際に動作した、アセンブリリストを示します。上のC言語の逆アセンブルリストをコードにしてみました。
無限ループなので、本来あるべきユーザープログラムのリターン処理がありません。(コメントアウトしています。)

(UMEHOSHI ITA基板で使っている制御チップのPIC32MX270F256B用(MIPS32系)アセンブリに必要な情報の概要です。)


  上記で編集したアセンブリソースを、左のボタンで「アッセンブル」できます。
番地から実行するUME専用Hexコマンドを、 最後に追加埋め込みする場合にチェックする==>

上記で生成した[UME専用Hexコマンド]のテキストを コピー(CTRL+A CTRL+C)してUMEHOSHI ITAへ転送して動かすことができます。
上記コードは、逆アセンブラのコードを真似ただけで無駄が多い。以下でアセンブリ的に無駄な部分を削除してみました。
前回の記述ではスタックのローカル変数を使っていましたが、可能な限りs0-s7のレジスタを使うように変更しました。
(auto変数を使う場合でも、fpのフレームポインタを使わわないで直接にスタック操作をする手法にします。)
それで、waitサブルーチンでカウントダウンに使っている変数を、s7のレジスタを使うように変更しました。
また、この作品のような無限ループであれば、戻る必要がないので戻り値の確保はコメント化しました。
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <xc.h>	// test.S (Lチカの無限ループ)
	.set noreorder #アセンブラに命令の順序を自動変更させない。
	.section	start_boot,address(0x80005000),code
	.ent	start_boot
start_boot:
	ADDIU	sp,sp,-8
	LUI	v0,0xa000		# 以下2行で、_HANDLES[_IDX_INIT_SUB_FUNC]要素のアドレスの0xa00040c8をv0に設定
	ORI	v0,v0,0x40c8 	# 0xA0004000+50*4=0xa00040c8 の下位16bit
	LUI	v1,%hi(start_main)
	ADDIU	v1,v1,%lo(start_main)	# v1のstart_main関数のアドレスを設定
	SW	v1,0(v0) 			# *v0 = v1 で「_HANDLES[_IDX_INIT_SUB_FUNC] = start_main;」を実現
	ADDIU	sp,sp,8
	JR	ra
	NOP

start_main:
.L4:
	LUI	v0,0xbf88	# 以下の3行で、0xbf886128のPORTBSETに0x8000を設定することで、D1のLEDを点灯
	li	v1,0x8000
	SW	v1,0x6128(v0)	# 以上の3行で1のLEDを点灯
	NOP
	JAL	wait		# チョット待つwait関数呼び出し(次行のRaに戻り番地を記憶してジャンプ)
	LUI	a0,0x3f
	NOP
	LUI	v0,0xbf88	# 以下の3行で、0xbf886124のPORTBCLRに0x8000を設定することで、D1のLEDを消灯
	li	v1,0x8000
	SW	v1,0x6124(v0)	# 以上の3行で1のLEDを消灯

	JAL	wait		# チョット待つwait関数呼び出し(次行のRaに戻り番地を記憶してジャンプ)
	LUI	a0,0x3f
	J	.L4		# 無限ループのジャンプ
	NOP
	.end test_main

wait:	ADDIU	sp,sp,-8	# 以下で使う24byteまでの退避サイスをスタックに作る。<====wait関数
	SW	ra,4(sp)	# 現在の関数の戻り番地をスタックに退避
	ADDU	s7, a0, zero	# 引数レジスタa0を、s7に記憶 		
Lwait1:	ADDIU	s7, s7,-1		# s7=s7+(-1)
	BNE 	s7, zero, Lwait1
	NOP
	ADDIU	sp,sp,8	# 関数実行前のスタックポインタに戻す 
	JR	ra
	NOP

上記コードを、Pythonで[UMEHOSHI ITA]基板で転送して実行する手順の紹介

予め、pythonが動作するターミナル(コマンドプロンプト)を、開いて準備して置くとよいでしょう。
以下では、R:\workを作業位置(カレントディレクトリ)にして説明しています。
また、pythonプログラムでは、serialモジュールを追加しておく必要があります。
「pip show serial」のコマンド操作で、インストールされているか確認できます。
インストールされていない場合は、「pip install pyserial」の操作で、インストールしておくと良いでしょう。

【1】 [UMEHOSHI ITA]基板で転送するテキストファイル「command.txt」を用意します。

上記の「アセンブラ編集部」の直下にある「アセンブル」ボタンをクリックします。
(このボタン右下の、指定アドレスから実行させるコード埋め込み用のチェックボックスがチェックされていること確認して行う)

「アセンブラ編集部」のソースにエラーが無ければ、[UME専用Hexコマンド]のテキストが、その下の TextArea に表示されます。
(エラーがあれば、エラーが無くHEXコードが生成されるまで、「アセンブラ編集部」の修正とアセンブル」を繰り返します)

メモ帳などで、「command.txt」のファイルを生成して開き、
上記操作で得られたTextAreaの[UME専用Hexコマンド]のテキストを、 コピー(CTRL+A CTRL+C)操作し、
  それを「command.txt」編集画面で貼り付け((CTRL+V)して、保存します。
保存位置は、pythonが動作するターミナルの作業位置(カレントディレクトリ)です。(後述例では、R:\workで示しています)

【2】 [UMEHOSHI ITA]基板へ[command.txt」を転送するpythonファイルを用意します。

「instruct.py」の名前のソースファイルを、[command.txt」と同じ位置に作ります。
(一度作れば、次回は使うだけで、作る必要はありません。(CTRL+A CTRL+C)操作で、コピー可能)


【3】 [UMEHOSHI ITA]基板へ[command.txt」を転送して実行させる。

上記で作った「instruct.py」と[command.txt」が存在する位置で、 pythonが動作するターミナル(コマンドプロンプト)を開きます。
以下では、この作業位置(カレントディレクトリ)が、R:\workである場合の例で説明しています。

まず [UMEHOSHI ITA]基板と、PCをUSBで接続します。

次にターミナルプロンプトを『powershell』にして、
『Get-CimInstance Win32_PnPEntity | Where-Object {$_ -like "*(COM*"} | Select-Object Caption』
のコマンド操作で、USBのシリアル デバイスのCOM番号を調べます。

実行例
R:\work>powershell
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6

PS R:\work> Get-CimInstance Win32_PnPEntity | Where-Object {$_ -like "*(COM*"} | Select-Object Caption

Caption
-------
Bluetooth リンク経由の標準シリアル (COM10)
USB シリアル デバイス (COM4)
Bluetooth リンク経由の標準シリアル (COM11)
PS R:\work>

この実行例から、USB シリアル デバイス がCOM4が使える状態になっていることが分かります。
このリンクページで示すように、デバイスマネージャで確認することもできます)
この番号の4を記憶して次の転送プログラム実行に進むのですが、 そのUSB シリアル デバイス が見つからない場合、次の点が考えられます。

いずれにしても、接続でUSB シリアル デバイスCOM番号が得られないと、次に進めません。

続いて、以下の『python command.txt』の操作で、転送実行プログラム(上記のアセンブラ用の例)を実行します。
実行例 (最初に上記で得られた番号の4をキー入力しています。)
R:\>python instruct.py
USB シリアル デバイスで、使用するCOMの番号を入力>>4
S108000500000F8FFBD2700A0023CC84042340080033C  70チェックサム: 0
USB受信● b'S108000500000F8FFBD2700A0023CC84042340080033C70\r\n'
USB受信● b'SET:80005000\r\n'
S10800050100024506324000043AC0800BD270800E003  C2チェックサム: 0
USB受信● b'S10800050100024506324000043AC0800BD270800E003C2\r\n'
USB受信● b'SET:80005010\r\n'
S1080005020000000000088BF023C00800324286143AC  C0チェックサム: 0
USB受信● b'S1080005020000000000088BF023C00800324286143ACC0\r\n'
USB受信● b'SET:80005020\r\n'
S108000503000000000001714000C3F00043C00000000  09チェックサム: 0
USB受信● b'S108000503000000000001714000C3F00043C0000000009\r\n'
USB受信● b'SET:80005030\r\n'
S10800050400088BF023C00800324246143AC1714000C  A2チェックサム: 0
USB受信● b'S10800050400088BF023C00800324246143AC1714000CA2\r\n'
USB受信● b'SET:80005040\r\n'
S1080005050003F00043C0914000800000000F8FFBD27  98チェックサム: 0
USB受信● b'S1080005050003F00043C0914000800000000F8FFBD2798\r\n'
USB受信● b'SET:80005050\r\n'
S1080005060000400BFAF21B88000FFFFF726FEFFE016  F1チェックサム: 0
USB受信● b'S1080005060000400BFAF21B88000FFFFF726FEFFE016F1\r\n'
USB受信● b'SET:80005060\r\n'
S108000507000000000000800BD270800E00300000000  01チェックサム: 0
USB受信● b'S108000507000000000000800BD270800E0030000000001\r\n'
USB受信● b'SET:80005070\r\n'
R008000500000  61チェックサム: 0
実行スタートの Enter >>
USB受信● b'R00800050000061\r\n'
USB受信● b'START:80005000\r\n'
終了確認の Enter >>>
R:\>

[command.txt」の中に"R008000500000"のような実行コマンドが存在すると、実行スタートの Enter >>の プロンプトが出て、一時停止します。
上記例では、そこでENTERキーを入力することで、"R00800050000061"が送られ、UMEHOSHI ITA基板で「0x80005000」番地から実行しますが、
まだ「 Lチカ 」は実行しません。
実行するのは _HANDLES[_IDX_INIT_SUB_FUNC] = start_main相当の登録処理です。
この登録の実行の後で、リセットボタンを押すと、登録関数実行で「 Lチカ 」が始まります。

リセットボタンの操作に関係なく、実行アドレスを指定したスタート用のコマンド文字列("R00〜")を送るこので 起動させるプログラムの作り方はこちらで示すページで示します。