2014年9月29日月曜日

ジャンク品の灯油ストーブを購入

ジャンク品の灯油ストーブを購入しました。1,198円+送料1,944円。

今までポータブルストーブを一つしか持っていなくて、家の 1階と 2階にいちいち持って歩かなくちゃならないのが面倒だったので、前からもう一つ欲しかったのです。
ホームセンターで 7,000円くらいで買える商品ですが、中古で安く買えて良かったです。

中にゴキブリとかがいたら嫌なので、分解清掃しました。
2011年製で、天板の焼け具合からほとんど使用されていないもののようです。
たまたま、右のいままで使っていた2008年製ストーブと同じメーカーで、暖房能力も同じものでした。年式が新しくなってコンパクトになっています。

BDS C のサンプル - CP/M のバージョンと TPA のサイズを表示

>BDS C の使い方 - 目次

/*
BDS C のサンプル
OSのバージョンと TPA のサイズを表示する

2014年9月29日作成
*/

#include <stdio.h>

main()
{
    short *TPA;
    short TPASIZE;
    short VER;
    
    TPA=0x0006;
    TPASIZE=*TPA-0x00FF-1;
    VER=bdos(0x0C,0);
    
    printf("CP/M Version 0x%04x\n",VER);
    printf("TOP OF MEMORY 0x%04x\n",*TPA-1);
    printf("TPA SIZE 0x%04x (%u) byte / %u kbyte\n",TPASIZE,TPASIZE,TPASIZE>>10);
}

CP/M エミュレーターで実行

>cpm mem\mem
CP/M Version 0x0122
TOP OF MEMORY 0xFDFF
TPA SIZE 0xFD00 (64768) byte / 63 kbyte

>

X1 で実行


MSX で実行


>BDS C の使い方 - 目次

2014年9月26日金曜日

ThinkPad E420 HDD交換

2年半くらい、ほぼ電源入れっぱなしで使っていた ThinkPad E420 のハードディスクが壊れたので、新品のハードディスクに交換しました。
容量は 500GBくらいあれば十分なのですが、500GB が 5,000円くらい、1TB が 6,000円くらいと値段があまり変わらないので、1TB の ハードディスクを購入しました。2TB も 7,000円くらいとなかなか安かったのですが、あまり容量が多くてもバックアップに困るので、今回は 1TB です。SSD は 512GB で2万円を超えるくらいと、高価なので買えません。

Windows7 から アップグレード版の Windows8 → Windows8.1 へとOSを入れ替えて使用していたので、また Windows7 から順番にインストールしていかなきゃならないかな? と思っていたのですが、Windows8.1 までをクリーンインストールした直後のバックアップ (コントロールパネル - ファイル履歴 - (左下)システムイメージバックアップ) が残っていたので、20分くらいで Windows8.1 が使える状態まで復旧出来ました。
その後 Windows Update が終了するまで何時間もかかりましたが。

データのバックアップは毎週取っていたので、データの紛失はありませんでした。バックアップ大事。

今回はバックアップを取るのに異常に時間が掛かるようになって、なんだかおかしいなと気づいたのですが、ディスクのプロパティからエラーチェックを行ってもエラーが検出されないので、そのまま一ヶ月くらい使っていました。
イベントログを見てもエラーではなく警告レベル。
回復モードで chkdsk を実行すると不良クラスターが多発していました。
今まで使っていたハードディスク。
2年と7ヶ月で 23,023時間使って、電源投入回数 191回。平均 5日づつの連続稼働でした。

2014年9月21日日曜日

BDS C で X1 turbo の DMA を使ってみる

>BDS C の使い方 - 目次

BDS C の使い方 - SHARP X1 で使ってみる で作った IO16.CRL の out16 関数を使っています。

test.c
/*
BDS C で SHARP X1 の DMA を使うテスト

2014年9月20日作成

X1 G-RAM はI/O空間
0x2000 - 0x27FF テキストアトリビュート
0x3000 - 0x37FF テキスト
0x4000 - 0x7FFF B
0x8000 - 0xBFFF R
0xC000 - 0xFFFF G
*/

void main()
{
    /* グラフィックパレット初期化 */
    out16(0x1000,0xAA); /* 1010 1010 */
    out16(0x1100,0xCC); /* 1100 1100 */
    out16(0x1200,0xF0); /* 1111 0000 */
    
    /* GRAMクリア */
    dma_rset();
    dma_vfil(0x4000, 0xC000, 0xAA);
    dma_enbl();
    puts("hit key\n");
    pause();
    dma_vfil(0x4000, 0xC000, 0xFF);
    dma_enbl();
    puts("hit key\n");
    pause();
    dma_vfil(0x4000, 0xC000, 0x55);
    dma_enbl();
    puts("hit key\n");
    pause();
    dma_vfil(0x4000, 0xC000, 0x00);
    dma_enbl();
    puts("hit key\n");
    pause();
    
    /* GRAM Bプレーン適当書き込み*/
    dma_m2v(0x0000, 0x4000, 0x4000);
    dma_enbl();
    dma_rset();
    puts("hit key\n");
    pause();

    dma_vfil(0x4000, 0x4000, 0xAA);
    dma_enbl();
}

dma.c
/*
    DMA.C
    
    2014年9月20日作成
*/

#define DMA_ADDR 0x1F80


/*  DMA RESET */
dma_rset()
{
    char a;
    for(a=6; 0!=a; a--)
    {
        out16(DMA_ADDR, 0xC0);
    }
}

/* ENABLE DMA */
dma_enbl()
{
    out16(DMA_ADDR,0x87);
}

dma_set(cmd, size)
char cmd[];
char size;
{
    char count;
    for(count=0; count!=size; count++)
    {
        out16(DMA_ADDR, cmd[count]);
    }
}

/*  VRAM FILL
    VRAM の addr から size を data で埋める */
dma_vfil(addr, size, data)
short addr,size;
char data;
{
    char cmd[13];
    /*initb(cmd, "0x00,0x00,0x00,0x00,0x00");*/
    
    size--;     /* DMAの都合により -1 */

    out16(addr,data);   /* 最初のアドレスにデータを書く。このデータをソースにDMAで転送する */
                        /* ソースを変数へのポインタで指定しようとしたらうまく行かなかった */
    cmd[ 0]=0x79;       /* 0111 1001    WR0 */
                        /*       +----- 0:B->A 1:A->B */
    cmd[ 1]=addr;       /* PORT A アドレスL */
    cmd[ 2]=addr>>8;    /* PORT A アドレスH */
    cmd[ 3]=size;       /* ブロックレングスL */
    cmd[ 4]=size>>8;    /* ブロックレングスH */

    cmd[ 5]=0x1C;       /* 0001 1100    WR1 PORT A */
                        /*   || +------ 0:メモリー 1:I/O */
                        /*   ++-------- 00:-- 01:++ 10/11:固定 */

    cmd[ 6]=0xCD;       /* 1100 1101    WR4 */
                        /*  ++--------- 00:バイト 01:コンティニュアス 10:バースト */
    cmd[ 7]=addr;       /* PORT B アドレスL */
    cmd[ 8]=addr>>8;    /* PORT B アドレスH */

    cmd[ 9]=0x38;       /* 0011 1000    WR2 PORT B */
                        /*   || +------ 0:メモリー 1:I/O */
                        /*   ++-------- 00:-- 01:++ 10/11:固定 */

    cmd[10]=0x9A;       /* 1001 1010    WR5 */
                        /*    | +------ 0:READY L 1:READY H */
                        /*    +-------- 0:CE 1:CE/WAIT */
    cmd[11]=0xCF;       /*              WR6 LOAD */
    cmd[12]=0xB3;       /*              WR6 FORCE READY */
    
    dma_set(cmd,13);
}

