Arsip: Cara Mudah Assembler di Delphi


by _aa_ in Articles more 14 years ago 15402
(Pengantar) buat yang mau diet ketat, boosting performance, atau cuma sekedar ingin tahu...
Target Audiens:
  • Idiot: Yes
  • Pemula: Yes
  • Terampil: ? (Questionable)
  • Mahir: No
  • Wizard: ? (Questionable)
  • Guru: Yes
Prerequisites (prasyarat):
  • pengetahuan assembler dikit-dikit
  • pengetahuan pascal delphi, cukup dikit aja.

Pendahuluan

bla-bla-bla... (silakan isi sendiri)

Bagian A.

Assembler di Delphi (atau dalam bahasa pemrograman apapun) tujuannya cuma dua:

  1. speed
  2. more speed!

Dalam asm Delphi, hal pertama yang harus difahami adalah model pemanggilan dan pertukaran nilai, dalam istilah kerennya: Calling Convention dan Parameter Passing (sepertinya enakan istilah keren yah? OK, mulai saat ini pake istilah keren aja lah supaya kedengerannya tidak culun).

Oh hiya, Sebagai fanatik Delphi, kita tidak akan ambil pusing dengan model lainnya, yang perlu kita tahu adalah model: pascal dan register. model pascal ini sebenarnya cuma untuk backward compatibility aja, tidak umum lagi dipakai (terutama dalam Object-orientednya Delphi), sementara dalam model register, semua argumen (kalau muat) akan dilewatkan melalui register. jadi ya selesai sudah, tidak ada yang perlu dipelajari lagi.

Sip dah!

BTW, jangan terintimidasi dengan istilah pointer atau adress yang nanti mungkin agak bertebaran di sini. semua sama aja artinya: ANGKA atau NOMOR, tidak lebih dan tidak kurang.

Bagian B.

Ini bagian nyantai aja. a bit of conscience.

yang mungkin perlu di-camkan baik-baik, di assembler itu tidak benar-benar ada pembagian tipe data, malah tidak dikenal itu monitor, keyboard, dan sebangsa peripherals lainnya. apatah lagi software, windows, sistem operasi dll., bahkan instruksi pun tidak ada. semua yang gituan itu nil, null, void, kosong, hampa (lho?)

yang ada cuma deretan bit seperti ini

...010010100100000000111100010010010100101010100101010100100110100100001110001110101010100111010100111100010
011101100100111110000110111101101010000100101000010001110111011101011011001111000101011100000111011101110010
101011110010011111000101011100101000010110000010000011101111101010001000100001110011100010111001101011000011
101000010011010001011000011110100001000110011001010100001101101111000110000101000000011110000001010100110011
011010001101011100010100001101001000101101001001001001110000100000101001010011101010010100101010101010101000
00100000111001000011010100100111X101001110101010110111001010001110101100000010001011110101100010010100110110
010000010010000101100111111000010110001010101001010110001010001111110100111101011001011001110100110001010011
011001000101000011011010011100111001111010010101010010110100101100100010010010111100110100101001100101100100
001000100X01001110000100000100110111110101001011101101010100000110000010100011000100111000101100111010101001
011110010001111111010010111011001100001000001011110101110010010011111000001101101010110001001000000010000110
100111010011110111010110010101101010000000010110010010110110011100110101101010X10010001011011001001011100011
101001111100001011111111010111100011001100101001101011000110001000001110001010111101010110001010101010010001
000001011100000110011011100101001101010100001000110010010110011111010100110000000010010001001010111101111101
111100000010010011100100001000001000110110110000110101101010010100000110011101001101011011000001001101011010
110011000010001000101000010101011111110010110101000111100001000100010100101001100101000111000111100000001101
111011100101010001000001010100000001101110000010111011000001000111110111000100100010000001001000101110110...
X : bit rusak

di memori dan di register (sebagai tempat penampungan sementara).

udah, intinya cuman itu thok.

setiap bit bisanya cuma kerlap-kerlip: 0 atau 1
karena setiap bit bebas sendiri-sendiri: mau kerlap keq, ato mau kerlip keq, maka deretan bit itu..


  kalo (seandainya saja) cuma ada 1 berarti bisa menampilkan: 0 atau 1
  kalo (seandainya saja) cuma ada 2 berarti bisa menampilkan: 00 01 10 atau 11

udah ngerti belon?

belon yah? agak rumit memang. terlalu sederhana buat orang secerdas kita.

kita ulang yah...

kosongin dulu otaknya.


  kalo seandainya (seandainya nih.., yang laen anggap aja rusak atau belon-
  dipasang) bit itu cuman 1 biji, maka bisa ada 2 kombinasi: 0 atau 1
  kalo saja bit itu cuman 2 biji, maka bisa ada 4 kombinasi: 00 01 10 atau 11
  kalo (seandainya) cuman 3 biji, maka bisa ada 8 kombinasi: 000 s.d. 111
  ...
  dst
  ...
  kalo (seandainya) ada N biji, maka bisa ada 2^N-1 kombinasi: 0..0xN s.d. 1..1xN
