Gönderen Konu: Pascal vs. C String yapısı  (Okunma sayısı 6667 defa)

0 Üye ve 4 Ziyaretçi konuyu incelemekte.

Çevrimdışı wizofwor

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 398
Pascal vs. C String yapısı
« : 03 Ocak 2017, 14:12:44 »
Bitmek bilmeyen denemelerimden birinde kullanıcının girdiği metni saklamak için 6502.org'daki bir veteran'ın önerisini takip ederek aşağıdaki gibi bir veri yapısı kullanıyordum.

Kod: [Seç]
;10 karakterlik dizi değişkeni
;parametreler
string_dim = 10
string_len = 0

;allocation
string:
    for i,0,string_dim {!by 0}
 

Fark etmiş olacağınız üzere etiketten itibaren ilk bayt'a katarın uzunluğunu yazıp, peşinden uzunluk kadar hafıza ayırıyorum.

Sabit değerli katarları saklamak için ise gayet klişe olan katar+0 metodunu kullanıyordum.

Kod: [Seç]
string !scr"hede ve hodo",0

Bugün string karşılaştırma rutinlerini araştırırken öğrendim ki ilk yapı Pascal, ikincisi C string yapısıymış.

Her iki yapı da aynı miktarda bellek kullanırken C yapısında katarı okumak daha kolay. Index tutmaya gerek olmadan bitiş değerini alana kadar okumaya devam ediyoruz ancak değer eklemek acayip yavaş oluyor. Katarın uzunluğunu kontrol etmek için sürekli olarak baştan saymak gerekiyor.

Bu bağlamda Paskal yapısı çok daha kullanışlı görünüyor. Mr. Ritchie'nin benden daha iyi bildiği bir şeyler olduğu kesin. Artık kendisine sorma şansım olmadığı için sizlere sorayım dedim:

Katar uzadıkça veri girişi yavaşlayan ikinci yapının tercih edilme sebebi ne olabilir?
Gosub ile gidilen yerden goto ile dönen adam

Çevrimdışı Ref

  • Yönetici
  • Özgür Retrocu
  • *
  • İleti: 2970
  • Advanced User Simulator
    • ae unutmadan
Ynt: Paskal vs. C String yapısı
« Yanıtla #1 : 03 Ocak 2017, 15:37:29 »
hah, kodu (ve aslında konuyu) tam anlamadığım için düzgün bir yorum yapamayabilirim (katar falan, öztürkçe coding terimleri, kutadgu bilig), fakat Pascal ile C arasındaki temel farkın esneklikten geldiğini biliyorum.

Pascal, basic gibi yüksek seviyeli diller son derece yapısaldır. Dolayısı ile herşeyin doğru düzgün halledilmesi gerekir. C'de ise büyük bir olanak yelpazesi vardır ve bazen bu durum performansın önüne bile geçebilir.

Yani, bahsettiğin C stili daha fazla esneklik sunuyor olduğu için kullanılagelmiş olabilir. Ama pointer ve malloc'un olduğu bir dünyada array'lerin anlamı ne kadar var o da tartışılır.

Çevrimdışı wizofwor

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 398
Ynt: Pascal vs. C String yapısı
« Yanıtla #2 : 03 Ocak 2017, 21:28:20 »
ASM'yle uğraşırken eski kitapları hatırladığımdan olsa gerek karakter seti, katar, sabit disk gibi unutulmuş terimlere fazlaca meyil ediyorum galiba.  O zaman durumu ACME söz dizimininden bağımsız ve biraz daha güncel Türkçeyle kısaca tekrardan izah edeyim.

1. Durumda:
 
5 harflik bir dizi değişkenlerini saklayacağım adres alanını String={5;C1;C2;C3;C4;C5} şeklinde tanımlıyorum. Burada ilk byte uzunluk bilgisi, diğerleri karakterler oluyor.

Mesela String="hede"; dediğimde bellekteki durum String={4;'h';'e';'d';'e';'x'} oluyor. (Sondaki x dizi için ayırdığım alanda kullanmadığım baytı temsil ediyor.)

- Diziyi ekrana basarken 0'den 4'e kadar bir döngü kurgulamak gerekiyor.
+ Diziye n adet karakter eklerken taşma olmaması için maksimum boyut ile 4+n'i karşılaştırmak yeterli. 