/*  MEMORY -> VRAM */
dma_m2v(src, dst ,size)
short src, dst, size;
{
    char cmd[13];

    size--;
    
    cmd[ 0]=0x7D;       /* 0111 1101    WR0 */
                        /*       +----- 0:B->A 1:A->B */
    cmd[ 1]=src;        /* PORT A アドレスL */
    cmd[ 2]=src>>8;     /* PORT A アドレスH */
    cmd[ 3]=size;       /* ブロックレングスL */
    cmd[ 4]=size>>8;    /* ブロックレングスH */

    cmd[ 5]=0x14;       /* 0001 0100    WR1 PORT A */
                        /*   || +------ 0:メモリー 1:I/O */
                        /*   ++-------- 00:-- 01:++ 10/11:固定 */

    cmd[ 6]=0xCD;       /* 1100 1101    WR4 */
                        /*  ++--------- 00:バイト 01:コンティニュアス 10:バースト */
    cmd[ 7]=dst;        /* PORT B アドレスL */
    cmd[ 8]=dst>>8;     /* PORT B アドレスH */

    cmd[ 9]=0x18;       /* 0001 1000    WR2 PORT B */
                        /*   || +------ 0:メモリー 1:I/O */
                        /*   ++-------- 00:-- 01:++ 10/11:固定 */

    cmd[10]=0x9A;       /* 1001 1010    WR5 READYはH有効 */
                        /*    | +------ 0:READY L 1:READY H */
                        /*    +-------- 0:CE 1:CE/WAIT */

    cmd[11]=0xCF;       /*              WR6 LOAD */
    cmd[12]=0xB3;       /*              WR6 FORCE READY */
    
    dma_set(cmd,13);
}

コンパイル・リンク
>cpm cc dma\dma
BD Software C Compiler v1.60  (part I)
  42K elbowroom
BD Software C Compiler v1.60 (part II)
  39K to spare
>cpm cc dma\test
BD Software C Compiler v1.60  (part I)
  43K elbowroom
BD Software C Compiler v1.60 (part II)
  40K to spare
>cpm clink dma\test c:dma c:io16
BD Software C Linker   v1.60

Last code address: 0C4E
Externals start at 0C4F, occupy 0000 bytes, last byte at 0C4F
Top of memory: FDFF
Stack space: F1B1
Writing output...
  52K link space remaining
>

Z80 DMA のハンドブックを読んでも、いまいちよくわからなかったんですが、決まりきった手順なのでこのように一度 DMA にコマンドを発行してみると、そんなに面倒でもなかったです。

>BDS C の使い方 - 目次

2014年9月16日火曜日

BDS C でコマンドライン引数を取得

>BDS C の使い方 - 目次

/*
    BDS C でコマンドライン引数を取得
    普通のC言語と同じですね
*/

#include <stdio.h>

main(argc, argv)
int argc;
char *argv[];
{
    short count;
    for(count=1; count!=argc; count++)
    {
        printf("%s ",argv[count]);
    }
    /* argv[0] (このプログラムの名前)は得られないようでした */
}
>cpm cc test\argv
BD Software C Compiler v1.60  (part I)
  43K elbowroom
BD Software C Compiler v1.60 (part II)
  40K to spare
>cpm clink test\argv
BD Software C Linker   v1.60

Last code address: 0E82
Externals start at 0E83, occupy 0006 bytes, last byte at 0E88
Top of memory: FDFF
Stack space: EF77
Writing output...
  51K link space remaining
>cpm test\argv

>cpm test\argv aaa bbb ccc
aaa bbb ccc
>cpm test\argv aaa bbb ccc 11 22 33
aaa bbb ccc 11 22 33
>

>BDS C の使い方 - 目次

BDS C の制限

>BDS C の使い方 - 目次

  • コメントは /* ~ */ 型式のみ
  • 変数の型は、char, short, int, unsigned のみ
    • unsigned short とか unsigned int と書かず、unsigned とだけ書く
    • char は符号無しのみで signed char に相当するものは無い
    • longfloat は使えませんが、代わりに long の演算ライブラリが付属している。float は無し。
  • 関数名は8文字までで、大文字小文字の区別はされない
    • C で書いた関数なら、8文字以上使うとエラーが出ますが、アセンブラで書いた関数の場合エラーが出ませんので 「func_aaa2 を呼び出そうとして func_aaa1 を呼び出してしまう」ということが起こるので注意
  • 変数名も8文字までですが、大文字小文字の区別はする
  • 配列は二次元まで
  • sizeof 演算子で配列の大きさを得られない
  • sizeof(int) のように、sizeof 演算子に型指定子を直接与えることが出来ない(変数名を与えなければいけない)
  • typedef が無い
  • 関数宣言時に引数の型を括弧内に書けない
  • 関数のプロトタイプ宣言ができない
このくらいの制限に気をつければ、プログラムを書くのに困らないんじゃないでしょうか。現代の C言語と比べると制限が多いですが、アセンブラで書くのと比べれば天国ですよね。


サンプル

/*
    BDS C の制限
*/

#include <stdio.h>

main ()
{
    /* char a=10; のように宣言時に初期化出来ない */
    char c;
    short s;
    int i;
    unsigned u;
    char array[20];

    c=0xFF;
    s=0xFFFF;
    i=0xFFFF;
    u=0xFFFF;

    printf("char        SIZE:%d 0x%04x = %%d:%5d %%u:%5u\n",sizeof c,c,c,c);
    printf("int         SIZE:%d 0x%04x = %%d:%5d %%u:%5u\n",sizeof i,i,i,i);
    printf("short       SIZE:%d 0x%04x = %%d:%5d %%u:%5u\n",sizeof s,s,s,s);
    printf("unsigned    SIZE:%d 0x%04x = %%d:%5d %%u:%5u\n",sizeof u,u,u,u);
    printf("array[20]   SIZE:%d\n\n",sizeof array);

    if(0<c) puts("char = unsigned\n");
    else puts("char = signed\n");

    if(0<s) puts("short = unsigned\n");
    else puts("short = signed\n");

    if(0<i) puts("int = unsigned\n");
    else puts("int = signed\n");

    if(0<u) puts("unsigned = unsigned\n");
    else puts("unsigned = signed\n");

    printf("\n%d %d",func1(20,5),func2(20,5));
}

