Great2! – código fonte e técnicas utilizadas
Em 1994 eu era o SysOp da WarmBoot BBS e cursava o último ano de Ciência da Computação. Durante muito tempo, os BBS reinaram absolutos conectando pessoas em todo o mundo, antes de serem eliminados da face da terra com a chegada da Internet para o público geral.
Além da troca de mensagens, os BBS serviam também como repositórios de arquivos, em geral sharewares, freewares, imagens, etc. Usuários discavam para o BBS para baixar/subir arquivos, trocar mensagens, etc. Havia uma tradição em que cada BBS adicionava um pequeno arquivo texto aos arquivos compactados (.arj, .zip, .lzh, etc) do acervo, indicando que ele foi baixado daquele determinado BBS, além de informações básicas como: o nome e telefone do BBS, parâmetros para conexão via modem, etc. Enfim, uma propaganda que ajudava a divulgar a existência do BBS para outras pessoas e, com sorte, captar novos usuários pagantes. Esses arquivos precisavam ser minúsculos, afinal, estamos falando de conexões discadas que variavam entre 2.400bps e 56Kbps, dependendo do seu modem e da qualidade da linha telefônica. Pra se ter ideia, um arquivo de 100KB demoraria entre 5 e 8 minutos para ser baixado em uma conexão de 2.400bps, dependendo das condições da linha!
O WarmBoot BBS era referência na distribuição de Demos de multimidia. Eramos distribuidores oficiais da Future Crew e da Twilight Zone. Sempre achei fantástico o que os demogroups faziam usando assembly, em uma época em que o clock da maioria dos PCs não passava de 40Mhz.
Decidi então fazer algo diferente da maioria dos BBS: ao invés de incluir um arquivo texto chato de “propaganda”, criei algo mais divertido e animado – uma “intro” em modo texto, o great2!. Como pode ser visto no vídeo abaixo (gravado em uma janela emulada do DOS), ao ser executado, o great2! fazia a “propaganda do BBS” quicar na tela, enquanto rolava uma música de fundo juntamente com um VU bargraph animado (no vídeo, a rolagem pode não parecer tão suave, afinal, foi gravado em um emulador de DOS que simulava inclusive a taxa de refresh de um monitor CRT).
Lembre-se, o Windows 95 nem mesmo tinha sido lançado e o Windows 3.1 era meramente uma interface gráfica tosca que rodava em cima do MS-DOS. A maioria dos programas ainda eram executados em modo texto (80 colunas x 25 linhas), nada de mouse, janelas etc! Aqueles mais abonados tinham placas de som em seus computadores (SoundBlaster era o suprassumo da época), monitores CRT (Syncmaster 3 era o que tinha de melhor) e placas “Trident” Super VGA com no máximo 1MB de RAM.
A parte mais interessante do great2! era com certeza o efeito da tela quicando. O modo texto, por padrão, tem um número limitado de colunas e linhas para se trabalhar (80 x 25). Normalmente, rolar um texto significava deslocar as linhas para cima ou para baixo, o que obviamente não causava uma rolagem “suave”, já que cada linha tinha 16 pixels de altura, ou seja, cada deslocamento significava mover o texto na tela no mínimo 16 pixels para cima ou para baixo. O “tchã” do great2! era usar recursos de programação gráfica VGA juntamente com o sincronismo do tempo de retrace vertical dos monitores para “pintar a tela” começando no momento certo e no “pixel” certo, causando um efeito de rolagem suave. O sincronismo do retrace era essencial – sem ele é como se partes da imagem parecessem “quebradas”.
Vou deixar a I.A. explicar o conceito, pois creio que ela fará isso de forma mais didática:
O “motor de rolagem” do programa não redesenha a tela: ele desenha o conteúdo uma vez na memória de vídeo VGA em modo texto e depois altera os registradores da [placa gráfica] VGA para mudar a linha inicial de exibição. Como a VGA permite deslocar a imagem por scanlines dentro das células de texto (25 linhas x 16 pixels = 400 scanlines no total), o movimento fica suave, mesmo sem usarmos um modo gráfico.
A sincronização é feita esperando o vertical retrace do monitor CRT, isto é, o breve intervalo entre um quadro e outro em que o feixe de raios catódicos volta ao topo da tela. Ao atualizar os registradores nesse momento, o código evita flicker e tearing, algo equivalente a usar VSync em gráficos modernos.
Em resumo: o efeito funciona como uma “câmera” que se move sobre uma tela já pronta, usando recursos do hardware VGA e sincronizando as mudanças com o refresh do monitor.
Também pedi pra ela fazer uma simulação animada, pra ficar ainda mais fácil de entender o que acontece:
Simulador de Hardware: Scroll Suave VGA (Modo Texto)
Lado Direito: A VRAM (Memória) está fixa. O retângulo vermelho representa os Registradores da VGA mudando a cada ciclo de Retrace.
É claro que o impacto do great2! não ficava apenas no movimento suave, mas também no tamanho minúsculo do executável (5.7Kb) e na música de fundo. Para tocar a música, usei o HSC Player, disponibilizado publicamente por Hannes Seifert, da NEO Productions (uma desenvolvedora de jogos da Austrália). A música também não é de minha autoria! Peguei pronta na internet e confesso que não me lembro quem era o autor. O “anúncio”, ou seja, o texto colorido foi feito usando o The Draw, um editor de telas ANSI amplamente utilizado pelos SysOps para construir as telas de boas vindas e menus dos BBS. Telas ANSI eram a única forma de adicionar texto colorido na interface de um BBS. Lembre-se, tudo era texto/bytes sendo transmitido pela linha telefônica da forma mais crua possível. Usando ANSI conseguiamos adicionar cores aos caracteres. Em conexões lentas, dava pra ver as telas sendo desenhadas caractere por caractere – haja paciência!
Abaixo está o código fonte do great2!, incluindo meus comentários escritos em um inglês vergonhoso e cheio de erros (tinha 19 anos e havia aprendido inglês por conta própria, lendo revistas e livros americanos sobre programação). Muitas vezes recebi mensagens de pessoas que desacreditavam que um executável tão pequeno podia fazer “tanta coisa”.
O código mostra várias técnicas típicas da programação Assembly/DOS que hoje parecem bem “artesanais”, mas eram normais na época: acesso direto à memória de vídeo (B800h) em vez de APIs gráficas, escrita direta em portas de hardware da VGA (03DAh, 03D4h, 03C8h, 03C9h), sincronização manual com o retrace vertical do monitor, rotação de palheta de cores, uso de interrupções BIOS/DOS (int 10h, int 16h, int 21h), controle explícito de registradores, segmentos e pilha, além de CLI/STI para bloquear interrupções durante trechos críticos.
Também chama atenção o uso de rotinas externas para música AdLib, com callback de IRQ para desenhar VUs na tela enquanto a música toca, e a tela inicial armazenada de forma comprimida e descompactada diretamente na memória de vídeo. Para um programador moderno, é curioso porque quase tudo que hoje seria delegado ao sistema operacional, driver gráfico, engine ou biblioteca era feito “na unha”, conversando diretamente com o hardware e sincronizando o tempo visual quase quadro a quadro.
A AdLib era uma placa de som para PCs, famosa no fim dos anos 1980/início dos 1990, que gerava música por síntese FM usando o chip Yamaha OPL2. As Sound Blasters e demais placas de som que vieram depois mantiveram a compatibilidade básica com a AdLib.
As barras do VU, que simulavam um spectrum analyzer, representavam na verdade o nível de sinal de cada instrumento que estava tocando naquele momento, e dava um toque especial e um certo dinamismo na apresentação.
O great2! era compilado com o tasm – o Turbo Assembler, assembler da Borland usado principalmente em DOS para escrever e compilar programas em linguagem Assembly para processadores x86, como o 8086/286/386.
;*==========================================================================*
;| WarmBoot BBS - WarmBoot Group Productions ( 07/94 ) - WarmBooter Typing |
;*==========================================================================*
;| |
;| This is the second BBS advertisement I write and I hope this one is |
;| better than the previous (great!.exe) :)) I decided to release this code |
;| to PD... you will find some VGA interesting tricks here, nothing very |
;| impressive but I think people with less experience with VGA programming |
;| (like me :-) will enjoy it ! |
;| |
;| The greetings go to : |
;| |
;| Dart Vader / Iguana / Future Crew / Renaissance / Twilight Zone |
;| Triton / CodeBlasters / and all the other groups out there & IRC users ! |
;| |
;| ->> Thanks to NEO Productions for the HSC-Player ! Great Work ! <<- |
;| |
;| Personal greetings go to : |
;| |
;| Dart Vader / Luiz Benfica / Logi Ragnarsson / Kevin Sartorelli |
;| Trug / JCAB / The people at Ciagri / Chico, Pedro, Pupo, |
;| Fabio, Waltinho, Marcelo, Tamora and all my friends at EEP ! |
;| |
;| Also I want to thanks the following musicians for make the best music |
;| in the world : |
;| |
;| Iron Maiden / Mettalica / Sepultura / AC/DC / Steve Vai |
;| Joe Satriani / Pantera / Vinnie Moore / Joe Tafola / Steve R. Voughan |
;| Ozzy Osbourne / Sex Pistols / Garotos Podres / Van Halen / King Diamond |
;| Blues Etilicos |
;| |
;+--------------------------------------------------------------------------+
;| Don't be a LAMER !!!!! Don't use this code to make your own BBS |
;| advertisement or I 'll have to kill you !!! :) |
;*--------------------------------------------------------------------------*
; This will run on a 286 or better with or without a VGA and ADLIB, of
; course, it will looks better if you have both :)
; I tried to optimize the code for both speed and size, but as I don't know
; so much about optimization, I think I don't optimized it anyway... :)
Extrn hscplayer:far ; Define some externals for the HSC-PLAYER
Extrn hsc_equalizer:byte
pilha segment word stack 'stack' ; Start Stack segment
db 256 dup(?)
pilha ends ; End Stack Segment
; End of idiot comments :)
codigo segment word public 'code'
assume cs:codigo,ds:codigo,ss:pilha
start:
push cs ; Make DS = CS
pop ds ;
Mov ax,0003h ; Change to mode 03, No no, it's not $13 !
Int 10h ; I want TEXT MODE :)
clc ; Clear Carry
mov ah,04h ; Function #04 of HSCPLayer (check for sound card)
call hscplayer ; Call it
jc no_adlib ; Ops... there is no Adlib here...
mov [sound],01h ; Yes, Sound Ok, so move 1 to sound...
xor ax,ax ; Function 0 of HSCPLayer
mov bx,ax ; BX = 0
push cs ; Make ES = CS
pop es
lea si,musica ; Get the Music Offset
call hscplayer ; And call the player
mov ah,06 ; Link User IRQ (HscPlay function)
lea dx,VUs ; Put Proc Offset here
mov bl,1 ; 1 to Link
call hscplayer ; Call the player
no_adlib:
Mov Vga,00 ; I think there is no VGA here...
Mov ax,1A00h ; Check for VGA
Int 10h
cmp bl,7 ; Display active code
je IsVGA ; It will work...
cmp bl,8 ; Display active code
jne No_VGA ; No VGA :-(
IsVGA: Mov Vga,1 ; Well, as we have VGA, put 1 on it...
mov [linha],400 ; Put the screen Up... we have to change it...
Call ScreenToLine ; DO it !
No_VGA:
lea si,tela ; SI = OFFSET TELA (Intro Screen Crunched)
xor di,di ; DI = 0000h
mov ax,0b800h ; AX = Screen Segment
mov es,ax ; ES = AX
mov cx,tela_length ; Length of the screen DATA
Call Uncrunch ; Call the uncrunch routine
cmp Vga,0 ; Is there a VGA here ?
jne Skip1 ; NO !!!! arrrrrrrgggh :-((((
jmp NotVGA
skip1:
mov dx,03c8h ; Change color 5 to bright WHITE
mov al,05
out dx,al
inc dl
mov al,63
out dx,al
out dx,al
out dx,al
Mov ah,02h ; BIOS function 02
Xor bh,bh ; Page number 00
Mov dh,25*2 ; Put the cursor on row 50 to hide it
int 10h ; Yes, I'm using BIOS to do this...
;====================================================================
; The Bouncing Screen is here !
;====================================================================
Volta3:
Mov speed,01 ; Start with Speed 1
Desce: Call ScreenToLine ; Put Screen in the right start line
mov ax,[speed] ; Get the speed
mov bl,03 ; and divide it
div bl ; by 3
xor ah,ah ; zero the remainder
sub [linha],ax ; sub it from linha
inc [speed] ; up the speed by one
cmp [linha],00 ; Is the line 0 ??
jg Desce ; No, it's biggest, so LOOP again...
Mov ah,01h ; We must check if
Int 16h ; the user pressed a key
Jz Sobe ; No, so continue
Mov ah,00 ; Yes, so extract it from the
Int 16h ; Keyb Buffer
Jmp Go_Out ; And GO OUT !!!!
Sobe: mov ax,[speed] ; Get the speed again
shr ax,02 ; Hummm... divide it by 4
add [linha],ax ; Add the result to line
dec [speed] ; Down the speed by one
Call ScreenToLine ; Put the screen in the right line start
cmp [speed],6 ; Is the speed greater than 6
jg Sobe ; Yes, so, LOOP again...
dec [kicks] ; Decrement the kicks number
jnz volta3 ; Is it 0 ? No, so, LOOP ALL again...
Go_Out:
Mov [linha],00 ; Put the screen on the right place
Call ScreenToLine ; Do it !
;====================================================================
; Here begins the MAIN LOOP...
;====================================================================
Loop_E:
mov cx,80 ; The screen has 80 columns
mov ah,14 ; We want yellow text
mov dx,[textcol] ; Move the start char pos to dx
bla4:
mov si,dx ; SI want to be the start char pos
xor di,di ; DI has to start in 0 (first column)
cli ; Disable Interrupts
Call retrace ; Again call the Wait for Retrace
bla2: mov al,texto [si] ; Get char
mov es:word ptr [di],ax ; ES points to B800 (Screen)
add di,2 ; Next column on the screen
inc si ; Point to next char
cmp di,2*80 ; End of line ?
jb bla2 ; No, loop...
inc dx ; Inc Text Column start
cmp dx,80*8 ; End of text ?
jb bla3 ; No, so loop...
xor dx,dx ; Yes, so, Start Column = 0
bla3:
sti ; Enable interrupts
loop bla4 ; Loop if not finished the 80 charac.
mov textcol,dx ; Save the last char pos
;====================================================================
; Preparing to change the PAL...
;====================================================================
mov cl,0ffh ; Color fade counter
mov dh,03h ; See below
ChangePal:
Call retrace ; Wait for retrace
mov dl,0c8h ; DX = 03c8h = Preparing to change the pallete
mov ax,05 ; Color 05
out dx,al ;
inc dl ; DX = 03c9h = Now we will change the r,g,b
mov al,cl ; Get CL and div it
shr ax,2 ; by 4 to shade the color
xor al,[temp] ; down to 0 OR up to 64
mov r,al ; Put the new color in r
mov g,al ; Put the new color in g
mov b,al ; Put the new color in b
mov al,color ; Color counter (starts with 2)
shr al,1 ; Div it by 2
dec al ; And dec it, so, the first time it will be = 0
mov si,ax ; Put it in SI
mov [r+si],63 ; And write the fixed color on the right place
mov al,r
out dx,al ; Change RED
mov al,g
out dx,al ; Change GREEN
mov al,b
out dx,al ; Change BLUE
loop ChangePal
dec cx ; DEC the color fade counter
inc color ; increment the color counter
cmp color,8 ; compare it with 8 = r,g,b were done
jb bla1 ; No, don't change
mov color,2 ; Starting with 2 again...
bla1:
xor [temp],0ffh
Mov ah,01h ; Again this shit for key check...
Int 16h
Jnz Skip ; Blah, key pressed... jump to end...
Jmp Loop_E ; No key pressed... Loop back !
skip: Xor ax,ax ; Extract key from keyb buffer
Int 16h
mov linha,00 ; Make linha = 0
gezzz: Call ScreenToLine ; Put the screen in line linha
add linha,5 ; Up the screen 5 lines
cmp linha,400 ; Compare with 400 lines
jb gezzz ; If is below loop...
cmp [sound],00 ; Is there AdLib here ?
je no_music ; No, so skip the next part...
mov ah,06 ; Link User IRQ (HscPlay function)
lea dx,Vus ; Put Proc Offset here
xor bl,bl ; 0 to UnLink
call hscplayer ; Call the player
mov ah,03 ; FadeOut the Music...
Call hscplayer ; Do it !
no_music:
xor di,di ; Clear the screen
mov ax,0015 ; We want a blank screen
mov cx,2000 ; 2*2000 bytes
rep stosw ; Repeat it 2000 times
mov ah,09h ; Print the end string
lea dx,mesg ; offset of the string
int 21h ; Arghh... DOS function...
gezzz2: Call ScreenToLine ; Put the screen down
sub linha,3 ; Sub 3 lines each time
cmp linha,0 ; Is the line 0 ?
jg gezzz2 ; No, it's greater so, loop....
mov linha,0 ; Make sure it stoped in the line 0
call screentoline ; Put it in the right start point...
mov dx,03c8h ; Back color 5 to the original values
mov al,05
out dx,al
inc dl
mov al,42
out dx,al
mov al,00
out dx,al
mov al,42
out dx,al
bla6:
Cmp [sound],00h ; There is song rolling ?
Je Gooo ; No, so go out !
Mov ah,02h ; Yes, so, Stop it !
Call hscplayer ; Stop it now !
Jmp Gooooo
Gooo :
mov ah,09h ; Print String Function
lea dx,mesg3 ; message offset
int 21h ; Arghh... DOS function again...
Gooooo:
mov ax, 4c00h ; Ufh... get out...
int 21h
NotVga:
xor ax,ax ; Wait for keypress function
int 16h ; Call int16h
mov ah,02 ; Set Cursor Position Function
mov bh,0 ; Display Active Page
mov dx,(24 shl 8)+80 ; ROW 24, COL 80
int 10h ; Call Int 10h !
mov ah,09h ; Print String Function
lea dx,mesg2 ; message offset
int 21h ; Arghh... DOS function again...
jmp bla6 ; Go to see if there is song rolling
;====================================================================
; Ok, now we have the little proc to draw the BARs on the screen
; This proc is called by HSCPLAY at each music interrupt update...
;====================================================================
VUs Proc Far
mov ax,seg HscPlayer ; Make DS = Segment of HSCPlayer
mov ds,ax
mov ax,0b800h ; ES points to screen segment
mov es,ax
xor si,si ; SI = number of music tracks (VUs)
NextBar:
mov ax,05 ; 5 caracters by each bar
mov bx,si ; Put actual bar in BX
inc bl ; Increment it (we have to start with 1)
mul bl ; And multiply to get the right column
shl ax,1 ; Mul it by 2 (we need to count the atrib too)
add ax,(9*160+1)+16 ; Start it in the atribute of column 16
add ax,si ; Let put a space between each bar
add ax,si ; yes... 2 times to work right ...
mov di,ax ; Ok, the column value in DI (bh=0)
Mov bl,[hsc_equalizer+si] ; Get the BAR VALUE
mov cx,0fh+1 ; CX = max bar value (0fh) + 1
inc bl ; Increment the bar value
DrawOneBar:
Mov dl,00010000b ; This is the color for the BAR
Cmp bl,cl ; What do we have to do ?
Ja jmp1 ; Hummm... Draw it
Xor dl,0ffh ; Hummm... Erase it
And es:byte ptr [di ],dl ; Erase this piece of the bar
And es:byte ptr [di+2],dl ; Next column
And es:byte ptr [di+4],dl ; Next column
And es:byte ptr [di+6],dl ; Next column
And es:byte ptr [di+8],dl ; Next column
Jmp jmp2 ; Jump... we don't want to draw the bar
jmp1:
Or es:byte ptr [di],dl ; Draw this piece of the bar
Or es:byte ptr [di+2],dl ; Next column
Or es:byte ptr [di+4],dl ; Next column
Or es:byte ptr [di+6],dl ; Next column
Or es:byte ptr [di+8],dl ; Next column
jmp2:
add di,160 ; Go to next line (2*80)
loop DrawOneBar ; Loop if the bar isn't finished yet...
inc si ; Get next bar
cmp si,9 ; Is it the last ?
jb NextBar ; No, so LOOP again...
ret
VUs Endp
;===================================================================
; This procedure shows the screen starting on the line specified in
; LINHA. The VGA screen standard mode has 400 lines / screen !
; This is the "funny" piece of the code... want more comments ?
; Write them :) There is a lot of VGA REGISTERS being changed here,
; if you don't know, you can "tell" to VGA start the screen display
; at any line you want (line = pixel line, not TEXT line), so you
; can make a smooth scrolling efect, like I did here...
;===================================================================
ScreenToLine Proc near ;
CLI ; Disable Interrupts
mov dx,03dah
@loop1: in ax,dx
and ax,08
cmp ax,08
je @loop1
mov ax,[linha] ; Move the line to ax
shr ax,4 ; Divide the line number by 16 (char size)
mov bl,80 ; Mul it by the screen horiz size (in columns)
mul bl
mov bx,ax ; Put the result in bx
Mov dl,0d4h
Mov ax,0ch
out dx,ax
mov al,bh
Inc dx
out dx,al
Dec dx
Mov ax,0dh
out dx,ax
Inc dx
mov al,bl
out dx,al
mov dl,0dah
@loop2: in ax,dx
and ax,08
cmp ax,08
jne @loop2
mov dl,0d4h
mov al,08h
out dx,al
mov ax,[linha]
and ax,0fh
Inc dx
out dx,al
STI ; Enable interrupts
Ret
linha dw 00
ScreenToLine endp
;===================================================================
; Powerful procedure to wait for retrace :))
;===================================================================
Retrace Proc near
push dx ; You know what it does, not ?
push ax ; Again...
mov dx,3dah ; No, no comments for this one...
VRT:
in al,dx
and al,8
jnz VRT ; Wait until Verticle Retrace starts
NoVRT:
in al,dx
and al,8
jz NoVRT ; Wait until Verticle Retrace Ends
pop ax ; Restore all the shit...
pop dx
RET
Retrace endp
;===================================================================
; The next PROC is the THEDRAW UNCRUNCH ROUTINE to UNPACK THE SCREEN
;===================================================================
UNCRUNCH PROC NEAR
;Parameters Required:
; DS:SI Crunched image source pointer.
; ES:DI Display address pointer.
; CX Length of crunched image source data.
;
PUSH SI ;Save registers.
PUSH DI
PUSH AX
PUSH BX
PUSH CX
PUSH DX
JCXZ Done
MOV DX,DI ;Save X coordinate for later.
XOR AX,AX ;Set Current attributes.
CLD
LOOPA: LODSB ;Get next character.
CMP AL,32 ;If a control character, jump.
JC ForeGround
STOSW ;Save letter on screen.
Next: LOOP LOOPA
JMP Short Done
ForeGround:
CMP AL,16 ;If less than 16, then change the
JNC BackGround ;foreground color. Otherwise jump.
AND AH,0F0H ;Strip off old foreground.
OR AH,AL
JMP Next
BackGround:
CMP AL,24 ;If less than 24, then change the
JZ NextLine ;background color. If exactly 24,
JNC FlashBitToggle ;then jump down to next line.
SUB AL,16 ;Otherwise jump to multiple output
shl al,5 ;routines.
AND AH,8FH ;Strip off old background.
OR AH,AL
JMP Next
NextLine:
ADD DX,160 ;If equal to 24,
MOV DI,DX ;then jump down to
JMP Next ;the next line.
FlashBitToggle:
CMP AL,27 ;Does user want to toggle the blink
JC MultiOutput ;attribute?
JNZ Next
XOR AH,128 ;Done.
JMP Next
MultiOutput:
CMP AL,25 ;Set Z flag if multi-space output.
MOV BX,CX ;Save main counter.
LODSB ;Get count of number of times
MOV CL,AL ;to display character.
MOV AL,32
JZ StartOutput ;Jump here if displaying spaces.
LODSB ;Otherwise get character to use.
DEC BX ;Adjust main counter.
StartOutput:
XOR CH,CH
INC CX
REP STOSW
MOV CX,BX
DEC CX ;Adjust main counter.
LOOPNZ LOOPA ;Loop if anything else to do...
Done: POP DX ;Restore registers.
POP CX
POP BX
POP AX
POP DI
POP SI
RET
UNCRUNCH ENDP
speed dw 0000
temp db 00
r db 00 ; Saves the value of RED
g db 00 ; Saves the value of GREEN
b db 00 ; Saves the value of BLUE
textcol dw 00 ; Pointer to the scrolling text
Vga db 00 ; Sound MUST follow VGA
Sound db 00 ; Don't change the position or it won't work !
color db 02 ; A little color counter to make the things right
kicks db 15 ; How many times the screen will bounce ?
include c:\code\great\great2.mus ; Include the music
include c:\code\great\teltest.asm ; Include the ANSI Screen
texto db 80 dup (' ')
db ' WARMBOOT BBS - A Maior Base de DEMOs de MultiMedia do Brasil ! '
db ' -> * 3 Gigas de arquivos - 1 GIGA ON-LINE * <- '
db ' * Suporte autorizado do ARJ, Inertia Player, Dual Mod Player e EGOF * '
db ' MEGAs de arquivos de Som, M£sica, C¢digos de DEMOS, Efeitos Gr ficos, '
db ' Protrackers, Utilit rios, GIFs, Programa‡„o MultiMedia e muito mais !!! '
db ' Representantes exclusivos dos grupos FUTURE CREW, RENAISSANCE e TWILIGHT ZONE '
db ' Ligue e confira : (0194) 26-5112 - 24horas - USR 14.4K (8,N,1) '
db 80 dup (' ')
mesg db 13,10,'Coded by WarmBooter /Wá (1994) !',13,10,'$'
mesg2 db ' Go and buy a VGA to see the real power of GREAT2 !',13,10,'$'
mesg3 db 'Want music ? Go and buy a SB now !',13,10,'$'
codigo ends
end start
;-------------------------------------------------------------------------
; This code (ALL THE CODE, everything but the THEDRAW UNPACK ROUTINES and
; the PLAYER) was made by ME ! It is freeware but you can't use it to do
; your Intro ! BE CREATIVE !!!!
;-------------------------------------------------------------------------
; Before finish this I would like to ask to these great musicians around
; the World (PM, Skaven, etc...) to stop to make these technos, it sucks !!!
; Make something more heavy !
;-------------------------------------------------------------------------