ファミコン
数字をカウントするだけ。プログラムの雛形にと思ってメモ。わけもわからず書いている部分が減ってきてよい感じです。
;;; 秒数をカウントして表示 .inesprg 1 ; プログラムバンク数 .ineschr 1 ; CHR データバンク数 .inesmir 0 ; 水平ミラーリング .inesmap 0 ; マッパー ;;; ======================= ;;; 割り込みハンドラバンク ;;; ======================= .bank 1 .org $FFFA ; $FFFA から開始 .dw main ; VBlank 割り込み .dw start ; リセット割り込み .dw 0 ; ハードウェア/ソフトウェア割り込み ;;; ======================= ;;; CHR データバンク ;;; ======================= .bank 2 ;; sprite .org $0000 ; .incbin "sprite.chr" ;; BG .org $1000 ;; http://hp.vector.co.jp/authors/VA042397/nes/sample.html .incbin "character.chr" ;;; ======================= ;;; プログラムバンク ;;; ======================= .bank 0 ;;; 定数 FPS: .equ 60 ; frame / second DIGITS: .equ 6 ; 表示桁数 POS1: .equ $20 ; 表示位置 higher byte POS2: .equ $7c ; 表示位置 lower byte ;;; 変数 FRAME: .equ $00 ; frame % FPS SECONDS: .equ $01 ; ここから DIGITS bytes は秒数の各桁 (十進) .org $8000 ;;; ----------------------- ;;; 初期化 start: ;; PPU レジスタ初期化 lda #%00010000 ; 初期化中は VBlank 割り込み禁止 sta $2000 ;; VBlank 待ち .waitVBlank: lda $2002 bpl .waitVBlank ; VBlankが発生して $2002 の7ビット目が1になるまで待機 ;; PPU レジスタ初期化 lda #%00000110 ; 初期化中はスプライトとBGを表示OFF sta $2001 ;; パレットロード lda #$3f sta $2006 lda #$00 sta $2006 ldx #0 .loadPalette: lda palette, x sta $2007 inx cpx #$04 bne .loadPalette ;; ゼロページクリア ldx #0 lda #0 .clearZeropage sta <SECONDS, x inx cpx #$ff bne .clearZeropage jsr drawCount ;; PPU レジスタセット lda #%00011110 ; スプライトとBGの表示をONにする sta $2001 lda #%10010000 ; 割り込み許可 sta $2000 .loop: jmp .loop palette: .byte $0f, $00, $10, $20 ;;; ----------------------- ;;; メイン main: jsr increment rti ;;; ----------------------- ;;; サブルーチン ;;; カウンタをひとつ進める ;;; 必要ならば再描画する increment: ;; FRAME をインクリメント inc <FRAME lda <FRAME cmp #FPS bne .return ;; 秒数を繰上げ lda #0 sta <FRAME ldx #0 .incrementSeconds inc <SECONDS, x lda <SECONDS, x cmp #10 bne .draw ; 繰上げ終わり lda #0 sta <SECONDS, x inx cpx #DIGITS bne .incrementSeconds ; 規定桁数以下なら引き続き繰り上げ .draw jsr drawCount .return rts ;;; カウンタを描画 drawCount: ldy #POS2 ; 表示位置下位byte. digit ごとに decrement ldx #0 .digit ;; 表示位置設定 lda #POS1 sta $2006 tya sta $2006 dey ;; 数字描画 lda <SECONDS, x clc adc #$30 ; '0' sta $2007 inx cpx #DIGITS bne .digit ;; スクロール設定 lda #$00 sta $2005 sta $2005 rts
ファミコン
カーソルを動かせるようになった。それだけなのに大変なの
;;; use nesasm ;;; カーソルを動かしてひらがなを指すだけのプログラム .inesprg 1 ; プログラムに使うバンクの数 .ineschr 1 ; bg, sprite データに使うバンクの数 .inesmir 0 ; 水平ミラーリング .inesmap 0 ; マッパー ;;; ======================= ;;; 割り込みハンドラバンク ;;; ======================= .bank 1 .org $FFFA .dw 0 ; VBlank割り込みハンドラ .dw start ; リセット割り込みハンドラ .dw 0 ; ハードウェア/ソフトウェア割り込みハンドラ ;;; ======================= ;;; データバンク ;;; ======================= .bank 2 ;; sprite .org $0000 .incbin "sprite.chr" ; カーソル画像だけ ;; background .org $1000 .incbin "character.chr" ; http://hp.vector.co.jp/authors/VA042397/nes/sample.html ;;; ======================= ;;; プログラムバンク ;;; ======================= .bank 0 ;;; ----------------------- ;;; マクロ ;;; VBlank待ちマクロ waitVBlank: .macro .wait\@: lda $2002 ; VBlankが発生し,$2002の7ビット目が1になるまで待機 bpl .wait\@ .endm ;;; 文字描画マクロ ;;; @params char drawc: .macro lda \1 sta $2007 .endm ;;; カラーパレット読み込みマクロ ;;; @params palette loadPalette: .macro lda #$3f sta $2006 lda #$00 sta $2006 ldx #$00 .load\@: lda \1, x sta $2007 inx cpx #$20 bne .load\@ .endm ;;; a % \1 = a modulo: .macro .add\@: ; a が負の時?よくわかんない cmp #0 bpl .sub\@ clc adc \1 jmp .add\@ .sub\@ ; a が \1 以上なら引く clc cmp \1 bcc .return\@ sec sbc \1 jmp .sub\@ .return\@: .endm ;;; a << \1 = a asln: .macro ldx #$00 .shift\@: cpx \1 beq .return\@ asl a inx jmp .shift\@ .return\@: .endm ;;; a >> \1 = a lsrn: .macro ldx #$00 .shift\@: cpx \1 beq .return\@ lsr a inx jmp .shift\@ .return\@: .endm ;;; a << \1 >> \1 = a dropln: .macro asln \1 lsrn \1 .endm ;;; ----------------------- ;;; 変数 .org $0000 ;;; ゼロページ変数 PARAM1 = $a0 ; サブルーチンの引数に使う PARAM2 = $a1 PARAM3 = $a2 PARAM4 = $a3 PARAM5 = $a4 KEYDATA = $f0 ; コントローラ情報。上位ビットから A, B, select, start, up, down, left, right .org $0300 ;;; 変数. ゼロページとの使い分けはよくわからない CursorX: .db 0 ; カーソルのX位置 CursorY: .db 0 ; カーソルのY位置 KeyResponseCount: .db 0 ; 毎フレーム移動すると早すぎるので、適度にスキップする用カウンタ ;;; ----------------------- ;;; メインプログラム .org $8000 start: jsr init jsr main ;;; ----------------------- ;;; サブルーチン ;;; 初期化 init: waitVBlank ;; PPU 初期化 lda #%00010000 sta $2000 lda #%00000110 ; スプライトと BG の表示を OFF にしておく sta $2001 loadPalette palette ; カラーパレットロード jsr drawCharTable ; ひらがな表描画 ;; カーソル位置初期化 lda #0 sta CursorX sta CursorY jsr setCursor lda #$00 sta KeyResponseCount ; キーに反応するフレームカウント初期化 sta <KEYDATA ;; スクロール設定 lda #$00 sta $2005 sta $2005 lda #%00011110 ; スプライトと BG の表示を ON sta $2001 rts ;;; ひらがな表描画 drawCharTable: lda #$80 ; あ sta <PARAM1 ;; 位置設定 lda #$21 sta <PARAM2 lda #$65 sta <PARAM3 ;; あ行 - な行 lda #$05 sta <$00 ; <$00 行カウンタ .aNo: ;; drawCharLine([あ-な], <PARAM2, <PARAM3, 5, 1) lda #$05 sta <PARAM4 lda #$01 sta <PARAM5 jsr drawCharLine jsr down ; 1行下に移動 dec <$00 bne .aNo ;; っ ゃ ゅ ょ ゛ lda <PARAM2 sta $2006 lda <PARAM3 sta $2006 drawc #$b3 ; っ drawc #$00 drawc #$b4 ; ゃ drawc #$00 drawc #$b5 ; ゅ drawc #$00 drawc #$b6 ; ょ drawc #$00 drawc #$b7 ; ゛ ;; は行 lda #$99 sta <PARAM1 lda #$21 sta <PARAM2 lda #$71 sta <PARAM3 jsr drawCharLine jsr down ;; ま行 jsr drawCharLine jsr down ;; や行 lda #$03 sta <PARAM4 lda #$03 sta <PARAM5 jsr drawCharLine jsr down ;; ら行 lda #$05 sta <PARAM4 lda #$01 sta <PARAM5 jsr drawCharLine jsr down ;; わ行 lda #$03 sta <PARAM4 lda #$03 sta <PARAM5 jsr drawCharLine jsr down ;; ゜, もどる lda <PARAM2 sta $2006 lda <PARAM3 sta $2006 drawc #$b8 ; ゜ drawc #$00 drawc #$00 drawc #$00 drawc #$a2 ; も drawc #$93 ; と drawc #$b7 ; ゛ drawc #$a8 ; る rts ;;; <PARAM2, <PARAM3 を VRAM アドレスとして、1行下に移動する down: lda <PARAM3 clc ; キャリーフラグクリア adc #$40 sta <PARAM3 bcc .return ; キャリーチェック inc <PARAM2 .return: rts ;;; ひらがな行描画(あ行、か行など) ;;; params char, pos1, pos2, length, interval drawCharLine: ;; 描画位置設定 lda <PARAM2 sta $2006 lda <PARAM3 sta $2006 ldx <PARAM4 .char: dex bmi .return drawc <PARAM1 ; 文字描画 inc <PARAM1 ; 次の文字に ;; 文字間のスペース ldy <PARAM5 .interval: dey bmi .char drawc #$00 jmp .interval .return rts ;;; main loop main: waitVBlank jsr readPad jsr moveCursor jmp main ;;; コントローラ情報を読む readPad: ;; 初期化 lda #$01 sta $4016 lda #$00 sta $4016 ;; ボタンを1個読んではshift ldx #$08 rol <KEYDATA ; キャリー分左ローテート .readButton lda $4016 and #$01 rol <KEYDATA ; 左ローテート ora <KEYDATA sta <KEYDATA dex bne .readButton rts ;;; カーソルを入力に合わせて移動 ;;; ボタンを押し始めた時、または ;;; KeyResponseCountが0の時に移動 moveCursor: lda <KEYDATA and #%00001111 bne .pressed .unpressed ; ボタンが押されてない時 lda #0 sta KeyResponseCount ; 待ちカウントをクリアして次回押されるのを待機 rts .pressed ; ボタンが押されてる時 lda KeyResponseCount bne .continue .first ; 今押さればかりの時 jmp .move ; 無条件で移動 .continue ; 前フレームから押されていた時 dec KeyResponseCount beq .move ; 待ちカウントが0になったときだけ移動 lda #0 ; 0でないときは待機 sta <KEYDATA rts .move ; <KEYDATA を読み取ってカーソルを移動 lda #$10 sta KeyResponseCount ; 待ちカウント初期化 lda <KEYDATA and #%00001000 bne .up lda <KEYDATA and #%00000100 bne .down lda <KEYDATA and #%00000010 bne .left lda <KEYDATA and #%00000001 bne .right rts .up: dec CursorY jsr validateCursor cmp #0 beq .up jsr setCursor rts .down: inc CursorY jsr validateCursor cmp #0 beq .down jsr setCursor rts .left: dec CursorX jsr validateCursor cmp #0 beq .left jsr setCursor rts .right: inc CursorX jsr validateCursor cmp #0 beq .right jsr setCursor rts ;;; CursorX, CursorY に合わせてカーソルスプライトを表示 setCursor: lda #$00 ; カーソル画像のアドレス sta $2003 ;; Y lda CursorY asln #$04 clc adc #86 sta $2004 lda #$00 sta $2004 sta $2004 ;; X lda CursorX asln #$04 clc adc #31 sta $2004 ;; KEYDATA クリア lda #$00 sta <KEYDATA rts ;;; CursorX, CursorY にちゃんとした位置が入っていれば #1, ;;; そうでなければ #0 を a に入れる validateCursor: lda CursorY modulo #$06 sta CursorY lda CursorX modulo #$0b sta CursorX cmp #$05 ; 真ん中の空白列はだめ beq .invalid cmp #$07 ; ひ列 beq .hihe cmp #$09 ; へ列 beq .hihe cmp #$0a ; 最終列 beq .last jmp .valid .hihe ; ひ・へ列の時 lda CursorY cmp #$02 ; や行はだめ beq .invalid cmp #$04 ; わ行以降はだめ bcs .invalid jmp .valid .last ; 最終列の時 lda CursorY cmp #$05 ; 最終行はだめ beq .invalid jmp .valid .valid lda #1 rts .invalid lda #0 rts ;;; カラーパレット palette: .byte $0f, $00, $10, $20 .byte $0f, $00, $10, $20 .byte $0f, $00, $10, $20 .byte $0f, $00, $10, $20 .byte $0f, $00, $10, $20 .byte $0f, $00, $10, $20 .byte $0f, $00, $10, $20 .byte $0f, $00, $10, $20
ファミコンプログラミング
アセンブラがことのほか難しい
;;; use nesasm ;;; http://alohakun.blog7.fc2.com/blog-entry-909.html を下地に作成 .inesprg 1 ; プログラムに使うバンクの数 .ineschr 1 ; CHR データに使うバンクの数 .inesmir 0 ; 水平ミラーリング .inesmap 0 ; マッパー ;;; ----------------------- ;;; データバンク ;;; ----------------------- .bank 2 .org $0000 ;; フォントデータのパターンテーブルを埋め込む ;; http://hp.vector.co.jp/authors/VA042397/nes/sample.html .incbin "character.chr" ;;; ----------------------- ;;; プログラムバンク ;;; ----------------------- .bank 0 .org $8000 ;;; start! start: jsr init jsr main ;;; end! ;;; VBlank待ちマクロ waitVBlank: .macro .wait\@: lda $2002 ; VBlankが発生し,$2002の7ビット目が1になるまで待機 bpl .wait\@ .endm ;;; 文字描画マクロ ;;; @params char drawc: .macro lda \1 sta $2007 .endm ;;; カラーパレット読み込みマクロ ;;; @params palette, length loadPalette: .macro lda #$3f sta $2006 lda #$00 sta $2006 ldx #$00 .load\@: lda \1, x sta $2007 inx cpx \2 bne .load\@ .endm ;;; 初期化 init: waitVBlank ;; PPU 初期化 lda #%00001000 sta $2000 lda #%00000110 ; スプライトと BG の表示を OFF にしておく sta $2001 loadPalette wordPalette, #$10 ; 文字用パレットロード jsr drawCharTable ; ひらがな表描画 ;; スクロール設定 lda #$00 sta $2005 sta $2005 lda #%00011110 ; スプライトと BG の表示を ON sta $2001 rts ;;; ひらがな表描画 drawCharTable: lda #$80 ; あ sta <$00 ; $00 文字 ;; 位置設定 lda #$21 sta <$01 ; $01 pos1 lda #$65 sta <$02 ; $02 pos2 ;; あ行 - な行 lda #$05 sta <$10 ; $10 行カウンタ .aNo: ;; drawCharLine([あ-な], <$01, <$02, 5, 1) lda #$05 sta <$03 lda #$01 sta <$04 jsr drawCharLine jsr down ; 1行下に移動 dec <$10 bne .aNo ;; っ ゃ ゅ ょ ゛ lda <$01 sta $2006 lda <$02 sta $2006 drawc #$b3 ; っ drawc #$00 drawc #$b4 ; ゃ drawc #$00 drawc #$b5 ; ゅ drawc #$00 drawc #$b6 ; ょ drawc #$00 drawc #$b7 ; ゛ ;; は行 lda #$99 sta <$00 lda #$21 sta <$01 lda #$71 sta <$02 jsr drawCharLine jsr down ;; ま行 jsr drawCharLine jsr down ;; や行 lda #$03 sta <$03 lda #$03 sta <$04 jsr drawCharLine jsr down ;; ら行 lda #$05 sta <$03 lda #$01 sta <$04 jsr drawCharLine jsr down ;; わ行 lda #$03 sta <$03 lda #$03 sta <$04 jsr drawCharLine jsr down ;; ゜, もどる lda <$01 sta $2006 lda <$02 sta $2006 drawc #$b8 ; ゜ drawc #$00 drawc #$00 drawc #$00 drawc #$00 drawc #$a2 ; も drawc #$93 ; と drawc #$b7 ; ゛ drawc #$a8 ; る rts ;;; <$01, <$02 を VRAM アドレスとして、1行下に移動する down: lda <$02 clc ; キャリーフラグクリア adc #$40 sta <$02 bcc .return ; キャリーチェック inc <$01 .return: rts ;;; ひらがな行描画(あ行、か行など) ;;; params char, pos1, pos2, length, interval drawCharLine: ;; 描画位置設定 lda <$01 sta $2006 lda <$02 sta $2006 ldx <$03 .char: dex bmi .return drawc <$00 ; 文字描画 inc <$00 ; 次の文字に ;; 文字間のスペース ldy <$04 .interval: dey bmi .char drawc #$00 jmp .interval .return rts ;;; main loop main: waitVBlank jmp main ;;; 文字色パレットテーブル wordPalette: .byte $0f, $00, $10, $20 .byte $0f, $06, $16, $26 .byte $0f, $08, $18, $28 .byte $0f, $0a, $1a, $2a ;;; ----------------------- ;;; 割り込みハンドラバンク ;;; ----------------------- .bank 1 .org $FFFA .dw 0 ; VBlank割り込みハンドラ .dw start ; リセット割り込みハンドラ .dw 0 ; ハードウェア/ソフトウェア割り込みハンドラ
__END__ 以下の文字列を取得、みたいなのは PHP でどうやるの?
<?php ob_start(); ?> plain text <?php $text = ob_get_clean(); ?>
とやるのが定石なのかな?ファイル末尾にはできないけど...ヒアドキュメントでいいか...
Objective-C から C++ の仮想関数を呼ぶ
仮想メンバ関数がある場合、当該C++クラスはObjective-Cのインスタンス変数として機能しません。
#import <Cocoa/Cocoa.h> struct Class0 { void foo(); }; struct Class1 { virtual void foo(); }; struct Class2 { Class2(int i, int j); }; @interface Foo :NSObject { Class0 class0; // OK Class1 class1; // エラー! Class1 *ptr; // OK—Fooのinitから'ptr = new Class1()'を呼び出し、 // Fooのdeallocから'delete ptr'を呼び出す Class2 class2; // 警告—コンストラクタを呼び出さない! ... @endC++では、仮想関数を含むクラスの各インスタンスが、適切な仮想関数テーブルポインタを含む必要があります。しかし、Objective-CランタイムはC++オブジェクトモデルを知らないため、仮想関数テーブルポインタを初期化することができません。同様に、Objective-Cランタイムは、それらのオブジェクトのC++コンストラクタまたはデストラクタに対して呼び出しを発行できません。C++クラスにユーザ定義コのンストラクタやデストラクタがあっても、それらは呼び出されません。そのような場合、コンパイラは警告を発します。
日本語ドキュメント - Apple Developer
とあるのだが、C++オブジェクトをポインタで持つなら問題ないの?仮想関数を呼んでも大丈夫なの?というのがよくわからないのでテスト。
#import <Foundation/NSObject.h> #import <stdio.h> // C++ 親クラス class Parent { public: virtual void sayHello() { printf("hello, I'm C++ parent.\n"); } virtual ~Parent() { printf("parent destructor\n"); } }; // C++ 子クラス class Child : public Parent { public: virtual void sayHello() { printf("hello, I'm C++ child.\n"); } virtual ~Child() { printf("child destructor\n"); } }; // Objective-C クラス @interface ObjC : NSObject { Parent *cppObject; // 親クラスのポインタとして保持 } - (void)sayHello; @end @implementation ObjC - (id)init { if (self = [super init]) { cppObject = new Child(); // 子クラスのオブジェクトを作成 } return self; } - (void)dealloc { delete cppObject; [super dealloc]; } - (void)sayHello { cppObject->sayHello(); } @end int main(int argc, char *argv[]) { id objc = [[ObjC alloc] init]; [objc sayHello]; [objc release]; }
これを実行。
$ gcc -o main main.mm -framework Foundation -lstdc++ $ ./main hello, I'm C++ child. child destructor parent destructor
期待通り動いてる。
解釈すると、C++オブジェクトの動作は 'Objective-Cランタイム' の外で行われている。Objectice-Cランタイムに暗黙的なC++オブジェクトの生成と破棄をさせなければ、つまりC++オブジェクトを全てポインタで持つのであれば、仮想関数があっても問題はない。ということだと思います。
'Call C++ Default Ctors/Dtors in Objective-C' を on にした時の挙動は調べないとわからない。
URLエンコードのデコード
- Java 1.5.0
http://blog.zakura.jp/cal/2008/04/javaurldecoder.html
修正したバージョンをあげている人が見つからなかったので書いた。
import java.io.*; import java.util.*; public class URLDecoder { public static String decode(String s) throws UnsupportedEncodingException { return decode(s, "JISAutoDetect"); } public static String decode(String s, String enc) throws UnsupportedEncodingException { boolean needToChange = false; int numChars = s.length(); int i = 0; char c; byte[] bytes = new byte[numChars]; int bytePos = 0; while (i < numChars) { c = s.charAt(i); switch (c) { case '+': bytes[bytePos++] = (byte)' '; i++; break; case '%': if (i + 3 > numChars) throw new IllegalArgumentException("URLDecoder: Incomplete trailing escape (%) pattern"); try { bytes[bytePos++] = (byte)Integer.parseInt(s.substring(i + 1, i + 3), 16); } catch (NumberFormatException e) { throw new IllegalArgumentException("URLDecoder: Illegal hex characters in escape (%) pattern - " + e.getMessage()); } i += 3; break; default: bytes[bytePos++] = (byte)c; i++; break; } } BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes, 0, bytePos), enc)); StringBuilder buffer = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { buffer.append(line); } return buffer.toString(); } catch (IOException e) { throw new IllegalArgumentException("URLDecoder: something wrong - " + e.getMessage()); } finally { try { if (reader != null) { reader.close(); } } catch (IOException e) { } } } }
自分用の用途でしか動作確認をしていないので気をつけてください。JISAutoDetect は UTF を認識してくれないのがかなりいまいち。Java の日本語周りのちゃんとしたライブラリってないのかな。使えば使うほど嫌いになるわ、じゃばじゃば。