Meninjau semula Komputer Z80: 6 Langkah
Meninjau semula Komputer Z80: 6 Langkah
Anonim
Meninjau semula Komputer Z80
Meninjau semula Komputer Z80
Meninjau semula Komputer Z80
Meninjau semula Komputer Z80

Pada masa lalu, saya telah menulis panduan bagaimana membina komputer berasaskan Z80, dan saya merancang litar sesederhana mungkin sehingga dapat dibina dengan semudah mungkin. Saya menulis program kecil juga menggunakan idea kesederhanaan yang sama. Reka bentuk ini berfungsi dengan baik, tetapi saya tidak begitu senang dengannya. Saya bermula dengan menulis semula program untuknya yang membolehkannya diprogramkan semasa menjalankan. Ini untuk membiarkan saya menguji kepingan kod tanpa harus mendedikasikannya kepada EEPROM, yang pada gilirannya, memerlukan saya memprogram semula EEPROM. Ini bukan idea yang menyeronokkan bagi saya. Kemudian saya mula memikirkan ruang memori. Sekiranya saya ingin menghubungkan antara perkakasan (terutamanya IO), sekeping kod berpotensi melebihi jumlah ruang memori yang tersedia untuk sistem. Ingat, reka bentuk hanya menggunakan bait yang lebih rendah dari bus alamat dan kemudian bait yang lebih rendah dari bait yang tinggi digunakan untuk memilih antara ruang ROM dan RAM. Ini bermakna saya hanya mempunyai 253 bait ruang untuk digunakan. Anda mungkin bertanya mengapa 253 dan bukannya 256. Itu kerana kod baru saya menyuntikkan tiga bait data pada akhir program bertulis (ini akan dibahas kemudian, kerana saya mengubahnya untuk berfungsi pada reka bentuk baru).

n

Saya mengulangkaji skema lama saya untuk melihat apa lagi yang sedang berlaku. Saya menemui cacat kecil dengan litar pemilihan memori, yang akan saya lindungi ketika sampai di sana. Versi yang dipermudahkan: semua permintaan menulis sebenarnya akan dilalui, walaupun selalu dimasukkan ke dalam RAM. Ini mungkin tidak perlu dibimbangkan, tetapi saya mahu melakukannya dengan betul kali ini. Dan dengan itu, saya mula melakar skema baru. Dua gambar yang dilampirkan pada halaman ini adalah sebelum dan selepas litar sebenar. Saya membersihkan banyak kabel spaghetti, tidak lucu.

n

Sekiranya anda mengikuti penyerahan asal saya dan merancang untuk mengikutinya, anda akan membenci saya. Sekiranya anda mula segar, anda beruntung. Cukup ambil bahagian dalam senarai (atau yang setara) dan ikuti.

Bekalan:

LM7805 - Pengatur 5 VoltZ80 - CPU; otak sistemAT28C64B - EEPROM. Penyimpanan data "Kekal" yang digunakan untuk firmware komputerIDT6116SA - SRAM; digunakan untuk menyimpan kod pengguna dan / atau penyimpanan data umumNE555 - Jam sistem74HC374 - Octal D-Latch with / OE; digunakan sebagai input chip74LS273 - Octal D-Latch with / MR; output chipTLC59211 - Cip pemacu LED (digunakan sehingga 74LS273 dapat menggerakkan LED, kerana ia sendiri tidak mampu mengeluarkan arus) MC14572 - Ini adalah cip "Pemandu Line", tetapi saya dapati ia sesuai untuk logik Kawalan Memori. Ia mempunyai 4 penyongsang, dan gerbang NAND dan NOR yang dibina pada74LS32 - Quad OR gateCD4001 - Quad NOR gateCD4040 - 12 Stage Ripple Counter; Pembahagi jam yang digambar, tetapi tidak dilaksanakan (untuk menjalankan sistem pada kelajuan jam yang lebih perlahan) 2 Resistor 10m Ohm - Satu digunakan dalam litar pemasa 555, jadi gunakan nilai apa sahaja yang anda mahukan untuk itu4 1K resistor Ohm - Satu digunakan untuk Litar pemasa 555, jadi gunakan apa sahaja yang anda mahukan. Satu lagi digunakan untuk memandu LED, jadi ubah juga jika anda mahu 8x330 Ohm Resistor Bus8x10K Ohm Resistor Bus11 LED - Tiga digunakan untuk status sistem dan lapan yang lain adalah output. Untuk 8, saya menggunakan paparan grafik bar (HDSP-4836) 4 Kapasitor - Dua digunakan LM7805; 0.22uF dan 0.1uF. Salah satunya adalah untuk pemasa 555, jadi gunakan apa yang anda rasa betul. Yang terakhir adalah untuk tetapan semula power-on; 100uF2 N. O. Push Button - Satu digunakan untuk input, yang lain untuk reset8 SPST DIP Switches - Input data; Saya menggunakan Piano Key styleWire. Banyak dan banyak wayar

n

CATATAN: versi lubang melalui MC14572 sudah usang, tetapi versi SMD masih aktif (walaupun status "bukan untuk reka bentuk baru"), jadi anda mungkin perlu membeli papan litar untuk membolehkan anda menggunakannya. 74LS32 kedua dapat digunakan sebagai pengganti MC14572 (lihat skema "litar pemilihan memori" dari ible sebelumnya)

Langkah 1: Gambaran Keseluruhan Perubahan + Skema

Tinjauan Pantas Perubahan + Skema
Tinjauan Pantas Perubahan + Skema

Cara membaca skema: Anak panah yang ditunjuk ke dalam cip adalah input: Input> -An panah yang ditunjuk dari cip adalah output: Output <-Busses menggunakan garis bukan anak panah: Bus | -

n

Sebilangan besar kerepek telah diambil dengan tepat. Sedikit penurunan telah diambil pada kerepek ini. Sebilangan besar cip juga mempunyai nombor pin dan label di atasnya. Mereka mungkin agak sukar dibaca. Pensil saya semakin kusam.

n

Dari segi sambungan litar, susun atur reka bentuk baru kebanyakannya tidak berubah dari yang asal. Saya menyambungkan nitle rendah alamat byte tinggi ke kenangan dan kemudian menggunakan bit rendah bahagian atas (A12) untuk pemilihan RAM / ROM. Ini bermaksud ruang ROM meningkat dari 0000-00FF hingga 0000-0FFF. Ruang Ram meningkat dari 0100-01FF menjadi 1000-1FFF. Saya juga menukar logik Kawalan Memori untuk reka bentuk yang lebih baik dan menambah dua LED status baru (dan beberapa logik gam). Saya juga telah melukis (tetapi tidak memasang) litar pembahagi jam. Itu untuk melaksanakan dua fungsi. Fungsi yang jelas adalah membahagikan frekuensi jam ke bawah. Fungsi lain adalah untuk tujuan PWM (Pulse Width Modulation), kerana 555 tidak menghasilkan gelombang dengan 50% kitaran tugas. Itu tidak begitu penting dalam litar ini, tetapi jika anda ingin menggunakan jam untuk menggerakkan beberapa LED, anda pasti akan menyedari kesannya (satu (set) LED akan lebih malap daripada yang lain). Selebihnya litar pada dasarnya tidak berubah.

Langkah 2: CPU, Memory dan Memory Control

CPU, Memory dan Memory Control
CPU, Memory dan Memory Control
CPU, Memory dan Memory Control
CPU, Memory dan Memory Control
CPU, Memory dan Memory Control
CPU, Memory dan Memory Control
CPU, Memory dan Memory Control
CPU, Memory dan Memory Control

Ini adalah bahagian di mana pembaca versi sebelumnya membenci saya. Dalam binaan asalnya, saya hanya melemparkan bahagian ke papan di tempat yang kelihatannya tidak menimbulkan masalah dengan membuat kabel. Hasilnya kelihatan seperti seseorang membuang sepiring spageti di atasnya dan seperti "wayar!" Saya mahu membersihkannya sedikit, jadi saya mulakan dengan merobek semuanya kecuali CPU, RAM dan ROM. Saya menarik hampir keseluruhan litar input, litar output, dan logik gam. Ini hampir menyakitkan untuk saya lakukan, tetapi itu perlu. Saya membiarkan semua sambungan data utuh dan bait alamat yang lebih rendah. Saya kemudian menghubungkan empat bit seterusnya dari bus alamat (A8-A11) ke cip ROM. Saya berhati-hati untuk mengelilingi cip kali ini untuk mempermudah proses pengaturcaraan semula. Saya juga melonjak sambungan alamat ke cip RAM.

