このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。
第4回のプログラム演習では、プログラムを終了させる際に、CPU を停止させ
るのではなく、 rjmp PC
により無限ループに入るようにしまし
た。
また、一定の時間を待つのに「ビジーウェイト」という手法で、何もしないこ
とを何万回と繰り替えして一定の時間を作りました。
この様な手法はどのコンピュータでも使える基本的な手法ですが、何もしてい ないときに電力を抑えたり、あるいは、複数のプログラムを平行して動かした り、外部と協調して動作させるなどには向きません。
そこで、今回はマイコンの機能として、CPU を止めたり、一定の時間をタイマー で計ってプログラムを動かします。
ATtiny2313 には sleep 命令があります。 この sleep 命令でどのような状態になるかはプログラム内部で 3 通りに指定 することができます(p.30)。 外部からの割り込み信号が来るまでほとんどの機能を停止するモードから、単 に CPU を止めるだけのモードもあります。 今回は、第4回の例4-1で使用できる、すべての I/O が存続していながら CPU だけを止める Idle モードを選びます。 Idle モードを利用するには次のようにします。
これを例4-1に取り入れたのが次のプログラムです。
;************
;* 例7-1 *
;************
.device attiny2313
.include <tn2313def.inc>
.cseg
.org 0x0000
rjmp reset
.org INT_VECTORS_SIZE
reset:
ldi r16,low(RAMEND)
out SPL,r16
ldi r16,0xff
out ddrb, r16
ldi r16,0
out ddrd,r16
in r16,mcucr
cbr r16, 1<<sm0 | 1<<sm1 ;idle mode
sbr r16, 1<<se ; enable sleep
out mcucr, r16
.def pattern = r16
main:
ldi pattern,0b01010101 ; 点灯させるビットパターン
out portb,pattern
sleep ; idle mode へ
.exit
外部の刺激などに対して、CPU があらかじめ指定した番地を呼び出すのを 割り込みと言います。 呼び出される番地を割り込みベクタと言います。 AVR では各種の割り込みそれぞれに対して、用意された番地のプログラムを呼 び出します。 これは 1 番地から連続した領域に設定されていますので、直接、割り込みベ クタに処理プログラムを書くことはできません。 そのため、通常はここにはジャンプ命令のみを書いて、割り込み処理プログラ ムへ処理を移します。
ここでは例題として前前回の演習問題4-5 を改造して、タイマ割り込みにより 一定周期でカウンタを増やすプログラムを作ることを考えます。
ATtiny2313 に内蔵されているタイマ Timer0 は 8bit ながら複雑な機能を持っ ています。 今回は、ほぼ 1 秒に一回カウンターを増やして値を表示するだけの処理をす るのが目標です。
これを実現するには、一定時間毎にカウンターを 1 増やして値を表示するプ ログラムを作る一方、初期設定をした後、カウンターを表示してタイマーをス タートした後 sleep するプログラムが必要になります。 なお、sleep中に割り込みが発生すると割り込みを処理した後、 sleep 命令の 次に制御が戻りますので、割り込み以外の処理をしない場合は、 sleep 命令 だけを実行する無限ループを作ります。
今回は単純なモードとして normal モードを使用します(pp.66-67)。 各モードの設定は pp.73-79 のレジスタを参照します。
Timer0 は 0x00 から数えて 0xff まで加算した後、0x00に戻る際にオーバフロー 割り込みを発生させます。 つまり、割り込みベクタは OVF0addr = 0x0006 になります。 そのための設定ですが、以下のようになります。
レジスタ名 | 値 | 意味 |
---|---|---|
TCCR0A | 0b00000000 | normalモードはすべて0 |
TCCR0B | 0b00000101 | クロックの 1024分の1で割り込 み発生 |
TIMSK | 0b00000010 | オーバーフロー検知だけ有 効 |
ATtiny2313 のシステムクロックは 8MHz に設定してあります(p.25)。 また、出荷時のヒューズビット CKDIV8 は ON になっています(p.29)。 従って、CLKPR をデフォルトのままで使用した場合、システムは 8M/8=1MHz で動作します。 そのため、 1024 で分周した後、さらに 256 回に一回の割合で割り込みが発 生するとすると、割り込みは 1024*256/1M ≒ 0.26 秒に一回発生することに なります。
演習 4-5 のプログラムを改造します。 counter という変数を用意し、次のサブルーチンを用意します。
このような準備をした後、次のプログラムを作ります。
また、割り込みの処理は次のようにします。
なお、割り込みの設定は OPTION_REG に Timer0 の仕様を入れます。 今回は内部クロックを使い、 Prescaler を 1:256 で使用します。 (デフォルト値は全て 1 にセットされています(データシート TABLE 6-1 p. 47)。 そして、 INTCON レジスタの割り込み許可 T0IE をセットし、割り込みフラグ T0IF をクリアし、全体の割り込み許可 GIE をセットします。
プログラムは次のようになります
;**********
;* 例7-2 *
;**********
.include <tn2313def.inc>
.cseg
.org 0x0000
rjmp reset
.org OVF0addr
rjmp timer0vec
.org INT_VECTORS_SIZE
reset:
ldi r16,low(RAMEND)
out SPL,r16
ldi r16,0xff
out ddrb, r16
initsleep:
in r16,mcucr
cbr r16, 1<<sm0 | 1<<sm1 ;idlemode
sbr r16, 1<<se ; enable sleep
out mcucr, r16
inittimer0:
ldi r16,0b00000000 ;normal mode
out tccr0a,r16
ldi r16,0b00000101 ; normal mode, prescaler=1024
out tccr0b, r16
ldi r16, 0b00000010 ; enable interruption for overflow
out timsk, r16
.def pattern = r16
.def counter = r17
clr counter
rcall dispcounter
sei
main:
sleep
rjmp main
timer0vec:
rcall inccounter
rcall dispcounter
reti
dispcounter: ;counter の値を表示
ldi ZH,high(startdata<<1)
ldi ZL,low(startdata<<1)
add ZL,counter
brcc dispcounter1
inc ZH
dispcounter1:
lpm pattern,Z
out portb,pattern
ret
inccounter:
inc counter
cpi counter,countmax
breq inccounter1
ret
inccounter1:
clr counter
ret
startdata:
.db 0b01110111, 0b00010100 ;0,1
.db 0b10110011, 0b10110110 ;2,3
.db 0b11010100, 0b11100110 ;4,5
.db 0b11100111, 0b00110100 ;6,7
.db 0b11110111, 0b11110110 ;8,9
.db 0b11110101, 0b11000111 ;A,b
.db 0b01100011, 0b10010111 ;C,d
.db 0b11100011, 0b11100001 ;E,F
enddata:
.equ num_start_adr = startdata <<1
.equ num_end_adr = enddata << 1
.equ countmax = low(num_end_adr - num_start_adr)
.exit
例7-2ではカウンタの動きが速すぎます。 Prescaler を使用しても最大で 1/256 にしかなりません。 内部クロック 4MHz では 1 マシンサイクルが 1μ秒なので、カウンタ一つ 増やす間隔は 256×256μ秒 ≒ 0.066 秒に一回になってます。 これを大体 1 秒の間隔にするにはさらに 15 倍程度遅くする必要があります。
そこで大体 1 秒毎に増えるカウンタを作りましょう。 そのためには割り込みがかかっても 15 回に一回しか反応しないようにします。 その機能を持つサブルーチンの名前を Postscalerと呼ぶ事にしま す。 これは新たにレジスタを使用して、 15 回中 14 回まではレジスタを加算する だけで戻り、15回目にレジスタを 0 にして、指定のサブルーチンを呼ぶもの です。
;**********
;* 例7-3 *
;**********
.include <tn2313def.inc>
.cseg
.org 0x0000
rjmp reset
.org OVF0addr
rjmp timer0vec
.org INT_VECTORS_SIZE
reset:
ldi r16,low(RAMEND)
out SPL,r16
ldi r16,0xff
out ddrb, r16
initsleep:
in r16,mcucr
cbr r16, 1<<sm0 | 1<<sm1 ;idlemode
sbr r16, 1<<se ; enable sleep
out mcucr, r16
inittimer0:
ldi r16,0b00000000 ;normal mode
out tccr0a,r16
ldi r16,0b00000101 ; normal mode, prescaler=1024
out tccr0b, r16
ldi r16, 0b00000010 ; enable interruption for overflow
out timsk, r16
.def pattern = r16
.def counter = r17
.def pcounter = r18
.equ ptime = 4
ldi pcounter,ptime
clr counter
rcall dispcounter
sei
main:
sleep
rjmp main
timer0vec:
rcall postscaler
reti
postscaler:
dec pcounter
breq postscaler1
ret
postscaler1:
ldi pcounter,ptime
rjmp target
target:
rcall inccounter
rcall dispcounter
ret
dispcounter: ;counter の値を表示
ldi ZH,high(startdata<<1)
ldi ZL,low(startdata<<1)
add ZL,counter
brcc dispcounter1
inc ZH
dispcounter1:
lpm pattern,Z
out portb,pattern
ret
inccounter:
inc counter
cpi counter,countmax
breq inccounter1
ret
inccounter1:
clr counter
ret
startdata:
.db 0b01110111, 0b00010100 ;0,1
.db 0b10110011, 0b10110110 ;2,3
.db 0b11010100, 0b11100110 ;4,5
.db 0b11100111, 0b00110100 ;6,7
.db 0b11110111, 0b11110110 ;8,9
.db 0b11110101, 0b11000111 ;A,b
.db 0b01100011, 0b10010111 ;C,d
.db 0b11100011, 0b11100001 ;E,F
enddata:
.equ num_start_adr = startdata <<1
.equ num_end_adr = enddata << 1
.equ countmax = low(num_end_adr - num_start_adr)
.exit
LED の小数点を約 0.5 秒程度で点滅させなさい。 さらに、 PORTD4 に接続したスイッチを押す度に LED の数字が増えるようにしな さい。
なお、レジスタ中の複数のビットを On, Off するには AND と OR 演算が有効で す。bit X を bit Y に変えるには (( X and 0) or Y) (C 言語だと X&=0; X|=Y;)とします。
LED の明るさを変えるには二つの方法があります。 今回は DUTY 比のコントロールについて考えます。 この方法は、早い周期で LED を点けたり消したりすることで、平均出力を下げる ことにより、人間の目には暗く見えるようにすることです。 この時、全体の中で ON になっている比率を DUTY 比と呼びます。
例 5-3 を思い出して下さい。 これは、PORTB0 から PORTB7 まで順番に点灯してくというプログラムでした。 その演習では、一つのポートの LED を点灯してから、次のポートを点灯させ るまで時間待ちをしました。 そこで、この waitを外したものを動かしてみましょう。 単純で改造がしやすい 5-3 2 のプログラムを使用します。 改造した物が次になります。 なお、全部消灯するパターンも取り除いています。
;**********
;* 例7-4 *
;**********
.include <tn2313def.inc>
.cseg
.org 0x0000
rjmp reset
.org INT_VECTORS_SIZE
reset:
ldi r16,low(RAMEND)
out SPL,r16
ldi r16,0xff
out ddrb, r16
.equ time=3
.def pattern = r16
.macro display
ldi pattern,@0
out portb,pattern
.endmacro
main:
display 0b00000001
display 0b00000011
display 0b00000111
display 0b00001111
display 0b00011111
display 0b00111111
display 0b01111111
display 0b11111111
rjmp main
.exit
実行すると、 LED の明るさが揃ってないことがわかります。
この場合、PORTB0 の duty 比は 100% です。一方、 PORTB7 の duty 比は1/8 = 12.5% になります。 但し、実験してみれば分かるように duty 比の高い LED の明るさの違いはほ とんど分かりません。 人間の目は対数的な特性があるため、指数関数的な変化でないとはっきり把握 できません。 つまり、 duty 比を連続して変化させる時は、比率を連続的に変化させても効 果が薄いことに注意します。
小数点の明るさを変化させることを考えましょう。 DUTY比を変えるには小数点だけを素早く ON, OFF させます。 つまり小数点は常に同じ値ではなく、割り込みにより On/OFF を繰り返します。 つまり、タイマ割り込みを使用し、 duty 比が 25% になるように 4 回に 1 回だけ set し、残りは clear するようにします。 なお比較のために、 PORTB は小数点以外は全て点灯させておきます。
プログラムにおいて、レジスタ crate の値は割り込みがかかる度に countrate, countrate-1, ... ,3,2,1, countrate, countrate-1, ... ,3,2,1, と周期的に変化して行きます。 そして、その度に あらかじめ W レジスタに入れた dutyrate の値から引き算します。 すると、crate - dutyrate < 0 となる時は C フラグが 0 になり、そうでなければ 1 になります。 ここで、 C フラグが 0 の時、つまり 1 ≤ crate < dutyrate の時に、 小数点を 光らせ、それ以外の時に小数点を消灯することで、 DUTY 比 = (dutyrate - 1)/countrate を実現します。 25% にするには countrate = 4, dutyrate = 2 とします。
;**********
;* 例7-5 *
;**********
.include <tn2313def.inc>
.cseg
.org 0x0000
rjmp reset
.org OVF0addr
rjmp timer0vec
.org INT_VECTORS_SIZE
reset:
ldi r16,low(RAMEND)
out SPL,r16
ldi r16,0xff
out ddrb, r16
initsleep:
in r16,mcucr
cbr r16, 1<<sm0 | 1<<sm1 ;idlemode
sbr r16, 1<<se ; enable sleep
out mcucr, r16
inittimer0:
ldi r16,0b00000000 ;normal mode
out tccr0a,r16
ldi r16,0b00000001 ; normal mode, prescaler=off
out tccr0b, r16
ldi r16, 0b00000010 ; enable interruption for overflow
out timsk, r16
.def pattern = r16
ldi pattern,0b11110111 ; pattern = 8
out portb,pattern
sei
main:
sleep
rjmp main
timer0vec:
push r16
in r16,SREG
push r16
rcall duty
pop r16
out SREG,r16
pop r16
reti
.def dutycounter = r17
.equ dcycle = 8
.equ drate = 1
duty:
inc dutycounter
cpi dutycounter,dcycle
brcs duty1
clr dutycounter
duty1:
cpi dutycounter,drate
brcs onstate
rjmp offstate
onstate:
sbi portb,3
ret
offstate:
cbi portb,3
ret
.exit
duty 比 1, 1/2, 1/4, 1/8, 1/16, 1/32, 1/64, 1/128 という階調を考えます。 これを RD4 に接続した sw により切替えることを考えます。 但し、これらは計算で求めるのではなく、以前に LED に数字を出した時のよ うにテーブルで参照するようにします。 W レジスタに求める duty 比を入れて、 getduty とすると必要な比 が W レジスタに得られるようにします。 但し、 0 の時、 128 を示し、 1 の時 64, 2 の時 32となる値、つまり最小 が 1 となる相対値を返します。
例7-5同様に PORTB は小数点以外は全て点灯させ、小数点の明るさを変えます。
プログラムは基本的には 例7-5 と演習5-6 のプログラムを流用します。 但し duty 比はプログラムの中で 1 を加え、 duty 比 = (dutyrate - 1)/maxcount ではなく、 duty 比 = dutyrate/maxcount となるようにします。
このプログラムを実現するには、 counter の値により duty_ratio を変化させ る必要があります。 つまり、 sw が押されるたびに、 counter の値を変化させ、その値により duty 比を変化させる必要があります。
;************
;* 例7-6 *
;************
.include <tn2313def.inc>
.cseg
.org 0x0000
rjmp reset
.org OVF0addr
rjmp timer0vec
.org INT_VECTORS_SIZE
reset:
ldi r16,low(RAMEND)
out SPL,r16
ldi r16,0xff
out ddrb, r16
initsleep:
in r16,mcucr
cbr r16, 1<<sm0 | 1<<sm1 ;idlemode
sbr r16, 1<<se ; enable sleep
out mcucr, r16
inittimer0:
ldi r16,0b00000000 ;normal mode
out tccr0a,r16
ldi r16,0b00000001 ; normal mode, prescaler=off
out tccr0b, r16
ldi r16, 0b00000010 ; enable interruption for overflow
out timsk, r16
.def pattern = r16
ldi pattern,0b11110111 ; pattern = 8
out portb,pattern
.def counter = r17
.def current = r18
.def last = r19
clr counter
sei
main:
rcall setduty
in current,PIND
sbrc last,PIND4
rjmp pressed
sbrs current,PIND4
rjmp notpressed
pressednow:
rcall inccounter
pressed:
notpressed:
mov last,current
rjmp main
timer0vec:
push r16
in r16,SREG
push r16
rcall duty
pop r16
out SREG,r16
pop r16
reti
.def dutycounter = r20
.def dcycle = r21
.equ drate = 1
duty:
inc dutycounter
cp dutycounter,dcycle
brcs duty1
clr dutycounter
duty1:
cpi dutycounter,drate
brcs onstate
rjmp offstate
onstate:
sbi portb,3
ret
offstate:
cbi portb,3
ret
setduty: ;counterの値に対応するduty比を設定
ldi ZH,high(startdutydata<<1)
ldi ZL,low(startdutydata<<1)
add ZL,counter
brcc setduty1
inc ZH
setduty1:
.equ mask = 0b00001000
lpm dcycle,Z
ret
inccounter:
inc counter
cpi counter,dutymax
breq inccounter1
ret
inccounter1:
clr counter
ret
startdutydata:
.db 1,2,4,8,16,32,64,128
enddutydata:
.equ duty_start_adr = startdutydata <<1
.equ duty_end_adr = enddutydata << 1
.equ dutymax = low(duty_end_adr - duty_start_adr)
.exit
例7-6のプログラムで、 PORTB には counter の値を表示するように改造し なさい。
DUTY 比 50% の LED はそれほど暗くなく、これを標準の明るさとして用いて も問題ない明るさです。 したがって、二つの別の LED を高速に交互に点灯させると、人間の目には二 つの別々の LED が同時に光っているように見えます。
そこで、7 セグメント LED の二桁表示をすることを考えます。 カソードコモン LED の場合、カソードを GND に繋ぎますが、ここにスイッチ を付けることで、 LED の点灯、消灯をコントロールできます。 スイッチは高速で、 PIC の出力により ON, OFF が出来なければなりません。 そのためトランジスタのスイッチング回路を使います。 回路図を示します。
始めに小数点を約 0.5 秒程度で点滅させることを考えます。 割り込みは Prescaler を 1:256 で使用し、さらに 7 回に一回毎に点滅させ ます。 点滅は点滅パターンを用意して PORTB に対して XOR をかけることで行います。 著者の回路では RB3 が小数点になってますので、 b'00001000' で XOR を行 います。
一方、 RD4 に接続したスイッチを押す度に LED の数字が増えるようにするに は、演習5-6 のプログラムを流用します。 但し、 PORTB にダイレクトに書き込むと小数点の点滅に影響してしまいます。 そこで、ビットパターンを特定の場所だけ上書きすることを考えます。 そのためには、「特定の場所を指定する」情報を考えなくてはいけません。 今、考えるのは PORTB の RA0 を温存したまま RA1 から RA7 までを変更する ことです。
つまり、PORTB に pattern を書き込むには、書き込みたい部分を 0 にした マスクと and を取り、それから pattern と or を取れば良いで す。
;************
;* 演習7-1 *
;************
.include <tn2313def.inc>
.cseg
.org 0x0000
rjmp reset
.org OVF0addr
rjmp timer0vec
.org INT_VECTORS_SIZE
reset:
ldi r16,low(RAMEND)
out SPL,r16
ldi r16,0xff
out ddrb, r16
ldi r16,1<<PIND4
out ddrd,r16
initsleep:
in r16,mcucr
cbr r16, 1<<sm0 | 1<<sm1 ;idlemode
sbr r16, 1<<se ; enable sleep
out mcucr, r16
inittimer0:
ldi r16,0b00000000 ;normal mode
out tccr0a,r16
ldi r16,0b00000101 ; normal mode, prescaler=1024
out tccr0b, r16
ldi r16, 0b00000010 ; enable interruption for overflow
out timsk, r16
.def pattern = r16
.def counter = r17
.def pcounter = r18
.equ ptime = 2
.def last = r19
.def current = r20
.def work = r21
clr counter
sei
main:
rcall dispcounter
in current,PIND
sbrc last,PIND4
rjmp pressed
sbrs current,PIND4
rjmp notpressed
pressednow:
rcall inccounter
pressed:
notpressed:
mov last,current
rjmp main
timer0vec:
push r16
in r16,SREG
push r16
rcall postscaler
pop r16
out SREG,r16
pop r16
reti
postscaler:
inc pcounter
cpi pcounter,ptime
breq postscaler1
ret
postscaler1:
clr pcounter
rjmp target
target:
rcall blinkdot
ret
blinkdot:
sbis portb,3 ; dot は 3bit目
rjmp dotoff
doton:
cbi portb,3
ret
dotoff:
sbi portb,3
ret
dispcounter: ;counter の値を表示
ldi ZH,high(startdata<<1)
ldi ZL,low(startdata<<1)
add ZL,counter
brcc dispcounter1
inc ZH
dispcounter1:
.equ mask = 0b00001000
cli
in work,portb
andi work,mask
lpm pattern,Z
or work,pattern
out portb,work
sei
ret
inccounter:
inc counter
cpi counter,countmax
breq inccounter1
ret
inccounter1:
clr counter
ret
startdata:
.db 0b01110111, 0b00010100 ;0,1
.db 0b10110011, 0b10110110 ;2,3
.db 0b11010100, 0b11100110 ;4,5
.db 0b11100111, 0b00110100 ;6,7
.db 0b11110111, 0b11110110 ;8,9
.db 0b11110101, 0b11000111 ;A,b
.db 0b01100011, 0b10010111 ;C,d
.db 0b11100011, 0b11100001 ;E,F
enddata:
.equ num_start_adr = startdata <<1
.equ num_end_adr = enddata << 1
.equ countmax = low(num_end_adr - num_start_adr)
.exit
例7-6のプログラムに、従来のプログラム中にある inccount, dispcount を 組み込むだけです。
;************
;* 演習7-2 *
;************
.include <tn2313def.inc>
.cseg
.org 0x0000
rjmp reset
.org OVF0addr
rjmp timer0vec
.org INT_VECTORS_SIZE
reset:
ldi r16,low(RAMEND)
out SPL,r16
ldi r16,0xff
out ddrb, r16
initsleep:
in r16,mcucr
cbr r16, 1<<sm0 | 1<<sm1 ;idlemode
sbr r16, 1<<se ; enable sleep
out mcucr, r16
inittimer0:
ldi r16,0b00000000 ;normal mode
out tccr0a,r16
ldi r16,0b00000001 ; normal mode, prescaler=off
out tccr0b, r16
ldi r16, 0b00000010 ; enable interruption for overflow
out timsk, r16
.def pattern = r16
ldi pattern,0b11110111 ; pattern = 8
out portb,pattern
.def counter = r17
.def current = r18
.def last = r19
clr counter
sei
main:
rcall setduty
rcall dispcounter
in current,PIND
sbrc last,PIND4
rjmp pressed
sbrs current,PIND4
rjmp notpressed
pressednow:
rcall inccounter
pressed:
notpressed:
mov last,current
rjmp main
timer0vec:
push r16
in r16,SREG
push r16
rcall duty
pop r16
out SREG,r16
pop r16
reti
.def dutycounter = r20
.def dcycle = r21
.equ drate = 1
duty:
inc dutycounter
cp dutycounter,dcycle
brcs duty1
clr dutycounter
duty1:
cpi dutycounter,drate
brcs onstate
rjmp offstate
onstate:
sbi portb,3
ret
offstate:
cbi portb,3
ret
setduty: ;counterの値に対応するduty比を設定
ldi ZH,high(startdutydata<<1)
ldi ZL,low(startdutydata<<1)
add ZL,counter
brcc setduty1
inc ZH
setduty1:
lpm dcycle,Z
ret
inccounter:
inc counter
cpi counter,dutymax
breq inccounter1
ret
inccounter1:
clr counter
ret
startdutydata:
.db 1,2,4,8,16,32,64,128
enddutydata:
.equ duty_start_adr = startdutydata <<1
.equ duty_end_adr = enddutydata << 1
.equ dutymax = low(duty_end_adr - duty_start_adr)
.def work = r22
dispcounter: ;counter の値を表示
ldi ZH,high(startdata<<1)
ldi ZL,low(startdata<<1)
add ZL,counter
brcc dispcounter1
inc ZH
dispcounter1:
.equ mask = 0b00001000
cli
in work,portb
andi work,mask
lpm pattern,Z
or work,pattern
out portb,work
sei
ret
startdata:
.db 0b01110111, 0b00010100 ;0,1
.db 0b10110011, 0b10110110 ;2,3
.db 0b11010100, 0b11100110 ;4,5
.db 0b11100111, 0b00110100 ;6,7
.db 0b11110111, 0b11110110 ;8,9
.db 0b11110101, 0b11000111 ;A,b
.db 0b01100011, 0b10010111 ;C,d
.db 0b11100011, 0b11100001 ;E,F
enddata:
.equ num_start_adr = startdata <<1
.equ num_end_adr = enddata << 1
.exit