sipirili kan?

karena jikalau ngeliatin bit-bit doang orang bisa gila sendiri, maka supaya gampang, dikelompokin lah bit-bit tersebut jadi 8 batang per-gepok, yang kemudian diberi istilah: byte.

seperti kita sudah pelajari dengan rumit barusan, 1 byte yang terdiri dari 8 bit akan bisa menampilkan 256 kombinasi angka dari 0 s.d. 255 atau dari 0000 0000 s.d. 1111 1111.
(sebagian perakit biasa menyebut 4 kelompok bit seperti itu sebagai: nibble)

dengan kapasitas memori rata-rata 256MB (1M = 1024 X 1024), maka akan terdapat deretan bit sebanyak 256 X 1024 X 1024 X 8 = 2.147.483.648 biji (2G). yang artinya ada 2^2147483647 kombinasi angka yang bisa ditampilkan

kalau kita telusuri:


  2^0 = 1 (1 digit)
  2^1 = 2 (1 digit)
  2^2 = 4 (1 digit)
  2^4 = 16 (2 digit)
  2^8 = 256 (3 digit)
  2^16 = 65536 (5 digit)
  2^32 = 4294967296 (10 digit)
  2^64 = 18446744073709551616 (20 digit)
  2^128 = 340282366920938463463374607431768211455 (39 digit)
  2^256 = 115792089237316195423570985008687907853269984.. (78 digit)
  2^512  :  155 digit
  2^1K  : 309 digit
  2^2K  : 617 digit
  2^4K  : 1234 digit
  2^8K  : 2467 digit
  2^16K  : 4931 digit
  2^32K  : 9865 digit
  2^64K  : 19279 digit
  2^128K  : kira-kira 19279 X 2 =~ 19000 X 2^1 digit
  2^256K  : kira-kira 19279 X 4 =~ 19000 X 2^2 digit
  2^512K  : kira-kira 19279 X 8 =~ 19000 X 2^3 digit
  2^1M  :~ 19000 X 2^4 digit
  2^2M  :~ 19000 X 2^5 digit
  2^4M  :~ 19000 X 2^6 digit
  2^8M  :~ 19000 X 2^7 digit
  2^16M  :~ 19000 X 2^8 digit
  2^32M  :~ 19000 X 2^9 digit
  2^64M  :~ 19000 X 2^10 digit
  2^128M  :~ 19000 X 2^11 digit
  2^256M  :~ 19000 X 2^12 digit
  2^512M  :~ 19000 X 2^13 digit
  2^1G  :~ 19000 X 2^14 digit
  2^2G  :~ 19000 X 2^15 digit =~ 19000 X 32768 =~ 622.592.000 digit

en so- memori utama akan mampu menampilkan kombinasi angka sebanyak: X
(X adalah sebuah angka yang digitnya lebih dari 622.592.000, atau disederhanakan sebagai: 1 dengan 622.592.000 nol dibelakangya)

itu baru memori utama...

dengan kapasitas harddisk rata-rata 120+GB (1G harddisk memang beneran cuma 1.000.000.000, geblek memang dasar pedagang) yang bisa dijadikan memori juga, terdapatlah (setelah cingcay dikit jadi 125GB): 125 X 1000.000.000 X 8 = 1.000.000.000.000, atau 1 tera (enolnya ada 12) deretan bit.

itu artinya ada 2^999999999999 kombinasi angka yang bisa ditampilkan.
yah, kira-kira... 1 dengan 300 milyar nol dibelakangnya.

itulah tipe data native bahasa assembly.

(selanjutnya beberapa keping dari deretan bit-bit tersebut diterjemahkan oleh CPU sebagai perintah, buat mengolah deretan bit-bit tersebut)

Ya udah, cuma gitu doang koq.

Bagian C.

kenapa sih pake byte? karena dulunya cpu intel emang cuma bisa mengakomodasi maksimum se-byte alias 8 bit sekali proses. tapi istilah itu sekarang sudah jadi ukuran standard segala macem yang berkaitan dengan komputer.

begitupun dengan deretan bit di memori, dikelompokkan jadi byte dan-- nah ini dia yang penting: tiap byte dikasih nomor urut (sesuai urutannya). nomor urut itu yang disebut sebagai: address (kayaknya gak ada yang aneh ya? hehe- iya emang, ga ada).