int func1(a, b)     /* int func1(int a, int b) とは書けない */
int a,b;
{
    return a+b;
}

int func2(a, b)
{
    return a-b;
}

実行結果

>cpm cc test\limit
BD Software C Compiler v1.60  (part I)
  41K elbowroom
BD Software C Compiler v1.60 (part II)
  39K to spare
>cpm clink test\limit
BD Software C Linker   v1.60

Last code address: 113B
Externals start at 113C, occupy 0006 bytes, last byte at 1141
Top of memory: FDFF
Stack space: ECBE
Writing output...
  51K link space remaining
>cpm test\limit
char            SIZE:1  0x00FF = %d:  255 %u:  255
int             SIZE:2  0xFFFF = %d:   -1 %u:65535
short           SIZE:2  0xFFFF = %d:   -1 %u:65535
unsigned        SIZE:2  0xFFFF = %d:   -1 %u:65535
array[20]       SIZE:2

char = unsigned
short = signed
int = signed
unsigned = unsigned

25 15
>

>BDS C の使い方 - 目次

BDS C の使い方 - 目次

CP/M 上で動く、パブリックドメインの Z80用Cコンパイラ BDS C の使い方。

BDS C の使い方 - SHARP X1 で使ってみる


>BDS C の使い方 - 目次

BDS C で マシン語とのリンクのやり方がわかったので、SHARP X1 で I/Oポートを操作してみます。

X1 は I/O空間が16ビットなので、アドレスを16ビットで指定する入出力関数を作りました。
; FILENAME : IO16.CZM
; BDS C 用 16ビットI/O空間入出力
;


        INCLUDE "BDSZASM.LIB"   ; アセンブラから c.ccc (ccc.asm) 
                                ; にアクセスするのに必要なヘッダファイル
        
        
        FUNCTION        in16    ; int in16(address)
        CALL    ma1toh          ; 1つ目の引数を HLとAレジスタに入れる
        PUSH    BC              ; BCレジスタは保存しなければいけない
        LD      B,H
        LD      C,L             ; BC=HL
        IN      A,(C)
        LD      L,A
        LD      H,0
        POP     BC
        RET                     ; HL が返り値になる
        ENDFUNC


        FUNCTION        out16   ; void out16(address, data)
        CALL    ma1toh          ; 1つ目の引数を HLとAレジスタに入れる
        LD      (arg1),HL       ; arg1 はBDSZASM.LIBで定義されているワーク
        CALL    ma2toh          ; 2つ目の引数を HLとAレジスタに入れる
                                ; 同じように 7番目の引数まで取り込める
        PUSH    BC
        LD      BC,(arg1)
        OUT     (C),L
        POP     BC
        RET
        ENDFUNC


        FUNCTION        di      ; 割り込み禁止
        DI
        RET
        ENDFUNC
        
        
        FUNCTION        ei      ; 割り込み許可
        EI
        RET
        ENDFUNC
        
        END

16ビットアドレスの入出力関数を使う簡単なテストで、VRAMの読み書きをするプログラムを作ってみました。
/*
FILENAME : X1.C
SHARP X1 で BDS C を使うテスト

X1 G-RAM はI/O空間
0x2000 - 0x27FF テキストアトリビュート
0x3000 - 0x37FF テキスト
0x4000 - 0x7FFF B
0x8000 - 0xBFFF R
0xC000 - 0xFFFF G
*/

#include <stdio.h>

void main()
{
    short addr;
    
    /* テキストVRAM適当書き込み */
    for(addr=(0x3000+80); (0x3000+80*5)!=addr; addr++)
    {
        out16(addr,addr);
    }
    
    /* テキストVRAM適当読み込みテスト */
    printf("0x3000=%02x\n",in16(0x3000));
    
    /* グラフィックパレット初期化 */
    out16(0x1000,0xAA); /* 1010 1010 */
    out16(0x1100,0xCC); /* 1100 1100 */
    out16(0x1200,0xF0); /* 1111 0000 */
    
    /* プライオリティ */
    /*out16(0x1300,0xFE);*/ /* 1111 1110 */

    /* 同時アクセスモード */
    di();
    out16(0x1A02,0x30); /* 0011 0000 */
    out16(0x1A02,0x10); /* 0001 0000 */
    
    /* GRAMクリア */
    for(addr=0x0000; 0x4000!=addr; addr++)
    {
        out16(addr,0);
    }
    /* 同時アクセス解除 */
    in16(0x1A02);
    ei();
    
    /* GRAM Bプレーン適当書き込み*/
    for(addr=0x4000; 0x8000!=addr; addr++)
    {
        out16(addr,addr);
    }
}

アセンブル、コンパイル、リンクして X1 の CP/M 互換 OS LSX-Dodgers 上で動かします。
>cpm zcasm test\IO16
BDS C CRL-format M80/L80 Preprocessor ver. 1.5
Processing the IN16 function...
Processing the OUT16 function...
Processing the DI function...
Processing the EI function...
C:IO16.MAC is ready to be assembled.

>msdos zasm test\IO16.MAC
/// ZASM /// Z-80 Assembler, MS-DOS version 1.64
Copyright (C) 1988-1994 by K.KAWABATA (Bunbun)

** Pass 1 **  Symbol table size  2000 Symbols
** Pass 2 **

  0 warning(s),   0 error(s) in assembly.

>cpm cload test\IO16
CASM Image Hex-to-Crl Converter -- v1.6
Copyright (c) 1983 William C. Colley III

C:IO16.CRL successfully generated.

>cpm cc test\x1
BD Software C Compiler v1.60  (part I)
  42K elbowroom
BD Software C Compiler v1.60 (part II)
  40K to spare

>cpm clink test\x1 test\io16
BD Software C Linker   v1.60

Last code address: 0F65
Externals start at 0F66, occupy 0006 bytes, last byte at 0F6B
Top of memory: FDFF
Stack space: EE94
Writing output...
  51K link space remaining

>
ちゃんと動きました。

I/O に 1バイト書くのに、引数をスタックにセットして、関数をコールして、関数側で引数を取り出してと効率が悪いので、VRAMのアクセスなどのようにまとまった I/Oアクセスは、マシン語で書くなり DMAを使うなりしないと遅いですね。

>BDS C の使い方 - 目次

2014年9月15日月曜日

BDS C の使い方 - マシン語とのリンク(ZASM.EXE を使う)

>BDS C の使い方 - 目次

M80 編の続きです。ZCASM の準備は M80 編をご覧ください。

必要なファイルの準備


ZASM.EXE