2. Durumda:

Program içinde sabit olarak kullandığım diyizi String = {C1;C2;C3;C4;C5;0} şeklinde tanımlıyorum. Burada son byte ('0') dizinin bittiğini işaret ediyor.

Mesela String="hede"; dediğimde bellekteki durum String={'h';'e';'d';'e';0;'x'} oluyor.

+ Bu durumda diziyi ekrana basarken dizinin boyutuna bakmaya gerek kalmıyor. '0' görene kadar sırayla bütün bytlar ekrane gidiyor.
- Diziye harf eklerken ise sıkıntı var. Dizinin halihazırdaki boyutunu anlamak için bütün elemanlara tek tek bakıp '0' değerini bulana kadar sayaç tutmak gerekiyor ki bu işlem dizi uzadıkça daha çok vakit kaybı demek.

Artık ilk sorumdaki cümleyi buraya kopyalayabilirim sanıyorum.
Dizi uzadıkça yavaşlayan ikinci yapının tercih edilme sebebi ne olabilir?

Esneklik konusunu biraz daha açabilir misin mesela? Olay dizileri bölerken veya birleştirirken dizi uzunluğuyla uğraşmamak mı?
Gosub ile gidilen yerden goto ile dönen adam

Çevrimdışı witchdoktor

  • RAAT
  • Normalleşmiş Retroman
  • *
  • İleti: 757
Ynt: Pascal vs. C String yapısı
« Yanıtla #3 : 04 Ocak 2017, 08:34:09 »
@wizofwor

String her kullanıldığında hangi uzunlukta olduğunun bilinmesi programlama paradigması açısından bir handikap bence. Ayrıca C'de string'ler herhangi bir 'dizi'den farklı değil ve pointer'lar üzerinden işlem yapılabilen dinamik bellek yapıları. String boyutu derleyici tarafından derleme zamanında belirlenebilirdi ama bu sabit uzunluk daha sonra string boyutunu değiştirmek için string boyut değerinin değiştirilmesini gerektirirdi. Ayrıca bir de string boyutunu belirleyen değişkenin kaç byte olması gerektiği ile ilgili bir takım varsayımlar olması gerekirdi. C'deki haliyle string boyutunda herhangi bir kısıtlama yok. Ayrıca string'i kullanacak olan döngü loop'unun sabit bir sayı kadar tekrarlanması ile döngü koşulunun "\0 değilse tekrarla"  şeklinde olması performans olarak bir fark yaratır mı?

Çevrimdışı peandoas

  • Retroman
  • ***
  • İleti: 29
Ynt: Pascal vs. C String yapısı
« Yanıtla #4 : 04 Ocak 2017, 08:35:48 »

Selam,


