Browse Source

Add two simple test ROMs + a test tileset

test0 just spins.

test1 draws to the background and scrolls it.
gpu
Zack Marvel 3 years ago
parent
commit
b1496e53cf
  1. 5
      test_roms/.gitignore
  2. 41
      test_roms/Makefile
  3. 92
      test_roms/header.asm
  4. 47
      test_roms/header_checksum.py
  5. BIN
      test_roms/img/tileset1.png
  6. 7
      test_roms/scripts/gentilemap1.py
  7. 87
      test_roms/scripts/image2tilemap.py
  8. 10
      test_roms/test0.asm
  9. 147
      test_roms/test1.asm

5
test_roms/.gitignore

@ -0,0 +1,5 @@
*.lbl
*.lst
*.gb
*.hex
*.bin

41
test_roms/Makefile

@ -0,0 +1,41 @@
CC = sdcc
AS = z80asm
PP = m4
CFLAGS =
LDFLAGS =
ROMS = test0.gb \
test1.gb
BINS = tileset1.bin \
tilemap1.bin
HEXS = $(patsubst %.gb,%.hex,$(ROMS)) \
$(patsubst %.bin,%.hex,$(BINS))
all: $(ROMS) $(HEXS) $(BINS)
%.asmpp: %.asm header.asm
$(PP) $(PPFLAGS) $< > [email protected]
%.gb: %.asmpp $(BINS)
$(AS) $(ASFLAGS) \
--list=$(patsubst %.asmpp,%.lst,$<) \
--label=$(patsubst %.asmpp,%.lbl,$<) -o [email protected] $<
%.hex: %.gb
hexdump -C $< > [email protected]
%.hex: %.bin
hexdump -C $< > [email protected]
tileset1.bin: img/tileset1.png scripts/image2tilemap.py
python3 scripts/image2tilemap.py $< [email protected]
tilemap1.bin: scripts/gentilemap1.py
python3 scripts/gentilemap1.py [email protected]
clean:
rm -f $(ROMS) *.asmpp *.bin

92
test_roms/header.asm

@ -0,0 +1,92 @@
;;
;; ROM HEADER
;;
ds 0x100, 0
org 0x0100
;;
;; Entry point
;;
nop
jp 0x0150
org 0x0104
;;
;; Nintendo logo
;;
db 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B
db 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D
db 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E
db 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99
db 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC
db 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E
org 0x0134
;;
;; Title
;;
dm "SLOWBOY", 1, 2, 3, 4
org 0x013f
;;
;; Manufacturer code
;;
db 0, 0, 0, 0
;;
;; CGB flag
;;
db 0
;;
;; New licensee code
;;
db 0, 0
;;
;; SGB flag
;;
db 0
;;
;; Cartridge type: ROM only
;;
db 0
;;
;; ROM size: no ROM banking, 32 kB
;;
db 0
;;
;; RAM size: no external RAM
;;
db 0
;;
;; Destination code: non-Japanese
;;
db 0x01
;;
;; Old licensee code: use new licensee code
;;
db 0x33
;;
;; Mask ROM version number
;;
db 0
;;
;; Header checksum
;;
db 177
;;
;; Global checksum -- doesn't matter
;;
db 0, 0

47
test_roms/header_checksum.py

@ -0,0 +1,47 @@
#title
hdr = [ord(c) for c in "SLOWBOY\0"]
# manuf code, new licensee code
hdr += [0, 0, 0, 0, 0]
# sgb flag
hdr += [0]
# cart type
hdr += [0]
# ROM size
hdr += [0]
# RAM size
hdr += [0]
# dest code
hdr += [1]
# old licensee code
hdr += [0x33]
# mask rom version
hdr += [0]
def not8(x):
return x ^ 0xff
def sub(x, y):
# x - y
return (x + not8(y) + 1) & 0xff
chksum = 0
for x in hdr:
chksum = sub(chksum, sub(x, 1))
print(chksum)
#chksum = 0
#for x in hdr:
# chksum = chksum - x - 1
#
#print(chksum & 0xff)

BIN
test_roms/img/tileset1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

7
test_roms/scripts/gentilemap1.py

@ -0,0 +1,7 @@
import sys
fname = sys.argv[1]
with open(fname, 'wb') as f:
f.write(bytes(x % 64 for x in range(0, 0x8000)))

87
test_roms/scripts/image2tilemap.py

