-
Notifications
You must be signed in to change notification settings - Fork 8
/
pads.lua
102 lines (87 loc) · 2.24 KB
/
pads.lua
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
95
96
97
98
99
100
101
102
local band, bor, bxor, bnot, lshift, rshift = bit.band, bit.bor, bit.bxor, bit.bnot, bit.lshift, bit.rshift
local map, rotatePositiveIdx, nthBitIsSet, nthBitIsSetInt =
UTILS.map,
UTILS.rotatePositiveIdx,
UTILS.nthBitIsSet,
UTILS.nthBitIsSetInt
Pads = {}
local Pads = Pads
Pads._mt = {__index = Pads}
function Pads:new(conf, cpu, apu)
local pads = {}
setmetatable(pads, Pads._mt)
pads:initialize(conf, cpu, apu)
return pads
end
function Pads:initialize(conf, cpu, apu)
self.conf = conf
self.cpu = cpu
self.apu = apu
self.pads = {Pad:new(), Pad:new()}
end
function Pads:reset()
self.cpu:add_mappings(0x4016, UTILS.bind(self.peek_401x, self), UTILS.bind(self.poke_4016, self))
self.cpu:add_mappings(0x4017, UTILS.bind(self.peek_401x, self), UTILS.bind(self.apu.poke_4017, self.apu)) -- delegate 4017H to APU
self.pads[1]:reset()
self.pads[2]:reset()
end
function Pads:peek_401x(addr)
self.cpu:update()
return bor(self.pads[addr - 0x4016 + 1]:peek(), 0x40)
end
function Pads:poke_4016(_addr, data)
self.pads[1]:poke(data)
self.pads[2]:poke(data)
end
-- APIs
function Pads:keydown(pad, btn)
self.pads[pad].buttons = bor(self.pads[pad].buttons, lshift(1, btn))
end
function Pads:keyup(pad, btn)
self.pads[pad].buttons = band(self.pads[pad].buttons, bnot(lshift(1, btn)))
end
-- each pad
Pad = UTILS.class()
Pad.A = 0
Pad.B = 1
Pad.SELECT = 2
Pad.START = 3
Pad.UP = 4
Pad.DOWN = 5
Pad.LEFT = 6
Pad.RIGHT = 7
function Pad:initialize()
self:reset()
end
function Pad:reset()
self.strobe = false
self.buttons = 0
self.stream = 0
end
function Pad:poke(data)
local prev = self.strobe
self.strobe = nthBitIsSetInt(data, 0) == 1
if prev and not self.strobe then
self.stream = bxor(lshift(self:poll_state(), 1), -512)
end
end
function Pad:peek()
if self.strobe then
return band(self:poll_state(), 1)
end
self.stream = rshift(self.stream, 1)
return nthBitIsSetInt(self.stream, 0)
end
function Pad:poll_state()
local state = self.buttons
-- prohibit impossible simultaneous keydown (right and left, up and down)
-- 0b00110000
if band(state, 0x30) == 0x30 then
state = band(state, 0xff - 0x30)
end
--0b00111111
if band(state, 0xc0) == 0xc0 then
state = band(state, 0xff - 0xc0)
end
return state
end