n

Dengan cara itu, saya terpaksa menggunakan logika kawalan memori. Dalam skema asal, saya telah menghubungkan talian pemproses / MREQ terus ke / CE ke kedua-dua cip memori, kemudian saya memasang kabel / WR ke RAM / KAMI. Kemudian saya mempunyai CPU / RD dan / MREQ secara logik ATAU bersama-sama dan A9. Pada dasarnya, ia disiapkan supaya semua permintaan memori mengaktifkan RAM dan ROM, tetapi A9 digunakan untuk memilih cip / OE mana yang dipilih. Ini baik-baik saja dan semua kerana cip akan tetap tidak aktif sehingga permintaan memori dibuat dan kemudian hanya satu / OE yang akan aktif semasa permintaan membaca. Ini menghalang crosstalk, tetapi memperkenalkan nuansa canggung. Oleh kerana A9 hanya digunakan untuk menentukan cip mana yang mengeluarkan data dan kerana CPU memiliki akses langsung ke pin RAM / WE, semua permintaan penulisan akan dilalui. Ini baik untuk ROM kerana mod penulisannya dihambat dengan mengikat / KAMI terus ke bekalan 5V. RAM, bagaimanapun, akan ditulis tanpa mengira A9. Ini bermaksud percubaan menulis ke lokasi ruang ROM akan menulis ke lokasi yang sama di ruang RAM.

n

Salah satu penyelesaian untuk ini adalah dengan menyusun semula logik kawalan sehingga CPU mempunyai akses langsung ke pin cip / OE dan / WE dan kemudian menggunakan MREQ dan A12 untuk memilih cip / CE mana yang digerakkan. Saya menggunakan idea ini, tetapi bukannya menggunakan empat pintu NOR dan penyongsang seperti reka bentuk asalnya, saya menemui cip kecil yang canggung yang sesuai untuk tugas ini. Saya terpaksa membuat litar yang hanya menggunakan gerbang logik yang terdapat di dalam cip, tetapi cukup mudah. A12 masuk terus ke pintu NAND dan pintu NOR. / MREQ dimasukkan ke gerbang NOR dan pujiannya dimasukkan ke gerbang NAND. Gerbang NAND digunakan untuk menggerakkan / CE untuk RAM dan output NOR terbalik dan digunakan untuk menggerakkan ROM / CE. Ini menjadikan / MREQ harus rendah sebelum salah satu cip dipilih dan kemudian A12 memilih mana yang akan dipilih. Dengan persediaan ini, sekarang permintaan menulis ke ROM tidak akan membuat apa-apa. Ia juga menjimatkan kuasa kerana hanya satu cip yang aktif dan bukan kedua-duanya. Bagi cip logik itu sendiri, kami masih mempunyai dua penyongsang yang tidak digunakan di dalamnya. Seseorang akan terbiasa kemudian, tetapi kita akan sampai di sana apabila sampai di sana.

Langkah 3: LED Status Sistem

LED Status Sistem
LED Status Sistem
LED Status Sistem
LED Status Sistem

Sebelum saya memulakan projek ini, saya cuba bersambung dengan IC tertentu, tetapi saya menghadapi masalah dengannya. Tidak yakin dengan apa yang sedang berlaku, saya menggunakan LED pemasangan panel untuk menyelidiki sekitar (salah satu unit yang mempunyai perintang yang terpasang). Melakukan ini memberi saya idea nostalgia yang masih digunakan hingga kini: LED status yang digunakan untuk menunjukkan sama ada memori sedang dibaca atau ditulis. Ia digunakan bersama dengan input LED yang sudah saya miliki. LED input disambungkan ke penjana isyarat / TUNGGU untuk menunjukkan kepada kami bahawa sistem ini, baik, menunggu input (saya akan sampai di sana, jangan risau). Saya mempertimbangkan untuk menambahkan LED untuk menunjukkan penulisan IO, tetapi saya menganggap bahawa LED output yang akan berubah sudah menjadi petunjuk yang baik. Memikirkannya, saya masih boleh menambahkannya. Walaupun begitu, saya merasa berguna untuk mengetahui sama ada ingatan sedang dibaca atau ditulis. Bagaimanapun, ini berguna untuk penyahpepijatan program. Saya benar-benar memanfaatkannya ketika berusaha untuk menjadikan program saya berfungsi: mengapa menulis ke memori? Itu belum sepatutnya dilakukan!”