Mr. Ritchie'ye sormak için geç ama Mr. Kernighan'ın olası yanıtına şuradan bakılabilir. Kendisi dokümanın sonuna doğru bir liste yapmış (1981'de, muhtemelen Borland'ın Turbo Pascal'ı ortalarda yokken) ve ilk maddesi şöyle:
Alıntı
1. Since the size of an array is part of its type, it is not possible to write general-purpose routines, that is, to deal with arrays of different sizes.  In particular, string handling is very difficult.
Konuyu okuyunca hevesle birkaç arama yaptım ama pek fazla doyurucu bilgi bulamadım, özellikle compiler ile çalışmış kişiler benden daha yetkin cevap verebilirler. Yine de Ref gibi konuyu tam olarak anlamıyorum ama kendisine katılıyorum. İki dilin ortaya çıkış tarihleri arasındaki çok kısa zamana rağmen (Pascal 69, C 70 sanırım) tasarlanma amaçları ve tanıtılmaları sırasında kullanılan ilkelerinde temel bir farklılık var. C "a general-purpose, imperative computer programming language, supporting structured programming, lexical variable scope and recursion, while a static type system prevents many unintended operations." olarak pazarlanırken, Pascal "small, efficient language intended to encourage good programming practices using structured programming and data structuring" pazarlanmış (tanımları wikipedia'dan aldım, çoğu kaynak aynısını söylemiş).


Kernighan'ın dediğinden hareketle bence bir sebep esneklik; UNIX (dolayısı ile C) geleneğinde yapılacak her işin sorumluluğunun kullanıcıya (o dönemde programcıya) ait olması isteniyor sanırım, ama asıl bununla alakalı sebep muhtemelen sistemin tamamiyle text stream'ler üzerine inşa edilmesi ve text arayüzünün evrensel olarak görülmesi (stdin, stdout, stderror ve pipe'lar, tüm datayı flat text file'de tutmalar vs). Bunlar tek bir ağızdan söylenen ve uygulanan prensipler değiller doğal olarak, her geçen birkaç kuruş ekleyerek konuyu felsefe haline getirmiş: The UNIX Philosophy. Bir yandanda C'de string tipinin hiç olmayıp handling için neredeyse assembly kabilinden işler yapmak gerekirken, aynı insanların Unix sistemleri acayip text manipulation araçları ile doldurmuş olması çok ilginç gelmişti bana, herhalde sistemi bi' ayağa kaldıracak kadar dili yapalım içine string yapısı koymaktansa oturur sistemde sed, awk, grep...  yazarız işimize bakarız diye düşündüler.
Dizi uzadıkça yavaşlayan ikinci yapının tercih edilme sebebi ne olabilir?
Muhtemelen yukarıdakilerle alakalı nedenlerden dolayı esneklik ve yavaşlamanın design decision'larına göre ihmal edilebilir olmasıdır, bunu da dilin içine string yapısı (ya da sınırlı uzunlukta string) koymaktansa serbest bırakmışlar ki port edilebilirlik açısındandır (dilin kendisi minimum eforla hedef platformda çalışır hale gelince, platform spesifik konuların kütüphaneler ile halledilmesi amaçlanmış olabilir). Pascal'ın data structure'lar ile çalışmaya meyilli dil yapısının aksine, text işleyecek adam oturur kütüphanesini yazar demişler.


İlk okumam gereken bağlantılar en sona kalmışlar:
http://stackoverflow.com/questions/4418708/whats-the-rationale-for-null-terminated-strings
https://en.wikipedia.org/wiki/Null-terminated_string#History
ve
https://www.bell-labs.com/usr/dmr/www/chist.html
Wikipedia linkinden:
Alıntı
At the time C (and the languages that it was derived from) was developed, memory was extremely limited, so using only one byte of overhead to store the length of a string was attractive. The only popular alternative at that time, usually called a "Pascal string" (a more modern term is "length-prefixed"), used a leading byte to store the length of the string. This allows the string to contain NUL and made finding the length need only one memory access (O(1) (constant) time), but limited string length to 255 characters (on a machine using 8-bit bytes). C designer Dennis Ritchie chose to follow the convention of NUL-termination, already established in BCPL, to avoid the limitation on the length of a string and because maintaining the count seemed, in his experience, less convenient than using a terminator.
Bu alıntının altında bide z80'den bahsediyor, hangi instruction bilen var mı?:
Alıntı
This had some influence on CPU instruction set design. Some CPUs in the 1970s and 1980s, such as the Zilog Z80 and the DEC VAX, had dedicated instructions for handling length-prefixed strings.


Bunların dışında Pascal'la ilgili aklımda kalan son ilginç ayrıntı, 2000'lerin başında byte'ın bir sayısında Pournelle Windows XP'deki buffer overflow'lardan birinden şikayet ederken 80'lerde işletim sistemi programcılarının içinden C'nin yerine Pascal'ın kullanılmasını öneren bir grubun türediğini fakat haklı iddialarını C'nin derlenme hızı yüzünden uygulamaya koyduramadıklarını anlatıyordu. Pascal'ın günlerine karşın C'nin saatlerle hesaplanan bir avantajı varmış o zamanlar.

Çevrimdışı nightlord

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 389
    • Night Network
Ynt: Pascal vs. C String yapısı
« Yanıtla #5 : 04 Ocak 2017, 08:39:43 »
Aslinda C'de string yok. Bahsettigin null-terminated string sadece bir convention. dilden gelen bir kavram degil. pascal tarzi stringleri de C' de kullanabilirdin. Bu tarzin neden tuttugu ile ilgili tarihsel sebepler vardir ama tercihin cok guclu sebeplerden geldigini sanmiyorum.  daha buyuk olasilikla, ilk birilieri hasbel kader kullandi ve kritik bir esik asildiginda var olan kod bu konvansiyon icin yazilmis oldugu icin herkes takip etti ihtimalen.

char* diger butun type pointer'lar gibi [] operatoru ile array gibi davranabildigi icin, null terminated stringler'de daha "dogal" bir algilanis sagliyor. yani mesela myStr[0] stringin ilk harfi oluyor.

std::string gibi daha etrafli temsillerde zaten implementasyonlarin cogu string uzunlugunu hazir halde bulundurur.

Çevrimdışı Ref

  • Yönetici
  • Özgür Retrocu
  • *
  • İleti: 2970
  • Advanced User Simulator
    • ae unutmadan
Ynt: Pascal vs. C String yapısı
« Yanıtla #6 : 04 Ocak 2017, 09:07:07 »
Esneklik konusunu biraz daha açabilir misin mesela? Olay dizileri bölerken veya birleştirirken dizi uzunluğuyla uğraşmamak mı?

bahsettiğim esnekliği nightlord çok güzel açıklamış zaten:

Aslinda C'de string yok. Bahsettigin null-terminated string sadece bir convention. dilden gelen bir kavram degil. pascal tarzi stringleri de C' de kullanabilirdin.


aynı şeyi witchdoktor da söylemiş aslında, peandoas öldürücü vuruş yaptığı için daha birşey ekleyemiyorum konuya :)
Güzel soru, güzel cevaplar valla, tadından yenmez :)

