目次: Linux
RISC CPUにはワード幅での読み書きしかできないアーキテクチャがありますが、より狭いハーフワードやバイトへのアクセスってどうしているのでしょう?
単純に考えると、ひとまず近しいアドレスからワード幅で読み出して、しかるべきシフト演算を行うことで、目的のハーフワードやバイトデータを得ていそうです。
例えば、ワード幅64bits、読みたいデータ幅16bits、アクセス先のアドレスが0x12として考えてみます。
まず、データバスにはアドレス0x12ではアクセスできませんので、ひとまず0x12を超えない最大の8の倍数(64bits = 8bytes)であるアドレス0x10から64bitsを読み出します。
このときバスから読み出したデータが0x1234_5678_0246_8aceだとして、バスから読み出したデータを読みたいデータ幅(= 16bits)ごとに分割し、符号ビットから近い順から並べると、
0x1234:
0x5678:
0x0246:
0x8ace:
となります。
リトルエンディアンシステムの場合、データの上位から、アドレス+6、アドレス+4、アドレス+2、アドレスそのもの、に対応しますので、
0x1234: アドレス+6 = 0x16
0x5678: アドレス+4 = 0x14
0x0246: アドレス+2 = 0x12
0x8ace: アドレスそのもの = 0x10
と対応します。
従って目的のアドレス0x12にあるデータは0x0246であることがわかり、バスから読み出したデータをシフトすべき量は16bitsであることがわかります。
同様にアドレス0x14ならばデータは0x5678となり、シフトすべき量は32bitsです。
このような処理をいちいち考えていると面倒で死にそうなので、コードで書いてみることにしました。
public static long ADDR_MASK_64 = ~0x7L;
public static long ADDR_MASK_32 = ~0x3L;
public static long ADDR_MASK_16 = ~0x1L;
public static long ADDR_MASK_8 = ~0x0L;
/**
* @param dataLenデータ幅
* @returnアドレスマスク
*/
public long getAddressMask(int dataLen) {
switch (dataLen) {
case 64:
return ADDR_MASK_64;
case 32:
return ADDR_MASK_32;
case 16:
return ADDR_MASK_16;
case 8:
return ADDR_MASK_8;
default:
throw new IllegalArgumentException("Data length" +
String.format("(0x%08x) is not supported.", dataLen));
}
}
/*
* @param addr データのアドレス
* @param data バスから読んだデータ
* @param busLen データバス幅
* @param dataLen データ幅
* @return addrにあるデータ
*/
public long readMasked(long addr, long data, int busLen, int dataLen) {
long busMask = getAddressMask(busLen);
long dataMask = getAddressMask(dataLen);
int sh = (int)(addr & ~busMask & dataMask) * 8;
return data >> sh;
}
ふっざけんなー!意味がわからんわー!!と叫んでいる半年後の自分が見えたので、併せて解説も書いておきます。
バスから読み出したデータをシフトする量を求める部分がaddr & ~busMask & dataMask * 8の部分です。
まずaddr & ~busMaskですが、バス幅で割った余りのアドレスを求めています。
例えば、幅が64bitsでアドレスが0x12ならば、8で割った余りのアドレス0x02を求めています。
次にaddr & ~busMask & dataMaskですが、データ幅境界にアドレスを揃えています。この意味と必要性の議論は後述します。
例えばデータ幅が16bitsならば、アドレスを2の倍数にします。アドレス0x01なら0x00、アドレス0x02なら0x02、アドレス0x03なら0x02です。
残りの処理はアドレスに8を掛けて右ビットシフトしています。関数の返値は上位に余計なデータが残っていますので、返り値を受け取った人は、余計な上位のデータを捨てる必要があります。
呼び出す側のコードは、こんな感じです。
//データバス幅
public static int BITS_DATA_BUS = 64;
public byte read8(long addr) {
return (byte)readMasked(addr, readWord(addr), BITS_DATA_BUS, 8);
}
public short read16(long addr) {
return (short)readMasked(addr, readWord(addr), BITS_DATA_BUS, 16);
}
public int read32(long addr) {
return (int)readMasked(addr, readWord(addr), BITS_DATA_BUS, 32);
}
他のアドレスが渡される場合も同様です。
他のバス幅、他のビット幅でも同じ考え方で処理可能です。
データ幅境界以外からデータを読んで良い、つまり0x13から読んだときに0x0246ではなくて、0x7802を返せるシステムならば、& dataMaskは不要です。
この方が便利ですが良いことばかりでもなく、バス幅をまたぐ際の読み出し、例えば0x17から16bits読む際の処理が必要になります。
しかし前述のコードではバス幅の境界をまたぐ読み出し方に対応できませんから、データ幅境界以外からデータを読めないようにして、異常動作しないように防いでいます。
この記事にコメントする
企業の生涯を追うと、若い時は素早くて何でもチャレンジするけれど、時が経つと緩慢で億劫になって何もしなくなり、やがて体が動かなくなって息絶えるんです。
なんか、企業と生き物って似ているなーと思ったら、色々浮かんできました。
メモ: 技術系の話はFacebookから転記しておくことにした。
この記事にコメントする
国道には○○号線、と番号が振られていて、重なっている区間もあります。中には始点や終点が別の国道と重なっている場合もあります。
じゃあ、国道の始まりと終わりは誰が決めるのか?と疑問に思い調べてみると、道路法に基づいた「一般国道の路線を指定する政令」(昭和四十年三月二十九日政令第五十八号)という政令で決められているそうです。
道路法といい、政令といい、何の捻りもないストレートな名前です。わかりやすいのでありがたくもあり、あまりにあっさり見つかったのでやや物足りなくもあり。うーん…。
我が家からもっとも近い国道はR171です。試しにR171について調べてみましょう。
先の政令58号によれば、国道171号線の起点は京都市、終点は神戸市です。重要な経過地は、向日市、長岡京市、京都府乙訓郡大山崎町、高槻市、茨木市、箕面市、池田市、伊丹市、尼崎市、西宮市(河原町)、芦屋市(清水町)です。
あれ?これだけ?どうも政令58号では具体的な位置(交差点名、何号線に吸収される、など)は言及していないようです。
道路整備促進期成同盟会全国協議会が発行していた「道路時刻表」によると、道路を走行した際の所要時間の概算値を見ることができます。
走行時間の付属情報として、始点、主な交差点(と交差する道)、終点も書いてあるため、どこで何の道が重なっているかがわかります。本来の用途と異なる使い方ですが、この際わかれば何でも良いのです。
が、しかし道路時刻表は2008年で絶版とのこと。うーん、最新の情報はどこにあるんですかねえ…。
この記事にコメントする
| < | 2014 | > | ||||
| << | < | 05 | > | >> | ||
| 日 | 月 | 火 | 水 | 木 | 金 | 土 |
| - | - | - | - | 1 | 2 | 3 |
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
wiki
Linux JM
Java API
2002年
2003年
2004年
2005年
2006年
2007年
2008年
2009年
2010年
2011年
2012年
2013年
2014年
2015年
2016年
2017年
2018年
2019年
2020年
2021年
2022年
2023年
2024年
2025年
過去日記について
アクセス統計
サーバ一覧
サイトの情報合計:
本日: