rpi_start.Sを読む (最終回)
2013/11/10(日) 16:14 NetBSD はてブ情報 はてブに登録 はてブ数

さて、第1回から第13回までrpi_start.Sを一通り読むことができました。

まだまだ、わからないところも多いですが、ARMアセンブリ言語も知らず、ARMアーキテクチャもまるで分らない状態からすれば、ずいぶんとやっていることがわかるようになりました。

処理の大まかな内容は次のような感じでしょうか。
  1. 割り込みを禁止する
  2. L1ページ変換テーブルを0に初期化
  3. 最低限のマッピングをL1ページ変換に設定
  4. 各種キャッシュをOFF
  5. L1ページテーブルをセット
  6. コントロールレジスタを調整
  7. MMUをON
  8. スタートルーチンへジャンプ
ARMのアセンブリ言語に対しては、命令もオペランドも種類が豊富すぎで、けっこう苦労しましたが、WebのARMアセンブリ言語の入門ページを見ながら、なんとか読み進められました。

一方、mcr, mrcによって操作される、ステータスレジスタ等々、CPUアーキテクチャに依存する部分はマニュアルを読みつつ、想像力を働かせれば、なんとなーく、ぼやーっと、つかめたかなぁといったところです。

一つのスタートアップルーチンを読んでおけば、ほかのスタートアップルーチンを読むときにも役に立つはずと信じて、今回の短期連載を終わりたいと思います。

名前:  非公開コメント   

  • TB-URL  http://www.tokuda.net/diary/0816/tb/

rpi_start.Sを読む (第13回)
2013/11/10(日) 15:36 NetBSD はてブ情報 はてブに登録 はてブ数

いよいよMMUもONになり、大詰めに近づいてまいりました。

今回読むのは次の部分です。
    239 	/*
    240 	 * Ensure that the coprocessor has finished turning on the MMU.
    241 	 */
    242 	mrc	p15, 0, r0, c0, c0, 0	/* Read an arbitrary value. */
    243 	mov	r0, r0			/* Stall until read completes. */
    244 
    245 	/*
    246 	 * Jump to start in locore.S, which in turn will call initarm and main.
    247 	 */
    248 	mov	pc, lr
    249 	nop
    250 	nop
    251 	nop
    252 	nop
さっそく進めましょう。
    239 	/*
    240 	 * Ensure that the coprocessor has finished turning on the MMU.
    241 	 */
    242 	mrc	p15, 0, r0, c0, c0, 0	/* Read an arbitrary value. */
    243 	mov	r0, r0			/* Stall until read completes. */
MMUがONになったことをmrcで値を読み込んで確認します。マニュアルによると、このmrc操作はRead Main ID Registerと書いてあります。プロセッサの情報をとる命令のようです。

243行目のmov r0, r0でリードが完了するのを待つ、とコメントにあります。こういうのは慣用句なんでしょうかね。

進みましょう。
    245 	/*
    246 	 * Jump to start in locore.S, which in turn will call initarm and main.
    247 	 */
    248 	mov	pc, lr
いよいよ、このスタートアップルーチンとはお別れです。つまり、リンクレジスタに格納していた、飛び先つまりLstartにあったルーチンのアドレスをプログラムカウンタ(pc)に格納し、そちらに制御を移します。

ついに、rpi_start.Sを読み切りました!

名前:  非公開コメント   

  • TB-URL  http://www.tokuda.net/diary/0815/tb/

rpi_start.Sを読む (第12回)
2013/11/10(日) 15:21 NetBSD はてブ情報 はてブに登録 はてブ数

さて、今回読むのは次の部分です。冒頭のコメントでMMUを有効にするというのがわかります。
    221 	/*
    222 	 * Enable the MMU, etc.
    223 	 */
    224 	mrc     p15, 0, r0, c1, c0, 0
    225 
    226 	ldr     r1, Lcontrol_wax
    227 	and	r0, r0, r1
    228 	ldr     r1, Lcontrol_clr
    229 	bic	r0, r0, r1
    230 	ldr     r1, Lcontrol_set
    231 	orr	r0, r0, r1
    232 	ldr	lr, Lstart
    233 
    234 	.align 5
    235 	@ turn mmu on!
    236 	mov	r0, r0
    237 	mcr	p15, 0, r0, c1, c0, 0
コメントを読んで盛り上がったところで、読み進めていきます。
    224 	mrc     p15, 0, r0, c1, c0, 0
mrcでコントロールレジスタ設定の値を読み込みます。
マニュアルには「MRC p15, 0, , c1, c0, 0 ; Read Control Register configuration data」とあります。

コントロールレジスタを再掲します。
31-30292827-26252423222120-191817161514131211109876-43210
SBZFATRSBZEEVEXPUFISBZITSBZDTL4RRVIZFRSBSBOWCAM
    226 	ldr     r1, Lcontrol_wax
    227 	and	r0, r0, r1
    228 	ldr     r1, Lcontrol_clr
    229 	bic	r0, r0, r1
    230 	ldr     r1, Lcontrol_set
    231 	orr	r0, r0, r1
ここでは、r0にロードされたコントロールレジスタに対して様々な操作を行います。

まずは、Lcontrol_waxをldrでロードし、r0とANDをとります。そのあと、Lcontrol_clrをロードし、r0のビットをクリア。その後、Lcontrol_setをロードし、r0とORをとります。Lcontorol_setではちゃんとMMU_ENABLEが立っています。

Lcontrol_waxは「/* bits to "write as existing" in the Control Register */」。
Lcontrol_clrは「/* bits to clear in the Control Register */」。
Lcontrol_setは「/* bits to set in the Control Register */」。

Lcontrol_clrは.word 0と定義されています。bicはNOTしてANDなので、すべてのビットが1になりますから、ANDを取ってもすべてのビットがONのまま、なにもクリアされない気がします。

で、下表をみると、最初が何であろうと、Lcontrol_wax, Lcontrol_setの両方に1が立っているビットがないので、結果的にはLcontrol_setしか残らないという気がしてきます。うーん。Lcontrol_setだけでも良かったような。
定義31-30292827-26252423222120-191817161514131211109876-43210
SBZFATRSBZEEVEXPUFISBZITSBZDTL4RRVIZFRSBSBOWCAM
Lcontrol_wax1 1111 1000001 10100000010000000000
Lcontrol_set0 0000 0000000 01010001000101111111
あ、ちがうか、両方が0のやつはクリアされるからLcontrol_waxの意味はあるのか。もしかして、waxって車のワックスのwaxなのかな。ワックスをかけて、きれいにする、余分なもの(ビット)を落として、きれいにする、みたいなニュアンスで。

次に進みます。
    232 	ldr	lr, Lstart
いきなりのlrです。lrはリンクレジスタでr14の別名で、今回はリンクレジスタlrにLstartのアドレスをロードしています。rpi_start.Sの最後で、このアドレスにジャンプするための下準備です。

Lstartでは.word startと定義されており、これはきっとsrc/sys/arch/arm/arm32/locore.Sの以下の部分ではないかと。
  ASENTRY_NP(start)
さて、飛び先も決まったということで次の行に進みましょう。
    234 	.align 5
    235 	@ turn mmu on!
.align 5でアラインメントを整えます。
@から始まるのは一行コメントかな。いよいよMMUをONにします。ってことですね。

しかし、align 5というのは何ビットアラインメントだ? と思って調べると、2の5乗、つまり32ビットアラインメントのようです。たしかに、MMUのようなアドレスを変換するような機能をONにするなら、中途半端な場所にいるとダメそうです。

さて、次に進みます。
    236 	mov	r0, r0
    237 	mcr	p15, 0, r0, c1, c0, 0
これまで色々とビットを操作してきたr0、つまり新しくコントロールレジスタに与えるべき値をmcrで書き込みます。コントロールレジスタの0ビット目に1を立てている、つまりMMUをENABLEにするわけです。