balik lagi ke byte, seperti telah termaktub diatas, sebuah byte bisa menampilkan kombinasi antara 0 sampai dengan 255, iya kan? iya lagih! (supaya gampang biasanya sih terus pake heksadesimal: antara $00 sd $FF).
semua kombinasi antara 0 s.d. 255 ini di-peta-kan atau dengan kata lain masing-masing bisa direpresentasi-kan oleh sebuah simbol (sesuai nomornya antara 0..255 tadi).
naah- simbol- simbol inilah yang biasa disebut dengan ASCII character. singkatnya disebut character ajah, atau char. termasuk huruf-huruf tulisan ini sebenarnya adalah cuma tampilan simbol dari deretan angka-angka byte-byte di memory.

jadi kalo dibilang byte di address $100 isinya $37, berarti byte di address $100 isinya adalah: $37!
(ya iya! apa sih maksudnyah?)

maksudnya kita cuma mengingatkan bahwa $37 itu adalah 1 byte dalam satu kesatuan, bukan berarti isinya dua hurup: tiga dan tujuh, begicuu...

selain byte pengelompokkan bits ini biasa dilakukan juga dalam kelipatannya:


  word: 2 byte atau 16 bit = 0 s.d. 65535
  double word (dword): 2 word = 4 byte = 32 bit = 0 s.d. 4294967295
  quadword: 2 dword = 4 word = 8 byte = 64 bit = 0..18446744073709551615
  double quadword = 2 quadword = 4 dword = 8 word = 16 byte = 128 bit

lainnya:


  tbyte: 5 word = 10 byte = 80 bit ~ format khusus double extended FP

hal lain yang perlu diperhatikan adalah tentang interpretasi deretan byte di memory tersebut menjadi sebuah nilai (buat dibaca, di-load ke register dll) kalo cuma 1 byte gak masy'alah pan? bisa langsung dilihat misalnya: oh- ternyata byte di address $100 nilainya adalah $37...(iya. udah!)

bagaimana kalao kita mau ambil sebuah nilai word dari adress $100 dan $101 (yang masing masing misalnya isinya adalah: $37 dan $2A) berapakah nilai yang kita peroleh?

pastinya $372A iya kan? tinggal sambungin aja.

ternyata salah tuh...

byte-order intel menggunakan mode little-endian, artinya urutan byte di memori dimulai dari yang paling kecil ke byte yang paling besar. besar/kecil disini adalah urutan signifikasi-letaknya dalam deretan angka. istilahnya: dari Least Significant Byte ke Most Significant Byte (LSB to MSB).

hal ini, bisa kita amati selanjutnya sangat tergantung kepada... eh- enggak usah ding. maaf salah- lupakan saja. (eh- gimana sih nih pak dosen!? :()

tadinya seh- mau bilang: ..tergantung kepada besaran yang mau kita ambil apakah itu word, dword dst.. tapi gak usah-lah nanti malah bingunn.. pake contoh aja lihat nanti.

pokoknya kayak di kita dah, yang paling kecil kena duluan: BBM naek, Listrik naek, kerusuhan, bom, PHK. terus naek sampe ke yang paling gede.

contoh:


address(H): 100 101 102 103 104 105 106 107 108 109 10A 10B 10C 10D 10E 10F
isi(H)    : 37  2A  10  20  30  40  50  60  70  80  90  11  22  33  44  55

ah- contohnya susah diley-out, ganti aja deh..

contoh:


alamat memory paling akhir/atas
-------------------------------
address (hex)	isi (hex)
  10F		55
  10E		44
  10D		33
  10C		22
			< pemisah supaya enak ngeliatnya
  10B		11
  10A		90
  109		80
  108		70
			< pemisah supaya enak ngeliatnya
  107		60
  106		50
  105		40
  104		30
			< pemisah supaya enak ngeliatnya
  103		20
  102		10
  101		2A
  100		37
-------------------------------
alamat memory paling awal/bawah

nilai word di address $100 adalah: $2A37 ($37 dulu, terus depannya: $2A)
nilai dword di address $100 adalah: $20102A37 ($37 dulu, paling gede: $20)
nilai qword di address $100 adalah: $6050403020102A37
nilai word di address $101 adalah: $102A ($2A dulu, terus depannya: $10)
nilai dword di address $101 adalah: $3020102A ($2A dulu, paling gede: $30)
nilai qword di address $101 adalah: $706050403020102A
nilai word di address $107 adalah: $7060 ($60 dulu, terus depannya: $70)
nilai dword di address $107 adalah: $90807060 ($60 dulu, paling gede: $90)
nilai qword di address $107 adalah: $4433221190807060

tebolak-balik yah?

tapi ini konsisten dengan pemuatan nilai kedalam register, misalnya:


  mov eax, dword ptr [109h] ; ngeload dword dari alamat memori $109
                            ; hasilnya eax = $22119080
                            ;           ax = $9080
                            ;           al = $80
  mov ax, word ptr [109h]   ; ngeload word dari alamat memori $109
                            ; hasilnya ax = $9080
                            ;          al = $80
  mov al, word ptr [109h]   ; ngeload byte dari alamat memori $109
                            ; hasilnya al = $80

tuh pan? kalo kita ngeload nilai dari address yang sama, byte-ordering nya tetep konsisten, baik itu pake dword, word atau byte.

Semua deklarasi yang ada di Delphi, tidak lebih dan tidak kurang cuma sekadar sebutan lain atawa alias dari sebuah address di memori. Selesai. gampang, kan?

Bagian D.

Alkisah dalam sebuah routine.. (model pemanggilan pascal nich, terpaksa kudu diungkit dikit, soalnya berkaitan erat sama keterangan selanjutnya), jika dibentang isi perutnya, akan terlihatlah bahwa Delphi (otomatis) menambahkan stack-frame (tepatnya ENTRY dan EXIT code), di-awal dan di-akhir routine tsb.

Enaknya langsung contoh aja ya:

misalnya kita punya 2 buah routine:

  1. Prototype fungsi1
    
      function AnuinDonkDek(A, B, C: integer; D: byte): integer;
      var
        i, j: integer;
        Ch: char;
      begin
        ...
      end;
    
    A, B, C dan D diatas disebut sebagai: Parameters
    i, j dan Ch disebut juga sebagai: Local Variables

  2. Prototype fungsi2
    
      function nDakMauAhh: integer;
      begin
        ...
      end;
    
    fungsi ini tidak punya Parameter ataupun Local Variable

Ketentuannya sbb.: jika suatu fungsi memiliki Local Variables dan/atau Parameters, maka ENTRY dan EXIT code-nya adalah sbb.:


  push ebp       // ENTRY-
  mov  ebp, esp  //   CODE
  ...
  pop ebp        // EXIT-
  ret N          //   CODE

tambahan khusus, kalau (setidaknya salah satunya) dari yang barusan diatas itu ternyata adalah Local Variables, maka bakal diselipkan ditengahnya:


  sub  esp, Locals
    ...
  mov  esp,ebp

dan bergabunglah mereka menjadi:


  push ebp
  mov  ebp, esp
    sub  esp, M  // M = jumlah byte Local Variables (kelipatan 4/DWORD)
    ...
    mov  esp, ebp
  pop ebp
  ret N          // N = jumlah byte Parameters (kelipatan 4/DWORD)

Gitu loh...

So-... fungsi AnuinDonkDek diatas akan menghasilkan ENTRY/EXIT code:


  push ebp
  mov ebp, esp
  add esp, -12   // = sub esp, 12 (buat 3 local vars: i, j dan Ch)
    ...
  mov esp, ebp
  pop ebp
  ret 16        // bersihin stack 4 tumpuk: A, B, C dan D

sementara fungsi nDakMauAhh akan menghasilkan ENTRY/EXIT code:


  ret           // yak.. gitu doang. bisa juga dibilang: ret 0

Nah, dalam model pemanggilan pascal ini, sewaktu kita panggil AnuinDonkDek, maka kita (Delphi yang otomatis ngerjain -sih) dari fungsi yang memanggil, musti memasukkan nilai A, B, C dan D berturut-turut kedalam stack:


  push A
  push B
  push C
  push D  // nilai D (byte) sebelumnya bakal di-promosi dulu ke 32bit

itulah sebabnya jumlah byte Parameter (buat RET diatas) jadi kelipatan 4, kalo Local Variables laen lagi, itu optimisasi buat CPU, tergantung ngaturnya, tapi totalnya (hampir) akan selalu DWORD aligned.

model pemanggilan yang laen sebenernya sih sama aja, cuma beda-beda dikit.

misalnya stdcall yang acapkali (bahasa tinggi neh...) nongol di Windows, cuma beda di nge-push-nya, dia dari ujung (D) ke pangkal (A). safecall juga nyaris sama dengan stdcall, bedanya do'i utamanya dipake buat Dual- Interface method yang support Automation-Object.

Nah lo,.. apaan tuh???

Udah lah, gak penting, pokoknya segala jargon COM, OLE, ActiveX dll. yang ribet dan kita kagak (mau) ngatri, berarti gak penting, setuju!?

(sebenernya sih yang disebut safe dari safecall itu tidak lain karena parameternya selain lewat stack juga di backup pake register).

mending kita tau model cdecl deh, yang sangat umum sekale di platform UNIX atau Leenooks; di cdecl ini, urutan tumpukan stack sama dengan stdcall/safecall, bedanya disini fungsi pemanggil-lah yang berkewajiban untuk ngeberesin stack;

jadi (diulang), kalo di pascal (juga stdcall dan safecall): stack diberesin sama fungsi yang dipanggil pada saat mau selesai, kalo di cdecl: stack diberesin sama fungsi yang memanggil ketika sahutan balik. singkatnya: cdecl bener-bener berkebalikan 180° dari model pascal.

terakhir dan yang paling penting musti kita tau adalah: model register, yang disarankan dan secara default digunakan oleh Delphi karena paling efisien, buat kita sih terutama kerana pemrograman object-oriented di Delphi mau tak mau harus pake model register.

Dalam model register, parameter dilewatkan.. ya via register dunk! berturut-turut dari kiri ke kanan: EAX, EDX, ECX. model pascal diatas jadi gak laku deh, ENTRY/EXIT code-nya cuma sekedar ret doang seperti fungsi nDakMauAhh diatas.

lalu- gimana jikalau parameter yang kita pasang bederet banyak?
he-he, sisanya yang gak ketampung regs, balik lagi layauw pake model pascal.

dalam pemanggilan model register ini Delphi membuat konvensi sbb:

untuk semua parameter variable (parameter dengan directive var didepannya) contoh:


  procedure ngex(var I: integer); //misalnya,
  procedure ngox(var S: string; var D: double); //misalnya lagi,

maka selalu, yang diantarkan adalah rujukannya (reference) atau pointer atas data ybs. inget-inget: ini berlaku buat semua parameter var, titik.

selain itu, perlu diketahui juga bahwa tidak semua laki-laki... ups! maksudnya, tidak semua jenis tipe data bisa dimasukkan lewat register; berikut dibawah ini cara pemassingan (translasi dari: 'passing', kamsudnyah) parameter value (tanpa directive) dan parameter konstan (dengan const)

jenis ordinal: char, byte, shortint, smallint, word, longword, cardinal, termasuk juga tipe enumerated dan pointer, semuanya bisa lewat register. cuma harus diingat bahwa meskipun lebarnya cuma 8-bit (seperti byte/char), tetep makan tempat satu register berukuran 32-bit. misalnya kalo 1 char di EAX, yang kepake cuma AL, sisa bit yang gak kepake, undefined. jangan pernah dikira-kira, kalo gak yakin dikosongin aja secara explisit.

set, serta record/array yang total jendralnya 1, 2 dan 4 byte (awas! angka-3 gak masuk hitungan loh) juga lewat register. eh- tapi inget, array di sini aray static, bukan array dinamik ya. (mis. array[0..1] of integer, gicu). kalo array dinamik tuh yang dideklarasikan mis. sbb:


  var
    idih: array of integer.

cat. kalo pake pake cdecl, stdcall atau safecall, semua jenis record bakal dibuntel bulet-bulet ke stack, gak efisien banget dah. jadi jangan pernah masukin record mentah-mentah pake model ini. pake pointernya aja yak?.
(lagian siapa yang mau, kita kan pake Delphi... hidup Delphi!)

semua tipe real (floating point) masuk lewat stack. int64 juga (help-nya delphi di sini koq bisa salah ya? ini buat IA64 kali yah?).
dengan digunakannya stack, maka kita musti tunduk sama model pascal, pake ENTRY/EXIT code, lantas ret N sebanyak asupan ke stack.

selain daripada semua diatas itu, daripada parameter variable (dengan daripada directive var) serta daripada selain daripada tipe daripada diatas, daripada semuanya diakses melalui daripada pointer daripada tipe data yang bersangkutan, yah.. daripada tidak sama sekali.

Begitulah akhirnya, pada prinsipnya, Delphi memberikan kebebasan kepada program untuk menggunakan EAX, EDX, dan ECX sebatas koridor yang telah ditetapkan (istilah tinggi lagi, bahasanya mahasiswa/aktivis yang baru lulus, ato anggota DPR yang baru dilantik/dikarbit; bahasa bubur, gak jelas, mengawang, meluap-luap gak bikin kenyang).

dan atas nama program, selanjutnya kita buebas, bas-bas.. seenak udel kita untuk memanipulasi, mengkorup dan mengobok-obok hak, eh- EAX, EDX, dan ECX buat kepentingan kita pribadi! hua-ha-ha-ha!

tapi eit! jangan coba-coba sentuh register-register yang lain yah!
..boleh sih colek dikit, asalkan pas kelar balikin lagi nilainya ke asal, termasuk kalo direction flag diset (STD), musti direset lagi lah dengan CLD. tambahan, buat constructor-destructor, register DL juga termasuk yang terlarang untuk diembat sembiringan.

Selama konvensi diatas di jaga, insya Allah, kita akan selamat sentosa, sepanjang time-slice dikandung thread-one.

Bagian E.

Bagian D-nya agak panjang juga yah?
akhirnya, nge-post assembler di Delphi sangat mudah, contoh yang paling sederhana (dan valid) adalah:


  procedure gakNgapaNgapain;
  asm
  end;

eureka! 100% assembly language pertama kita dalam Delphi!

lagi dong...


  function Ciek: integer; asm mov eax, 1; end;
(bad-style. meski sah-sah aja sih)

  function Double(const Ch: char): byte; register overload assembler ASM
    mov al, @Ch; shl eax END;
(bad-bad-style...)

sebaiknya, kata orang-orang sih.., setiap baris program hanya berisi paling banyak 1 instruksi assembler. (cara yang sok ribet untuk bilang: sebaris satu; padahal masih salah, kalo paling banyak satu -berarti boleh setengah, hayo! bego- siapa sih tuh yang mulaiin?)

o-iya, Delphi menetapkan EAX sebagai hasil dari sebuah fungsi (Result), jadi setelah kita akrobat jungkir-balik pake repne scasb, cemplungin dah tuh hasil akhirnya ke EAX, celeprot!

seperti tersebut dalam kisah diatas noh! parameter value/const int64 masuknya bakal lewat stack (udah gua bilang: kalo parameter var selalu pake pointer, gak nyimak luh ah!). yang koclak-nya tuh, kalo sebuah fungsi menghasilkan int64 maka Result-nya akan dibalikin lewat EDX:EAX (64-bit) karena EAX kalo sorangan wae mah, kagak bakal muat menampung segenap isi int64.

wanti-wanti nih, khususon buat fungsi string. Delphi meletakkan Result seolah-olah sebagai parameter paling akhir, coba lihat deh:


  function CindilAbang(const S: string): string;
  // S = EAX
  asm
    ...
    mov S, Result
    mov eax, @Result
    ...
  end;

Result-nya apa hayo...?

kedua baris di atas ternyata ngehasilin code assembly yang sama:


  mov eax, edx

lalu kalau kita tambahkan parameter sehingga prototype fungsinya menjadi


  function CindilAbang(const S: string; I: integer): string;
  // S = EAX, I = EDX
maka code assembly yang tadi tuh jadi:

  mov eax, ecx

yang parah kalo registernya habis kepake, maka Result di tarok-nya di stack deh.

dimanakah gerangan parahnya?

parahnya karena Result itu sebenulnya (kolaborasi benar + betul) adalah pointer ke deretan char (yang dimulai dari index-1).

ketika Result itu ditaruh di register, kita bisa langsung mengambil (misalnya) char pertama alias Result[1] dengan:


  mov al, [Result] // alias: mov al, [edx]

nah ketika Result itu disimpan di stack, kiat -eh kita, 'rak iso sa' karep'e dewe melakukannya persis -plek, sama seperti kepada bung register tadi. itu adalah suatu hil yang mustahal, kerana stack itu sendiri ya sekadar adress thok. disini persisnya kita mengambil angka dari angka, yakni pointer atas string dari alamat stack.

terpaksalah kita musti comot dulu isi stack tsb. ke register, baru kita bisa cuplik isi memori dari register (yang sekarang berisi address string) tsb:


  mov esi, Result  //  disinih sih sama ajah dengan: mov esi, [Result]
                   //  atau: mov esi, @Result //kagak ada beda semuanya
  mov esi, [esi]   //  load pointer string yang nilainya ada di esi
  mov al, [esi]    //  ambil karakter pertama

agak berbelit yay? tapi syukur alhamdulillah sekarang semuanya udah pada mafhum kan? he-he-he...

juga kalo bikin fungsi string, kita musti merhatiin reference-count dari Result string yang dihasilkan. soalnya pointer yang dikasih sama delphi sebagai Result mungkin sekali berisi string yang udah ada dan masih/sedang dipake sama fungsi lainnya. kalo fungsi kita nanti menghasilkan string yang sama dengan string yang sudah ada, Result-nya tinggal nunjuk ke string yang ada tersebut dan reference-count-nya dinaekin satu, kalo belon ada, kita musti membuat string global baru, yang mungkin nanti dipake sama fungsi-fungsi lainnya. ribet banget yah? iya memang...

trik yang paling gampang. daripada kelenger ngitung ref-count-nya sebuah string, adalah dengan mengeset Result sebagai empty-string di awal fungsi, biar systemnya Delphi yang ngurusin ref-count. lantas kita musti buat string global baru sesuai dengan hasil fungsi string kita (dengan memanggil Uniquestring atau StrSetlength).


  // kite udah buat penjelasannye pake boso londo nih, supaye keren..
  //
  // maintaining String reference count is such a daunting task,
  // the easier way is simply clearing Result before using it
  // (if the Result already empty it does nothing, else it will takes
  // care of previous instance of the string, check ref-count, whether
  // it was str constant etc., and then freeing memory if necessary)
  //
  // afterwise then we should call Uniquestring (already done if we do
  // explicitly set the length of the string/Result)
  ...
  mov eax, Result            // where the result will be stored
  call System.@LStrClr       // cleared for ease
  mov edx, MyLength          // Length of String
  call System.@LStrSetLength // result: new allocation pointer in EAX
  ...

hati-hati juga, hampir semua fungsi string dari system bakalan menghancurkan isi EDX dan ECX (Result seperti biasa di EAX).

terakhir tentang string, jangan lupa kita musti jaga tuh Result baek-baek sebab nantinya ntu address bakal jadi sogokan kita buat fungsi yang manggil. katenye Delphi mah, ntu address Result musti dibalikin lewat EAX, tapi.. (sebenernya rahasia nih), sepanjang kite -penulis- bikin fungsi string, EAX kita cuekin pun kagak pape tuh, mau diisi paan juga kagak ngaruh, nyang penting sogokan buat si Result musti nyampe, jangan pernah sampe salah kamar, bisa-bisa dibentak kernel dah kita orang,.. access violation!! katanyah.

Bagian F.

apaan lagi yah? yang kira-kira perlu?

udah aja lah.

code sample:
contoh ini diambil dari unit ordinals (tolong, style jelek-nya jangan ditiru)

Code sample 1: fungsi ROL/ROR.


//~~~~~~~~~~~~~~~~~~~~~~
// ROL/ROR
//~~~~~~~~~~~~~~~~~~~~~~
// fungsi untuk ngerol, memutar bit dalam satu nilai.
type
  r64 = packed record
    lo, hi: integer;
  end;
function rol(const I: integer): integer; overload asm rol I, 1 end;
function rol(const I: integer; const ShiftCount: integer): integer; overload asm
  mov ecx, ShiftCount
  rol I, cl
end;
function ror(const I: integer): integer; overload asm ror I, 1 end;
function ror(const I: integer; const ShiftCount: integer): integer; overload asm
  mov ecx, ShiftCount
  ror I, cl
end;
function rol(const I: int64): int64; overload asm
    // I masuk lewat stack
    mov edx, I.r64.hi  // using register is faster than direct memory access
    mov eax, I.r64.lo  // in Pentium they could even be run parallelized
    shl eax, 1; rcl edx, 1
    jnc @done; or eax, 1
  @done: //popfd
end;
function ror(const I: int64): int64; overload asm
    // I masuk lewat stack
    mov edx, I.r64.hi
    mov eax, I.r64.lo
    shr edx, 1; rcr eax, 1
    jnc @done; or edx, 1 shl 31
  @done: //popfd
end;
function rol(const I: int64; const ShiftCount: integer): int64; overload asm
    // I masuk lewat stack, jadi ShiftCount = eax
    mov ecx, ShiftCount //upon shift this value will be taken MODULO 32
    mov edx, I.r64.hi
    mov eax, I.r64.lo
    and ecx, $3f; jz @exit
    cmp cl, 32; jb @begin
    //xchg eax, edx   // avoid LOCK prefixed xchg instruction
    mov eax, edx      // simple move should be faster & pairing enable
    mov edx, I.r64.lo //
    jz @exit
  @begin:
    push ebx; mov ebx, eax
    shld eax, edx, cl
    shld edx, ebx, cl
  @done: pop ebx
  @exit:
end;
function ror(const I: int64; const ShiftCount: integer): int64; overload asm
    // I masuk lewat stack, jadi ShiftCount = eax
    mov ecx, ShiftCount //upon shift this value will be taken MODULO 32
    mov edx, I.r64.hi
    mov eax, I.r64.lo
    and ecx, $3f; jz @exit
    cmp cl, 32; jb @begin
    //xchg eax, edx   // avoid LOCK prefixed xchg instruction
    mov eax, edx      // simple move should be faster & pairing enable
    mov edx, I.r64.lo //
    jz @exit
  @begin:
    push ebx; mov ebx, edx
    shrd edx, eax, cl
    shrd eax, ebx, cl
  @done: pop ebx
  @exit:
end;

Code sample 2: Pseudo Random Generator.

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Pseudo-random generator, sumber asli dari Uniform Random Number Generator.
// Copyright by Agner Fog, http://www.agner.org/random/, lisensi GNU GPL
//
// (versi asli dalam pure assembler language)
// kita cuma benerin fungsi floating pointnya yang gak jalan + inisialisasi
// yang (mestinya) lebih baik. setidaknya yang ini bener-bener ngerjain
// -apa yang maunya dilakukan sama versi aslinya, berkaitan sama real number-
// ++
// enhance double ke extended (full 64-bits, bukan cuma sekedar casting),
// history buffer diatur ulang, supaya klop sama pengembalian nilai bertipe
// int64, nyocokin min/max range, trimming sana-sini, dll. Delphi's specific.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// karena ini aslinya bukan punya penulis, jadi kalo ada angka2 aneh (MAGIC)
// yang gak jelas asal-muasalnya, tanya sendiri aja gih sama yang buat.
// (sekali lagi: tolong, style jelek-nya jangan ditiru)
//  interface...
//function RandomInt64: int64; overload
//function RandomInt(const Max: cardinal): cardinal; register overload
//function RandomInt(const Min, Max: integer): integer; register overload
//procedure RandomInit(const I: integer = $19091969); register
const
  RandseedEx: array[0..4] of integer = ($19091969, $22101969, $09022004, 0, -1);
    // cat. array[3] masih 0 karena anakku yang kedua belon lahir,
    // kira-kira sekitar januari/februari 2005 lah.
 // implementation...
var
  factors: array[boolean] of int64 = ($13FB7DD4FFC7, 7627861919189 - 1);
  X: integer absolute RandSeedEx;
  f: integer absolute factors;
function RandShuffle: int64; register asm
  push edi
  mov eax, f.0; mul X.16        // x4
  mov ecx, eax; mov eax, X.12   // x3
    mov edi, edx
  mov X.16, eax; mul f.8
  add ecx,eax; mov eax, X.8     // x2
    adc edi, edx
  mov X.12, eax; mul f.12
  add ecx, eax; mov eax, X.0    // x0
    adc edi, edx
  mov X.8, eax; mul f.4
    add eax, ecx; adc edx, edi
    add eax, X.4; adc edx, 0
  mov X.0, eax; mov X.4, edx    // x1
  pop edi
end;
function RandomInt64: int64; register asm call RandShuffle end;
function RandomInt(const Max: cardinal): cardinal; register asm
  test Max, -1; jnz @begin; ret
@begin: push Max
  call RandShuffle; pop edx
  mul edx; mov eax, edx
end;
function RandomInt(const Min, Max: integer): integer; register asm
// Min-Max range should not exceed cardinal boundary minus 1
// (max difference = 4294967295)
  sub max, min; jns @_
    xor eax, eax; ret          // zeronize
  @_: inc max                  // difference = (max - min) +1
  push min; push max           // ...save
  call RandShuffle             // get R
  pop edx; pop ecx             // ..restore
  mul edx                      // multiply R by difference (truncated)
  lea eax, edx + ecx
end;
type
  f80 = packed record          // 80 bits extended floating point
    lo, hi: integer;
    exp: word;
  end;
function RandomExt: Extended; register asm
  call RandShuffle             //  edx:eax
  or edx, 1 shl 31             //  normalized bit-63 in edx
  mov Result.f80.lo, eax
  mov Result.f80.hi, edx
  mov Result.f80.exp, -1 shr 18// 3fffh
  fLd1                         //  load 1.0, since...
  fLd Result                   //  1 < Result < 2
  fSubRP                       //  after sub, now 0 < Result < 1
  fStP Result                  //  store back, pop!
  wait                         //  be polite, please...
end;
{ Original version
  RandomDbl PROC NEAR
  public RandomDbl
    CALL    RandomBit            ; random bits
    MOV     EDX, EAX             ; fast conversion to float
    SHR     EAX, 12
    OR      EAX, 3FF00000H
    SHL     EDX, 20
    MOV     DWORD PTR [TEMP+4], EAX
    MOV     DWORD PTR [TEMP], EDX
    FLD1
    FLD     QWORD PTR [TEMP]     ; partial memory stall here
    FSUBR
    RET
  RandomDbl ENDP
}
procedure RandomInit(const I: integer = $19091969); register
const
  PRIME0 = 7;
  PRIME1 = $01C8E80D; // 29943821 : 1 1100 1000 1110 1000 0000 1101
                      //            ~ 1 11001000 11101000 00010101
  e: extended = 0;    // use extended for broader range of generated number
asm
  push edi; mov edi, 4
  @LFill:
    imul eax, PRIME1
    dec eax; mov X[edi*4], eax
    dec edi; jge @LFill
  mov edi, PRIME0
  @@LRand: call RandomExt
    fstp e // note: for consistency, never directly alter X in RandomExt
    mov eax, e.f80.lo; xor X, eax
    // mov edx, e.ext.hi; mov X.4, edx
    // warn: we should not use edx, since it has been convoluted.
    //       (the highest bit is always 1)
    dec edi; jnz @@LRand
  pop edi
end;
Code sample 3: fungsi string.

Untuk contoh kode yang berkaitan dengan string, silakan lihat cxposdemo, bisa di-download dari Delphi Super Page (DSP)
dan please, please... bug-report-nya

_EOF_
this document has actually been written for personal CSS styling exercise purposes

Jakarta, 0 Desember 2005,
last revision: 0 Desember 2005,
aa, Adrian H. dan Inge, DR.

Local Business Directory, Search Engine Submission & SEO Tools FreeWebSubmission.com SonicRun.com