全ROMチェックサム計算プログラム

全ROMデータを加算したチェックサム計算プログラムを作りたいのですが、16bitCPUのためかアドレスインクリメントで0xFFFFの次が0x0000に戻ってしまい、全ROM領域を加算することができません。
ここで聞くことではないかもしれませんが、分かる方いたら御教示お願いいたします。

【やりたいこと】
アドレス0x00000~0x1FFFF(128KB)のROMデータを1byte単位で加算した下位2byteのチェックサム値を算出する
(Cソースでもアセンブラでも可)

【試したCソース】
#define ROM_START 0x00000
#define ROM_END 0x1FFFF

main()
{
unsigned char *adr;
unsigned char *rom_end;
unsigned short sum;

adr = (unsigned char *)ROM_START;
rom_end = (unsigned char *)ROM_END;
sum = 0;

while(adr <= rom_end) {
sum += *adr;
adr ++;
};
}
  • yamamotoさんはじめまして。

    >加算した下位2byteのチェックサム値を算出する

    下位2byteだけを使うならunsigned shortで桁あふれが起きても問題ないと思います。
    どうせ上位は捨ててしまうのですから。


    ちゃんと4バイトで計算して下位2バイトを残すのであれば
    unsigned short sum(2バイト)ではなく
    unsigned long sum(4バイト)で試してみてはいかがでしょう。

  • yamamotoさんこんなんでいかがでしょうか?
    ごめんなさい最初の投稿は早とちりでした。

    問題は、コンパイラオプションでラージモデル(コード1Mバイト、データ1Mバイト)を選択したにも関わらず、ポインタ変数(4バイト変数)が64K空間しか扱えていないということですね。(少なくともアセンブラコード上はadr++を2バイト変数として扱っている模様)

    対策でポインタをやめて、キャストでメモリアクセスに変えてみました。

    #define ROM_START 0x00000
    #define ROM_END 0x1FFFF

    main()
    {
    unsigned long adr;
    unsigned long rom_end;
    unsigned short sum;

    adr = ROM_START;
    rom_end = ROM_END;
    sum = 0;

    while(adr <= rom_end) {
    sum += (unsigned short)(*(unsigned char *)adr);
    adr ++;
    };
    }

  • yamamotoさん

    こんにちはTOYKEYと申します。
    現在の問題は20bitアドレスを扱いたいのに、
    16bitアドレスを使用したコードが出力される為に
    起こる問題ですね。

    このような場合対象となる変数に__farをつけるとそのポインタは20bitフルでアクセスできるようなコードになるはずです。

    __far unsigned long adr;
    __far unsigned long rom_end;

    こうしたら望み通りの動作になりませんか?
    試してみてください。
  • 分かりました!
    コンパイラの仕様でどんなに頑張ってもポインタのインクリメントやアドレス比較は下位2バイトしかしてくれないそうです。

    help見ると
    >- farポインタの加減算は,下位2バイトで行い,上位は変化しません。
    ですって。

    ポインタを使っての64K超えは無理なので、
    「回答番号 2」のコードでやるしかなさそうです。

  • 頭の体操でROMのチェックサム計算アセンブラ版を作ってみました。
    16bitマイコンで16bitを超える計算させると、頭がパンクしますね。

    ちなみに、シミュレータでブレークポイントを使うとROMのチェックサムが狂うようです。OCDエミュレータならソフトブレークを設定した命令コードが書き換わるのは仕方ないですけれども、シミュレータでこの仕打ちにはやられました。


    【アセンブラソース】
    ROMSTART EQU 000000H
    ROMEND EQU 01FFFFH
    ORG 0H
    DW START
    ORG 0D8H
    START:
    CLRW AX
    MOVW DE,AX; DEにチェックサム
    MOVW HL,#LOWW ROMSTART
    MOV B,#LOW (ROMSTART/10000H)
    LOOPS:
    MOV A,B
    MOV ES,A
    CMP A,#LOW (ROMEND/10000H)
    BZ $LOOPE ;最終セグメントならLOOPEへ
    LOOP:
    MOV A,ES:[HL]
    MOV X,A
    CLRB A
    XCHW AX,DE
    ADDW AX,DE
    XCHW AX,DE
    INCW HL
    CLRW AX
    CMPW AX,HL
    BNZ $LOOP
    MOV A,B
    INC B
    CMP A,#LOW (ROMEND/10000H)
    BC $LOOPS ;LOOPSに無条件ジャンプでも良さそう
    BR $LOOPOUT
    LOOPE:
    MOV A,ES:[HL]
    MOV X,A
    CLRB A
    XCHW AX,DE
    ADDW AX,DE
    XCHW AX,DE
    MOVW AX,#LOWW ROMEND
    CMPW AX,HL
    INCW HL
    BNZ $LOOPE
    LOOPOUT:
    MOVW AX,DE ;チェックサムをAXにコピー
    BR $$ ;無限ループで止まる
    END

  • みなさん、ご回答ありがとうございました。
    (アセンブラの回答まで)
    ポインタで「__far」を付けたりして試してたんですが、
    コンパイラの仕様で下位2byteでしかやってくれないとは・・・
    やられました。。。