@ -0,0 +1,87 @@
#!/usr/bin/env python3
from PIL import Image
def rgb_i2bit(iterable):
"""Consumes an iterable of byte values and generates pairs of bytes
representing 8 pixels.
"""
try:
iterable = iter(iterable)
while True:
hi = 0
lo = 0
for i in range(8):
c = (next(iterable) >> 6) ^ 0x3
hi |= (c >> 1) << (7 - i)
lo |= (c & 1) << (7 - i)
yield hi
yield lo
except StopIteration:
raise StopIteration
def imageto2bit(img, tile_size):
"""Convert a (grayscale) PIL Image to 2-bit
"""
#img = Image.open(filename).convert(mode='L')
width, height = img.size
twidth, theight = tile_size
width_tiles = width // twidth
height_tiles = height // theight
img_bytes = img.tobytes()
assert len(img_bytes) == width_tiles*height_tiles*twidth*theight
tiles = []
for i in range(width_tiles*height_tiles):
tile = []
tx = (i % width_tiles) * twidth
ty = (i // width_tiles) * twidth
for j in range(theight):
y = ty + j
row = img_bytes[y*width+tx:y*width+tx+twidth]
tile.extend(row)
tiles.append(tile)
encoded = []
for tile in tiles:
assert len(tile) == twidth*theight
encoded.extend(rgb_i2bit(tile))
return bytes(encoded)
if __name__ == '__main__':
import argparse
import sys
parser = argparse.ArgumentParser(description=(
'Convert an image to a GameBoy tilemap (2-bit color).'
))
parser.add_argument('in_file', type=str,
help=('An image (any format supported by Pillow) to'
'be converted'))
parser.add_argument('out_file', type=str,
help='A writable output file.')
parser.add_argument('--asm', action='store_true',
help='Output in Z80 assembly instead of binary data.')
args = parser.parse_args()
try:
img = Image.open(args.in_file).convert('L')
except IOError:
parser.print_help()
sys.exit()
#tileset = Tileset(img.tobytes(), img.size, (8, 8), encoded=False)
rgb2_bytes = bytes(imageto2bit(img, (8, 8)))
if args.asm:
with open(args.out_file, 'w') as f:
for i, b in enumerate(rgb2_bytes):
f.write('db %{:08b}\n'.format(b))
if i & 0x7 == 0x7:
f.write('\n')
else:
with open(args.out_file, 'wb') as f:
f.write(rgb2_bytes)
print('Wrote output to {}'.format(args.out_file))

10
test_roms/test0.asm

@ -0,0 +1,10 @@
include "header.asm"
org 0x0150
;;
;; Actual entry point
;;
loop:
nop
jr 0xfd ;; -3
jr loop

147
test_roms/test1.asm

@ -0,0 +1,147 @@
;;
;; Test 1
;;
;; Test background display.
;;
define(LCDC, 0xff40)
define(STAT, 0xff41)
define(SCY, 0xff42)
define(SCX, 0xff43)
define(LY, 0xff44)
define(LYC, 0xff45)
define(BGP, 0xff47)
define(OBP0, 0xff48)
define(OBP1, 0xff49)
define(WY, 0xff4a)
define(WX, 0xff4b)
define(VRAM ,0x8000)
include "header.asm"
org 0x0150
;;
;; Entry point
;;
;; initialize graphics registers
;; LCDC:
;; - disable display
;; - select BG tiledata at 0x8000
;; - disable window display
;; - disable sprite display
;; - disable BG display
ld hl, LCDC
ld (hl), 0x10
;; disable STAT interrupts
inc hl
ld (hl), 0x00
;; SCX, SCY = 0
inc hl
ld (hl), 0x00
inc hl
ld (hl), 0x00
;; LYC = 0
inc hl
inc hl
ld (hl), 0x00
;; BGP = black (3), dark gray, light gray, white (0)
inc hl
ld (hl), 0xe4
;; OBPx = 0 (transparent)
inc hl
ld (hl), 0x00
inc hl
ld (hl), 0x00
;; WY, WX = 0
inc hl
ld (hl), 0x00
inc hl
ld (hl), 0x00
;; copy tileset into VRAM (0x8000-0x8fff)
ld hl, 0x8000
ld bc, tileset
loop0:
ld a, h
cp 0x90
jr z, endloop0
ld a, (bc)
ld (hl), a
inc bc
inc hl
ld a, (bc)
ld (hl), a
inc bc
inc hl
jr loop0
endloop0:
;halt
;; copy tilemap into VRAM (0x9800-0x9bff)
ld hl, 0x9800
ld bc, tilemap
loop1:
ld a, h
cp 0x9c
jr z, endloop1
ld a, (bc)
ld (hl), a
inc bc
inc hl
ld a, (bc)
ld (hl), a
inc bc
inc hl
jr loop1
endloop1:
;; enable BG display
ld hl, LCDC
set 0, (hl)
;; enable display
set 7, (hl)
ld bc, 0x0000
loop2:
inc bc
ld a, b
cp 0x01
jr z, endloop2
nop
nop
nop
nop
jr loop2
endloop2:
ld b, 0
ld hl, BGP
ld (hl), 0xe4
ld hl, SCX
inc (hl)
ld a, (hl)
; if a == 6*16 then a = 0
cp 6*16
jr nz, else2
ld a, 0
else2:
ld (hl), a
jr loop2
halt
tileset:
incbin "tileset1.bin"
tilemap:
incbin "tilemap1.bin"
Loading…
Cancel
Save