Çevrimdışı matahari

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 209
    • The Blog of Mert Börü
Ynt: Pascal vs. C String yapısı
« Yanıtla #7 : 04 Ocak 2017, 12:32:30 »
Bu alıntının altında bide z80'den bahsediyor, hangi instruction bilen var mı?:
Alıntı
This had some influence on CPU instruction set design. Some CPUs in the 1970s and 1980s, such as the Zilog Z80 and the DEC VAX, had dedicated instructions for handling length-prefixed strings.

2. cümlenin son kelimesi olan "strings" yerine "arrays" kullanılması, genelleme açısından çok daha doğru olacak. Zira, Z80 mimarisinde bulunan LDI/LDD, CPI/CPD, LDIR/LDDR ve CPIR/CPDR komutları sadece length-prefixed strings için değil, sabit uzunluktaki tüm array türleri için kullanılabilir.

...dilden gelen bir kavram degil.

Harika tespit!

Her programlama dilinin bir başka dile göre avantaj ve dezavantajları mevcuttur. Bir programlama dili tasarlamak, "zor tercihler yapmak" demektir. Sistem gereksinimlerine, kullanıcıların taleplerine, dönemin trendlerine, vs. kulak vermeyi gerektirir. Bu bağlamda; programlama dili bünyesinde sunulan özellikleri, dilin getirdiği zorlamalar olarak değil, programlama dilini geliştirenlerin tercihleri olarak görmekte fayda var.

Öte yanda, programlama dili geliştirenlerin aksine, günlük hayatta kod yazan programcılar çok daha şanslıdır. Geliştirdiği projenin gereksinim duyduğu esneklik/performans için ne gerekiyorsa onu seçmekte özgürdür. Array içinde sabit sayıda karakter varsa, performans adına LBPS (length-byte prefixed strings) kullanılır, aksi durumda ise NBTS (null-byte terminated strings) tercih edilir. Sonuçta, hem Pascal hem de C üzerinde el yordamı ile LBPS/NBTS implement etmek mümkün.

Programlama dillerinin kurallarına bağlı kalmamak, kod optimizasyonu kadar yaratıcılığımız için de faydalı. ;)

Çevrimdışı wizofwor

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 398
Ynt: Pascal vs. C String yapısı
« Yanıtla #8 : 06 Ocak 2017, 02:03:05 »
@wizofwor

