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 $98VDPControl equ $99TilePos equ 0 * 8RomSize 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.romor with Glass (you can choose)
$ java -jar Glass.jar hello_screen2.asm -L out.sym out.romStep 4a: run with openMSX
$ <path to>/openmsx -machine C-BIOS_MSX1_EU -cart out.romNow 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 $98VDPControl equ $99RomSize equ $4000TilePos 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, 255CopyToVRAM 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.romor with Glass (you can choose)
$ java -jar Glass.jar hello_screen2.asm -L out.sym out.romStep 4b: run with openMSX
$ <path to>/openmsx -machine C-BIOS_MSX1_EU -cart out.romNow you should see 3 coloured blocks as in the first example.
