-
Notifications
You must be signed in to change notification settings - Fork 8
/
lcd.go
94 lines (78 loc) · 2.01 KB
/
lcd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package main
import (
"ndsemu/emu/hwio"
log "ndsemu/emu/logger"
)
var modLcd = log.NewModule("lcd")
const (
cVBlankFlag = (1 << 0)
cHBlankFlag = (1 << 1)
cVMatchFlag = (1 << 2)
cVBlankIrq = (1 << 3)
cHBlankIrq = (1 << 4)
cVMatchIrq = (1 << 5)
)
type HwLcdConfig struct {
VBlankFirstLine int
VBlankLastLine int
HBlankFirstDot int
}
type HwLcd struct {
Irq *HwIrq
Cfg *HwLcdConfig
DispStat hwio.Reg16 `hwio:"offset=4,rwmask=0xFFF8,rcb"`
VCount hwio.Reg16 `hwio:"offset=6,readonly,rcb"`
}
func NewHwLcd(irq *HwIrq, cfg *HwLcdConfig) *HwLcd {
lcd := &HwLcd{Irq: irq, Cfg: cfg}
hwio.MustInitRegs(lcd)
lcd.VCount.ReadCb = lcd.ReadVCOUNT // speedup - abused by megamanzero
return lcd
}
func (lcd *HwLcd) ReadDISPSTAT(stat uint16) uint16 {
x, y := Emu.Sync.DotPos()
// VBlank: not set on line 227
if y >= lcd.Cfg.VBlankFirstLine && y <= lcd.Cfg.VBlankLastLine {
stat |= cVBlankFlag
}
// HBlank: the flag is kept to 0 for 268 dots / ~1600 cycles (even
// if the screen is 256 dots)
if x > lcd.Cfg.HBlankFirstDot {
stat |= cHBlankFlag
}
// VMatch
vmatch := int(stat>>8 | (stat&0x80)<<1)
if y == vmatch {
stat |= cVMatchFlag
}
return stat
}
func (lcd *HwLcd) ReadVCOUNT(_ uint16) uint16 {
_, y := Emu.Sync.DotPos()
return uint16(y) & 0x1FF
}
func (lcd *HwLcd) SyncEvent(x, y int) {
switch x {
case 0:
if y == lcd.Cfg.VBlankFirstLine {
if lcd.DispStat.Value&cVBlankIrq != 0 {
modLcd.InfoZ("VBlank IRQ").String("irq", lcd.Irq.Name).End()
lcd.Irq.Raise(IrqVBlank)
}
}
vmatch := int(lcd.DispStat.Value>>8 | (lcd.DispStat.Value&0x80)<<1)
if y == vmatch && lcd.DispStat.Value&cVMatchIrq != 0 {
modLcd.InfoZ("VMatch IRQ").String("irq", lcd.Irq.Name).Int("line", y).End()
lcd.Irq.Raise(IrqVMatch)
}
case lcd.Cfg.HBlankFirstDot:
if !(y >= lcd.Cfg.VBlankFirstLine && y <= lcd.Cfg.VBlankLastLine) {
if lcd.DispStat.Value&cHBlankIrq != 0 {
// modLcd.WithField("irq", lcd.Irq.Name).Info("HBlank IRQ")
lcd.Irq.Raise(IrqHBlank)
}
}
default:
// panic("unreachable")
}
}