String her kullanıldığında hangi uzunlukta olduğunun bilinmesi programlama paradigması açısından bir handikap bence. Ayrıca C'de string'ler herhangi bir 'dizi'den farklı değil ve pointer'lar üzerinden işlem yapılabilen dinamik bellek yapıları. String boyutu derleyici tarafından derleme zamanında belirlenebilirdi ama bu sabit uzunluk daha sonra string boyutunu değiştirmek için string boyut değerinin değiştirilmesini gerektirirdi. Ayrıca bir de string boyutunu belirleyen değişkenin kaç byte olması gerektiği ile ilgili bir takım varsayımlar olması gerekirdi. C'deki haliyle string boyutunda herhangi bir kısıtlama yok. Ayrıca string'i kullanacak olan döngü loop'unun sabit bir sayı kadar tekrarlanması ile döngü koşulunun "\0 değilse tekrarla"  şeklinde olması performans olarak bir fark yaratır mı?

Malum olduğu üzere ASM ile program yazarken string verisini tutacam tablonun boyutunu önceden tanımlamak ve program işletilirken bu tanımlı alan içinde kalacağını kontrol etmek zorundayım.

İlk başta kurguladığım rutin keyboard buffer'dan bir karakter alıyor ve eğer NULL değilse string tablosuna ekliyordu. Bellek taşmasını kontrol etmek için string'in boyunu tablonun başına yazdığımda kontrol için  aşağıdakine benzer bir kod yeterli oluyordu.

Kod: [Seç]
   LDX string
   CMP #DIM+1
   BEQ .overflow
   INX
   STA string,x

NULL terminated string modelini uygulamak istediğimde aynı kontrolü yapmak için NULL karakteri bulana kadar bütün tabloyu okumam gerekti.

Kod: [Seç]
.loop
   LDX #00
   LDA string,x
   BNE .loop     
   CPX #DIM+1
   BEQ .overflow
   INX
   STA string,x

Alttaki kod içerdiği döngü yüzünden n karakter uzunluğunda bir dizi için n kat yavaş çalışıyor.

Tabi bu durum 6502 özelinde geçerli ve benim kısıtlı tecrübelerime dayanıyor. Farklı tekniklerle bu kontrole gerek duymadan da karakter eklenebiliyordur.

Aslinda C'de string yok. Bahsettigin null-terminated string sadece bir convention. dilden gelen bir kavram degil. pascal tarzi stringleri de C' de kullanabilirdin. Bu tarzin neden tuttugu ile ilgili tarihsel sebepler vardir ama tercihin cok guclu sebeplerden geldigini sanmiyorum.  daha buyuk olasilikla, ilk birilieri hasbel kader kullandi ve kritik bir esik asildiginda var olan kod bu konvansiyon icin yazilmis oldugu icin herkes takip etti ihtimalen.

char* diger butun type pointer'lar gibi [] operatoru ile array gibi davranabildigi icin, null terminated stringler'de daha "dogal" bir algilanis sagliyor. yani mesela myStr[0] stringin ilk harfi oluyor.

std::string gibi daha etrafli temsillerde zaten implementasyonlarin cogu string uzunlugunu hazir halde bulundurur.


Kernighan'ın dediğinden hareketle bence bir sebep esneklik; UNIX (dolayısı ile C) geleneğinde yapılacak her işin sorumluluğunun kullanıcıya (o dönemde programcıya) ait olması isteniyor sanırım, ama asıl bununla alakalı sebep muhtemelen sistemin tamamiyle text stream'ler üzerine inşa edilmesi ve text arayüzünün evrensel olarak görülmesi (stdin, stdout, stderror ve pipe'lar, tüm datayı flat text file'de tutmalar vs). Bunlar tek bir ağızdan söylenen ve uygulanan prensipler değiller doğal olarak, her geçen birkaç kuruş ekleyerek konuyu felsefe haline getirmiş: The UNIX Philosophy. Bir yandanda C'de string tipinin hiç olmayıp handling için neredeyse assembly kabilinden işler yapmak gerekirken, aynı insanların Unix sistemleri acayip text manipulation araçları ile doldurmuş olması çok ilginç gelmişti bana, herhalde sistemi bi' ayağa kaldıracak kadar dili yapalım içine string yapısı koymaktansa oturur sistemde sed, awk, grep...  yazarız işimize bakarız diye düşündüler.
Dizi uzadıkça yavaşlayan ikinci yapının tercih edilme sebebi ne olabilir?