n

Untuk mengawal LED ini, saya menggunakan gerbang quad NOR. Saya menggunakan semua pintu pagar. Hanya dua yang digunakan untuk menghasilkan isyarat status, tetapi cip tersebut tidak mempunyai keupayaan kuasa untuk benar-benar menggerakkan LED. Mereka mampu menenggelamkan banyak tenaga, jadi saya menggunakan dua pintu NOR yang lain sebagai penyongsang dan menghubungkan LED seperti itu. Kerana satu LED digunakan untuk menunjukkan pembacaan dan yang lain untuk menulis, dan permintaan membaca dan menulis tidak akan berlaku pada masa yang sama, saya dapat melepaskan hanya menggunakan satu perintang untuk kedua LED. Mengenai isyarat yang saya perlukan untuk menyahkod, itu juga cukup mudah. Saya mahu semua permintaan membaca memori ditunjukkan, jadi gerbang NOR pertama mempunyai / MREQ dan / RD pada inputnya. Status menulis agak sukar, tetapi semudah itu. Saya masih menggunakan / MREQ sebagai satu input, tetapi menggunakan / WR seperti yang lain akan menyebabkan nuansa kecil yang ingin saya hindari. Itu akan menunjukkan SEMUA permintaan menulis. Saya hanya mahukan yang benar-benar dilalui. Jadi bagaimana saya akan melakukannya? Nah, ingat bagaimana saya telah menyediakan sistem sehingga hanya RAM yang dapat ditulis? Saya menggunakan RAM / CE sebagai input lain ke pintu NOR. Ini bermaksud bahawa LED hanya akan menyala ketika RAM dipilih dan permintaan menulis sedang dibuat. Dari segi warna LED, saya memilih oren sebagai penunjuk baca (tetapi saya hanya menjumpai warna kuning) dan merah sebagai penunjuk tulis.

Langkah 4: Input dan Output

Input dan Keluaran
Input dan Keluaran
Input dan Keluaran
Input dan Keluaran
Input dan Keluaran
Input dan Keluaran

Pada langkah sebelumnya, anda mungkin menyedari bahawa saya telah menambahkan beberapa komponen yang lain ke papan. Saya menempah ruang sehingga saya tidak sengaja meletakkan wayar di mana saya mahukan komponen (oleh itu saya perlu mencari lokasi baru untuk komponen tersebut). Anda mungkin juga menyedari saya meninggalkan suis input di tempatnya dan menyambung ke rel elektrik. Saya memutuskan bahawa lokasi asal adalah tempat yang tepat dan memutuskan untuk meletakkan LED output berdekatan (di atas). Di sebelah kanan paparan bar adalah kait input. Di atasnya adalah kait output, dan di sebelah kirinya adalah pemacu LED. Saya mulakan dengan menyambungkan paparan ke pemacu kerana itu adalah yang paling mudah dilakukan. Kemudian saya menyambungkan suis ke bahagian input selak input. Seterusnya saya menghubungkan bahagian output kait output ke pemacu LED. Ini mungkin kelihatan seperti perintah yang tidak masuk akal untuk membuat kabel ini, tetapi ia adalah kerana suatu alasan. Input dari kait output harus dihubungkan ke bus data dan juga output dari kait input. Ideanya adalah untuk menghubungkan output dari kait input ke input dari kait output, yang saya lakukan. Kemudian yang harus saya lakukan ialah membuat kekacauan itu disambungkan ke bas data. Tidak kira di mana sambungan ini berlaku secara fizikal kerana semuanya akan disambungkan secara elektrik. Komputer kini hampir selesai.

Langkah 5: Tetapkan Semula dan Selesaikan Input dan Output

Maaf, tiada gambar untuk langkah ini. Rujuk langkah sebelumnya untuk gambar.

n