ザイログニーモニックのソースをアセンブルするために、MS-DOS 上で動作する、川端晃史 さんの Z80アセンブラ ZASM を使用します。CP/M の ASM.COM とほとんど同じというところが、今回の用途に都合が良いです。
ダウンロードしたら、ZASM.EXE をコンパイラと同じフォルダにコピーしておきます。

MSDOS.EXE

64ビット版 Windows のコマンドプロンプトでは、MS-DOS の(16ビット)アプリケーションが動作しませんので、Windows のコマンドプロンプトで MS-DOS のエミュレーションを行う TAKEDA, toshiya さんの MS-DOS Player for Win32-x64 を使用します。
こちらもダウンロードしたら、MSDOS.EXE をコンパイラと同じフォルダにコピーしておきます。


BDS.LIB の修正


ASM.COM と ZASM.EXE では、ソースコード記述の決まりが少し異なるので、ヘッダファイルの修正が必要です。 ZASM.EXE でアセンブルできるように、BDS.LIB 内にある

  • page 命令を削除
  • if ~ endif 命令をすべて削除
    • if not cpm ~ endif ブロックは、ブロックごと削除
    • if cpm ~ endif ブロックは ブロック内を残す

修正をして、別名で保存します。今回は BDSZASM.LIB という名前にしました。

準備はこれで終了です。


アセンブラで関数の作成


ザイログニーモニックでアセンブラの関数を作成します。インテルニーモニックの時と同じ書き方です。
; BDS C でマシン語とのリンクのテスト
; ザイログニーモニック
; ZASM.EXE 用


        INCLUDE "BDSZASM.LIB"   ; アセンブラから c.ccc (ccc.asm) 
                                ; にアクセスするのに必要なヘッダファイル
        
        
        FUNCTION        testfunc        ; int testfunc(int a, int b)
                                        ; a + b の結果を返す関数
        CALL    ma1toh          ; 1つ目の引数を HLとAレジスタに入れる
        EX      DE,HL
        CALL    ma2toh          ; 2つ目の引数を HLとAレジスタに入れる
                                ; 同じように 7番目の引数まで取り込める
        ADD     HL,DE           ; HL=HL+DE
        RET                     ; HL が返り値になる
        ENDFUNC
        
        
        FUNCTION        di      ; 割り込み禁止
        DI
        RET
        ENDFUNC
        
        
        FUNCTION        ei      ; 割り込み許可
        EI
        RET
        ENDFUNC
        
        END
TESTZASM.CZM という名前で保存しました。

アセンブル、リンクして実行します。
使用したasmfuncはインテルニーモニックの時に使用したものです。
>cpm zcasm test\testzasm
BDS C CRL-format M80/L80 Preprocessor ver. 1.5
Processing the TESTFUNC function...
Processing the DI function...
Processing the EI function...
C:testzasm.MAC is ready to be assembled.

>msdos zasm test\testzasm.mac
/// ZASM /// Z-80 Assembler, MS-DOS version 1.64
Copyright (C) 1988-1994 by K.KAWABATA (Bunbun)

** Pass 1 **  Symbol table size  2000 Symbols
** Pass 2 **

  0 warning(s),   0 error(s) in assembly.

>cpm cload test\testzasm
CASM Image Hex-to-Crl Converter -- v1.6
Copyright (c) 1983 William C. Colley III

C:testzasm.CRL successfully generated.

>cpm clink test\asmfunc test\testzasm
BD Software C Linker   v1.60

Last code address: 0EA0
Externals start at 0EA1, occupy 0006 bytes, last byte at 0EA6
Top of memory: FDFF
Stack space: EF59
Writing output...
  51K link space remaining

>cpm test\asmfunc
123 + 456 = 579

>
無事動きました。マシン語と C言語のリンクができれば、何でも出来ますね。

