Bu ders bir önceki dersten bazı bilgileri miras alarak ilerleyecektir. Bu derste çizgi çizme ve ayrıca sözde rastgele sayı üretimi konusunda bazı bilgiler verilecektir. Ders 6’nın kodlarına ihtiyacınız olacaktır. Bu tutorial’in ilk bölümündeki çözümler kısmından indirebilirsiniz.
Noktalar
İlk ders için daha dişe dokunur imajlar çizdirmeyi beklemeniz gayet normal. Bir şeyler çizdirebilseydik gerçekten güzel olurdu. Bütün çizimlerdeki en temel bileşenlerden biri çizgidir. Eğer ekranda iki nokta arasında çizgi çizdirebilirsek, bu çizgilerin kombinasyonlarından ekranda daha karmaşık imajlar çizdirebiliriz.
Bu assembly kodumuzda bunu yapmaya çalışacağız ama ilk olarak bazı yardımcı fonksiyonlar yazacağız. SetPixel olarak çağırcağımız ve belirli piksel üzerindeki rengi ayarlayacak olan fonksiyon bunlardan biri. r0 ve r1 registerları üzerinde işlem yapacağız. Bu bize çok yardımcı olacak bir koddur. Bir şeyler çizdirmeden önce nereye çizdireceğimizi belirlemek ilk yapacağımız iştir. Bunu yapmanın bence en iyi yolu çizdireceğimiz yer depolayan hafıza kısımlarına sahip olmaktır.
drawing.s isminde bir dosya oluşturun ve aşağıdaki kodu içine kopyalayın:
[code]
.section .data
.align 1
foreColour:
.hword 0xFFFF
.align 2
graphicsAddress:
.int 0
.section .text
.globl SetForeColour
SetForeColour:
cmp r0,#0x10000
movhs pc,lr
ldr r1,=foreColour
strh r0,[r1]
mov pc,lr
.globl SetGraphicsAddress
SetGraphicsAddress:
ldr r1,=graphicsAddress
str r0,[r1]
mov pc,lr
[/code]
Yukarıda verilerle birlikte tanımladıklarım bir çift fonksiyondur. Onları ne çizceceğimiz ve nereye çizeceğimizi kontrol etmek için bir çeyler çizmeden önce main.s dosyası içinde tanımlayacağız. Bizim bir sonraki hedefimiz SetPixel metodunu uygulamak olacak. Bunun iki parametre alması gerekli. Çünkü kullanacağımız x ve y olmak üzere iki koordinat var. neyi çizceğimiz ve nereye çizceğimizi tam olarak knotrol etmek istiyorsak graphicsAddress ve foreColour kullanmalıyız. Bunu çabucak uygulamayı düşünüyorsanız yapın, eğer yapamıyorsanız yapılacak adımları özetlememiz yerinde olacaktır.
1. graphicsAddress’ini yükle.
2. x ve y koordinatlarının yükseklik ve genişlikten az olduğunu kontrol et.
3. Yazmak için pikselin adresini hesapla.(frameBufferAddress + x + y * width * pixel size)
4. foreColour da yükle.
5. adreste sakla.
Yukarıdakilerinin uygulaması aşağıdaki koddur:
1.2.
[code]
.globl DrawPixel
DrawPixel:
px .req r0
py .req r1
addr .req r2
ldr addr,=graphicsAddress
ldr addr,[addr]height .req r3
ldr height,[addr,#4]
sub height,#1
cmp py,height
movhi pc,lr
.unreq height
width .req r3
ldr width,[addr,#0]
sub width,#1
cmp px,width
movhi pc,lr
[/code]
Genişlik ve yüksekliğin frame buffer içinde sırayla 0 ve 4’ün ofsetinde tanımlandığını hatırlayın. İhtiyaç duyduğunuzda frameBuffer.s dosyasına dönebilirsiniz.
3.
[code]
ldr addr,[addr,#32]
add width,#1
mla px,py,width,px
.unreq width
.unreq py
add addr, px,lsl #1
.unreq px
[/code]
mla burada py ve width değerini çarpar, onları mul’da olduğu gibi px içine atar fakat atmadan önce adresini px kadar öteler. Bu kod yüksek renk frame bufferı ayarlar. Bu adresi ayarlamak için doğrudan shift ötelemesi kullanarak hesap yapıldı.
4.
[code]
fore .req r3
ldr fore,=foreColour
ldrh fore,[fore]
[/code]
5.
[code]
strh fore,[addr]
.unreq fore
.unreq addr
mov pc,lr
[/code]
Çizgiler
Bu belalı bir iştir. Çizgi çizdirmek göründüğü kadar kolay olmayabilir. İşletim sistemi yazarken, herşeyin kendi tarafımızdan yapıldığının farkına varmalıyız ve çizgi çizdirme için de herhangi bir istisna yoktur. Bu yüzden tavsiyem, herhangi iki nokta arasında nasıl çizgi çizdirebileceğimiz konusunda bir kaç dakika düşünelim.
Umuyorum çoğu stratejinin temel fikri çizginin açısını hesaplamakla ilgili olacaktır ve bu şekilde devam edilecektir. Bu kulağa muhteşem geliyor fakat aslında berbat bir fikirdir. Çünkü gradyan hesaplamaları gibi hesaplamalar bölme işlemi içerir ve tam sayıların takibine devam edebilmek assembly’de zordur. Bunun için elimizde Bresenham’ın algoritması var. Bu algoritma ekleme, çıkarma ve bit kaydırma işlemlerini kullanarak doğru çizme işlemi yaptırmaya yarar.
Bilgisayar ekranında doğru çizdirmek için başka DDA(Digital Differantial Analyzer) gibi yöntemler de mevcuttur fakat DDA bizim için uygun değildir. Çünkü eğim hesaplama işlemi yapılır. Kayan noktalı sayılar üzerinde işlem yapmak yuvarlama hataları barındırabilir ve assembly için daha zordur. Pseudo kod aşağıdaki gibi ifade edilebilir:
[code]
if x1 > x0 then
set deltax to x1 – x0
set stepx to +1
otherwise
set deltax to x0 – x1
set stepx to -1
end if
set error to deltax – deltay
until x0 = x1 + stepx or y0 = y1 + stepy
setPixel(x0, y0)
if error × 2 ≥ -deltay then
set x0 to x0 + stepx
set error to error – deltay
end if
if error × 2 ≤ deltax then
set y0 to y0 + stepy
set error to error + deltax
end if
repeat
[/code]
Aşağıdaki kod ise pseudo(sözde kod) kodun bir uygulamasıdır. Siz isterseniz kendiniz de yazabilirsiniz:
[code]
.globl DrawLine
DrawLine:
push {r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
x0 .req r9
x1 .req r10
y0 .req r11
y1 .req r12
mov x0,r0
mov x1,r2
mov y0,r1
mov y1,r3
dx .req r4
dyn .req r5 /* Note that we only ever use -deltay, so I store its negative for speed. (hence dy<strong>n</strong>) */
sx .req r6
sy .req r7
err .req r8
cmp x0,x1
subgt dx,x0,x1
movgt sx,#-1
suble dx,x1,x0
movle sx,#1
cmp y0,y1
subgt dyn,y1,y0
movgt sy,#-1
suble dyn,y0,y1
movle sy,#1
add err,dx,dyn
add x1,sx
add y1,sy
pixelLoop$:
teq x0,x1
teqne y0,y1
popeq {r4,r5,r6,r7,r8,r9,r10,r11,r12,pc}
mov r0,x0
mov r1,y0
bl DrawPixel
cmp dyn, err,lsl #1
addle err,dyn
addle x0,sx
cmp dx, err,lsl #1
addge err,dx
addge y0,sy
b pixelLoop$
.unreq x0
.unreq x1
.unreq y0
.unreq y1
.unreq dx
.unreq dyn
.unreq sx
.unreq sy
.unreq err
[/code]
Rastgelelik
Şimdi çizgi çizdirebiliriz. Bunu resim çizdirmek ve belirsiz bir şeyler çizdirmek için kullabiliyor olmamıza rağmen, bilgisayar rastgeleliği konusunda bilgilendirme yapmak için kullanmamızın yeri olduğuna karar verdim. Rastgele koordinatlardan iki çift seçeceğiz ve daha sonra değişik renklerle diğer seçilen noktaya çizim yapacağız. Rastgele sayı üretmek bir çok yerde kullanmak isteyeceğimiz bir işlev olacaktır. Random sayılar belirli bir matematik formülle üretilirler. Siz de kendiniz herhangi bir matematik formülünden rastgele sayı üretebilirsiniz fakat örneğin 4x^2/64 gibi bir işlemi ele aldığımızda bunun düşük kalitede random sayı ürettiğini söyleyebiliriz. Eğer x yerine 0 koyarsak sonuç daima sıfır olacaktır. Bu rastgele sayı üretmek için iyi bir formülasyon değildir. Rastgele sayı üretmekle ilgili bir çok algoritma vardır. Bunları araştırabilirsiniz. Bizim burada kullanacağımız kuadrik eşleşim üreticisi olacaktır. Bu iyi bir uygulamadır. 0 ile 2^32-1 kadar random sayı üretebilir. Kulanacağımız fonksiyon:
xn+1 = axn2 + bxn + c mod 232
olacaktır. Burada çeşitli sınırlar vardır:
1. a çifttir.
2. b= a+1 mod 4
3. c tektir.
Bu fonksiyondan aşağıdaki kodu yazabiliriz. Dosyamızın adı random.s olacaktır:
[code]
.globl Random
Random:
xnm .req r0
a .req r1
mov a,#0xef00
mul a,xnm
mul a,xnm
add a,xnm
.unreq xnm
add r0,a,#73
.unreq a
mov pc,lr
[/code]
a = EF0016 keyfi olarak kullanıldı. Kısıtlamalarımız düşünüldüğünde b=1 ve c=73 sonunucunu alırız.
Sonuç
Şimdi bütün fonksiyonlarımız hazır. Frame buffer bilgi adresini aldıktan sonra, aşağıdakileri yapmak için ana değişiklikler:
1. Frame buffer bilgi adresi içeren r0 ile SetGraphicsAddress fonksiyonunu çağır.
2. 4 register’ı 0’a ayarla. Biri renk olacak, biri random sayı olacak, biri x koordinatı ve diğeri ise y koordinatı olacak.
3. Son random numarayı giriş olarak kullanarak, tekrar yeni bir x koordinatı üret.
4. y koortinatı içermesi için bir önceki x koordinatının son random değerini kullan.
5. y koordinatını içeren son random sayıyı güncelle.
6. 0’dan 0xFFFF değerine kadar renk üretmesi için SetForeColour fonksiyonunu çağır.
7. 0 ve FFFFFFFF arasında ürettiğimiz değerleri mantıksal olarak 22 sağa kaydırma işlemi kullanarak 0 ve 1023 arasına dönüştür.
8. y koordinatının ekranda olduğunu doğrula. Yani 0 ve 767 arasında olup olmadığına bak. Eğer değilse 3. adıma dön.
9. Şimdiki x ve y koordinatları arasında son x ve y koordinatlarına doğru çizgi çiz.
10. Şimdik noktalardan bir tanesini içermesi için x ve koordinatlarını güncelle.
11. 3. adıma dön.
Eğer yapamazsanız endişelenmeyin. Çözüm sayfasında kodun yazılmış halini bulabilirsiniz. Bitirdiğinizde Raspberry Pi üzerinde test edebilirsiniz. Ekran üzerinde çok hızlı aralıklarla random çizgiler göreceksiniz.
Başarılı olduysanız elinizdeki fonksiyonlarla bir şeyler yapma konusunda yürekli olun. Çizgilerden daha karmaşık şekiller çizebilirsiniz.