Новичок
Профиль
Группа: Участник
Сообщений: 6
Регистрация: 28.5.2010
Репутация: нет Всего: нет
|
Здравствуйте. Пишу програму которая должна реализовывать нитевую многозадачность в дос, а конкретней выводить на экран две змейки каждая управляемая своей нитью. Проблема следующая, при работе почему-то видоизменяются переменные oldInt08h, oldStack, по этому при исполнении мого обработчика прерывания с которого я вызываю старый передаётся управление на неопределённое место, а при установки в конце програмы SS i SP получаем аналогично полный бред. Долго уже не могу разобратся в чём проблема, надеюсь на ваши свежии и конструктивные идеи, зарание благодарю за ответы...(транслирую и линкую TASM.exe, TLINK.exe) код: | Код | ;Нить Ариадны помогает всем заблудившимся в своих проблемах найти выход и вспомнить, ;что Солнце Мудрости никогда не переставало светить stacksg SEGMENT PARA stack 'stack' db 1024 dup(?) stacksg ends ;======================================================================================================== datasg SEGMENT PARA 'data'
threadStruc struc tAx dw ? tBx dw ? tCx dw ? tDx dw ? tSi dw ? tDi dw ? tBp dw ? tSp dw ? tIp dw ? tFlags dw ? threadStruc ends oldInt08h dd ? ;адреса старого обробника oldStack dd ? ;значення сегментногорегістру та вершини стеку на початку програми currentThread db ? ;номер поточної нитки thHeraclit threadStruc <> thDemocrit threadStruc <> thHeraclitStack db 2048 dup(?) thDemocritStack db 2048 dup(?) prevRandNum dd ? ;попереднє випадкове число initRandFlag db 0 ;прапорець ініціалізації ГВЧ rndMultiplier dd 16807 ;множник rndDivisor dd 2147483647 ;дільник datasg ends ;============================================================================================================================== codesg SEGMENT PARA 'code' ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- main Proc Far .386 ;використовуємо 32розрядні регістри і комманди pusha popa assume CS:codesg,DS:datasg,SS:stacksg PUSH DS XOR AX,AX PUSH AX MOV AX,datasg MOV DS,AX MOV word ptr oldStack,SP MOV word ptr oldStack+2,SS CALL initVideo CALL initThreads PUSH BP MOV BP,SP ;помістити всі локальні змінні в стек, для забезпечення повторного входження PUSH 1 ;інкремент до Х на кожному кроці xInc Equ word ptr [BP-2] PUSH 0 ;інкремент до y на кожному кроці yInc Equ word ptr [BP-4] PUSH 128-4 ;відносна адреса голови буфера lineCoords coordsHead Equ word ptr [BP-6] PUSH 0 ;відносна адреса хвоста буфера lineCoords coordsTail Equ word ptr [BP-8] lineCoords Equ word ptr [BP-(64*2)-8]
MOV CX,64 MOV AX,37 ;заповнити буфер координатами (37, 37) SUB SP,64*2 ;di зменшується бо встановили DF=1 MOV DI,SP PUSH DS POP ES ;рядок отримувач ES:DI REP stosw ;записуємо значення АХ в рядок і повторюємо СХ раз
PUSH 0A000h POP ES ;ES - адреса відеопам'яті Life: ;головний цикл CALL displayGreek ;зобразити поточний стан PUSH BX MOV BX,37 ;ймовірність зміни напрямку 2/37 CALL getRandomNumber CALL selectPath POP BX CALL changePosition CALL delay ;затримка MOV AH,01h INT 16h ;якщо не натиснуто жодної клавіші JZ Life ;продовжити основний цикл MOV AH,00h ;а інакше - прочитати клавішу INT 16h MOV AH,00h ;ще раз затримуємося INT 16h
CALL shutdownThreads ;а інакше вимкнути двозадачність CALL initTextMode MOV SP,word ptr oldStack MOV SS,word ptr oldStack+2 RETF main EndP ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- selectPath Proc Near
MOV AX,xInc MOV BX,yInc CMP DX,0 ;якщо це число 0, JE TurnRight ;повернемо направо, DEC DX ;а якщо 1 то наліво JNZ Continue ;якщо інше то рухаємося прямо NEG AX ;поворот наліво XCHG AX,BX ;dY = -dX, dX = dY JMP short Continue TurnRight: NEG BX ;поворот направо XCHG AX,BX ;dY = dX, dX = -dY Continue: MOV xInc,AX ;записати нові значення інкрементів MOV yInc,BX RET selectPath EndP ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- changePosition Proc Near
MOV DI,coordsHead ;DI - адреса голови MOV CX,lineCoords[DI] ;СХ - рядок MOV DX,lineCoords[DI+2] ;DX - стовпець ADD CX,yInc ;добавити інкременти ADD DX,xInc CMP CX,-1 JNE F1 MOV CX,200 F1: CMP CX,201 JNE F2 MOV CX,0 F2: CMP DX,-1 JNE F3 MOV DX,320 F3: CMP DX,321 JNE F4 MOV DX,0 F4: ADD DI,4 ;DI - наступна точка в буфері, AND DI,127 ;якщо DI > 128, DI = DI - 128 MOV coordsHead,DI ;тепер голова знаходиться тут MOV lineCoords[DI],CX ;записати її координати MOV lineCoords[DI+2],DX MOV DI,coordsTail ;змістити вперед на одну позицію хвіст ADD DI,4 AND DI,127 MOV coordsTail ,DI
RET changePosition EndP ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- initVideo Proc Near
MOV AX,13h ;відеорежим 13h INT 10h ;320x200x256 RET initVideo EndP ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- initTextMode Proc Near MOV AX,3 ; відеорежим 3 INT 10h ; 80x24 RET initTextMode EndP ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- initThreads Proc Near PUSHF ;зберігаємо регістри PUSHA PUSH ES PUSH DS MOV AX,3508h ;AH = 35h, AL = номер переривання INT 21h ;визначити адресу обробоника MOV word ptr oldInt08h ,BX ;зберігаємо його зміщення MOV word ptr oldInt08h+2,ES ;та сегмент MOV AX,2508h ;AH = 25h, AL = номер переривання MOV DX,Seg INT08hHandler ;встановлюємо наший обробник MOV DS,DX LEA DX,word ptr offset INT08hHandler INT 21h POP DS POP ES POPA POPF MOV currentThread,1 MOV thHeraclit.tAx,AX ;заповнюємо структури MOV thDemocrit.tAx,AX ;Heraclit,Democrit MOV thHeraclit.tBx,BX ;в яких зберігається вміст MOV thDemocrit.tBx,BX ;всіх регістрів крім сегментних MOV thHeraclit.tCx,CX MOV thDemocrit.tCx,CX MOV thHeraclit.tDx,DX MOV thDemocrit.tDx,DX MOV thHeraclit.tSi,SI MOV thDemocrit.tSi,SI MOV thHeraclit.tDi,DI MOV thDemocrit.tDi,DI MOV thHeraclit.tBp,BP MOV thDemocrit.tBp,BP LEA AX,word ptr offset thHeraclitStack+512 MOV thHeraclit.tSp,AX LEA AX,word ptr offset thDemocritStack+512 MOV thDemocrit.tSp,AX POP AX ;в ах записуємо адресу звідки викликали initThreads(ір) MOV thHeraclit.tIp,AX MOV thDemocrit.tIp,AX MOV thDemocrit.tBx,2 MOV BX,4 MOV thHeraclit.tBx,BX ;встановлюэмо кольори для наших греків PUSHF POP AX ;в ах записуємо прапорці MOV thHeraclit.tFlags,AX MOV thDemocrit.tFlags,AX MOV SP,thHeraclit.tSp ;встановити стек нитки Heraclit MOV AX,datasg MOV SS,AX JMP thHeraclit.tIp ;Heraclit initThreads EndP ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- INT08hHandler Proc Far saveDi dw ? ;змінні для тимчасового зберігання saveSi dw ? ;регістрів PUSHF CALL oldInt08h ;sti MOV saveDi,BP MOV BP,SP PUSH AX PUSH BX PUSHF
MOV AX,word ptr [BP+2] ;прочитати адресу сегменту з якого викликалось переривання MOV BX,CS CMP AX,BX ;порівняти її з CS JNE GoOut ;якщо переривання int08h викликалось не з нитки то нічого не робимо POPF ;а якщо ні то відновлюємо регістри POP BX POP AX MOV BP,saveDi MOV saveDi,DI MOV saveSi,SI CMP currentThread,1 ;визначити з якої нитки на яку потрібно передати керування JE HeraclitToDemocrit ;то перейти на ;якщо з Democrit на Heraclit MOV currentThread,1 ;змінити поточну нитку з Democrit на Heraclit, де-юре MOV SI,offset thHeraclit ;встановити SI і DI MOV DI,offset thDemocrit ;на відповідні структури JMP short ChangeActiveThread HeraclitToDemocrit: ;якщо з Heraclit на Democrit MOV currentThread,2 ;змінити поточну нитку з Heraclit на Democrit MOV SI,offset thDemocrit ;встановити SI і DI MOV DI,offset thHeraclit ;на відповідні структури ChangeActiveThread: MOV AX,[SI].tSi ;записуємо регістри SI i DI(збережені) в структуру за адресою [SI] PUSH saveSi ;зазружаємо регістри SI i DI із структури(нитки) за адресою [DI] POP [DI].tSi MOV saveSi,AX MOV AX,[SI].tDi PUSH saveDi POP [DI].tDi MOV saveDi,AX ;тепер всі основні регістри MOV [DI].tAx,AX MOV AX,[SI].tAx MOV [DI].tBx,BX MOV BX,[SI].tBx MOV [DI].tCx,CX MOV CX,[SI].tCx MOV [DI].tDx,DX MOV DX,[SI].tDx MOV [DI].tBp,BP MOV BP,[SI].tBp PUSHF ;тепер прапорці POP [DI].tFlags PUSH [SI].tFlags POPF ;зберегти адресу повернення POP [DI].tIp ;це переривання тому загружається прапорці,сегмент і ір. Дістаємо ір ADD SP,4 ;цією командою звільняємося від CS і прапорців, стек тепер пустий ;змінити активний стек MOV [DI].tSp,SP MOV SP,[SI].tSp PUSH [SI].tIp ;адреса повернення записуэмо в стек(новий) MOV DI,saveDi MOV SI,saveSi RETN ;закінчуємо і по вертаємося до іншою нитки з її місця завершення GoOut: POPF ;відновити регістри POP BX POP AX MOV BP,saveDi IRET INT08hHandler EndP ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- displayGreek Proc Near
MOV DI,coordsTail ;почати вивід з хвоста ContinueGreekDisplay: CMP DI,coordsHead ;якщо DI рівний адресі голови то JE GreekDisplayed ;закінчуємо вивід CALL displayPoint ;а якщо ні то вивести точку на монітор ADD DI,4 ;встановити DI на наступну точку AND DI,127 JMP short ContinueGreekDisplay GreekDisplayed: CALL displayPoint MOV DI,coordsTail ;вивести точку в хвості PUSH BX MOV BX,0 ;нульовим кольором CALL displayPoint ;тобто cтерти POP BX RET displayGreek EndP ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- displayPoint Proc Near
MOV CX,lineCoords[DI] ;рядок MOV DX,lineCoords[DI+2] ;стовпець PUSH DI LEA ecx,[ecx*4+ecx] ;CX = рядок * 5 SHL CX,6 ;CX = рядок * 5 * 64 = рядок * 320 ADD DX,CX ;DX = рядок * 320 + стовпець = адрес MOV DI,DX MOV AL,BL ;загружаєм колір stosb ;записати байт в відеопам'ять ES(A000h):DI POP DI
RET displayPoint EndP ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ;Xn =(rndMultiplier * Xn-1 ) mod rndDivisor ;в bx максимально можливе число, випадкове число в dx getRandomNumber Proc Near CMP initRandFlag,0 ;якщо ще не викликавася ГВЧ, то треба виконати JE RndInit ;ініціалізацію MOV eax,prevRandNum ;а інакше - помножити попереднє RndNumCalc: MUL rndMultiplier ;на множник DIV rndDivisor ;і поділити на дільник MOV prevRandNum,edx ;залишок від ділення і є новим випадковим числом MOV eax,edx XOR edx,edx ;в edx має бути нуль edx-страша частина eax-молодша DIV ebx ;поділити його на максимально можливе RET ;і повернути залишок в EDX RndInit: PUSH DS PUSH 0040h ;ініціалізація генератора POP DS ;0040h:006Ch - MOV eax,DS:[006CH] ;лічильник перериваннь таймера BIOS POP DS MOV prevRandNum,eax ;саме він буде першим випадковим числом INC initRandFlag JMP RndNumCalc getRandomNumber EndP ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- delay Proc Near MOV CX,3000 S0: PUSH CX MOV CX,3000 S1: LOOP S1 POP CX LOOP S0 RET delay EndP ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- shutdownThreads Proc Near MOV AX,2508h ;відновлюєм старий обробник PUSH DS LDS DX,oldInt08h INT 21h POP DS RET shutdownThreads EndP ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- codesg ends ;============================================================================================================================== End main
|
|