名前:  非公開コメント   

  • TB-URL  http://www.tokuda.net/diary/0814/tb/

rpi_start.Sを読む (第11回)
2013/11/10(日) 14:08 NetBSD はてブ情報 はてブに登録 はてブ数

さて、今回読むのは次の部分です。
    211 	ldr	r0, Ltemp_l1_table	/* The page table address */
    212 	mcr	p15, 0, r0, c2, c0, 0	/* Set Translation Table Base */
    213 
    214 	mov	r0, #0
    215 	mcr	p15, 0, r0, c8, c7, 0	/* Invalidate TLBs */
    216 
    217 	/* Set the Domain Access register.  Very important! */
    218 	mov     r0, #((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT)
    219 	mcr	p15, 0, r0, c3, c0, 0
さっそく始めましょう。
    211 	ldr	r0, Ltemp_l1_table	/* The page table address */
    212 	mcr	p15, 0, r0, c2, c0, 0	/* Set Translation Table Base */
さきほど作成した0x4000のサイズを持つLtemp_l1_tableの先頭アドレスをr0にロードし、TTBをセットしています。Ltemp_l1_tableの先頭アドレスをTTBにするよっ、てことですかね。

マニュアルにも「MCR p15, 0, , c2, c0, 0 ; Write Translation Table Base Register 0」とあります。

次に進みます。
    214 	mov	r0, #0
    215 	mcr	p15, 0, r0, c8, c7, 0	/* Invalidate TLBs */
マニュアルによるとc8, c7, 0の組み合わせは「Invalidate unified TLB unlocked entries」とのこと。c8がTLB関連で、c7がunified TLBを指し、0がInvalidate TLB unlocked entriesを指すようです。
現時点の物理・仮想の変換バッファを無効化するということでいいのかな。

次は、なにやら重要だよというコメントから始まっています。
    217 	/* Set the Domain Access register.  Very important! */
    218 	mov     r0, #((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT)
    219 	mcr	p15, 0, r0, c3, c0, 0
久々に定義を追っかける気がしますが、PMAP_DOMAIN_KERNELはpmap.hで次のように定義されています。
  #define	PMAP_DOMAIN_KERNEL	0	/* The kernel uses domain #0 */
DOMAIN_CLIENTはpte.hで次のように定義されています。
  /*
   * Domain Types for the Domain Access Control Register.
   */
  #define	DOMAIN_FAULT	0x00		/* no access */
  #define	DOMAIN_CLIENT	0x01		/* client */
  #define	DOMAIN_RESERVED	0x02		/* reserved */
  #define	DOMAIN_MANAGER	0x03		/* manager */
えーとつまり、
  (0x01 << (0 * 2) | (0x01)
なので、0x01ですよね。最初の(DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2))って意味ないような。machdep.cでも似たような書き方をしているのですけど、そういうおまじないってことなんでしょうか。

謎です。

ドメインアクセスコントロールレジスタのフォーマットは次のようになっていますから、ドメイン0に0x01がセットされていると。

313029282726252423222120191817161514131211109876543210
D15D14D13D12D11D10D9D8D7D6D5D4D3D2D1D0
0x01はマニュアルには「Client. Accesses are checked against the access permission bits in the TLB entry.」とあります。TLBエントリのアクセスパーミッションビットに対してチェックを行う、ということかな。

アクセスパーミッション(AP)は10bit,11bitでしたね。
31-20191817161514-1211-1098-543210
セクションのベースアドレス(12bit)SBZ0nGSAPXTEXAPIMPドメインXNCB10
今回読んだ部分では、各種キャッシュを止めて、TTBをセット&無効化し、ドメインアクセスレジスタを設定しました。

名前:  非公開コメント   

  • TB-URL  http://www.tokuda.net/diary/0813/tb/

rpi_start.Sを読む (第10回)
2013/11/10(日) 12:28 NetBSD はてブ情報 はてブに登録 はてブ数

いよいよ10回目に入りました。