Muhtemelen yukarıdakilerle alakalı nedenlerden dolayı esneklik ve yavaşlamanın design decision'larına göre ihmal edilebilir olmasıdır, bunu da dilin içine string yapısı (ya da sınırlı uzunlukta string) koymaktansa serbest bırakmışlar ki port edilebilirlik açısındandır (dilin kendisi minimum eforla hedef platformda çalışır hale gelince, platform spesifik konuların kütüphaneler ile halledilmesi amaçlanmış olabilir). Pascal'ın data structure'lar ile çalışmaya meyilli dil yapısının aksine, text işleyecek adam oturur kütüphanesini yazar demişler.
.   

Biraz araştırınca C'deki scanf() ve gets() komutlarının overflow kontrolü yapmadığını öğrendim. Nette bol miktarda bu komutları kesinlikle kullanmamak gerektiğini savunan bolca yazı var. Hal böyle olunca olay daha bir anlamlı hale geldi. Stringlerle bu kadar haşır neşir olacak adam kendi kütüphanesi yazsın fikri aklıma yattı gibi.

Bir diğer açıdan bakınca NULL terminated stringleri okumak daha kolay. Sadece okunan değerin NULL olup olmadığına bakmak yeterli oluyor. Okuma işlemi daha sık gerçekleştiği için bu avantaj ağır basmış olabilir.

Bu kadar teorik konuşmadan sonra biraz kod yazmak istedim. Aşağıda yazdığım üç altprogram var.

Bu kadar aleyhine konuştuktan sonra NULL terminated yapıya geçmiş olmam biraz ilginç gelebilir. (İnceledikçe sevdim diyelim :) ) Daha sonra 256 karakterden uzun textlerle haşır neşir olursam self modifying kodlarla işlem yapması daha kolay olacağını düşündüğüm ve bu yapıdaki veriyi işleyen kodların yapısı daha basit ve anlaşılır geldiği için böyle bir tercih yaptım.

İlk olarak print rutini basitçe temp0 adresindeki poniterın gösterdiği metni Kernal rutinlerini kullanarak ekrana basıyor.