Anda mungkin perhatikan pada gambar terakhir dari langkah sebelumnya, saya memasang butang hijau dan cip logik yang lain. Cip itu adalah pintu OR. Dua pintu digunakan untuk menghasilkan isyarat / TUNGGU. Nah, seseorang menghasilkan isyarat dengan OR-ing / IORQ dan / RD dari pemproses. Keluaran dimasukkan ke gerbang kedua, di mana ia akan mendapat OR ke butang tekan lagi. Butang membawa input gerbang tinggi, sehingga menjadikan output tinggi. Output ini dimasukkan ke pin pemproses / TUNGGU. Walaupun tidak ditekan, perintang menahan input rendah. Pada mulanya saya menggunakan perintang 10K, tetapi LS32 sebenarnya mengeluarkan voltan pada input. Perintang tidak menurunkannya cukup rendah dan saya harus menggantinya dengan 1K. Bagaimanapun, idenya adalah bahawa apabila permintaan baca IO dibuat, gerbang ATAU pertama dan kedua memberitahu pemproses untuk menunggu. Setelah anda menetapkan suis input ke apa sahaja yang anda mahukan, anda menekan butang dan ia mengeluarkan CPU dari keadaan menunggu. LED "input" hijau, seperti yang saya sebut pada langkah sebelumnya, disambungkan sehingga apabila pin / TUNGGU rendah, lampu akan menyala.

n

Tetapi kita belum selesai. Flip flop input memerlukan isyarat untuk memberitahukannya kapan input data berlaku dan harus dimasukkan ke CPU. Pin jam ini aktif tinggi. Sebelum ini, kami hanya menyambungkannya ke butang. Ini masih merupakan pilihan yang sah, tetapi kali ini saya memilih untuk meletakkannya pada output yang sama dengan gerbang ATAU kedua. IC ini juga mempunyai pin / OE yang perlu dipacu. Sekiranya ia dipegang tinggi, ia tidak akan memasukkan data ke dalam bas. Sekiranya ditahan rendah, selalu akan menaiki bas. Untuk memperbaikinya, saya hanya menggunakan gerbang ATAU ketiga. Input adalah / IORQ dan / RD dan output terus ke kait / OE.

n

Selak output juga memerlukan pin jam untuk digerakkan. Sekali lagi, ia aktif tinggi. Dalam skema saya, saya menarik pintu OR keempat secara langsung menggerakkan pin menggunakan / IORQ dan / WR. Ini berarti bahawa pin jam akan tetap tinggi hingga permintaan menulis dibuat, maka akan menjadi rendah kemudian tinggi lagi. Ini mungkin akan baik-baik saja kerana bas data akan tetap memiliki data yang valid di atasnya setelah percubaan menulis, tetapi dari sudut kejuruteraan, adalah reka bentuk sampah. Saya tidak menyedari ralat ini sehingga selepas saya mengambil gambar terakhir, tetapi saya mematikan sambungan itu dan kemudian memasukkan output gerbang OR ke salah satu penyongsang yang tidak digunakan dari logik kawalan memori, kemudian menghubungkan outputnya ke pin jam. Saya juga membetulkan skema dan menemui ralat lain yang telah saya buat. Saya membetulkannya juga.

n

Dengan semua yang akhirnya selesai, saya mempunyai sedikit kerja yang perlu dilakukan: litar semula. Saya menambah butang ke papan dan menggunakan perintang 10K untuk menahan satu sisi tinggi. Bahagian lain terus menuju ke tanah. Sisi yang dipegang tinggi adalah output / RESET, yang masuk ke setiap cip dengan pin / RESET (CPU dan kait output). Untuk mencapai tetapan semula power-on, saya menambahkan kapasitor ke output / RESET. Ideanya adalah bahawa perintang nilai besar akan menyebabkan kapasitor yang agak besar mengecas perlahan dan menahan pin / RESET rendah untuk sejumlah kitaran jam (CPU memerlukan empat kitaran jam). Anda mungkin sudah dapat meneka sisi negatif litar ini. Ia sama negatifnya dengan versi sebelumnya kerana litarnya sama. Apabila butang ditekan, kapasitor pada dasarnya dipendekkan melalui butang. Ini buruk untuk kedua-dua topi dan butang, jadi jika anda ingin menjadikan binaan anda sedikit lebih kekal, anda mungkin ingin merancang semula. Saya memikirkan pemasa 555 lain yang disediakan dalam mod monostable. Tetapi dengan itu, litar komputer kini sudah selesai. Yay. Sekarang perlu diprogramkan.

Langkah 6: Pengaturcaraan

