Sangat menyenangkan untuk memenuhi keinginan saya, terutama dari masa lalu yang jauh, begitu jauh sehingga saya sudah lupa bahwa saya pernah menginginkannya. Saya tahu sedikit tentang demoscene dan saya tentu saja tidak pernah mengikuti penulis atau pekerjaan mereka, saya hanya suka menonton apa yang terjadi. Kadang-kadang saya ingin mengetahuinya, tetapi kemudian saya tidak memiliki pengetahuan dan pengalaman, kemudian ketekunan, dan kemudian saya benar-benar kehilangan minat dalam hal ini. Tetapi baru-baru ini, teman saya, dengan siapa kami belajar pada waktu itu dan yang memasok kami dengan semua produk baru, termasuk demo, dengan BBS dan Fidonet, karena ia hampir semua memiliki telepon dan modem dan komputer pada saat yang sama, mengunjungi
CAFePARTY dengan pekerjaannya yang membuat saya membuka arsip komputer pertama saya, pilih demo dan cari tahu.
Mengevaluasi kekuatan saya secara objektif, saya mengambil intro 128 byte yang saya sukai secara visual. File
pentagra.com
ditandatangani oleh
Mcm , 128 byte, terakhir diubah 9/24/1996 18:10:14, hex dump:
000000: b0 13 cd 10 68 00 a0 07 06 1f ac ba c8 03 ee 42
000010: b1 40 ee 40 6e 6e e2 fa b8 3f 3f bb 40 01 bf 40
000020: 05 57 b1 78 ab 03 fb e2 fb 5f b1 60 88 01 aa 03
000030: fb 03 fb e2 f7 b1 61 88 01 aa 2b fb 2b fb e2 f7
000040: bf d1 99 57 b1 78 ab 2b fb e2 fb 5f b1 8f f3 ab
000050: 81 fe 00 fa 73 12 ac 0a c0 74 0d 48 88 44 fe 88
000060: 04 88 40 ff 88 84 bf fe 03 f2 42 75 e3 e4 60 3c
000070: 01 75 a5 b8 03 00 cd 10 c3 00 00 00 00 4d 63 6d
Dari arsip yang sama saya menarik:
- Hiew 6,11 ( 6,50 dapat ditemukan di situs) - Saya menggunakannya sebagai disassembler
- Paket TASM - dengan mana saya mengumpulkan kode yang diterima kembali untuk memastikan bahwa saya tidak mengacaukan apa pun
- Bantuan TECH Flambeaux Software! 6.0 - referensi online yang cukup terperinci dan komprehensif untuk API DOS, fungsi BIOS, perangkat keras dan assembler
- Mayko G.V. Assembler untuk IBM PC - referensi format berukuran hampir saku untuk semua perintah dasar Intel 8086 dan aturan pemformatan teks program. Tanpa detail arsitektur dan dengan contoh-contoh dasar, hanya hal-hal yang paling mendasar. Ada hampir semua yang Anda butuhkan di sini, tetapi Anda tidak dapat menulis dalam assembler selain dari lingkungan.
- Oleh karena itu, buku kedua Zubkov S.V. Assembler. Untuk DOS, Windows, dan Unix - Panduan untuk Nook Hardware dan DOS
Dari implementasi minimal yang ekstrim, kita harus mengharapkan penggunaan trik dan pendekatan non-standar, tetapi terlepas dari beberapa asumsi dalam kondisi awal, saya tidak melihat trik teknis, tetapi saya melihat trik algoritmik. Dan di sini beberapa kata harus dikatakan tentang pengalaman itu. Apa yang bisa menjadi kesulitannya? Baik dalam implementasi atau dalam algoritma. Misalnya, dalam perintah
mov di, 099d1h
, Anda mungkin takut dengan konstanta sihir. Tetapi jika Anda berada dalam konteks penggunaan, menjadi jelas bahwa ini adalah alamat untuk akses pada koordinat layar X dan Y, di mana X = 17, Y = 123, 320 adalah resolusi horizontal layar dalam piksel. Bersama-sama, ini memberi kita 17 + 123 * 320, konversi koordinat dua dimensi menjadi satu dimensi.
Melihat sekarang apa yang terjadi di layar, saya dapat dengan mudah membayangkan bagaimana saya bisa mengimplementasikan ini, walaupun tidak dengan 128 byte, bahkan jika tidak 100% mirip, tapi saya bisa. Dan 20 tahun yang lalu, saya tidak bisa, walaupun saya mengeluarkan semua alat yang saya gunakan dari rak berdebu dan saya tidak perlu menjelajah Internet untuk memahami cara kerjanya. Oleh karena itu, pertama-tama, ini adalah konteks, pemahaman tentang APA yang terjadi, dan pertanyaan tentang trik dan BAGAIMANA melakukannya adalah di tempat kedua.
Apa yang kita lihat:
- 5 baris pentagram. Ini bukan garis langsung yang tidak dapat dipisahkan menurut semua kanon. Kami hanya melihat sosok umum, tanpa detail
- Efek nyala, yang terdiri dari dua bagian penting: palet yang dipilih dengan benar dan algoritma untuk secara konstan mengubah warna titik-titik pada layar dengan unsur-unsur ketidakpastian, tetapi mempertahankan urutan palet terus menerus untuk titik-titik tetangga. Misalnya, Anda dapat menghitung seluruh layar saat ini dengan rata-rata nilai piksel tetangga dari layar sebelumnya, dan menambahkan lebih banyak titik "cerah" di tempat-tempat acak, atau tidak di tempat-tempat acak, tetapi nilainya acak, atau tidak sama sekali kebetulan, hanya menjauh dari urutan linier. Salah satu opsi adalah bagaimana hal itu dilakukan dalam DOOM . Hasilnya harus dalam bentuk warna yang mengalir satu sama lain, dari area terang yang terus muncul hingga memudar
Masih memahami bagaimana ini dilakukan. Deskripsi lebih lanjut tidak akan menggantikan pengetahuan tentang arsitektur komputer dan fungsi-fungsi DOS atau assembler, tetapi memiliki pengetahuan ini akan memungkinkan Anda untuk memahami dan fokus pada esensi dari apa yang terjadi. Setelah mulai menulis, saya menyadari bahwa ternyata semuanya sama dalam detail yang cukup, tetapi saya tidak bisa menolaknya agar tidak hilang dalam arti cerita.
DOS dan memuat program .COM
Program dalam file
.com
adalah kode bersih, tanpa header, Anda hanya perlu meletakkannya di tempat yang tepat. Inilah yang dilakukan DOS, atau lebih tepatnya panggilan sistem 4Bh. Cukup banyak tindakan yang terjadi, mari kita pikirkan hasilnya:
- Semua segmen mendaftar CS, DS, ES, SS dimuat dengan nilai tunggal
- 65536 byte dicadangkan untuk seluruh program, tepat satu segmen yang ditunjukkan oleh semua register segmen. 256 byte pertama ditempati oleh header sistem - PSP (Program Segment Prefix). Di CS: 0, bidang pertama PSP, perintah INT 20h terletak - untuk mengakhiri program saat ini dan mentransfer kontrol ke proses induk. Program itu sendiri dimulai dengan CS: 100h address dan menempati 128 byte berikut
- Kata 0000h didorong ke tumpukan, register SP adalah FFFEh. Ini berarti bahwa dua byte terakhir di segmen ini di alamat SS: FFFEh diatur ulang. Bahkan, ini adalah alamat pengirim terdekat dari prosedur, yang akan membawa kita ke perintah penyelesaian di CS: 0
- Register AL dan AH berisi flag kesalahan untuk menentukan huruf drive dari argumen pertama dan kedua ketika program dipanggil. Jika tidak ada kesalahan, maka itu adalah 0, jika ada maka FFh
Saya dengan tulus percaya bahwa dalam kasus umum status register tidak ditentukan. Tetapi dalam kode yang dianalisis, menurut pendapat saya, asumsi yang sangat berani dibuat tentang keadaan awal mereka, khususnya tentang CX, register SI dan bendera arah DF. Saya tidak menemukan konfirmasi ini dalam daftar sumber yang menghasilkan di atas, jadi saya pergi untuk melihat melalui sumber
MS-DOS 2.0 :
- Tentang DF, kita dapat mengasumsikan bahwa itu disetel ulang oleh perintah
cld
, karena yang terakhir menggunakan arah maju sebelum mentransfer kontrol ke cld
baris, oleh karena itu, DF diatur ulang. Meskipun tidak ada penggunaan eksplisit cld
di tempat ini, perintah untuk menghapus bendera arah ditemui cukup sering sebelum banyak transfer lainnya - SI berisi 100 jam, karena digunakan untuk menentukan offset yang akan dimuat ke dalam register oleh penghitung perintah IP
- CX sama dengan FFh, karena digunakan sebagai penghitung dengan nilai awal 80h untuk mentransfer isi dari seluruh baris perintah dan, karenanya, setelah mentransfernya adalah 0. Dan setelah itu, CL, sebagai variabel sementara, memuat FFh dan digunakan untuk mengatur bendera kesalahan huruf drive di AL dan AH
Tidak ada sumber versi yang lebih baru, tetapi ada
sumber DOSBox :
reg_ax=reg_bx=0;reg_cx=0xff; reg_dx=pspseg; reg_si=RealOff(csip); reg_di=RealOff(sssp);
Artinya, ini bertepatan dengan apa yang saya lihat dalam kode sumber MS-DOS (versi ke-2!), Anda dapat melihat nilai awal register lain, ini dia inisialisasi khusus dan eksplisit. Untuk MS-DOS, nilai register selain AX, segmen, dan stack adalah dasar penggunaannya untuk tujuan lain, ini bukan dogma atau standar, oleh karena itu, mereka tidak disebutkan di mana pun. Tetapi di sisi lain, ekosistem yang telah terbentuk dan seluruh rasa sakit Microsoft dalam mendukung kompatibilitas dengan versi lama, memaksa untuk menyeret semua nilai yang dihasilkan secara acak, menjadi sedikit dimengerti, karena programmer sangat terbiasa dengannya.
Akhirnya, bagi kami pengetahuan ini cukup, kami mulai memulihkan program dari header:
.186 .model tiny .code .startup
Kami menentukan jenis prosesor 80186, karena kami menggunakan perintah
outsb
, yang hanya muncul dalam model ini. Satu segmen kode dan titik masuk ke program, yang, bersama dengan definisi model memori
tiny
, akan memungkinkan kompiler menghitung dengan benar semua offset variabel dan transisi. Saat membangun
tlink
, tombol
/t
digunakan, pada output ini akan memberikan file
.com
.
Grafis dan palet
Untuk beralih ke mode grafis, Anda perlu beralih ke fungsi BIOS, yang disebut gangguan 10 jam, AH = 0, di AL kami menempatkan pengenal mode yang diinginkan - 13 jam:
mov al, 13h ;b0 13 int 10h ;cd 10
Harap dicatat bahwa kami tidak menyentuh AH, dengan asumsi bahwa ada nol, sesuai dengan kondisi pemuatan program. Mode yang dipilih sesuai dengan resolusi grafis 320 kali 200 piksel dengan palet 256 warna. Untuk menampilkan titik di layar, Anda perlu menulis ke area memori, yang dimulai dengan alamat A000h: 0, byte yang sesuai dengan warna. Isi register data segmen dengan nilai ini:
push 0a000h ;68 00 a0 pop es ;07 push es ;06 pop ds ;1f
Logikanya, memori diatur sebagai larik dua dimensi di mana koordinat layar ditampilkan, 0: 0 sesuai dengan sudut kiri atas. Setelah beralih mode, itu diisi dengan nol - hitam di palet default. Rumus untuk menerjemahkan ke perpindahan linear adalah
X + Y * L , di mana L adalah resolusi horizontal, dalam kasus kami 320. Dalam formulir ini, saya akan menulis di tempat-tempat di mana konstanta digunakan, ketika menerjemahkan teks program mereka dihitung secara otomatis.
Untuk mengubah palet, kami langsung mengakses peralatan menggunakan port input / output:
lodsb ;ac mov dx, 03c8h ;ba c8 03 out dx, al ;ee
Perintah pertama dimuat ke AL ββbyte data yang terletak di DS: SI. Di DS, kami telah memuat alamat segmen memori video dan kami tahu bahwa itu diisi dengan nol, di SI - dalam kasus umum, tidak diketahui bahwa setidaknya 0. Tidak masalah bagi kami di mana pun SI menunjukkan, kami hampir pasti masuk ke memori video yang menempati dengan resolusi ini 320 * 200 = 64000 byte, hampir seluruh segmen. Dengan demikian, kami berharap bahwa setelah perintah ini AL = 0. Unit ditambahkan atau dikurangi menjadi SI, tergantung pada pengaturan bendera arah DF. Meskipun ini juga tidak terlalu penting bagi kami, di mana pun SI bergerak, kami masih tetap berada di area memori video yang penuh dengan nol.
Selanjutnya, muat DX dengan nomor port 03C8h, output yang menentukan warna 256 yang akan kita timpa. Dalam kasus kami, ini adalah 0 dari AL.
Warna dikodekan dalam palet RGB dan untuk ini Anda harus menulis ke port 03C9h (satu lebih dari 3C8h) tiga kali berturut-turut, satu kali untuk masing-masing komponen. Kecerahan maksimum komponen adalah 63, minimum adalah 0.
inc dx ;42 mov cl, 64 ;b1 40 PALETTE: out dx, al ;ee inc ax ;40 outsb ;6e outsb ;6e loop PALETTE ;e2 fa(-6), 6
Tambah DX satu per satu sehingga memiliki nomor port yang diinginkan. CL adalah penghitung siklus kami dari 64, dan kami mengasumsikan bahwa CH = 0, seperti yang dijelaskan sebelumnya berdasarkan kondisi pemuatan awal. Selanjutnya, kita output komponen pertama ke port - yang merah, kecerahan yang akan disimpan dalam AL, itu yang akan kita ubah, pada langkah pertama 0. Setelah itu kita tingkatkan kecerahannya satu per satu untuk ditampilkan pada iterasi berikutnya. Selanjutnya, kita menjalankan dua perintah
outsb
menulis ke port, yang nomornya terdapat dalam DX, byte dari area memori DS: SI, ingat bahwa kita memiliki nol di sana. SI setiap kali berubah satu.
Segera setelah kami menyimpulkan ketiga komponen, sebuah unit secara otomatis ditambahkan ke nomor warna. Dengan demikian, tidak perlu mendefinisikan ulang warna dengan keluaran ke port 3C8h jika warnanya berturut-turut, seperti yang diperlukan. Perintah
loop
akan mengurangi CX oleh satu, jika nilai bukan nol diperoleh, itu akan pergi ke awal siklus, jika 0, lalu ke perintah berikutnya setelah siklus.
Sebanyak 64 kali pengulangan. Pada setiap pengulangan, kami menentukan warna, mulai dari 0 hingga 63, komponen merah dengan kecerahan bertepatan dengan nomor warna saat ini. Kami mengatur ulang komponen hijau dan biru untuk mendapatkan palet seperti itu dari kecerahan minimum hingga maksimum merah:
Garis
Siapkan warna awal dan nilai koordinat:
LINES: mov ax, 03f3fh ;b8 3f 3f mov bx, 0+1*320 ;bb 40 01 mov di, 64+4*320 ;bf 40 05 push di ;57
Dalam AL dan AH, kami memuat masing-masing warna 63 (3Fh), maksimum yang dimungkinkan, AX mendefinisikan dua titik sekaligus. BX - resolusi horisontal maksimum. Di masa depan, ini akan digunakan untuk menambah atau mengurangi satu baris dari koordinat saat ini. DI - koordinat 64: 4, simpan di tumpukan.
Gambar garis pertama dari sudut kiri atas ke ekstrem kanan :
mov cl, 120 ;b1 78 LINE1: stosw ;ab add di, bx ;03 fb loop LINE1 ;e2 fb(-5)
Konfigurasikan penghitung - ini akan menjadi jumlah baris. Selanjutnya, simpan kata (dua byte) dari AX ke alamat ES: DI. Tindakan ini akan menampilkan dua titik pada layar dengan warna maksimum dari palet kami, karena ES dikonfigurasi untuk memori video, dan koordinat tertentu diatur dalam DI. Setelah tindakan ini, 2 akan ditambahkan ke DI, karena dua byte ditulis. Kami jelas tidak mengatur bendera arah DF dan bergantung pada fakta bahwa itu diatur ulang, sekali lagi kami mengingat kondisi awal kami untuk memuat program. Kalau tidak, keduanya akan diambil, yang tidak memungkinkan menggambar garis yang diinginkan.
Selanjutnya, DI = DI + BX, yang setara dengan meningkatkan koordinat Y per satu. Dengan demikian, dalam tubuh siklus, dua titik digambar dalam satu garis, koordinat X bertambah 2, dan koordinat Y sebesar 1 dan tindakan ini diulang 120 kali, gambar dengan hasilnya sedikit lebih rendah.
Baris kedua adalah dari kiri atas ke atas :
pop di ;5f mov cl, 96 ;b1 60 LINE2: mov [bx+di], al ;88 01 stosb ;aa add di, bx ;03 fb add di, bx ;03 fb loop LINE2 ;e2 f7(-9)
Kami mengembalikan koordinat awal 64: 4 dan mengatur penghitung ke 96 pengulangan. Kami mencetak satu titik, tetapi satu baris di bawah koordinat saat ini. Seperti sebelumnya, ini dicapai dengan menambahkan nilai dari BX, hanya tanpa menyimpan koordinat baru. Konstruksi
[bx+di]
atau
[bx][di]
disebut pengalamatan basis dengan pengindeksan dan bekerja pada tingkat prosesor, bukan penerjemah. Register segmen default dengan BX adalah DS. Setelah itu kami menampilkan titik kedua, tetapi sudah di koordinat saat ini. DI, dan karena itu X bertambah satu, karena hanya perintah transfer satu byte yang
stosb
-
stosb
. Dua perintah terakhir dari tubuh siklus adalah peningkatan Y sebesar 2, yang kita gunakan lagi BX.
Setelah menggambar dua garis, gambar berikut diperoleh di dekat sudut kiri atas:
Koordinat kiri dan atas, di sebelah kanan alamat offset dalam memori video. Poin 64: 4 akan ditarik dua kali.
Baris ketiga adalah dari atas ke sudut kanan atas :
mov cl, 97 ;b1 61 LINE3: mov [bx+di], al ;88 01 stosb ;aa sub di, bx ;2b fb sub di, bx ;2b fb loop LINE3 ;e2 f7(-9)
DI sudah berisi nilai koordinat yang diinginkan 160: 196, kita perlu menggambar garis dari atas di mana garis sebelumnya berakhir, bergerak ke atas layar sambil mempertahankan sudut yang sama. Dengan demikian, siklusnya hampir identik. CX ditingkatkan sebesar 1, karena koordinat Y saat ini adalah 2 lebih (lebih rendah) daripada di mana garis sebelumnya berakhir, sudah dihitung untuk iterasi berikutnya. Karena itu, untuk sampai ke sudut atas, Anda perlu mengambil langkah ekstra. Pergerakan sepanjang X berlanjut ke arah yang sama - ditambah satu setelah setiap iterasi, dan sepanjang Y, alih-alih menambahkan, kita kurangi keduanya. Poin ditampilkan dalam urutan yang sama, pertama lebih rendah lalu atas.
Baris keempat adalah dari paling kiri ke sudut kanan atas: mov di, 17+123*320 ;bf d1 99 push di ;57 mov cl, 120 ;b1 78 LINE4: stosw ;ab sub di, bx ;2b fb(-5) loop LINE4
Kami lagi dalam koordinat yang diperlukan, tetapi ini tidak digunakan, tampaknya agar tidak mengubah bendera arah DF. Oleh karena itu, koordinat baru ditempatkan di DI dan disimpan di tumpukan.
Selanjutnya, semuanya identik dengan baris pertama, hanya koordinat Y tidak tumbuh, tetapi menurun, kita naik.
Baris kelima adalah horizontal: pop di ;5f mov cl, 143 ;b1 8f rep stosw ;f3 ab
Semuanya sederhana di sini, mekanisme transmisi mikroprosesor digunakan, karena garis horizontal berhubungan dengan peningkatan sederhana pada alamat setiap titik berikutnya. Dalam DI, alamat yang sesuai dengan koordinat sudut ekstrem kiri, disimpan pada langkah sebelumnya, dikembalikan. Jumlah pengulangan dalam CX diatur dan awalan pengulangan diterapkan dengan perintah transfer kata.
Setelah tindakan ini, kami memiliki pentagram yang sepenuhnya digambar dalam warna paling cerah. 80 byte digunakan dan 48 cadangan.
Sihir api
Kami menetapkan syarat batas untuk perhitungan: FLAME: cmp si, 320*200 ;81 fe 00 fa jae NEXT_PIXEL ;73 12 lodsb ;ac or al,al ;0a c0 jz NEXT_PIXEL ;74 0d
Di SI akan ada koordinat titik saat ini untuk perhitungan, jika kita melampaui batas layar, maka kita tidak melakukan perhitungan dengan titik ini, kita lanjutkan menghitung yang berikutnya.
lodsb
memuat byte dari area DS: SI ke dalam AL, yaitu warna titik pada koordinat saat ini. Jika 0, maka kami juga tidak melakukan apa pun dan beralih ke poin berikutnya.
Perhitungan warna baruIni adalah algoritma utama untuk mengubah nilai warna pada layar, ini bukan nyala api, ini adalah dasar untuk itu. Kami menghitung titik tetangga dan mencapai kontinuitas warna:
dec ax ;48 mov [si-2], al ;88 44 fe mov [si], al ;88 04 mov [bx+si-1], al ;88 40 ff mov [si-1-1*320], al ;88 84 bf fe
Kurangi dari AX, sebenarnya dari AL, unit yang berisi nilai warna bukan nol yang diperoleh dari koordinat saat ini. Selanjutnya, kami menuliskan nilai yang diperoleh ke semua titik tetangga, relatif terhadap koordinat saat ini, yaitu, sedikit dari mereka, berdasarkan palet kami.
Karena setelah
lodsb
, nilai SI meningkat satu dan tidak lagi sesuai dengan titik yang warnanya kita baca dalam AL, ini harus disesuaikan. Perhatikan bahwa perintah transfer byte
stosb
tidak lagi digunakan, sebagai gantinya,
mov
digunakan untuk menentukan alamat tempat nilai akan ditempatkan. Jika kami menerima bahwa koordinat saat ini adalah X: Y, untuk mereka SI-1, maka:
mov [si-2], al
- merekam warna baru pada titik X-1: Y, di sebelah kiri warna saat ini. 2 dikurangi dari SI karena alasan yang dijelaskan di atas, karena unit tambahan telah ditambahkan ke dalamnyamov [si], al
- rekam warna baru pada titik X + 1: Y, di sebelah kanan warna saat ini. SI sudah memiliki X +1mov [bx+si-1], al
- menulis warna baru ke titik X: Y + 1, di bawah yang sekarang. Sekali lagi gunakan BX untuk Y + 1mov [si-1-1*320], al
- menulis warna baru ke titik X: Y-1, di atas yang sekarang. Kami tidak akan dapat menggunakan BX, karena kami harus mengambil koordinat, arsitektur prosesor tidak memungkinkan kami melakukan ini dalam formulir ini, oleh karena itu konstanta digunakan sesuai dengan rumus reduksi koordinat
Register segmen adalah DS, yang digunakan secara default dengan SI dan BX.
Tidak ada situasi yang diperiksa ketika titik menyentuh tepi layar. Ini tidak dapat menyebabkan kegagalan, karena kami akan selalu berada dalam batas-batas segmen video. Titik tetangga dapat jatuh ke area yang tidak dilaporkan dengan alamat lebih dari 64.000 atau ke jalur yang berdekatan, yang tidak membahayakan kita dan bahkan sedikit membantu, seperti yang akan dilihat dari uraian lebih lanjut.
Sihir yang sama, perhitungan koordinat titik berikutnya NEXT_PIXEL: add si, dx ;03 f2 inc dx ;42 jnz FLAME ;75 e3(-29)
Mari kita kembali sedikit, kami tidak secara khusus menetapkan nilai SI awal di mana saja, dan di DX kami masih memiliki jumlah port input output yang kami gunakan untuk palet. Kami hanya melakukan tiga tindakan sederhana SI = SI + DX, jelas ini akan menetapkan koordinat baru, yang mana? DX = DX + 1 dan jika DX tidak sama dengan 0, maka kembali ke algoritma dasar untuk mendapatkan dan menghitung titik tetangga, yaitu, apakah DX semacam counter?
Kami tahu bahwa kami perlu berkeliling semua titik dan menghitung perubahan kecerahan tetangga mereka. Jika Anda melakukan ini berturut-turut, maka kita mungkin akan mendapatkan gradien statis, mungkin tidak merata, tetapi tidak berubah di sekitar baris kami.
Kita tahu ukuran layar kita dan berapa banyak poin yang perlu kita lewati, tetapi di sini kita hampir mengabaikannya, lebih tepatnya, memilih nilai tutup 65536 daripada 64000 yang tepat. DX benar-benar sebuah penghitung, hanya 65536. Tetapi mengapa nilai awalnya tidak penting dan mengapa kita mengambil Apakah nilai akhir lebih besar dari total titik pada layar?Karena kita berkeliling poin tidak berturut-turut dan tidak semua. Setiap koordinat linier berikutnya lebih besar dari yang sebelumnya dengan nilai DX. Artinya, dalam SI jumlah elemen DX dari perkembangan aritmatika sederhana: 0,1,2,3,4,5,6, ..., 362,363, ..., 65535. Ini sudah memberi kita non-linearitas, jika Anda mulai dengan SI = 0 dan DX = 0, maka di SI kita mendapatkan: 0,1,3,4,6,10,15,15,21, ..., 65341,65703, ..., 2147450880.Tapi itu tidak semua, karena dimensi SI adalah 16 bit, kita tidak bisa mendapatkan nilai lebih besar dari 65535, terjadi overflow dan modulo 65536 sisanya tetap dalam SI. Rumus perhitungan koordinat linear mengambil bentuk SI = (SI + DX) MOD 65536, yang benar-benar memecah urutan kontinu: 0,1,3,4,6,10,15,21, ..., 65341,167.530.894, ...Sekarang kita ingat bahwa SI tidak diinisialisasi dengan cara apa pun, yaitu, waktu berikutnya kita kembali ke siklus ini maka kita akan mulai dari koordinat tempat kami tinggalkan, dan bukan dari 0 atau beberapa yang diberikan. Ini akan menambah kekacauan pada urutan kami - memperpanjang jumlah elemen yang tidak berulang. Kalau tidak, traversal poin akan selalu sama, meskipun non-linear. Efek nyala akan muncul, tetapi tidak begitu jelas. Jika kita membicarakan triknya, maka ini dia.DX, selalu, kecuali untuk penggunaan pertama, secara implisit dimulai pada 0 sebagai hasil dari luapan inc dx
.Dan sedikit lebih banyak kekacauan ditambahkan oleh nilai batas kami, karena untuk SI> = 64000 tidak ada titik yang akan ditarik dan urutan output sedikit bingung. Dan melewatkan semua poin dengan nilai nol mengarah ke efek pengapian di beberapa detik pertama program. Ini karena siklus penuh berakhir lebih cepat, karena sebagian besar poin tidak diproses. Tetapi yang paling penting, karena kecerahan untuk sebagian besar poin hanya akan meningkat, mereka tidak dapat dikaburkan oleh bagian dimmer tetangga - mereka hanya belum ada, dan nilai nol tidak dihitung. Setelah area yang benar-benar hitam hilang, keseimbangan terbentuk, beberapa area akan meningkatkan kecerahan, dan beberapa akan berkurang.Akibatnya, kita tidak bisa lagi berbicara tentang urutan atau gradien apa pun, poin tidak disalurkan, setiap kali dalam urutan baru, termasuk mengulang beberapa kali atau melewatkan sama sekali. Ini mengarah pada pembentukan daerah-daerah dengan kecerahan berbeda yang dicampur satu sama lain, berubah pada setiap iterasi baru.Tapi ini belum semuanya, jika Anda tidak menambahkan titik terang baru, maka pada akhirnya semuanya akan dibayar kembali. Oleh karena itu, setelah DX mencapai nilai maksimumnya, kami kembali untuk menggambar lima garis terang lagi dan lagi menghitung semua poin di layar: in al, 60h ;e4 60 cmp al, 01h ;3c 01 jne LINES ;75 a5(-91)
Tetapi sebelum itu, kita membaca dari port 60h, ini adalah keyboard, kode pindaian dari tombol terakhir ditekan. Untuk ESC sama dengan 1. Jika demikian, tombol ESC ditekan, kami bergerak menuju pintu keluar.Penyelesaian
Perlu diperhatikan bahwa saat memperbarui layar saat ini, yang membutuhkan waktu, Anda tidak dapat keluar dari program, yaitu reaksi terhadap ESC akan tertunda. Jika selama menunggu dan setelah ESC beberapa tombol ditekan, kami akan tetap berada di program, hanya kode pindaian terakhir yang dapat dibaca dari port. Satu hal lagi, kita tidak mengganti atau menggunakan fungsi sistem DOS dan BIOS untuk ini, terlepas dari apa yang kita baca dari port, tombol yang ditekan ditempatkan di buffer melingkar dan mungkin akan dibaca dari sana oleh program berikutnya setelah intro kita selesai, kemungkinan besar file manajer atau command.com
. Ini akan menyebabkan pemrosesan, misalnya, Komandan Volkov di ESC akan menyembunyikan panelnya.Tetap kembali ke mode teks 3: mov ax, 03h ;b8 03 00 int 10h ;cd 10
Diasumsikan bahwa kami berada dalam mode ini sebelum peluncuran program, tetapi dalam kasus umum ini mungkin tidak demikian. Di sini kami memperbarui seluruh AX, karena kami tahu pasti bahwa AH tidak mengandung 0.Sekarang Anda dapat keluar: retn ;c3
Ini adalah perintah keluar dekat dari prosedur yang mengambil nilai kata ditempatkan di sana (dua byte) dari tumpukan dan memuatnya ke penghitung perintah IP. Menurut kondisi awal, kita memiliki angka nol di stack, ini akan membawa kita ke alamat CS: 0, di mana seperti yang kita tahu kode perintah terletak int 20h
- shutdown.Dan 7 byte untuk hak cipta: dd 0h ;00 00 00 00 db 'Mcm' ;4d 63 6d end
Kita dapat mengatakan bahwa masih ada tempat yang akan saya habiskan untuk inisialisasi yang lebih ketat, tetapi karena semuanya bekerja di DOSBox modern, penulis mungkin melakukan semuanya dengan benar.Mari kita bahas sekali lagi:- ,
- 4 , : X+1 Y+2, X+2 Y+1. , . ,
- SI=(SI+DX) MOD 65536, DX , , , SI. 1. 65536 , , . , β
add si, dx
inc dx
, , - ESC ,
. .186 .model tiny .code .startup mov al, 13h int 10h push 0a000h pop es push es pop ds lodsb mov dx, 03c8h out dx, al inc dx mov cl, 040h PALETTE: out dx, al inc ax outsb outsb loop PALETTE LINES: mov ax, 03f3fh mov bx, 0+1*320 mov di, 64+4*320 push di mov cl, 120 LINE1: stosw add di, bx loop LINE1 pop di mov cl, 96 LINE2: mov [bx+di], al stosb add di, bx add di, bx loop LINE2 mov cl, 97 LINE3: mov [bx+di], al stosb sub di, bx sub di, bx loop LINE3 mov di, 17+123*320 push di mov cl, 120 LINE4: stosw sub di, bx loop LINE4 pop di mov cl, 143 rep stosw FLAME: cmp si, 320*200 jae NEXT_PIXEL lodsb or al,al jz NEXT_PIXEL dec ax mov [si-2], al mov [si], al mov [bx+si-1], al mov [si-1-1*320], al NEXT_PIXEL: add si, dx inc dx jnz FLAME in al, 60h cmp al, 01h jne LINES mov ax, 03h int 10h retn dd 0h db 'Mcm' end
Untuk mengkompilasi, Anda harus melakukan: tasm pentagra.asm
dan tlink /t pentagra.obj
.Saya tidak tahu apakah sudah jelas bagi Anda APA dan BAGAIMANA penerapannya, tetapi bagi saya tampaknya pendekatan yang indah dan tidak biasa digunakan untuk menciptakan efek nyala api. Meskipun saya tidak dapat membandingkan, mungkin semua orang melakukannya, dan sekarang Anda dapat melakukan hal yang sama.