Kod: [Seç]
;--- Subroutine: PRINT ---
;
; input temp0(#<source add), temp1(#>source add)
; print chars in a loop
; quit when recieve #00 value


!MACRO PRINT .color, .label { ; Print text using CHAROUT
lda #.color
sta CURCOL
ldx #<.label
stx temp0
ldx #>.label
stx temp1
jsr print
}

!zone print
.NULL = $00 ; Null char
print:
ldy #0
- lda (temp0),y
beq .out

jsr CHROUT
iny
jmp -

.out
rts

printf rutini metni ekrana basarken '%' karakterine denk gelirse araya 'pname' stringini ekliyor. 

Kod: [Seç]
;--- Subroutine: PRINTF ---
;
; input temp0(#<source add), temp1(#>source add)
; pname (player name)
;
; print chars in a loop
; quit when recive #00 value
; print play name when revieve char('%')

!MACRO PRINTF .color, .label { ; Print text using CHAROUT
lda #.color ; with used defined
sta CURCOL ; user name
ldx #<.label
stx temp0
ldx #>.label
stx temp1
jsr printf
}

!zone printf {
.NAME = $25 ; Placeholder for player name   

printf:
ldy #0
- lda (temp0),y
beq .out
cmp #.NAME
beq .printName

jsr CHROUT
iny
jmp -

.out
rts

.printName
tya
pha

ldy #0
-- lda pname,y
beq +

jsr CHROUT
iny
jmp --

+ pla
tay
iny
jmp -
}


scanf rutini ise keyboard buffer'dan aldığı karakteri gerekli kontrolleri yaptıktan sonra önce string'in sonuna ekliyor, sonra da ekrana basıyor. Daha doğrusu öyle yapması lazım ama yapmıyor. Çakılıyor.

Kod: [Seç]
;--- Subroutine: SCANF ---
;
; output temp0(#<destionation addr), temp1(#>destination addr)
;

!MACRO SCANF .color, .label { ; ask a question
lda #.color ; and record the
sta CURCOL ; answer
ldx #<.label
stx temp0
ldx #>.label
stx temp1
jsr scanf
}

!zone scanf {

.RETRN = $0d ; char('return')
.DELET = $14 ; char('delete')
.DIM = 40 ; Allocated string dimention
.NULL = $00 ; Null char

scanf:
ldy #$00

.scan:
;tya
;pha
jsr GETIN ; get char
cmp .NULL
beq .scan ; no key is pressed

cmp #RETRN
beq .out ; return key

cmp #DELET
beq .deleteChar ; delete key pressed

cpy #.DIM
;bne .tooLong ; string too long

;pla
;tay
ldy #00
sta (temp0),y ; put char
jsr CHROUT ; print char to screen
iny ; increase char count
  jmp .scan

.deleteChar:
cpx #00
beq + ; if zero quit
jsr CHROUT ; update screen
lda #00
sta instr,x ; erase last char
dex ; decrease char count
+ jmp .scan

.tooLong
+PRINT $02, error1
.out
rts
}
Gosub ile gidilen yerden goto ile dönen adam

Çevrimdışı witchdoktor

  • RAAT
  • Normalleşmiş Retroman
  • *
  • İleti: 757
Ynt: Pascal vs. C String yapısı
« Yanıtla #9 : 06 Ocak 2017, 08:39:56 »
@wizofwor

C gibi dillerde boyut uzunluğuna sürekli ihtiyacın olan string'ler için bir structure ve yapılacak işlemler için fonksiyonlar tanımlaman daha mantıklı. Dilin kendi sunduğu veri tipleri bu tarz işlerde genelde yetersiz kalıyor, programın veri üzerindeki tam kontrolünü engelliyor. Sonraki jenerasyon OOP dillerinde zaten kendi veri tiplerini ve metodları tanımlayıp daha rahat kullanabiliyorsun.

Çevrimdışı nightlord

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 389
    • Night Network
Ynt: Pascal vs. C String yapısı
« Yanıtla #10 : 07 Ocak 2017, 03:32:28 »
scanf'te y registerini loopun her iterasyonunda 0'liyorsun gibi goruyorum. o yuzden cikmiyor. arti olarak chrin chrout falan gibi rutinler registerlara ne yapiyor onu da programcinin el kitabindan kontrol et eger etmediysen.


Çevrimdışı wizofwor

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 398
Ynt: Pascal vs. C String yapısı
« Yanıtla #11 : 08 Ocak 2017, 02:30:59 »
Sonunda NULL terminated scanf rutinimi çalıştırmayı başardım. Aşağıdaki kod temp0,temp1 adresinde gösterilen yere, temp3 adresindeki sayı kadar karakter kaydediyor. Buffer'dan return karakteri gelince dizinin sonuna NULL ekleyip çıkıyor.

Kod: [Seç]
;--- Subroutine: SCANF ---
; input temp0(#<destionation addr), temp1(#>destination addr)
; acc(color), temp2(string dimention)
;

!MACRO SCANF .color, .label, .dimention {
lda #<.label
sta temp0
lda #>.label
sta temp1
lda #.dimention
sta temp2
lda #.color
jsr scanf
}

!zone player_name_prompt {
;temp0,temp1 < target address
;temp2: < max string size
;temp3: < index

scanf:
sta CURCOL
lda #00
sta temp3

.scan:
RETRN = $0D ; return key
DELET = $14 ; delete key
jsr GETIN ; get char
cmp #00
beq .scan
cmp #RETRN
beq .endScan
cmp #DELET
beq .deleteChar

.addChar:
ldy temp3 ; get index
cpy temp2 ; compare with max string size
beq .scan ; if index=max > do not add
sta (temp0),y ; put char
inc temp3 ; increase index size
jsr CHROUT ; print char to screen
  jmp .scan

.deleteChar:
ldy temp3 ; get index
beq .scan
jsr CHROUT ; update screen
dey ; decrease index
lda #00
sta (temp0),y ; erase last char
sty temp3 ; store increased size
jmp .scan

.endScan
lda #$00
ldy temp3 ; get index
sta (temp0),y ; put char
rts
}

Gosub ile gidilen yerden goto ile dönen adam

Çevrimdışı nightlord

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 389
    • Night Network
Ynt: Pascal vs. C String yapısı
« Yanıtla #12 : 10 Ocak 2017, 01:08:11 »
Eline saglik wizofwor. Yalniz ne kadar farkindasin bilmiyorum ama bu kalkistigin kod baya zor bir is. senin scanf dedigin sey aslinda yaklasik olarak bir "EditBox". UI widget kutuphanelerinde genelde en belali olarak bilinen widget'tir EditBox. Cursor takibi, input takibi, rendering vs boyle kolay gorunur ama acayip kil bir problemdir. Kodun akisina bakarsan "mantiksal" olarak hayli cetrefilli bir akisi oldugunu gorebilirsin.

kucuk bir not: deleteChar bolumunde son karakterin yerine 0 yaziyorsun ya. Onu yapmana gerek yok. sadece indexi geriye alman yeterli. temp0 ve temp1'in gosterdigi bufferda basta "garbage" oldugunu varsayabilirsin. sadece indexe kadar olan ilk harfler gecerli data. boylece o iki instructiondan kurtulabilirsin.

Çevrimdışı wizofwor

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 398
Ynt: Pascal vs. C String yapısı
« Yanıtla #13 : 10 Ocak 2017, 23:19:40 »
Haklısın galiba zira algoritmayı oturtana kadar bayağı bir uğraştım. Öyle ki o bahsettiğin gerksiz kodu hangi iterasyonda koyduğumun farkında dahi değilim. Kernal rutinleri olmasa işin içinden çıkamazdım herhalde.

Senden label isimleri ve format konusunda yorum gelmediğine bu konularda biraz gelişme var diyebilir miyiz?

Gosub ile gidilen yerden goto ile dönen adam

Çevrimdışı nightlord

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 389
    • Night Network
Ynt: Pascal vs. C String yapısı
« Yanıtla #14 : 11 Ocak 2017, 04:57:37 »
Alıntı
Senden label isimleri ve format konusunda yorum gelmediğine bu konularda biraz gelişme var diyebilir miyiz?

Estafurullah :) Evet benim nacizane gorusume gore elbette stilistik olarak cok pozitif var: label isimleri. zone kullanimi (ozellikle zone{ } ile) ve zone icinde local label kullanimi hat safada olumlu.

Sordugun icin soyleyebilecegim bir kac nokta var ama BUNLARIN HIC BIRI COK ONEMLI DEGIL:
- dimention->dimension
- birkac mesaj onceki kodlarda ++ -- gibi isimsiz label'lar var. ben sahsen onlari hataya yol acabilir goruyorum. onun yerine her zaman zone{} ve local label'lari tercih ederim. Son mesajda onlari hic kullanmamissin zaten.
- RETRN ve DELET'in tanimlandigi yer loop'un icinde. Ben genelde instructionlarin arasinda "=" ile label tanimlamayi tercih etmiyorum. eski printf kodundaki gibi zone'un basinda tanimlamak kodun akisini okurken daha rahat gormek konusunda faydali bence.
- temp0..temp4 biraz daha oynadiklari role gore isimlendirilebilir. mesela
  - temp0 -> buffer
  - temp1 -> buffer + 1
  - temp2 -> maxSize (veya capacity)
  - temp3 -> index (veya cursor)
- ayrica bunlar daha buyuk bir codebase'de kolaylikla baska metodlara da uygulanabilir isimler olacagindan eger bu metoda spesifik yapamak istiyorsan, metod adiyla prefix edilebilir
  - scanf_buffer, scanf_capacity, scanf_cursor etc.

dikkat edersen, bu sekilde isimlendirme aslinda rutinlerin basina koydugun commentlerin bir kismini bile gereksiz kiliyor. Bu da beni, sahsen en faydali gordugum "rehber felsefe"lerimden birine bagliyor:

Bir kod parcasinin anlasilmasi icin yorum ekleme ihtiyaci hissediyorsam, belki o kodu daha anlasilir hale getirmem lazim.

Bu isimlendirme ile veya kodun hafifce farkli sekilde organize edilmesi ile basarilabilir. Bazen yorum yine de faydali olabilir. Ama eklemeden once son kez kendine sormakta fayda var "bunu daha anlasilabilir kilabilir miyim"