Memprogram perkara ini adalah mimpi buruk. Saya membina pengaturcara Arduino EEPROM. Tidak berjaya. Saya membina yang lain berdasarkan reka bentuk dan pengekodan orang lain. Masih tidak berjaya. Saya kembali ke kaedah yang dicuba-dan-benar untuk menetapkan alamat dan bait data secara manual dengan tangan. Entah bagaimana, saya mengacaukannya. Saya mencuba lagi dan masih salah. Saya kembali sekali lagi dan mendapati ia tidak lagi menggunakan satu bait, jadi saya membetulkannya dan akhirnya berjaya, terima kasih Tuhan.

n

Mengenai program yang sebenarnya, nampaknya sangat kompleks dan sukar diikuti, tetapi tidak. Cukup mudah, sebenarnya. Separuh daripadanya adalah menyalin nombor. Separuh lagi dikongsi antara matematik 16-bit, lompatan bersyarat, dan lebih banyak lagi menyalin nombor. Oleh itu, izinkan saya melaluinya dan memberitahu anda bagaimana ia berfungsi.

n

Permulaan hanya menetapkan beberapa nilai daftar untuk digunakan oleh program. Gelung program agak lebih rumit, tetapi tidak banyak. Pertama, ia menerima input ke daftar A pada port 00. Kemudian daftar E ditulis ke memori. Pada dua gelung pertama, daftar E mengandungi data sampah, jadi kami berusaha menulisnya ke ruang byte dua byte terakhir kerana sebenarnya tidak akan ditulis; penunjuk alamat (IY) kemudian dinaikkan. Nilai yang disimpan dalam D kemudian dipindahkan ke E untuk ditulis seterusnya. A kemudian dimuat ke D dan L dan E disalin ke H. HL adalah tempat perbandingan nilai berlaku melalui pengurangan dan memeriksa ZF (bendera sifar). Nilai pertama dibandingkan dengan disimpan di register B dan C. B dan C diperlakukan sebagai register 16-bit tunggal, BC. Sekiranya nilainya sama, maka program langsung menuju ke ruang RAM, di mana kod pengguna dianggap berada. Sekiranya kod dalam BC tidak sepadan, maka HL dimuat semula dengan nilai awal dari D dan E dan dibandingkan lagi dengan nilai dalam SP dengan cara yang sama dibandingkan dengan BC. Sekiranya sesuai, hasilnya sama, tetapi tiga bait tambahan ditulis ke memori. Byte adalah kod yang menyebabkan CPU melompat kembali ke awal programnya (tetapan semula perisian). Sekiranya perbandingan kedua tidak sepadan, program ini bergerak ke tempat yang mendapat nilai dari pengguna.

n

LD SP, EDBFH; kod exe (tambah lompat)

n

LD IY, FFEH; penunjuk memori awal untuk penyimpanan kod

n

LD BC, EDC3H; kod exe (tiada gelung)

n

gelung; arahan assembler jadi kita tidak perlu tahu di mana ingatan bahagian ini berada

n

DALAM A, (00H); dapatkan data program

n

LD (IY + 00H), E; E mengandungi kod yang akan disimpan

n

INC IY; pindah ke lokasi memori seterusnya

n

LD E, D; ld D ke dalam E

n

LD D, A; l A ke D

n

LD H, E; ld E ke H

n

LD L, D; ld D menjadi L

n

ATAU A; menetapkan semula bendera bawa

n

SBC HL, BC; mengembalikan 0 jika kod exe 2 dimasukkan

n

JP Z, 1000H; jika ya, lompat ke dan laksanakan program

n

LD H, E; jika tidak, muatkan semula ini ke nilai yang betul

n

LD L, D

n

ATAU A; tolak pertama mungkin telah menetapkan bendera bawa. Bersihkannya

n

SBC HL, SP; mengembalikan 0 jika kod exe 1 dimasukkan

n

JP NZ, gelung; jika tidak, ulang proses (bermula dengan mendapatkan nilai)

n

LD (IY + 00H), C3H; jika tidak, masukkan kod lompat pada akhir program pengguna

n

LD (IY + 01H), 00H; melompat pada dasarnya bertindak sebagai tetapan semula perisian

n

LD (IY + 02H), 00H; ia adalah tetapan semula sepenuhnya sekiranya daftar diubah suai

n

JP 1000H; lompat ke dan laksanakan program pengguna

Disyorkan: