שאלה מוזרה

dor_ian

New member
שאלה מוזרה

מיועד לאלה היותר וותיקים בתחום. כתבתי תוכנית פשוטה ביותר שמציירת כוכבית '*' על כל המסך (במצב טקסט) לאחר שהמסך מתמלא היא מציירת מההתחלה תו נקודה '.' עד שמתמלא וחוזר חלילה. ולמטה יש לי מספר שהוא COUNTER כתבתי אותה בכוונה בצורה הכי פשוטה וישירה. את התוכנית כתבתי ב BORLAND C++ גירסה 3.11 לDOS. וגם כתבתי אותה ב QUICK-BASIC גירסה 4.5 העתיקה שיכולה לקמפל לEXE. וגם כתבתי TASM 16BIT יופי! עכשיו פישפשתי אצלי בדיסקטים ומצאתי DOS גירסה 3.11 וגם 6.22 בשני הגירסאות עשיתי מבחן לשלושת התוכניות. מה לדעתכם היו התוצאות? ככה: התוכנית ב C עבדה לאט! ניתן לראות את המסך מתמלא. התוכנית ב אסמבלי עבדה קצת יותר מהר (עדיין בושה) והתוכנית בBASIC עבדה פי 20 יותר מהר לפחות!!! הCOUNTER טס! רואים מסך מלא שמהבהב בין '*' ל '.' !!!! איך זה יתכן???? מה זה הקונספירציה הזאת? הנה עוד משהו מוזר: בדקתי את התוכנית באסמבלי באמולטור DOSBOX תחת XP ושם היה עבדה הרבה הרבה הרבה יותר מהר! מוזר. יש סיכוי שהכרטיס מסך שלי (GFORCE 2MX ) לא תומך כמו שצריך המצב טקסט? סתם השערה פרועה. מרוב שהשקיעו ב3D שכחו מצב טקסט
 

dor_ian

New member
המשך

כשאני נהיה אובססבי כלום לא יעזור
הסקרנות גרמה לי להחליף כ.מסך ובאמת יש שיפור פלאים. הכל רץ הרבה יותר מהר. כלומר יש השפעה גם לכ.מסך. אבל...עדיין הBASIC מנצח. להלן זמנים מקורבים: BASIC ממלא 221 מסכים בשניה. C ממלא 39 מסכים בשניה ASM ממלא 19 מסכים בשניה. לא מובן איך זה. BASIC מהיר פי יותר מ11 מASM. הנה התוכנית בASM. היות ואני חדש בזה אולי אני עושה משהו מחוסר הבנה: (החלק האחרון משתמש במחסנית כדי להציג את הCOUNTER אני לא מאמין שזה יוריד את הביצועים עד כדי כך (בדקתי))
data segment counter dw 0 tenw dw 10 data ends sseg segment stack 'stack' db 100h dup() sseg ends code segment assume cs:code,ds:data start: mov ax,data mov ds,ax ddd: ;place cursor mov ah,2 mov bh,0 mov dl,0 ; X cord mov dh,0 ; y cord int 10h ;;draw mov cx,80*23 loop1: mov dl , '*' int 21h loop loop1 ;place cursor in 0,0 mov ah,2 mov bh,0 mov dl,0 ; X cord mov dh,0 ; y cord int 10h ;;draw lower border ;;put 13 in cx mov cx,80*23 loop2: mov dl ,'.' int 21h loop loop2 ;incrment counter mov ax , [counter] inc ax mov [counter] , ax ;display counter mov cx,0 mov ax,counter shoov: mov dx,0 div tenw push dx inc cx cmp ax,0 jne shoov mov ah,2 a: pop dx add dl,'0' int 21h loop a jmp ddd ;end program mov ah,4ch int 21h code ends end start​
 

voguemaster

New member
את כל מה שאייל הציע

תיישם, אבל צריך גם באסמבלי לעשות אותו הדבר. הבעיה היא שאתה בכל איטרציה של הלולאה קורא ל-interrupt 21 של DOS לציור תו אחד. זה לא יעיל בטירוף! זה אפילו גרוע יותר מאשר לעבוד ישירות עם שגרות interrupt של ה-BIOS. אתה יכול לעשות משהו כזה:
mov cx, 40*23 ; 80*23 / 2 = number of WORDs to fill mov ax, 02A2Ah ; two asterisks mov es, 0A000h xor di,di cld repnz stosw es:[di]​
כמובן שאפשר לעשות את זה בעוד צורות. אפשר עם BUFFER אחר בזיכרון והעברה מהירה עם repnz movs ואפשר אפילו עם לולאה רגילה. רק לא לקרוא בכל איטרציה לאינטרפט!!!!
 

dor_ian

New member
תארתי לעצמי שאני לא יודע מספיק

אני גם לא מכיר את חלק מהפקודות שרשמת. אם בא לך תסביר למה לא יעיל לקרוא לINT 21. איזה פתרון אחר יש? בכל מקרה ניסית את מה שאייל אמר. הQBASIC לא שופך הישר. ואם גורמים לו לעשות זאת מגיעים למהירות מטורפת גם בהרצה ללא קימפול!! מעניין.
 

voguemaster

New member
לפני שאני הולך לישון

ספציפית לגבי INT 21 - זו קריאה ל"פונקציה". קורים כמה וכמה דברים מאחורי הקלעים כשאתה קורא ל-interrupt כלשהו. זה לא עניין של למצוא את המיקום של השיגרה בזיכרון ולקפוץ אליה, זה טיפה יותר מסובך מזה כי interrupts הם חיות מורכבות. וגם אם זה היה קטע של קפיצה בלבד לשיגרה החדשה, שים לב שזו שיגרה שמשתמש בתורה בשיגרת BIOS, שהיא עצמה די איטית. תחשוב שאתה משלם את המחיר על כל הקוד המיותר בכל פעם שאתה רוצה לכתוב תו אחד למסך. במקום זה, תשלם אותו פעם אחת (אם בכלל) ותכתוב הכל במכה אחת. ובדוגמא שהצגתי אתה בכלל לא עובר דרך ה-BIOS, אתה רק מניח שהמיקום בזיכרון של המסך (הרגיל) הוא A000, אבל גם את זה אפשר לאחזר. בוא ותשמע משהו: גם הלולאה הפשוטה ביותר תעבוד מהר יותר מ-BASIC וכד'. הנה משהו פשוט יותר ממה שכתבתי קודם:
mov cx, 80*23 mov al, 02Ah mov es,0A000h xor di,di WriteByte: mov es:[di],al inc di loop WriteByte ; or if you want, you can use: dec cx, jnz WriteByte​
גם הלולאה הזו תעבוד מהר מאוד. ד"א, תמיד עדיף dec בשילוב עם jnz (או jmp אחר) על פני loop. תשכח ש-loop קיימת, עשה לעצמך טובה. בכל מקרה, הקוד הראשון שנתתי אמור להיות הדבר הכי מהיר שיש (אולי מלבד לכתוב כל פעם DWORD במקום WORD, שאמור תיאורטית לעבוד מהר יותר במחשבי 32 ביט).
 

voguemaster

New member
תיקון אחד:

אסור לטעון ערך IMMEDIATE ישירות לתוך segment register ב-real mode DOS. חייבים לעבור דרך אוגר אחר, כך: mov dx, 0a000h mov es,dx אבל מעבר לכך הדוגמאות תקפות.
 

dor_ian

New member
תודה רבה!

בסוף תוגמלתי יפה על מאמצי
השכלתי בהחלט. תודה גם לאינגלמו על אף שהקוד שלו 32ביט שאני כרגע משאיר לימים אחרים.
 
אם כבר נכנסים

באמ-אמא של הקוד, קודם כל עדיף REP MOVSD מאשר REP MOVSW. אבל אפילו זה כבר לא נכון כיום. קראתי מתישהו, שפקודת REP כבר לא יותר טובה מ-LOOP שמעתיק כל פעם DWORD, אלא להיפך. משהו מהיר יותר מ-REP יהיה כתוב משהו בסגנון הזה:
mov esi,0xa000h mov ecx, 80*23/4 copy_loop: mov ecx[esi] dec ecx jnz copy_loop​
 
נו...

אז יש כמה שגיאות קומפילציה, אבל הבנת את מהות האלגוריתם, לא? גם כתבתי 0xa000 במקום 0a000h.
 

אלדד28

New member
אז זהו, שלא בטוח שהבנתי ../images/Emo6.gif

אתה מתכוון למשהו כמו '*',[mov ecx[esi? (זה פסאודו קוד
) בכל אופן, יש לינק מצוין שווגי הביא פעם על אופטימיזציות לפנטיום... הוא בקישורי הפורום.
 
אפשר לחסוך 4 פקודות!!! ../images/Emo6.gif

במקום:
mov esi, 0a000h mov ecx, 80*23 copy_loop: mov eax, ecx add eax, esi mov [eax], AST sub ecx,4 jnz copy_loop​
אפשר לכתוב:
mov ecx, 80*23 copy_loop: mov 0a000h[ecx] sub ecx,4 jnz copy_loop​
אחחח איפה ימי ה-SET_PIXEL היפים באחד ושלוש חמישיות CYCLES, אה?
 

voguemaster

New member
אמממ

נכון שעדיף DWORD באופן עקרוני אבל נגביל את עצמנו ל-16 ביט במקרה הזה
פעם זה היה יותר קריטי, היום פחות. ובנוגע לשאלה מה יותר טוב, rep, loop או jnz: 1. LOOP תמיד הכי איטית, בכל מצב. 2. REP תהיה יותר מהירה בד"כ אלא אם כן יש בעיה ב-instruction pairing או שיש STALLים אחרים בגישה לנתונים/רגיסטרים. זה כמעט לא קורה...
 
שאלה גסה.

האם בדקת שהתכנית ב-BASIC עובדת נכון? תאורטית, אולי התפלח באג מקרי, והתכנית מדלגת על משהו? ניסית להאיט בכוונה את התהליך, או לבדוק בדרך אחרת כלשהי את תקינות תכנית ה-BASIC?
 

dor_ian

New member
מה???

באג מיקרי? בבייסיק? כתבתי בבייסיק מספר תוכניות. אפילו כשמריצים בלי לקמפל באינטרפרטר זה רץ מהר מהתוכנית שלי בASM. והתוכנית פשוטה מאוד.
 
למעלה