Code: Select all
.model small
.stack 1024
.data
UserRam BYTE 256 DUP (0)
Metatile_LUT_ToUse equ UserRam ;word data
MetatileMapToUse equ UserRam+2 ;word data
PixelPlotX equ UserRam+4 ;byte data, drawing cursor X pos
PixelPlotY equ UserRam+5 ;byte data, drawing cursor Y pos
null_tile byte 8,8
byte 0,0,0,0
byte 0,0,0,0
byte 0,0,0,0
byte 0,0,0,0
byte 0,0,0,0
byte 0,0,0,0
byte 0,0,0,0
byte 0,0,0,0
;this tile is completely blank.
flowertile_PAL0CMP byte 8,8
byte 0AAh,0AAh,0AAh,0AAh
byte 0AAh,0AAh,0AAh,0AAh
byte 0AAh,0A4h,04Ah,0AAh
byte 0AAh,04Ch,0C4h,0AAh
byte 0A2h,024h,042h,02Ah
byte 0AAh,022h,022h,0AAh
byte 0AAh,0AAh,0AAh,0AAh
byte 0AAh,0AAh,0AAh,0AAh
;PAL0CMP stands for "Palette 0 Compression," meaning that each byte uses palette 0 and thus
;the top nibble goes unused in each pixel. Therefore two pixels can be encoded per byte
;and unpacked just before storing into VRAM.
blueflowertile_PAL0CMP byte 8,8
byte 0AAh,0AAh,0AAh,0AAh
byte 0AAh,0AAh,0AAh,0AAh
byte 0AAh,0A1h,01Ah,0AAh
byte 0AAh,019h,091h,0AAh
byte 0A2h,021h,012h,02Ah
byte 0AAh,022h,022h,0AAh
byte 0AAh,0AAh,0AAh,0AAh
byte 0AAh,0AAh,0AAh,0AAh
grasstile_PAL0CMP byte 8,8
byte 0AAh,0AAh,0AAh,0AAh
byte 0A2h,0A2h,0AAh,0AAh
byte 0AAh,02Ah,0AAh,0AAh
byte 0AAh,0AAh,0AAh,0AAh
byte 0AAh,0AAh,0AAh,0AAh
byte 0AAh,0AAh,0A2h,0A2h
byte 0AAh,0AAh,0AAh,02Ah
byte 0AAh,0AAh,0AAh,0AAh
greentile_PAL0CMP byte 8,8
byte 0AAh,0AAh,0AAh,0AAh
byte 0AAh,0AAh,0AAh,0AAh
byte 0A2h,0AAh,0AAh,0AAh
byte 0AAh,0AAh,0AAh,0AAh
byte 0AAh,0AAh,0AAh,02Ah
byte 0AAh,0AAh,0AAh,0AAh
byte 0AAh,0A2h,0AAh,0AAh
byte 0AAh,0AAh,0AAh,0AAh
TilemapLookupTable00 word null_tile ;tile 0
word greentile_PAL0CMP ;tile 1
word grasstile_PAL0CMP ;tile 2
word flowertile_PAL0CMP ;tile 3
word blueflowertile_PAL0CMP ;tile 4
Tilemap byte 960 DUP(0)
;tilemap ram
;you can make this value smaller or larger, but it must be a multiple of decimal 40.
; TL TR BL BR
Metatiles00 byte 00h,00h,00h,00h ;m0: null metatile
byte 01h,01h,01h,01h ;m1: thin grass metatile
byte 01h,02h,02h,01h ;m2: medium grass metatile
byte 04h,03h,03h,04h ;m3: two blue flowers, two red flowers
MetatileMap00 byte 02,02,02,03,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row 0-1
byte 01,01,03,02,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row 2-3
byte 02,03,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row 4-5
byte 01,02,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row 6-7
byte 01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row 8-9
byte 01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row A-B
byte 02,02,02,03,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row C-D
byte 01,01,03,02,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row E-F
byte 02,03,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row 10-11
byte 01,02,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row 12-13
byte 01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row 14-15
byte 01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row 16-17
byte 01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01 ;row 18-19
; 40 columns, 12 rows of metatiles.
; Each entry in this array represents a 16x16 pixel area,
; made of 4 8x8 tiles each.
;;;;;; END OF DATA SEGMENT
.code
start:
mov ax,@data
mov ds,ax
mov ah,0 ;command to change video modes
mov al,13h ;VGA
int 10h ;change video mode to VGA
mov ax,0A000h ;VGA VRAM
mov es,ax
;setup the tilemap
mov si,offset Metatiles00
mov word ptr [ds:MetatileMapToUse],si
mov si,offset MetatileMap00
call SetupTilemap
;draw the tilemap
mov ax, offset TilemapLookupTable00
mov word ptr [ds:Metatile_LUT_ToUse],ax
mov si, offset Tilemap
call Draw_VGA_Tilemap
mov ax,4C00h
int 21h
;end of main program
;--------------------------------------------------------------
; SUBROUTINES
;--------------------------------------------------------------
LocateVRAM:
;input: DH = row, DL = column (measured in 8x8 pixel squares.)
;DH ranges from 0 to 39 (these are decimal)
;DL ranges from 0 to 24 (these are decimal)
;outputs to DI
push dx
push bx
push ax
;store x pos.
mov ah,0
mov al,dh
shl AX,1
shl AX,1
shl AX,1
mov di,ax
mov ax,80
shl ax,1
shl ax,1
shl ax,1
shl ax,1
shl ax,1
mov dh,0 ;set DX = 00DL
mul dx
;only seems to work as a 32-bit product
;even though the product never exceeds 16 bits.
add di,ax
pop ax
pop bx
pop dx
ret
;--------------------------------------------------------------
DrawBitmap_VGA:
;FOR UNCOMPRESSED BITMAP DATA ONLY
;input: ds:si = source bitmap
;ch = bitmap height in pixels
;cl = bitmap width in pixels.
DrawBitmap_VGA_YAgain:
push di ;now bp+2 = cl and bp+3 = ch
mov ch,byte ptr[ds:PixelPlotY]
DrawBitmap_VGA_XAgain:
movsb
dec ch
jnz DrawBitmap_VGA_XAgain
pop di
add di,320 ;one row of pixels = screen width
dec cl
jnz DrawBitmap_VGA_YAgain
ret
;--------------------------------------------------------------
DrawBitmap_VGA_Palette0:
;This is for the "Palette 0 Compression" algorithm I created.
;It's very very simple: Each byte stores two pixels. Since the
;first 16 colors have a leading zero nibble, we can store twice as much info in
;one byte. The bytes are unpacked at runtime and stored in pairs.
;This does limit us to 16 colors but if we were doing that anyway it's ok
;NORMAL: 03,03,03,03,03,03,03,03
;PAL0CMP: 33,33,33,33
;unfortunately this does not work with graphics that have odd dimensions.
DrawBitmap_VGA_YAgain_Palette0:
push di
mov ch,byte ptr[ds:PixelPlotY]
DrawBitmap_VGA_XAgain_Palette0:
lodsb ;get compressed byte in al
mov ah,al ;copy it to ah
and ah,00001111b ;chop off the top nibble
and al,11110000b ;chop off the bottom nibble
;it's easier to do this little-endian since stosb only works with al
shr al,1
shr al,1
shr al,1
shr al,1 ;right shift four times
stosb ;store al into vram
xchg ah,al ;swap to get the next pixel
stosb ;store al (old ah) into vram
dec ch
dec ch
jnz DrawBitmap_VGA_XAgain_Palette0
pop di
add di,320
dec cl
jnz DrawBitmap_VGA_YAgain_Palette0
ret
;--------------------------------------------------------------
Draw_VGA_Tilemap:
;INPUT:
;DS:SI = TILEMAP SOURCE
mov dx,0 ;reset drawing cursors to top left of screen
Draw_VGA_Tilemap_NextRow:
Draw_VGA_Tilemap_NextColumn:
lodsb ;get the next tile index
push si
push bx
mov ah,0
mov bx,word ptr [ds:Metatile_LUT_ToUse]
;in a later build pull this from ram, to allow for more
;lookup tables than just one.
call XLATW
;returns the offset of the tile's bitmap data in ax
mov si,ax
call LocateVRAM ;adjust DI according to DX.
lodsb
mov cl,al
lodsb
mov ch,al
mov word ptr [ds:PixelPlotX],cx
call DrawBitmap_VGA_Palette0
pop bx
pop si
inc dh
cmp dh,40
jb Draw_VGA_Tilemap_NextColumn
mov dh,0
inc dl
cmp dl,18h
jb Draw_VGA_Tilemap_NextRow
done_Draw_VGA_Tilemap:
ret
SetupTilemap:
;ds:si = metatiles list to load
;es:di = Tilemap
push es
mov dx,seg Tilemap
mov es,dx
mov di,offset Tilemap
mov dx,0
mov cx,18h ;max screen height (in 8x8 squares)
repeat_tilemap_nextRow:
repeat_tilemap_nextSquare:
push di
lodsb ;get metatile index
push si
mov si,word ptr [ds:MetatileMapToUse]
shl al,1
shl al,1 ;each row is 4 bytes.
;once you've selected a row, go ahead and copy to tilemap ram.
mov bx,0
mov bl,al
lea ax,[bx+si]
mov si,ax ;now si is pointing to the beginning of the desired row.
movsb
movsb
add di,40-2 ;get to next row of tilemap just underneath where we were.
movsb
movsb
pop si
pop di
inc di
inc di
inc dx
inc dx ;dx mimics di for the purposes of telling when we're done.
cmp dx,40
jb repeat_tilemap_nextSquare
mov dx,0
add di,40 ;skip a row so that we don't draw over top of what's already there
loop repeat_tilemap_nextRow
pop es
ret
XLATW:
;input: ds:bx = the table you wish to look up
;AL= the raw index into this table as if it were byte data.
;So don't left shift prior to calling this.
;AH is destroyed.
SHL AL,1
mov ah,al ;copy AL to AH
XLAT ;returns low byte in al
inc ah
xchg al,ah ;XLAT only works with AL
XLAT ;returns high byte in al (old ah)
xchg al,ah
;now the word is loaded into ax, big-endian.
ret
end start ;EOF