arch z80
const FOR_MSX2_OR_LATER = 1
include "msx.oc"
msx:link-as-rom main _
module main {
macro next(addr) ={
HL@%=addr -> [scroller]
return
}
data scroll-mode = byte * 1 : bss
data active-page = byte * 1 : bss
data adjust-x = byte * 1 : bss
data scroller = word * 1 : bss
data vdp-command = byte * 15 : bss
proc main() {
SP <- msx:STACK_ADDR
msx:enable-slot/page2 // skip EI
msx:set-vdp-registers,
0 0b0000_0110, // mode: graphic-4, intr/h: false
1 0b0110_0000, // visible: true, intr/v: true, sprite-size: 16, sprite-scale: 1
2 0b0001_1111, // vdp-page: 0
5 0b1111_1111, // sat-addr: 0x7c00
7 0b0000_0001, // backgound-color: 1
8 0b0000_1000, // vram: 64k, sprite-visible: true
9 0b0000_1000, // height: 192, interlace: true
11 0b0000_0011, // sat-page: 3
16 0 // palette-index: 0
msx:set-vdp-palette,
msx:rgb(0 0 0), // 0
msx:rgb(0 0 0), // 1
msx:rgb(0 0 7), // 2
msx:rgb(0 7 0), // 3
msx:rgb(0 7 7), // 4
msx:rgb(7 0 0), // 5
msx:rgb(7 0 7), // 6
msx:rgb(7 7 0), // 7
msx:rgb(7 7 7), // 8
msx:rgb(7 7 7), // 9
msx:rgb(7 7 7), // a
msx:rgb(7 7 7), // b
msx:rgb(7 7 7), // c
msx:rgb(7 7 7), // d
msx:rgb(7 7 7), // e
msx:rgb(7 7 7) // f
init-sprites()
setup-scroller()
msx:update-hook msx:H_KEYI keyi
msx:set-vdp-registers 0 0b0001_0110, 19 60 // intr/h: true, intr/h-y:60
fallthrough
}
proc main-loop() {
msx:wait-vsync
msx:set-vdp-registers 2 0b0001_1111, 18 0 // vdp-page: 0, adjust: 0 0
@8 . msx:snsmat(A => A) & 0b0000_0001; if zero? { // SPC
msx:set-vdp-register 0 0b0000_0110 // intr/h: false
msx:wait-vsync
@[scroll-mode] | A; if zero? {
setup-scroller16()
} else {
setup-scroller()
}
msx:set-vdp-register 0 0b0001_0110 // intr/h: true
recur
}
HL@[scroller] . call/HL()
recur
}
proc call/HL() { goto/HL }
proc keyi() {
// already di
msx:set-vdp-register! 15 1 // S#1
A -in msx:VDP_STAT >*$ 1 // FH
msx:set-vdp-register! 15 0
return-if not-carry?
msx:set-vdp-register! 2 [active-page]
msx:set-vdp-register! 18 [adjust-x]
return
}
proc setup-scroller() {
clear-screen()
load-image()
A - A -> [adjust-x] -> [scroll-mode]
@0b0011_1111 -> [active-page]
HL@frame0 -> [scroller]
return
proc frame0() {
@[active-page] + 0b0010_0000 & 0b0011_1111 -> [active-page]
@8 -> [adjust-x]
data params/l = byte [0 0, 64 0, 232 0, 64 1, 8 0, 128 0, 0, 0, msx:VDP/HMMM] : rodata
HL@params/l . hmmm(HL => !)
data params/r = byte [8 0, 64 0, 0 0, 64 1, 232 0, 128 0, 0, 0, msx:VDP/HMMM] : rodata
HL@params/r . hmmm(HL => !)
next frame1
}
proc frame1() {
@9 -> [adjust-x]
E@0 . render-mask(E => !)
next frame2
}
proc frame2() {
@10 -> [adjust-x]
next frame3
}
proc frame3() {
@11 -> [adjust-x]
E@1 . render-mask(E => !)
next frame4
}
proc frame4() {
@12 -> [adjust-x]
next frame5
}
proc frame5() {
@13 -> [adjust-x]
E@2 . render-mask(E => !)
next frame6
}
proc frame6() {
@14 -> [adjust-x]
next frame7
}
proc frame7() {
@15 -> [adjust-x]
next frame0
}
}
proc setup-scroller16() {
clear-screen()
load-image()
A - A -> [adjust-x] ++ -> [scroll-mode]
@0b0011_1111 -> [active-page]
HL@frame0 -> [scroller]
return
proc frame0() {
@[active-page] + 0b0010_0000 & 0b0011_1111 -> [active-page]
@8 -> [adjust-x]
data params/l = byte [0 0, 64 0, 224 0, 64 1, 16 0, 128 0, 0, 0, msx:VDP/HMMM] : rodata
HL@params/l . hmmm(HL => !)
data params/r = byte [16 0, 64 0, 0 0, 64 1, 224 0, 128 0, 0, 0, msx:VDP/HMMM] : rodata
HL@params/r . hmmm(HL => !)
next frame1
}
proc frame1() {
@10 -> [adjust-x]
E@0 . render-mask(E => !)
next frame2
}
proc frame2() {
@12 -> [adjust-x]
E@1 . render-mask(E => !)
next frame3
}
proc frame3() {
@14 -> [adjust-x]
E@2 . render-mask(E => !)
next frame4
}
proc frame4() {
@0 -> [adjust-x]
E@3 . render-mask(E => !)
next frame5
}
proc frame5() {
@2 -> [adjust-x]
E@4 . render-mask(E => !)
next frame6
}
proc frame6() {
@4 -> [adjust-x]
E@5 . render-mask(E => !)
next frame7
}
proc frame7() {
@6 -> [adjust-x]
E@6 . render-mask(E => !)
next frame0
}
}
proc hmmm(HL => !) {
memcpy vdp-command _ sizeof(vdp-command)
HL@vdp-command
@[active-page] & BIT-5; if not-zero? {
HL<->DE {
A - A -> [HL@7 + DE] ++ -> [HL@3 + DE]
}
}
execute-vdp-command(HL => HL ! C)
return
}
proc render-mask(E => !) {
// page +0; Y:64 to Y:127(64 lines)
@[active-page] >* 4 & 0b010 -> D // 0bXYZ1_1111 -> 0b0000_00Z0
HL <- ((128 * 64) | BIT-14) . { @E + L -> L }
B@64 . write(B D HL => ! B HL)
// page +1; Y:128 to Y:184(56 lines)
D ++
HL <- ((128 * 0) | BIT-14) . { @E + L -> L }
B@64 . write(B D HL => ! B HL)
return
proc write(B D HL => ! B HL) {
di/ei {
msx:set-vdp-register! 14 D
loop {
@L -out msx:VDP_ADDR
@H -out msx:VDP_ADDR
A - A -out msx:VDP_DATA
@128 . {HL+A}
} while/B-
}
return
}
}
proc init-sprites() {
di/ei {
msx:set-vdp-register! 14 0b111 // page:3, 0x7c00
msx:set-vdp-write-addr! 0x3c00
}
@216 -out msx:VDP_DATA -out msx:VDP_DATA
return
}
proc clear-screen() {
data params = byte [0 0, 0 0, 0 0, 0 0, 0 1, 0 2, 0, 0, msx:VDP/HMMV] : rodata
HL@params . execute-vdp-command(HL => HL ! C)
return
}
proc wait-vdp-ready(-*) {
loop {
di/ei {
msx:set-vdp-register! 15 2
A -in msx:VDP_STAT >*$ 1 // CE
msx:set-vdp-register! 15 0
}
} while carry?
return
}
proc load-image() {
data params = byte [0 0, 0 0, 256 0, 192 0] : rodata
wait-vdp-ready(-*)
di/ei {
msx:set-vdp-register! 17 36
HL@params . BC@asword(sizeof(params) msx:VDP_REGS) . { OTIR }
@[HL@image-data] -out C // R#44
A - A -out C // R#45
@msx:VDP/HMMC -out C // R#46
msx:set-vdp-register! 17 (44 | 0x80) // no auto-increment
HL ++
DE <- asword(0 6) // loop 256 * 6 times
loop {
loop {
expand-loop 16 { OUTI }
} while/D-
} while/E-
}
return
}
proc execute-vdp-command(HL => HL ! C) {
C@msx:VDP_REGS
msx:set-vdp-register 17 32
wait-vdp-ready(-*)
expand-loop 15 { OUTI }
return
}
section rodata
image-data: incbin "./image.dat"
}