ZASM の説明書に書いてありますが、ZASM で直接バイナリファイルを出力させると ORG 命令が複数あるとき一部のコードが出力されないので、HEX ファイルで出力させてから、 CLOAD.COM でバイナリ(CRL)に変換しています。(CLOAD.COM については、インテルニーモニック編をご覧ください
M80 の時より ZASM のほうがすっきりしていいですね。

>BDS C の使い方 - 目次

間引きした人参のかき揚げ丼

今日のご飯は、間引きした人参と玉葱のかき揚げ丼。
揚げ油を新しくしたので、とても美味しかった。

2014年9月14日日曜日

枝豆を収穫

枝豆が膨らんできたので収穫して食べました。冷凍枝豆も美味しいけど、畑で採った枝豆はもっと味が濃くて美味しいです。

6月に種を蒔いて、8月に植え替えたキャベツが大きくなってきました。初めてキャベツを蒔いてみましたが、ちゃんと育っているようです。

2014年9月13日土曜日

BDS C の使い方 - マシン語とのリンク(M80.COM を使う)

>BDS C の使い方 - 目次

インテルニーモニック編の続きです。M80 の代わりに ZASM.EXE を使う例もご覧ください。

必要なファイルの準備


M80.COM L80.COM

ザイログニーモニックのソースをアセンブルするために、CP/M で動くマイクロソフト社のアセンブラ M80.COM と、リンカ L80.COM が必要になりますので、なんとか入手してください。(windows 用の Z80 アセンブラでも試してみましたが、BDS C でうまくリンクできませんでした M80.COM L80.COM の代わりに、MS-DOS版の ZASM.EXE を使うことが出来ました。そちらもご覧ください。)
http://www.retroarchive.org/ さんの CP/M のページで M80.COM と L80.COM をダウンロードできるようになっているのですが、ライセンスについての記述を見つけることが出来ませんでした。
今回は MSX-DOS TOOLS に入っている物を使用しました。持っててよかった MSX-DOS TOOLS。
M80.COM L80.COM をコンパイラのあるフォルダにコピーしておきます。

ZCASM.COM

CASM.COM のザイログニーモニック用である、ZCASM.COM のソースコードがBDS C のアーカイブに含まれているので、BDS C でコンパイルしてバイナリを作成します。

bsdc160\ZCASM.LBR        ZCASM のソースコードなどが入っているアーカイブファイル
bsdc160\LDIR.COM         .LBR ファイルの内容を表示するツール
bsdc160\LBREXT.COM       .LBR ファイルから内容を取り出すツール
extra\BDSCIO.H           BDS C 1.5 のヘッダファイル(ZCASMのコンパイルに必要)
extra\DEFF15.CRL         BDS C 1.5 のライブラリ(ZCASMのコンパイルに必要)

をコンパイラのあるフォルダにコピーします。
まず ZCASM.LBR の中にどんなファイルが入っているか見てみます。
>cpm ldir zcasm

Library File = zcasm   .LBR

   Name           Length     Method   Cre Date   Mod Date       Comments
============   ============ ========  ====================  =================
ZCASM   .CZ     90r  11.25k Crunched  17 Feb 89  17 Feb 89  (--> ZCASM.C  )
ZCASM   .CZM    90r  11.25k Crunched  17 Feb 89  17 Feb 89  (--> ZCASM.COM)
ZCASM   .DZC    29r   3.62k Crunched  17 Feb 89  17 Feb 89  (--> ZCASM.DOC)
ZCASM   .SUB     1r   0.12k    --     17 Feb 89  17 Feb 89
ZCASM15 .CZ     97r  12.12k Crunched  16 Oct 89  16 Oct 89  (--> ZCASM15.C  )

ZCASM.LBR を解凍します。
>cpm lbrext zcasm *.* U
 File: ZCASM.CZ     (02/17/89) ---> B0:ZCASM.C
 File: ZCASM.CZM    (02/17/89) ---> B0:ZCASM.COM
 File: ZCASM.DZC    (02/17/89) ---> B0:ZCASM.DOC
 File: ZCASM.SUB    (02/17/89) ---> B0:
 File: ZCASM15.CZ   (10/16/89) ---> B0:ZCASM15.C

解凍されたファイルに ZCASM.COM が含まれていますが、古いバージョンなので削除して、ZCASM15.C をコンパイルして新しく作ります。
>cpm cc zcasm15
BD Software C Compiler v1.60  (part I)
  20K elbowroom
BD Software C Compiler v1.60 (part II)
  23K to spare
>cpm clink zcasm15 deff15             <-- BDS C V1.5のライブラリを指定するのを忘れない
BD Software C Linker   v1.60

Last code address: 5A70
Externals start at 5A71, occupy 256F bytes, last byte at 7FDF
Top of memory: FDFF
Stack space: 7E20
Writing output...
  32K link space remaining
>del zcasm.com

>ren zcasm15.com ZCASM.COM

>cpm zcasm
BDS C CRL-format M80/L80 Preprocessor ver. 1.5
Usage:  zcasm [-c] [-o <name>] <filename>
-C: don't strip comments from input and output
-O <name>: Call the output file <name>.MAC
ZCASM.COM の V1.5 が出来上がりました。


BDS.LIB の修正


ASM.COM と M80.COM では、ソースコード記述の決まりがすこし異なるので、ヘッダファイルの修正が必要です。 M80.COM でアセンブルできるように、BDS.LIB 内にある

  • equ のシンボルフィールド(ラベル)末尾の コロン ':' を削除
  • dsdb のラベルの末尾の コロン ':' の付いていないところに コロン ':' を追加
  • if not cpm のブロック内をコメントアウト

して、別名で保存します。今回は BDSZ.LIB という名前にしました。
   ↓ equ 命令のシンボル(ラベル)から コロン ':' を全部削除する
base:   equ 0000h       ;either 0 or 4200h for CP/M systems
fcb:    equ base+5ch    ;default file control block
tbuff:  equ base+80h    ;sector buffer
     ↓ ds と db 命令のシンボル(ラベル)に コロン ':' をつける
errnum: ds      1       ;error code from file I/O operations
rseed:  ds      8       ;the random generator seed
args:   ds      14      ;"arghak" puts args passed on stack here.
↓ この三行をコメントアウトする
        if not cpm
;CCCORG EQU     WHATEVER        ;if not runninng under cp/m, set this to load addr,
;RAM    EQU     WHATEVER2       ;set this to address of CCC's ram area
;BASE   EQU     WHATEVER3       ;and this to the base of system memory (`base' is 
                                ;the re-boot location under cp/m; for non-cp/m oper-
                                ;ation, it should be set to a safe place to jump to on
                                ;error or user-abort.
        endif
M80のマニュアルを見ても文法合ってるようなんだけど、アセンブルされてしまってラベルの二重定義になってしまう
準備はこれで終了です。


アセンブラで関数の作成


ザイログニーモニックでアセンブラの関数を作成します。インテルニーモニックの時と同じ書き方です。
; BDS C でマシン語とのリンクのテスト
; ザイログニーモニック
; M80.COM と L80.COM が必要

        .Z80                    ; M80 に Z80 命令セットモードを指示
        ASEG                    ; PC絶対モード
                                ; ソースの先頭に必要なので、忘れないように注意
        
        INCLUDE "BDSZ.LIB"      ; アセンブラから c.ccc (ccc.asm) 
                                ; にアクセスするのに必要なヘッダファイル(上で修正したもの)
        
        
        FUNCTION        testfunc        ; int testfunc(int a, int b)
                                        ; a + b の結果を返す関数
        CALL    ma1toh          ; 1つ目の引数を HLとAレジスタに入れる
        EX      DE,HL
        CALL    ma2toh          ; 2つ目の引数を HLとAレジスタに入れる
                                ; 同じように 7番目の引数まで取り込める
        ADD     HL,DE           ; HL=HL+DE
        RET                     ; HL が返り値になる
        ENDFUNC
        
        
        FUNCTION        di      ; 割り込み禁止
        DI
        RET
        ENDFUNC
        
        
        FUNCTION        ei      ; 割り込み許可
        EI
        RET
        ENDFUNC
        
        END
アセンブル、リンクして実行してみました。
使用したasmfuncはインテルニーモニックの時に使用したものです。
>cpm zcasm test\testfunc
BDS C CRL-format M80/L80 Preprocessor ver. 1.5
Processing the TESTFUNC function...
Processing the DI function...
Processing the EI function...
C:testfunc.MAC is ready to be assembled.

>cpm m80 = test\testfunc

No Fatal error(s)

>cpm l80 test\testfunc, test\testfunc.crl/n/e

MSX.L-80  1.00  01-Apr-85  (c) 1981,1985 Microsoft
%Overlaying Program area

Data    0100    069C    < 1436>

52376 Bytes Free
[0000   069C        6]

>cpm clink test\asmfunc test\testfunc
BD Software C Linker   v1.60

Last code address: 0EA0
Externals start at 0EA1, occupy 0006 bytes, last byte at 0EA6
Top of memory: FDFF
Stack space: EF59
Writing output...
  51K link space remaining

>cpm test\asmfunc
123 + 456 = 579

>
無事動きました。マシン語と C言語のリンクができれば、何でも出来ますね。

L80 でオーバーラップの警告が出ていますが、出力ファイル先頭に L80 が生成するジャンプ命令と、CRL ファイルのディレクトリデータのアドレスが重複す為だと思います。バイナリエディタで生成された CRL ファイルを見てみると正しい値になっているので、異常無いと思います。
L80 のマニュアルを見ても、L80 では実行ファイル先頭のジャンプ命令を、生成しないように指定できないようでした。

>BDS C の使い方 - 目次

BDS C の使い方 - マシン語とのリンク(インテルニーモニック)

>BDS C の使い方 - 目次

必要なファイルの準備


ASM.COM

C 言語から使えるマシン語の関数を使用するために、CP/M 標準のアセンブラ ASM.COM が必要になります。The Unofficial CP/M Web site で入手できる、CP/M 2.2 BINARY (cpm22-b.zip) 等から ASM.COM をコンパイラのあるフォルダにコピーしておきます。
(CP/M は、現在ライセンスを保有している Lineo社 の許可を受けて "Unofficial CP/M Web Site" で配布されていますが、"Unofficial CP/M Web Site" 以外で再配布しても良いとは書かれていないようでした。)

CASM.COM と CLOAD.COM

BDS C 用の関数を作成する ASM.COM 用のプリプロセッサ CASM.COMASM.COM が出力した HEXファイルをバイナリファイルに変換する CLOAD.COM が必要なので、BDS C のアーカイブに含まれているソースコードから BDS C でコンパイルして作成します。
bdsc160\work\CASM.C
bdsc160\work\CLOAD.C
をコンパイラのあるフォルダにコピーして、windows のコマンドプロンプトで以下のようにコンパイルしました。
>cpm cc casm -e4b00
BD Software C Compiler v1.60  (part I)
  20K elbowroom
BD Software C Compiler v1.60 (part II)
  24K to spare

>cpm clink casm
BD Software C Linker   v1.60

Last code address: 4AF5
Externals start at 4B00, occupy 1285 bytes, last byte at 5D84
Top of memory: FDFF
Stack space: A07B
Writing output...
  36K link space remaining 

>cpm cc cload
BD Software C Compiler v1.60  (part I)
  40K elbowroom
BD Software C Compiler v1.60 (part II)
  38K to spare

>cpm clink cload
BD Software C Linker   v1.60

Last code address: 1FCB
Externals start at 1FCC, occupy 0148 bytes, last byte at 2113
Top of memory: FDFF
Stack space: DCEC
Writing output...
  47K link space remaining

BDS.LIB

アセンブラで書いた関数から、引数のアクセス等 BDS C の機能を利用するためのヘッダファイル BDS.LIB が BDS C のアーカイブに含まれているので、bdsc160\work\BDS.LIB をコンパイラのあるフォルダにコピーしておきます。

準備はこれで終了です。


アセンブラで関数の作成


試しにアセンブラで簡単な関数を作成してみます。
インテルニーモニックで下のソースコードを書いて TESTFUNC.CSM という名前で保存しました。
; BDS C でマシン語とのリンクのテスト
; インテルニーモニック


        INCLUDE "BDS.LIB"       ; アセンブラから c.ccc (ccc.asm) 
                                ; にアクセスするのに必要なヘッダファイル
        
        
        FUNCTION        testfunc        ; int testfunc(int a, int b)
                                        ; a + b の結果を返す関数
        CALL    ma1toh          ; 1つ目の引数を HLとAレジスタに入れる
        XCHG                    ; EX DE,HL
        CALL    ma2toh          ; 2つ目の引数を HLとAレジスタに入れる
                                ; 同じように 7番目の引数まで取り込める
        DAD     D               ; HL=HL+DE
        RET                     ; HL が返り値になる
        ENDFUNC
        
        
        FUNCTION        di      ; 割り込み禁止
        DI
        RET
        ENDFUNC
        
        
        FUNCTION        ei      ; 割り込み許可
        EI
        RET
        ENDFUNC

        END
FUNCTION ~ ENDFUNC 擬似命令で囲まれた部分が、C言語から見える一つの関数になります。
C 言語から引数がスタックに積まれて関数がコールされ、関数は HL レジスタに返り値を入れてリターンします。関数では BC レジスタ以外のレジスタを破壊しても問題ありません。
マニュアルの「Chapter 2 CRLファイルの構造」「Chapter 8.1 CASM プリプロセッサ」に説明があるので、ざっと読んでおきます。

アセンブル

windowsのコマンドプロンプトで以下のようにアセンブルしました。
>cpm casm test\TESTFUNC

>cpm asm test\TESTFUNC

>cpm cload test\TESTFUNC
これで、TESTFUNC.CRL が作成されます。


C 言語から使ってみる


作成したアセンブラの関数を、C 言語から使ってみます。下のソースコードを asmfunc.c という名前で作成しました。
#include <stdio.h>

main()
{
    int a,b,c;
   
    a=123;
    b=456;
    c=testfunc(a,b);    /* アセンブラで書いた関数を呼び出し */
   
    printf("%d + %d = %d\n",a,b,c);
}
コンパイルして TESTFUNC.CRL とリンクさせます。
>cpm cc test\asmfunc.c                        コンパイル

>cpm clink test\asmfunc test\TESTFUNC         asmfunc と TESTFUNC のリンク

>cpm test\asmfunc                             実行
123 + 456 = 579
無事動きました。

割り込みを禁止/許可する関数も作ったので、MSX の VDP を直接アクセスして縦スクロールしてみるプログラム msxvdp.c を作ってみました。
/*
    BDS C でMSXのI/Oを直接操作でVDPをいじってみる
*/

#define VDP 0x98

scrool(line)
int line;
{
    di();
    outp(VDP+1,line);
    outp(VDP+1,0x80+23);
    ei();
}

main()
{
    char count;
   
    di();               /* 割り込み禁止 */
    outp(VDP+1,0);      /* VRAM アドレスセット */
    outp(VDP+1,0x40);   /* VRAM アドレスセット 書き込み指示 */
    ei();               /* 割り込み許可 */
   
    for(count=0xFF; 0!=count; count--)
    {
        outp(VDP,count);    /* VRAM 書き込み 自動インクリメント*/
    }
   
    for(count=0xFF; 0!=count; count--)
    {
        scrool(count);
        sleep(1);       /* BDS C にある時間待ちの関数 */
    }
}
コンパイルして TESTFUNC.CRL とリンクさせます。
>cpm cc test\msxvdp.c                        コンパイル

>cpm clink test\msxvdp test\testfunc         msxvdp と TESTFUNC のリンク
MSX で msxvdp.com を実行してみると

にゅろーんと1ドット単位で縦スクロースしました。
やってみるとけっこう簡単に C 言語とマシン語とのリンクができましが、インテルニーモニックだと馴染みが薄いし、表記できない Z80の命令もあるので、ザイログニーモニック編に続きます。

>BDS C の使い方 - 目次

2014年9月12日金曜日

BDS C の使い方 - MSXで使ってみる

>BDS C の使い方 - 目次

パブリックドメインの C コンパイラ、 BDS C を MSX で使ってみます。今回は画面キャプチャが容易なエミュレータを使用しましたが、実機でも同じように使用できます。
BDS C の概要は BDS C の使い方 - とりあえず使ってみる などをご覧ください。


インストール


インストールと言っても、必要なファイルをコピーするだけの簡単な作業です。
配布されている ZIPファイル (bdsc-all.zip) から、以下のファイルを MSX-DOS の起動ディスク(のルートディレクトリ)にコピーするだけです。
bdsc160\
    CC.COM           コンパイラ本体
    CC2.COM          コンパイラ本体2
    CLINC.COM        リンカ
    DEFF.CRL         標準ライブラリ
    DEFF2.CRL        標準ライブラリ2
    C.CCC            スタートアップルーチン

bdsc160\work\
    STDIO.H          標準ライブラリのヘッダファイル


Hello, world!


実際に Bドライブ に用意した hello.c をコンパイルしてみました。
cc b:hello             コンパイル(CC2は自動的に呼び出されます)
clink b:hello          リンク

コンパイルとリンクが正常に終了したら、出来上がった hello.com を実行してみます。
b:hello

"Hello, world!" と表示されたら成功です。

その時の、MSXの画面の様子はこんな感じ。

BDS C には Z80 の I/Oポートを直接アクセスする関数があるので、MSX の VDP を直接操作するプログラムを作ってみます。

/*
    BDS C でMSXのI/Oを直接操作してVDPをいじってみる
*/

#define VDP 0x98

main()
{
    char count;

    outp(VDP+1,0);           /* VRAM アドレスセット */
    outp(VDP+1,0x40);        /* VRAM アドレスセット 書き込み指示 */
    for(count=0xFF; 0!=count; count--)
    {
        outp(VDP,count);     /* VRAM 書き込み 自動インクリメント*/
    }
}

ここで使った outp() なんかの便利な関数は、マニュアルの Chapter3 で簡潔に説明されていますし、ソースコード(DEFF2A.CSM DEFF2B.CSM)も付属していますので見てみてください。英語だけれど見ればわかる程度の文章です。
BASICによくある、メモリを直接読み書きする PEEK POKE 関数や、レジスタに値をセットして指定アドレスを CALL する 関数、BDOSコール、メモリのブロック転送なんかも有って便利に使えそうです。

コンパイルして実行してみると

動きました。
動いたのですが、VDPのアクセス中に、割り込みを禁止しなければならない所でそれを行っていないので、この例では VRAMアドレスの指定がきちんと出来ていません。

というわけで、マシン語とのリンクに続きます。

>BDS C の使い方 - 目次

2014年9月11日木曜日

BDS C の使い方 - とりあえず使ってみる

>BDS C の使い方 - 目次

Z80 のプログラムを書く用事があったんですが、アセンブラは大変なので自由に使える Cコンパイラは無いものかな?と google で検索すると、BDS C V1.6 が明確にパブリックドメイン宣言されて、BD Software さんから配布されています。
他にもいくつかあるようでしたが、BDS C には立派な PDF形式のマニュアルも同梱されていて、使い方を調べやすそうな感じがしたので、今回はこれを使うことに決めました。

シャープから X1 用として販売されていた Cコンパイラが、この BDS C (α-C) だったようですね。


ダウンロード


BD Software さんの BDS C のページ の Primary Downloads の所から 1.5MB の ZIP ファイル (bdsc-all.zip) をダウンロードします。この bdsc-all.zip に C コンパイラの動作に必要なファイルすべてと、PDF形式のマニュアルが含まれています。
Documentation のところに容量の大きなマニュアル (bdsc-guide-full.pdf) もあるのですが、表紙画像の有無だけの違いのようで、内容は同じようでした。

BDS C は CP/M 上で動作するのですが、実機より現代の PC で動作させたほうが高速ですし、普段使っているテキストエディタも使えてずっと便利なので、Windows 上で CP/M のエミュレータを使用して、その上で BDS C を実行することにします。
今回は、村上 敬司 さんの CP/M program EXEcuter (CPM.EXE) を使用します。64bit版 windows では MS-DOS汎用のプログラムは動作しないので、win32版の CP/M program EXEcutor for Win32 をダウンロードしておきます。


コンパイラを動かすのに必要なファイル


以下のファイルだけがあればコンパイラが動作できます。

bdsc160\
    CC.COM           コンパイラ本体
    CC2.COM          コンパイラ本体2
    CLINC.COM        リンカ
    DEFF.CRL         標準ライブラリ
    DEFF2.CRL        標準ライブラリ2
    C.CCC            スタートアップルーチン

bdsc160\work\
    STDIO.H          標準ライブラリのヘッダファイル

適当な作業フォルダ(ディレクトリ)を作成して、これらのファイルをコピーします。bdsc160\ と bdsc160\work\ の中身をすべてコピーしても良いです。
続いて、windows 上で動作させるために、CP/M program EXEcuter の実行ファイル CPM.EXE も同じフォルダにコピーします。

動かしてみる


これで動作の準備が整ったので、とりあえずコンパイラを起動してみましょうか。
コマンドプロンプトを開いて、ファイルをコピーしたフォルダに移動して、

cpm cc

と入力します。
C:\   \work>cpm cc
BD Software C Compiler v1.60  (part I)
Usage:
cc  [-p] [-o] [-a ] [-d ] [-m ] [-e ] [-r ]

C:\   \work>
このように使い方が表示されたら正常です。


Hello, world!


続けて、実際に簡単なプログラムをコンパイルしてみます。
ソースファイル .\test\hello.c をメモ帳や普段使っているテキストエディタで作成します。
内容はこんな感じで
#include <stdio.h>

main()
{
    printf("Hello, world!\n");
}

早速コンパイルします。
cpm cc test\hello             コンパイル(CC2は自動的に呼び出されます)
cpm clink test\hello          リンク

コンパイルとリンクが正常に終了したら、出来上がった hello.com を実行してみます。
cpm test\hello

"Hello, world!" と表示されたら成功です。

出力はこんな感じ

C:\   \work>cpm cc test\hello
BD Software C Compiler v1.60  (part I)
  43K elbowroom
BD Software C Compiler v1.60 (part II)
  40K to spare
C:\   \work>cpm clink test\hello
BD Software C Linker   v1.60

Last code address: 0E49
Externals start at 0E4A, occupy 0006 bytes, last byte at 0E4F
Top of memory: FDFF
Stack space: EFB0
Writing output...
  51K link space remaining
C:\   \work>cpm test\hello
Hello, world!

C:\   \work>

出来上がったファイルは次の通り
hello.c            自分で書いたソースファイル
hello.CRL          hello.c をコンパイルして出来たライブラリファイル
                   同名のソースファイル内のすべての関数が入る
hello.com          hello.CRL とスタートアップルーチン (C.CCC) や
                   使用したライブラリをリンクして出来上がった実行ファイル

とても簡単に使うことができました。
コンパイルとリンクを続けて行う、バッチファイルを作っておいても楽で良いですね。



BDS C のマニュアルはきちんとしていて読みやすいのですが、OCRで作成したのか単語中に余分なスペースが入っていて、機械翻訳にそのまま掛けられずちょっと不便でした。
当初は頑張って英語の付属マニュアルを読んでいたのですが、なにか楽できないかなと検索したら、amazon で古本の「BDS C の使い方 稲川幸則 渡辺修 共訳 工学図書」が送料込み 257円で在庫していたので、さっと購入しました。本のタイトルからは少しわかりにくいのですが、この本は V1.5a の BDS C User's Manual をそのまま翻訳したもので、 v1.6 でもほとんどそのまま通用しました。(V1.5 と V1.6 ではライブラリが変更されていて、独自のフィル操作関数だったのが V1.6 で標準Cライブラリと同じになっています。配布されている V1.6 には V1.5 のライブラリ(BDSCIO.H と DEFF15.CRL)も付属しています)

>BDS C の使い方 - 目次

2014年9月3日水曜日

CRC-16-CCITT をテーブルを使わないで計算するソースコード

ついでに、テーブルを使わないでCRCを計算する関数も作成しました。どうしても使用するプログラムメモリを削減したい場合向け。

テーブルを使って高速に計算する関数はこちら

/*
CRC-16-CCITT を計算する
// wikipediaにある実装例の通り(wikipediaの例は多項式が反転、シフトの向きが右)
// http://ja.wikipedia.org/wiki/%E5%B7%A1%E5%9B%9E%E5%86%97%E9%95%B7%E6%A4%9C%E6%9F%BB

MB8877のデータシートに
X^0+X^5+X^12+X^16 と書いてあったので、フロッピーのCRCは CRC-16-CCITT

2014年9月2日作成
*/


unsigned short crc_1byte(unsigned char in, unsigned short crc)
{
/* 1byteづつCRCを計算する
wikipediaにある実装例を、テーブルを使わないように書き換えただけ。

unsigned short crc;
crc = 0xffff; // 初期値
crc = crc_1byte(0xA1, crc);
crc = crc_1byte(0xA1, crc);
crc = crc_1byte(0xA1, crc);
crc = crc_1byte(0xFE, crc); // A1 A1 A1 FE : ID ADDRESS MARK
crc = crc_1byte(0x02, crc); // C
crc = crc_1byte(0x00, crc); // H
crc = crc_1byte(0x03, crc); // R
crc = crc_1byte(0x02, crc); // N

これで crc に計算結果が入る (0x4165)
*/

#define polynomial 0x1021 // 多項式 CRC-16-CCITT X^16+X^12+X^5+X^0 (1 0001 0000 0010 0001)
// 0x8005 //        CRC-16-IBM X^16+X^15+X^2+X^0 (1 1000 0000 0000 0101)
unsigned char bit;
unsigned short a;

a = ((in << 8) ^ crc) & 0xFF00;
for (bit = 8; bit != 0; bit--)
{
if (0 == (a & 0x8000)) a = (a << 1); // 最上位ビットが0ならシフトのみ
else a = polynomial ^ (a << 1); // 最上位ビットが1ならシフトして多項式でXOR
}
crc = a ^ (crc << 8);

return crc;
}

unsigned short crc_block(unsigned char buff[], unsigned short size, unsigned crc)
{
/*
ブロックのCRCを計算する

unsigned short crc;
unsigned char buff[]=
{
0xA1,0xA1,0xA1,0xFE // ID ADDRESS MARK
,0x02 // C
,0x00 // H
,0x03 // R
,0x02 // N
,0x41 // CRC H
,0x65 // CRC L
};
crc = crc_block(buff, 8, 0xFFFF);
これで crc に IDAM から N までの計算結果が入る (0x4165)

crc = crc_block(buff,10, 0xFFFF);
CRCを含めて計算すると、CRCが正しければ計算結果がゼロになる
*/

unsigned short pos;

for(pos=0; pos!=size; pos++)
{
crc = crc_1byte(buff[pos], crc);
}

return crc;
}


2014年9月2日火曜日

CRC-16-CCITT を計算するソースコード

CRCを計算する用事があったので、CRCを計算する関数を作成しました。

テーブルを使わない、省メモリな関数はこちら

Visual Studio Express 2013 で動かしてみて計算が合っている様子だったので、たぶん間違っていないんじゃないかな?

/*
CRC-16-CCITT を計算する
// wikipediaにある実装例の通り(wikipediaの例は多項式が反転、シフトの向きが右)
// http://ja.wikipedia.org/wiki/%E5%B7%A1%E5%9B%9E%E5%86%97%E9%95%B7%E6%A4%9C%E6%9F%BB

MB8877のデータシートに
X^0+X^5+X^12+X^16 と書いてあったので、フロッピーのCRCは CRC-16-CCITT

2014年9月1日作成


// テーブルを作成する
void print_crctable(void)
{
#define polynomial 0x1021 // 多項式 CRC-16-CCITT X^16+X^12+X^5+X^0 (1 0001 0000 0010 0001)
// 0x8005 //        CRC-16-IBM X^16+X^15+X^2+X^0 (1 1000 0000 0000 0101)

unsigned int i, j;
unsigned short a;

printf("\n多項式 0x%04X\n", polynomial);

for (i = 0; i < 256; i++)
{
a = (i << 8);
for (j = 0; j < 8; j++)
{
if (0 == (a & 0x8000)) a = (a << 1); // 最上位ビットが0ならシフトのみ
else a = polynomial ^ (a << 1); // 最上位ビットが1ならシフトして多項式でXOR
}
printf("0x%04X,", a);
if (((i & 0x0F) == 0x07) || ((i & 0x0F) == 0x0F)) printf("\n");
}
printf("\n");
}
*/


const unsigned short crc_table[256] =
{
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
};

unsigned short crc_1byte(unsigned char in, unsigned short crc)
{
/*
1byteづつCRCを計算する

unsigned short crc;
crc = 0xffff; // 初期値
crc = crc_1byte(0xA1, crc);
crc = crc_1byte(0xA1, crc);
crc = crc_1byte(0xA1, crc);
crc = crc_1byte(0xFE, crc); // A1 A1 A1 FE : ID ADDRESS MARK
crc = crc_1byte(0x02, crc); // C
crc = crc_1byte(0x00, crc); // H
crc = crc_1byte(0x03, crc); // R
crc = crc_1byte(0x02, crc); // N

これで crc に計算結果が入る (0x4165)
*/


return crc_table[((crc >> 8) ^ in) & 0xFF] ^ (crc << 8);
}

unsigned short crc_block(unsigned char buff[], unsigned short size, unsigned crc)
{
/*
ブロックのCRCを計算する

unsigned short crc;
unsigned char buff[]=
{
0xA1,0xA1,0xA1,0xFE // ID ADDRESS MARK
,0x02 // C
,0x00 // H
,0x03 // R
,0x02 // N
,0x41 // CRC H
,0x65 // CRC L
};
crc = crc_block(buff, 8, 0xFFFF);
これで crc に IDAM から N までの計算結果が入る (0x4165)

crc = crc_block(buff,10, 0xFFFF);
CRCを含めて計算すると、CRCが正しければ計算結果がゼロになる
*/

unsigned short pos;

for(pos=0; pos!=size; pos++)
{
crc = crc_1byte(buff[pos], crc);
}

return crc;
}