今回読むのは次の部分です。
    194 	/*
    195 	 * In theory, because the MMU is off, we shouldn't need all of this,
    196 	 * but let's not take any chances and do a typical sequence to set
    197 	 * the Translation Table Base.
    198 	 */
    199 	mov	r0, #0			/* SBZ */
    200 	Invalidate_I_cache(r0)
    201 
    202 	mcr	p15, 0, r0, c7, c14, 0	/* Clean and Invalidate Entire Data Cache */
    203 
    204         ldr     r2, Lctl_ID_dis		/* Disable I+D caches */
    205 	mrc	p15, 0, r1, c1, c0, 0	/*  "       "   "     */
    206 	and	r1, r1, r2		/*  "       "   "     */
    207 	mcr	p15, 0, r1, c1, c0, 0	/*  "       "   "     */
    208 
    209 	mcr	p15, 0, r0, c7, c10, 4	/* Drain the write buffers. */
まずはコメントです。
    194 	/*
    195 	 * In theory, because the MMU is off, we shouldn't need all of this,
    196 	 * but let's not take any chances and do a typical sequence to set
    197 	 * the Translation Table Base.
    198 	 */
うーん。どう訳すんでしょう。

理論上、MMUはオフなので、これから行う操作がすべて必要ではないけれど、この辺でやるしかないので、TTB(Translation Table Base)をセットする典型的な手順を実行します、って感じですかねぇ。

まぁ、先に進みましょうか。
    199 	mov	r0, #0			/* SBZ */
    200 	Invalidate_I_cache(r0)
SBZって何かとしらべたところ、SBZ = Should Be Zeroの略なんですね。ちなみに、SBO = Should Be Oneの略のようです。

movでレジスタr0をゼロにしてInvalidate_I_cache(reg)を実行します。

Invalidate_I_cache(reg)はずいぶん最初のほうに記述がありました。
     92 /*
     93  * Workaround Erratum 411920
     94  *
     95  *	- value of arg 'reg' Should Be Zero
     96  */
     97 #define Invalidate_I_cache(reg) \
     98 	.p2align 5;								\
     99 	mcr	p15, 0, reg, c7, c5, 0;	/* Invalidate Entire I cache */		\
    100 	mcr	p15, 0, reg, c7, c5, 0;	/* Invalidate Entire I cache */		\
    101 	mcr	p15, 0, reg, c7, c5, 0;	/* Invalidate Entire I cache */		\
    102 	mcr	p15, 0, reg, c7, c5, 0;	/* Invalidate Entire I cache */		\
    103 	nop;									\
    104 	nop;									\
    105 	nop;									\
    106 	nop;									\
    107 	nop;									\
    108 	nop;									\
    109 	nop;									\
    110 	nop;									\
    111 	nop;									\
    112 	nop;									\
    113 	nop;
Errata 411920に対応するためのI cacheを0にクリアします。
ちなみに、I cacheはInstruction cacheでD cacheはData cacheです。

c7はキャッシュ操作のレジスタです。ARM1176JZ-S™ Technical Reference Manualに、「MCR p15, 0, , c7, c5, 0 Invalidate entire Instruction Cache」とあります。
なんでかわかりませんが、4回ほど連発してますね。

このあたりから、マニュアルと首っ引きで調べる感じです。

それでは、202行目に戻りましょう。
    202 	mcr	p15, 0, r0, c7, c14, 0	/* Clean and Invalidate Entire Data Cache */
これも「MCR p15, 0, , c7, c14, 0 SBZ Clean and Invalidate Entire Data Cache.」とあります。
さっきがインストラクションキャッシュだったので、今度はデータキャッシュですね。

進めましょう。
    204         ldr     r2, Lctl_ID_dis		/* Disable I+D caches */
    205 	mrc	p15, 0, r1, c1, c0, 0	/*  "       "   "     */
    206 	and	r1, r1, r2		/*  "       "   "     */
    207 	mcr	p15, 0, r1, c1, c0, 0	/*  "       "   "     *
mrc, mcr命令にあるc1は Control Registerです。

Lctl_ID_disをr2にロードして。mcrでコントロールレジスタを読んで、Lctl_ID_disとのANDを取って、mrcで書き戻しています。。
I cache, D cacheをinvalidにしていますって、コメントのままですね。

ここからコントロールレジスタがたくさん出てくるので、フォーマットと定義を整理しておきましょう。

まずはレジスタのフォーマットです。
31-30292827-26252423222120-191817161514131211109876-43210
SBZFATRSBZEEVEXPUFISBZITSBZDTL4RRVIZFRSBSBOWCAM
それぞれのフィールドの定義は/sys/arch/arm/include/armreg.hにあります。
  /* CPU control register (CP15 register 1) */
  #define CPU_CONTROL_MMU_ENABLE	0x00000001 /* M: MMU/Protection unit enable */
  #define CPU_CONTROL_AFLT_ENABLE	0x00000002 /* A: Alignment fault enable */
  #define CPU_CONTROL_DC_ENABLE		0x00000004 /* C: IDC/DC enable */
  #define CPU_CONTROL_WBUF_ENABLE 	0x00000008 /* W: Write buffer enable */
  #define CPU_CONTROL_32BP_ENABLE 	0x00000010 /* P: 32-bit exception handlers */
  #define CPU_CONTROL_32BD_ENABLE 	0x00000020 /* D: 32-bit addressing */
  #define CPU_CONTROL_LABT_ENABLE 	0x00000040 /* L: Late abort enable */
  #define CPU_CONTROL_BEND_ENABLE 	0x00000080 /* B: Big-endian mode */
  #define CPU_CONTROL_SYST_ENABLE 	0x00000100 /* S: System protection bit */
  #define CPU_CONTROL_ROM_ENABLE	0x00000200 /* R: ROM protection bit */
  #define CPU_CONTROL_CPCLK		0x00000400 /* F: Implementation defined */
  #define CPU_CONTROL_SWP_ENABLE	0x00000400 /* SW: SWP{B} perform normally. */
  #define CPU_CONTROL_BPRD_ENABLE 	0x00000800 /* Z: Branch prediction enable */
  #define CPU_CONTROL_IC_ENABLE   	0x00001000 /* I: IC enable */
  #define CPU_CONTROL_VECRELOC		0x00002000 /* V: Vector relocation */
  #define CPU_CONTROL_ROUNDROBIN	0x00004000 /* RR: Predictable replacement */
  #define CPU_CONTROL_V4COMPAT		0x00008000 /* L4: ARMv4 compat LDR R15 etc */
  #define CPU_CONTROL_FI_ENABLE		0x00200000 /* FI: Low interrupt latency */
  #define CPU_CONTROL_UNAL_ENABLE	0x00400000 /* U: unaligned data access */
  #define CPU_CONTROL_XP_ENABLE		0x00800000 /* XP: extended page table */
  #define CPU_CONTROL_V_ENABLE		0x01000000 /* VE: Interrupt vectors enable */
  #define CPU_CONTROL_EX_BEND		0x02000000 /* EE: exception endianness */
  #define CPU_CONTROL_NMFI		0x08000000 /* NMFI: Non maskable FIQ */
  #define CPU_CONTROL_TR_ENABLE		0x10000000 /* TRE: */
  #define CPU_CONTROL_AF_ENABLE		0x20000000 /* AFE: Access flag enable */
  #define CPU_CONTROL_TE_ENABLE		0x40000000 /* TE: Thumb Exception enable */
ファイル後半で定義されているコントロールレジスタに関連するのは次の四つ (Lcontrol_set, Lcontrol_clr, Lcontrol_wax, Lctl_ID_dis) になります。waxって何でしょうねぇ。
    268 	/* bits to set in the Control Register */
    269 Lcontrol_set:
    270 	.word CPU_CONTROL_MMU_ENABLE  | \
    271 	      CPU_CONTROL_AFLT_ENABLE | \
    272 	      CPU_CONTROL_DC_ENABLE   | \
    273 	      CPU_CONTROL_WBUF_ENABLE |    /* not defined in 1176 */   \
    274 	      CPU_CONTROL_32BP_ENABLE | \
    275 	      CPU_CONTROL_32BD_ENABLE | \
    276 	      CPU_CONTROL_LABT_ENABLE | \
    277 	      CPU_CONTROL_SYST_ENABLE | \
    278 		(1 << 16) | 	/* SBO - Global enable for data tcm */ \
    279 		(1 << 18) |	/* SBO - Global enable for insn tcm */ \
    280 	      CPU_CONTROL_IC_ENABLE
    281 
    282 
    283 	/* bits to clear in the Control Register */
    284 Lcontrol_clr:
    285 	.word	0
    286 
    287 	/* bits to "write as existing" in the Control Register */
    288 Lcontrol_wax:
    289 	.word	(3 << 30) | \
    290 		(1 << 29) | \
    291 		(1 << 28) | \
    292 		(3 << 26) | \
    293 		(3 << 19) | \
    294 		(1 << 17) | \
    295 	        (1 << 10)
    296 
    297 	/* bits to disable the caches */
    298 Lctl_ID_dis:
    299 	.word	~(CPU_CONTROL_IC_ENABLE|CPU_CONTROL_DC_ENABLE)
それぞれを一覧にしてみます。Lcontrol_clrは全部0なので省略です。
定義31-30292827-26252423222120-191817161514131211109876-43210
SBZFATRSBZEEVEXPUFISBZITSBZDTL4RRVIZFRSBSBOWCAM
Lctl_ID_dis1 1111 1111111 11111110111111111011
Lcontrol_wax1 1111 1000001 10100000010000000000
Lcontrol_set0 0000 0000000 01010001000101111111
209行目に戻り、ふたたび、c7のキャッシュまわりです。
    209 	mcr	p15, 0, r0, c7, c10, 4	/* Drain the write buffers. */
うーん、マニュアルでは「MCR p15,0,,c7,c10,4 ; Data Synchronization Barrier operation.」と書いてあります。どういうことでしょう?

どうやら、昔はDrain the write buffersと呼ばれていたそうです。マニュアルを読んでもまるで自信はないですが、どうやらバリアというだけあって、この命令より前のメモリの操作やらキャッシュやらの操作が完了するまで待ちますよ、ってな感じの動きをするようです。

たしかに、ここまでいろいろキャッシュの操作をしてきたので、それらの操作がちゃんと終わるまで待ちましょう、という命令のようです。

mcr, mrc命令はマニュアルを拾い読みするだけでは理解に至らないことが多くて苦戦します。

名前:  非公開コメント   

  • TB-URL  http://www.tokuda.net/diary/0812/tb/

rpi_start.Sを読む (第9回)
2013/11/10(日) 11:01 NetBSD はてブ情報 はてブに登録 はてブ数

今回読むのは次の部分です。
    186 	.unreq	va
    187 	.unreq	pa
    188 	.unreq	n_sec
    189 	.unreq	attr
    190 	.unreq	itable
    191 	.unreq	l1table
    192 	.unreq	l1sfrm
これは、157行目からの
    157 	l1table	.req r0
    158 	va	.req r1
    159 	pa	.req r2
    160 	n_sec	.req r3
    161 	attr	.req r4
    162 	itable	.req r5
    163 	l1sfrm	.req r6
に対して、反対のことをしているわけですね。

これで終わりです。だと面白くないので、ちょっと振り返ってみましょう。

第6回から第8回までは、第3回でゼロに初期化したL1テーブルを設定する一連の処理でした。

処理は、
  • mmu_init_tableに設定された三つのva, pa, n_sec, attrによるループ
  • n_secによるループ
という二重ループから構成されていました。

