Gilbert Francois Duivesteijn
The goal of this page is to show how to create graphics in screen mode 2. The examples in this page are the very minimum to create a tile, an 8x8 pixel sized block. Both examples give the output as seen in the screenshot above.
Step 1: Watch the tutorial from ChibiAkumas to learn how the tile based Graphics system of the MSX works.
Lesson S6 - Easy Tiles on the MSX1
Lesson P5 - Bitmap graphics on the TI-83 and MSX
Note: On the MSX1, there are some details to be careful with! The command otir to copy a block of memory to the VRAM cannot be used on an MSX1. There is a speed limit when accessing the VRAM of 29 cycles. The otir instruction is too fast and it results in incomplete tiles in VRAM. On the (real) MSX1, I had lines skipped in the tiles. The examples on this page will be using an alternative routine for the otir instruction, as suggested by Grauw (see references). The MSX2 does not have these speed limits, since the VDP is faster than the otir instruction.
Using bios functions gives the benefit of smaller code and guaranteed to work subroutines. The downside is that it might be not the most efficient way to perform tasks.
Step 2a: Type in the code below and save to a file named hello_screen2.asm
. The code is made for a ROM. If you prefer to compile to a CAS or BIN file, look at the How to create a ROM, BIN or CAS file page.
xxxxxxxxxx
; Hello Screen2
; Minimal example for showing an 8x8 bitmap in screen 2,
; using BIOS functions.
; ==[ Constants ]===============================================
ORGADR equ $4000
CHGMOD equ $005f
; Function : Switches to given screenmode
; Input : A - screen mode
; Registers: All
LDIRVM equ $005c
; Function : Block transfer from memory to VRAM
; Input : BC - blocklength
; DE - Start address of VRAM
; HL - Start address of memory
; Registers: All
SETWRT equ $0053
; Function: sets VRAM address to VDP and enables it to be written.
; Input: HL for VRAM address
; Output: none
; Registers: AF
VDPData equ $98
VDPControl equ $99
TilePos equ 0 * 8
RomSize equ $4000
; ==[ Header ]==================================================
; Place header inside the binary.
org ORGADR
; ROM header
db "AB"
dw Main
dw 0, 0, 0, 0, 0, 0
; ==[ Program ]=================================================
Main:
; Change to screen 2
ld a, 2
call CHGMOD
; Copy Pattern0 to VRAM
ld hl, Pattern0
ld de, $0000 + TilePos
ld bc, $0008
call LDIRVM
; Copy Color0 map to VRAM
ld hl, Color0
ld de, $2000 + TilePos
ld bc, $0008
call LDIRVM
; Place pattern0 to (x,y)=(2, 1)
ld hl, $1800 + 1 + 1*32
call SETWRT
ld a, $00
out (VDPData), a
; Place pattern0 to (x,y)=(4, 2)
ld hl, $1800 + 2 + 2*32
call SETWRT
ld a, $00
out (VDPData), a
di
halt
Pattern0:
db %10101010
db %01010101
db %10000010
db %01000001
db %10000010
db %01000001
db %10101010
db %01010101
Color0:
db $16
db $19
db $1b
db $13
db $12
db $15
db $14
db $1d
; ==[ ROM Padding ]=============================================
ProgEnd:
ds $4000 + RomSize - ProgEnd, 255
The listing above does the following things:
CHGMOD
.Pattern0
, a 8x8 pixel block to VRAM in the Pattern Generator Table, using the bios function LDIRVM
, where hl
has the Pattern0 address in RAM, de
holds the destination address in VRAM, bc
is the block size to transfer.Color0
, a 8x8 colour code block to VRAM in the Color Table, using the bios function LDIRVM
, where hl
has the Pattern0 address in RAM, de
holds the destination address in VRAM, bc
is the block size to transfer.hl
. Then make the VDP ready for writing a value with the bios function SETWRT
. Finally load the pattern index number in register a
and send the value via the out($98)
to the VDP. Step 3a: Compile
with VASM
$ vasmz80_oldstyle hello_screen2.asm -chklabels -nocase -Dvasm=1 -Fbin -L out.sym -o out.rom
or with Glass (you can choose)
$ java -jar Glass.jar hello_screen2.asm -L out.sym out.rom
Step 4a: run with openMSX
$ <path to>/openmsx -machine C-BIOS_MSX1_EU -cart out.rom
Now you should see 3 coloured blocks in a diagonal.
Using your own written functions gives the benefit of code that you fully understand and enables you to optimize it as far as you can. Usually, the code footprint is larger than using ready made BIOS functions.
Step 2b: Type in the code below and save to a file named hello_screen2.asm
.
xxxxxxxxxx
; Hello Screen2
; Minimal example for showing an 8x8 bitmap in screen 2,
; using self written 'copy to VRAM' routines.
;
; ==[ Constants ]===============================================
ORGADR equ $4000
CHGMOD equ $005f
; Function : Switches to given screenmode
; Input : A - screen mode
; Registers: All
VDPData equ $98
VDPControl equ $99
RomSize equ $4000
TilePos equ 0 * 8
; ==[ Header ]==================================================
; Place header inside the binary.
org ORGADR
; ROM header
db "AB"
dw Main
dw 0, 0, 0, 0, 0, 0
; ==[ Program ]=================================================
Main:
; Change to screen 2
ld a, 2
call CHGMOD
; Copy Pattern0 to VRAM
ld hl, $0000 + TilePos
call SetVDPWriteAddress
ld hl, Pattern0
ld b, 8
ld c, VDPData
call CopyToVRAM
; Copy Color0 map to VRAM
ld hl, $2000 + TilePos
call SetVDPWriteAddress
ld hl, Color0
ld b, 8
ld c, VDPData
call CopyToVRAM
; Place pattern0 to (x,y)=(2, 1)
ld hl, $1800 + 1 + 1*32
call SetVDPWriteAddress
ld a, $00
out (VDPData), a
; Place pattern0 to (x,y)=(4, 2)
ld hl, $1800 + 2 + 2*32
call SetVDPWriteAddress
ld a, $00
out (VDPData), a
di
halt
CopyToVRAM:
; Don't use otir on MSX1
; See http://map.grauw.nl/articles/vdp_tut.php#vramtiming
outi
jp nz, CopyToVRAM
ret
SetVDPWriteAddress:
ld a, l
out (VDPControl), a
ld a, h
or %01000000
out (VDPControl), a
ret
Pattern0:
db %10101010
db %01010101
db %10000010
db %01000001
db %10000010
db %01000001
db %10101010
db %01010101
Color0:
db $16
db $19
db $1b
db $13
db $12
db $15
db $14
db $1d
; ==[ ROM Padding ]=============================================
ProgEnd:
ds $4000 + RomSize - ProgEnd, 255
CopyToVRAM
with outi is used. This is different from the ChibiAkumas tutorials (at the time of writing, June 2022).CopyToVRAM
subroutine is the fastest possible implementation for copying data to VRAM, according to the info page of VDP programming tutorial :: VRAM timings.Step 3b: Compile
with VASM
$ vasmz80_oldstyle hello_screen2.asm -chklabels -nocase -Dvasm=1 -Fbin -L out.sym -o out.rom
or with Glass (you can choose)
$ java -jar Glass.jar hello_screen2.asm -L out.sym out.rom
Step 4b: run with openMSX
$ <path to>/openmsx -machine C-BIOS_MSX1_EU -cart out.rom
Now you should see 3 coloured blocks as in the first example.