mmu_init_tableは、次のようなテーブルでした。
vapansecattr
0x00x00x6430330x412
0xC00000000x00x6430330x041E
0xF20000000x200000000x010FFFFF0x0412
これが、L1テーブルに格納されるころには次のようになり、
vapan_sec
0x00000x000004120x06
0x20000x0000041E0x06
0x3C800x200004120x10
L1テーブルは次のように設定されたということになります。
L1 offsetL1 entry
00000x00000412
00040x00000412
00080x00000412
000C0x00000412
00100x00000412
00140x00000412
00180x00000000
001C0x00000000
(中略)
1FF80x00000000
1FFC0x00000000
20000x0000041E
20040x0000041E
20080x0000041E
200C0x0000041E
20100x0000041E
20140x0000041E
20180x00000000
201C0x00000000
(中略)
3C780x00000000
3C7C0x00000000
3C800x20000412
3C840x20000412
3C880x20000412
3C8C0x20000412
3C900x20000412
3C940x20000412
3C980x20000412
3C9C0x20000412
3CA00x20000412
3CA40x20000412
3CA80x20000412
3CAC0x20000412
3CB00x20000412
3CB40x20000412
3CB80x20000412
3CBC0x20000412
3CC00x00000000
3CC40x00000000
(以下略)

名前:  非公開コメント   

  • TB-URL  http://www.tokuda.net/diary/0811/tb/

rpi_start.Sを読む (第8回)
2013/11/10(日) 10:24 NetBSD はてブ情報 はてブに登録 はてブ数

前回の終わりに169行目にジャンプしたところから再開します。

今回読むのは以下の部分です。
    169 2:	str	pa, [l1table, va]
    170 	add	va, va, #4
    171 	add	pa, pa, #(L1_S_SIZE)
    172 	adds	n_sec, n_sec, #-1
    173 	bhi	2b
まずは169行目から。
    169 2:	str	pa, [l1table, va]
strはストア命令です。無印なので32ビットストアですね。レジスタpaをアドレス[l1table, va]にストアします。角かっこがいやらしいですね。

[Rn, Rm]はMem[Rn + Rm]ですから、
  *(l1table + va) = pa;
ということになります。Ll1_tableは.word 0x4000の空間でしたね。
    170 	add	va, va, #4
    171 	add	pa, pa, #(L1_S_SIZE)
addは加算ですね。L1_S_SIZEはセクションのサイズ、つまり1MBですから
  va += 4;
  pa += 0x00100000; /* L1_S_SIZEは1M */
となります。

次に進みます。
    172 	adds	n_sec, n_sec, #-1
    173 	bhi	2b
addsのaddは加算命令で、のこりのsはステータスレジスタの更新あり、でしたね。
セクションから1減算し、ゼロでなければラベル2にジャンプする、という処理になりますね。つまり、今回読んでいるソースはn_secでループしているわけですね。

ループを抜けると、再びラベル3の処理に入ります。そして、新しいMMU_INITマクロで設定したva, pa, n_sec, attrを読み込んで、vaとpaを適切に変換して、また、今回読んだループに入るというわけです。二重ループをこうやって実現しているんですね。

しかし、よくわからないのが173行目にあるbhiのhiです。
これは、キャリーフラグが1かつゼロフラグが0である、という意味になります。

キャリーフラグが1とは桁あふれが発生した場合で、ゼロフラグが0とは命令の結果が0でない(0ならフラグは1)である、という意味です。

なるほど、n_secから1を引いたときにゼロになればゼロフラグが1になりそうです。
一方で、キャリーフラグはn_secつまり0x06や0x10からマイナス1されるだけなので、ちっとも桁あふれなど起きなさそうです。

良く考えると、マイナス1を2の補数で表すと全部の桁が1になります。全部の桁が1に対して正の数を足せば、桁あふれが起きそうな気がします。もしかすると、それでキャリーフラグが立つのかもしれません。

だったら、ゼロフラグだけ見ていればよい気もします。すっきりしないなぁ。

すっきりしませんが、今回読んだ部分をCっぽく書いて終わりにしましょう。
do {
  *(l1table + va) = pa;
  va += 4;
  pa += 0x00100000; /* L1_S_SIZEは1M */
} while (--n_sec > 0)

名前:  非公開コメント   

  • TB-URL  http://www.tokuda.net/diary/0810/tb/