Compare commits
1355
Commits
v0.1.3
...
release_0_7_0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea75c7d8c1 | ||
|
|
43095f3198 | ||
|
|
5899f386ba | ||
|
|
6a0f9e82c5 | ||
|
|
c2d551ff5a | ||
|
|
192c7bd927 | ||
|
|
b48a8bb6b1 | ||
|
|
e5484d3391 | ||
|
|
7674e7bf08 | ||
|
|
c747cd1fa2 | ||
|
|
11650e3638 | ||
|
|
a8753c3466 | ||
|
|
c99280bc29 | ||
|
|
905f20b151 | ||
|
|
ff1afc72f2 | ||
|
|
6d82d04a49 | ||
|
|
ad81218e40 | ||
|
|
e4afee9716 | ||
|
|
d0b3e73525 | ||
|
|
e90096763d | ||
|
|
43fb823b5f | ||
|
|
e50e6a2019 | ||
|
|
430116a1d8 | ||
|
|
1026f1336b | ||
|
|
e362b55a32 | ||
|
|
fc9f715de8 | ||
|
|
b359d4e7e4 | ||
|
|
c45b3c0e1b | ||
|
|
e04f40b5aa | ||
|
|
dff293e751 | ||
|
|
b3180cdc01 | ||
|
|
4162503368 | ||
|
|
6bae7ed8b9 | ||
|
|
74c161bd17 | ||
|
|
08e489025b | ||
|
|
c326e0afec | ||
|
|
8aaca4c0b4 | ||
|
|
a4f81979e8 | ||
|
|
89344d5ad7 | ||
|
|
da9b266bb8 | ||
|
|
dccfafc4e1 | ||
|
|
111bfab3b5 | ||
|
|
c7d344af8f | ||
|
|
e1a2849c90 | ||
|
|
e06e5259c3 | ||
|
|
aba9d61e34 | ||
|
|
a6f379881e | ||
|
|
f419b32104 | ||
|
|
8d9bfc2b48 | ||
|
|
c28e951fc7 | ||
|
|
07f4ddbf7e | ||
|
|
5516d670f6 | ||
|
|
cc6f538bf6 | ||
|
|
1fddef4b1b | ||
|
|
6e4255f6a6 | ||
|
|
74ffc7729e | ||
|
|
e3a4e4b643 | ||
|
|
40545f84cf | ||
|
|
d39c0b990a | ||
|
|
2b03a7a5bc | ||
|
|
b8076a748d | ||
|
|
7a674b1363 | ||
|
|
5a246934eb | ||
|
|
b7e2c11dbd | ||
|
|
9d60cac01f | ||
|
|
8e96005d86 | ||
|
|
85d8be6bf2 | ||
|
|
c194feda5f | ||
|
|
7e6c3f34bf | ||
|
|
39d2243955 | ||
|
|
66321a11a4 | ||
|
|
c44644bb96 | ||
|
|
ed91024191 | ||
|
|
b81b3b10aa | ||
|
|
8be1f5c889 | ||
|
|
d827220bbf | ||
|
|
2f275b8f9f | ||
|
|
c3278b7bf0 | ||
|
|
86bd2ca58a | ||
|
|
8422b11337 | ||
|
|
b7a100da9c | ||
|
|
b109f9f867 | ||
|
|
1d6bda3561 | ||
|
|
53cd663792 | ||
|
|
7a0e1f41ce | ||
|
|
4ecc31906d | ||
|
|
4c2e770f37 | ||
|
|
2049521883 | ||
|
|
158142c2c2 | ||
|
|
4f716dc681 | ||
|
|
539827ecf5 | ||
|
|
6eea2b1b81 | ||
|
|
ae200d1062 | ||
|
|
ba68055eab | ||
|
|
6f7e9aec5e | ||
|
|
b756921ad1 | ||
|
|
313132138a | ||
|
|
d057099aa8 | ||
|
|
da4dbf742d | ||
|
|
776f2227f6 | ||
|
|
d785e6be4d | ||
|
|
5e83e8e3e7 | ||
|
|
24b55b9650 | ||
|
|
232ba5ab2e | ||
|
|
b6f479d355 | ||
|
|
1289f43ab1 | ||
|
|
76472292e4 | ||
|
|
5b0753e0d8 | ||
|
|
157777ef3e | ||
|
|
f94daedd27 | ||
|
|
6df700c206 | ||
|
|
b7bcbe9524 | ||
|
|
55754d9ef2 | ||
|
|
afc7df1148 | ||
|
|
713c45faf8 | ||
|
|
0b9dc5e4c3 | ||
|
|
194884dd6f | ||
|
|
91aa5d4975 | ||
|
|
6f2f2b2489 | ||
|
|
1d6e34fd37 | ||
|
|
2623cbaf1a | ||
|
|
a20b552469 | ||
|
|
f512c6fb6e | ||
|
|
3cc6237083 | ||
|
|
c4decf377c | ||
|
|
3988e8977b | ||
|
|
7483750d7d | ||
|
|
61ff6f58e9 | ||
|
|
0bee699e1d | ||
|
|
878d3096d2 | ||
|
|
1a0c3292b5 | ||
|
|
824d560f09 | ||
|
|
580a5e27c6 | ||
|
|
0443eaf6ae | ||
|
|
9117a4ab91 | ||
|
|
441c72ba15 | ||
|
|
bf079a1e70 | ||
|
|
9df217a317 | ||
|
|
92a31b1fff | ||
|
|
0a962c0276 | ||
|
|
d993e0260b | ||
|
|
49b470eb96 | ||
|
|
c9ec1fe474 | ||
|
|
e3086fbf8f | ||
|
|
7c3fc84d86 | ||
|
|
d7ce296f57 | ||
|
|
f8407028b4 | ||
|
|
18fba28c95 | ||
|
|
68016c627b | ||
|
|
9d89330183 | ||
|
|
b8a9e8f133 | ||
|
|
4955a2cd16 | ||
|
|
a8d3431ae9 | ||
|
|
7ff4d2180b | ||
|
|
e88de09993 | ||
|
|
832ed0fa34 | ||
|
|
78573df6b2 | ||
|
|
9f0777ed88 | ||
|
|
6bae70713c | ||
|
|
90f11f95fe | ||
|
|
fa15e030bf | ||
|
|
1ef3868708 | ||
|
|
99c475abf1 | ||
|
|
dfe86665b8 | ||
|
|
a315a14547 | ||
|
|
4fa5d7722d | ||
|
|
64b3ab2439 | ||
|
|
c1135f6152 | ||
|
|
3a5c313852 | ||
|
|
af7bf89b1f | ||
|
|
49be803015 | ||
|
|
8df1cd076c | ||
|
|
bb05683b12 | ||
|
|
662f3c86ec | ||
|
|
5416376efe | ||
|
|
f34c9d6f10 | ||
|
|
e3db7226b4 | ||
|
|
d79284e07a | ||
|
|
9191d4d19f | ||
|
|
0b74ed78ef | ||
|
|
f51589dad5 | ||
|
|
82e41634cd | ||
|
|
bd3fae3df6 | ||
|
|
9230e66e5c | ||
|
|
0523c6b7c5 | ||
|
|
39c61f49f4 | ||
|
|
4d6b6c0aec | ||
|
|
79f91c27ba | ||
|
|
dc9543dc22 | ||
|
|
bef79c34a2 | ||
|
|
83b34f8b57 | ||
|
|
18a6d284ad | ||
|
|
ada89ce61b | ||
|
|
b328f873ab | ||
|
|
ca954f6d90 | ||
|
|
97ed14aead | ||
|
|
d52cf7a64a | ||
|
|
bb2d531499 | ||
|
|
c82913f742 | ||
|
|
6508fe59e0 | ||
|
|
e5843bc816 | ||
|
|
d3c617219b | ||
|
|
735a8fd38e | ||
|
|
1bde465e06 | ||
|
|
a4682cc20a | ||
|
|
839fa98885 | ||
|
|
7c2e623559 | ||
|
|
977d5710e6 | ||
|
|
34444131ad | ||
|
|
8adbc566c9 | ||
|
|
5327cf489f | ||
|
|
e995898b06 | ||
|
|
9df8aa4abb | ||
|
|
ae063a68dc | ||
|
|
8636b5d873 | ||
|
|
664e0f195a | ||
|
|
085339a12b | ||
|
|
abd2c7dc9c | ||
|
|
a8ede8ba8b | ||
|
|
826461bb40 | ||
|
|
02536f8b1f | ||
|
|
06c2f5066e | ||
|
|
bdfaf503dc | ||
|
|
14ce26e755 | ||
|
|
c46878786a | ||
|
|
0fa85d43d4 | ||
|
|
b4ff598727 | ||
|
|
526ff7de82 | ||
|
|
995179f1cc | ||
|
|
0b0babc623 | ||
|
|
20f3228237 | ||
|
|
c27004ec78 | ||
|
|
612458f544 | ||
|
|
80a9d03503 | ||
|
|
75598f6131 | ||
|
|
4f7631cfb5 | ||
|
|
62a46c6168 | ||
|
|
574bbf7b0d | ||
|
|
acae4681ae | ||
|
|
42ad6ae973 | ||
|
|
808c4954bf | ||
|
|
e80cfcfc88 | ||
|
|
9772c73bbc | ||
|
|
de06c511ff | ||
|
|
bc7712a4ac | ||
|
|
4ca0074c8c | ||
|
|
fed4a9adc0 | ||
|
|
3d11d0eb33 | ||
|
|
7b91a17212 | ||
|
|
585d0ed98b | ||
|
|
a483b654b5 | ||
|
|
6a78ece5c5 | ||
|
|
1e8d4eec48 | ||
|
|
88920f344d | ||
|
|
f7cce89882 | ||
|
|
fe2cece60e | ||
|
|
978a66ff73 | ||
|
|
f7806f9467 | ||
|
|
45aea85cef | ||
|
|
c451ee717a | ||
|
|
a07167d3d4 | ||
|
|
02cfb0b4f3 | ||
|
|
3bc2175dcc | ||
|
|
bed5cd8048 | ||
|
|
c169c906a3 | ||
|
|
17444c9c84 | ||
|
|
bf1b938fce | ||
|
|
46d4767d93 | ||
|
|
e35c55fe38 | ||
|
|
acd935ef62 | ||
|
|
c9c0eae84e | ||
|
|
c72a345f5b | ||
|
|
102a52e471 | ||
|
|
c76338c34f | ||
|
|
bf2b84e4a7 | ||
|
|
9f059eca52 | ||
|
|
53360e00e2 | ||
|
|
ef6ff6b71e | ||
|
|
546fa6abd1 | ||
|
|
e875c40a15 | ||
|
|
a98d49b136 | ||
|
|
44a095a77c | ||
|
|
15b6147000 | ||
|
|
9e89a4be8e | ||
|
|
61a8c4ec3a | ||
|
|
9746b15b4e | ||
|
|
7372f88dc1 | ||
|
|
d7382233d8 | ||
|
|
fb065187e4 | ||
|
|
bf71c9d9b6 | ||
|
|
7a987127f4 | ||
|
|
e0fe67aa72 | ||
|
|
f6c958c865 | ||
|
|
9bb34eac8b | ||
|
|
85571bc741 | ||
|
|
8f46820d92 | ||
|
|
dbb2c92142 | ||
|
|
0d1a29f9fc | ||
|
|
b8b5ac6376 | ||
|
|
40b6ecc6bc | ||
|
|
f3ff649d3b | ||
|
|
953569d21b | ||
|
|
a0c4cb4a70 | ||
|
|
188d857911 | ||
|
|
9bc9d1c75a | ||
|
|
8926b517e9 | ||
|
|
6d46bf8ae3 | ||
|
|
032a8c9e35 | ||
|
|
a7dfe172fa | ||
|
|
7fe48483cd | ||
|
|
8e3a9fd280 | ||
|
|
d75d9f6be9 | ||
|
|
769bec7271 | ||
|
|
655aa52a90 | ||
|
|
ba6c23778c | ||
|
|
4b19ec0c2b | ||
|
|
e388818682 | ||
|
|
02d2c54cd3 | ||
|
|
890fa6bebb | ||
|
|
a4c4785b93 | ||
|
|
7993f8bc51 | ||
|
|
8d5f07fa3b | ||
|
|
023fcb9507 | ||
|
|
a5ba1ca608 | ||
|
|
b769d8fef6 | ||
|
|
32ff25bf68 | ||
|
|
f98593103b | ||
|
|
30ca2aab8e | ||
|
|
8a8a608f6e | ||
|
|
d63d307f6e | ||
|
|
487be8a1a7 | ||
|
|
2518bd0dc2 | ||
|
|
e95c8d51c2 | ||
|
|
4971b827da | ||
|
|
420557e898 | ||
|
|
6d5e216de9 | ||
|
|
e8af50a30e | ||
|
|
525d67bcc8 | ||
|
|
d981b88344 | ||
|
|
4a4883b84d | ||
|
|
16c460b154 | ||
|
|
0ecf89aae3 | ||
|
|
28d34b8246 | ||
|
|
3c56521b70 | ||
|
|
096b7ea42b | ||
|
|
d5a8f07c52 | ||
|
|
345fbaa3ca | ||
|
|
1d96905d76 | ||
|
|
99679ececc | ||
|
|
eb45f5fec4 | ||
|
|
b86bda5bb1 | ||
|
|
e2731add29 | ||
|
|
c9a621176e | ||
|
|
cf720db33a | ||
|
|
29e619b1e8 | ||
|
|
6f28fb86c9 | ||
|
|
c94c8d6499 | ||
|
|
01038d2a76 | ||
|
|
03ffbb69a8 | ||
|
|
a3d4af03bb | ||
|
|
9d728e8c4e | ||
|
|
36d54d15e1 | ||
|
|
c4dfa5b7be | ||
|
|
75c2380584 | ||
|
|
7c08dbf325 | ||
|
|
9bf05444b2 | ||
|
|
a3504c87ca | ||
|
|
7143c62c95 | ||
|
|
4e8b5da233 | ||
|
|
c7f746434f | ||
|
|
60e336dbb8 | ||
|
|
8d11df9e5a | ||
|
|
05d5818c5c | ||
|
|
cabf23c380 | ||
|
|
e82d8ade13 | ||
|
|
d2bfb39ad2 | ||
|
|
3611a29c09 | ||
|
|
0f6e3eb211 | ||
|
|
57d1a2b62c | ||
|
|
d5249393ef | ||
|
|
11c0315f9b | ||
|
|
a3fb0cf907 | ||
|
|
ea2384d36e | ||
|
|
e4d4fe3c34 | ||
|
|
5905b2e5fd | ||
|
|
7e2515e87c | ||
|
|
3d2cfdf169 | ||
|
|
6fcfafb742 | ||
|
|
af8ffdfd2b | ||
|
|
b932caba32 | ||
|
|
3eb2619fe5 | ||
|
|
3e11db9a0c | ||
|
|
c6f37d0e4f | ||
|
|
a0a821a4c0 | ||
|
|
49b3b9fb1a | ||
|
|
e7f0ad58c1 | ||
|
|
82c643ff50 | ||
|
|
457831f4bc | ||
|
|
2571929a77 | ||
|
|
81d0912d2d | ||
|
|
d1d9f42119 | ||
|
|
450e18b8b8 | ||
|
|
3db38e87a0 | ||
|
|
379ff53dc9 | ||
|
|
ce93da6ffe | ||
|
|
ee2654ac24 | ||
|
|
354ff22657 | ||
|
|
6b65279459 | ||
|
|
12c28fed49 | ||
|
|
38f0b147a5 | ||
|
|
bec9d989db | ||
|
|
cab84d9844 | ||
|
|
1b039c09fe | ||
|
|
fbf59244b8 | ||
|
|
fa36761d7f | ||
|
|
d08c49aae0 | ||
|
|
fcc941fe20 | ||
|
|
96bcd4f884 | ||
|
|
e3371e62f3 | ||
|
|
82eec0a174 | ||
|
|
933dc6ebc4 | ||
|
|
1e6cae953d | ||
|
|
6d463de2b3 | ||
|
|
bbbc4663d1 | ||
|
|
02e1ec9bc4 | ||
|
|
3df3f6fd7b | ||
|
|
2c6ab8329e | ||
|
|
675376f2b4 | ||
|
|
15a34c6364 | ||
|
|
1bfe856eb2 | ||
|
|
7e71f16f9a | ||
|
|
38a64f9dfe | ||
|
|
e02aa6869e | ||
|
|
7db4eea691 | ||
|
|
b30d4608da | ||
|
|
81ca79911a | ||
|
|
e58d12ed5b | ||
|
|
d549f7d98f | ||
|
|
83fb7adf6c | ||
|
|
1d43a71773 | ||
|
|
ae184e4ab7 | ||
|
|
e6eccb38eb | ||
|
|
9da9886121 | ||
|
|
a1968d7196 | ||
|
|
7f5d44e0ff | ||
|
|
acf5feac80 | ||
|
|
d187d4b218 | ||
|
|
9808745072 | ||
|
|
02ba45c536 | ||
|
|
107db44327 | ||
|
|
dc5d0b3d1b | ||
|
|
7496f5266c | ||
|
|
91d848ebf3 | ||
|
|
e2733d20b2 | ||
|
|
637f6cd735 | ||
|
|
638260eb8e | ||
|
|
30468f786c | ||
|
|
46e50e9d58 | ||
|
|
7c29d0c0cf | ||
|
|
514fb8c10e | ||
|
|
53c862a88e | ||
|
|
b6b8bd1819 | ||
|
|
fd0bbb12c3 | ||
|
|
f2aa58c6f4 | ||
|
|
611493d966 | ||
|
|
e1bb04f740 | ||
|
|
1ade1de223 | ||
|
|
b0bda528c3 | ||
|
|
819e712bfe | ||
|
|
28b9b5af25 | ||
|
|
e9b137c2dd | ||
|
|
95ea3fa19c | ||
|
|
7587cf4401 | ||
|
|
7086749072 | ||
|
|
63b7e03697 | ||
|
|
678f2df60f | ||
|
|
9e57f14d60 | ||
|
|
987c1c6921 | ||
|
|
ffddfee379 | ||
|
|
a2f659ee48 | ||
|
|
d7d02e3c3a | ||
|
|
bb0c6722b6 | ||
|
|
979a54fb20 | ||
|
|
e4f9082b9a | ||
|
|
d95dc32d13 | ||
|
|
53ad66e8c3 | ||
|
|
eba2af633f | ||
|
|
95ce326e5b | ||
|
|
a5448a7de5 | ||
|
|
9231944d96 | ||
|
|
d69d1fa01a | ||
|
|
05efe46eaa | ||
|
|
dbda808a4a | ||
|
|
a049791855 | ||
|
|
ea1c18022e | ||
|
|
516633dc42 | ||
|
|
dc196a57e3 | ||
|
|
2a2820560d | ||
|
|
665656a99b | ||
|
|
658c8bdadc | ||
|
|
5fef40fb4d | ||
|
|
e69390cee7 | ||
|
|
7f647cf68f | ||
|
|
a130a41e69 | ||
|
|
78e127efdb | ||
|
|
ee38b4c813 | ||
|
|
3440557b6d | ||
|
|
d329a6fb22 | ||
|
|
7ebb5e4139 | ||
|
|
57ccbabecb | ||
|
|
eb26db16d7 | ||
|
|
4c8732d71b | ||
|
|
1cc98a5f04 | ||
|
|
de9258a87f | ||
|
|
37f53b4c05 | ||
|
|
a8aa669ba4 | ||
|
|
a5082316e9 | ||
|
|
20ba3ae101 | ||
|
|
4c7634bcb3 | ||
|
|
a21ae81d8a | ||
|
|
aeb3c85f59 | ||
|
|
1f04275ec1 | ||
|
|
4e3e9d0b4d | ||
|
|
358c640721 | ||
|
|
e36f36e15f | ||
|
|
7b17d41e96 | ||
|
|
d6bfa22f72 | ||
|
|
e6e5ad80d8 | ||
|
|
798b0c25cc | ||
|
|
22a56b8a87 | ||
|
|
44bbf73f92 | ||
|
|
00ffa62a91 | ||
|
|
de2200d36d | ||
|
|
710c15a2e9 | ||
|
|
443f1376bc | ||
|
|
a3a91a355b | ||
|
|
ab2572d7ea | ||
|
|
267002cd28 | ||
|
|
63066f4f13 | ||
|
|
caf9a12e9a | ||
|
|
1f62d9383f | ||
|
|
7727994d21 | ||
|
|
8a8696a3c4 | ||
|
|
8998028497 | ||
|
|
a4193c8a4b | ||
|
|
170c6f8705 | ||
|
|
43f493afb4 | ||
|
|
ee22c2f7db | ||
|
|
be3edd9590 | ||
|
|
023fe10d24 | ||
|
|
f66723fab9 | ||
|
|
8cc43feffc | ||
|
|
09a79b4974 | ||
|
|
642012017c | ||
|
|
2444ca413b | ||
|
|
77d4bc349a | ||
|
|
a2a444d6e0 | ||
|
|
4b3686faee | ||
|
|
85c4adf65f | ||
|
|
b69fedff84 | ||
|
|
5fd386f698 | ||
|
|
0ced658970 | ||
|
|
b415a4078d | ||
|
|
f18ac341fe | ||
|
|
63ce9e0a42 | ||
|
|
e1c485be84 | ||
|
|
34e538ae5d | ||
|
|
9995c51ffd | ||
|
|
e58a7c24ac | ||
|
|
777428f2d2 | ||
|
|
5b60212f2a | ||
|
|
a00bad7ed4 | ||
|
|
25b42e9d53 | ||
|
|
8d6c7eb896 | ||
|
|
7bf5be70f7 | ||
|
|
fb9f944458 | ||
|
|
92e873b996 | ||
|
|
9fddaa0c0c | ||
|
|
4a0fb71e67 | ||
|
|
274da6b24b | ||
|
|
15aeac3805 | ||
|
|
28ab0e2edb | ||
|
|
b54ad0498e | ||
|
|
4399059e4d | ||
|
|
43003046cb | ||
|
|
d157e205e9 | ||
|
|
5f21aef2b0 | ||
|
|
829309c70c | ||
|
|
c6a1c22ba6 | ||
|
|
b71e95fc2c | ||
|
|
04a3b84c83 | ||
|
|
86e0c04896 | ||
|
|
5ce276a11a | ||
|
|
5768f5aca6 | ||
|
|
1078f663ae | ||
|
|
0ac32c8375 | ||
|
|
4a9c9687c6 | ||
|
|
73c11f630b | ||
|
|
660de33686 | ||
|
|
69135b5c04 | ||
|
|
69b910399a | ||
|
|
158156d13d | ||
|
|
e63c59cb34 | ||
|
|
13ab5daa86 | ||
|
|
ef792f9ddb | ||
|
|
47cea614a1 | ||
|
|
aedf53821f | ||
|
|
7f5e145212 | ||
|
|
f528bfd45d | ||
|
|
686f3f266b | ||
|
|
5b1214a48e | ||
|
|
3f433d2c87 | ||
|
|
1a084f3d51 | ||
|
|
cd6f11693a | ||
|
|
b939777cec | ||
|
|
d6b86f4d85 | ||
|
|
3a1bc175ea | ||
|
|
db45c29a65 | ||
|
|
7d3505c55a | ||
|
|
fd872598d8 | ||
|
|
8cd0ac2fe1 | ||
|
|
7efa43875d | ||
|
|
44c513c4c9 | ||
|
|
2ee73ac3a8 | ||
|
|
28c3ee3fed | ||
|
|
f929aad6e3 | ||
|
|
e309de25a6 | ||
|
|
10d315a8f2 | ||
|
|
2b64948eb5 | ||
|
|
1f50f8d1d4 | ||
|
|
a1b74fe8fe | ||
|
|
ed5fd2cce4 | ||
|
|
bee3290936 | ||
|
|
beddab753d | ||
|
|
512176dbd8 | ||
|
|
fb6cf1d09c | ||
|
|
66201e2ddf | ||
|
|
ec844b96c0 | ||
|
|
f72e8ff4a6 | ||
|
|
b06eddd39d | ||
|
|
6f51f6b593 | ||
|
|
0294ffb9c8 | ||
|
|
d8d8aa4e2c | ||
|
|
141253b254 | ||
|
|
646be93b4c | ||
|
|
26aa7d72cc | ||
|
|
8e9c4afe70 | ||
|
|
bbc9d34839 | ||
|
|
a8c490cda5 | ||
|
|
4606bb3f06 | ||
|
|
aaaa7df625 | ||
|
|
9d4fb82e3c | ||
|
|
9fafc9eaf0 | ||
|
|
1ef59d0acf | ||
|
|
7fd7b91fac | ||
|
|
d927637dca | ||
|
|
4f2ac23784 | ||
|
|
f658b4db79 | ||
|
|
0c4ad8dc2a | ||
|
|
165c6fc8ce | ||
|
|
2e12669a4c | ||
|
|
52c00a5f15 | ||
|
|
ab6d960ffa | ||
|
|
fbf9eeb34d | ||
|
|
046d6672e2 | ||
|
|
75dfaa1e64 | ||
|
|
1115dde719 | ||
|
|
57206fd42a | ||
|
|
4c27ba27c5 | ||
|
|
ba91cd80d5 | ||
|
|
6f1f31c069 | ||
|
|
5467a72294 | ||
|
|
1b8eb456eb | ||
|
|
d720b93d0b | ||
|
|
eeab3a558f | ||
|
|
658138bcbc | ||
|
|
1190935d98 | ||
|
|
bfbc9133eb | ||
|
|
ad6a4837f8 | ||
|
|
039d3da365 | ||
|
|
a7e61ed446 | ||
|
|
b5075d29a8 | ||
|
|
91caaa612a | ||
|
|
d64477afa1 | ||
|
|
f6bac3809f | ||
|
|
f0cbd3ec9f | ||
|
|
7c1f25b46a | ||
|
|
ee9dbb297d | ||
|
|
c20709aa32 | ||
|
|
92cb7d5423 | ||
|
|
202a456a2b | ||
|
|
3294b949eb | ||
|
|
2aebb3eb2b | ||
|
|
95917e3f57 | ||
|
|
f186904281 | ||
|
|
c5df018e56 | ||
|
|
a541f297a3 | ||
|
|
df475d18d8 | ||
|
|
2f0c934ef9 | ||
|
|
fd836909df | ||
|
|
289e09e77b | ||
|
|
7ae9862745 | ||
|
|
41b9be476c | ||
|
|
52302d7274 | ||
|
|
333190eb97 | ||
|
|
eccabc6ee0 | ||
|
|
bb058620c3 | ||
|
|
9eb153f18f | ||
|
|
aaba6c1516 | ||
|
|
38e205a25b | ||
|
|
e1a237441d | ||
|
|
57c3072482 | ||
|
|
73332e5ccd | ||
|
|
1f673135ac | ||
|
|
aa455485c9 | ||
|
|
9307c4c1d9 | ||
|
|
40c3bac35a | ||
|
|
6eaee46144 | ||
|
|
0f2f112156 | ||
|
|
4721c45750 | ||
|
|
a6e022ad13 | ||
|
|
702c651c4a | ||
|
|
1154e441aa | ||
|
|
e463b581ea | ||
|
|
11d9f695e7 | ||
|
|
0c607d5728 | ||
|
|
67b915a5dd | ||
|
|
bb27c19087 | ||
|
|
8a7ddc38a6 | ||
|
|
b0a21b5334 | ||
|
|
dff38e7b40 | ||
|
|
1f1af9fd7f | ||
|
|
858693c638 | ||
|
|
9b14bb04ca | ||
|
|
c101c49c54 | ||
|
|
4bb2fcc7c9 | ||
|
|
08cea4eef8 | ||
|
|
883da8e219 | ||
|
|
78ebca6e13 | ||
|
|
5e2a644399 | ||
|
|
11774f549e | ||
|
|
d9d849fc5d | ||
|
|
e19e89a5d4 | ||
|
|
f193c7979c | ||
|
|
3035f7ff83 | ||
|
|
4afa64828b | ||
|
|
c45c3d0059 | ||
|
|
baca51faff | ||
|
|
bc51c5c989 | ||
|
|
5069146392 | ||
|
|
59a983b921 | ||
|
|
4e463d8d50 | ||
|
|
94fe4f9fa3 | ||
|
|
0fb48229a7 | ||
|
|
b41a2cd1e4 | ||
|
|
c4b1fcc0f9 | ||
|
|
0f35920cd8 | ||
|
|
7d977de7e1 | ||
|
|
07d898662d | ||
|
|
caed880216 | ||
|
|
b338082b3f | ||
|
|
9dc39cbae3 | ||
|
|
80cabfad16 | ||
|
|
38ca2abc2e | ||
|
|
73bdea1951 | ||
|
|
00af2b2680 | ||
|
|
a735aa3139 | ||
|
|
6b2b6112f8 | ||
|
|
bf08806145 | ||
|
|
f9e92e973f | ||
|
|
8dc75d7535 | ||
|
|
03857e318e | ||
|
|
16f62432c4 | ||
|
|
8b1f24b090 | ||
|
|
b448f2f36c | ||
|
|
97eb5b14dc | ||
|
|
7eee2a509a | ||
|
|
42c3c0cced | ||
|
|
9588b95a08 | ||
|
|
2edcdce334 | ||
|
|
8853f86e1d | ||
|
|
e374bfa35b | ||
|
|
b88e4a9a3b | ||
|
|
537730b956 | ||
|
|
edf779ffcc | ||
|
|
121061dcdf | ||
|
|
83d7396850 | ||
|
|
6b2d3e3c96 | ||
|
|
e98a6e40a9 | ||
|
|
28fbe299c3 | ||
|
|
297d8e6227 | ||
|
|
004bc62c28 | ||
|
|
ed1c0bcb0c | ||
|
|
22a46c55e4 | ||
|
|
ea88812f4f | ||
|
|
58fe2f10f0 | ||
|
|
3a1d9b8bbb | ||
|
|
0e4b179d33 | ||
|
|
77fef8c148 | ||
|
|
59817ccb2c | ||
|
|
bf3e8bf11e | ||
|
|
9acbed0605 | ||
|
|
cf98951b82 | ||
|
|
f72b519c86 | ||
|
|
07ce05eaa9 | ||
|
|
b324e814a9 | ||
|
|
c69810559b | ||
|
|
28c4f361ac | ||
|
|
3d57da2a70 | ||
|
|
00406dff19 | ||
|
|
69de927c6c | ||
|
|
72cbca10e1 | ||
|
|
34f715e754 | ||
|
|
cae61cef89 | ||
|
|
6411cfb6f3 | ||
|
|
898712a85c | ||
|
|
4fa0f5d292 | ||
|
|
1ccde1cb94 | ||
|
|
ad08132319 | ||
|
|
415e561f1d | ||
|
|
ee8b7021da | ||
|
|
625976dac8 | ||
|
|
1ac157da77 | ||
|
|
64a595f26a | ||
|
|
3ad9a57e4f | ||
|
|
7f777bf385 | ||
|
|
56bf1d37a0 | ||
|
|
c92b2e845f | ||
|
|
68e73e391f | ||
|
|
c0637b3794 | ||
|
|
e98c87213e | ||
|
|
3d4b4c0f4b | ||
|
|
8145122b08 | ||
|
|
7399c5a9be | ||
|
|
10f0e412f8 | ||
|
|
4b7aba5173 | ||
|
|
35b66fc4f9 | ||
|
|
13eb76e091 | ||
|
|
3cf1e035ba | ||
|
|
a6b025d37d | ||
|
|
edfcbd9937 | ||
|
|
612b477d48 | ||
|
|
6c9bf8936a | ||
|
|
bb551faa4a | ||
|
|
a136e5a8b1 | ||
|
|
4796f5e9bc | ||
|
|
3504fe171b | ||
|
|
ab1f142ba0 | ||
|
|
4120b61d00 | ||
|
|
7f957d280b | ||
|
|
2be3bc02dd | ||
|
|
c27357906a | ||
|
|
985a19d6d1 | ||
|
|
3f5dcc340c | ||
|
|
f09936ac82 | ||
|
|
d030931173 | ||
|
|
825bd5f8e5 | ||
|
|
bc0b1dc1eb | ||
|
|
630be16f6c | ||
|
|
44a91cae10 | ||
|
|
d575b78aab | ||
|
|
6e44ba7fa2 | ||
|
|
6986f88c3f | ||
|
|
988578886e | ||
|
|
c4c7e3e610 | ||
|
|
b516f85ca8 | ||
|
|
1e4fe7cee2 | ||
|
|
debf7a7c7e | ||
|
|
5a1388b6df | ||
|
|
777aca2fd3 | ||
|
|
69e5bc9068 | ||
|
|
c8135d9af6 | ||
|
|
9e62fd7f26 | ||
|
|
8977f3c107 | ||
|
|
728c9fd5a9 | ||
|
|
bd49793889 | ||
|
|
16d17fdb8e | ||
|
|
4487d0ac49 | ||
|
|
7138fcfbf7 | ||
|
|
c45886db19 | ||
|
|
ffa65c3b70 | ||
|
|
2d603d2216 | ||
|
|
61190b14fc | ||
|
|
9886cc165a | ||
|
|
ce09776be2 | ||
|
|
5be1a8e065 | ||
|
|
09683d3597 | ||
|
|
590b7eed18 | ||
|
|
af5ad10728 | ||
|
|
ac9eb0731a | ||
|
|
9a64fbe4d8 | ||
|
|
efe160c502 | ||
|
|
dc887a4dae | ||
|
|
b9f1950797 | ||
|
|
e16c53fabb | ||
|
|
8351d2d481 | ||
|
|
08785f48b7 | ||
|
|
75c6215f98 | ||
|
|
6b136f9e8f | ||
|
|
2ddbbd10de | ||
|
|
9fa3e85353 | ||
|
|
4390df5107 | ||
|
|
ecd854fdb4 | ||
|
|
513b500f75 | ||
|
|
edf75d592c | ||
|
|
1376847f9f | ||
|
|
17348a7f5e | ||
|
|
4f31916ffb | ||
|
|
943144d91a | ||
|
|
34e01bbf07 | ||
|
|
7f1135b9a4 | ||
|
|
f68dd77007 | ||
|
|
436d8b892a | ||
|
|
dc6f57fd55 | ||
|
|
65262d5738 | ||
|
|
773b93ee06 | ||
|
|
82c7e2a4c6 | ||
|
|
546cdbd77d | ||
|
|
c265508067 | ||
|
|
dd4e27d810 | ||
|
|
a41bc9af8f | ||
|
|
3bfd9da14f | ||
|
|
c573ff6752 | ||
|
|
060366c5ad | ||
|
|
6da41eafc4 | ||
|
|
0124311e00 | ||
|
|
f515528907 | ||
|
|
b453b70bd8 | ||
|
|
0ac4bd56a8 | ||
|
|
3811a291e2 | ||
|
|
3415a4ddb4 | ||
|
|
b7f0f463a5 | ||
|
|
8e682019e3 | ||
|
|
cf495bcf9f | ||
|
|
fb0eaffc6d | ||
|
|
07ad1b93a3 | ||
|
|
d36cd60e6c | ||
|
|
5e809a8095 | ||
|
|
791c2261d6 | ||
|
|
163a7cb620 | ||
|
|
6bb705711b | ||
|
|
4136f33c7e | ||
|
|
6dca2016fc | ||
|
|
678673089d | ||
|
|
28b6751f30 | ||
|
|
79aceca54a | ||
|
|
6a8c397deb | ||
|
|
9cdf757fd5 | ||
|
|
36bdbe5479 | ||
|
|
8004340674 | ||
|
|
a2cc3b2433 | ||
|
|
afa05eb15e | ||
|
|
de5eaa6452 | ||
|
|
7517502475 | ||
|
|
8f2b1fb008 | ||
|
|
891b38e446 | ||
|
|
7dea1da4ae | ||
|
|
4ce900b44c | ||
|
|
f3f2d9be03 | ||
|
|
77729c2445 | ||
|
|
d71b9a8b2f | ||
|
|
181f1558cd | ||
|
|
2750332396 | ||
|
|
f115e911d7 | ||
|
|
3ab493de4c | ||
|
|
3e25f9515a | ||
|
|
246d897f4c | ||
|
|
7e84c2498f | ||
|
|
e670b89e3b | ||
|
|
bd0d90b21d | ||
|
|
1a0636f8d7 | ||
|
|
2e134c9c55 | ||
|
|
5391d80669 | ||
|
|
36b486bb74 | ||
|
|
4ad06a29b2 | ||
|
|
e58143b355 | ||
|
|
1f5476fcce | ||
|
|
461c0471af | ||
|
|
9c3ad57432 | ||
|
|
e748ba4f53 | ||
|
|
b8ed223bfe | ||
|
|
c970a162e7 | ||
|
|
c321f67309 | ||
|
|
7f7f987341 | ||
|
|
aebcb60e55 | ||
|
|
1a18c71b50 | ||
|
|
03a6c5103d | ||
|
|
128b346e0a | ||
|
|
415fa2ea77 | ||
|
|
9951bf39f9 | ||
|
|
8948b5d613 | ||
|
|
5086347239 | ||
|
|
9d4520d0e0 | ||
|
|
5b9f457a89 | ||
|
|
3a4739d651 | ||
|
|
76b62fd001 | ||
|
|
b1f645758a | ||
|
|
bc1b050d85 | ||
|
|
03d5f74aee | ||
|
|
9f05cc34df | ||
|
|
285dc330bd | ||
|
|
baf8ebf01a | ||
|
|
9d16dd550e | ||
|
|
78d6da976c | ||
|
|
dd6ee15c37 | ||
|
|
0db634747e | ||
|
|
6e59c1db89 | ||
|
|
61382a500a | ||
|
|
3a51dee658 | ||
|
|
cc38b844d7 | ||
|
|
c6105c0a04 | ||
|
|
93a40ea926 | ||
|
|
db8d746688 | ||
|
|
997344f303 | ||
|
|
16e9b7de41 | ||
|
|
3486513433 | ||
|
|
0806e3f66f | ||
|
|
39b4da28b3 | ||
|
|
5a67135a0b | ||
|
|
42f1e0e49b | ||
|
|
27c3f2cb9b | ||
|
|
a07cf92aed | ||
|
|
01e3b763a6 | ||
|
|
0ae04d7367 | ||
|
|
ebc054881f | ||
|
|
a20dd508aa | ||
|
|
6180a1818a | ||
|
|
d3eead2eec | ||
|
|
853d6f7a83 | ||
|
|
93ac68bca5 | ||
|
|
1e43adfc89 | ||
|
|
7a3f194486 | ||
|
|
2c0262afa7 | ||
|
|
196ad10903 | ||
|
|
b7dda06abf | ||
|
|
fcf8fcc8e5 | ||
|
|
dbc5594cb6 | ||
|
|
4cbb86e1c4 | ||
|
|
f513a41a3d | ||
|
|
c106152d26 | ||
|
|
facc68be25 | ||
|
|
3ff0631ed9 | ||
|
|
b1ba65744e | ||
|
|
b67d59594e | ||
|
|
2e255c6b9f | ||
|
|
3f33731662 | ||
|
|
d05e66d217 | ||
|
|
2d80ae8987 | ||
|
|
17383a2a2a | ||
|
|
9257a9e49c | ||
|
|
70a194b930 | ||
|
|
2573109866 | ||
|
|
9dfa5b421d | ||
|
|
9da8ba18e6 | ||
|
|
76bc683820 | ||
|
|
3b22c4707d | ||
|
|
96e6e05372 | ||
|
|
e2222c3924 | ||
|
|
31e8f3c894 | ||
|
|
9368caf64d | ||
|
|
38e584a072 | ||
|
|
313aa56710 | ||
|
|
4cbf74b6b8 | ||
|
|
33417e7025 | ||
|
|
4021dab059 | ||
|
|
626df76abb | ||
|
|
abcd5da72e | ||
|
|
97a847bc03 | ||
|
|
ab93bbe2ae | ||
|
|
0f0b726444 | ||
|
|
b92e5a22ec | ||
|
|
17b0018b42 | ||
|
|
39cf780327 | ||
|
|
e89f66eca9 | ||
|
|
b6d78bfa0d | ||
|
|
c33a346edf | ||
|
|
61a2ad53cb | ||
|
|
2c1794c42e | ||
|
|
8a4c1cc411 | ||
|
|
330d0414a5 | ||
|
|
3802ce26a1 | ||
|
|
4abe615b84 | ||
|
|
a412ac572f | ||
|
|
b2b5fb228f | ||
|
|
8f186479e2 | ||
|
|
4c3a88a284 | ||
|
|
d6b4936796 | ||
|
|
9d0fe224f4 | ||
|
|
6e0374f6b5 | ||
|
|
9e5f5284b3 | ||
|
|
c596ed1713 | ||
|
|
91cf4d88fb | ||
|
|
a96fc003bd | ||
|
|
d44b29c21e | ||
|
|
070893f425 | ||
|
|
9621339dca | ||
|
|
ede28208d8 | ||
|
|
7739f36e38 | ||
|
|
f8c8799840 | ||
|
|
43fff2384e | ||
|
|
1b21b62ab4 | ||
|
|
a1516e92b6 | ||
|
|
6fb883e8e3 | ||
|
|
6e295807ac | ||
|
|
f2674e31e0 | ||
|
|
4690764bba | ||
|
|
3c1cf9fa86 | ||
|
|
1f47a9223e | ||
|
|
33e3963e1b | ||
|
|
cd4c3e888a | ||
|
|
7916e2245d | ||
|
|
abd0aaff03 | ||
|
|
c39d5b78f6 | ||
|
|
4d40895f2c | ||
|
|
e477b8b81b | ||
|
|
b118d61e55 | ||
|
|
2f62b397b5 | ||
|
|
907a5b2690 | ||
|
|
0849bf0821 | ||
|
|
305034817d | ||
|
|
ec410fc9ce | ||
|
|
68a7931591 | ||
|
|
c9159e5321 | ||
|
|
8c9b861e74 | ||
|
|
5797fa5d7e | ||
|
|
8ef9a8ece3 | ||
|
|
fc01f7e7f9 | ||
|
|
3b0dca51b0 | ||
|
|
da415d54bf | ||
|
|
b4608c0455 | ||
|
|
d5a0b50c6f | ||
|
|
87858c89ca | ||
|
|
a6f816d697 | ||
|
|
0ad041d476 | ||
|
|
1eb20527c8 | ||
|
|
e3e86d56c4 | ||
|
|
1df912cf9e | ||
|
|
4351832355 | ||
|
|
59faf6d6a6 | ||
|
|
725af7d460 | ||
|
|
a363e34cc5 | ||
|
|
ea041c0e33 | ||
|
|
83479e770d | ||
|
|
e2f2289897 | ||
|
|
844c72eccc | ||
|
|
6b1534cc67 | ||
|
|
e8cd23de30 | ||
|
|
7c2d6a781c | ||
|
|
f1510b2cc3 | ||
|
|
357a94326c | ||
|
|
0824d6fc67 | ||
|
|
6c0372d30b | ||
|
|
92ccca6aa8 | ||
|
|
dd3587f38e | ||
|
|
7d83131cc5 | ||
|
|
66e85a21c7 | ||
|
|
90a9fdae1f | ||
|
|
3fb2ded1d5 | ||
|
|
f76af4b3f3 | ||
|
|
717fc2ad8d | ||
|
|
c05bab779e | ||
|
|
a52c757c9f | ||
|
|
970a87a6bb | ||
|
|
d8bc1fd0ae | ||
|
|
7501267e22 | ||
|
|
13b55754af | ||
|
|
972ddf7840 | ||
|
|
322d0c6657 | ||
|
|
2054396a04 | ||
|
|
039de852ec | ||
|
|
144c345daf | ||
|
|
de83cd02e0 | ||
|
|
6380ab5e26 | ||
|
|
b346ff468e | ||
|
|
5a9fdfec7e | ||
|
|
d19893dab5 | ||
|
|
2521d69883 | ||
|
|
2ab83ea784 | ||
|
|
2437490100 | ||
|
|
e4533c7a8c | ||
|
|
1e5ffbedde | ||
|
|
79638566e5 | ||
|
|
03daf0e361 | ||
|
|
d219f7e7ed | ||
|
|
95cbfc643d | ||
|
|
5898e81684 | ||
|
|
394411ac74 | ||
|
|
ea76864009 | ||
|
|
43ce4dfe9e | ||
|
|
ebc06f87ca | ||
|
|
5bbe92995c | ||
|
|
411bffc41c | ||
|
|
ff1f20a3ee | ||
|
|
9c5d1246c7 | ||
|
|
8dd7cb0621 | ||
|
|
d30329297b | ||
|
|
a95c67907c | ||
|
|
0f533160c7 | ||
|
|
e163bca720 | ||
|
|
8c6939c0b0 | ||
|
|
2d0e9143e2 | ||
|
|
87f4827e1d | ||
|
|
4a585ccb2f | ||
|
|
aa0aa4fa31 | ||
|
|
84fa15d854 | ||
|
|
4add45b4f6 | ||
|
|
4f101ad7ff | ||
|
|
c1db2eb8c3 | ||
|
|
82d19dafe4 | ||
|
|
5286db75a8 | ||
|
|
917f95fd4d | ||
|
|
fe31975692 | ||
|
|
95f7652d65 | ||
|
|
3ec9c4fcc6 | ||
|
|
2f87c60799 | ||
|
|
03bfca946a | ||
|
|
ed2dcdf68e | ||
|
|
4304763ba2 | ||
|
|
c1e42a1397 | ||
|
|
d0a1ffc957 | ||
|
|
df0f11a03b | ||
|
|
2d92f0b8f0 | ||
|
|
aad13cd131 | ||
|
|
a513fe19ac | ||
|
|
f4beb510a4 | ||
|
|
d731dae8e3 | ||
|
|
c9087c2a60 | ||
|
|
14ae3ba7f9 | ||
|
|
5a91de8c90 | ||
|
|
e3b32540df | ||
|
|
a37904dd86 | ||
|
|
cf25629d1e | ||
|
|
0ca790b92e | ||
|
|
d1fe2b2459 | ||
|
|
d4e8164f7e | ||
|
|
08351fb37a | ||
|
|
85e53d4108 | ||
|
|
aa05ae6fec | ||
|
|
1565b7bcd7 | ||
|
|
b409186b8d | ||
|
|
418a97afa1 | ||
|
|
5132455efe | ||
|
|
c0ad5542a8 | ||
|
|
3a27ad0b57 | ||
|
|
2b413144dc | ||
|
|
3ebcc707d2 | ||
|
|
7775e9ecc2 | ||
|
|
03d843ddf2 | ||
|
|
eb51d102bb | ||
|
|
25eb44841e | ||
|
|
b333af0666 | ||
|
|
76c8b7710b | ||
|
|
70e198602b | ||
|
|
206f0fa759 | ||
|
|
fd6ce8f660 | ||
|
|
727d01d4f6 | ||
|
|
ae22853141 | ||
|
|
d418c81eff | ||
|
|
2a29ca73c9 | ||
|
|
54936004fd | ||
|
|
74c95119f2 | ||
|
|
366c1b8bfa | ||
|
|
a993ba85cf | ||
|
|
226c91327d | ||
|
|
b8bf3e3aac | ||
|
|
288426fe3c | ||
|
|
72cc388104 | ||
|
|
378180d8dc | ||
|
|
78c34e98cd | ||
|
|
2792c4f2af | ||
|
|
447db2139a | ||
|
|
564c8f9978 | ||
|
|
c50c0c3fbf | ||
|
|
cabb4d616d | ||
|
|
631271d716 | ||
|
|
9d27abd94f | ||
|
|
148dfc2a8b | ||
|
|
3acace1333 | ||
|
|
0221cfcd71 | ||
|
|
f351077efb | ||
|
|
e84be9dbca | ||
|
|
46ddf5511d | ||
|
|
89e957e7a2 | ||
|
|
982b431579 | ||
|
|
bf7c65bdf4 | ||
|
|
8e5a0667f8 | ||
|
|
19b84f3c35 | ||
|
|
08fc60898b | ||
|
|
082391983e | ||
|
|
504e56ebdc | ||
|
|
455b761956 | ||
|
|
b56dad1c7b | ||
|
|
9ba5695ce5 | ||
|
|
66099dd9af | ||
|
|
b689bc57d6 | ||
|
|
a69d83b60b | ||
|
|
86840ae241 | ||
|
|
3c51961e0e | ||
|
|
d014c98c8d | ||
|
|
a98fd896cd | ||
|
|
d6cdca958e | ||
|
|
efdea7bf19 | ||
|
|
0d3301964d | ||
|
|
fe1e3ce3e9 | ||
|
|
bb326a3749 | ||
|
|
27725c1d74 | ||
|
|
e026db5893 | ||
|
|
43f04c233c | ||
|
|
a8baa8c555 | ||
|
|
728584be27 | ||
|
|
b9adb4a6bc | ||
|
|
ae48a07313 | ||
|
|
956034d7e5 | ||
|
|
6cd9f35b9b | ||
|
|
689f936f7e | ||
|
|
6977fbfd8b | ||
|
|
77e4672d8d | ||
|
|
d34720fd7d | ||
|
|
d9c4d1cc1a | ||
|
|
f644caa51a | ||
|
|
1eb87257da | ||
|
|
32ce63371a | ||
|
|
ec86b0fb3a | ||
|
|
1d346ae63a | ||
|
|
74cd30b811 | ||
|
|
7fb9a24e39 | ||
|
|
afeb6ee377 | ||
|
|
f29042b531 | ||
|
|
09bfb054fb | ||
|
|
2677e107e6 | ||
|
|
66cd58461d | ||
|
|
bb0ebb1f2d | ||
|
|
27c75a9a90 | ||
|
|
d0cd3b8d84 | ||
|
|
9af9eaaa76 | ||
|
|
8c8f42f76c | ||
|
|
51fe68905b | ||
|
|
7fe70ecc56 | ||
|
|
d03cda5923 | ||
|
|
30ac07d4f0 | ||
|
|
8857052055 | ||
|
|
ce11fedc6e | ||
|
|
43d4145a98 | ||
|
|
295defa5f1 | ||
|
|
f801f97e04 | ||
|
|
f48c3dd51a | ||
|
|
62296fe351 | ||
|
|
32f36bcefc | ||
|
|
bc8a22cc30 | ||
|
|
f631ef9bd2 | ||
|
|
f7341ff400 | ||
|
|
fd429f2f6c | ||
|
|
fb3e5849bb | ||
|
|
7854b05654 | ||
|
|
500dab07e8 |
No files matched your search
+21
@@ -0,0 +1,21 @@
|
||||
arm-user
|
||||
armeb-user
|
||||
config-host.*
|
||||
dyngen
|
||||
i386
|
||||
i386-softmmu
|
||||
i386-user
|
||||
ppc-softmmu
|
||||
ppc-user
|
||||
qemu-doc.html
|
||||
qemu-tech.html
|
||||
qemu.1
|
||||
qemu.pod
|
||||
qemu-img.1
|
||||
qemu-img.pod
|
||||
sparc-user
|
||||
qemu-img
|
||||
sparc-softmmu
|
||||
x86_64-softmmu
|
||||
sparc64-user
|
||||
sparc64-softmmu
|
||||
+111
-111
@@ -1,36 +1,14 @@
|
||||
------------------------------------------------------------------------------
|
||||
NOTE:
|
||||
Some code of the Twin package was modified for DOSEMU by the DOSEMU-team.
|
||||
The original is 'Copyright 1997 Willows Software, Inc.' and generously
|
||||
was put under the GNU Library General Public License.
|
||||
( for more information see http://www.willows.com/ )
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
We make use of section 3 of the GNU Library General Public License
|
||||
('...opt to apply the terms of the ordinary GNU General Public License...'),
|
||||
because the resulting product is an integrated part of DOSEMU and
|
||||
can not be considered to be a 'library' in the terms of Library License.
|
||||
|
||||
Therefore, the below GNU LIBRARY GENERAL PUBLIC LICENSE applies only to the
|
||||
_unchanged_ Twin package from Willows. For the DOSEMU-changed parts the normal
|
||||
GNU GENERAL PUBLIC LICENSE applies. This GPL (file COPYING) can be found in
|
||||
the root directory of the DOSEMU distribution.
|
||||
|
||||
The act of transformation to GPL was indicated to the maintainer of the Twin
|
||||
package (Rob Penrose <rob@Canopy.Com>) and he acknowledge agreement.
|
||||
|
||||
Nov. 1 1997, The DOSEMU team.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, USA
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the library GPL. It is
|
||||
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
@@ -39,97 +17,109 @@ freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Library General Public License, applies to some
|
||||
specially designated Free Software Foundation software, and to any
|
||||
other libraries whose authors decide to use it. You can use it for
|
||||
your libraries, too.
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if
|
||||
you distribute copies of the library, or if you modify it.
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link a program with the library, you must provide
|
||||
complete object files to the recipients so that they can relink them
|
||||
with the library, after making changes to the library and recompiling
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
Our method of protecting your rights has two steps: (1) copyright
|
||||
the library, and (2) offer you this license which gives you legal
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
Also, for each distributor's protection, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
library. If the library is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original
|
||||
version, so that any problems introduced by others will not reflect on
|
||||
the original authors' reputations.
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that companies distributing free
|
||||
software will individually obtain patent licenses, thus in effect
|
||||
transforming the program into proprietary software. To prevent this,
|
||||
we have made it clear that any patent must be licensed for everyone's
|
||||
free use or not licensed at all.
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the ordinary
|
||||
GNU General Public License, which was designed for utility programs. This
|
||||
license, the GNU Library General Public License, applies to certain
|
||||
designated libraries. This license is quite different from the ordinary
|
||||
one; be sure to read it in full, and don't assume that anything in it is
|
||||
the same as in the ordinary license.
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
The reason we have a separate public license for some libraries is that
|
||||
they blur the distinction we usually make between modifying or adding to a
|
||||
program and simply using it. Linking a program with a library, without
|
||||
changing the library, is in some sense simply using the library, and is
|
||||
analogous to running a utility program or application program. However, in
|
||||
a textual and legal sense, the linked executable is a combined work, a
|
||||
derivative of the original library, and the ordinary General Public License
|
||||
treats it as such.
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
Because of this blurred distinction, using the ordinary General
|
||||
Public License for libraries did not effectively promote software
|
||||
sharing, because most developers did not use the libraries. We
|
||||
concluded that weaker conditions might promote sharing better.
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
However, unrestricted linking of non-free programs would deprive the
|
||||
users of those programs of all benefit from the free status of the
|
||||
libraries themselves. This Library General Public License is intended to
|
||||
permit developers of non-free programs to use free libraries, while
|
||||
preserving your freedom as a user of such programs to change the free
|
||||
libraries that are incorporated in them. (We have not seen how to achieve
|
||||
this as regards changes in header files, but we have achieved it as regards
|
||||
changes in the actual functions of the Library.) The hope is that this
|
||||
will lead to faster development of free libraries.
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, while the latter only
|
||||
works together with the library.
|
||||
|
||||
Note that it is possible for a library to be covered by the ordinary
|
||||
General Public License rather than by this special one.
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library which
|
||||
contains a notice placed by the copyright holder or other authorized
|
||||
party saying it may be distributed under the terms of this Library
|
||||
General Public License (also called "this License"). Each licensee is
|
||||
addressed as "you".
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
@@ -278,7 +268,7 @@ distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also compile or
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
@@ -305,23 +295,31 @@ of these things:
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Accompany the work with a written offer, valid for at
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
c) If distribution of the work is made by offering access to copy
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
d) Verify that the user has already received a copy of these
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the source code distributed need not include anything that is normally
|
||||
distributed (in either source or binary form) with the major
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
@@ -370,7 +368,7 @@ Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
@@ -413,7 +411,7 @@ excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Library General Public License from time to time.
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
@@ -459,7 +457,7 @@ DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: How to Apply These Terms to Your New Libraries
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
@@ -476,18 +474,18 @@ convey the exclusion of warranty; and each file should have at least the
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@@ -502,3 +500,5 @@ necessary. Here is a sample; alter the names:
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
@@ -1,8 +1,320 @@
|
||||
version 0.7.0:
|
||||
|
||||
- better BIOS translation and HDD geometry auto-detection
|
||||
- user mode networking bug fix
|
||||
- undocumented FPU ops support
|
||||
- Cirrus VGA: support for 1280x1024x[8,15,16] modes
|
||||
- 'pidfile' option
|
||||
- .dmg disk image format support (Johannes Schindelin)
|
||||
- keymaps support (initial patch by Johannes Schindelin)
|
||||
- big endian ARM support (Lennert Buytenhek)
|
||||
- added generic 64 bit target support
|
||||
- x86_64 target support
|
||||
- initial APIC support
|
||||
- MMX/SSE/SSE2/PNI support
|
||||
- PC parallel port support (Mark Jonckheere)
|
||||
- initial SPARC64 support (Blue Swirl)
|
||||
- SPARC target boots Linux (Blue Swirl)
|
||||
- armv5te user mode support (Paul Brook)
|
||||
- ARM VFP support (Paul Brook)
|
||||
- ARM "Angel" semihosting syscalls (Paul Brook)
|
||||
- user mode gdb stub support (Paul Brook)
|
||||
- Samba 3 support
|
||||
- initial Cocoa support (Pierre d'Herbemont)
|
||||
- generic FPU emulation code
|
||||
- Virtual PC read-only disk image support (Alex Beregszaszi)
|
||||
|
||||
version 0.6.1:
|
||||
|
||||
- Mac OS X port (Pierre d'Herbemont)
|
||||
- Virtual console support
|
||||
- Better monitor line edition
|
||||
- New block device layer
|
||||
- New 'qcow' growable disk image support with AES encryption and
|
||||
transparent decompression
|
||||
- VMware 3 and 4 read-only disk image support (untested)
|
||||
- Support for up to 4 serial ports
|
||||
- TFTP server support (Magnus Damm)
|
||||
- Port redirection support in user mode networking
|
||||
- Support for not executable data sections
|
||||
- Compressed loop disk image support (Johannes Schindelin)
|
||||
- Level triggered IRQ fix (aka NE2000 PCI performance fix) (Steve
|
||||
Wormley)
|
||||
- Fixed Fedora Core 2 problems (now you can run qemu without any
|
||||
LD_ASSUME_KERNEL tricks on FC2)
|
||||
- DHCP fix for Windows (accept DHCPREQUEST alone)
|
||||
- SPARC system emulation (Blue Swirl)
|
||||
- Automatic Samba configuration for host file access from Windows.
|
||||
- '-loadvm' and '-full-screen' options
|
||||
- ne2000 savevm support (Johannes Schindelin)
|
||||
- Ctrl-Alt is now the default grab key. Ctrl-Alt-[0-9] switches to
|
||||
the virtual consoles.
|
||||
- BIOS floppy fix for NT4 (Mike Nordell, Derek Fawcus, Volker Ruppert)
|
||||
- Floppy fixes for NT4 and NT5 (Mike Nordell)
|
||||
- NT4 IDE fixes (Ben Pfaf, Mike Nordell)
|
||||
- SDL Audio support and SB16 fixes (malc)
|
||||
- ENTER instruction bug fix (initial patch by Stefan Kisdaroczi)
|
||||
- VGA font change fix
|
||||
- VGA read-only CRTC register fix
|
||||
|
||||
version 0.6.0:
|
||||
|
||||
- minimalist FPU exception support (NetBSD FPU probe fix)
|
||||
- cr0.ET fix (Win95 boot)
|
||||
- *BSD port (Markus Niemisto)
|
||||
- I/O access fix (signaled by Mark Jonckheere)
|
||||
- IDE drives serial number fix (Mike Nordell)
|
||||
- int13 CDROM BIOS fix (aka Solaris x86 install CD fix)
|
||||
- int15, ah=86 BIOS fix (aka Solaris x86 hardware probe hang up fix)
|
||||
- BSR/BSF "undefined behaviour" fix
|
||||
- vmdk2raw: convert VMware disk images to raw images
|
||||
- PCI support
|
||||
- NE2K PCI support
|
||||
- dummy VGA PCI support
|
||||
- VGA font selection fix (Daniel Serpell)
|
||||
- PIC reset fix (Hidemi KAWAI)
|
||||
- PIC spurious irq support (aka Solaris install bug)
|
||||
- added '-localtime' option
|
||||
- Cirrus CL-GD54xx VGA support (initial patch by Makoto Suzuki (suzu))
|
||||
- APM and system shutdown support
|
||||
- Fixed system reset
|
||||
- Support for other PC BIOSes
|
||||
- Initial PowerMac hardware emulation
|
||||
- PowerMac/PREP OpenFirmware compatible BIOS (Jocelyn Mayer)
|
||||
- initial IDE BMDMA support (needed for Darwin x86)
|
||||
- Set the default memory size for PC emulation to 128 MB
|
||||
|
||||
version 0.5.5:
|
||||
|
||||
- SDL full screen support (initial patch by malc)
|
||||
- VGA support on PowerPC PREP
|
||||
- VBE fixes (Matthew Mastracci)
|
||||
- PIT fixes (aka Win98 hardware probe and "VGA slowness" bug)
|
||||
- IDE master only fixes (aka Win98 CD-ROM probe bug)
|
||||
- ARM load/store half word fix (Ulrich Hecht)
|
||||
- FDC fixes for Win98
|
||||
|
||||
version 0.5.4:
|
||||
|
||||
- qemu-fast fixes
|
||||
- BIOS area protection fix (aka EMM386.EXE fix) (Mike Nordell)
|
||||
- keyboard/mouse fix (Mike Nordell)
|
||||
- IDE fixes (Linux did not recognized slave drivers)
|
||||
- VM86 EIP masking fix (aka NT5 install fix) (Mike Nordell)
|
||||
- QEMU can now boot a PowerPC Linux kernel (Jocelyn Mayer)
|
||||
- User mode network stack
|
||||
- imul imm8 fix + 0x82 opcode support (Hidemi KAWAI)
|
||||
- precise self modifying code (aka BeOS install bug)
|
||||
|
||||
version 0.5.3:
|
||||
|
||||
- added Bochs VESA VBE support
|
||||
- VGA memory map mode 3 access fix (OS/2 install fix)
|
||||
- IDE fixes (Jens Axboe)
|
||||
- CPU interrupt fixes
|
||||
- fixed various TLB invalidation cases (NT install)
|
||||
- fixed cr0.WP semantics (XP install)
|
||||
- direct chaining support for SPARC and PowerPC (faster)
|
||||
- ARM NWFPE support (initial patch by Ulrich Hecht)
|
||||
- added specific x86 to x86 translator (close to native performance
|
||||
in qemu-i386 and qemu-fast)
|
||||
- shm syscalls support (Paul McKerras)
|
||||
- added accurate CR0.MP/ME/TS emulation
|
||||
- fixed DMA memory write access (Win95 boot floppy fix)
|
||||
- graphical x86 linux loader
|
||||
- command line monitor
|
||||
- generic removable device support
|
||||
- support of CD-ROM change
|
||||
- multiple network interface support
|
||||
- initial x86-64 host support (Gwenole Beauchesne)
|
||||
- lret to outer priviledge fix (OS/2 install fix)
|
||||
- task switch fixes (SkyOS boot)
|
||||
- VM save/restore commands
|
||||
- new timer API
|
||||
- more precise RTC emulation (periodic timers + time updates)
|
||||
- Win32 port (initial patch by Kazu)
|
||||
|
||||
version 0.5.2:
|
||||
|
||||
- improved soft MMU speed (assembly functions and specializing)
|
||||
- improved multitasking speed by avoiding flushing TBs when
|
||||
switching tasks
|
||||
- improved qemu-fast speed
|
||||
- improved self modifying code handling (big performance gain in
|
||||
softmmu mode).
|
||||
- fixed IO checking
|
||||
- fixed CD-ROM detection (win98 install CD)
|
||||
- fixed addseg real mode bug (GRUB boot fix)
|
||||
- added ROM memory support (win98 boot)
|
||||
- fixed 'call Ev' in case of paging exception
|
||||
- updated the script 'qemu-binfmt-conf.sh' to use QEMU automagically
|
||||
when launching executables for the supported target CPUs.
|
||||
- PowerPC system emulation update (Jocelyn Mayer)
|
||||
- PC floppy emulation and DMA fixes (Jocelyn Mayer)
|
||||
- polled mode for PIC (Jocelyn Mayer)
|
||||
- fixed PTE dirty bit handling
|
||||
- fixed xadd same reg bug
|
||||
- fixed cmpxchg exception safeness
|
||||
- access to virtual memory in gdb stub
|
||||
- task gate and NT flag fixes
|
||||
- eflags optimisation fix for string operations
|
||||
|
||||
version 0.5.1:
|
||||
|
||||
- float access fixes when using soft mmu
|
||||
- PC emulation support on PowerPC
|
||||
- A20 support
|
||||
- IDE CD-ROM emulation
|
||||
- ARM fixes (Ulrich Hecht)
|
||||
- SB16 emulation (malc)
|
||||
- IRET and INT fixes in VM86 mode with IOPL=3
|
||||
- Port I/Os use TSS io map
|
||||
- Full task switching/task gate support
|
||||
- added verr, verw, arpl, fcmovxx
|
||||
- PowerPC target support (Jocelyn Mayer)
|
||||
- Major SPARC target fixes (dynamically linked programs begin to work)
|
||||
|
||||
version 0.5.0:
|
||||
|
||||
- full hardware level VGA emulation
|
||||
- graphical display with SDL
|
||||
- added PS/2 mouse and keyboard emulation
|
||||
- popw (%esp) fix
|
||||
- mov to/from segment data width fix
|
||||
- added real mode support
|
||||
- added Bochs BIOS and LGPL'ed VGA BIOS loader in qemu
|
||||
- m68k host port (Richard Zidlicky)
|
||||
- partial soft MMU support for memory mapped I/Os
|
||||
- multi-target build
|
||||
- fixed: no error code in hardware interrupts
|
||||
- fixed: pop ss, mov ss, x and sti disable hardware irqs for the next insn
|
||||
- correct single stepping thru string operations
|
||||
- preliminary SPARC target support (Thomas M. Ogrisegg)
|
||||
- tun-fd option (Rusty Russell)
|
||||
- automatic IDE geometry detection
|
||||
- renamed 'vl' to qemu[-fast] and user qemu to qemu-{cpu}.
|
||||
- added man page
|
||||
- added full soft mmu mode to launch unpatched OSes.
|
||||
|
||||
version 0.4.3:
|
||||
|
||||
- x86 exception fix in case of nop instruction.
|
||||
- gcc 3.2.2 bug workaround (RedHat 9 fix)
|
||||
- sparc and Alpha host fixes
|
||||
- many ARM target fixes: 'ls' and 'bash' can be launched.
|
||||
|
||||
version 0.4.2:
|
||||
|
||||
- many exception handling fixes (can compile a Linux kernel inside vl)
|
||||
- IDE emulation support
|
||||
- initial GDB stub support
|
||||
- deferred update support for disk images (Rusty Russell)
|
||||
- accept User Mode Linux Copy On Write disk images
|
||||
- SMP kernels can at least be booted
|
||||
|
||||
version 0.4.1:
|
||||
|
||||
- more accurate timer support in vl.
|
||||
- more reliable NE2000 probe in vl.
|
||||
- added 2.5.66 kernel in vl-test.
|
||||
- added VLTMPDIR environment variable in vl.
|
||||
|
||||
version 0.4:
|
||||
|
||||
- initial support for ring 0 x86 processor emulation
|
||||
- fixed signal handling for correct dosemu DPMI emulation
|
||||
- fast x86 MMU emulation with mmap()
|
||||
- fixed popl (%esp) case
|
||||
- Linux kernel can be executed by QEMU with the 'vl' command.
|
||||
|
||||
version 0.3:
|
||||
|
||||
- initial support for ARM emulation
|
||||
- added fnsave, frstor, fnstenv, fldenv FPU instructions
|
||||
- added FPU register save in signal emulation
|
||||
- initial ARM port
|
||||
- Sparc and Alpha ports work on the regression test
|
||||
- generic ioctl number conversion
|
||||
- fixed ioctl type conversion
|
||||
|
||||
version 0.2:
|
||||
|
||||
- PowerPC disassembly and ELF symbols output (Rusty Russell)
|
||||
- flock support (Rusty Russell)
|
||||
- ugetrlimit support (Rusty Russell)
|
||||
- fstat64 fix (Rusty Russell)
|
||||
- initial Alpha port (Falk Hueffner)
|
||||
- initial IA64 port (Matt Wilson)
|
||||
- initial Sparc and Sparc64 port (David S. Miller)
|
||||
- added HLT instruction
|
||||
- LRET instruction fix.
|
||||
- added GPF generation for I/Os.
|
||||
- added INT3 and TF flag support.
|
||||
- SHL instruction C flag fix.
|
||||
- mmap emulation for host page size > 4KB
|
||||
- self-modifying code support
|
||||
- better VM86 support (dosemu works on non trivial programs)
|
||||
- precise exception support (EIP is computed correctly in most cases)
|
||||
- more precise LDT/GDT/IDT emulation
|
||||
- faster segment load in vm86 mode
|
||||
- direct chaining of basic blocks (faster emulation)
|
||||
|
||||
version 0.1.6:
|
||||
|
||||
- automatic library search system. QEMU can now work with unpatched
|
||||
ELF dynamic loader and libc (Rusty Russell).
|
||||
- ISO C warning fixes (Alistair Strachan)
|
||||
- first self-virtualizable version (works only as long as the
|
||||
translation cache is not flushed)
|
||||
- RH9 fixes
|
||||
|
||||
version 0.1.5:
|
||||
|
||||
- ppc64 support + personality() patch (Rusty Russell)
|
||||
- first Alpha CPU patches (Falk Hueffner)
|
||||
- removed bfd.h dependancy
|
||||
- fixed shrd, shld, idivl and divl on PowerPC.
|
||||
- fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC).
|
||||
|
||||
version 0.1.4:
|
||||
|
||||
- more accurate VM86 emulation (can launch small DOS 16 bit
|
||||
executables in wine).
|
||||
- fixed push/pop fs/gs
|
||||
- added iret instruction.
|
||||
- added times() syscall and SIOCATMARK ioctl.
|
||||
|
||||
version 0.1.3:
|
||||
|
||||
- S390 support (Ulrich Weigand)
|
||||
- glibc 2.3.x compile fix (Ulrich Weigand)
|
||||
- socketcall endian fix (Ulrich Weigand)
|
||||
- struct sockaddr endian fix (Ulrich Weigand)
|
||||
- sendmsg/recvmsg endian fix (Ulrich Weigand)
|
||||
- execve endian fix (Ulrich Weigand)
|
||||
- fdset endian fix (Ulrich Weigand)
|
||||
- partial setsockopt syscall support (Ulrich Weigand)
|
||||
- more accurate pushf/popf emulation
|
||||
- first partial vm86() syscall support (can be used with runcom example).
|
||||
- added bound, cmpxchg8b, cpuid instructions
|
||||
- added 16 bit addressing support/override for string operations
|
||||
- poll() fix
|
||||
|
||||
version 0.1.2:
|
||||
|
||||
- compile fixes
|
||||
- xlat instruction
|
||||
- xchg instruction memory lock
|
||||
- added simple vm86 example (not working with QEMU yet). The 54 byte
|
||||
DOS executable 'pi_10.com' program was released by Bertram
|
||||
Felgenhauer (more information at http://www.boo.net/~jasonp/pipage.html).
|
||||
|
||||
version 0.1.1:
|
||||
|
||||
- glibc 2.2 compilation fixes
|
||||
- added -s and -L options
|
||||
- binary distribution of x86 glibc and wine
|
||||
- big endian fixes in ELF loader and getdents.
|
||||
|
||||
version 0.1:
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
The following points clarify the QEMU licenses:
|
||||
|
||||
1) The QEMU virtual CPU core library (libqemu.a) and the QEMU PC
|
||||
system emulator are released under the GNU Lesser General Public
|
||||
License.
|
||||
|
||||
2) The Linux user mode QEMU emulator is released under the GNU General
|
||||
Public License.
|
||||
|
||||
3) QEMU is a trademark of Fabrice Bellard.
|
||||
|
||||
Fabrice Bellard.
|
||||
@@ -1,131 +1,131 @@
|
||||
include config.mak
|
||||
-include config-host.mak
|
||||
|
||||
CFLAGS=-Wall -O2 -g
|
||||
CFLAGS=-Wall -O2 -g -fno-strict-aliasing
|
||||
ifdef CONFIG_DARWIN
|
||||
CFLAGS+= -mdynamic-no-pic
|
||||
endif
|
||||
LDFLAGS=-g
|
||||
LIBS=
|
||||
DEFINES=-DHAVE_BYTESWAP_H
|
||||
DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
TOOLS=qemu-img$(EXESUF)
|
||||
ifdef CONFIG_STATIC
|
||||
LDFLAGS+=-static
|
||||
endif
|
||||
DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1
|
||||
|
||||
ifeq ($(ARCH),i386)
|
||||
CFLAGS+=-fomit-frame-pointer
|
||||
OP_CFLAGS=$(CFLAGS) -mpreferred-stack-boundary=2
|
||||
ifeq ($(GCC_MAJOR),3)
|
||||
OP_CFLAGS+= -falign-functions=0
|
||||
all: dyngen$(EXESUF) $(TOOLS) $(DOCS)
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
ifdef CONFIG_KQEMU
|
||||
ifdef CONFIG_WIN32
|
||||
$(MAKE) -C kqemu -f Makefile.winnt
|
||||
else
|
||||
OP_CFLAGS+= -malign-functions=0
|
||||
$(MAKE) -C kqemu
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ppc)
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
endif
|
||||
qemu-img$(EXESUF): qemu-img.c block.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c
|
||||
$(CC) -DQEMU_TOOL $(CFLAGS) $(LDFLAGS) $(DEFINES) -o $@ $^ -lz $(LIBS)
|
||||
|
||||
ifeq ($(GCC_MAJOR),3)
|
||||
# very important to generate a return at the end of every operation
|
||||
OP_CFLAGS+=-fno-reorder-blocks -fno-optimize-sibling-calls
|
||||
endif
|
||||
|
||||
#########################################################
|
||||
|
||||
DEFINES+=-D_GNU_SOURCE
|
||||
LDSCRIPT=$(ARCH).ld
|
||||
LIBS+=-lm
|
||||
|
||||
# profiling code
|
||||
ifdef TARGET_GPROF
|
||||
LDFLAGS+=-p
|
||||
main.o: CFLAGS+=-p
|
||||
endif
|
||||
|
||||
OBJS= elfload.o main.o syscall.o signal.o
|
||||
SRCS:= $(OBJS:.o=.c)
|
||||
OBJS+= libqemu.a
|
||||
|
||||
LIBOBJS+=thunk.o translate-i386.o op-i386.o exec-i386.o
|
||||
# NOTE: the disassembler code is only needed for debugging
|
||||
LIBOBJS+=i386-dis.o dis-buf.o
|
||||
|
||||
all: qemu qemu-doc.html
|
||||
|
||||
qemu: $(OBJS)
|
||||
$(CC) -Wl,-T,$(LDSCRIPT) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
depend: $(SRCS)
|
||||
$(CC) -MM $(CFLAGS) $^ 1>.depend
|
||||
|
||||
# libqemu
|
||||
|
||||
libqemu.a: $(LIBOBJS)
|
||||
rm -f $@
|
||||
$(AR) rcs $@ $(LIBOBJS)
|
||||
|
||||
dyngen: dyngen.c
|
||||
$(HOST_CC) -O2 -Wall -g $< -o $@
|
||||
|
||||
translate-i386.o: translate-i386.c op-i386.h cpu-i386.h
|
||||
|
||||
op-i386.h: op-i386.o dyngen
|
||||
./dyngen -o $@ $<
|
||||
|
||||
op-i386.o: op-i386.c opreg_template.h ops_template.h
|
||||
$(CC) $(OP_CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
dyngen$(EXESUF): dyngen.c
|
||||
$(HOST_CC) $(CFLAGS) $(DEFINES) -o $@ $^
|
||||
|
||||
clean:
|
||||
# avoid old build problems by removing potentially incorrect old files
|
||||
rm -f config.mak config.h op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
||||
rm -f *.o *.a $(TOOLS) dyngen$(EXESUF) TAGS *.pod *~ */*~
|
||||
$(MAKE) -C tests clean
|
||||
rm -f *.o *.a *~ qemu dyngen TAGS
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
ifdef CONFIG_KQEMU
|
||||
$(MAKE) -C kqemu clean
|
||||
endif
|
||||
|
||||
distclean: clean
|
||||
rm -f config.mak config.h
|
||||
rm -f config-host.mak config-host.h
|
||||
for d in $(TARGET_DIRS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
done
|
||||
|
||||
install: qemu
|
||||
install -m 755 -s qemu $(prefix)/bin
|
||||
KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \
|
||||
ar de en-us fi fr-be hr it lv nl pl ru th \
|
||||
common de-ch es fo fr-ca hu ja mk nl-be pt sl tr
|
||||
|
||||
install: all
|
||||
mkdir -p "$(bindir)"
|
||||
install -m 755 -s $(TOOLS) "$(bindir)"
|
||||
mkdir -p "$(datadir)"
|
||||
install -m 644 pc-bios/bios.bin pc-bios/vgabios.bin \
|
||||
pc-bios/vgabios-cirrus.bin \
|
||||
pc-bios/ppc_rom.bin \
|
||||
pc-bios/proll.elf \
|
||||
pc-bios/linux_boot.bin "$(datadir)"
|
||||
mkdir -p "$(docdir)"
|
||||
install -m 644 qemu-doc.html qemu-tech.html "$(docdir)"
|
||||
ifndef CONFIG_WIN32
|
||||
mkdir -p "$(mandir)/man1"
|
||||
install qemu.1 qemu-img.1 "$(mandir)/man1"
|
||||
mkdir -p "$(datadir)/keymaps"
|
||||
install -m 644 $(addprefix keymaps/,$(KEYMAPS)) "$(datadir)/keymaps"
|
||||
endif
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
ifdef CONFIG_KQEMU
|
||||
cd kqemu ; ./install.sh
|
||||
endif
|
||||
|
||||
# various test targets
|
||||
test speed: qemu
|
||||
make -C tests $@
|
||||
test speed test2: all
|
||||
$(MAKE) -C tests $@
|
||||
|
||||
TAGS:
|
||||
etags *.[ch] i386/*.[ch]
|
||||
etags *.[ch] tests/*.[ch]
|
||||
|
||||
# documentation
|
||||
qemu-doc.html: qemu-doc.texi
|
||||
%.html: %.texi
|
||||
texi2html -monolithic -number $<
|
||||
|
||||
FILES= \
|
||||
README COPYING COPYING.LIB TODO Changelog VERSION \
|
||||
dyngen.c ioctls.h ops_template.h syscall_types.h\
|
||||
Makefile elf.h linux_bin.h segment.h thunk.c\
|
||||
elfload.c main.c signal.c thunk.h\
|
||||
cpu-i386.h qemu.h op-i386.c opc-i386.h syscall-i386.h translate-i386.c\
|
||||
dis-asm.h gen-i386.h op-i386.h syscall.c\
|
||||
dis-buf.c i386-dis.c opreg_template.h syscall_defs.h\
|
||||
i386.ld ppc.ld exec-i386.h exec-i386.c configure \
|
||||
tests/Makefile\
|
||||
tests/test-i386.c tests/test-i386-shift.h tests/test-i386.h\
|
||||
tests/test-i386-muldiv.h tests/test-i386-code16.S\
|
||||
tests/hello.c tests/hello tests/sha1.c \
|
||||
tests/testsig.c tests/testclone.c tests/testthread.c \
|
||||
qemu-doc.texi qemu-doc.html
|
||||
qemu.1: qemu-doc.texi
|
||||
./texi2pod.pl $< qemu.pod
|
||||
pod2man --section=1 --center=" " --release=" " qemu.pod > $@
|
||||
|
||||
FILE=qemu-$(VERSION)
|
||||
qemu-img.1: qemu-img.texi
|
||||
./texi2pod.pl $< qemu-img.pod
|
||||
pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@
|
||||
|
||||
FILE=qemu-$(shell cat VERSION)
|
||||
|
||||
# tar release (use 'make -k tar' on a checkouted tree)
|
||||
tar:
|
||||
rm -rf /tmp/$(FILE)
|
||||
mkdir -p /tmp/$(FILE)
|
||||
cp -P $(FILES) /tmp/$(FILE)
|
||||
( cd /tmp ; tar zcvf ~/$(FILE).tar.gz $(FILE) )
|
||||
cp -r . /tmp/$(FILE)
|
||||
( cd /tmp ; tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS )
|
||||
rm -rf /tmp/$(FILE)
|
||||
|
||||
# generate a binary distribution including the test binary environnment
|
||||
BINPATH=/usr/local/qemu-i386
|
||||
|
||||
# generate a binary distribution
|
||||
tarbin:
|
||||
tar zcvf /tmp/qemu-i386-glibc21.tar.gz \
|
||||
$(BINPATH)/etc $(BINPATH)/lib $(BINPATH)/bin
|
||||
tar zcvf /tmp/qemu-i386-wine.tar.gz \
|
||||
$(BINPATH)/X11R6 $(BINPATH)/wine
|
||||
( cd / ; tar zcvf ~/qemu-$(VERSION)-i386.tar.gz \
|
||||
$(bindir)/qemu \
|
||||
$(bindir)/qemu-system-ppc \
|
||||
$(bindir)/qemu-system-sparc \
|
||||
$(bindir)/qemu-system-x86_64 \
|
||||
$(bindir)/qemu-i386 \
|
||||
$(bindir)/qemu-arm \
|
||||
$(bindir)/qemu-sparc \
|
||||
$(bindir)/qemu-ppc \
|
||||
$(bindir)/qemu-img \
|
||||
$(datadir)/bios.bin \
|
||||
$(datadir)/vgabios.bin \
|
||||
$(datadir)/vgabios-cirrus.bin \
|
||||
$(datadir)/ppc_rom.bin \
|
||||
$(datadir)/proll.elf \
|
||||
$(datadir)/linux_boot.bin \
|
||||
$(docdir)/qemu-doc.html \
|
||||
$(docdir)/qemu-tech.html \
|
||||
$(mandir)/man1/qemu.1 $(mandir)/man1/qemu-img.1 )
|
||||
|
||||
ifneq ($(wildcard .depend),)
|
||||
include .depend
|
||||
|
||||
+471
@@ -0,0 +1,471 @@
|
||||
include config.mak
|
||||
|
||||
TARGET_BASE_ARCH:=$(TARGET_ARCH)
|
||||
ifeq ($(TARGET_ARCH), x86_64)
|
||||
TARGET_BASE_ARCH:=i386
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), sparc64)
|
||||
TARGET_BASE_ARCH:=sparc
|
||||
endif
|
||||
TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)
|
||||
VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/audio
|
||||
DEFINES=-I. -I$(TARGET_PATH) -I$(SRC_PATH)
|
||||
ifdef CONFIG_USER_ONLY
|
||||
VPATH+=:$(SRC_PATH)/linux-user
|
||||
DEFINES+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ARCH)
|
||||
endif
|
||||
CFLAGS=-Wall -O2 -g -fno-strict-aliasing
|
||||
#CFLAGS+=-Werror
|
||||
LDFLAGS=-g
|
||||
LIBS=
|
||||
HELPER_CFLAGS=$(CFLAGS)
|
||||
DYNGEN=../dyngen$(EXESUF)
|
||||
# user emulator name
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
ifeq ($(TARGET_WORDS_BIGENDIAN),yes)
|
||||
QEMU_USER=qemu-armeb
|
||||
else
|
||||
QEMU_USER=qemu-arm
|
||||
endif
|
||||
else
|
||||
QEMU_USER=qemu-$(TARGET_ARCH)
|
||||
endif
|
||||
# system emulator name
|
||||
ifdef CONFIG_SOFTMMU
|
||||
ifeq ($(TARGET_ARCH), i386)
|
||||
QEMU_SYSTEM=qemu$(EXESUF)
|
||||
else
|
||||
QEMU_SYSTEM=qemu-system-$(TARGET_ARCH)$(EXESUF)
|
||||
endif
|
||||
else
|
||||
QEMU_SYSTEM=qemu-fast
|
||||
endif
|
||||
|
||||
ifdef CONFIG_USER_ONLY
|
||||
PROGS=$(QEMU_USER)
|
||||
else
|
||||
ifeq ($(TARGET_ARCH), i386)
|
||||
|
||||
ifeq ($(ARCH), i386)
|
||||
PROGS+=$(QEMU_SYSTEM)
|
||||
ifndef CONFIG_SOFTMMU
|
||||
CONFIG_STATIC=y
|
||||
endif
|
||||
else
|
||||
# the system emulator using soft mmu is portable
|
||||
ifdef CONFIG_SOFTMMU
|
||||
PROGS+=$(QEMU_SYSTEM)
|
||||
endif
|
||||
endif # ARCH != i386
|
||||
|
||||
endif # TARGET_ARCH = i386
|
||||
|
||||
ifeq ($(TARGET_ARCH), x86_64)
|
||||
ifdef CONFIG_SOFTMMU
|
||||
PROGS+=$(QEMU_SYSTEM)
|
||||
endif
|
||||
endif # TARGET_ARCH = x86_64
|
||||
|
||||
ifeq ($(TARGET_ARCH), ppc)
|
||||
|
||||
ifeq ($(ARCH), ppc)
|
||||
PROGS+=$(QEMU_SYSTEM)
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH), i386)
|
||||
ifdef CONFIG_SOFTMMU
|
||||
PROGS+=$(QEMU_SYSTEM)
|
||||
endif
|
||||
endif # ARCH = i386
|
||||
|
||||
ifeq ($(ARCH), x86_64)
|
||||
ifdef CONFIG_SOFTMMU
|
||||
PROGS+=$(QEMU_SYSTEM)
|
||||
endif
|
||||
endif # ARCH = x86_64
|
||||
|
||||
endif # TARGET_ARCH = ppc
|
||||
|
||||
ifeq ($(TARGET_ARCH), sparc)
|
||||
|
||||
ifeq ($(ARCH), ppc)
|
||||
PROGS+=$(QEMU_SYSTEM)
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH), i386)
|
||||
ifdef CONFIG_SOFTMMU
|
||||
PROGS+=$(QEMU_SYSTEM)
|
||||
endif
|
||||
endif # ARCH = i386
|
||||
|
||||
ifeq ($(ARCH), x86_64)
|
||||
ifdef CONFIG_SOFTMMU
|
||||
PROGS+=$(QEMU_SYSTEM)
|
||||
endif
|
||||
endif # ARCH = x86_64
|
||||
|
||||
endif # TARGET_ARCH = sparc
|
||||
|
||||
ifeq ($(TARGET_ARCH), sparc64)
|
||||
ifdef CONFIG_SOFTMMU
|
||||
PROGS+=$(QEMU_SYSTEM)
|
||||
endif
|
||||
endif # TARGET_ARCH = sparc64
|
||||
|
||||
endif # !CONFIG_USER_ONLY
|
||||
|
||||
ifdef CONFIG_STATIC
|
||||
LDFLAGS+=-static
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),i386)
|
||||
CFLAGS+=-fomit-frame-pointer
|
||||
OP_CFLAGS=$(CFLAGS) -mpreferred-stack-boundary=2
|
||||
ifeq ($(HAVE_GCC3_OPTIONS),yes)
|
||||
OP_CFLAGS+= -falign-functions=0 -fno-gcse
|
||||
else
|
||||
OP_CFLAGS+= -malign-functions=0
|
||||
endif
|
||||
|
||||
ifdef TARGET_GPROF
|
||||
USE_I386_LD=y
|
||||
endif
|
||||
ifdef CONFIG_STATIC
|
||||
USE_I386_LD=y
|
||||
endif
|
||||
ifdef USE_I386_LD
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386.ld
|
||||
else
|
||||
# WARNING: this LDFLAGS is _very_ tricky : qemu is an ELF shared object
|
||||
# that the kernel ELF loader considers as an executable. I think this
|
||||
# is the simplest way to make it self virtualizable!
|
||||
LDFLAGS+=-Wl,-shared
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),x86_64)
|
||||
OP_CFLAGS=$(CFLAGS) -falign-functions=0
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/x86_64.ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ppc)
|
||||
CFLAGS+= -D__powerpc__
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/ppc.ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),s390)
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/s390.ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc)
|
||||
CFLAGS+=-m32 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6
|
||||
LDFLAGS+=-m32
|
||||
OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0
|
||||
HELPER_CFLAGS=$(CFLAGS) -ffixed-i0 -mflat
|
||||
# -static is used to avoid g1/g3 usage by the dynamic linker
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/sparc.ld -static
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc64)
|
||||
CFLAGS+=-m64 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6
|
||||
LDFLAGS+=-m64
|
||||
OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),alpha)
|
||||
# -msmall-data is not used because we want two-instruction relocations
|
||||
# for the constant constructions
|
||||
OP_CFLAGS=-Wall -O2 -g
|
||||
# Ensure there's only a single GP
|
||||
CFLAGS += -msmall-data
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/alpha.ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
CFLAGS += -mno-sdata
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),arm)
|
||||
OP_CFLAGS=$(CFLAGS) -mno-sched-prolog -fno-omit-frame-pointer
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/arm.ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),m68k)
|
||||
OP_CFLAGS=$(CFLAGS) -fomit-frame-pointer
|
||||
LDFLAGS+=-Wl,-T,m68k.ld
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_GCC3_OPTIONS),yes)
|
||||
# very important to generate a return at the end of every operation
|
||||
OP_CFLAGS+=-fno-reorder-blocks -fno-optimize-sibling-calls
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_DARWIN),yes)
|
||||
OP_CFLAGS+= -mdynamic-no-pic
|
||||
LIBS+=-lmx
|
||||
endif
|
||||
|
||||
#########################################################
|
||||
|
||||
DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
LIBS+=-lm
|
||||
ifndef CONFIG_USER_ONLY
|
||||
LIBS+=-lz
|
||||
endif
|
||||
ifdef CONFIG_WIN32
|
||||
LIBS+=-lwinmm -lws2_32 -liphlpapi
|
||||
endif
|
||||
|
||||
# profiling code
|
||||
ifdef TARGET_GPROF
|
||||
LDFLAGS+=-p
|
||||
main.o: CFLAGS+=-p
|
||||
endif
|
||||
|
||||
OBJS= elfload.o main.o syscall.o mmap.o signal.o path.o osdep.o thunk.o
|
||||
ifeq ($(TARGET_ARCH), i386)
|
||||
OBJS+= vm86.o
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), arm)
|
||||
OBJS+=nwfpe/fpa11.o nwfpe/fpa11_cpdo.o \
|
||||
nwfpe/fpa11_cpdt.o nwfpe/fpa11_cprt.o nwfpe/fpopcode.o nwfpe/single_cpdo.o \
|
||||
nwfpe/double_cpdo.o nwfpe/extended_cpdo.o arm-semi.o
|
||||
endif
|
||||
SRCS:= $(OBJS:.o=.c)
|
||||
OBJS+= libqemu.a
|
||||
|
||||
# cpu emulator library
|
||||
LIBOBJS=exec.o kqemu.o translate-op.o translate-all.o cpu-exec.o\
|
||||
translate.o op.o
|
||||
ifdef CONFIG_SOFTFLOAT
|
||||
LIBOBJS+=fpu/softfloat.o
|
||||
else
|
||||
LIBOBJS+=fpu/softfloat-native.o
|
||||
endif
|
||||
DEFINES+=-I$(SRC_PATH)/fpu
|
||||
|
||||
ifeq ($(TARGET_ARCH), i386)
|
||||
LIBOBJS+=helper.o helper2.o
|
||||
ifeq ($(ARCH), i386)
|
||||
LIBOBJS+=translate-copy.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), x86_64)
|
||||
LIBOBJS+=helper.o helper2.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), ppc)
|
||||
LIBOBJS+= op_helper.o helper.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||
LIBOBJS+= op_helper.o helper.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), arm)
|
||||
LIBOBJS+= op_helper.o
|
||||
endif
|
||||
|
||||
# NOTE: the disassembler code is only needed for debugging
|
||||
LIBOBJS+=disas.o
|
||||
ifeq ($(findstring i386, $(TARGET_ARCH) $(ARCH)),i386)
|
||||
USE_I386_DIS=y
|
||||
endif
|
||||
ifeq ($(findstring x86_64, $(TARGET_ARCH) $(ARCH)),x86_64)
|
||||
USE_I386_DIS=y
|
||||
endif
|
||||
ifdef USE_I386_DIS
|
||||
LIBOBJS+=i386-dis.o
|
||||
endif
|
||||
ifeq ($(findstring alpha, $(TARGET_ARCH) $(ARCH)),alpha)
|
||||
LIBOBJS+=alpha-dis.o
|
||||
endif
|
||||
ifeq ($(findstring ppc, $(TARGET_ARCH) $(ARCH)),ppc)
|
||||
LIBOBJS+=ppc-dis.o
|
||||
endif
|
||||
ifeq ($(findstring sparc, $(TARGET_BASE_ARCH) $(ARCH)),sparc)
|
||||
LIBOBJS+=sparc-dis.o
|
||||
endif
|
||||
ifeq ($(findstring arm, $(TARGET_ARCH) $(ARCH)),arm)
|
||||
LIBOBJS+=arm-dis.o
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
OBJS += ia64-syscall.o
|
||||
endif
|
||||
ifdef CONFIG_GDBSTUB
|
||||
OBJS+=gdbstub.o
|
||||
endif
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
$(QEMU_USER): $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
ifeq ($(ARCH),alpha)
|
||||
# Mark as 32 bit binary, i. e. it will be mapped into the low 31 bit of
|
||||
# the address space (31 bit so sign extending doesn't matter)
|
||||
echo -ne '\001\000\000\000' | dd of=qemu bs=1 seek=48 count=4 conv=notrunc
|
||||
endif
|
||||
|
||||
# must use static linking to avoid leaving stuff in virtual address space
|
||||
VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o
|
||||
VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o
|
||||
|
||||
SOUND_HW = sb16.o
|
||||
AUDIODRV = audio.o noaudio.o wavaudio.o
|
||||
ifdef CONFIG_SDL
|
||||
AUDIODRV += sdlaudio.o
|
||||
endif
|
||||
ifdef CONFIG_OSS
|
||||
AUDIODRV += ossaudio.o
|
||||
endif
|
||||
|
||||
pc.o: DEFINES := -DUSE_SB16 $(DEFINES)
|
||||
|
||||
ifdef CONFIG_ADLIB
|
||||
SOUND_HW += fmopl.o adlib.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_FMOD
|
||||
AUDIODRV += fmodaudio.o
|
||||
audio.o fmodaudio.o: DEFINES := -I$(CONFIG_FMOD_INC) $(DEFINES)
|
||||
LIBS += $(CONFIG_FMOD_LIB)
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), i386)
|
||||
# Hardware support
|
||||
VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
|
||||
VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o
|
||||
VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), ppc)
|
||||
VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
|
||||
VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o
|
||||
VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o mixeng.o
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||
VL_OBJS+= sun4m.o tcx.o lance.o iommu.o m48t08.o magic-load.o slavio_intctl.o slavio_timer.o slavio_serial.o fdc.o esp.o
|
||||
endif
|
||||
ifdef CONFIG_GDBSTUB
|
||||
VL_OBJS+=gdbstub.o
|
||||
endif
|
||||
ifdef CONFIG_SDL
|
||||
VL_OBJS+=sdl.o
|
||||
endif
|
||||
ifdef CONFIG_COCOA
|
||||
VL_OBJS+=cocoa.o
|
||||
COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa
|
||||
endif
|
||||
ifdef CONFIG_SLIRP
|
||||
DEFINES+=-I$(SRC_PATH)/slirp
|
||||
SLIRP_OBJS=cksum.o if.o ip_icmp.o ip_input.o ip_output.o \
|
||||
slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o \
|
||||
tcp_subr.o tcp_timer.o udp.o bootp.o debug.o tftp.o
|
||||
VL_OBJS+=$(addprefix slirp/, $(SLIRP_OBJS))
|
||||
endif
|
||||
|
||||
VL_LDFLAGS=
|
||||
# specific flags are needed for non soft mmu emulator
|
||||
ifdef CONFIG_STATIC
|
||||
VL_LDFLAGS+=-static
|
||||
endif
|
||||
ifndef CONFIG_SOFTMMU
|
||||
VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386-vl.ld
|
||||
endif
|
||||
ifndef CONFIG_DARWIN
|
||||
ifndef CONFIG_WIN32
|
||||
VL_LIBS=-lutil
|
||||
endif
|
||||
endif
|
||||
ifdef TARGET_GPROF
|
||||
vl.o: CFLAGS+=-p
|
||||
VL_LDFLAGS+=-p
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
VL_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
|
||||
endif
|
||||
|
||||
$(QEMU_SYSTEM): $(VL_OBJS) libqemu.a
|
||||
$(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS)
|
||||
|
||||
cocoa.o: cocoa.m
|
||||
$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
sdl.o: sdl.c keymaps.c sdl_keysym.h
|
||||
$(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
|
||||
|
||||
sdlaudio.o: sdlaudio.c
|
||||
$(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
|
||||
|
||||
depend: $(SRCS)
|
||||
$(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend
|
||||
|
||||
# libqemu
|
||||
|
||||
libqemu.a: $(LIBOBJS)
|
||||
rm -f $@
|
||||
$(AR) rcs $@ $(LIBOBJS)
|
||||
|
||||
translate.o: translate.c gen-op.h opc.h cpu.h
|
||||
|
||||
translate-all.o: translate-all.c opc.h cpu.h
|
||||
|
||||
translate-op.o: translate-all.c op.h opc.h cpu.h
|
||||
|
||||
op.h: op.o $(DYNGEN)
|
||||
$(DYNGEN) -o $@ $<
|
||||
|
||||
opc.h: op.o $(DYNGEN)
|
||||
$(DYNGEN) -c -o $@ $<
|
||||
|
||||
gen-op.h: op.o $(DYNGEN)
|
||||
$(DYNGEN) -g -o $@ $<
|
||||
|
||||
op.o: op.c
|
||||
$(CC) $(OP_CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
helper.o: helper.c
|
||||
$(CC) $(HELPER_CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), i386)
|
||||
op.o: op.c opreg_template.h ops_template.h ops_template_mem.h ops_mem.h ops_sse.h
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), arm)
|
||||
op.o: op.c op_template.h
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||
op.o: op.c op_template.h op_mem.h
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), ppc)
|
||||
op.o: op.c op_template.h op_mem.h
|
||||
op_helper.o: op_helper_mem.h
|
||||
endif
|
||||
|
||||
mixeng.o: mixeng.c mixeng.h mixeng_template.h
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
%.o: %.S
|
||||
$(CC) $(DEFINES) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *.o *.a *~ $(PROGS) gen-op.h opc.h op.h nwfpe/*.o slirp/*.o fpu/*.o
|
||||
|
||||
install: all
|
||||
ifneq ($(PROGS),)
|
||||
install -m 755 -s $(PROGS) "$(bindir)"
|
||||
endif
|
||||
|
||||
ifneq ($(wildcard .depend),)
|
||||
include .depend
|
||||
endif
|
||||
@@ -1,45 +1,3 @@
|
||||
The QEMU x86 emulator
|
||||
---------------------
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
Type
|
||||
|
||||
./configure
|
||||
make
|
||||
|
||||
to build qemu and libqemu.a.
|
||||
|
||||
Type
|
||||
|
||||
make install
|
||||
|
||||
to install QEMU in /usr/local/bin
|
||||
|
||||
* On x86 you should be able to launch any program by using the
|
||||
libraries installed on your PC. For example:
|
||||
|
||||
./qemu -L / /bin/ls
|
||||
|
||||
* On non x86 CPUs, you need first to download at least an x86 glibc
|
||||
(qemu-i386-glibc21.tar.gz on the qemu web page). Ensure that
|
||||
LD_LIBRARY_PATH is not set:
|
||||
|
||||
unset LD_LIBRARY_PATH
|
||||
|
||||
Then you can launch the precompiled 'ls' x86 executable:
|
||||
|
||||
./qemu /usr/local/qemu-i386/bin/ls
|
||||
|
||||
You can look at /usr/local/qemu-i386/bin/qemu-conf.sh so that QEMU is
|
||||
automatically launched by the Linux kernel when you try to launch x86
|
||||
executables.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Read the documentation in qemu-doc.html.
|
||||
|
||||
|
||||
Fabrice Bellard.
|
||||
@@ -0,0 +1,16 @@
|
||||
Information about the various packages used to build the current qemu
|
||||
x86 binary distribution:
|
||||
|
||||
* gcc 2.95.2 was used for the build. A glibc 2.1.3 Debian distribution
|
||||
was used to get most of the binary packages.
|
||||
|
||||
* wine-20020411 tarball
|
||||
|
||||
./configure --prefix=/usr/local/wine-i386
|
||||
|
||||
All exe and libs were stripped. Some compile time tools and the
|
||||
includes were deleted.
|
||||
|
||||
* ldconfig was launched to build the library links:
|
||||
|
||||
qemu-i386 /usr/gnemul/qemu-i386/bin/ldconfig-i386 -C /usr/gnemul/qemu-i386/etc/ld.so.cache
|
||||
@@ -1,10 +1,57 @@
|
||||
- optimize translated cache chaining (DLL PLT-like system)
|
||||
short term:
|
||||
----------
|
||||
- debug option in 'configure' script + disable -fomit-frame-pointer
|
||||
- Precise VGA timings for old games/demos (malc patch)
|
||||
- merge PIC spurious interrupt patch
|
||||
- merge Solaris patch
|
||||
- warning for OS/2: must not use 128 MB memory (merge bochs cmos patch ?)
|
||||
- config file (at least for windows/Mac OS X)
|
||||
- commit message if execution of code in IO memory
|
||||
- update doc: PCI infos.
|
||||
- VNC patch + Synaptic patch.
|
||||
- basic VGA optimizations
|
||||
- physical memory cache (reduce qemu-fast address space size to about 32 MB)
|
||||
- better code fetch (different exception handling + CS.limit support)
|
||||
- do not resize vga if invalid size.
|
||||
- avoid looping if only exceptions
|
||||
- cycle counter for all archs
|
||||
- TLB code protection support for PPC
|
||||
- see openMosix Doc
|
||||
- disable SMC handling for ARM/SPARC/PPC (not finished)
|
||||
- see undefined flags for BTx insn
|
||||
- user/kernel PUSHL/POPL in helper.c
|
||||
- keyboard output buffer filling timing emulation
|
||||
- return UD exception if LOCK prefix incorrectly used
|
||||
- test ldt limit < 7 ?
|
||||
- tests for each target CPU
|
||||
- fix CCOP optimisation
|
||||
- fix all remaining thread lock issues (must put TBs in a specific invalid
|
||||
state, find a solution for tb_flush()).
|
||||
- fix arm fpu rounding (at least for float->integer conversions)
|
||||
- SMP support
|
||||
|
||||
ppc specific:
|
||||
------------
|
||||
- TLB invalidate not needed if msr_pr changes
|
||||
- SPR_ENCODE() not useful
|
||||
- enable shift optimizations ?
|
||||
|
||||
lower priority:
|
||||
--------------
|
||||
- more friendly BIOS (logo)
|
||||
- int15 ah=86: use better timing
|
||||
- suppress shift_mem ops
|
||||
- fix some 16 bit sp push/pop overflow (pusha/popa, lcall lret)
|
||||
- optimize FPU operations (evaluate x87 stack pointer statically)
|
||||
- add IPC syscalls
|
||||
- use -msoft-float on ARM
|
||||
- use kernel traps for unaligned accesses on ARM ?
|
||||
- handle rare page fault cases (in particular if page fault in helpers or
|
||||
in syscall emulation code).
|
||||
- fix thread stack freeing (use kernel 2.5.x CLONE_CHILD_CLEARTID)
|
||||
- more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit
|
||||
issues, fix 16 bit uid issues)
|
||||
- finish signal handing (fp87 state, more siginfo conversions)
|
||||
- verify thread support (clone() and various locks)
|
||||
- vm86 syscall support
|
||||
- overrides/16bit for string ops
|
||||
- make it self runnable (use same trick as ld.so : include its own relocator and libc)
|
||||
- improved 16 bit support
|
||||
- fix FPU exceptions (in particular: gen_op_fpush not before mem load)
|
||||
- use page_unprotect_range in every suitable syscall to handle all
|
||||
cases of self modifying code.
|
||||
- use gcc as a backend to generate better code (easy to do by using
|
||||
op-i386.c operations as local inline functions).
|
||||
@@ -0,0 +1,431 @@
|
||||
/* a.out.h
|
||||
|
||||
Copyright 1997, 1998, 1999, 2001 Red Hat, Inc.
|
||||
|
||||
This file is part of Cygwin.
|
||||
|
||||
This software is a copyrighted work licensed under the terms of the
|
||||
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
||||
details. */
|
||||
|
||||
#ifndef _A_OUT_H_
|
||||
#define _A_OUT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define COFF_IMAGE_WITH_PE
|
||||
#define COFF_LONG_SECTION_NAMES
|
||||
|
||||
/*** coff information for Intel 386/486. */
|
||||
|
||||
|
||||
/********************** FILE HEADER **********************/
|
||||
|
||||
struct external_filehdr {
|
||||
short f_magic; /* magic number */
|
||||
short f_nscns; /* number of sections */
|
||||
unsigned long f_timdat; /* time & date stamp */
|
||||
unsigned long f_symptr; /* file pointer to symtab */
|
||||
unsigned long f_nsyms; /* number of symtab entries */
|
||||
short f_opthdr; /* sizeof(optional hdr) */
|
||||
short f_flags; /* flags */
|
||||
};
|
||||
|
||||
/* Bits for f_flags:
|
||||
* F_RELFLG relocation info stripped from file
|
||||
* F_EXEC file is executable (no unresolved external references)
|
||||
* F_LNNO line numbers stripped from file
|
||||
* F_LSYMS local symbols stripped from file
|
||||
* F_AR32WR file has byte ordering of an AR32WR machine (e.g. vax)
|
||||
*/
|
||||
|
||||
#define F_RELFLG (0x0001)
|
||||
#define F_EXEC (0x0002)
|
||||
#define F_LNNO (0x0004)
|
||||
#define F_LSYMS (0x0008)
|
||||
|
||||
|
||||
|
||||
#define I386MAGIC 0x14c
|
||||
#define I386PTXMAGIC 0x154
|
||||
#define I386AIXMAGIC 0x175
|
||||
|
||||
/* This is Lynx's all-platform magic number for executables. */
|
||||
|
||||
#define LYNXCOFFMAGIC 0415
|
||||
|
||||
#define I386BADMAG(x) (((x).f_magic != I386MAGIC) \
|
||||
&& (x).f_magic != I386AIXMAGIC \
|
||||
&& (x).f_magic != I386PTXMAGIC \
|
||||
&& (x).f_magic != LYNXCOFFMAGIC)
|
||||
|
||||
#define FILHDR struct external_filehdr
|
||||
#define FILHSZ 20
|
||||
|
||||
|
||||
/********************** AOUT "OPTIONAL HEADER"=
|
||||
**********************/
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned short magic; /* type of file */
|
||||
unsigned short vstamp; /* version stamp */
|
||||
unsigned long tsize; /* text size in bytes, padded to FW bdry*/
|
||||
unsigned long dsize; /* initialized data " " */
|
||||
unsigned long bsize; /* uninitialized data " " */
|
||||
unsigned long entry; /* entry pt. */
|
||||
unsigned long text_start; /* base of text used for this file */
|
||||
unsigned long data_start; /* base of data used for this file=
|
||||
*/
|
||||
}
|
||||
AOUTHDR;
|
||||
|
||||
#define AOUTSZ 28
|
||||
#define AOUTHDRSZ 28
|
||||
|
||||
#define OMAGIC 0404 /* object files, eg as output */
|
||||
#define ZMAGIC 0413 /* demand load format, eg normal ld output */
|
||||
#define STMAGIC 0401 /* target shlib */
|
||||
#define SHMAGIC 0443 /* host shlib */
|
||||
|
||||
|
||||
/* define some NT default values */
|
||||
/* #define NT_IMAGE_BASE 0x400000 moved to internal.h */
|
||||
#define NT_SECTION_ALIGNMENT 0x1000
|
||||
#define NT_FILE_ALIGNMENT 0x200
|
||||
#define NT_DEF_RESERVE 0x100000
|
||||
#define NT_DEF_COMMIT 0x1000
|
||||
|
||||
/********************** SECTION HEADER **********************/
|
||||
|
||||
|
||||
struct external_scnhdr {
|
||||
char s_name[8]; /* section name */
|
||||
unsigned long s_paddr; /* physical address, offset
|
||||
of last addr in scn */
|
||||
unsigned long s_vaddr; /* virtual address */
|
||||
unsigned long s_size; /* section size */
|
||||
unsigned long s_scnptr; /* file ptr to raw data for section */
|
||||
unsigned long s_relptr; /* file ptr to relocation */
|
||||
unsigned long s_lnnoptr; /* file ptr to line numbers */
|
||||
unsigned short s_nreloc; /* number of relocation entries */
|
||||
unsigned short s_nlnno; /* number of line number entries*/
|
||||
unsigned long s_flags; /* flags */
|
||||
};
|
||||
|
||||
#define SCNHDR struct external_scnhdr
|
||||
#define SCNHSZ 40
|
||||
|
||||
/*
|
||||
* names of "special" sections
|
||||
*/
|
||||
#define _TEXT ".text"
|
||||
#define _DATA ".data"
|
||||
#define _BSS ".bss"
|
||||
#define _COMMENT ".comment"
|
||||
#define _LIB ".lib"
|
||||
|
||||
/********************** LINE NUMBERS **********************/
|
||||
|
||||
/* 1 line number entry for every "breakpointable" source line in a section.
|
||||
* Line numbers are grouped on a per function basis; first entry in a function
|
||||
* grouping will have l_lnno = 0 and in place of physical address will be the
|
||||
* symbol table index of the function name.
|
||||
*/
|
||||
struct external_lineno {
|
||||
union {
|
||||
unsigned long l_symndx; /* function name symbol index, iff l_lnno 0 */
|
||||
unsigned long l_paddr; /* (physical) address of line number */
|
||||
} l_addr;
|
||||
unsigned short l_lnno; /* line number */
|
||||
};
|
||||
|
||||
#define LINENO struct external_lineno
|
||||
#define LINESZ 6
|
||||
|
||||
/********************** SYMBOLS **********************/
|
||||
|
||||
#define E_SYMNMLEN 8 /* # characters in a symbol name */
|
||||
#define E_FILNMLEN 14 /* # characters in a file name */
|
||||
#define E_DIMNUM 4 /* # array dimensions in auxiliary entry */
|
||||
|
||||
struct __attribute__((packed)) external_syment
|
||||
{
|
||||
union {
|
||||
char e_name[E_SYMNMLEN];
|
||||
struct {
|
||||
unsigned long e_zeroes;
|
||||
unsigned long e_offset;
|
||||
} e;
|
||||
} e;
|
||||
unsigned long e_value;
|
||||
unsigned short e_scnum;
|
||||
unsigned short e_type;
|
||||
char e_sclass[1];
|
||||
char e_numaux[1];
|
||||
};
|
||||
|
||||
#define N_BTMASK (0xf)
|
||||
#define N_TMASK (0x30)
|
||||
#define N_BTSHFT (4)
|
||||
#define N_TSHIFT (2)
|
||||
|
||||
union external_auxent {
|
||||
struct {
|
||||
unsigned long x_tagndx; /* str, un, or enum tag indx */
|
||||
union {
|
||||
struct {
|
||||
unsigned short x_lnno; /* declaration line number */
|
||||
unsigned short x_size; /* str/union/array size */
|
||||
} x_lnsz;
|
||||
unsigned long x_fsize; /* size of function */
|
||||
} x_misc;
|
||||
union {
|
||||
struct { /* if ISFCN, tag, or .bb */
|
||||
unsigned long x_lnnoptr;/* ptr to fcn line # */
|
||||
unsigned long x_endndx; /* entry ndx past block end */
|
||||
} x_fcn;
|
||||
struct { /* if ISARY, up to 4 dimen. */
|
||||
char x_dimen[E_DIMNUM][2];
|
||||
} x_ary;
|
||||
} x_fcnary;
|
||||
unsigned short x_tvndx; /* tv index */
|
||||
} x_sym;
|
||||
|
||||
union {
|
||||
char x_fname[E_FILNMLEN];
|
||||
struct {
|
||||
unsigned long x_zeroes;
|
||||
unsigned long x_offset;
|
||||
} x_n;
|
||||
} x_file;
|
||||
|
||||
struct {
|
||||
unsigned long x_scnlen; /* section length */
|
||||
unsigned short x_nreloc; /* # relocation entries */
|
||||
unsigned short x_nlinno; /* # line numbers */
|
||||
unsigned long x_checksum; /* section COMDAT checksum */
|
||||
unsigned short x_associated;/* COMDAT associated section index */
|
||||
char x_comdat[1]; /* COMDAT selection number */
|
||||
} x_scn;
|
||||
|
||||
struct {
|
||||
unsigned long x_tvfill; /* tv fill value */
|
||||
unsigned short x_tvlen; /* length of .tv */
|
||||
char x_tvran[2][2]; /* tv range */
|
||||
} x_tv; /* info about .tv section (in auxent of symbol .tv)) */
|
||||
|
||||
};
|
||||
|
||||
#define SYMENT struct external_syment
|
||||
#define SYMESZ 18
|
||||
#define AUXENT union external_auxent
|
||||
#define AUXESZ 18
|
||||
|
||||
#define _ETEXT "etext"
|
||||
|
||||
/********************** RELOCATION DIRECTIVES **********************/
|
||||
|
||||
struct external_reloc {
|
||||
char r_vaddr[4];
|
||||
char r_symndx[4];
|
||||
char r_type[2];
|
||||
};
|
||||
|
||||
#define RELOC struct external_reloc
|
||||
#define RELSZ 10
|
||||
|
||||
/* end of coff/i386.h */
|
||||
|
||||
/* PE COFF header information */
|
||||
|
||||
#ifndef _PE_H
|
||||
#define _PE_H
|
||||
|
||||
/* NT specific file attributes */
|
||||
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001
|
||||
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002
|
||||
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
|
||||
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008
|
||||
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080
|
||||
#define IMAGE_FILE_32BIT_MACHINE 0x0100
|
||||
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200
|
||||
#define IMAGE_FILE_SYSTEM 0x1000
|
||||
#define IMAGE_FILE_DLL 0x2000
|
||||
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
|
||||
|
||||
/* additional flags to be set for section headers to allow the NT loader to
|
||||
read and write to the section data (to replace the addresses of data in
|
||||
dlls for one thing); also to execute the section in .text's case=
|
||||
*/
|
||||
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
|
||||
#define IMAGE_SCN_MEM_EXECUTE 0x20000000
|
||||
#define IMAGE_SCN_MEM_READ 0x40000000
|
||||
#define IMAGE_SCN_MEM_WRITE 0x80000000
|
||||
|
||||
/*
|
||||
* Section characteristics added for ppc-nt
|
||||
*/
|
||||
|
||||
#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* Reserved. */
|
||||
|
||||
#define IMAGE_SCN_CNT_CODE 0x00000020 /* Section contains code. */
|
||||
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* Section contains initialized data. */
|
||||
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* Section contains uninitialized data. */
|
||||
|
||||
#define IMAGE_SCN_LNK_OTHER 0x00000100 /* Reserved. */
|
||||
#define IMAGE_SCN_LNK_INFO 0x00000200 /* Section contains comments or some other type of information. */
|
||||
#define IMAGE_SCN_LNK_REMOVE 0x00000800 /* Section contents will not become part of image. */
|
||||
#define IMAGE_SCN_LNK_COMDAT 0x00001000 /* Section contents comdat. */
|
||||
|
||||
#define IMAGE_SCN_MEM_FARDATA 0x00008000
|
||||
|
||||
#define IMAGE_SCN_MEM_PURGEABLE 0x00020000
|
||||
#define IMAGE_SCN_MEM_16BIT 0x00020000
|
||||
#define IMAGE_SCN_MEM_LOCKED 0x00040000
|
||||
#define IMAGE_SCN_MEM_PRELOAD 0x00080000
|
||||
|
||||
#define IMAGE_SCN_ALIGN_1BYTES 0x00100000
|
||||
#define IMAGE_SCN_ALIGN_2BYTES 0x00200000
|
||||
#define IMAGE_SCN_ALIGN_4BYTES 0x00300000
|
||||
#define IMAGE_SCN_ALIGN_8BYTES 0x00400000
|
||||
#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 /* Default alignment if no others are specified. */
|
||||
#define IMAGE_SCN_ALIGN_32BYTES 0x00600000
|
||||
#define IMAGE_SCN_ALIGN_64BYTES 0x00700000
|
||||
|
||||
|
||||
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* Section contains extended relocations. */
|
||||
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* Section is not cachable. */
|
||||
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* Section is not pageable. */
|
||||
#define IMAGE_SCN_MEM_SHARED 0x10000000 /* Section is shareable. */
|
||||
|
||||
/* COMDAT selection codes. */
|
||||
|
||||
#define IMAGE_COMDAT_SELECT_NODUPLICATES (1) /* Warn if duplicates. */
|
||||
#define IMAGE_COMDAT_SELECT_ANY (2) /* No warning. */
|
||||
#define IMAGE_COMDAT_SELECT_SAME_SIZE (3) /* Warn if different size. */
|
||||
#define IMAGE_COMDAT_SELECT_EXACT_MATCH (4) /* Warn if different. */
|
||||
#define IMAGE_COMDAT_SELECT_ASSOCIATIVE (5) /* Base on other section. */
|
||||
|
||||
/* Magic values that are true for all dos/nt implementations */
|
||||
#define DOSMAGIC 0x5a4d
|
||||
#define NT_SIGNATURE 0x00004550
|
||||
|
||||
/* NT allows long filenames, we want to accommodate this. This may break
|
||||
some of the bfd functions */
|
||||
#undef FILNMLEN
|
||||
#define FILNMLEN 18 /* # characters in a file name */
|
||||
|
||||
|
||||
#ifdef COFF_IMAGE_WITH_PE
|
||||
/* The filehdr is only weired in images */
|
||||
|
||||
#undef FILHDR
|
||||
struct external_PE_filehdr
|
||||
{
|
||||
/* DOS header fields */
|
||||
unsigned short e_magic; /* Magic number, 0x5a4d */
|
||||
unsigned short e_cblp; /* Bytes on last page of file, 0x90 */
|
||||
unsigned short e_cp; /* Pages in file, 0x3 */
|
||||
unsigned short e_crlc; /* Relocations, 0x0 */
|
||||
unsigned short e_cparhdr; /* Size of header in paragraphs, 0x4 */
|
||||
unsigned short e_minalloc; /* Minimum extra paragraphs needed, 0x0 */
|
||||
unsigned short e_maxalloc; /* Maximum extra paragraphs needed, 0xFFFF */
|
||||
unsigned short e_ss; /* Initial (relative) SS value, 0x0 */
|
||||
unsigned short e_sp; /* Initial SP value, 0xb8 */
|
||||
unsigned short e_csum; /* Checksum, 0x0 */
|
||||
unsigned short e_ip; /* Initial IP value, 0x0 */
|
||||
unsigned short e_cs; /* Initial (relative) CS value, 0x0 */
|
||||
unsigned short e_lfarlc; /* File address of relocation table, 0x40 */
|
||||
unsigned short e_ovno; /* Overlay number, 0x0 */
|
||||
char e_res[4][2]; /* Reserved words, all 0x0 */
|
||||
unsigned short e_oemid; /* OEM identifier (for e_oeminfo), 0x0 */
|
||||
unsigned short e_oeminfo; /* OEM information; e_oemid specific, 0x0 */
|
||||
char e_res2[10][2]; /* Reserved words, all 0x0 */
|
||||
unsigned long e_lfanew; /* File address of new exe header, 0x80 */
|
||||
char dos_message[16][4]; /* other stuff, always follow DOS header */
|
||||
unsigned int nt_signature; /* required NT signature, 0x4550 */
|
||||
|
||||
/* From standard header */
|
||||
|
||||
unsigned short f_magic; /* magic number */
|
||||
unsigned short f_nscns; /* number of sections */
|
||||
unsigned long f_timdat; /* time & date stamp */
|
||||
unsigned long f_symptr; /* file pointer to symtab */
|
||||
unsigned long f_nsyms; /* number of symtab entries */
|
||||
unsigned short f_opthdr; /* sizeof(optional hdr) */
|
||||
unsigned short f_flags; /* flags */
|
||||
};
|
||||
|
||||
|
||||
#define FILHDR struct external_PE_filehdr
|
||||
#undef FILHSZ
|
||||
#define FILHSZ 152
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned short magic; /* type of file */
|
||||
unsigned short vstamp; /* version stamp */
|
||||
unsigned long tsize; /* text size in bytes, padded to FW bdry*/
|
||||
unsigned long dsize; /* initialized data " " */
|
||||
unsigned long bsize; /* uninitialized data " " */
|
||||
unsigned long entry; /* entry pt. */
|
||||
unsigned long text_start; /* base of text used for this file */
|
||||
unsigned long data_start; /* base of all data used for this file */
|
||||
|
||||
/* NT extra fields; see internal.h for descriptions */
|
||||
unsigned long ImageBase;
|
||||
unsigned long SectionAlignment;
|
||||
unsigned long FileAlignment;
|
||||
unsigned short MajorOperatingSystemVersion;
|
||||
unsigned short MinorOperatingSystemVersion;
|
||||
unsigned short MajorImageVersion;
|
||||
unsigned short MinorImageVersion;
|
||||
unsigned short MajorSubsystemVersion;
|
||||
unsigned short MinorSubsystemVersion;
|
||||
char Reserved1[4];
|
||||
unsigned long SizeOfImage;
|
||||
unsigned long SizeOfHeaders;
|
||||
unsigned long CheckSum;
|
||||
unsigned short Subsystem;
|
||||
unsigned short DllCharacteristics;
|
||||
unsigned long SizeOfStackReserve;
|
||||
unsigned long SizeOfStackCommit;
|
||||
unsigned long SizeOfHeapReserve;
|
||||
unsigned long SizeOfHeapCommit;
|
||||
unsigned long LoaderFlags;
|
||||
unsigned long NumberOfRvaAndSizes;
|
||||
/* IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; */
|
||||
char DataDirectory[16][2][4]; /* 16 entries, 2 elements/entry, 4 chars */
|
||||
|
||||
} PEAOUTHDR;
|
||||
|
||||
|
||||
#undef AOUTSZ
|
||||
#define AOUTSZ (AOUTHDRSZ + 196)
|
||||
|
||||
#undef E_FILNMLEN
|
||||
#define E_FILNMLEN 18 /* # characters in a file name */
|
||||
#endif
|
||||
|
||||
/* end of coff/pe.h */
|
||||
|
||||
#define DT_NON (0) /* no derived type */
|
||||
#define DT_PTR (1) /* pointer */
|
||||
#define DT_FCN (2) /* function */
|
||||
#define DT_ARY (3) /* array */
|
||||
|
||||
#define ISPTR(x) (((x) & N_TMASK) == (DT_PTR << N_BTSHFT))
|
||||
#define ISFCN(x) (((x) & N_TMASK) == (DT_FCN << N_BTSHFT))
|
||||
#define ISARY(x) (((x) & N_TMASK) == (DT_ARY << N_BTSHFT))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _A_OUT_H_ */
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef QEMU_AES_H
|
||||
#define QEMU_AES_H
|
||||
|
||||
#define AES_MAXNR 14
|
||||
#define AES_BLOCK_SIZE 16
|
||||
|
||||
struct aes_key_st {
|
||||
uint32_t rd_key[4 *(AES_MAXNR + 1)];
|
||||
int rounds;
|
||||
};
|
||||
typedef struct aes_key_st AES_KEY;
|
||||
|
||||
int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
|
||||
AES_KEY *key);
|
||||
int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
|
||||
AES_KEY *key);
|
||||
|
||||
void AES_encrypt(const unsigned char *in, unsigned char *out,
|
||||
const AES_KEY *key);
|
||||
void AES_decrypt(const unsigned char *in, unsigned char *out,
|
||||
const AES_KEY *key);
|
||||
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
|
||||
const unsigned long length, const AES_KEY *key,
|
||||
unsigned char *ivec, const int enc);
|
||||
|
||||
#endif
|
||||
+1963
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,128 @@
|
||||
OUTPUT_FORMAT("elf64-alpha", "elf64-alpha",
|
||||
"elf64-alpha")
|
||||
OUTPUT_ARCH(alpha)
|
||||
ENTRY(__start)
|
||||
SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
|
||||
SECTIONS
|
||||
{
|
||||
/* Read-only sections, merged into text segment: */
|
||||
. = 0x60000000 + SIZEOF_HEADERS;
|
||||
.interp : { *(.interp) }
|
||||
.hash : { *(.hash) }
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.gnu.version : { *(.gnu.version) }
|
||||
.gnu.version_d : { *(.gnu.version_d) }
|
||||
.gnu.version_r : { *(.gnu.version_r) }
|
||||
.rel.text :
|
||||
{ *(.rel.text) *(.rel.gnu.linkonce.t*) }
|
||||
.rela.text :
|
||||
{ *(.rela.text) *(.rela.gnu.linkonce.t*) }
|
||||
.rel.data :
|
||||
{ *(.rel.data) *(.rel.gnu.linkonce.d*) }
|
||||
.rela.data :
|
||||
{ *(.rela.data) *(.rela.gnu.linkonce.d*) }
|
||||
.rel.rodata :
|
||||
{ *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
|
||||
.rela.rodata :
|
||||
{ *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
|
||||
.rel.got : { *(.rel.got) }
|
||||
.rela.got : { *(.rela.got) }
|
||||
.rel.ctors : { *(.rel.ctors) }
|
||||
.rela.ctors : { *(.rela.ctors) }
|
||||
.rel.dtors : { *(.rel.dtors) }
|
||||
.rela.dtors : { *(.rela.dtors) }
|
||||
.rel.init : { *(.rel.init) }
|
||||
.rela.init : { *(.rela.init) }
|
||||
.rel.fini : { *(.rel.fini) }
|
||||
.rela.fini : { *(.rela.fini) }
|
||||
.rel.bss : { *(.rel.bss) }
|
||||
.rela.bss : { *(.rela.bss) }
|
||||
.rel.plt : { *(.rel.plt) }
|
||||
.rela.plt : { *(.rela.plt) }
|
||||
.init : { *(.init) } =0x47ff041f
|
||||
.text :
|
||||
{
|
||||
*(.text)
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
*(.gnu.linkonce.t*)
|
||||
} =0x47ff041f
|
||||
_etext = .;
|
||||
PROVIDE (etext = .);
|
||||
.fini : { *(.fini) } =0x47ff041f
|
||||
.rodata : { *(.rodata) *(.gnu.linkonce.r*) }
|
||||
.rodata1 : { *(.rodata1) }
|
||||
.reginfo : { *(.reginfo) }
|
||||
/* Adjust the address for the data segment. We want to adjust up to
|
||||
the same address within the page on the next page up. */
|
||||
. = ALIGN(0x100000) + (. & (0x100000 - 1));
|
||||
.data :
|
||||
{
|
||||
*(.data)
|
||||
*(.gnu.linkonce.d*)
|
||||
CONSTRUCTORS
|
||||
}
|
||||
.data1 : { *(.data1) }
|
||||
.ctors :
|
||||
{
|
||||
*(.ctors)
|
||||
}
|
||||
.dtors :
|
||||
{
|
||||
*(.dtors)
|
||||
}
|
||||
.plt : { *(.plt) }
|
||||
.got : { *(.got.plt) *(.got) }
|
||||
.dynamic : { *(.dynamic) }
|
||||
/* We want the small data sections together, so single-instruction offsets
|
||||
can access them all, and initialized data all before uninitialized, so
|
||||
we can shorten the on-disk segment size. */
|
||||
.sdata : { *(.sdata) }
|
||||
_edata = .;
|
||||
PROVIDE (edata = .);
|
||||
__bss_start = .;
|
||||
.sbss : { *(.sbss) *(.scommon) }
|
||||
.bss :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
}
|
||||
_end = . ;
|
||||
PROVIDE (end = .);
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
/* These must appear regardless of . */
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm",
|
||||
"elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
ENTRY(_start)
|
||||
SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
|
||||
SECTIONS
|
||||
{
|
||||
/* Read-only sections, merged into text segment: */
|
||||
. = 0x60000000 + SIZEOF_HEADERS;
|
||||
.interp : { *(.interp) }
|
||||
.hash : { *(.hash) }
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.gnu.version : { *(.gnu.version) }
|
||||
.gnu.version_d : { *(.gnu.version_d) }
|
||||
.gnu.version_r : { *(.gnu.version_r) }
|
||||
.rel.text :
|
||||
{ *(.rel.text) *(.rel.gnu.linkonce.t*) }
|
||||
.rela.text :
|
||||
{ *(.rela.text) *(.rela.gnu.linkonce.t*) }
|
||||
.rel.data :
|
||||
{ *(.rel.data) *(.rel.gnu.linkonce.d*) }
|
||||
.rela.data :
|
||||
{ *(.rela.data) *(.rela.gnu.linkonce.d*) }
|
||||
.rel.rodata :
|
||||
{ *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
|
||||
.rela.rodata :
|
||||
{ *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
|
||||
.rel.got : { *(.rel.got) }
|
||||
.rela.got : { *(.rela.got) }
|
||||
.rel.ctors : { *(.rel.ctors) }
|
||||
.rela.ctors : { *(.rela.ctors) }
|
||||
.rel.dtors : { *(.rel.dtors) }
|
||||
.rela.dtors : { *(.rela.dtors) }
|
||||
.rel.init : { *(.rel.init) }
|
||||
.rela.init : { *(.rela.init) }
|
||||
.rel.fini : { *(.rel.fini) }
|
||||
.rela.fini : { *(.rela.fini) }
|
||||
.rel.bss : { *(.rel.bss) }
|
||||
.rela.bss : { *(.rela.bss) }
|
||||
.rel.plt : { *(.rel.plt) }
|
||||
.rela.plt : { *(.rela.plt) }
|
||||
.init : { *(.init) } =0x47ff041f
|
||||
.text :
|
||||
{
|
||||
*(.text)
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
*(.gnu.linkonce.t*)
|
||||
} =0x47ff041f
|
||||
_etext = .;
|
||||
PROVIDE (etext = .);
|
||||
.fini : { *(.fini) } =0x47ff041f
|
||||
.rodata : { *(.rodata) *(.gnu.linkonce.r*) }
|
||||
.rodata1 : { *(.rodata1) }
|
||||
.reginfo : { *(.reginfo) }
|
||||
/* Adjust the address for the data segment. We want to adjust up to
|
||||
the same address within the page on the next page up. */
|
||||
. = ALIGN(0x100000) + (. & (0x100000 - 1));
|
||||
.data :
|
||||
{
|
||||
*(.data)
|
||||
*(.gnu.linkonce.d*)
|
||||
CONSTRUCTORS
|
||||
}
|
||||
.data1 : { *(.data1) }
|
||||
.ctors :
|
||||
{
|
||||
*(.ctors)
|
||||
}
|
||||
.dtors :
|
||||
{
|
||||
*(.dtors)
|
||||
}
|
||||
.plt : { *(.plt) }
|
||||
.got : { *(.got.plt) *(.got) }
|
||||
.dynamic : { *(.dynamic) }
|
||||
/* We want the small data sections together, so single-instruction offsets
|
||||
can access them all, and initialized data all before uninitialized, so
|
||||
we can shorten the on-disk segment size. */
|
||||
.sdata : { *(.sdata) }
|
||||
_edata = .;
|
||||
PROVIDE (edata = .);
|
||||
__bss_start = .;
|
||||
.sbss : { *(.sbss) *(.scommon) }
|
||||
.bss :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
}
|
||||
_end = . ;
|
||||
PROVIDE (end = .);
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
/* These must appear regardless of . */
|
||||
}
|
||||
+911
@@ -0,0 +1,911 @@
|
||||
/*
|
||||
* QEMU Audio subsystem
|
||||
*
|
||||
* Copyright (c) 2003-2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include "vl.h"
|
||||
|
||||
#define USE_WAV_AUDIO
|
||||
|
||||
#include "audio/audio_int.h"
|
||||
|
||||
#define dolog(...) AUD_log ("audio", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
#define QC_AUDIO_DRV "QEMU_AUDIO_DRV"
|
||||
#define QC_VOICES "QEMU_VOICES"
|
||||
#define QC_FIXED_FORMAT "QEMU_FIXED_FORMAT"
|
||||
#define QC_FIXED_FREQ "QEMU_FIXED_FREQ"
|
||||
|
||||
static HWVoice *hw_voices;
|
||||
|
||||
AudioState audio_state = {
|
||||
1, /* use fixed settings */
|
||||
44100, /* fixed frequency */
|
||||
2, /* fixed channels */
|
||||
AUD_FMT_S16, /* fixed format */
|
||||
1, /* number of hw voices */
|
||||
-1 /* voice size */
|
||||
};
|
||||
|
||||
/* http://www.df.lth.se/~john_e/gems/gem002d.html */
|
||||
/* http://www.multi-platforms.com/Tips/PopCount.htm */
|
||||
uint32_t popcount (uint32_t u)
|
||||
{
|
||||
u = ((u&0x55555555) + ((u>>1)&0x55555555));
|
||||
u = ((u&0x33333333) + ((u>>2)&0x33333333));
|
||||
u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
|
||||
u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
|
||||
u = ( u&0x0000ffff) + (u>>16);
|
||||
return u;
|
||||
}
|
||||
|
||||
inline uint32_t lsbindex (uint32_t u)
|
||||
{
|
||||
return popcount ((u&-u)-1);
|
||||
}
|
||||
|
||||
int audio_get_conf_int (const char *key, int defval)
|
||||
{
|
||||
int val = defval;
|
||||
char *strval;
|
||||
|
||||
strval = getenv (key);
|
||||
if (strval) {
|
||||
val = atoi (strval);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
const char *audio_get_conf_str (const char *key, const char *defval)
|
||||
{
|
||||
const char *val = getenv (key);
|
||||
if (!val)
|
||||
return defval;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
void AUD_log (const char *cap, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
fprintf (stderr, "%s: ", cap);
|
||||
va_start (ap, fmt);
|
||||
vfprintf (stderr, fmt, ap);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Soft Voice
|
||||
*/
|
||||
void pcm_sw_free_resources (SWVoice *sw)
|
||||
{
|
||||
if (sw->buf) qemu_free (sw->buf);
|
||||
if (sw->rate) st_rate_stop (sw->rate);
|
||||
sw->buf = NULL;
|
||||
sw->rate = NULL;
|
||||
}
|
||||
|
||||
int pcm_sw_alloc_resources (SWVoice *sw)
|
||||
{
|
||||
sw->buf = qemu_mallocz (sw->hw->samples * sizeof (st_sample_t));
|
||||
if (!sw->buf)
|
||||
return -1;
|
||||
|
||||
sw->rate = st_rate_start (sw->freq, sw->hw->freq);
|
||||
if (!sw->rate) {
|
||||
qemu_free (sw->buf);
|
||||
sw->buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pcm_sw_fini (SWVoice *sw)
|
||||
{
|
||||
pcm_sw_free_resources (sw);
|
||||
}
|
||||
|
||||
int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq,
|
||||
int nchannels, audfmt_e fmt)
|
||||
{
|
||||
int bits = 8, sign = 0;
|
||||
|
||||
switch (fmt) {
|
||||
case AUD_FMT_S8:
|
||||
sign = 1;
|
||||
case AUD_FMT_U8:
|
||||
break;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
sign = 1;
|
||||
case AUD_FMT_U16:
|
||||
bits = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
sw->hw = hw;
|
||||
sw->freq = freq;
|
||||
sw->fmt = fmt;
|
||||
sw->nchannels = nchannels;
|
||||
sw->shift = (nchannels == 2) + (bits == 16);
|
||||
sw->align = (1 << sw->shift) - 1;
|
||||
sw->left = 0;
|
||||
sw->pos = 0;
|
||||
sw->wpos = 0;
|
||||
sw->live = 0;
|
||||
sw->ratio = (sw->hw->freq * ((int64_t) INT_MAX)) / sw->freq;
|
||||
sw->bytes_per_second = sw->freq << sw->shift;
|
||||
sw->conv = mixeng_conv[nchannels == 2][sign][bits == 16];
|
||||
|
||||
pcm_sw_free_resources (sw);
|
||||
return pcm_sw_alloc_resources (sw);
|
||||
}
|
||||
|
||||
/* Hard voice */
|
||||
void pcm_hw_free_resources (HWVoice *hw)
|
||||
{
|
||||
if (hw->mix_buf)
|
||||
qemu_free (hw->mix_buf);
|
||||
hw->mix_buf = NULL;
|
||||
}
|
||||
|
||||
int pcm_hw_alloc_resources (HWVoice *hw)
|
||||
{
|
||||
hw->mix_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t));
|
||||
if (!hw->mix_buf)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pcm_hw_fini (HWVoice *hw)
|
||||
{
|
||||
if (hw->active) {
|
||||
ldebug ("pcm_hw_fini: %d %d %d\n", hw->freq, hw->nchannels, hw->fmt);
|
||||
pcm_hw_free_resources (hw);
|
||||
hw->pcm_ops->fini (hw);
|
||||
memset (hw, 0, audio_state.drv->voice_size);
|
||||
}
|
||||
}
|
||||
|
||||
void pcm_hw_gc (HWVoice *hw)
|
||||
{
|
||||
if (hw->nb_voices)
|
||||
return;
|
||||
|
||||
pcm_hw_fini (hw);
|
||||
}
|
||||
|
||||
int pcm_hw_get_live (HWVoice *hw)
|
||||
{
|
||||
int i, alive = 0, live = hw->samples;
|
||||
|
||||
for (i = 0; i < hw->nb_voices; i++) {
|
||||
if (hw->pvoice[i]->live) {
|
||||
live = audio_MIN (hw->pvoice[i]->live, live);
|
||||
alive += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (alive)
|
||||
return live;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pcm_hw_get_live2 (HWVoice *hw, int *nb_active)
|
||||
{
|
||||
int i, alive = 0, live = hw->samples;
|
||||
|
||||
*nb_active = 0;
|
||||
for (i = 0; i < hw->nb_voices; i++) {
|
||||
if (hw->pvoice[i]->live) {
|
||||
if (hw->pvoice[i]->live < live) {
|
||||
*nb_active = hw->pvoice[i]->active != 0;
|
||||
live = hw->pvoice[i]->live;
|
||||
}
|
||||
alive += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (alive)
|
||||
return live;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
void pcm_hw_dec_live (HWVoice *hw, int decr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hw->nb_voices; i++) {
|
||||
if (hw->pvoice[i]->live) {
|
||||
hw->pvoice[i]->live -= decr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pcm_hw_clear (HWVoice *hw, void *buf, int len)
|
||||
{
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
switch (hw->fmt) {
|
||||
case AUD_FMT_S16:
|
||||
case AUD_FMT_S8:
|
||||
memset (buf, len << hw->shift, 0x00);
|
||||
break;
|
||||
|
||||
case AUD_FMT_U8:
|
||||
memset (buf, len << hw->shift, 0x80);
|
||||
break;
|
||||
|
||||
case AUD_FMT_U16:
|
||||
{
|
||||
unsigned int i;
|
||||
uint16_t *p = buf;
|
||||
int shift = hw->nchannels - 1;
|
||||
|
||||
for (i = 0; i < len << shift; i++) {
|
||||
p[i] = INT16_MAX;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int pcm_hw_write (SWVoice *sw, void *buf, int size)
|
||||
{
|
||||
int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
|
||||
int ret = 0, pos = 0;
|
||||
if (!sw)
|
||||
return size;
|
||||
|
||||
hwsamples = sw->hw->samples;
|
||||
samples = size >> sw->shift;
|
||||
|
||||
if (!sw->live) {
|
||||
sw->wpos = sw->hw->rpos;
|
||||
}
|
||||
wpos = sw->wpos;
|
||||
live = sw->live;
|
||||
dead = hwsamples - live;
|
||||
swlim = (dead * ((int64_t) INT_MAX)) / sw->ratio;
|
||||
swlim = audio_MIN (swlim, samples);
|
||||
|
||||
ldebug ("size=%d live=%d dead=%d swlim=%d wpos=%d\n",
|
||||
size, live, dead, swlim, wpos);
|
||||
if (swlim)
|
||||
sw->conv (sw->buf, buf, swlim);
|
||||
|
||||
while (swlim) {
|
||||
dead = hwsamples - live;
|
||||
left = hwsamples - wpos;
|
||||
blck = audio_MIN (dead, left);
|
||||
if (!blck) {
|
||||
/* dolog ("swlim=%d\n", swlim); */
|
||||
break;
|
||||
}
|
||||
isamp = swlim;
|
||||
osamp = blck;
|
||||
st_rate_flow (sw->rate, sw->buf + pos, sw->hw->mix_buf + wpos, &isamp, &osamp);
|
||||
ret += isamp;
|
||||
swlim -= isamp;
|
||||
pos += isamp;
|
||||
live += osamp;
|
||||
wpos = (wpos + osamp) % hwsamples;
|
||||
}
|
||||
|
||||
sw->wpos = wpos;
|
||||
sw->live = live;
|
||||
return ret << sw->shift;
|
||||
}
|
||||
|
||||
int pcm_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
{
|
||||
int sign = 0, bits = 8;
|
||||
|
||||
pcm_hw_fini (hw);
|
||||
ldebug ("pcm_hw_init: %d %d %d\n", freq, nchannels, fmt);
|
||||
if (hw->pcm_ops->init (hw, freq, nchannels, fmt)) {
|
||||
memset (hw, 0, audio_state.drv->voice_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (hw->fmt) {
|
||||
case AUD_FMT_S8:
|
||||
sign = 1;
|
||||
case AUD_FMT_U8:
|
||||
break;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
sign = 1;
|
||||
case AUD_FMT_U16:
|
||||
bits = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
hw->nb_voices = 0;
|
||||
hw->active = 1;
|
||||
hw->shift = (hw->nchannels == 2) + (bits == 16);
|
||||
hw->bytes_per_second = hw->freq << hw->shift;
|
||||
hw->align = (1 << hw->shift) - 1;
|
||||
hw->samples = hw->bufsize >> hw->shift;
|
||||
hw->clip = mixeng_clip[hw->nchannels == 2][sign][bits == 16];
|
||||
if (pcm_hw_alloc_resources (hw)) {
|
||||
pcm_hw_fini (hw);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dist (void *hw)
|
||||
{
|
||||
if (hw) {
|
||||
return (((uint8_t *) hw - (uint8_t *) hw_voices)
|
||||
/ audio_state.drv->voice_size) + 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define ADVANCE(hw) \
|
||||
((hw) ? advance (hw, audio_state.drv->voice_size) : hw_voices)
|
||||
|
||||
HWVoice *pcm_hw_find_any (HWVoice *hw)
|
||||
{
|
||||
int i = dist (hw);
|
||||
for (; i < audio_state.nb_hw_voices; i++) {
|
||||
hw = ADVANCE (hw);
|
||||
return hw;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HWVoice *pcm_hw_find_any_active (HWVoice *hw)
|
||||
{
|
||||
int i = dist (hw);
|
||||
for (; i < audio_state.nb_hw_voices; i++) {
|
||||
hw = ADVANCE (hw);
|
||||
if (hw->active)
|
||||
return hw;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HWVoice *pcm_hw_find_any_active_enabled (HWVoice *hw)
|
||||
{
|
||||
int i = dist (hw);
|
||||
for (; i < audio_state.nb_hw_voices; i++) {
|
||||
hw = ADVANCE (hw);
|
||||
if (hw->active && hw->enabled)
|
||||
return hw;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HWVoice *pcm_hw_find_any_passive (HWVoice *hw)
|
||||
{
|
||||
int i = dist (hw);
|
||||
for (; i < audio_state.nb_hw_voices; i++) {
|
||||
hw = ADVANCE (hw);
|
||||
if (!hw->active)
|
||||
return hw;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HWVoice *pcm_hw_find_specific (HWVoice *hw, int freq,
|
||||
int nchannels, audfmt_e fmt)
|
||||
{
|
||||
while ((hw = pcm_hw_find_any_active (hw))) {
|
||||
if (hw->freq == freq &&
|
||||
hw->nchannels == nchannels &&
|
||||
hw->fmt == fmt)
|
||||
return hw;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HWVoice *pcm_hw_add (int freq, int nchannels, audfmt_e fmt)
|
||||
{
|
||||
HWVoice *hw;
|
||||
|
||||
if (audio_state.fixed_format) {
|
||||
freq = audio_state.fixed_freq;
|
||||
nchannels = audio_state.fixed_channels;
|
||||
fmt = audio_state.fixed_fmt;
|
||||
}
|
||||
|
||||
hw = pcm_hw_find_specific (NULL, freq, nchannels, fmt);
|
||||
|
||||
if (hw)
|
||||
return hw;
|
||||
|
||||
hw = pcm_hw_find_any_passive (NULL);
|
||||
if (hw) {
|
||||
hw->pcm_ops = audio_state.drv->pcm_ops;
|
||||
if (!hw->pcm_ops)
|
||||
return NULL;
|
||||
|
||||
if (pcm_hw_init (hw, freq, nchannels, fmt)) {
|
||||
pcm_hw_gc (hw);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
return hw;
|
||||
}
|
||||
|
||||
return pcm_hw_find_any (NULL);
|
||||
}
|
||||
|
||||
int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw)
|
||||
{
|
||||
SWVoice **pvoice = qemu_mallocz ((hw->nb_voices + 1) * sizeof (sw));
|
||||
if (!pvoice)
|
||||
return -1;
|
||||
|
||||
memcpy (pvoice, hw->pvoice, hw->nb_voices * sizeof (sw));
|
||||
qemu_free (hw->pvoice);
|
||||
hw->pvoice = pvoice;
|
||||
hw->pvoice[hw->nb_voices++] = sw;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw)
|
||||
{
|
||||
int i, j;
|
||||
if (hw->nb_voices > 1) {
|
||||
SWVoice **pvoice = qemu_mallocz ((hw->nb_voices - 1) * sizeof (sw));
|
||||
|
||||
if (!pvoice) {
|
||||
dolog ("Can not maintain consistent state (not enough memory)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; i < hw->nb_voices; i++) {
|
||||
if (j >= hw->nb_voices - 1) {
|
||||
dolog ("Can not maintain consistent state "
|
||||
"(invariant violated)\n");
|
||||
return -1;
|
||||
}
|
||||
if (hw->pvoice[i] != sw)
|
||||
pvoice[j++] = hw->pvoice[i];
|
||||
}
|
||||
qemu_free (hw->pvoice);
|
||||
hw->pvoice = pvoice;
|
||||
hw->nb_voices -= 1;
|
||||
}
|
||||
else {
|
||||
qemu_free (hw->pvoice);
|
||||
hw->pvoice = NULL;
|
||||
hw->nb_voices = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SWVoice *pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt)
|
||||
{
|
||||
SWVoice *sw;
|
||||
HWVoice *hw;
|
||||
|
||||
sw = qemu_mallocz (sizeof (*sw));
|
||||
if (!sw)
|
||||
goto err1;
|
||||
|
||||
hw = pcm_hw_add (freq, nchannels, fmt);
|
||||
if (!hw)
|
||||
goto err2;
|
||||
|
||||
if (pcm_hw_add_sw (hw, sw))
|
||||
goto err3;
|
||||
|
||||
if (pcm_sw_init (sw, hw, freq, nchannels, fmt))
|
||||
goto err4;
|
||||
|
||||
return sw;
|
||||
|
||||
err4:
|
||||
pcm_hw_del_sw (hw, sw);
|
||||
err3:
|
||||
pcm_hw_gc (hw);
|
||||
err2:
|
||||
qemu_free (sw);
|
||||
err1:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SWVoice *AUD_open (SWVoice *sw, const char *name,
|
||||
int freq, int nchannels, audfmt_e fmt)
|
||||
{
|
||||
if (!audio_state.drv) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sw && freq == sw->freq && sw->nchannels == nchannels && sw->fmt == fmt) {
|
||||
return sw;
|
||||
}
|
||||
|
||||
if (sw) {
|
||||
ldebug ("Different format %s %d %d %d\n",
|
||||
name,
|
||||
sw->freq == freq,
|
||||
sw->nchannels == nchannels,
|
||||
sw->fmt == fmt);
|
||||
}
|
||||
|
||||
if (nchannels != 1 && nchannels != 2) {
|
||||
dolog ("Bogus channel count %d for voice %s\n", nchannels, name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!audio_state.fixed_format && sw) {
|
||||
pcm_sw_fini (sw);
|
||||
pcm_hw_del_sw (sw->hw, sw);
|
||||
pcm_hw_gc (sw->hw);
|
||||
if (sw->name) {
|
||||
qemu_free (sw->name);
|
||||
sw->name = NULL;
|
||||
}
|
||||
qemu_free (sw);
|
||||
sw = NULL;
|
||||
}
|
||||
|
||||
if (sw) {
|
||||
HWVoice *hw = sw->hw;
|
||||
if (!hw) {
|
||||
dolog ("Internal logic error voice %s has no hardware store\n",
|
||||
name);
|
||||
return sw;
|
||||
}
|
||||
|
||||
if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) {
|
||||
pcm_sw_fini (sw);
|
||||
pcm_hw_del_sw (hw, sw);
|
||||
pcm_hw_gc (hw);
|
||||
if (sw->name) {
|
||||
qemu_free (sw->name);
|
||||
sw->name = NULL;
|
||||
}
|
||||
qemu_free (sw);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
sw = pcm_create_voice_pair (freq, nchannels, fmt);
|
||||
if (!sw) {
|
||||
dolog ("Failed to create voice %s\n", name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (sw->name) {
|
||||
qemu_free (sw->name);
|
||||
sw->name = NULL;
|
||||
}
|
||||
sw->name = qemu_strdup (name);
|
||||
return sw;
|
||||
}
|
||||
|
||||
void AUD_close (SWVoice *sw)
|
||||
{
|
||||
if (!sw)
|
||||
return;
|
||||
|
||||
pcm_sw_fini (sw);
|
||||
pcm_hw_del_sw (sw->hw, sw);
|
||||
pcm_hw_gc (sw->hw);
|
||||
if (sw->name) {
|
||||
qemu_free (sw->name);
|
||||
sw->name = NULL;
|
||||
}
|
||||
qemu_free (sw);
|
||||
}
|
||||
|
||||
int AUD_write (SWVoice *sw, void *buf, int size)
|
||||
{
|
||||
int bytes;
|
||||
|
||||
if (!sw->hw->enabled)
|
||||
dolog ("Writing to disabled voice %s\n", sw->name);
|
||||
bytes = sw->hw->pcm_ops->write (sw, buf, size);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void AUD_run (void)
|
||||
{
|
||||
HWVoice *hw = NULL;
|
||||
|
||||
while ((hw = pcm_hw_find_any_active_enabled (hw))) {
|
||||
int i;
|
||||
if (hw->pending_disable && pcm_hw_get_live (hw) <= 0) {
|
||||
hw->enabled = 0;
|
||||
hw->pcm_ops->ctl (hw, VOICE_DISABLE);
|
||||
for (i = 0; i < hw->nb_voices; i++) {
|
||||
hw->pvoice[i]->live = 0;
|
||||
/* hw->pvoice[i]->old_ticks = 0; */
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
hw->pcm_ops->run (hw);
|
||||
assert (hw->rpos < hw->samples);
|
||||
for (i = 0; i < hw->nb_voices; i++) {
|
||||
SWVoice *sw = hw->pvoice[i];
|
||||
if (!sw->active && !sw->live && sw->old_ticks) {
|
||||
int64_t delta = qemu_get_clock (vm_clock) - sw->old_ticks;
|
||||
if (delta > audio_state.ticks_threshold) {
|
||||
ldebug ("resetting old_ticks for %s\n", sw->name);
|
||||
sw->old_ticks = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int AUD_get_free (SWVoice *sw)
|
||||
{
|
||||
int free;
|
||||
|
||||
if (!sw)
|
||||
return 4096;
|
||||
|
||||
free = ((sw->hw->samples - sw->live) << sw->hw->shift) * sw->ratio
|
||||
/ INT_MAX;
|
||||
|
||||
free &= ~sw->hw->align;
|
||||
if (!free) return 0;
|
||||
|
||||
return free;
|
||||
}
|
||||
|
||||
int AUD_get_buffer_size (SWVoice *sw)
|
||||
{
|
||||
return sw->hw->bufsize;
|
||||
}
|
||||
|
||||
void AUD_adjust (SWVoice *sw, int bytes)
|
||||
{
|
||||
if (!sw)
|
||||
return;
|
||||
sw->old_ticks += (ticks_per_sec * (int64_t) bytes) / sw->bytes_per_second;
|
||||
}
|
||||
|
||||
void AUD_reset (SWVoice *sw)
|
||||
{
|
||||
sw->active = 0;
|
||||
sw->old_ticks = 0;
|
||||
}
|
||||
|
||||
int AUD_calc_elapsed (SWVoice *sw)
|
||||
{
|
||||
int64_t now, delta, bytes;
|
||||
int dead, swlim;
|
||||
|
||||
if (!sw)
|
||||
return 0;
|
||||
|
||||
now = qemu_get_clock (vm_clock);
|
||||
delta = now - sw->old_ticks;
|
||||
bytes = (delta * sw->bytes_per_second) / ticks_per_sec;
|
||||
if (delta < 0) {
|
||||
dolog ("whoops delta(<0)=%lld\n", delta);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dead = sw->hw->samples - sw->live;
|
||||
swlim = ((dead * (int64_t) INT_MAX) / sw->ratio);
|
||||
|
||||
if (bytes > swlim) {
|
||||
return swlim;
|
||||
}
|
||||
else {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
void AUD_enable (SWVoice *sw, int on)
|
||||
{
|
||||
int i;
|
||||
HWVoice *hw;
|
||||
|
||||
if (!sw)
|
||||
return;
|
||||
|
||||
hw = sw->hw;
|
||||
if (on) {
|
||||
if (!sw->live)
|
||||
sw->wpos = sw->hw->rpos;
|
||||
if (!sw->old_ticks) {
|
||||
sw->old_ticks = qemu_get_clock (vm_clock);
|
||||
}
|
||||
}
|
||||
|
||||
if (sw->active != on) {
|
||||
if (on) {
|
||||
hw->pending_disable = 0;
|
||||
if (!hw->enabled) {
|
||||
hw->enabled = 1;
|
||||
for (i = 0; i < hw->nb_voices; i++) {
|
||||
ldebug ("resetting voice\n");
|
||||
sw = hw->pvoice[i];
|
||||
sw->old_ticks = qemu_get_clock (vm_clock);
|
||||
}
|
||||
hw->pcm_ops->ctl (hw, VOICE_ENABLE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hw->enabled && !hw->pending_disable) {
|
||||
int nb_active = 0;
|
||||
for (i = 0; i < hw->nb_voices; i++) {
|
||||
nb_active += hw->pvoice[i]->active != 0;
|
||||
}
|
||||
|
||||
if (nb_active == 1) {
|
||||
hw->pending_disable = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
sw->active = on;
|
||||
}
|
||||
}
|
||||
|
||||
static struct audio_output_driver *drvtab[] = {
|
||||
#ifdef CONFIG_OSS
|
||||
&oss_output_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FMOD
|
||||
&fmod_output_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_SDL
|
||||
&sdl_output_driver,
|
||||
#endif
|
||||
&no_output_driver,
|
||||
#ifdef USE_WAV_AUDIO
|
||||
&wav_output_driver,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int voice_init (struct audio_output_driver *drv)
|
||||
{
|
||||
audio_state.opaque = drv->init ();
|
||||
if (audio_state.opaque) {
|
||||
if (audio_state.nb_hw_voices > drv->max_voices) {
|
||||
dolog ("`%s' does not support %d multiple hardware channels\n"
|
||||
"Resetting to %d\n",
|
||||
drv->name, audio_state.nb_hw_voices, drv->max_voices);
|
||||
audio_state.nb_hw_voices = drv->max_voices;
|
||||
}
|
||||
hw_voices = qemu_mallocz (audio_state.nb_hw_voices * drv->voice_size);
|
||||
if (hw_voices) {
|
||||
audio_state.drv = drv;
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
dolog ("Not enough memory for %d `%s' voices (each %d bytes)\n",
|
||||
audio_state.nb_hw_voices, drv->name, drv->voice_size);
|
||||
drv->fini (audio_state.opaque);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dolog ("Could not init `%s' audio\n", drv->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_vm_stop_handler (void *opaque, int reason)
|
||||
{
|
||||
HWVoice *hw = NULL;
|
||||
|
||||
while ((hw = pcm_hw_find_any (hw))) {
|
||||
if (!hw->pcm_ops)
|
||||
continue;
|
||||
|
||||
hw->pcm_ops->ctl (hw, reason ? VOICE_ENABLE : VOICE_DISABLE);
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_atexit (void)
|
||||
{
|
||||
HWVoice *hw = NULL;
|
||||
|
||||
while ((hw = pcm_hw_find_any (hw))) {
|
||||
if (!hw->pcm_ops)
|
||||
continue;
|
||||
|
||||
hw->pcm_ops->ctl (hw, VOICE_DISABLE);
|
||||
hw->pcm_ops->fini (hw);
|
||||
}
|
||||
audio_state.drv->fini (audio_state.opaque);
|
||||
}
|
||||
|
||||
static void audio_save (QEMUFile *f, void *opaque)
|
||||
{
|
||||
}
|
||||
|
||||
static int audio_load (QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AUD_init (void)
|
||||
{
|
||||
int i;
|
||||
int done = 0;
|
||||
const char *drvname;
|
||||
|
||||
audio_state.fixed_format =
|
||||
!!audio_get_conf_int (QC_FIXED_FORMAT, audio_state.fixed_format);
|
||||
audio_state.fixed_freq =
|
||||
audio_get_conf_int (QC_FIXED_FREQ, audio_state.fixed_freq);
|
||||
audio_state.nb_hw_voices =
|
||||
audio_get_conf_int (QC_VOICES, audio_state.nb_hw_voices);
|
||||
|
||||
if (audio_state.nb_hw_voices <= 0) {
|
||||
dolog ("Bogus number of voices %d, resetting to 1\n",
|
||||
audio_state.nb_hw_voices);
|
||||
}
|
||||
|
||||
drvname = audio_get_conf_str (QC_AUDIO_DRV, NULL);
|
||||
if (drvname) {
|
||||
int found = 0;
|
||||
for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
|
||||
if (!strcmp (drvname, drvtab[i]->name)) {
|
||||
done = voice_init (drvtab[i]);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
dolog ("Unknown audio driver `%s'\n", drvname);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_add_vm_stop_handler (audio_vm_stop_handler, NULL);
|
||||
atexit (audio_atexit);
|
||||
|
||||
if (!done) {
|
||||
for (i = 0; !done && i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
|
||||
if (drvtab[i]->can_be_default)
|
||||
done = voice_init (drvtab[i]);
|
||||
}
|
||||
}
|
||||
|
||||
audio_state.ticks_threshold = ticks_per_sec / 50;
|
||||
audio_state.freq_threshold = 100;
|
||||
|
||||
register_savevm ("audio", 0, 1, audio_save, audio_load, NULL);
|
||||
if (!done) {
|
||||
dolog ("Can not initialize audio subsystem\n");
|
||||
voice_init (&no_output_driver);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* QEMU Audio subsystem header
|
||||
*
|
||||
* Copyright (c) 2003-2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef QEMU_AUDIO_H
|
||||
#define QEMU_AUDIO_H
|
||||
|
||||
#include "mixeng.h"
|
||||
|
||||
typedef enum {
|
||||
AUD_FMT_U8,
|
||||
AUD_FMT_S8,
|
||||
AUD_FMT_U16,
|
||||
AUD_FMT_S16
|
||||
} audfmt_e;
|
||||
|
||||
typedef struct SWVoice SWVoice;
|
||||
|
||||
SWVoice * AUD_open (SWVoice *sw, const char *name, int freq,
|
||||
int nchannels, audfmt_e fmt);
|
||||
void AUD_init (void);
|
||||
void AUD_log (const char *cap, const char *fmt, ...)
|
||||
__attribute__ ((__format__ (__printf__, 2, 3)));;
|
||||
void AUD_close (SWVoice *sw);
|
||||
int AUD_write (SWVoice *sw, void *pcm_buf, int size);
|
||||
void AUD_adjust (SWVoice *sw, int leftover);
|
||||
void AUD_reset (SWVoice *sw);
|
||||
int AUD_get_free (SWVoice *sw);
|
||||
int AUD_get_buffer_size (SWVoice *sw);
|
||||
void AUD_run (void);
|
||||
void AUD_enable (SWVoice *sw, int on);
|
||||
int AUD_calc_elapsed (SWVoice *sw);
|
||||
|
||||
static inline void *advance (void *p, int incr)
|
||||
{
|
||||
uint8_t *d = p;
|
||||
return (d + incr);
|
||||
}
|
||||
|
||||
uint32_t popcount (uint32_t u);
|
||||
inline uint32_t lsbindex (uint32_t u);
|
||||
|
||||
#define audio_MIN(a, b) ((a)>(b)?(b):(a))
|
||||
#define audio_MAX(a, b) ((a)<(b)?(b):(a))
|
||||
|
||||
#endif /* audio.h */
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* QEMU Audio subsystem header
|
||||
*
|
||||
* Copyright (c) 2003-2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef QEMU_AUDIO_INT_H
|
||||
#define QEMU_AUDIO_INT_H
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
struct pcm_ops;
|
||||
|
||||
typedef struct HWVoice {
|
||||
int active;
|
||||
int enabled;
|
||||
int pending_disable;
|
||||
int valid;
|
||||
int freq;
|
||||
|
||||
f_sample *clip;
|
||||
audfmt_e fmt;
|
||||
int nchannels;
|
||||
|
||||
int align;
|
||||
int shift;
|
||||
|
||||
int rpos;
|
||||
int bufsize;
|
||||
|
||||
int bytes_per_second;
|
||||
st_sample_t *mix_buf;
|
||||
|
||||
int samples;
|
||||
int64_t old_ticks;
|
||||
int nb_voices;
|
||||
struct SWVoice **pvoice;
|
||||
struct pcm_ops *pcm_ops;
|
||||
} HWVoice;
|
||||
|
||||
extern struct pcm_ops no_pcm_ops;
|
||||
extern struct audio_output_driver no_output_driver;
|
||||
|
||||
extern struct pcm_ops oss_pcm_ops;
|
||||
extern struct audio_output_driver oss_output_driver;
|
||||
|
||||
extern struct pcm_ops sdl_pcm_ops;
|
||||
extern struct audio_output_driver sdl_output_driver;
|
||||
|
||||
extern struct pcm_ops wav_pcm_ops;
|
||||
extern struct audio_output_driver wav_output_driver;
|
||||
|
||||
extern struct pcm_ops fmod_pcm_ops;
|
||||
extern struct audio_output_driver fmod_output_driver;
|
||||
|
||||
struct audio_output_driver {
|
||||
const char *name;
|
||||
void *(*init) (void);
|
||||
void (*fini) (void *);
|
||||
struct pcm_ops *pcm_ops;
|
||||
int can_be_default;
|
||||
int max_voices;
|
||||
int voice_size;
|
||||
};
|
||||
|
||||
typedef struct AudioState {
|
||||
int fixed_format;
|
||||
int fixed_freq;
|
||||
int fixed_channels;
|
||||
int fixed_fmt;
|
||||
int nb_hw_voices;
|
||||
int64_t ticks_threshold;
|
||||
int freq_threshold;
|
||||
void *opaque;
|
||||
struct audio_output_driver *drv;
|
||||
} AudioState;
|
||||
extern AudioState audio_state;
|
||||
|
||||
struct SWVoice {
|
||||
int freq;
|
||||
audfmt_e fmt;
|
||||
int nchannels;
|
||||
|
||||
int shift;
|
||||
int align;
|
||||
|
||||
t_sample *conv;
|
||||
|
||||
int left;
|
||||
int pos;
|
||||
int bytes_per_second;
|
||||
int64_t ratio;
|
||||
st_sample_t *buf;
|
||||
void *rate;
|
||||
|
||||
int wpos;
|
||||
int live;
|
||||
int active;
|
||||
int64_t old_ticks;
|
||||
HWVoice *hw;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct pcm_ops {
|
||||
int (*init) (HWVoice *hw, int freq, int nchannels, audfmt_e fmt);
|
||||
void (*fini) (HWVoice *hw);
|
||||
void (*run) (HWVoice *hw);
|
||||
int (*write) (SWVoice *sw, void *buf, int size);
|
||||
int (*ctl) (HWVoice *hw, int cmd, ...);
|
||||
};
|
||||
|
||||
void pcm_sw_free_resources (SWVoice *sw);
|
||||
int pcm_sw_alloc_resources (SWVoice *sw);
|
||||
void pcm_sw_fini (SWVoice *sw);
|
||||
int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq,
|
||||
int nchannels, audfmt_e fmt);
|
||||
|
||||
void pcm_hw_clear (HWVoice *hw, void *buf, int len);
|
||||
HWVoice * pcm_hw_find_any (HWVoice *hw);
|
||||
HWVoice * pcm_hw_find_any_active (HWVoice *hw);
|
||||
HWVoice * pcm_hw_find_any_passive (HWVoice *hw);
|
||||
HWVoice * pcm_hw_find_specific (HWVoice *hw, int freq,
|
||||
int nchannels, audfmt_e fmt);
|
||||
HWVoice * pcm_hw_add (int freq, int nchannels, audfmt_e fmt);
|
||||
int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw);
|
||||
int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw);
|
||||
SWVoice * pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt);
|
||||
|
||||
void pcm_hw_free_resources (HWVoice *hw);
|
||||
int pcm_hw_alloc_resources (HWVoice *hw);
|
||||
void pcm_hw_fini (HWVoice *hw);
|
||||
void pcm_hw_gc (HWVoice *hw);
|
||||
int pcm_hw_get_live (HWVoice *hw);
|
||||
int pcm_hw_get_live2 (HWVoice *hw, int *nb_active);
|
||||
void pcm_hw_dec_live (HWVoice *hw, int decr);
|
||||
int pcm_hw_write (SWVoice *sw, void *buf, int len);
|
||||
|
||||
int audio_get_conf_int (const char *key, int defval);
|
||||
const char *audio_get_conf_str (const char *key, const char *defval);
|
||||
|
||||
struct audio_output_driver;
|
||||
|
||||
#define VOICE_ENABLE 1
|
||||
#define VOICE_DISABLE 2
|
||||
|
||||
#endif /* audio_int.h */
|
||||
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* QEMU FMOD audio output driver
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <fmod.h>
|
||||
#include <fmod_errors.h>
|
||||
#include "vl.h"
|
||||
|
||||
#include "audio/audio_int.h"
|
||||
|
||||
typedef struct FMODVoice {
|
||||
HWVoice hw;
|
||||
unsigned int old_pos;
|
||||
FSOUND_SAMPLE *fmod_sample;
|
||||
int channel;
|
||||
} FMODVoice;
|
||||
|
||||
#define dolog(...) AUD_log ("fmod", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
#define QC_FMOD_DRV "QEMU_FMOD_DRV"
|
||||
#define QC_FMOD_FREQ "QEMU_FMOD_FREQ"
|
||||
#define QC_FMOD_SAMPLES "QEMU_FMOD_SAMPLES"
|
||||
#define QC_FMOD_CHANNELS "QEMU_FMOD_CHANNELS"
|
||||
#define QC_FMOD_BUFSIZE "QEMU_FMOD_BUFSIZE"
|
||||
#define QC_FMOD_THRESHOLD "QEMU_FMOD_THRESHOLD"
|
||||
|
||||
static struct {
|
||||
int nb_samples;
|
||||
int freq;
|
||||
int nb_channels;
|
||||
int bufsize;
|
||||
int threshold;
|
||||
} conf = {
|
||||
2048,
|
||||
44100,
|
||||
1,
|
||||
0,
|
||||
128
|
||||
};
|
||||
|
||||
#define errstr() FMOD_ErrorString (FSOUND_GetError ())
|
||||
|
||||
static int fmod_hw_write (SWVoice *sw, void *buf, int len)
|
||||
{
|
||||
return pcm_hw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static void fmod_clear_sample (FMODVoice *fmd)
|
||||
{
|
||||
HWVoice *hw = &fmd->hw;
|
||||
int status;
|
||||
void *p1 = 0, *p2 = 0;
|
||||
unsigned int len1 = 0, len2 = 0;
|
||||
|
||||
status = FSOUND_Sample_Lock (
|
||||
fmd->fmod_sample,
|
||||
0,
|
||||
hw->samples << hw->shift,
|
||||
&p1,
|
||||
&p2,
|
||||
&len1,
|
||||
&len2
|
||||
);
|
||||
|
||||
if (!status) {
|
||||
dolog ("Failed to lock sample\nReason: %s\n", errstr ());
|
||||
return;
|
||||
}
|
||||
|
||||
if ((len1 & hw->align) || (len2 & hw->align)) {
|
||||
dolog ("Locking sample returned unaligned length %d, %d\n",
|
||||
len1, len2);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (len1 + len2 != hw->samples << hw->shift) {
|
||||
dolog ("Locking sample returned incomplete length %d, %d\n",
|
||||
len1 + len2, hw->samples << hw->shift);
|
||||
goto fail;
|
||||
}
|
||||
pcm_hw_clear (hw, p1, hw->samples);
|
||||
|
||||
fail:
|
||||
status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
|
||||
if (!status) {
|
||||
dolog ("Failed to unlock sample\nReason: %s\n", errstr ());
|
||||
}
|
||||
}
|
||||
|
||||
static int fmod_write_sample (HWVoice *hw, uint8_t *dst, st_sample_t *src,
|
||||
int src_size, int src_pos, int dst_len)
|
||||
{
|
||||
int src_len1 = dst_len, src_len2 = 0, pos = src_pos + dst_len;
|
||||
st_sample_t *src1 = src + src_pos, *src2 = 0;
|
||||
|
||||
if (src_pos + dst_len > src_size) {
|
||||
src_len1 = src_size - src_pos;
|
||||
src2 = src;
|
||||
src_len2 = dst_len - src_len1;
|
||||
pos = src_len2;
|
||||
}
|
||||
|
||||
if (src_len1) {
|
||||
hw->clip (dst, src1, src_len1);
|
||||
memset (src1, 0, src_len1 * sizeof (st_sample_t));
|
||||
advance (dst, src_len1);
|
||||
}
|
||||
|
||||
if (src_len2) {
|
||||
hw->clip (dst, src2, src_len2);
|
||||
memset (src2, 0, src_len2 * sizeof (st_sample_t));
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
static int fmod_unlock_sample (FMODVoice *fmd, void *p1, void *p2,
|
||||
unsigned int blen1, unsigned int blen2)
|
||||
{
|
||||
int status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, blen1, blen2);
|
||||
if (!status) {
|
||||
dolog ("Failed to unlock sample\nReason: %s\n", errstr ());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_lock_sample (FMODVoice *fmd, int pos, int len,
|
||||
void **p1, void **p2,
|
||||
unsigned int *blen1, unsigned int *blen2)
|
||||
{
|
||||
HWVoice *hw = &fmd->hw;
|
||||
int status;
|
||||
|
||||
status = FSOUND_Sample_Lock (
|
||||
fmd->fmod_sample,
|
||||
pos << hw->shift,
|
||||
len << hw->shift,
|
||||
p1,
|
||||
p2,
|
||||
blen1,
|
||||
blen2
|
||||
);
|
||||
|
||||
if (!status) {
|
||||
dolog ("Failed to lock sample\nReason: %s\n", errstr ());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((*blen1 & hw->align) || (*blen2 & hw->align)) {
|
||||
dolog ("Locking sample returned unaligned length %d, %d\n",
|
||||
*blen1, *blen2);
|
||||
fmod_unlock_sample (fmd, *p1, *p2, *blen1, *blen2);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fmod_hw_run (HWVoice *hw)
|
||||
{
|
||||
FMODVoice *fmd = (FMODVoice *) hw;
|
||||
int rpos, live, decr;
|
||||
void *p1 = 0, *p2 = 0;
|
||||
unsigned int blen1 = 0, blen2 = 0;
|
||||
unsigned int len1 = 0, len2 = 0;
|
||||
int nb_active;
|
||||
|
||||
live = pcm_hw_get_live2 (hw, &nb_active);
|
||||
if (live <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hw->pending_disable
|
||||
&& nb_active
|
||||
&& conf.threshold
|
||||
&& live <= conf.threshold) {
|
||||
ldebug ("live=%d nb_active=%d\n", live, nb_active);
|
||||
return;
|
||||
}
|
||||
|
||||
decr = live;
|
||||
|
||||
#if 1
|
||||
if (fmd->channel >= 0) {
|
||||
int pos2 = (fmd->old_pos + decr) % hw->samples;
|
||||
int pos = FSOUND_GetCurrentPosition (fmd->channel);
|
||||
|
||||
if (fmd->old_pos < pos && pos2 >= pos) {
|
||||
decr = pos - fmd->old_pos - (pos2 == pos) - 1;
|
||||
}
|
||||
else if (fmd->old_pos > pos && pos2 >= pos && pos2 < fmd->old_pos) {
|
||||
decr = (hw->samples - fmd->old_pos) + pos - (pos2 == pos) - 1;
|
||||
}
|
||||
/* ldebug ("pos=%d pos2=%d old=%d live=%d decr=%d\n", */
|
||||
/* pos, pos2, fmd->old_pos, live, decr); */
|
||||
}
|
||||
#endif
|
||||
|
||||
if (decr <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fmod_lock_sample (fmd, fmd->old_pos, decr, &p1, &p2, &blen1, &blen2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
len1 = blen1 >> hw->shift;
|
||||
len2 = blen2 >> hw->shift;
|
||||
ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
|
||||
decr = len1 + len2;
|
||||
rpos = hw->rpos;
|
||||
|
||||
if (len1) {
|
||||
rpos = fmod_write_sample (hw, p1, hw->mix_buf, hw->samples, rpos, len1);
|
||||
}
|
||||
|
||||
if (len2) {
|
||||
rpos = fmod_write_sample (hw, p2, hw->mix_buf, hw->samples, rpos, len2);
|
||||
}
|
||||
|
||||
fmod_unlock_sample (fmd, p1, p2, blen1, blen2);
|
||||
|
||||
pcm_hw_dec_live (hw, decr);
|
||||
hw->rpos = rpos % hw->samples;
|
||||
fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
|
||||
}
|
||||
|
||||
static int AUD_to_fmodfmt (audfmt_e fmt, int stereo)
|
||||
{
|
||||
int mode = FSOUND_LOOP_NORMAL;
|
||||
|
||||
switch (fmt) {
|
||||
case AUD_FMT_S8:
|
||||
mode |= FSOUND_SIGNED | FSOUND_8BITS;
|
||||
break;
|
||||
|
||||
case AUD_FMT_U8:
|
||||
mode |= FSOUND_UNSIGNED | FSOUND_8BITS;
|
||||
break;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
mode |= FSOUND_SIGNED | FSOUND_16BITS;
|
||||
break;
|
||||
|
||||
case AUD_FMT_U16:
|
||||
mode |= FSOUND_UNSIGNED | FSOUND_16BITS;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
|
||||
return mode;
|
||||
}
|
||||
|
||||
static void fmod_hw_fini (HWVoice *hw)
|
||||
{
|
||||
FMODVoice *fmd = (FMODVoice *) hw;
|
||||
|
||||
if (fmd->fmod_sample) {
|
||||
FSOUND_Sample_Free (fmd->fmod_sample);
|
||||
fmd->fmod_sample = 0;
|
||||
|
||||
if (fmd->channel >= 0) {
|
||||
FSOUND_StopSound (fmd->channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int fmod_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
{
|
||||
int bits16, mode, channel;
|
||||
FMODVoice *fmd = (FMODVoice *) hw;
|
||||
|
||||
mode = AUD_to_fmodfmt (fmt, nchannels == 2 ? 1 : 0);
|
||||
fmd->fmod_sample = FSOUND_Sample_Alloc (
|
||||
FSOUND_FREE, /* index */
|
||||
conf.nb_samples, /* length */
|
||||
mode, /* mode */
|
||||
freq, /* freq */
|
||||
255, /* volume */
|
||||
128, /* pan */
|
||||
255 /* priority */
|
||||
);
|
||||
|
||||
if (!fmd->fmod_sample) {
|
||||
dolog ("Failed to allocate FMOD sample\nReason: %s\n", errstr ());
|
||||
return -1;
|
||||
}
|
||||
|
||||
channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
|
||||
if (channel < 0) {
|
||||
dolog ("Failed to start playing sound\nReason: %s\n", errstr ());
|
||||
FSOUND_Sample_Free (fmd->fmod_sample);
|
||||
return -1;
|
||||
}
|
||||
fmd->channel = channel;
|
||||
|
||||
hw->freq = freq;
|
||||
hw->fmt = fmt;
|
||||
hw->nchannels = nchannels;
|
||||
bits16 = fmt == AUD_FMT_U16 || fmt == AUD_FMT_S16;
|
||||
hw->bufsize = conf.nb_samples << (nchannels == 2) << bits16;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
{
|
||||
int status;
|
||||
FMODVoice *fmd = (FMODVoice *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
fmod_clear_sample (fmd);
|
||||
status = FSOUND_SetPaused (fmd->channel, 0);
|
||||
if (!status) {
|
||||
dolog ("Failed to resume channel %d\nReason: %s\n",
|
||||
fmd->channel, errstr ());
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
status = FSOUND_SetPaused (fmd->channel, 1);
|
||||
if (!status) {
|
||||
dolog ("Failed to pause channel %d\nReason: %s\n",
|
||||
fmd->channel, errstr ());
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
int type;
|
||||
} drvtab[] = {
|
||||
{"none", FSOUND_OUTPUT_NOSOUND},
|
||||
#ifdef _WIN32
|
||||
{"winmm", FSOUND_OUTPUT_WINMM},
|
||||
{"dsound", FSOUND_OUTPUT_DSOUND},
|
||||
{"a3d", FSOUND_OUTPUT_A3D},
|
||||
{"asio", FSOUND_OUTPUT_ASIO},
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
{"oss", FSOUND_OUTPUT_OSS},
|
||||
{"alsa", FSOUND_OUTPUT_ALSA},
|
||||
{"esd", FSOUND_OUTPUT_ESD},
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
{"mac", FSOUND_OUTPUT_MAC},
|
||||
#endif
|
||||
#if 0
|
||||
{"xbox", FSOUND_OUTPUT_XBOX},
|
||||
{"ps2", FSOUND_OUTPUT_PS2},
|
||||
{"gcube", FSOUND_OUTPUT_GC},
|
||||
#endif
|
||||
{"nort", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
|
||||
};
|
||||
|
||||
static void *fmod_audio_init (void)
|
||||
{
|
||||
int i;
|
||||
double ver;
|
||||
int status;
|
||||
int output_type = -1;
|
||||
const char *drv = audio_get_conf_str (QC_FMOD_DRV, NULL);
|
||||
|
||||
ver = FSOUND_GetVersion ();
|
||||
if (ver < FMOD_VERSION) {
|
||||
dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (drv) {
|
||||
int found = 0;
|
||||
for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
|
||||
if (!strcmp (drv, drvtab[i].name)) {
|
||||
output_type = drvtab[i].type;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
dolog ("Unknown FMOD output driver `%s'\n", drv);
|
||||
}
|
||||
}
|
||||
|
||||
if (output_type != -1) {
|
||||
status = FSOUND_SetOutput (output_type);
|
||||
if (!status) {
|
||||
dolog ("FSOUND_SetOutput(%d) failed\nReason: %s\n",
|
||||
output_type, errstr ());
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
conf.freq = audio_get_conf_int (QC_FMOD_FREQ, conf.freq);
|
||||
conf.nb_samples = audio_get_conf_int (QC_FMOD_SAMPLES, conf.nb_samples);
|
||||
conf.nb_channels =
|
||||
audio_get_conf_int (QC_FMOD_CHANNELS,
|
||||
(audio_state.nb_hw_voices > 1
|
||||
? audio_state.nb_hw_voices
|
||||
: conf.nb_channels));
|
||||
conf.bufsize = audio_get_conf_int (QC_FMOD_BUFSIZE, conf.bufsize);
|
||||
conf.threshold = audio_get_conf_int (QC_FMOD_THRESHOLD, conf.threshold);
|
||||
|
||||
if (conf.bufsize) {
|
||||
status = FSOUND_SetBufferSize (conf.bufsize);
|
||||
if (!status) {
|
||||
dolog ("FSOUND_SetBufferSize (%d) failed\nReason: %s\n",
|
||||
conf.bufsize, errstr ());
|
||||
}
|
||||
}
|
||||
|
||||
status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
|
||||
if (!status) {
|
||||
dolog ("FSOUND_Init failed\nReason: %s\n", errstr ());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void fmod_audio_fini (void *opaque)
|
||||
{
|
||||
FSOUND_Close ();
|
||||
}
|
||||
|
||||
struct pcm_ops fmod_pcm_ops = {
|
||||
fmod_hw_init,
|
||||
fmod_hw_fini,
|
||||
fmod_hw_run,
|
||||
fmod_hw_write,
|
||||
fmod_hw_ctl
|
||||
};
|
||||
|
||||
struct audio_output_driver fmod_output_driver = {
|
||||
"fmod",
|
||||
fmod_audio_init,
|
||||
fmod_audio_fini,
|
||||
&fmod_pcm_ops,
|
||||
1,
|
||||
INT_MAX,
|
||||
sizeof (FMODVoice)
|
||||
};
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* QEMU Mixing engine
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
* Copyright (c) 1998 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
//#define DEBUG_FP
|
||||
#include "audio/mixeng.h"
|
||||
|
||||
#define IN_T int8_t
|
||||
#define IN_MIN CHAR_MIN
|
||||
#define IN_MAX CHAR_MAX
|
||||
#define SIGNED
|
||||
#include "mixeng_template.h"
|
||||
#undef SIGNED
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef IN_T
|
||||
|
||||
#define IN_T uint8_t
|
||||
#define IN_MIN 0
|
||||
#define IN_MAX UCHAR_MAX
|
||||
#include "mixeng_template.h"
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef IN_T
|
||||
|
||||
#define IN_T int16_t
|
||||
#define IN_MIN SHRT_MIN
|
||||
#define IN_MAX SHRT_MAX
|
||||
#define SIGNED
|
||||
#include "mixeng_template.h"
|
||||
#undef SIGNED
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef IN_T
|
||||
|
||||
#define IN_T uint16_t
|
||||
#define IN_MIN 0
|
||||
#define IN_MAX USHRT_MAX
|
||||
#include "mixeng_template.h"
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef IN_T
|
||||
|
||||
t_sample *mixeng_conv[2][2][2] = {
|
||||
{
|
||||
{
|
||||
conv_uint8_t_to_mono,
|
||||
conv_uint16_t_to_mono
|
||||
},
|
||||
{
|
||||
conv_int8_t_to_mono,
|
||||
conv_int16_t_to_mono
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
conv_uint8_t_to_stereo,
|
||||
conv_uint16_t_to_stereo
|
||||
},
|
||||
{
|
||||
conv_int8_t_to_stereo,
|
||||
conv_int16_t_to_stereo
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
f_sample *mixeng_clip[2][2][2] = {
|
||||
{
|
||||
{
|
||||
clip_uint8_t_from_mono,
|
||||
clip_uint16_t_from_mono
|
||||
},
|
||||
{
|
||||
clip_int8_t_from_mono,
|
||||
clip_int16_t_from_mono
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
clip_uint8_t_from_stereo,
|
||||
clip_uint16_t_from_stereo
|
||||
},
|
||||
{
|
||||
clip_int8_t_from_stereo,
|
||||
clip_int16_t_from_stereo
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* August 21, 1998
|
||||
* Copyright 1998 Fabrice Bellard.
|
||||
*
|
||||
* [Rewrote completly the code of Lance Norskog And Sundry
|
||||
* Contributors with a more efficient algorithm.]
|
||||
*
|
||||
* This source code is freely redistributable and may be used for
|
||||
* any purpose. This copyright notice must be maintained.
|
||||
* Lance Norskog And Sundry Contributors are not responsible for
|
||||
* the consequences of using this software.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Sound Tools rate change effect file.
|
||||
*/
|
||||
/*
|
||||
* Linear Interpolation.
|
||||
*
|
||||
* The use of fractional increment allows us to use no buffer. It
|
||||
* avoid the problems at the end of the buffer we had with the old
|
||||
* method which stored a possibly big buffer of size
|
||||
* lcm(in_rate,out_rate).
|
||||
*
|
||||
* Limited to 16 bit samples and sampling frequency <= 65535 Hz. If
|
||||
* the input & output frequencies are equal, a delay of one sample is
|
||||
* introduced. Limited to processing 32-bit count worth of samples.
|
||||
*
|
||||
* 1 << FRAC_BITS evaluating to zero in several places. Changed with
|
||||
* an (unsigned long) cast to make it safe. MarkMLl 2/1/99
|
||||
*/
|
||||
|
||||
/* Private data */
|
||||
typedef struct ratestuff {
|
||||
uint64_t opos;
|
||||
uint64_t opos_inc;
|
||||
uint32_t ipos; /* position in the input stream (integer) */
|
||||
st_sample_t ilast; /* last sample in the input stream */
|
||||
} *rate_t;
|
||||
|
||||
/*
|
||||
* Prepare processing.
|
||||
*/
|
||||
void *st_rate_start (int inrate, int outrate)
|
||||
{
|
||||
rate_t rate = (rate_t) qemu_mallocz (sizeof (struct ratestuff));
|
||||
|
||||
if (!rate) {
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (inrate == outrate) {
|
||||
// exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (inrate >= 65535 || outrate >= 65535) {
|
||||
// exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
rate->opos = 0;
|
||||
|
||||
/* increment */
|
||||
rate->opos_inc = (inrate * ((int64_t) UINT_MAX)) / outrate;
|
||||
|
||||
rate->ipos = 0;
|
||||
rate->ilast.l = 0;
|
||||
rate->ilast.r = 0;
|
||||
return rate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processed signed long samples from ibuf to obuf.
|
||||
* Return number of samples processed.
|
||||
*/
|
||||
void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
|
||||
int *isamp, int *osamp)
|
||||
{
|
||||
rate_t rate = (rate_t) opaque;
|
||||
st_sample_t *istart, *iend;
|
||||
st_sample_t *ostart, *oend;
|
||||
st_sample_t ilast, icur, out;
|
||||
int64_t t;
|
||||
|
||||
ilast = rate->ilast;
|
||||
|
||||
istart = ibuf;
|
||||
iend = ibuf + *isamp;
|
||||
|
||||
ostart = obuf;
|
||||
oend = obuf + *osamp;
|
||||
|
||||
if (rate->opos_inc == 1ULL << 32) {
|
||||
int i, n = *isamp > *osamp ? *osamp : *isamp;
|
||||
for (i = 0; i < n; i++) {
|
||||
obuf[i].l += ibuf[i].r;
|
||||
obuf[i].r += ibuf[i].r;
|
||||
}
|
||||
*isamp = n;
|
||||
*osamp = n;
|
||||
return;
|
||||
}
|
||||
|
||||
while (obuf < oend) {
|
||||
|
||||
/* Safety catch to make sure we have input samples. */
|
||||
if (ibuf >= iend)
|
||||
break;
|
||||
|
||||
/* read as many input samples so that ipos > opos */
|
||||
|
||||
while (rate->ipos <= (rate->opos >> 32)) {
|
||||
ilast = *ibuf++;
|
||||
rate->ipos++;
|
||||
/* See if we finished the input buffer yet */
|
||||
if (ibuf >= iend) goto the_end;
|
||||
}
|
||||
|
||||
icur = *ibuf;
|
||||
|
||||
/* interpolate */
|
||||
t = rate->opos & 0xffffffff;
|
||||
out.l = (ilast.l * (INT_MAX - t) + icur.l * t) / INT_MAX;
|
||||
out.r = (ilast.r * (INT_MAX - t) + icur.r * t) / INT_MAX;
|
||||
|
||||
/* output sample & increment position */
|
||||
#if 0
|
||||
*obuf++ = out;
|
||||
#else
|
||||
obuf->l += out.l;
|
||||
obuf->r += out.r;
|
||||
obuf += 1;
|
||||
#endif
|
||||
rate->opos += rate->opos_inc;
|
||||
}
|
||||
|
||||
the_end:
|
||||
*isamp = ibuf - istart;
|
||||
*osamp = obuf - ostart;
|
||||
rate->ilast = ilast;
|
||||
}
|
||||
|
||||
void st_rate_stop (void *opaque)
|
||||
{
|
||||
qemu_free (opaque);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* QEMU Mixing engine header
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef QEMU_MIXENG_H
|
||||
#define QEMU_MIXENG_H
|
||||
|
||||
typedef void (t_sample) (void *dst, const void *src, int samples);
|
||||
typedef void (f_sample) (void *dst, const void *src, int samples);
|
||||
typedef struct { int64_t l; int64_t r; } st_sample_t;
|
||||
|
||||
extern t_sample *mixeng_conv[2][2][2];
|
||||
extern f_sample *mixeng_clip[2][2][2];
|
||||
|
||||
void *st_rate_start (int inrate, int outrate);
|
||||
void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
|
||||
int *isamp, int *osamp);
|
||||
void st_rate_stop (void *opaque);
|
||||
|
||||
#endif /* mixeng.h */
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* QEMU Mixing engine
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tusen tack till Mike Nordell
|
||||
* dec++'ified by Dscho
|
||||
*/
|
||||
|
||||
#ifdef SIGNED
|
||||
#define HALFT IN_MAX
|
||||
#define HALF IN_MAX
|
||||
#else
|
||||
#define HALFT ((IN_MAX)>>1)
|
||||
#define HALF HALFT
|
||||
#endif
|
||||
|
||||
static int64_t inline glue(conv_,IN_T) (IN_T v)
|
||||
{
|
||||
#ifdef SIGNED
|
||||
return (INT_MAX*(int64_t)v)/HALF;
|
||||
#else
|
||||
return (INT_MAX*((int64_t)v-HALFT))/HALF;
|
||||
#endif
|
||||
}
|
||||
|
||||
static IN_T inline glue(clip_,IN_T) (int64_t v)
|
||||
{
|
||||
if (v >= INT_MAX)
|
||||
return IN_MAX;
|
||||
else if (v < -INT_MAX)
|
||||
return IN_MIN;
|
||||
|
||||
#ifdef SIGNED
|
||||
return (IN_T) (v*HALF/INT_MAX);
|
||||
#else
|
||||
return (IN_T) (v+INT_MAX/2)*HALF/INT_MAX;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void glue(glue(conv_,IN_T),_to_stereo) (void *dst, const void *src,
|
||||
int samples)
|
||||
{
|
||||
st_sample_t *out = (st_sample_t *) dst;
|
||||
IN_T *in = (IN_T *) src;
|
||||
while (samples--) {
|
||||
out->l = glue(conv_,IN_T) (*in++);
|
||||
out->r = glue(conv_,IN_T) (*in++);
|
||||
out += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue(glue(conv_,IN_T),_to_mono) (void *dst, const void *src,
|
||||
int samples)
|
||||
{
|
||||
st_sample_t *out = (st_sample_t *) dst;
|
||||
IN_T *in = (IN_T *) src;
|
||||
while (samples--) {
|
||||
out->l = glue(conv_,IN_T) (in[0]);
|
||||
out->r = out->l;
|
||||
out += 1;
|
||||
in += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue(glue(clip_,IN_T),_from_stereo) (void *dst, const void *src,
|
||||
int samples)
|
||||
{
|
||||
st_sample_t *in = (st_sample_t *) src;
|
||||
IN_T *out = (IN_T *) dst;
|
||||
while (samples--) {
|
||||
*out++ = glue(clip_,IN_T) (in->l);
|
||||
*out++ = glue(clip_,IN_T) (in->r);
|
||||
in += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue(glue(clip_,IN_T),_from_mono) (void *dst, const void *src,
|
||||
int samples)
|
||||
{
|
||||
st_sample_t *in = (st_sample_t *) src;
|
||||
IN_T *out = (IN_T *) dst;
|
||||
while (samples--) {
|
||||
*out++ = glue(clip_,IN_T) (in->l + in->r);
|
||||
in += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#undef HALF
|
||||
#undef HALFT
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* QEMU NULL audio output driver
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
#include "audio/audio_int.h"
|
||||
|
||||
typedef struct NoVoice {
|
||||
HWVoice hw;
|
||||
int64_t old_ticks;
|
||||
} NoVoice;
|
||||
|
||||
#define dolog(...) AUD_log ("noaudio", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
static void no_hw_run (HWVoice *hw)
|
||||
{
|
||||
NoVoice *no = (NoVoice *) hw;
|
||||
int rpos, live, decr, samples;
|
||||
st_sample_t *src;
|
||||
int64_t now = qemu_get_clock (vm_clock);
|
||||
int64_t ticks = now - no->old_ticks;
|
||||
int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec;
|
||||
|
||||
if (bytes > INT_MAX)
|
||||
samples = INT_MAX >> hw->shift;
|
||||
else
|
||||
samples = bytes >> hw->shift;
|
||||
|
||||
live = pcm_hw_get_live (hw);
|
||||
if (live <= 0)
|
||||
return;
|
||||
|
||||
no->old_ticks = now;
|
||||
decr = audio_MIN (live, samples);
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int convert_samples = audio_MIN (samples, left_till_end_samples);
|
||||
|
||||
src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
|
||||
memset (src, 0, convert_samples * sizeof (st_sample_t));
|
||||
|
||||
rpos = (rpos + convert_samples) % hw->samples;
|
||||
samples -= convert_samples;
|
||||
}
|
||||
|
||||
pcm_hw_dec_live (hw, decr);
|
||||
hw->rpos = rpos;
|
||||
}
|
||||
|
||||
static int no_hw_write (SWVoice *sw, void *buf, int len)
|
||||
{
|
||||
return pcm_hw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int no_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
{
|
||||
hw->freq = freq;
|
||||
hw->nchannels = nchannels;
|
||||
hw->fmt = fmt;
|
||||
hw->bufsize = 4096;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void no_hw_fini (HWVoice *hw)
|
||||
{
|
||||
(void) hw;
|
||||
}
|
||||
|
||||
static int no_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *no_audio_init (void)
|
||||
{
|
||||
return &no_audio_init;
|
||||
}
|
||||
|
||||
static void no_audio_fini (void *opaque)
|
||||
{
|
||||
}
|
||||
|
||||
struct pcm_ops no_pcm_ops = {
|
||||
no_hw_init,
|
||||
no_hw_fini,
|
||||
no_hw_run,
|
||||
no_hw_write,
|
||||
no_hw_ctl
|
||||
};
|
||||
|
||||
struct audio_output_driver no_output_driver = {
|
||||
"none",
|
||||
no_audio_init,
|
||||
no_audio_fini,
|
||||
&no_pcm_ops,
|
||||
1,
|
||||
1,
|
||||
sizeof (NoVoice)
|
||||
};
|
||||
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* QEMU OSS audio output driver
|
||||
*
|
||||
* Copyright (c) 2003-2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include <assert.h>
|
||||
#include "vl.h"
|
||||
|
||||
#include "audio/audio_int.h"
|
||||
|
||||
typedef struct OSSVoice {
|
||||
HWVoice hw;
|
||||
void *pcm_buf;
|
||||
int fd;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
int mmapped;
|
||||
int old_optr;
|
||||
} OSSVoice;
|
||||
|
||||
#define dolog(...) AUD_log ("oss", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
#define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE"
|
||||
#define QC_OSS_NFRAGS "QEMU_OSS_NFRAGS"
|
||||
#define QC_OSS_MMAP "QEMU_OSS_MMAP"
|
||||
#define QC_OSS_DEV "QEMU_OSS_DEV"
|
||||
|
||||
#define errstr() strerror (errno)
|
||||
|
||||
static struct {
|
||||
int try_mmap;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
const char *dspname;
|
||||
} conf = {
|
||||
.try_mmap = 0,
|
||||
.nfrags = 4,
|
||||
.fragsize = 4096,
|
||||
.dspname = "/dev/dsp"
|
||||
};
|
||||
|
||||
struct oss_params {
|
||||
int freq;
|
||||
audfmt_e fmt;
|
||||
int nchannels;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
};
|
||||
|
||||
static int oss_hw_write (SWVoice *sw, void *buf, int len)
|
||||
{
|
||||
return pcm_hw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int AUD_to_ossfmt (audfmt_e fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AUD_FMT_S8: return AFMT_S8;
|
||||
case AUD_FMT_U8: return AFMT_U8;
|
||||
case AUD_FMT_S16: return AFMT_S16_LE;
|
||||
case AUD_FMT_U16: return AFMT_U16_LE;
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_to_audfmt (int fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AFMT_S8: return AUD_FMT_S8;
|
||||
case AFMT_U8: return AUD_FMT_U8;
|
||||
case AFMT_S16_LE: return AUD_FMT_S16;
|
||||
case AFMT_U16_LE: return AUD_FMT_U16;
|
||||
default:
|
||||
dolog ("Internal logic error: Unrecognized OSS audio format %d\n"
|
||||
"Aborting\n",
|
||||
fmt);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PCM
|
||||
static void oss_dump_pcm_info (struct oss_params *req, struct oss_params *obt)
|
||||
{
|
||||
dolog ("parameter | requested value | obtained value\n");
|
||||
dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
|
||||
dolog ("channels | %10d | %10d\n", req->nchannels, obt->nchannels);
|
||||
dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
|
||||
dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags);
|
||||
dolog ("fragsize | %10d | %10d\n", req->fragsize, obt->fragsize);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd)
|
||||
{
|
||||
int fd;
|
||||
int mmmmssss;
|
||||
audio_buf_info abinfo;
|
||||
int fmt, freq, nchannels;
|
||||
const char *dspname = conf.dspname;
|
||||
|
||||
fd = open (dspname, O_RDWR | O_NONBLOCK);
|
||||
if (-1 == fd) {
|
||||
dolog ("Could not initialize audio hardware. Failed to open `%s':\n"
|
||||
"Reason:%s\n",
|
||||
dspname,
|
||||
errstr ());
|
||||
return -1;
|
||||
}
|
||||
|
||||
freq = req->freq;
|
||||
nchannels = req->nchannels;
|
||||
fmt = req->fmt;
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
|
||||
dolog ("Could not initialize audio hardware\n"
|
||||
"Failed to set sample size\n"
|
||||
"Reason: %s\n",
|
||||
errstr ());
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
|
||||
dolog ("Could not initialize audio hardware\n"
|
||||
"Failed to set number of channels\n"
|
||||
"Reason: %s\n",
|
||||
errstr ());
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
|
||||
dolog ("Could not initialize audio hardware\n"
|
||||
"Failed to set frequency\n"
|
||||
"Reason: %s\n",
|
||||
errstr ());
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) {
|
||||
dolog ("Could not initialize audio hardware\n"
|
||||
"Failed to set non-blocking mode\n"
|
||||
"Reason: %s\n",
|
||||
errstr ());
|
||||
goto err;
|
||||
}
|
||||
|
||||
mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
|
||||
if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
|
||||
dolog ("Could not initialize audio hardware\n"
|
||||
"Failed to set buffer length (%d, %d)\n"
|
||||
"Reason:%s\n",
|
||||
conf.nfrags, conf.fragsize,
|
||||
errstr ());
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &abinfo)) {
|
||||
dolog ("Could not initialize audio hardware\n"
|
||||
"Failed to get buffer length\n"
|
||||
"Reason:%s\n",
|
||||
errstr ());
|
||||
goto err;
|
||||
}
|
||||
|
||||
obt->fmt = fmt;
|
||||
obt->nchannels = nchannels;
|
||||
obt->freq = freq;
|
||||
obt->nfrags = abinfo.fragstotal;
|
||||
obt->fragsize = abinfo.fragsize;
|
||||
*pfd = fd;
|
||||
|
||||
if ((req->fmt != obt->fmt) ||
|
||||
(req->nchannels != obt->nchannels) ||
|
||||
(req->freq != obt->freq) ||
|
||||
(req->fragsize != obt->fragsize) ||
|
||||
(req->nfrags != obt->nfrags)) {
|
||||
#ifdef DEBUG_PCM
|
||||
dolog ("Audio parameters mismatch\n");
|
||||
oss_dump_pcm_info (req, obt);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PCM
|
||||
oss_dump_pcm_info (req, obt);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
err:
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void oss_hw_run (HWVoice *hw)
|
||||
{
|
||||
OSSVoice *oss = (OSSVoice *) hw;
|
||||
int err, rpos, live, decr;
|
||||
int samples;
|
||||
uint8_t *dst;
|
||||
st_sample_t *src;
|
||||
struct audio_buf_info abinfo;
|
||||
struct count_info cntinfo;
|
||||
|
||||
live = pcm_hw_get_live (hw);
|
||||
if (live <= 0)
|
||||
return;
|
||||
|
||||
if (oss->mmapped) {
|
||||
int bytes;
|
||||
|
||||
err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
|
||||
if (err < 0) {
|
||||
dolog ("SNDCTL_DSP_GETOPTR failed\nReason: %s\n", errstr ());
|
||||
return;
|
||||
}
|
||||
|
||||
if (cntinfo.ptr == oss->old_optr) {
|
||||
if (abs (hw->samples - live) < 64)
|
||||
dolog ("overrun\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cntinfo.ptr > oss->old_optr) {
|
||||
bytes = cntinfo.ptr - oss->old_optr;
|
||||
}
|
||||
else {
|
||||
bytes = hw->bufsize + cntinfo.ptr - oss->old_optr;
|
||||
}
|
||||
|
||||
decr = audio_MIN (bytes >> hw->shift, live);
|
||||
}
|
||||
else {
|
||||
err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
|
||||
if (err < 0) {
|
||||
dolog ("SNDCTL_DSP_GETOSPACE failed\nReason: %s\n", errstr ());
|
||||
return;
|
||||
}
|
||||
|
||||
decr = audio_MIN (abinfo.bytes >> hw->shift, live);
|
||||
if (decr <= 0)
|
||||
return;
|
||||
}
|
||||
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int convert_samples = audio_MIN (samples, left_till_end_samples);
|
||||
|
||||
src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
|
||||
dst = advance (oss->pcm_buf, rpos << hw->shift);
|
||||
|
||||
hw->clip (dst, src, convert_samples);
|
||||
if (!oss->mmapped) {
|
||||
int written;
|
||||
|
||||
written = write (oss->fd, dst, convert_samples << hw->shift);
|
||||
/* XXX: follow errno recommendations ? */
|
||||
if (written == -1) {
|
||||
dolog ("Failed to write audio\nReason: %s\n", errstr ());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (written != convert_samples << hw->shift) {
|
||||
int wsamples = written >> hw->shift;
|
||||
int wbytes = wsamples << hw->shift;
|
||||
if (wbytes != written) {
|
||||
dolog ("Unaligned write %d, %d\n", wbytes, written);
|
||||
}
|
||||
memset (src, 0, wbytes);
|
||||
decr -= samples;
|
||||
rpos = (rpos + wsamples) % hw->samples;
|
||||
break;
|
||||
}
|
||||
}
|
||||
memset (src, 0, convert_samples * sizeof (st_sample_t));
|
||||
|
||||
rpos = (rpos + convert_samples) % hw->samples;
|
||||
samples -= convert_samples;
|
||||
}
|
||||
if (oss->mmapped) {
|
||||
oss->old_optr = cntinfo.ptr;
|
||||
}
|
||||
|
||||
pcm_hw_dec_live (hw, decr);
|
||||
hw->rpos = rpos;
|
||||
}
|
||||
|
||||
static void oss_hw_fini (HWVoice *hw)
|
||||
{
|
||||
int err;
|
||||
OSSVoice *oss = (OSSVoice *) hw;
|
||||
|
||||
ldebug ("oss_hw_fini\n");
|
||||
err = close (oss->fd);
|
||||
if (err) {
|
||||
dolog ("Failed to close OSS descriptor\nReason: %s\n", errstr ());
|
||||
}
|
||||
oss->fd = -1;
|
||||
|
||||
if (oss->pcm_buf) {
|
||||
if (oss->mmapped) {
|
||||
err = munmap (oss->pcm_buf, hw->bufsize);
|
||||
if (err) {
|
||||
dolog ("Failed to unmap OSS buffer\nReason: %s\n",
|
||||
errstr ());
|
||||
}
|
||||
}
|
||||
else {
|
||||
qemu_free (oss->pcm_buf);
|
||||
}
|
||||
oss->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
{
|
||||
OSSVoice *oss = (OSSVoice *) hw;
|
||||
struct oss_params req, obt;
|
||||
|
||||
assert (!oss->fd);
|
||||
req.fmt = AUD_to_ossfmt (fmt);
|
||||
req.freq = freq;
|
||||
req.nchannels = nchannels;
|
||||
req.fragsize = conf.fragsize;
|
||||
req.nfrags = conf.nfrags;
|
||||
|
||||
if (oss_open (&req, &obt, &oss->fd))
|
||||
return -1;
|
||||
|
||||
hw->freq = obt.freq;
|
||||
hw->fmt = oss_to_audfmt (obt.fmt);
|
||||
hw->nchannels = obt.nchannels;
|
||||
|
||||
oss->nfrags = obt.nfrags;
|
||||
oss->fragsize = obt.fragsize;
|
||||
hw->bufsize = obt.nfrags * obt.fragsize;
|
||||
|
||||
oss->mmapped = 0;
|
||||
if (conf.try_mmap) {
|
||||
oss->pcm_buf = mmap (0, hw->bufsize, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, oss->fd, 0);
|
||||
if (oss->pcm_buf == MAP_FAILED) {
|
||||
dolog ("Failed to mmap OSS device\nReason: %s\n",
|
||||
errstr ());
|
||||
} else {
|
||||
int err;
|
||||
int trig = 0;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
|
||||
errstr ());
|
||||
}
|
||||
else {
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
"Reason: %s\n", errstr ());
|
||||
}
|
||||
else {
|
||||
oss->mmapped = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
err = munmap (oss->pcm_buf, hw->bufsize);
|
||||
if (err) {
|
||||
dolog ("Failed to unmap OSS device\nReason: %s\n",
|
||||
errstr ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
oss->pcm_buf = qemu_mallocz (hw->bufsize);
|
||||
if (!oss->pcm_buf) {
|
||||
close (oss->fd);
|
||||
oss->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oss_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
{
|
||||
int trig;
|
||||
OSSVoice *oss = (OSSVoice *) hw;
|
||||
|
||||
if (!oss->mmapped)
|
||||
return 0;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
ldebug ("enabling voice\n");
|
||||
pcm_hw_clear (hw, oss->pcm_buf, hw->samples);
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
"Reason: %s\n", errstr ());
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
ldebug ("disabling voice\n");
|
||||
trig = 0;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
|
||||
errstr ());
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *oss_audio_init (void)
|
||||
{
|
||||
conf.fragsize = audio_get_conf_int (QC_OSS_FRAGSIZE, conf.fragsize);
|
||||
conf.nfrags = audio_get_conf_int (QC_OSS_NFRAGS, conf.nfrags);
|
||||
conf.try_mmap = audio_get_conf_int (QC_OSS_MMAP, conf.try_mmap);
|
||||
conf.dspname = audio_get_conf_str (QC_OSS_DEV, conf.dspname);
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void oss_audio_fini (void *opaque)
|
||||
{
|
||||
}
|
||||
|
||||
struct pcm_ops oss_pcm_ops = {
|
||||
oss_hw_init,
|
||||
oss_hw_fini,
|
||||
oss_hw_run,
|
||||
oss_hw_write,
|
||||
oss_hw_ctl
|
||||
};
|
||||
|
||||
struct audio_output_driver oss_output_driver = {
|
||||
"oss",
|
||||
oss_audio_init,
|
||||
oss_audio_fini,
|
||||
&oss_pcm_ops,
|
||||
1,
|
||||
INT_MAX,
|
||||
sizeof (OSSVoice)
|
||||
};
|
||||
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* QEMU SDL audio output driver
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <SDL.h>
|
||||
#include <SDL_thread.h>
|
||||
#include "vl.h"
|
||||
|
||||
#include "audio/audio_int.h"
|
||||
|
||||
typedef struct SDLVoice {
|
||||
HWVoice hw;
|
||||
} SDLVoice;
|
||||
|
||||
#define dolog(...) AUD_log ("sdl", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
#define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES"
|
||||
|
||||
#define errstr() SDL_GetError ()
|
||||
|
||||
static struct {
|
||||
int nb_samples;
|
||||
} conf = {
|
||||
1024
|
||||
};
|
||||
|
||||
struct SDLAudioState {
|
||||
int exit;
|
||||
SDL_mutex *mutex;
|
||||
SDL_sem *sem;
|
||||
int initialized;
|
||||
} glob_sdl;
|
||||
typedef struct SDLAudioState SDLAudioState;
|
||||
|
||||
static void sdl_hw_run (HWVoice *hw)
|
||||
{
|
||||
(void) hw;
|
||||
}
|
||||
|
||||
static int sdl_lock (SDLAudioState *s)
|
||||
{
|
||||
if (SDL_LockMutex (s->mutex)) {
|
||||
dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_unlock (SDLAudioState *s)
|
||||
{
|
||||
if (SDL_UnlockMutex (s->mutex)) {
|
||||
dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_post (SDLAudioState *s)
|
||||
{
|
||||
if (SDL_SemPost (s->sem)) {
|
||||
dolog ("SDL_SemPost failed\nReason: %s\n", errstr ());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_wait (SDLAudioState *s)
|
||||
{
|
||||
if (SDL_SemWait (s->sem)) {
|
||||
dolog ("SDL_SemWait failed\nReason: %s\n", errstr ());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_unlock_and_post (SDLAudioState *s)
|
||||
{
|
||||
if (sdl_unlock (s))
|
||||
return -1;
|
||||
|
||||
return sdl_post (s);
|
||||
}
|
||||
|
||||
static int sdl_hw_write (SWVoice *sw, void *buf, int len)
|
||||
{
|
||||
int ret;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
sdl_lock (s);
|
||||
ret = pcm_hw_write (sw, buf, len);
|
||||
sdl_unlock_and_post (s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int AUD_to_sdlfmt (audfmt_e fmt, int *shift)
|
||||
{
|
||||
*shift = 0;
|
||||
switch (fmt) {
|
||||
case AUD_FMT_S8: return AUDIO_S8;
|
||||
case AUD_FMT_U8: return AUDIO_U8;
|
||||
case AUD_FMT_S16: *shift = 1; return AUDIO_S16LSB;
|
||||
case AUD_FMT_U16: *shift = 1; return AUDIO_U16LSB;
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static int sdl_to_audfmt (int fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AUDIO_S8: return AUD_FMT_S8;
|
||||
case AUDIO_U8: return AUD_FMT_U8;
|
||||
case AUDIO_S16LSB: return AUD_FMT_S16;
|
||||
case AUDIO_U16LSB: return AUD_FMT_U16;
|
||||
default:
|
||||
dolog ("Internal logic error: Unrecognized SDL audio format %d\n"
|
||||
"Aborting\n", fmt);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = SDL_OpenAudio (req, obt);
|
||||
if (status) {
|
||||
dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ());
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void sdl_close (SDLAudioState *s)
|
||||
{
|
||||
if (s->initialized) {
|
||||
sdl_lock (s);
|
||||
s->exit = 1;
|
||||
sdl_unlock_and_post (s);
|
||||
SDL_PauseAudio (1);
|
||||
SDL_CloseAudio ();
|
||||
s->initialized = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
||||
{
|
||||
SDLVoice *sdl = opaque;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
HWVoice *hw = &sdl->hw;
|
||||
int samples = len >> hw->shift;
|
||||
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (samples) {
|
||||
int to_mix, live, decr;
|
||||
|
||||
/* dolog ("in callback samples=%d\n", samples); */
|
||||
sdl_wait (s);
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
sdl_lock (s);
|
||||
live = pcm_hw_get_live (hw);
|
||||
if (live <= 0)
|
||||
goto again;
|
||||
|
||||
/* dolog ("in callback live=%d\n", live); */
|
||||
to_mix = audio_MIN (samples, live);
|
||||
decr = to_mix;
|
||||
while (to_mix) {
|
||||
int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
|
||||
st_sample_t *src = hw->mix_buf + hw->rpos;
|
||||
|
||||
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
|
||||
hw->clip (buf, src, chunk);
|
||||
memset (src, 0, chunk * sizeof (st_sample_t));
|
||||
hw->rpos = (hw->rpos + chunk) % hw->samples;
|
||||
to_mix -= chunk;
|
||||
buf += chunk << hw->shift;
|
||||
}
|
||||
samples -= decr;
|
||||
pcm_hw_dec_live (hw, decr);
|
||||
|
||||
again:
|
||||
sdl_unlock (s);
|
||||
}
|
||||
/* dolog ("done len=%d\n", len); */
|
||||
}
|
||||
|
||||
static void sdl_hw_fini (HWVoice *hw)
|
||||
{
|
||||
ldebug ("sdl_hw_fini %d fixed=%d\n",
|
||||
glob_sdl.initialized, audio_state.fixed_format);
|
||||
sdl_close (&glob_sdl);
|
||||
}
|
||||
|
||||
static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
{
|
||||
SDLVoice *sdl = (SDLVoice *) hw;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
SDL_AudioSpec req, obt;
|
||||
int shift;
|
||||
|
||||
ldebug ("sdl_hw_init %d freq=%d fixed=%d\n",
|
||||
s->initialized, freq, audio_state.fixed_format);
|
||||
|
||||
if (nchannels != 2) {
|
||||
dolog ("Bogus channel count %d\n", nchannels);
|
||||
return -1;
|
||||
}
|
||||
|
||||
req.freq = freq;
|
||||
req.format = AUD_to_sdlfmt (fmt, &shift);
|
||||
req.channels = nchannels;
|
||||
req.samples = conf.nb_samples;
|
||||
shift <<= nchannels == 2;
|
||||
|
||||
req.callback = sdl_callback;
|
||||
req.userdata = sdl;
|
||||
|
||||
if (sdl_open (&req, &obt))
|
||||
return -1;
|
||||
|
||||
hw->freq = obt.freq;
|
||||
hw->fmt = sdl_to_audfmt (obt.format);
|
||||
hw->nchannels = obt.channels;
|
||||
hw->bufsize = obt.samples << shift;
|
||||
|
||||
s->initialized = 1;
|
||||
s->exit = 0;
|
||||
SDL_PauseAudio (0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
SDL_PauseAudio (0);
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
SDL_PauseAudio (1);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *sdl_audio_init (void)
|
||||
{
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
conf.nb_samples = audio_get_conf_int (QC_SDL_SAMPLES, conf.nb_samples);
|
||||
|
||||
if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
|
||||
dolog ("SDL failed to initialize audio subsystem\nReason: %s\n",
|
||||
errstr ());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->mutex = SDL_CreateMutex ();
|
||||
if (!s->mutex) {
|
||||
dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ());
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->sem = SDL_CreateSemaphore (0);
|
||||
if (!s->sem) {
|
||||
dolog ("Failed to create SDL semaphore\nReason: %s\n", errstr ());
|
||||
SDL_DestroyMutex (s->mutex);
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void sdl_audio_fini (void *opaque)
|
||||
{
|
||||
SDLAudioState *s = opaque;
|
||||
sdl_close (s);
|
||||
SDL_DestroySemaphore (s->sem);
|
||||
SDL_DestroyMutex (s->mutex);
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
}
|
||||
|
||||
struct pcm_ops sdl_pcm_ops = {
|
||||
sdl_hw_init,
|
||||
sdl_hw_fini,
|
||||
sdl_hw_run,
|
||||
sdl_hw_write,
|
||||
sdl_hw_ctl
|
||||
};
|
||||
|
||||
struct audio_output_driver sdl_output_driver = {
|
||||
"sdl",
|
||||
sdl_audio_init,
|
||||
sdl_audio_fini,
|
||||
&sdl_pcm_ops,
|
||||
1,
|
||||
1,
|
||||
sizeof (SDLVoice)
|
||||
};
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* QEMU WAV audio output driver
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
#include "audio/audio_int.h"
|
||||
|
||||
typedef struct WAVVoice {
|
||||
HWVoice hw;
|
||||
QEMUFile *f;
|
||||
int64_t old_ticks;
|
||||
void *pcm_buf;
|
||||
int total_samples;
|
||||
} WAVVoice;
|
||||
|
||||
#define dolog(...) AUD_log ("wav", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
static struct {
|
||||
const char *wav_path;
|
||||
} conf = {
|
||||
.wav_path = "qemu.wav"
|
||||
};
|
||||
|
||||
static void wav_hw_run (HWVoice *hw)
|
||||
{
|
||||
WAVVoice *wav = (WAVVoice *) hw;
|
||||
int rpos, live, decr, samples;
|
||||
uint8_t *dst;
|
||||
st_sample_t *src;
|
||||
int64_t now = qemu_get_clock (vm_clock);
|
||||
int64_t ticks = now - wav->old_ticks;
|
||||
int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec;
|
||||
|
||||
if (bytes > INT_MAX)
|
||||
samples = INT_MAX >> hw->shift;
|
||||
else
|
||||
samples = bytes >> hw->shift;
|
||||
|
||||
live = pcm_hw_get_live (hw);
|
||||
if (live <= 0)
|
||||
return;
|
||||
|
||||
wav->old_ticks = now;
|
||||
decr = audio_MIN (live, samples);
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int convert_samples = audio_MIN (samples, left_till_end_samples);
|
||||
|
||||
src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
|
||||
dst = advance (wav->pcm_buf, rpos << hw->shift);
|
||||
|
||||
hw->clip (dst, src, convert_samples);
|
||||
qemu_put_buffer (wav->f, dst, convert_samples << hw->shift);
|
||||
memset (src, 0, convert_samples * sizeof (st_sample_t));
|
||||
|
||||
rpos = (rpos + convert_samples) % hw->samples;
|
||||
samples -= convert_samples;
|
||||
wav->total_samples += convert_samples;
|
||||
}
|
||||
|
||||
pcm_hw_dec_live (hw, decr);
|
||||
hw->rpos = rpos;
|
||||
}
|
||||
|
||||
static int wav_hw_write (SWVoice *sw, void *buf, int len)
|
||||
{
|
||||
return pcm_hw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
/* VICE code: Store number as little endian. */
|
||||
static void le_store (uint8_t *buf, uint32_t val, int len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
buf[i] = (uint8_t) (val & 0xff);
|
||||
val >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
{
|
||||
WAVVoice *wav = (WAVVoice *) hw;
|
||||
int bits16 = 0, stereo = audio_state.fixed_channels == 2;
|
||||
uint8_t hdr[] = {
|
||||
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
|
||||
0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
|
||||
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
switch (audio_state.fixed_fmt) {
|
||||
case AUD_FMT_S8:
|
||||
case AUD_FMT_U8:
|
||||
break;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
case AUD_FMT_U16:
|
||||
bits16 = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
hdr[34] = bits16 ? 0x10 : 0x08;
|
||||
hw->freq = 44100;
|
||||
hw->nchannels = stereo ? 2 : 1;
|
||||
hw->fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
|
||||
hw->bufsize = 4096;
|
||||
wav->pcm_buf = qemu_mallocz (hw->bufsize);
|
||||
if (!wav->pcm_buf)
|
||||
return -1;
|
||||
|
||||
le_store (hdr + 22, hw->nchannels, 2);
|
||||
le_store (hdr + 24, hw->freq, 4);
|
||||
le_store (hdr + 28, hw->freq << (bits16 + stereo), 4);
|
||||
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
|
||||
|
||||
wav->f = fopen (conf.wav_path, "wb");
|
||||
if (!wav->f) {
|
||||
dolog ("failed to open wave file `%s'\nReason: %s\n",
|
||||
conf.wav_path, strerror (errno));
|
||||
qemu_free (wav->pcm_buf);
|
||||
wav->pcm_buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
qemu_put_buffer (wav->f, hdr, sizeof (hdr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wav_hw_fini (HWVoice *hw)
|
||||
{
|
||||
WAVVoice *wav = (WAVVoice *) hw;
|
||||
int stereo = hw->nchannels == 2;
|
||||
uint8_t rlen[4];
|
||||
uint8_t dlen[4];
|
||||
uint32_t rifflen = (wav->total_samples << stereo) + 36;
|
||||
uint32_t datalen = wav->total_samples << stereo;
|
||||
|
||||
if (!wav->f || !hw->active)
|
||||
return;
|
||||
|
||||
le_store (rlen, rifflen, 4);
|
||||
le_store (dlen, datalen, 4);
|
||||
|
||||
qemu_fseek (wav->f, 4, SEEK_SET);
|
||||
qemu_put_buffer (wav->f, rlen, 4);
|
||||
|
||||
qemu_fseek (wav->f, 32, SEEK_CUR);
|
||||
qemu_put_buffer (wav->f, dlen, 4);
|
||||
|
||||
fclose (wav->f);
|
||||
wav->f = NULL;
|
||||
|
||||
qemu_free (wav->pcm_buf);
|
||||
wav->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static int wav_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *wav_audio_init (void)
|
||||
{
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void wav_audio_fini (void *opaque)
|
||||
{
|
||||
ldebug ("wav_fini");
|
||||
}
|
||||
|
||||
struct pcm_ops wav_pcm_ops = {
|
||||
wav_hw_init,
|
||||
wav_hw_fini,
|
||||
wav_hw_run,
|
||||
wav_hw_write,
|
||||
wav_hw_ctl
|
||||
};
|
||||
|
||||
struct audio_output_driver wav_output_driver = {
|
||||
"wav",
|
||||
wav_audio_init,
|
||||
wav_audio_fini,
|
||||
&wav_pcm_ops,
|
||||
1,
|
||||
1,
|
||||
sizeof (WAVVoice)
|
||||
};
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Block driver for the various disk image formats used by Bochs
|
||||
* Currently only for "growing" type in read-only mode
|
||||
*
|
||||
* Copyright (c) 2005 Alex Beregszaszi
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
#include "block_int.h"
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
#define HEADER_MAGIC "Bochs Virtual HD Image"
|
||||
#define HEADER_VERSION 0x00010000
|
||||
#define HEADER_SIZE 512
|
||||
|
||||
#define REDOLOG_TYPE "Redolog"
|
||||
#define GROWING_TYPE "Growing"
|
||||
|
||||
// not allocated: 0xffffffff
|
||||
|
||||
// always little-endian
|
||||
struct bochs_header {
|
||||
char magic[32]; // "Bochs Virtual HD Image"
|
||||
char type[16]; // "Redolog"
|
||||
char subtype[16]; // "Undoable" / "Volatile" / "Growing"
|
||||
uint32_t version;
|
||||
uint32_t header; // size of header
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t catalog; // num of entries
|
||||
uint32_t bitmap; // bitmap size
|
||||
uint32_t extent; // extent size
|
||||
uint64_t disk; // disk size
|
||||
char padding[HEADER_SIZE - 64 - 8 - 20];
|
||||
} redolog;
|
||||
char padding[HEADER_SIZE - 64 - 8];
|
||||
} extra;
|
||||
};
|
||||
|
||||
typedef struct BDRVBochsState {
|
||||
int fd;
|
||||
|
||||
uint32_t *catalog_bitmap;
|
||||
int catalog_size;
|
||||
|
||||
int data_offset;
|
||||
|
||||
int bitmap_blocks;
|
||||
int extent_blocks;
|
||||
int extent_size;
|
||||
} BDRVBochsState;
|
||||
|
||||
static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const struct bochs_header *bochs = (const void *)buf;
|
||||
|
||||
if (buf_size < HEADER_SIZE)
|
||||
return 0;
|
||||
|
||||
if (!strcmp(bochs->magic, HEADER_MAGIC) &&
|
||||
!strcmp(bochs->type, REDOLOG_TYPE) &&
|
||||
!strcmp(bochs->subtype, GROWING_TYPE) &&
|
||||
(le32_to_cpu(bochs->version) == HEADER_VERSION))
|
||||
return 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bochs_open(BlockDriverState *bs, const char *filename)
|
||||
{
|
||||
BDRVBochsState *s = bs->opaque;
|
||||
int fd, i;
|
||||
struct bochs_header bochs;
|
||||
|
||||
fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0) {
|
||||
fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
bs->read_only = 1; // no write support yet
|
||||
|
||||
s->fd = fd;
|
||||
|
||||
if (read(fd, &bochs, sizeof(bochs)) != sizeof(bochs)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (strcmp(bochs.magic, HEADER_MAGIC) ||
|
||||
strcmp(bochs.type, REDOLOG_TYPE) ||
|
||||
strcmp(bochs.subtype, GROWING_TYPE) ||
|
||||
(le32_to_cpu(bochs.version) != HEADER_VERSION)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
|
||||
|
||||
lseek(s->fd, le32_to_cpu(bochs.header), SEEK_SET);
|
||||
|
||||
s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog);
|
||||
s->catalog_bitmap = qemu_malloc(s->catalog_size * 4);
|
||||
if (!s->catalog_bitmap)
|
||||
goto fail;
|
||||
if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) !=
|
||||
s->catalog_size * 4)
|
||||
goto fail;
|
||||
for (i = 0; i < s->catalog_size; i++)
|
||||
le32_to_cpus(&s->catalog_bitmap[i]);
|
||||
|
||||
s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
|
||||
|
||||
s->bitmap_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.bitmap) - 1) / 512;
|
||||
s->extent_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.extent) - 1) / 512;
|
||||
|
||||
s->extent_size = le32_to_cpu(bochs.extra.redolog.extent);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
{
|
||||
BDRVBochsState *s = bs->opaque;
|
||||
int64_t offset = sector_num * 512;
|
||||
int64_t extent_index, extent_offset, bitmap_offset, block_offset;
|
||||
char bitmap_entry;
|
||||
|
||||
// seek to sector
|
||||
extent_index = offset / s->extent_size;
|
||||
extent_offset = (offset % s->extent_size) / 512;
|
||||
|
||||
if (s->catalog_bitmap[extent_index] == 0xffffffff)
|
||||
{
|
||||
// fprintf(stderr, "page not allocated [%x - %x:%x]\n",
|
||||
// sector_num, extent_index, extent_offset);
|
||||
return -1; // not allocated
|
||||
}
|
||||
|
||||
bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] *
|
||||
(s->extent_blocks + s->bitmap_blocks));
|
||||
block_offset = bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
|
||||
|
||||
// fprintf(stderr, "sect: %x [ext i: %x o: %x] -> %x bitmap: %x block: %x\n",
|
||||
// sector_num, extent_index, extent_offset,
|
||||
// le32_to_cpu(s->catalog_bitmap[extent_index]),
|
||||
// bitmap_offset, block_offset);
|
||||
|
||||
// read in bitmap for current extent
|
||||
lseek(s->fd, bitmap_offset + (extent_offset / 8), SEEK_SET);
|
||||
|
||||
read(s->fd, &bitmap_entry, 1);
|
||||
|
||||
if (!((bitmap_entry >> (extent_offset % 8)) & 1))
|
||||
{
|
||||
// fprintf(stderr, "sector (%x) in bitmap not allocated\n",
|
||||
// sector_num);
|
||||
return -1; // not allocated
|
||||
}
|
||||
|
||||
lseek(s->fd, block_offset, SEEK_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bochs_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVBochsState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
if (!seek_to_sector(bs, sector_num))
|
||||
{
|
||||
ret = read(s->fd, buf, 512);
|
||||
if (ret != 512)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
memset(buf, 0, 512);
|
||||
nb_sectors--;
|
||||
sector_num++;
|
||||
buf += 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bochs_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBochsState *s = bs->opaque;
|
||||
qemu_free(s->catalog_bitmap);
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_bochs = {
|
||||
"bochs",
|
||||
sizeof(BDRVBochsState),
|
||||
bochs_probe,
|
||||
bochs_open,
|
||||
bochs_read,
|
||||
NULL,
|
||||
bochs_close,
|
||||
};
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* QEMU Block driver for CLOOP images
|
||||
*
|
||||
* Copyright (c) 2004 Johannes E. Schindelin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
#include "block_int.h"
|
||||
#include <zlib.h>
|
||||
|
||||
typedef struct BDRVCloopState {
|
||||
int fd;
|
||||
uint32_t block_size;
|
||||
uint32_t n_blocks;
|
||||
uint64_t* offsets;
|
||||
uint32_t sectors_per_block;
|
||||
uint32_t current_block;
|
||||
char* compressed_block;
|
||||
char* uncompressed_block;
|
||||
z_stream zstream;
|
||||
} BDRVCloopState;
|
||||
|
||||
static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const char* magic_version_2_0="#!/bin/sh\n"
|
||||
"#V2.0 Format\n"
|
||||
"modprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n";
|
||||
int length=strlen(magic_version_2_0);
|
||||
if(length>buf_size)
|
||||
length=buf_size;
|
||||
if(!memcmp(magic_version_2_0,buf,length))
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cloop_open(BlockDriverState *bs, const char *filename)
|
||||
{
|
||||
BDRVCloopState *s = bs->opaque;
|
||||
uint32_t offsets_size,max_compressed_block_size=1,i;
|
||||
|
||||
s->fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (s->fd < 0)
|
||||
return -1;
|
||||
bs->read_only = 1;
|
||||
|
||||
/* read header */
|
||||
if(lseek(s->fd,128,SEEK_SET)<0) {
|
||||
cloop_close:
|
||||
close(s->fd);
|
||||
return -1;
|
||||
}
|
||||
if(read(s->fd,&s->block_size,4)<4)
|
||||
goto cloop_close;
|
||||
s->block_size=be32_to_cpu(s->block_size);
|
||||
if(read(s->fd,&s->n_blocks,4)<4)
|
||||
goto cloop_close;
|
||||
s->n_blocks=be32_to_cpu(s->n_blocks);
|
||||
|
||||
/* read offsets */
|
||||
offsets_size=s->n_blocks*sizeof(uint64_t);
|
||||
if(!(s->offsets=(uint64_t*)malloc(offsets_size)))
|
||||
goto cloop_close;
|
||||
if(read(s->fd,s->offsets,offsets_size)<offsets_size)
|
||||
goto cloop_close;
|
||||
for(i=0;i<s->n_blocks;i++) {
|
||||
s->offsets[i]=be64_to_cpu(s->offsets[i]);
|
||||
if(i>0) {
|
||||
uint32_t size=s->offsets[i]-s->offsets[i-1];
|
||||
if(size>max_compressed_block_size)
|
||||
max_compressed_block_size=size;
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize zlib engine */
|
||||
if(!(s->compressed_block=(char*)malloc(max_compressed_block_size+1)))
|
||||
goto cloop_close;
|
||||
if(!(s->uncompressed_block=(char*)malloc(s->block_size)))
|
||||
goto cloop_close;
|
||||
if(inflateInit(&s->zstream) != Z_OK)
|
||||
goto cloop_close;
|
||||
s->current_block=s->n_blocks;
|
||||
|
||||
s->sectors_per_block = s->block_size/512;
|
||||
bs->total_sectors = s->n_blocks*s->sectors_per_block;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int cloop_read_block(BDRVCloopState *s,int block_num)
|
||||
{
|
||||
if(s->current_block != block_num) {
|
||||
int ret;
|
||||
uint32_t bytes = s->offsets[block_num+1]-s->offsets[block_num];
|
||||
|
||||
lseek(s->fd, s->offsets[block_num], SEEK_SET);
|
||||
ret = read(s->fd, s->compressed_block, bytes);
|
||||
if (ret != bytes)
|
||||
return -1;
|
||||
|
||||
s->zstream.next_in = s->compressed_block;
|
||||
s->zstream.avail_in = bytes;
|
||||
s->zstream.next_out = s->uncompressed_block;
|
||||
s->zstream.avail_out = s->block_size;
|
||||
ret = inflateReset(&s->zstream);
|
||||
if(ret != Z_OK)
|
||||
return -1;
|
||||
ret = inflate(&s->zstream, Z_FINISH);
|
||||
if(ret != Z_STREAM_END || s->zstream.total_out != s->block_size)
|
||||
return -1;
|
||||
|
||||
s->current_block = block_num;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cloop_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVCloopState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for(i=0;i<nb_sectors;i++) {
|
||||
uint32_t sector_offset_in_block=((sector_num+i)%s->sectors_per_block),
|
||||
block_num=(sector_num+i)/s->sectors_per_block;
|
||||
if(cloop_read_block(s, block_num) != 0)
|
||||
return -1;
|
||||
memcpy(buf+i*512,s->uncompressed_block+sector_offset_in_block*512,512);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cloop_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVCloopState *s = bs->opaque;
|
||||
close(s->fd);
|
||||
if(s->n_blocks>0)
|
||||
free(s->offsets);
|
||||
free(s->compressed_block);
|
||||
free(s->uncompressed_block);
|
||||
inflateEnd(&s->zstream);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_cloop = {
|
||||
"cloop",
|
||||
sizeof(BDRVCloopState),
|
||||
cloop_probe,
|
||||
cloop_open,
|
||||
cloop_read,
|
||||
NULL,
|
||||
cloop_close,
|
||||
};
|
||||
|
||||
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Block driver for the COW format
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
#include "vl.h"
|
||||
#include "block_int.h"
|
||||
#include <sys/mman.h>
|
||||
|
||||
/**************************************************************/
|
||||
/* COW block driver using file system holes */
|
||||
|
||||
/* user mode linux compatible COW file */
|
||||
#define COW_MAGIC 0x4f4f4f4d /* MOOO */
|
||||
#define COW_VERSION 2
|
||||
|
||||
struct cow_header_v2 {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
char backing_file[1024];
|
||||
int32_t mtime;
|
||||
uint64_t size;
|
||||
uint32_t sectorsize;
|
||||
};
|
||||
|
||||
typedef struct BDRVCowState {
|
||||
int fd;
|
||||
uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */
|
||||
uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */
|
||||
int cow_bitmap_size;
|
||||
int64_t cow_sectors_offset;
|
||||
} BDRVCowState;
|
||||
|
||||
static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const struct cow_header_v2 *cow_header = (const void *)buf;
|
||||
|
||||
if (be32_to_cpu(cow_header->magic) == COW_MAGIC &&
|
||||
be32_to_cpu(cow_header->version) == COW_VERSION)
|
||||
return 100;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cow_open(BlockDriverState *bs, const char *filename)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int fd;
|
||||
struct cow_header_v2 cow_header;
|
||||
int64_t size;
|
||||
|
||||
fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0) {
|
||||
fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
}
|
||||
s->fd = fd;
|
||||
/* see if it is a cow image */
|
||||
if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (be32_to_cpu(cow_header.magic) != COW_MAGIC ||
|
||||
be32_to_cpu(cow_header.version) != COW_VERSION) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* cow image found */
|
||||
size = be64_to_cpu(cow_header.size);
|
||||
bs->total_sectors = size / 512;
|
||||
|
||||
pstrcpy(bs->backing_file, sizeof(bs->backing_file),
|
||||
cow_header.backing_file);
|
||||
|
||||
#if 0
|
||||
if (cow_header.backing_file[0] != '\0') {
|
||||
if (stat(cow_header.backing_file, &st) != 0) {
|
||||
fprintf(stderr, "%s: could not find original disk image '%s'\n", filename, cow_header.backing_file);
|
||||
goto fail;
|
||||
}
|
||||
if (st.st_mtime != be32_to_cpu(cow_header.mtime)) {
|
||||
fprintf(stderr, "%s: original raw disk image '%s' does not match saved timestamp\n", filename, cow_header.backing_file);
|
||||
goto fail;
|
||||
}
|
||||
fd = open(cow_header.backing_file, O_RDONLY | O_LARGEFILE);
|
||||
if (fd < 0)
|
||||
goto fail;
|
||||
bs->fd = fd;
|
||||
}
|
||||
#endif
|
||||
/* mmap the bitmap */
|
||||
s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
|
||||
s->cow_bitmap_addr = mmap(get_mmap_addr(s->cow_bitmap_size),
|
||||
s->cow_bitmap_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, s->fd, 0);
|
||||
if (s->cow_bitmap_addr == MAP_FAILED)
|
||||
goto fail;
|
||||
s->cow_bitmap = s->cow_bitmap_addr + sizeof(cow_header);
|
||||
s->cow_sectors_offset = (s->cow_bitmap_size + 511) & ~511;
|
||||
return 0;
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void cow_set_bit(uint8_t *bitmap, int64_t bitnum)
|
||||
{
|
||||
bitmap[bitnum / 8] |= (1 << (bitnum%8));
|
||||
}
|
||||
|
||||
static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum)
|
||||
{
|
||||
return !!(bitmap[bitnum / 8] & (1 << (bitnum%8)));
|
||||
}
|
||||
|
||||
|
||||
/* Return true if first block has been changed (ie. current version is
|
||||
* in COW file). Set the number of continuous blocks for which that
|
||||
* is true. */
|
||||
static inline int is_changed(uint8_t *bitmap,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
int *num_same)
|
||||
{
|
||||
int changed;
|
||||
|
||||
if (!bitmap || nb_sectors == 0) {
|
||||
*num_same = nb_sectors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
changed = is_bit_set(bitmap, sector_num);
|
||||
for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) {
|
||||
if (is_bit_set(bitmap, sector_num + *num_same) != changed)
|
||||
break;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
return is_changed(s->cow_bitmap, sector_num, nb_sectors, pnum);
|
||||
}
|
||||
|
||||
static int cow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int ret, n;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
if (is_changed(s->cow_bitmap, sector_num, nb_sectors, &n)) {
|
||||
lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET);
|
||||
ret = read(s->fd, buf, n * 512);
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
} else {
|
||||
memset(buf, 0, n * 512);
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int ret, i;
|
||||
|
||||
lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET);
|
||||
ret = write(s->fd, buf, nb_sectors * 512);
|
||||
if (ret != nb_sectors * 512)
|
||||
return -1;
|
||||
for (i = 0; i < nb_sectors; i++)
|
||||
cow_set_bit(s->cow_bitmap, sector_num + i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cow_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
munmap(s->cow_bitmap_addr, s->cow_bitmap_size);
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
static int cow_create(const char *filename, int64_t image_sectors,
|
||||
const char *image_filename, int flags)
|
||||
{
|
||||
int fd, cow_fd;
|
||||
struct cow_header_v2 cow_header;
|
||||
struct stat st;
|
||||
|
||||
if (flags)
|
||||
return -ENOTSUP;
|
||||
|
||||
cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
if (cow_fd < 0)
|
||||
return -1;
|
||||
memset(&cow_header, 0, sizeof(cow_header));
|
||||
cow_header.magic = cpu_to_be32(COW_MAGIC);
|
||||
cow_header.version = cpu_to_be32(COW_VERSION);
|
||||
if (image_filename) {
|
||||
fd = open(image_filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0) {
|
||||
close(cow_fd);
|
||||
return -1;
|
||||
}
|
||||
if (fstat(fd, &st) != 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
cow_header.mtime = cpu_to_be32(st.st_mtime);
|
||||
realpath(image_filename, cow_header.backing_file);
|
||||
}
|
||||
cow_header.sectorsize = cpu_to_be32(512);
|
||||
cow_header.size = cpu_to_be64(image_sectors * 512);
|
||||
write(cow_fd, &cow_header, sizeof(cow_header));
|
||||
/* resize to include at least all the bitmap */
|
||||
ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3));
|
||||
close(cow_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BlockDriver bdrv_cow = {
|
||||
"cow",
|
||||
sizeof(BDRVCowState),
|
||||
cow_probe,
|
||||
cow_open,
|
||||
cow_read,
|
||||
cow_write,
|
||||
cow_close,
|
||||
cow_create,
|
||||
cow_is_allocated,
|
||||
};
|
||||
#endif
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* QEMU Block driver for DMG images
|
||||
*
|
||||
* Copyright (c) 2004 Johannes E. Schindelin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
#include "block_int.h"
|
||||
#include "bswap.h"
|
||||
#include <zlib.h>
|
||||
|
||||
typedef struct BDRVDMGState {
|
||||
int fd;
|
||||
|
||||
/* each chunk contains a certain number of sectors,
|
||||
* offsets[i] is the offset in the .dmg file,
|
||||
* lengths[i] is the length of the compressed chunk,
|
||||
* sectors[i] is the sector beginning at offsets[i],
|
||||
* sectorcounts[i] is the number of sectors in that chunk,
|
||||
* the sectors array is ordered
|
||||
* 0<=i<n_chunks */
|
||||
|
||||
uint32_t n_chunks;
|
||||
uint32_t* types;
|
||||
uint64_t* offsets;
|
||||
uint64_t* lengths;
|
||||
uint64_t* sectors;
|
||||
uint64_t* sectorcounts;
|
||||
uint32_t current_chunk;
|
||||
char* compressed_chunk;
|
||||
char* uncompressed_chunk;
|
||||
z_stream zstream;
|
||||
} BDRVDMGState;
|
||||
|
||||
static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
int len=strlen(filename);
|
||||
if(len>4 && !strcmp(filename+len-4,".dmg"))
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static off_t read_off(int fd)
|
||||
{
|
||||
uint64_t buffer;
|
||||
if(read(fd,&buffer,8)<8)
|
||||
return 0;
|
||||
return be64_to_cpu(buffer);
|
||||
}
|
||||
|
||||
static off_t read_uint32(int fd)
|
||||
{
|
||||
uint32_t buffer;
|
||||
if(read(fd,&buffer,4)<4)
|
||||
return 0;
|
||||
return be32_to_cpu(buffer);
|
||||
}
|
||||
|
||||
static int dmg_open(BlockDriverState *bs, const char *filename)
|
||||
{
|
||||
BDRVDMGState *s = bs->opaque;
|
||||
off_t info_begin,info_end,last_in_offset,last_out_offset;
|
||||
uint32_t count;
|
||||
uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i;
|
||||
|
||||
s->fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (s->fd < 0)
|
||||
return -1;
|
||||
bs->read_only = 1;
|
||||
s->n_chunks = 0;
|
||||
s->offsets = s->lengths = s->sectors = s->sectorcounts = 0;
|
||||
|
||||
/* read offset of info blocks */
|
||||
if(lseek(s->fd,-0x1d8,SEEK_END)<0) {
|
||||
dmg_close:
|
||||
close(s->fd);
|
||||
return -1;
|
||||
}
|
||||
info_begin=read_off(s->fd);
|
||||
if(info_begin==0)
|
||||
goto dmg_close;
|
||||
if(lseek(s->fd,info_begin,SEEK_SET)<0)
|
||||
goto dmg_close;
|
||||
if(read_uint32(s->fd)!=0x100)
|
||||
goto dmg_close;
|
||||
if((count = read_uint32(s->fd))==0)
|
||||
goto dmg_close;
|
||||
info_end = info_begin+count;
|
||||
if(lseek(s->fd,0xf8,SEEK_CUR)<0)
|
||||
goto dmg_close;
|
||||
|
||||
/* read offsets */
|
||||
last_in_offset = last_out_offset = 0;
|
||||
while(lseek(s->fd,0,SEEK_CUR)<info_end) {
|
||||
uint32_t type;
|
||||
|
||||
count = read_uint32(s->fd);
|
||||
if(count==0)
|
||||
goto dmg_close;
|
||||
type = read_uint32(s->fd);
|
||||
if(type!=0x6d697368 || count<244)
|
||||
lseek(s->fd,count-4,SEEK_CUR);
|
||||
else {
|
||||
int new_size, chunk_count;
|
||||
if(lseek(s->fd,200,SEEK_CUR)<0)
|
||||
goto dmg_close;
|
||||
chunk_count = (count-204)/40;
|
||||
new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
|
||||
s->types = realloc(s->types, new_size/2);
|
||||
s->offsets = realloc(s->offsets, new_size);
|
||||
s->lengths = realloc(s->lengths, new_size);
|
||||
s->sectors = realloc(s->sectors, new_size);
|
||||
s->sectorcounts = realloc(s->sectorcounts, new_size);
|
||||
|
||||
for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) {
|
||||
s->types[i] = read_uint32(s->fd);
|
||||
if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) {
|
||||
if(s->types[i]==0xffffffff) {
|
||||
last_in_offset = s->offsets[i-1]+s->lengths[i-1];
|
||||
last_out_offset = s->sectors[i-1]+s->sectorcounts[i-1];
|
||||
}
|
||||
chunk_count--;
|
||||
i--;
|
||||
if(lseek(s->fd,36,SEEK_CUR)<0)
|
||||
goto dmg_close;
|
||||
continue;
|
||||
}
|
||||
read_uint32(s->fd);
|
||||
s->sectors[i] = last_out_offset+read_off(s->fd);
|
||||
s->sectorcounts[i] = read_off(s->fd);
|
||||
s->offsets[i] = last_in_offset+read_off(s->fd);
|
||||
s->lengths[i] = read_off(s->fd);
|
||||
if(s->lengths[i]>max_compressed_size)
|
||||
max_compressed_size = s->lengths[i];
|
||||
if(s->sectorcounts[i]>max_sectors_per_chunk)
|
||||
max_sectors_per_chunk = s->sectorcounts[i];
|
||||
}
|
||||
s->n_chunks+=chunk_count;
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize zlib engine */
|
||||
if(!(s->compressed_chunk=(char*)malloc(max_compressed_size+1)))
|
||||
goto dmg_close;
|
||||
if(!(s->uncompressed_chunk=(char*)malloc(512*max_sectors_per_chunk)))
|
||||
goto dmg_close;
|
||||
if(inflateInit(&s->zstream) != Z_OK)
|
||||
goto dmg_close;
|
||||
|
||||
s->current_chunk = s->n_chunks;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_sector_in_chunk(BDRVDMGState* s,
|
||||
uint32_t chunk_num,int sector_num)
|
||||
{
|
||||
if(chunk_num>=s->n_chunks || s->sectors[chunk_num]>sector_num ||
|
||||
s->sectors[chunk_num]+s->sectorcounts[chunk_num]<=sector_num)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num)
|
||||
{
|
||||
/* binary search */
|
||||
uint32_t chunk1=0,chunk2=s->n_chunks,chunk3;
|
||||
while(chunk1!=chunk2) {
|
||||
chunk3 = (chunk1+chunk2)/2;
|
||||
if(s->sectors[chunk3]>sector_num)
|
||||
chunk2 = chunk3;
|
||||
else if(s->sectors[chunk3]+s->sectorcounts[chunk3]>sector_num)
|
||||
return chunk3;
|
||||
else
|
||||
chunk1 = chunk3;
|
||||
}
|
||||
return s->n_chunks; /* error */
|
||||
}
|
||||
|
||||
static inline int dmg_read_chunk(BDRVDMGState *s,int sector_num)
|
||||
{
|
||||
if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) {
|
||||
int ret;
|
||||
uint32_t chunk = search_chunk(s,sector_num);
|
||||
|
||||
if(chunk>=s->n_chunks)
|
||||
return -1;
|
||||
|
||||
s->current_chunk = s->n_chunks;
|
||||
switch(s->types[chunk]) {
|
||||
case 0x80000005: { /* zlib compressed */
|
||||
int i;
|
||||
|
||||
ret = lseek(s->fd, s->offsets[chunk], SEEK_SET);
|
||||
if(ret<0)
|
||||
return -1;
|
||||
|
||||
/* we need to buffer, because only the chunk as whole can be
|
||||
* inflated. */
|
||||
i=0;
|
||||
do {
|
||||
ret = read(s->fd, s->compressed_chunk+i, s->lengths[chunk]-i);
|
||||
if(ret<0 && errno==EINTR)
|
||||
ret=0;
|
||||
i+=ret;
|
||||
} while(ret>=0 && ret+i<s->lengths[chunk]);
|
||||
|
||||
if (ret != s->lengths[chunk])
|
||||
return -1;
|
||||
|
||||
s->zstream.next_in = s->compressed_chunk;
|
||||
s->zstream.avail_in = s->lengths[chunk];
|
||||
s->zstream.next_out = s->uncompressed_chunk;
|
||||
s->zstream.avail_out = 512*s->sectorcounts[chunk];
|
||||
ret = inflateReset(&s->zstream);
|
||||
if(ret != Z_OK)
|
||||
return -1;
|
||||
ret = inflate(&s->zstream, Z_FINISH);
|
||||
if(ret != Z_STREAM_END || s->zstream.total_out != 512*s->sectorcounts[chunk])
|
||||
return -1;
|
||||
break; }
|
||||
case 1: /* copy */
|
||||
ret = read(s->fd, s->uncompressed_chunk, s->lengths[chunk]);
|
||||
if (ret != s->lengths[chunk])
|
||||
return -1;
|
||||
break;
|
||||
case 2: /* zero */
|
||||
memset(s->uncompressed_chunk, 0, 512*s->sectorcounts[chunk]);
|
||||
break;
|
||||
}
|
||||
s->current_chunk = chunk;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dmg_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVDMGState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for(i=0;i<nb_sectors;i++) {
|
||||
uint32_t sector_offset_in_chunk;
|
||||
if(dmg_read_chunk(s, sector_num+i) != 0)
|
||||
return -1;
|
||||
sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk];
|
||||
memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dmg_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVDMGState *s = bs->opaque;
|
||||
close(s->fd);
|
||||
if(s->n_chunks>0) {
|
||||
free(s->types);
|
||||
free(s->offsets);
|
||||
free(s->lengths);
|
||||
free(s->sectors);
|
||||
free(s->sectorcounts);
|
||||
}
|
||||
free(s->compressed_chunk);
|
||||
free(s->uncompressed_chunk);
|
||||
inflateEnd(&s->zstream);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_dmg = {
|
||||
"dmg",
|
||||
sizeof(BDRVDMGState),
|
||||
dmg_probe,
|
||||
dmg_open,
|
||||
dmg_read,
|
||||
NULL,
|
||||
dmg_close,
|
||||
};
|
||||
|
||||
+677
@@ -0,0 +1,677 @@
|
||||
/*
|
||||
* Block driver for the QCOW format
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
#include "block_int.h"
|
||||
#include <zlib.h>
|
||||
#include "aes.h"
|
||||
|
||||
/**************************************************************/
|
||||
/* QEMU COW block driver with compression and encryption support */
|
||||
|
||||
#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
|
||||
#define QCOW_VERSION 1
|
||||
|
||||
#define QCOW_CRYPT_NONE 0
|
||||
#define QCOW_CRYPT_AES 1
|
||||
|
||||
#define QCOW_OFLAG_COMPRESSED (1LL << 63)
|
||||
|
||||
typedef struct QCowHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint64_t backing_file_offset;
|
||||
uint32_t backing_file_size;
|
||||
uint32_t mtime;
|
||||
uint64_t size; /* in bytes */
|
||||
uint8_t cluster_bits;
|
||||
uint8_t l2_bits;
|
||||
uint32_t crypt_method;
|
||||
uint64_t l1_table_offset;
|
||||
} QCowHeader;
|
||||
|
||||
#define L2_CACHE_SIZE 16
|
||||
|
||||
typedef struct BDRVQcowState {
|
||||
int fd;
|
||||
int cluster_bits;
|
||||
int cluster_size;
|
||||
int cluster_sectors;
|
||||
int l2_bits;
|
||||
int l2_size;
|
||||
int l1_size;
|
||||
uint64_t cluster_offset_mask;
|
||||
uint64_t l1_table_offset;
|
||||
uint64_t *l1_table;
|
||||
uint64_t *l2_cache;
|
||||
uint64_t l2_cache_offsets[L2_CACHE_SIZE];
|
||||
uint32_t l2_cache_counts[L2_CACHE_SIZE];
|
||||
uint8_t *cluster_cache;
|
||||
uint8_t *cluster_data;
|
||||
uint64_t cluster_cache_offset;
|
||||
uint32_t crypt_method; /* current crypt method, 0 if no key yet */
|
||||
uint32_t crypt_method_header;
|
||||
AES_KEY aes_encrypt_key;
|
||||
AES_KEY aes_decrypt_key;
|
||||
} BDRVQcowState;
|
||||
|
||||
static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset);
|
||||
|
||||
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const QCowHeader *cow_header = (const void *)buf;
|
||||
|
||||
if (be32_to_cpu(cow_header->magic) == QCOW_MAGIC &&
|
||||
be32_to_cpu(cow_header->version) == QCOW_VERSION)
|
||||
return 100;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow_open(BlockDriverState *bs, const char *filename)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int fd, len, i, shift;
|
||||
QCowHeader header;
|
||||
|
||||
fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0) {
|
||||
fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
}
|
||||
s->fd = fd;
|
||||
if (read(fd, &header, sizeof(header)) != sizeof(header))
|
||||
goto fail;
|
||||
be32_to_cpus(&header.magic);
|
||||
be32_to_cpus(&header.version);
|
||||
be64_to_cpus(&header.backing_file_offset);
|
||||
be32_to_cpus(&header.backing_file_size);
|
||||
be32_to_cpus(&header.mtime);
|
||||
be64_to_cpus(&header.size);
|
||||
be32_to_cpus(&header.crypt_method);
|
||||
be64_to_cpus(&header.l1_table_offset);
|
||||
|
||||
if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION)
|
||||
goto fail;
|
||||
if (header.size <= 1 || header.cluster_bits < 9)
|
||||
goto fail;
|
||||
if (header.crypt_method > QCOW_CRYPT_AES)
|
||||
goto fail;
|
||||
s->crypt_method_header = header.crypt_method;
|
||||
if (s->crypt_method_header)
|
||||
bs->encrypted = 1;
|
||||
s->cluster_bits = header.cluster_bits;
|
||||
s->cluster_size = 1 << s->cluster_bits;
|
||||
s->cluster_sectors = 1 << (s->cluster_bits - 9);
|
||||
s->l2_bits = header.l2_bits;
|
||||
s->l2_size = 1 << s->l2_bits;
|
||||
bs->total_sectors = header.size / 512;
|
||||
s->cluster_offset_mask = (1LL << (63 - s->cluster_bits)) - 1;
|
||||
|
||||
/* read the level 1 table */
|
||||
shift = s->cluster_bits + s->l2_bits;
|
||||
s->l1_size = (header.size + (1LL << shift) - 1) >> shift;
|
||||
|
||||
s->l1_table_offset = header.l1_table_offset;
|
||||
s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
|
||||
if (!s->l1_table)
|
||||
goto fail;
|
||||
lseek(fd, s->l1_table_offset, SEEK_SET);
|
||||
if (read(fd, s->l1_table, s->l1_size * sizeof(uint64_t)) !=
|
||||
s->l1_size * sizeof(uint64_t))
|
||||
goto fail;
|
||||
for(i = 0;i < s->l1_size; i++) {
|
||||
be64_to_cpus(&s->l1_table[i]);
|
||||
}
|
||||
/* alloc L2 cache */
|
||||
s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
|
||||
if (!s->l2_cache)
|
||||
goto fail;
|
||||
s->cluster_cache = qemu_malloc(s->cluster_size);
|
||||
if (!s->cluster_cache)
|
||||
goto fail;
|
||||
s->cluster_data = qemu_malloc(s->cluster_size);
|
||||
if (!s->cluster_data)
|
||||
goto fail;
|
||||
s->cluster_cache_offset = -1;
|
||||
|
||||
/* read the backing file name */
|
||||
if (header.backing_file_offset != 0) {
|
||||
len = header.backing_file_size;
|
||||
if (len > 1023)
|
||||
len = 1023;
|
||||
lseek(fd, header.backing_file_offset, SEEK_SET);
|
||||
if (read(fd, bs->backing_file, len) != len)
|
||||
goto fail;
|
||||
bs->backing_file[len] = '\0';
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
qemu_free(s->l1_table);
|
||||
qemu_free(s->l2_cache);
|
||||
qemu_free(s->cluster_cache);
|
||||
qemu_free(s->cluster_data);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int qcow_set_key(BlockDriverState *bs, const char *key)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint8_t keybuf[16];
|
||||
int len, i;
|
||||
|
||||
memset(keybuf, 0, 16);
|
||||
len = strlen(key);
|
||||
if (len > 16)
|
||||
len = 16;
|
||||
/* XXX: we could compress the chars to 7 bits to increase
|
||||
entropy */
|
||||
for(i = 0;i < len;i++) {
|
||||
keybuf[i] = key[i];
|
||||
}
|
||||
s->crypt_method = s->crypt_method_header;
|
||||
|
||||
if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
|
||||
return -1;
|
||||
if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
|
||||
return -1;
|
||||
#if 0
|
||||
/* test */
|
||||
{
|
||||
uint8_t in[16];
|
||||
uint8_t out[16];
|
||||
uint8_t tmp[16];
|
||||
for(i=0;i<16;i++)
|
||||
in[i] = i;
|
||||
AES_encrypt(in, tmp, &s->aes_encrypt_key);
|
||||
AES_decrypt(tmp, out, &s->aes_decrypt_key);
|
||||
for(i = 0; i < 16; i++)
|
||||
printf(" %02x", tmp[i]);
|
||||
printf("\n");
|
||||
for(i = 0; i < 16; i++)
|
||||
printf(" %02x", out[i]);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The crypt function is compatible with the linux cryptoloop
|
||||
algorithm for < 4 GB images. NOTE: out_buf == in_buf is
|
||||
supported */
|
||||
static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
uint8_t *out_buf, const uint8_t *in_buf,
|
||||
int nb_sectors, int enc,
|
||||
const AES_KEY *key)
|
||||
{
|
||||
union {
|
||||
uint64_t ll[2];
|
||||
uint8_t b[16];
|
||||
} ivec;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < nb_sectors; i++) {
|
||||
ivec.ll[0] = cpu_to_le64(sector_num);
|
||||
ivec.ll[1] = 0;
|
||||
AES_cbc_encrypt(in_buf, out_buf, 512, key,
|
||||
ivec.b, enc);
|
||||
sector_num++;
|
||||
in_buf += 512;
|
||||
out_buf += 512;
|
||||
}
|
||||
}
|
||||
|
||||
/* 'allocate' is:
|
||||
*
|
||||
* 0 to not allocate.
|
||||
*
|
||||
* 1 to allocate a normal cluster (for sector indexes 'n_start' to
|
||||
* 'n_end')
|
||||
*
|
||||
* 2 to allocate a compressed cluster of size
|
||||
* 'compressed_size'. 'compressed_size' must be > 0 and <
|
||||
* cluster_size
|
||||
*
|
||||
* return 0 if not allocated.
|
||||
*/
|
||||
static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t offset, int allocate,
|
||||
int compressed_size,
|
||||
int n_start, int n_end)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int min_index, i, j, l1_index, l2_index;
|
||||
uint64_t l2_offset, *l2_table, cluster_offset, tmp;
|
||||
uint32_t min_count;
|
||||
int new_l2_table;
|
||||
|
||||
l1_index = offset >> (s->l2_bits + s->cluster_bits);
|
||||
l2_offset = s->l1_table[l1_index];
|
||||
new_l2_table = 0;
|
||||
if (!l2_offset) {
|
||||
if (!allocate)
|
||||
return 0;
|
||||
/* allocate a new l2 entry */
|
||||
l2_offset = lseek(s->fd, 0, SEEK_END);
|
||||
/* round to cluster size */
|
||||
l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1);
|
||||
/* update the L1 entry */
|
||||
s->l1_table[l1_index] = l2_offset;
|
||||
tmp = cpu_to_be64(l2_offset);
|
||||
lseek(s->fd, s->l1_table_offset + l1_index * sizeof(tmp), SEEK_SET);
|
||||
if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
return 0;
|
||||
new_l2_table = 1;
|
||||
}
|
||||
for(i = 0; i < L2_CACHE_SIZE; i++) {
|
||||
if (l2_offset == s->l2_cache_offsets[i]) {
|
||||
/* increment the hit count */
|
||||
if (++s->l2_cache_counts[i] == 0xffffffff) {
|
||||
for(j = 0; j < L2_CACHE_SIZE; j++) {
|
||||
s->l2_cache_counts[j] >>= 1;
|
||||
}
|
||||
}
|
||||
l2_table = s->l2_cache + (i << s->l2_bits);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
/* not found: load a new entry in the least used one */
|
||||
min_index = 0;
|
||||
min_count = 0xffffffff;
|
||||
for(i = 0; i < L2_CACHE_SIZE; i++) {
|
||||
if (s->l2_cache_counts[i] < min_count) {
|
||||
min_count = s->l2_cache_counts[i];
|
||||
min_index = i;
|
||||
}
|
||||
}
|
||||
l2_table = s->l2_cache + (min_index << s->l2_bits);
|
||||
lseek(s->fd, l2_offset, SEEK_SET);
|
||||
if (new_l2_table) {
|
||||
memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
|
||||
if (write(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) !=
|
||||
s->l2_size * sizeof(uint64_t))
|
||||
return 0;
|
||||
} else {
|
||||
if (read(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) !=
|
||||
s->l2_size * sizeof(uint64_t))
|
||||
return 0;
|
||||
}
|
||||
s->l2_cache_offsets[min_index] = l2_offset;
|
||||
s->l2_cache_counts[min_index] = 1;
|
||||
found:
|
||||
l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
|
||||
cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
||||
if (!cluster_offset ||
|
||||
((cluster_offset & QCOW_OFLAG_COMPRESSED) && allocate == 1)) {
|
||||
if (!allocate)
|
||||
return 0;
|
||||
/* allocate a new cluster */
|
||||
if ((cluster_offset & QCOW_OFLAG_COMPRESSED) &&
|
||||
(n_end - n_start) < s->cluster_sectors) {
|
||||
/* if the cluster is already compressed, we must
|
||||
decompress it in the case it is not completely
|
||||
overwritten */
|
||||
if (decompress_cluster(s, cluster_offset) < 0)
|
||||
return 0;
|
||||
cluster_offset = lseek(s->fd, 0, SEEK_END);
|
||||
cluster_offset = (cluster_offset + s->cluster_size - 1) &
|
||||
~(s->cluster_size - 1);
|
||||
/* write the cluster content */
|
||||
lseek(s->fd, cluster_offset, SEEK_SET);
|
||||
if (write(s->fd, s->cluster_cache, s->cluster_size) !=
|
||||
s->cluster_size)
|
||||
return -1;
|
||||
} else {
|
||||
cluster_offset = lseek(s->fd, 0, SEEK_END);
|
||||
if (allocate == 1) {
|
||||
/* round to cluster size */
|
||||
cluster_offset = (cluster_offset + s->cluster_size - 1) &
|
||||
~(s->cluster_size - 1);
|
||||
ftruncate(s->fd, cluster_offset + s->cluster_size);
|
||||
/* if encrypted, we must initialize the cluster
|
||||
content which won't be written */
|
||||
if (s->crypt_method &&
|
||||
(n_end - n_start) < s->cluster_sectors) {
|
||||
uint64_t start_sect;
|
||||
start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
|
||||
memset(s->cluster_data + 512, 0xaa, 512);
|
||||
for(i = 0; i < s->cluster_sectors; i++) {
|
||||
if (i < n_start || i >= n_end) {
|
||||
encrypt_sectors(s, start_sect + i,
|
||||
s->cluster_data,
|
||||
s->cluster_data + 512, 1, 1,
|
||||
&s->aes_encrypt_key);
|
||||
lseek(s->fd, cluster_offset + i * 512, SEEK_SET);
|
||||
if (write(s->fd, s->cluster_data, 512) != 512)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cluster_offset |= QCOW_OFLAG_COMPRESSED |
|
||||
(uint64_t)compressed_size << (63 - s->cluster_bits);
|
||||
}
|
||||
}
|
||||
/* update L2 table */
|
||||
tmp = cpu_to_be64(cluster_offset);
|
||||
l2_table[l2_index] = tmp;
|
||||
lseek(s->fd, l2_offset + l2_index * sizeof(tmp), SEEK_SET);
|
||||
if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
return 0;
|
||||
}
|
||||
return cluster_offset;
|
||||
}
|
||||
|
||||
static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
*pnum = n;
|
||||
return (cluster_offset != 0);
|
||||
}
|
||||
|
||||
static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
|
||||
const uint8_t *buf, int buf_size)
|
||||
{
|
||||
z_stream strm1, *strm = &strm1;
|
||||
int ret, out_len;
|
||||
|
||||
memset(strm, 0, sizeof(*strm));
|
||||
|
||||
strm->next_in = (uint8_t *)buf;
|
||||
strm->avail_in = buf_size;
|
||||
strm->next_out = out_buf;
|
||||
strm->avail_out = out_buf_size;
|
||||
|
||||
ret = inflateInit2(strm, -12);
|
||||
if (ret != Z_OK)
|
||||
return -1;
|
||||
ret = inflate(strm, Z_FINISH);
|
||||
out_len = strm->next_out - out_buf;
|
||||
if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) ||
|
||||
out_len != out_buf_size) {
|
||||
inflateEnd(strm);
|
||||
return -1;
|
||||
}
|
||||
inflateEnd(strm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset)
|
||||
{
|
||||
int ret, csize;
|
||||
uint64_t coffset;
|
||||
|
||||
coffset = cluster_offset & s->cluster_offset_mask;
|
||||
if (s->cluster_cache_offset != coffset) {
|
||||
csize = cluster_offset >> (63 - s->cluster_bits);
|
||||
csize &= (s->cluster_size - 1);
|
||||
lseek(s->fd, coffset, SEEK_SET);
|
||||
ret = read(s->fd, s->cluster_data, csize);
|
||||
if (ret != csize)
|
||||
return -1;
|
||||
if (decompress_buffer(s->cluster_cache, s->cluster_size,
|
||||
s->cluster_data, csize) < 0) {
|
||||
return -1;
|
||||
}
|
||||
s->cluster_cache_offset = coffset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret, index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
if (!cluster_offset) {
|
||||
memset(buf, 0, 512 * n);
|
||||
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
if (decompress_cluster(s, cluster_offset) < 0)
|
||||
return -1;
|
||||
memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
|
||||
} else {
|
||||
lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
|
||||
ret = read(s->fd, buf, n * 512);
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
if (s->crypt_method) {
|
||||
encrypt_sectors(s, sector_num, buf, buf, n, 0,
|
||||
&s->aes_decrypt_key);
|
||||
}
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret, index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0,
|
||||
index_in_cluster,
|
||||
index_in_cluster + n);
|
||||
if (!cluster_offset)
|
||||
return -1;
|
||||
lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
|
||||
if (s->crypt_method) {
|
||||
encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1,
|
||||
&s->aes_encrypt_key);
|
||||
ret = write(s->fd, s->cluster_data, n * 512);
|
||||
} else {
|
||||
ret = write(s->fd, buf, n * 512);
|
||||
}
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcow_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
qemu_free(s->l1_table);
|
||||
qemu_free(s->l2_cache);
|
||||
qemu_free(s->cluster_cache);
|
||||
qemu_free(s->cluster_data);
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
static int qcow_create(const char *filename, int64_t total_size,
|
||||
const char *backing_file, int flags)
|
||||
{
|
||||
int fd, header_size, backing_filename_len, l1_size, i, shift;
|
||||
QCowHeader header;
|
||||
char backing_filename[1024];
|
||||
uint64_t tmp;
|
||||
struct stat st;
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
memset(&header, 0, sizeof(header));
|
||||
header.magic = cpu_to_be32(QCOW_MAGIC);
|
||||
header.version = cpu_to_be32(QCOW_VERSION);
|
||||
header.size = cpu_to_be64(total_size * 512);
|
||||
header_size = sizeof(header);
|
||||
backing_filename_len = 0;
|
||||
if (backing_file) {
|
||||
realpath(backing_file, backing_filename);
|
||||
if (stat(backing_filename, &st) != 0) {
|
||||
return -1;
|
||||
}
|
||||
header.mtime = cpu_to_be32(st.st_mtime);
|
||||
header.backing_file_offset = cpu_to_be64(header_size);
|
||||
backing_filename_len = strlen(backing_filename);
|
||||
header.backing_file_size = cpu_to_be32(backing_filename_len);
|
||||
header_size += backing_filename_len;
|
||||
header.cluster_bits = 9; /* 512 byte cluster to avoid copying
|
||||
unmodifyed sectors */
|
||||
header.l2_bits = 12; /* 32 KB L2 tables */
|
||||
} else {
|
||||
header.cluster_bits = 12; /* 4 KB clusters */
|
||||
header.l2_bits = 9; /* 4 KB L2 tables */
|
||||
}
|
||||
header_size = (header_size + 7) & ~7;
|
||||
shift = header.cluster_bits + header.l2_bits;
|
||||
l1_size = ((total_size * 512) + (1LL << shift) - 1) >> shift;
|
||||
|
||||
header.l1_table_offset = cpu_to_be64(header_size);
|
||||
if (flags) {
|
||||
header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
|
||||
} else {
|
||||
header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
|
||||
}
|
||||
|
||||
/* write all the data */
|
||||
write(fd, &header, sizeof(header));
|
||||
if (backing_file) {
|
||||
write(fd, backing_filename, backing_filename_len);
|
||||
}
|
||||
lseek(fd, header_size, SEEK_SET);
|
||||
tmp = 0;
|
||||
for(i = 0;i < l1_size; i++) {
|
||||
write(fd, &tmp, sizeof(tmp));
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qcow_get_cluster_size(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
if (bs->drv != &bdrv_qcow)
|
||||
return -1;
|
||||
return s->cluster_size;
|
||||
}
|
||||
|
||||
/* XXX: put compressed sectors first, then all the cluster aligned
|
||||
tables to avoid losing bytes in alignment */
|
||||
int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
z_stream strm;
|
||||
int ret, out_len;
|
||||
uint8_t *out_buf;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
if (bs->drv != &bdrv_qcow)
|
||||
return -1;
|
||||
|
||||
out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
|
||||
if (!out_buf)
|
||||
return -1;
|
||||
|
||||
/* best compression, small window, no zlib header */
|
||||
memset(&strm, 0, sizeof(strm));
|
||||
ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
|
||||
Z_DEFLATED, -12,
|
||||
9, Z_DEFAULT_STRATEGY);
|
||||
if (ret != 0) {
|
||||
qemu_free(out_buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strm.avail_in = s->cluster_size;
|
||||
strm.next_in = (uint8_t *)buf;
|
||||
strm.avail_out = s->cluster_size;
|
||||
strm.next_out = out_buf;
|
||||
|
||||
ret = deflate(&strm, Z_FINISH);
|
||||
if (ret != Z_STREAM_END && ret != Z_OK) {
|
||||
qemu_free(out_buf);
|
||||
deflateEnd(&strm);
|
||||
return -1;
|
||||
}
|
||||
out_len = strm.next_out - out_buf;
|
||||
|
||||
deflateEnd(&strm);
|
||||
|
||||
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
|
||||
/* could not compress: write normal cluster */
|
||||
qcow_write(bs, sector_num, buf, s->cluster_sectors);
|
||||
} else {
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 2,
|
||||
out_len, 0, 0);
|
||||
cluster_offset &= s->cluster_offset_mask;
|
||||
lseek(s->fd, cluster_offset, SEEK_SET);
|
||||
if (write(s->fd, out_buf, out_len) != out_len) {
|
||||
qemu_free(out_buf);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_free(out_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BlockDriver bdrv_qcow = {
|
||||
"qcow",
|
||||
sizeof(BDRVQcowState),
|
||||
qcow_probe,
|
||||
qcow_open,
|
||||
qcow_read,
|
||||
qcow_write,
|
||||
qcow_close,
|
||||
qcow_create,
|
||||
qcow_is_allocated,
|
||||
qcow_set_key,
|
||||
};
|
||||
|
||||
|
||||
+336
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* Block driver for the VMDK format
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
* Copyright (c) 2005 Filip Navara
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
#include "block_int.h"
|
||||
|
||||
#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
|
||||
#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
|
||||
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
uint32_t flags;
|
||||
uint32_t disk_sectors;
|
||||
uint32_t granularity;
|
||||
uint32_t l1dir_offset;
|
||||
uint32_t l1dir_size;
|
||||
uint32_t file_sectors;
|
||||
uint32_t cylinders;
|
||||
uint32_t heads;
|
||||
uint32_t sectors_per_track;
|
||||
} VMDK3Header;
|
||||
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
uint32_t flags;
|
||||
int64_t capacity;
|
||||
int64_t granularity;
|
||||
int64_t desc_offset;
|
||||
int64_t desc_size;
|
||||
int32_t num_gtes_per_gte;
|
||||
int64_t rgd_offset;
|
||||
int64_t gd_offset;
|
||||
int64_t grain_offset;
|
||||
char filler[1];
|
||||
char check_bytes[4];
|
||||
} __attribute__((packed)) VMDK4Header;
|
||||
|
||||
#define L2_CACHE_SIZE 16
|
||||
|
||||
typedef struct BDRVVmdkState {
|
||||
int fd;
|
||||
int64_t l1_table_offset;
|
||||
int64_t l1_backup_table_offset;
|
||||
uint32_t *l1_table;
|
||||
uint32_t *l1_backup_table;
|
||||
unsigned int l1_size;
|
||||
uint32_t l1_entry_sectors;
|
||||
|
||||
unsigned int l2_size;
|
||||
uint32_t *l2_cache;
|
||||
uint32_t l2_cache_offsets[L2_CACHE_SIZE];
|
||||
uint32_t l2_cache_counts[L2_CACHE_SIZE];
|
||||
|
||||
unsigned int cluster_sectors;
|
||||
} BDRVVmdkState;
|
||||
|
||||
static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
uint32_t magic;
|
||||
|
||||
if (buf_size < 4)
|
||||
return 0;
|
||||
magic = be32_to_cpu(*(uint32_t *)buf);
|
||||
if (magic == VMDK3_MAGIC ||
|
||||
magic == VMDK4_MAGIC)
|
||||
return 100;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_open(BlockDriverState *bs, const char *filename)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int fd, i;
|
||||
uint32_t magic;
|
||||
int l1_size;
|
||||
|
||||
fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0) {
|
||||
fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
bs->read_only = 1;
|
||||
}
|
||||
if (read(fd, &magic, sizeof(magic)) != sizeof(magic))
|
||||
goto fail;
|
||||
magic = be32_to_cpu(magic);
|
||||
if (magic == VMDK3_MAGIC) {
|
||||
VMDK3Header header;
|
||||
if (read(fd, &header, sizeof(header)) !=
|
||||
sizeof(header))
|
||||
goto fail;
|
||||
s->cluster_sectors = le32_to_cpu(header.granularity);
|
||||
s->l2_size = 1 << 9;
|
||||
s->l1_size = 1 << 6;
|
||||
bs->total_sectors = le32_to_cpu(header.disk_sectors);
|
||||
s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
|
||||
s->l1_backup_table_offset = 0;
|
||||
s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
|
||||
} else if (magic == VMDK4_MAGIC) {
|
||||
VMDK4Header header;
|
||||
|
||||
if (read(fd, &header, sizeof(header)) != sizeof(header))
|
||||
goto fail;
|
||||
bs->total_sectors = le32_to_cpu(header.capacity);
|
||||
s->cluster_sectors = le32_to_cpu(header.granularity);
|
||||
s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
|
||||
s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
|
||||
if (s->l1_entry_sectors <= 0)
|
||||
goto fail;
|
||||
s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1)
|
||||
/ s->l1_entry_sectors;
|
||||
s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
|
||||
s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
/* read the L1 table */
|
||||
l1_size = s->l1_size * sizeof(uint32_t);
|
||||
s->l1_table = qemu_malloc(l1_size);
|
||||
if (!s->l1_table)
|
||||
goto fail;
|
||||
if (lseek(fd, s->l1_table_offset, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (read(fd, s->l1_table, l1_size) != l1_size)
|
||||
goto fail;
|
||||
for(i = 0; i < s->l1_size; i++) {
|
||||
le32_to_cpus(&s->l1_table[i]);
|
||||
}
|
||||
|
||||
if (s->l1_backup_table_offset) {
|
||||
s->l1_backup_table = qemu_malloc(l1_size);
|
||||
if (!s->l1_backup_table)
|
||||
goto fail;
|
||||
if (lseek(fd, s->l1_backup_table_offset, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (read(fd, s->l1_backup_table, l1_size) != l1_size)
|
||||
goto fail;
|
||||
for(i = 0; i < s->l1_size; i++) {
|
||||
le32_to_cpus(&s->l1_backup_table[i]);
|
||||
}
|
||||
}
|
||||
|
||||
s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
|
||||
if (!s->l2_cache)
|
||||
goto fail;
|
||||
s->fd = fd;
|
||||
return 0;
|
||||
fail:
|
||||
qemu_free(s->l1_backup_table);
|
||||
qemu_free(s->l1_table);
|
||||
qemu_free(s->l2_cache);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t offset, int allocate)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
unsigned int l1_index, l2_offset, l2_index;
|
||||
int min_index, i, j;
|
||||
uint32_t min_count, *l2_table, tmp;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
l1_index = (offset >> 9) / s->l1_entry_sectors;
|
||||
if (l1_index >= s->l1_size)
|
||||
return 0;
|
||||
l2_offset = s->l1_table[l1_index];
|
||||
if (!l2_offset)
|
||||
return 0;
|
||||
for(i = 0; i < L2_CACHE_SIZE; i++) {
|
||||
if (l2_offset == s->l2_cache_offsets[i]) {
|
||||
/* increment the hit count */
|
||||
if (++s->l2_cache_counts[i] == 0xffffffff) {
|
||||
for(j = 0; j < L2_CACHE_SIZE; j++) {
|
||||
s->l2_cache_counts[j] >>= 1;
|
||||
}
|
||||
}
|
||||
l2_table = s->l2_cache + (i * s->l2_size);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
/* not found: load a new entry in the least used one */
|
||||
min_index = 0;
|
||||
min_count = 0xffffffff;
|
||||
for(i = 0; i < L2_CACHE_SIZE; i++) {
|
||||
if (s->l2_cache_counts[i] < min_count) {
|
||||
min_count = s->l2_cache_counts[i];
|
||||
min_index = i;
|
||||
}
|
||||
}
|
||||
l2_table = s->l2_cache + (min_index * s->l2_size);
|
||||
lseek(s->fd, (int64_t)l2_offset * 512, SEEK_SET);
|
||||
if (read(s->fd, l2_table, s->l2_size * sizeof(uint32_t)) !=
|
||||
s->l2_size * sizeof(uint32_t))
|
||||
return 0;
|
||||
s->l2_cache_offsets[min_index] = l2_offset;
|
||||
s->l2_cache_counts[min_index] = 1;
|
||||
found:
|
||||
l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
|
||||
cluster_offset = le32_to_cpu(l2_table[l2_index]);
|
||||
if (!cluster_offset) {
|
||||
if (!allocate)
|
||||
return 0;
|
||||
cluster_offset = lseek(s->fd, 0, SEEK_END);
|
||||
ftruncate(s->fd, cluster_offset + (s->cluster_sectors << 9));
|
||||
cluster_offset >>= 9;
|
||||
/* update L2 table */
|
||||
tmp = cpu_to_le32(cluster_offset);
|
||||
l2_table[l2_index] = tmp;
|
||||
lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET);
|
||||
if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
return 0;
|
||||
/* update backup L2 table */
|
||||
if (s->l1_backup_table_offset != 0) {
|
||||
l2_offset = s->l1_backup_table[l1_index];
|
||||
lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET);
|
||||
if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
cluster_offset <<= 9;
|
||||
return cluster_offset;
|
||||
}
|
||||
|
||||
static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 0);
|
||||
index_in_cluster = sector_num % s->cluster_sectors;
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
*pnum = n;
|
||||
return (cluster_offset != 0);
|
||||
}
|
||||
|
||||
static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret, index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 0);
|
||||
index_in_cluster = sector_num % s->cluster_sectors;
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
if (!cluster_offset) {
|
||||
memset(buf, 0, 512 * n);
|
||||
} else {
|
||||
lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
|
||||
ret = read(s->fd, buf, n * 512);
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret, index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 1);
|
||||
if (!cluster_offset)
|
||||
return -1;
|
||||
lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
|
||||
ret = write(s->fd, buf, n * 512);
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vmdk_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
qemu_free(s->l1_table);
|
||||
qemu_free(s->l2_cache);
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_vmdk = {
|
||||
"vmdk",
|
||||
sizeof(BDRVVmdkState),
|
||||
vmdk_probe,
|
||||
vmdk_open,
|
||||
vmdk_read,
|
||||
vmdk_write,
|
||||
vmdk_close,
|
||||
NULL, /* no create yet */
|
||||
vmdk_is_allocated,
|
||||
};
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Block driver for Conectix/Microsoft Virtual PC images
|
||||
*
|
||||
* Copyright (c) 2005 Alex Beregszaszi
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
#include "block_int.h"
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
#define HEADER_SIZE 512
|
||||
|
||||
//#define CACHE
|
||||
|
||||
// always big-endian
|
||||
struct vpc_subheader {
|
||||
char magic[8]; // "conectix" / "cxsparse"
|
||||
union {
|
||||
struct {
|
||||
uint32_t unk1[2];
|
||||
uint32_t unk2; // always zero?
|
||||
uint32_t subheader_offset;
|
||||
uint32_t unk3; // some size?
|
||||
char creator[4]; // "vpc "
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
char guest[4]; // "Wi2k"
|
||||
uint32_t unk4[7];
|
||||
uint8_t vnet_id[16]; // virtual network id, purpose unknown
|
||||
// next 16 longs are used, but dunno the purpose
|
||||
// next 6 longs unknown, following 7 long maybe a serial
|
||||
char padding[HEADER_SIZE - 84];
|
||||
} main;
|
||||
struct {
|
||||
uint32_t unk1[2]; // all bits set
|
||||
uint32_t unk2; // always zero?
|
||||
uint32_t pagetable_offset;
|
||||
uint32_t unk3;
|
||||
uint32_t pagetable_entries; // 32bit/entry
|
||||
uint32_t pageentry_size; // 512*8*512
|
||||
uint32_t nb_sectors;
|
||||
char padding[HEADER_SIZE - 40];
|
||||
} sparse;
|
||||
char padding[HEADER_SIZE - 8];
|
||||
} type;
|
||||
};
|
||||
|
||||
typedef struct BDRVVPCState {
|
||||
int fd;
|
||||
|
||||
int pagetable_entries;
|
||||
uint32_t *pagetable;
|
||||
|
||||
uint32_t pageentry_size;
|
||||
#ifdef CACHE
|
||||
uint8_t *pageentry_u8;
|
||||
uint32_t *pageentry_u32;
|
||||
uint16_t *pageentry_u16;
|
||||
|
||||
uint64_t last_bitmap;
|
||||
#endif
|
||||
} BDRVVPCState;
|
||||
|
||||
static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
if (!strncmp(buf, "conectix", 8))
|
||||
return 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpc_open(BlockDriverState *bs, const char *filename)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
int fd, i;
|
||||
struct vpc_subheader header;
|
||||
|
||||
fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0) {
|
||||
fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
bs->read_only = 1; // no write support yet
|
||||
|
||||
s->fd = fd;
|
||||
|
||||
if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE)
|
||||
goto fail;
|
||||
|
||||
if (strncmp(header.magic, "conectix", 8))
|
||||
goto fail;
|
||||
lseek(s->fd, be32_to_cpu(header.type.main.subheader_offset), SEEK_SET);
|
||||
|
||||
if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE)
|
||||
goto fail;
|
||||
|
||||
if (strncmp(header.magic, "cxsparse", 8))
|
||||
goto fail;
|
||||
|
||||
bs->total_sectors = ((uint64_t)be32_to_cpu(header.type.sparse.pagetable_entries) *
|
||||
be32_to_cpu(header.type.sparse.pageentry_size)) / 512;
|
||||
|
||||
lseek(s->fd, be32_to_cpu(header.type.sparse.pagetable_offset), SEEK_SET);
|
||||
|
||||
s->pagetable_entries = be32_to_cpu(header.type.sparse.pagetable_entries);
|
||||
s->pagetable = qemu_malloc(s->pagetable_entries * 4);
|
||||
if (!s->pagetable)
|
||||
goto fail;
|
||||
if (read(s->fd, s->pagetable, s->pagetable_entries * 4) !=
|
||||
s->pagetable_entries * 4)
|
||||
goto fail;
|
||||
for (i = 0; i < s->pagetable_entries; i++)
|
||||
be32_to_cpus(&s->pagetable[i]);
|
||||
|
||||
s->pageentry_size = be32_to_cpu(header.type.sparse.pageentry_size);
|
||||
#ifdef CACHE
|
||||
s->pageentry_u8 = qemu_malloc(512);
|
||||
if (!s->pageentry_u8)
|
||||
goto fail;
|
||||
s->pageentry_u32 = s->pageentry_u8;
|
||||
s->pageentry_u16 = s->pageentry_u8;
|
||||
s->last_pagetable = -1;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
uint64_t offset = sector_num * 512;
|
||||
uint64_t bitmap_offset, block_offset;
|
||||
uint32_t pagetable_index, pageentry_index;
|
||||
|
||||
pagetable_index = offset / s->pageentry_size;
|
||||
pageentry_index = (offset % s->pageentry_size) / 512;
|
||||
|
||||
if (pagetable_index > s->pagetable_entries || s->pagetable[pagetable_index] == 0xffffffff)
|
||||
return -1; // not allocated
|
||||
|
||||
bitmap_offset = 512 * s->pagetable[pagetable_index];
|
||||
block_offset = bitmap_offset + 512 + (512 * pageentry_index);
|
||||
|
||||
// printf("sector: %llx, index: %x, offset: %x, bioff: %llx, bloff: %llx\n",
|
||||
// sector_num, pagetable_index, pageentry_index,
|
||||
// bitmap_offset, block_offset);
|
||||
|
||||
// disabled by reason
|
||||
#if 0
|
||||
#ifdef CACHE
|
||||
if (bitmap_offset != s->last_bitmap)
|
||||
{
|
||||
lseek(s->fd, bitmap_offset, SEEK_SET);
|
||||
|
||||
s->last_bitmap = bitmap_offset;
|
||||
|
||||
// Scary! Bitmap is stored as big endian 32bit entries,
|
||||
// while we used to look it up byte by byte
|
||||
read(s->fd, s->pageentry_u8, 512);
|
||||
for (i = 0; i < 128; i++)
|
||||
be32_to_cpus(&s->pageentry_u32[i]);
|
||||
}
|
||||
|
||||
if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
|
||||
return -1;
|
||||
#else
|
||||
lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
|
||||
|
||||
read(s->fd, &bitmap_entry, 1);
|
||||
|
||||
if ((bitmap_entry >> (pageentry_index % 8)) & 1)
|
||||
return -1; // not allocated
|
||||
#endif
|
||||
#endif
|
||||
lseek(s->fd, block_offset, SEEK_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpc_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
if (!seek_to_sector(bs, sector_num))
|
||||
{
|
||||
ret = read(s->fd, buf, 512);
|
||||
if (ret != 512)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
memset(buf, 0, 512);
|
||||
nb_sectors--;
|
||||
sector_num++;
|
||||
buf += 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vpc_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
qemu_free(s->pagetable);
|
||||
#ifdef CACHE
|
||||
qemu_free(s->pageentry_u8);
|
||||
#endif
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_vpc = {
|
||||
"vpc",
|
||||
sizeof(BDRVVPCState),
|
||||
vpc_probe,
|
||||
vpc_open,
|
||||
vpc_read,
|
||||
NULL,
|
||||
vpc_close,
|
||||
};
|
||||
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
* QEMU System Emulator block driver
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
#include "block_int.h"
|
||||
|
||||
#ifdef _BSD
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/disk.h>
|
||||
#endif
|
||||
|
||||
static BlockDriverState *bdrv_first;
|
||||
static BlockDriver *first_drv;
|
||||
|
||||
void bdrv_register(BlockDriver *bdrv)
|
||||
{
|
||||
bdrv->next = first_drv;
|
||||
first_drv = bdrv;
|
||||
}
|
||||
|
||||
/* create a new block device (by default it is empty) */
|
||||
BlockDriverState *bdrv_new(const char *device_name)
|
||||
{
|
||||
BlockDriverState **pbs, *bs;
|
||||
|
||||
bs = qemu_mallocz(sizeof(BlockDriverState));
|
||||
if(!bs)
|
||||
return NULL;
|
||||
pstrcpy(bs->device_name, sizeof(bs->device_name), device_name);
|
||||
if (device_name[0] != '\0') {
|
||||
/* insert at the end */
|
||||
pbs = &bdrv_first;
|
||||
while (*pbs != NULL)
|
||||
pbs = &(*pbs)->next;
|
||||
*pbs = bs;
|
||||
}
|
||||
return bs;
|
||||
}
|
||||
|
||||
BlockDriver *bdrv_find_format(const char *format_name)
|
||||
{
|
||||
BlockDriver *drv1;
|
||||
for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
|
||||
if (!strcmp(drv1->format_name, format_name))
|
||||
return drv1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bdrv_create(BlockDriver *drv,
|
||||
const char *filename, int64_t size_in_sectors,
|
||||
const char *backing_file, int flags)
|
||||
{
|
||||
if (!drv->bdrv_create)
|
||||
return -ENOTSUP;
|
||||
return drv->bdrv_create(filename, size_in_sectors, backing_file, flags);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static void get_tmp_filename(char *filename, int size)
|
||||
{
|
||||
/* XXX: find a better function */
|
||||
tmpnam(filename);
|
||||
}
|
||||
#else
|
||||
static void get_tmp_filename(char *filename, int size)
|
||||
{
|
||||
int fd;
|
||||
/* XXX: race condition possible */
|
||||
pstrcpy(filename, size, "/tmp/vl.XXXXXX");
|
||||
fd = mkstemp(filename);
|
||||
close(fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* XXX: force raw format if block or character device ? It would
|
||||
simplify the BSD case */
|
||||
static BlockDriver *find_image_format(const char *filename)
|
||||
{
|
||||
int fd, ret, score, score_max;
|
||||
BlockDriver *drv1, *drv;
|
||||
uint8_t *buf;
|
||||
size_t bufsize = 1024;
|
||||
|
||||
fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
#ifdef DIOCGSECTORSIZE
|
||||
{
|
||||
unsigned int sectorsize = 512;
|
||||
if (!ioctl(fd, DIOCGSECTORSIZE, §orsize) &&
|
||||
sectorsize > bufsize)
|
||||
bufsize = sectorsize;
|
||||
}
|
||||
#endif
|
||||
buf = malloc(bufsize);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
ret = read(fd, buf, bufsize);
|
||||
if (ret < 0) {
|
||||
close(fd);
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
drv = NULL;
|
||||
score_max = 0;
|
||||
for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
|
||||
score = drv1->bdrv_probe(buf, ret, filename);
|
||||
if (score > score_max) {
|
||||
score_max = score;
|
||||
drv = drv1;
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
return drv;
|
||||
}
|
||||
|
||||
int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot)
|
||||
{
|
||||
return bdrv_open2(bs, filename, snapshot, NULL);
|
||||
}
|
||||
|
||||
int bdrv_open2(BlockDriverState *bs, const char *filename, int snapshot,
|
||||
BlockDriver *drv)
|
||||
{
|
||||
int ret;
|
||||
char tmp_filename[1024];
|
||||
|
||||
bs->read_only = 0;
|
||||
bs->is_temporary = 0;
|
||||
bs->encrypted = 0;
|
||||
|
||||
if (snapshot) {
|
||||
BlockDriverState *bs1;
|
||||
int64_t total_size;
|
||||
|
||||
/* if snapshot, we create a temporary backing file and open it
|
||||
instead of opening 'filename' directly */
|
||||
|
||||
/* if there is a backing file, use it */
|
||||
bs1 = bdrv_new("");
|
||||
if (!bs1) {
|
||||
return -1;
|
||||
}
|
||||
if (bdrv_open(bs1, filename, 0) < 0) {
|
||||
bdrv_delete(bs1);
|
||||
return -1;
|
||||
}
|
||||
total_size = bs1->total_sectors;
|
||||
bdrv_delete(bs1);
|
||||
|
||||
get_tmp_filename(tmp_filename, sizeof(tmp_filename));
|
||||
/* XXX: use cow for linux as it is more efficient ? */
|
||||
if (bdrv_create(&bdrv_qcow, tmp_filename,
|
||||
total_size, filename, 0) < 0) {
|
||||
return -1;
|
||||
}
|
||||
filename = tmp_filename;
|
||||
bs->is_temporary = 1;
|
||||
}
|
||||
|
||||
pstrcpy(bs->filename, sizeof(bs->filename), filename);
|
||||
if (!drv) {
|
||||
drv = find_image_format(filename);
|
||||
if (!drv)
|
||||
return -1;
|
||||
}
|
||||
bs->drv = drv;
|
||||
bs->opaque = qemu_mallocz(drv->instance_size);
|
||||
if (bs->opaque == NULL && drv->instance_size > 0)
|
||||
return -1;
|
||||
|
||||
ret = drv->bdrv_open(bs, filename);
|
||||
if (ret < 0) {
|
||||
qemu_free(bs->opaque);
|
||||
return -1;
|
||||
}
|
||||
#ifndef _WIN32
|
||||
if (bs->is_temporary) {
|
||||
unlink(filename);
|
||||
}
|
||||
#endif
|
||||
if (bs->backing_file[0] != '\0' && drv->bdrv_is_allocated) {
|
||||
/* if there is a backing file, use it */
|
||||
bs->backing_hd = bdrv_new("");
|
||||
if (!bs->backing_hd) {
|
||||
fail:
|
||||
bdrv_close(bs);
|
||||
return -1;
|
||||
}
|
||||
if (bdrv_open(bs->backing_hd, bs->backing_file, 0) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs->inserted = 1;
|
||||
|
||||
/* call the change callback */
|
||||
if (bs->change_cb)
|
||||
bs->change_cb(bs->change_opaque);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bdrv_close(BlockDriverState *bs)
|
||||
{
|
||||
if (bs->inserted) {
|
||||
if (bs->backing_hd)
|
||||
bdrv_delete(bs->backing_hd);
|
||||
bs->drv->bdrv_close(bs);
|
||||
qemu_free(bs->opaque);
|
||||
#ifdef _WIN32
|
||||
if (bs->is_temporary) {
|
||||
unlink(bs->filename);
|
||||
}
|
||||
#endif
|
||||
bs->opaque = NULL;
|
||||
bs->drv = NULL;
|
||||
bs->inserted = 0;
|
||||
|
||||
/* call the change callback */
|
||||
if (bs->change_cb)
|
||||
bs->change_cb(bs->change_opaque);
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_delete(BlockDriverState *bs)
|
||||
{
|
||||
/* XXX: remove the driver list */
|
||||
bdrv_close(bs);
|
||||
qemu_free(bs);
|
||||
}
|
||||
|
||||
/* commit COW file into the raw image */
|
||||
int bdrv_commit(BlockDriverState *bs)
|
||||
{
|
||||
int64_t i;
|
||||
int n, j;
|
||||
unsigned char sector[512];
|
||||
|
||||
if (!bs->inserted)
|
||||
return -ENOENT;
|
||||
|
||||
if (bs->read_only) {
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (!bs->backing_hd) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
for (i = 0; i < bs->total_sectors;) {
|
||||
if (bs->drv->bdrv_is_allocated(bs, i, 65536, &n)) {
|
||||
for(j = 0; j < n; j++) {
|
||||
if (bdrv_read(bs, i, sector, 1) != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
i += n;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return -1 if error */
|
||||
int bdrv_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
int ret, n;
|
||||
BlockDriver *drv = bs->drv;
|
||||
|
||||
if (!bs->inserted)
|
||||
return -1;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
if (sector_num == 0 && bs->boot_sector_enabled) {
|
||||
memcpy(buf, bs->boot_sector_data, 512);
|
||||
n = 1;
|
||||
} else if (bs->backing_hd) {
|
||||
if (drv->bdrv_is_allocated(bs, sector_num, nb_sectors, &n)) {
|
||||
ret = drv->bdrv_read(bs, sector_num, buf, n);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
} else {
|
||||
/* read from the base image */
|
||||
ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
ret = drv->bdrv_read(bs, sector_num, buf, nb_sectors);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
/* no need to loop */
|
||||
break;
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return -1 if error */
|
||||
int bdrv_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
if (!bs->inserted)
|
||||
return -1;
|
||||
if (bs->read_only)
|
||||
return -1;
|
||||
return bs->drv->bdrv_write(bs, sector_num, buf, nb_sectors);
|
||||
}
|
||||
|
||||
void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr)
|
||||
{
|
||||
*nb_sectors_ptr = bs->total_sectors;
|
||||
}
|
||||
|
||||
/* force a given boot sector. */
|
||||
void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size)
|
||||
{
|
||||
bs->boot_sector_enabled = 1;
|
||||
if (size > 512)
|
||||
size = 512;
|
||||
memcpy(bs->boot_sector_data, data, size);
|
||||
memset(bs->boot_sector_data + size, 0, 512 - size);
|
||||
}
|
||||
|
||||
void bdrv_set_geometry_hint(BlockDriverState *bs,
|
||||
int cyls, int heads, int secs)
|
||||
{
|
||||
bs->cyls = cyls;
|
||||
bs->heads = heads;
|
||||
bs->secs = secs;
|
||||
}
|
||||
|
||||
void bdrv_set_type_hint(BlockDriverState *bs, int type)
|
||||
{
|
||||
bs->type = type;
|
||||
bs->removable = ((type == BDRV_TYPE_CDROM ||
|
||||
type == BDRV_TYPE_FLOPPY));
|
||||
}
|
||||
|
||||
void bdrv_set_translation_hint(BlockDriverState *bs, int translation)
|
||||
{
|
||||
bs->translation = translation;
|
||||
}
|
||||
|
||||
void bdrv_get_geometry_hint(BlockDriverState *bs,
|
||||
int *pcyls, int *pheads, int *psecs)
|
||||
{
|
||||
*pcyls = bs->cyls;
|
||||
*pheads = bs->heads;
|
||||
*psecs = bs->secs;
|
||||
}
|
||||
|
||||
int bdrv_get_type_hint(BlockDriverState *bs)
|
||||
{
|
||||
return bs->type;
|
||||
}
|
||||
|
||||
int bdrv_get_translation_hint(BlockDriverState *bs)
|
||||
{
|
||||
return bs->translation;
|
||||
}
|
||||
|
||||
int bdrv_is_removable(BlockDriverState *bs)
|
||||
{
|
||||
return bs->removable;
|
||||
}
|
||||
|
||||
int bdrv_is_read_only(BlockDriverState *bs)
|
||||
{
|
||||
return bs->read_only;
|
||||
}
|
||||
|
||||
int bdrv_is_inserted(BlockDriverState *bs)
|
||||
{
|
||||
return bs->inserted;
|
||||
}
|
||||
|
||||
int bdrv_is_locked(BlockDriverState *bs)
|
||||
{
|
||||
return bs->locked;
|
||||
}
|
||||
|
||||
void bdrv_set_locked(BlockDriverState *bs, int locked)
|
||||
{
|
||||
bs->locked = locked;
|
||||
}
|
||||
|
||||
void bdrv_set_change_cb(BlockDriverState *bs,
|
||||
void (*change_cb)(void *opaque), void *opaque)
|
||||
{
|
||||
bs->change_cb = change_cb;
|
||||
bs->change_opaque = opaque;
|
||||
}
|
||||
|
||||
int bdrv_is_encrypted(BlockDriverState *bs)
|
||||
{
|
||||
if (bs->backing_hd && bs->backing_hd->encrypted)
|
||||
return 1;
|
||||
return bs->encrypted;
|
||||
}
|
||||
|
||||
int bdrv_set_key(BlockDriverState *bs, const char *key)
|
||||
{
|
||||
int ret;
|
||||
if (bs->backing_hd && bs->backing_hd->encrypted) {
|
||||
ret = bdrv_set_key(bs->backing_hd, key);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!bs->encrypted)
|
||||
return 0;
|
||||
}
|
||||
if (!bs->encrypted || !bs->drv || !bs->drv->bdrv_set_key)
|
||||
return -1;
|
||||
return bs->drv->bdrv_set_key(bs, key);
|
||||
}
|
||||
|
||||
void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size)
|
||||
{
|
||||
if (!bs->inserted || !bs->drv) {
|
||||
buf[0] = '\0';
|
||||
} else {
|
||||
pstrcpy(buf, buf_size, bs->drv->format_name);
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
|
||||
void *opaque)
|
||||
{
|
||||
BlockDriver *drv;
|
||||
|
||||
for (drv = first_drv; drv != NULL; drv = drv->next) {
|
||||
it(opaque, drv->format_name);
|
||||
}
|
||||
}
|
||||
|
||||
BlockDriverState *bdrv_find(const char *name)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
|
||||
for (bs = bdrv_first; bs != NULL; bs = bs->next) {
|
||||
if (!strcmp(name, bs->device_name))
|
||||
return bs;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
|
||||
for (bs = bdrv_first; bs != NULL; bs = bs->next) {
|
||||
it(opaque, bs->device_name);
|
||||
}
|
||||
}
|
||||
|
||||
const char *bdrv_get_device_name(BlockDriverState *bs)
|
||||
{
|
||||
return bs->device_name;
|
||||
}
|
||||
|
||||
void bdrv_info(void)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
|
||||
for (bs = bdrv_first; bs != NULL; bs = bs->next) {
|
||||
term_printf("%s:", bs->device_name);
|
||||
term_printf(" type=");
|
||||
switch(bs->type) {
|
||||
case BDRV_TYPE_HD:
|
||||
term_printf("hd");
|
||||
break;
|
||||
case BDRV_TYPE_CDROM:
|
||||
term_printf("cdrom");
|
||||
break;
|
||||
case BDRV_TYPE_FLOPPY:
|
||||
term_printf("floppy");
|
||||
break;
|
||||
}
|
||||
term_printf(" removable=%d", bs->removable);
|
||||
if (bs->removable) {
|
||||
term_printf(" locked=%d", bs->locked);
|
||||
}
|
||||
if (bs->inserted) {
|
||||
term_printf(" file=%s", bs->filename);
|
||||
if (bs->backing_file[0] != '\0')
|
||||
term_printf(" backing_file=%s", bs->backing_file);
|
||||
term_printf(" ro=%d", bs->read_only);
|
||||
term_printf(" drv=%s", bs->drv->format_name);
|
||||
if (bs->encrypted)
|
||||
term_printf(" encrypted");
|
||||
} else {
|
||||
term_printf(" [not inserted]");
|
||||
}
|
||||
term_printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************/
|
||||
/* RAW block driver */
|
||||
|
||||
typedef struct BDRVRawState {
|
||||
int fd;
|
||||
} BDRVRawState;
|
||||
|
||||
static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
return 1; /* maybe */
|
||||
}
|
||||
|
||||
static int raw_open(BlockDriverState *bs, const char *filename)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int fd;
|
||||
int64_t size;
|
||||
#ifdef _BSD
|
||||
struct stat sb;
|
||||
#endif
|
||||
|
||||
fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0) {
|
||||
fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
bs->read_only = 1;
|
||||
}
|
||||
#ifdef _BSD
|
||||
if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) {
|
||||
#ifdef DIOCGMEDIASIZE
|
||||
if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size))
|
||||
#endif
|
||||
size = lseek(fd, 0LL, SEEK_END);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
size = lseek(fd, 0, SEEK_END);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
/* On Windows hosts it can happen that we're unable to get file size
|
||||
for CD-ROM raw device (it's inherent limitation of the CDFS driver). */
|
||||
if (size == -1)
|
||||
size = LONG_LONG_MAX;
|
||||
#endif
|
||||
bs->total_sectors = size / 512;
|
||||
s->fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int raw_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
lseek(s->fd, sector_num * 512, SEEK_SET);
|
||||
ret = read(s->fd, buf, nb_sectors * 512);
|
||||
if (ret != nb_sectors * 512)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int raw_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
lseek(s->fd, sector_num * 512, SEEK_SET);
|
||||
ret = write(s->fd, buf, nb_sectors * 512);
|
||||
if (ret != nb_sectors * 512)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void raw_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
static int raw_create(const char *filename, int64_t total_size,
|
||||
const char *backing_file, int flags)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (flags || backing_file)
|
||||
return -ENOTSUP;
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
if (fd < 0)
|
||||
return -EIO;
|
||||
ftruncate(fd, total_size * 512);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BlockDriver bdrv_raw = {
|
||||
"raw",
|
||||
sizeof(BDRVRawState),
|
||||
raw_probe,
|
||||
raw_open,
|
||||
raw_read,
|
||||
raw_write,
|
||||
raw_close,
|
||||
raw_create,
|
||||
};
|
||||
|
||||
void bdrv_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_raw);
|
||||
#ifndef _WIN32
|
||||
bdrv_register(&bdrv_cow);
|
||||
#endif
|
||||
bdrv_register(&bdrv_qcow);
|
||||
bdrv_register(&bdrv_vmdk);
|
||||
bdrv_register(&bdrv_cloop);
|
||||
bdrv_register(&bdrv_dmg);
|
||||
bdrv_register(&bdrv_bochs);
|
||||
bdrv_register(&bdrv_vpc);
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* QEMU System Emulator block driver
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef BLOCK_INT_H
|
||||
#define BLOCK_INT_H
|
||||
|
||||
struct BlockDriver {
|
||||
const char *format_name;
|
||||
int instance_size;
|
||||
int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
|
||||
int (*bdrv_open)(BlockDriverState *bs, const char *filename);
|
||||
int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors);
|
||||
int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors);
|
||||
void (*bdrv_close)(BlockDriverState *bs);
|
||||
int (*bdrv_create)(const char *filename, int64_t total_sectors,
|
||||
const char *backing_file, int flags);
|
||||
int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum);
|
||||
int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
|
||||
struct BlockDriver *next;
|
||||
};
|
||||
|
||||
struct BlockDriverState {
|
||||
int64_t total_sectors;
|
||||
int read_only; /* if true, the media is read only */
|
||||
int inserted; /* if true, the media is present */
|
||||
int removable; /* if true, the media can be removed */
|
||||
int locked; /* if true, the media cannot temporarily be ejected */
|
||||
int encrypted; /* if true, the media is encrypted */
|
||||
/* event callback when inserting/removing */
|
||||
void (*change_cb)(void *opaque);
|
||||
void *change_opaque;
|
||||
|
||||
BlockDriver *drv;
|
||||
void *opaque;
|
||||
|
||||
int boot_sector_enabled;
|
||||
uint8_t boot_sector_data[512];
|
||||
|
||||
char filename[1024];
|
||||
char backing_file[1024]; /* if non zero, the image is a diff of
|
||||
this file image */
|
||||
int is_temporary;
|
||||
|
||||
BlockDriverState *backing_hd;
|
||||
|
||||
/* NOTE: the following infos are only hints for real hardware
|
||||
drivers. They are not used by the block driver */
|
||||
int cyls, heads, secs, translation;
|
||||
int type;
|
||||
char device_name[32];
|
||||
BlockDriverState *next;
|
||||
};
|
||||
|
||||
#endif /* BLOCK_INT_H */
|
||||
@@ -0,0 +1,202 @@
|
||||
#ifndef BSWAP_H
|
||||
#define BSWAP_H
|
||||
|
||||
#include "config-host.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef HAVE_BYTESWAP_H
|
||||
#include <byteswap.h>
|
||||
#else
|
||||
|
||||
#define bswap_16(x) \
|
||||
({ \
|
||||
uint16_t __x = (x); \
|
||||
((uint16_t)( \
|
||||
(((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \
|
||||
(((uint16_t)(__x) & (uint16_t)0xff00U) >> 8) )); \
|
||||
})
|
||||
|
||||
#define bswap_32(x) \
|
||||
({ \
|
||||
uint32_t __x = (x); \
|
||||
((uint32_t)( \
|
||||
(((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \
|
||||
})
|
||||
|
||||
#define bswap_64(x) \
|
||||
({ \
|
||||
uint64_t __x = (x); \
|
||||
((uint64_t)( \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \
|
||||
})
|
||||
|
||||
#endif /* !HAVE_BYTESWAP_H */
|
||||
|
||||
static inline uint16_t bswap16(uint16_t x)
|
||||
{
|
||||
return bswap_16(x);
|
||||
}
|
||||
|
||||
static inline uint32_t bswap32(uint32_t x)
|
||||
{
|
||||
return bswap_32(x);
|
||||
}
|
||||
|
||||
static inline uint64_t bswap64(uint64_t x)
|
||||
{
|
||||
return bswap_64(x);
|
||||
}
|
||||
|
||||
static inline void bswap16s(uint16_t *s)
|
||||
{
|
||||
*s = bswap16(*s);
|
||||
}
|
||||
|
||||
static inline void bswap32s(uint32_t *s)
|
||||
{
|
||||
*s = bswap32(*s);
|
||||
}
|
||||
|
||||
static inline void bswap64s(uint64_t *s)
|
||||
{
|
||||
*s = bswap64(*s);
|
||||
}
|
||||
|
||||
#if defined(WORDS_BIGENDIAN)
|
||||
#define be_bswap(v, size) (v)
|
||||
#define le_bswap(v, size) bswap ## size(v)
|
||||
#define be_bswaps(v, size)
|
||||
#define le_bswaps(p, size) *p = bswap ## size(*p);
|
||||
#else
|
||||
#define le_bswap(v, size) (v)
|
||||
#define be_bswap(v, size) bswap ## size(v)
|
||||
#define le_bswaps(v, size)
|
||||
#define be_bswaps(p, size) *p = bswap ## size(*p);
|
||||
#endif
|
||||
|
||||
#define CPU_CONVERT(endian, size, type)\
|
||||
static inline type endian ## size ## _to_cpu(type v)\
|
||||
{\
|
||||
return endian ## _bswap(v, size);\
|
||||
}\
|
||||
\
|
||||
static inline type cpu_to_ ## endian ## size(type v)\
|
||||
{\
|
||||
return endian ## _bswap(v, size);\
|
||||
}\
|
||||
\
|
||||
static inline void endian ## size ## _to_cpus(type *p)\
|
||||
{\
|
||||
endian ## _bswaps(p, size)\
|
||||
}\
|
||||
\
|
||||
static inline void cpu_to_ ## endian ## size ## s(type *p)\
|
||||
{\
|
||||
endian ## _bswaps(p, size)\
|
||||
}\
|
||||
\
|
||||
static inline type endian ## size ## _to_cpup(const type *p)\
|
||||
{\
|
||||
return endian ## size ## _to_cpu(*p);\
|
||||
}\
|
||||
\
|
||||
static inline void cpu_to_ ## endian ## size ## w(type *p, type v)\
|
||||
{\
|
||||
*p = cpu_to_ ## endian ## size(v);\
|
||||
}
|
||||
|
||||
CPU_CONVERT(be, 16, uint16_t)
|
||||
CPU_CONVERT(be, 32, uint32_t)
|
||||
CPU_CONVERT(be, 64, uint64_t)
|
||||
|
||||
CPU_CONVERT(le, 16, uint16_t)
|
||||
CPU_CONVERT(le, 32, uint32_t)
|
||||
CPU_CONVERT(le, 64, uint64_t)
|
||||
|
||||
/* unaligned versions (optimized for frequent unaligned accesses)*/
|
||||
|
||||
#if defined(__i386__) || defined(__powerpc__)
|
||||
|
||||
#define cpu_to_le16wu(p, v) cpu_to_le16w(p, v)
|
||||
#define cpu_to_le32wu(p, v) cpu_to_le32w(p, v)
|
||||
#define le16_to_cpupu(p) le16_to_cpup(p)
|
||||
#define le32_to_cpupu(p) le32_to_cpup(p)
|
||||
|
||||
#define cpu_to_be16wu(p, v) cpu_to_be16w(p, v)
|
||||
#define cpu_to_be32wu(p, v) cpu_to_be32w(p, v)
|
||||
|
||||
#else
|
||||
|
||||
static inline void cpu_to_le16wu(uint16_t *p, uint16_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v;
|
||||
p1[1] = v >> 8;
|
||||
}
|
||||
|
||||
static inline void cpu_to_le32wu(uint32_t *p, uint32_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v;
|
||||
p1[1] = v >> 8;
|
||||
p1[2] = v >> 16;
|
||||
p1[3] = v >> 24;
|
||||
}
|
||||
|
||||
static inline uint16_t le16_to_cpupu(const uint16_t *p)
|
||||
{
|
||||
const uint8_t *p1 = (const uint8_t *)p;
|
||||
return p1[0] | (p1[1] << 8);
|
||||
}
|
||||
|
||||
static inline uint32_t le32_to_cpupu(const uint32_t *p)
|
||||
{
|
||||
const uint8_t *p1 = (const uint8_t *)p;
|
||||
return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24);
|
||||
}
|
||||
|
||||
static inline void cpu_to_be16wu(uint16_t *p, uint16_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v >> 8;
|
||||
p1[1] = v;
|
||||
}
|
||||
|
||||
static inline void cpu_to_be32wu(uint32_t *p, uint32_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v >> 24;
|
||||
p1[1] = v >> 16;
|
||||
p1[2] = v >> 8;
|
||||
p1[3] = v;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define cpu_to_32wu cpu_to_be32wu
|
||||
#else
|
||||
#define cpu_to_32wu cpu_to_le32wu
|
||||
#endif
|
||||
|
||||
#undef le_bswap
|
||||
#undef be_bswap
|
||||
#undef le_bswaps
|
||||
#undef be_bswaps
|
||||
|
||||
#endif /* BSWAP_H */
|
||||
@@ -0,0 +1,614 @@
|
||||
/*
|
||||
* QEMU Cocoa display driver
|
||||
*
|
||||
* Copyright (c) 2005 Pierre d'Herbemont
|
||||
* many code/inspiration from SDL 1.2 code (LGPL)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
/*
|
||||
Todo : x miniaturize window
|
||||
x center the window
|
||||
- save window position
|
||||
- handle keyboard event
|
||||
- handle mouse event
|
||||
- non 32 bpp support
|
||||
- full screen
|
||||
- mouse focus
|
||||
x simple graphical prompt to demo
|
||||
- better graphical prompt
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
NSWindow *window = NULL;
|
||||
NSQuickDrawView *qd_view = NULL;
|
||||
|
||||
|
||||
int gArgc;
|
||||
char **gArgv;
|
||||
DisplayState current_ds;
|
||||
|
||||
/* main defined in qemu/vl.c */
|
||||
int qemu_main(int argc, char **argv);
|
||||
|
||||
/* To deal with miniaturization */
|
||||
@interface QemuWindow : NSWindow
|
||||
{ }
|
||||
@end
|
||||
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
Qemu Video Driver
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
cocoa_update
|
||||
------------------------------------------------------
|
||||
*/
|
||||
static void cocoa_update(DisplayState *ds, int x, int y, int w, int h)
|
||||
{
|
||||
//printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
|
||||
|
||||
/* Use QDFlushPortBuffer() to flush content to display */
|
||||
RgnHandle dirty = NewRgn ();
|
||||
RgnHandle temp = NewRgn ();
|
||||
|
||||
SetEmptyRgn (dirty);
|
||||
|
||||
/* Build the region of dirty rectangles */
|
||||
MacSetRectRgn (temp, x, y,
|
||||
x + w, y + h);
|
||||
MacUnionRgn (dirty, temp, dirty);
|
||||
|
||||
/* Flush the dirty region */
|
||||
QDFlushPortBuffer ( [ qd_view qdPort ], dirty );
|
||||
DisposeRgn (dirty);
|
||||
DisposeRgn (temp);
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
cocoa_resize
|
||||
------------------------------------------------------
|
||||
*/
|
||||
static void cocoa_resize(DisplayState *ds, int w, int h)
|
||||
{
|
||||
const int device_bpp = 32;
|
||||
static void *screen_pixels;
|
||||
static int screen_pitch;
|
||||
NSRect contentRect;
|
||||
|
||||
//printf("resizing to %d %d\n", w, h);
|
||||
|
||||
contentRect = NSMakeRect (0, 0, w, h);
|
||||
if(window)
|
||||
{
|
||||
[window close];
|
||||
[window release];
|
||||
}
|
||||
window = [ [ QemuWindow alloc ] initWithContentRect:contentRect
|
||||
styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask
|
||||
backing:NSBackingStoreBuffered defer:NO];
|
||||
if(!window)
|
||||
{
|
||||
fprintf(stderr, "(cocoa) can't create window\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(qd_view)
|
||||
[qd_view release];
|
||||
|
||||
qd_view = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ];
|
||||
|
||||
if(!qd_view)
|
||||
{
|
||||
fprintf(stderr, "(cocoa) can't create qd_view\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
[ window setAcceptsMouseMovedEvents:YES ];
|
||||
[ window setTitle:@"Qemu" ];
|
||||
[ window setReleasedWhenClosed:NO ];
|
||||
|
||||
/* Set screen to black */
|
||||
[ window setBackgroundColor: [NSColor blackColor] ];
|
||||
|
||||
/* set window position */
|
||||
[ window center ];
|
||||
|
||||
[ qd_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
|
||||
[ [ window contentView ] addSubview:qd_view ];
|
||||
[ qd_view release ];
|
||||
[ window makeKeyAndOrderFront:nil ];
|
||||
|
||||
/* Careful here, the window seems to have to be onscreen to do that */
|
||||
LockPortBits ( [ qd_view qdPort ] );
|
||||
screen_pixels = GetPixBaseAddr ( GetPortPixMap ( [ qd_view qdPort ] ) );
|
||||
screen_pitch = GetPixRowBytes ( GetPortPixMap ( [ qd_view qdPort ] ) );
|
||||
UnlockPortBits ( [ qd_view qdPort ] );
|
||||
{
|
||||
int vOffset = [ window frame ].size.height -
|
||||
[ qd_view frame ].size.height - [ qd_view frame ].origin.y;
|
||||
|
||||
int hOffset = [ qd_view frame ].origin.x;
|
||||
|
||||
screen_pixels += (vOffset * screen_pitch) + hOffset * (device_bpp/8);
|
||||
}
|
||||
ds->data = screen_pixels;
|
||||
ds->linesize = screen_pitch;
|
||||
ds->depth = device_bpp;
|
||||
ds->width = w;
|
||||
ds->height = h;
|
||||
|
||||
current_ds = *ds;
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
keymap conversion
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int keymap[] =
|
||||
{
|
||||
30, //'a' 0x0
|
||||
31, //'s'
|
||||
32, //'d'
|
||||
33, //'f'
|
||||
35, //'h'
|
||||
34, //'g'
|
||||
44, //'z'
|
||||
45, //'x'
|
||||
46, //'c'
|
||||
47, //'v'
|
||||
0, // 0 0x0a
|
||||
48, //'b'
|
||||
16, //'q'
|
||||
17, //'w'
|
||||
18, //'e'
|
||||
19, //'r'
|
||||
21, //'y' 0x10
|
||||
20, //'t'
|
||||
2, //'1'
|
||||
3, //'2'
|
||||
4, //'3'
|
||||
5, //'4'
|
||||
7, //'6'
|
||||
6, //'5'
|
||||
0, //'='
|
||||
10, //'9'
|
||||
8, //'7' 0x1A
|
||||
0, //'-'
|
||||
9, //'8'
|
||||
11, //'0'
|
||||
27, //']'
|
||||
24, //'o'
|
||||
22, //'u' 0x20
|
||||
26, //'['
|
||||
23, //'i'
|
||||
25, //'p'
|
||||
28, //'\n'
|
||||
38, //'l'
|
||||
36, //'j'
|
||||
40, //'"'
|
||||
37, //'k'
|
||||
39, //';'
|
||||
15, //'\t' 0x30
|
||||
0, //' '
|
||||
0, //'`'
|
||||
14, //'<backspace>'
|
||||
0, //'' 0x34
|
||||
0, //'<esc>'
|
||||
0, //'<esc>'
|
||||
/* Not completed to finish see http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */
|
||||
};
|
||||
|
||||
static int cocoa_keycode_to_qemu(int keycode)
|
||||
{
|
||||
if(sizeof(keymap) <= keycode)
|
||||
{
|
||||
printf("(cocoa) warning unknow keycode 0x%x\n", keycode);
|
||||
return 0;
|
||||
}
|
||||
return keymap[keycode];
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
cocoa_refresh
|
||||
------------------------------------------------------
|
||||
*/
|
||||
static void cocoa_refresh(DisplayState *ds)
|
||||
{
|
||||
//printf("cocoa_refresh \n");
|
||||
NSDate *distantPast;
|
||||
NSEvent *event;
|
||||
NSAutoreleasePool *pool;
|
||||
int grab = 1;
|
||||
|
||||
pool = [ [ NSAutoreleasePool alloc ] init ];
|
||||
distantPast = [ NSDate distantPast ];
|
||||
|
||||
if (is_active_console(vga_console))
|
||||
vga_update_display();
|
||||
do {
|
||||
event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
|
||||
inMode: NSDefaultRunLoopMode dequeue:YES ];
|
||||
if (event != nil) {
|
||||
switch ([event type]) {
|
||||
case NSKeyDown:
|
||||
if(grab)
|
||||
{
|
||||
int keycode = cocoa_keycode_to_qemu([event keyCode]);
|
||||
|
||||
if (keycode & 0x80)
|
||||
kbd_put_keycode(0xe0);
|
||||
kbd_put_keycode(keycode & 0x7f);
|
||||
}
|
||||
break;
|
||||
case NSKeyUp:
|
||||
if(grab)
|
||||
{
|
||||
int keycode = cocoa_keycode_to_qemu([event keyCode]);
|
||||
|
||||
if (keycode & 0x80)
|
||||
kbd_put_keycode(0xe0);
|
||||
kbd_put_keycode(keycode | 0x80);
|
||||
}
|
||||
break;
|
||||
case NSScrollWheel:
|
||||
|
||||
case NSLeftMouseDown:
|
||||
case NSLeftMouseUp:
|
||||
|
||||
case NSOtherMouseDown:
|
||||
case NSRightMouseDown:
|
||||
|
||||
case NSOtherMouseUp:
|
||||
case NSRightMouseUp:
|
||||
|
||||
case NSMouseMoved:
|
||||
case NSOtherMouseDragged:
|
||||
case NSRightMouseDragged:
|
||||
case NSLeftMouseDragged:
|
||||
|
||||
default: [NSApp sendEvent:event];
|
||||
}
|
||||
}
|
||||
} while(event != nil);
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
cocoa_cleanup
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void cocoa_cleanup(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
cocoa_display_init
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
void cocoa_display_init(DisplayState *ds, int full_screen)
|
||||
{
|
||||
ds->dpy_update = cocoa_update;
|
||||
ds->dpy_resize = cocoa_resize;
|
||||
ds->dpy_refresh = cocoa_refresh;
|
||||
|
||||
cocoa_resize(ds, 640, 400);
|
||||
|
||||
atexit(cocoa_cleanup);
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
Interface with Cocoa
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
QemuWindow
|
||||
Some trick from SDL to use miniwindow
|
||||
------------------------------------------------------
|
||||
*/
|
||||
static void QZ_SetPortAlphaOpaque ()
|
||||
{
|
||||
/* Assume 32 bit if( bpp == 32 )*/
|
||||
if ( 1 ) {
|
||||
|
||||
uint32_t *pixels = (uint32_t*) current_ds.data;
|
||||
uint32_t rowPixels = current_ds.linesize / 4;
|
||||
uint32_t i, j;
|
||||
|
||||
for (i = 0; i < current_ds.height; i++)
|
||||
for (j = 0; j < current_ds.width; j++) {
|
||||
|
||||
pixels[ (i * rowPixels) + j ] |= 0xFF000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@implementation QemuWindow
|
||||
- (void)miniaturize:(id)sender
|
||||
{
|
||||
|
||||
/* make the alpha channel opaque so anim won't have holes in it */
|
||||
QZ_SetPortAlphaOpaque ();
|
||||
|
||||
[ super miniaturize:sender ];
|
||||
|
||||
}
|
||||
- (void)display
|
||||
{
|
||||
/*
|
||||
This method fires just before the window deminaturizes from the Dock.
|
||||
|
||||
We'll save the current visible surface, let the window manager redraw any
|
||||
UI elements, and restore the SDL surface. This way, no expose event
|
||||
is required, and the deminiaturize works perfectly.
|
||||
*/
|
||||
|
||||
/* make sure pixels are fully opaque */
|
||||
QZ_SetPortAlphaOpaque ();
|
||||
|
||||
/* save current visible SDL surface */
|
||||
[ self cacheImageInRect:[ qd_view frame ] ];
|
||||
|
||||
/* let the window manager redraw controls, border, etc */
|
||||
[ super display ];
|
||||
|
||||
/* restore visible SDL surface */
|
||||
[ self restoreCachedImage ];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
QemuCocoaGUIController
|
||||
NSApp's delegate - indeed main object
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
@interface QemuCocoaGUIController : NSObject
|
||||
{
|
||||
}
|
||||
- (void)applicationDidFinishLaunching: (NSNotification *) note;
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification;
|
||||
|
||||
- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
|
||||
|
||||
- (void)startEmulationWithArgc:(int)argc argv:(char**)argv;
|
||||
@end
|
||||
|
||||
@implementation QemuCocoaGUIController
|
||||
/* Called when the internal event loop has just started running */
|
||||
- (void)applicationDidFinishLaunching: (NSNotification *) note
|
||||
{
|
||||
|
||||
/* Display an open dialog box if no argument were passed or
|
||||
if qemu was launched from the finder ( the Finder passes "-psn" ) */
|
||||
|
||||
if( gArgc <= 1 || strncmp (gArgv[1], "-psn", 4) == 0)
|
||||
{
|
||||
NSOpenPanel *op = [[NSOpenPanel alloc] init];
|
||||
|
||||
cocoa_resize(¤t_ds, 640, 400);
|
||||
|
||||
[op setPrompt:@"Boot image"];
|
||||
|
||||
[op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
|
||||
|
||||
[op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil]
|
||||
modalForWindow:window modalDelegate:self
|
||||
didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* or Launch Qemu, with the global args */
|
||||
[self startEmulationWithArgc:gArgc argv:gArgv];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
printf("Application will terminate\n");
|
||||
qemu_system_shutdown_request();
|
||||
/* In order to avoid a crash */
|
||||
exit(0);
|
||||
}
|
||||
|
||||
- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
|
||||
{
|
||||
if(returnCode == NSCancelButton)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(returnCode == NSOKButton)
|
||||
{
|
||||
char *bin = "qemu";
|
||||
char *img = (char*)[ [ sheet filename ] cString];
|
||||
|
||||
char **argv = (char**)malloc( sizeof(char*)*3 );
|
||||
|
||||
asprintf(&argv[0], "%s", bin);
|
||||
asprintf(&argv[1], "-hda");
|
||||
asprintf(&argv[2], "%s", img);
|
||||
|
||||
printf("Using argc %d argv %s -hda %s\n", 3, bin, img);
|
||||
|
||||
[self startEmulationWithArgc:3 argv:(char**)argv];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)startEmulationWithArgc:(int)argc argv:(char**)argv
|
||||
{
|
||||
int status;
|
||||
/* Launch Qemu */
|
||||
printf("starting qemu...\n");
|
||||
status = qemu_main (argc, argv);
|
||||
exit(status);
|
||||
}
|
||||
@end
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
Application Creation
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* Dock Connection */
|
||||
typedef struct CPSProcessSerNum
|
||||
{
|
||||
UInt32 lo;
|
||||
UInt32 hi;
|
||||
} CPSProcessSerNum;
|
||||
|
||||
extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
|
||||
extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
|
||||
extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
|
||||
|
||||
/* Menu Creation */
|
||||
static void setApplicationMenu(void)
|
||||
{
|
||||
/* warning: this code is very odd */
|
||||
NSMenu *appleMenu;
|
||||
NSMenuItem *menuItem;
|
||||
NSString *title;
|
||||
NSString *appName;
|
||||
|
||||
appName = @"Qemu";
|
||||
appleMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||
|
||||
/* Add menu items */
|
||||
title = [@"About " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Hide " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
|
||||
|
||||
menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
|
||||
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
|
||||
|
||||
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Quit " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
|
||||
|
||||
|
||||
/* Put menu into the menubar */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
||||
[menuItem setSubmenu:appleMenu];
|
||||
[[NSApp mainMenu] addItem:menuItem];
|
||||
|
||||
/* Tell the application object that this is now the application menu */
|
||||
[NSApp setAppleMenu:appleMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[appleMenu release];
|
||||
[menuItem release];
|
||||
}
|
||||
|
||||
/* Create a window menu */
|
||||
static void setupWindowMenu(void)
|
||||
{
|
||||
NSMenu *windowMenu;
|
||||
NSMenuItem *windowMenuItem;
|
||||
NSMenuItem *menuItem;
|
||||
|
||||
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
|
||||
/* "Minimize" item */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
|
||||
[windowMenu addItem:menuItem];
|
||||
[menuItem release];
|
||||
|
||||
/* Put menu into the menubar */
|
||||
windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
[[NSApp mainMenu] addItem:windowMenuItem];
|
||||
|
||||
/* Tell the application object that this is now the window menu */
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[windowMenu release];
|
||||
[windowMenuItem release];
|
||||
|
||||
}
|
||||
|
||||
static void CustomApplicationMain (argc, argv)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
QemuCocoaGUIController *gui_controller;
|
||||
CPSProcessSerNum PSN;
|
||||
|
||||
[NSApplication sharedApplication];
|
||||
|
||||
if (!CPSGetCurrentProcess(&PSN))
|
||||
if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
|
||||
if (!CPSSetFrontProcess(&PSN))
|
||||
[NSApplication sharedApplication];
|
||||
|
||||
/* Set up the menubar */
|
||||
[NSApp setMainMenu:[[NSMenu alloc] init]];
|
||||
setApplicationMenu();
|
||||
setupWindowMenu();
|
||||
|
||||
/* Create SDLMain and make it the app delegate */
|
||||
gui_controller = [[QemuCocoaGUIController alloc] init];
|
||||
[NSApp setDelegate:gui_controller];
|
||||
|
||||
/* Start the main event loop */
|
||||
[NSApp run];
|
||||
|
||||
[gui_controller release];
|
||||
[pool release];
|
||||
}
|
||||
|
||||
/* Real main of qemu-cocoa */
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
gArgc = argc;
|
||||
gArgv = argv;
|
||||
|
||||
CustomApplicationMain (argc, argv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -15,10 +15,11 @@ TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c"
|
||||
TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o"
|
||||
TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}"
|
||||
TMPS="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.S"
|
||||
TMPH="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.h"
|
||||
|
||||
# default parameters
|
||||
prefix="/usr/local"
|
||||
prefix=""
|
||||
interp_prefix="/usr/gnemul/qemu-%M"
|
||||
static="no"
|
||||
cross_prefix=""
|
||||
cc="gcc"
|
||||
host_cc="gcc"
|
||||
@@ -26,51 +27,108 @@ ar="ar"
|
||||
make="make"
|
||||
strip="strip"
|
||||
cpu=`uname -m`
|
||||
target_list=""
|
||||
case "$cpu" in
|
||||
i386|i486|i586|i686|i86pc|BePC)
|
||||
cpu="x86"
|
||||
cpu="i386"
|
||||
;;
|
||||
armv4l)
|
||||
armv*b)
|
||||
cpu="armv4b"
|
||||
;;
|
||||
armv*l)
|
||||
cpu="armv4l"
|
||||
;;
|
||||
alpha)
|
||||
cpu="alpha"
|
||||
;;
|
||||
"Power Macintosh"|ppc)
|
||||
"Power Macintosh"|ppc|ppc64)
|
||||
cpu="powerpc"
|
||||
;;
|
||||
mips)
|
||||
cpu="mips"
|
||||
;;
|
||||
s390)
|
||||
cpu="s390"
|
||||
;;
|
||||
sparc)
|
||||
cpu="sparc"
|
||||
;;
|
||||
sparc64)
|
||||
cpu="sparc64"
|
||||
;;
|
||||
ia64)
|
||||
cpu="ia64"
|
||||
;;
|
||||
m68k)
|
||||
cpu="m68k"
|
||||
;;
|
||||
x86_64|amd64)
|
||||
cpu="x86_64"
|
||||
;;
|
||||
*)
|
||||
cpu="unknown"
|
||||
;;
|
||||
esac
|
||||
gprof="no"
|
||||
bigendian="no"
|
||||
mingw32="no"
|
||||
EXESUF=""
|
||||
gdbstub="yes"
|
||||
slirp="yes"
|
||||
adlib="no"
|
||||
oss="no"
|
||||
fmod="no"
|
||||
fmod_lib=""
|
||||
fmod_inc=""
|
||||
linux="no"
|
||||
kqemu="no"
|
||||
kernel_path=""
|
||||
cocoa="no"
|
||||
|
||||
# OS specific
|
||||
targetos=`uname -s`
|
||||
case $targetos in
|
||||
BeOS)
|
||||
prefix="/boot/home/config"
|
||||
# helps building libavcodec
|
||||
CFLAGS="-O2 -DPIC"
|
||||
# no need for libm, but the inet stuff
|
||||
# Check for BONE
|
||||
if (echo $BEINCLUDES|grep 'headers/be/bone' >/dev/null); then
|
||||
extralibs="-lbind -lsocket"
|
||||
else
|
||||
echo "Not sure building for net_server will succeed... good luck."
|
||||
extralibs="-lsocket"
|
||||
fi ;;
|
||||
BSD/OS)
|
||||
extralibs="-lpoll -lgnugetopt -lm"
|
||||
make="gmake"
|
||||
CYGWIN*)
|
||||
mingw32="yes"
|
||||
CFLAGS="-O2 -mno-cygwin"
|
||||
;;
|
||||
MINGW32*)
|
||||
mingw32="yes"
|
||||
;;
|
||||
FreeBSD)
|
||||
bsd="yes"
|
||||
oss="yes"
|
||||
if [ "$cpu" = "i386" ] ; then
|
||||
kqemu="yes"
|
||||
fi
|
||||
;;
|
||||
NetBSD)
|
||||
bsd="yes"
|
||||
oss="yes"
|
||||
;;
|
||||
OpenBSD)
|
||||
bsd="yes"
|
||||
oss="yes"
|
||||
;;
|
||||
Darwin)
|
||||
bsd="yes"
|
||||
darwin="yes"
|
||||
;;
|
||||
*)
|
||||
oss="yes"
|
||||
linux="yes"
|
||||
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
|
||||
kqemu="yes"
|
||||
fi
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
if [ "$bsd" = "yes" ] ; then
|
||||
if [ ! "$darwin" = "yes" ] ; then
|
||||
make="gmake"
|
||||
fi
|
||||
fi
|
||||
|
||||
# find source path
|
||||
# XXX: we assume an absolute path is given when launching configure,
|
||||
# except in './configure' case.
|
||||
@@ -86,6 +144,8 @@ for opt do
|
||||
case "$opt" in
|
||||
--prefix=*) prefix=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--interp-prefix=*) interp_prefix=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--source-path=*) source_path=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--cross-prefix=*) cross_prefix=`echo $opt | cut -d '=' -f 2`
|
||||
@@ -98,12 +158,34 @@ for opt do
|
||||
;;
|
||||
--extra-ldflags=*) LDFLAGS="${opt#--extra-ldflags=}"
|
||||
;;
|
||||
--extra-libs=*) extralibs=${opt#--extra-libs=}
|
||||
;;
|
||||
--cpu=*) cpu=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--target-list=*) target_list=${opt#--target-list=}
|
||||
;;
|
||||
--enable-gprof) gprof="yes"
|
||||
;;
|
||||
--static) static="yes"
|
||||
;;
|
||||
--disable-sdl) sdl="no"
|
||||
;;
|
||||
--enable-fmod) fmod="yes"
|
||||
;;
|
||||
--fmod-lib=*) fmod_lib=${opt#--fmod-lib=}
|
||||
;;
|
||||
--fmod-inc=*) fmod_inc=${opt#--fmod-inc=}
|
||||
;;
|
||||
--enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-"
|
||||
;;
|
||||
--disable-slirp) slirp="no"
|
||||
;;
|
||||
--enable-adlib) adlib="yes"
|
||||
;;
|
||||
--disable-kqemu) kqemu="no"
|
||||
;;
|
||||
--kernel-path=*) kernel_path=${opt#--kernel-path=}
|
||||
;;
|
||||
--enable-cocoa) cocoa="yes" ; sdl="no"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -116,6 +198,25 @@ cc="${cross_prefix}${cc}"
|
||||
ar="${cross_prefix}${ar}"
|
||||
strip="${cross_prefix}${strip}"
|
||||
|
||||
if test "$mingw32" = "yes" ; then
|
||||
linux="no"
|
||||
EXESUF=".exe"
|
||||
gdbstub="no"
|
||||
oss="no"
|
||||
if [ "$cpu" = "i386" ] ; then
|
||||
kqemu="yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$target_list" ; then
|
||||
# these targets are portable
|
||||
target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu"
|
||||
# the following are Linux specific
|
||||
if [ "$linux" = "yes" ] ; then
|
||||
target_list="i386-user arm-user armeb-user sparc-user ppc-user $target_list"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$cross_prefix" ; then
|
||||
|
||||
# ---
|
||||
@@ -137,28 +238,74 @@ fi
|
||||
else
|
||||
|
||||
# if cross compiling, cannot launch a program, so make a static guess
|
||||
if test "$cpu" = "powerpc" -o "$cpu" = "mips" ; then
|
||||
if test "$cpu" = "powerpc" -o "$cpu" = "mips" -o "$cpu" = "s390" -o "$cpu" = "sparc" -o "$cpu" = "sparc64" -o "$cpu" = "m68k" -o "$cpu" = "armv4b"; then
|
||||
bigendian="yes"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# check gcc version
|
||||
# check gcc options support
|
||||
cat > $TMPC <<EOF
|
||||
int main(void) {
|
||||
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
|
||||
return 0;
|
||||
#else
|
||||
#error gcc < 3.2
|
||||
#endif
|
||||
}
|
||||
EOF
|
||||
|
||||
gcc_major="2"
|
||||
if $cc -o $TMPO $TMPC 2> /dev/null ; then
|
||||
gcc_major="3"
|
||||
have_gcc3_options="no"
|
||||
if $cc -fno-reorder-blocks -fno-optimize-sibling-calls -o $TMPO $TMPC 2> /dev/null ; then
|
||||
have_gcc3_options="yes"
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# SDL probe
|
||||
|
||||
sdl_too_old=no
|
||||
|
||||
if test -z "$sdl" ; then
|
||||
|
||||
sdl_config="sdl-config"
|
||||
sdl=no
|
||||
sdl_static=no
|
||||
|
||||
if test "$mingw32" = "yes" -a ! -z "$cross_prefix" ; then
|
||||
# win32 cross compilation case
|
||||
sdl_config="i386-mingw32msvc-sdl-config"
|
||||
sdl=yes
|
||||
else
|
||||
# normal SDL probe
|
||||
cat > $TMPC << EOF
|
||||
#include <SDL.h>
|
||||
#undef main /* We don't want SDL to override our main() */
|
||||
int main( void ) { return SDL_Init (SDL_INIT_VIDEO); }
|
||||
EOF
|
||||
|
||||
if $cc -o $TMPE `$sdl_config --cflags 2> /dev/null` $TMPC `$sdl_config --libs 2> /dev/null` 2> /dev/null ; then
|
||||
_sdlversion=`$sdl_config --version | sed 's/[^0-9]//g'`
|
||||
if test "$_sdlversion" -lt 121 ; then
|
||||
sdl_too_old=yes
|
||||
else
|
||||
sdl=yes
|
||||
fi
|
||||
|
||||
# static link with sdl ?
|
||||
if test "$sdl" = "yes" ; then
|
||||
aa="no"
|
||||
`$sdl_config --static-libs | grep \\\-laa > /dev/null` && aa="yes"
|
||||
sdl_static_libs=`$sdl_config --static-libs`
|
||||
if [ "$aa" = "yes" ] ; then
|
||||
sdl_static_libs="$sdl_static_libs `aalib-config --static-libs`"
|
||||
fi
|
||||
|
||||
if $cc -o $TMPE `$sdl_config --cflags 2> /dev/null` $TMPC $sdl_static_libs 2> /dev/null; then
|
||||
sdl_static=yes
|
||||
fi
|
||||
|
||||
fi # static link
|
||||
|
||||
fi # sdl compile test
|
||||
|
||||
fi # cross compilation
|
||||
fi # -z $sdl
|
||||
|
||||
if test x"$1" = x"-h" -o x"$1" = x"--help" ; then
|
||||
cat << EOF
|
||||
|
||||
@@ -169,71 +316,399 @@ EOF
|
||||
echo "Standard options:"
|
||||
echo " --help print this message"
|
||||
echo " --prefix=PREFIX install in PREFIX [$prefix]"
|
||||
echo " for audio/video/image support"
|
||||
echo " --interp-prefix=PREFIX where to find shared libraries, etc."
|
||||
echo " use %M for cpu name [$interp_prefix]"
|
||||
echo " --target-list=LIST set target list [$target_list]"
|
||||
echo ""
|
||||
echo "kqemu kernel acceleration support:"
|
||||
echo " --disable-kqemu disable kqemu build"
|
||||
echo " --kernel-path=PATH set the kernel path (configure probes it)"
|
||||
echo ""
|
||||
echo "Advanced options (experts only):"
|
||||
echo " --source-path=PATH path of source code [$source_path]"
|
||||
echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]"
|
||||
echo " --cc=CC use C compiler CC [$cc]"
|
||||
echo " --make=MAKE use specified make [$make]"
|
||||
echo " --static enable static build [$static]"
|
||||
echo " --enable-mingw32 enable Win32 cross compilation with mingw32"
|
||||
echo " --enable-fmod enable FMOD audio output driver"
|
||||
echo " --fmod-lib path to FMOD library"
|
||||
echo " --fmod-inc path to FMOD includes"
|
||||
echo ""
|
||||
echo "NOTE: The object files are build at the place where configure is launched"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Install prefix $prefix"
|
||||
echo "Source path $source_path"
|
||||
echo "C compiler $cc"
|
||||
echo "make $make"
|
||||
echo "CPU $cpu"
|
||||
echo "Big Endian $bigendian"
|
||||
echo "gprof enabled $gprof"
|
||||
if test "$mingw32" = "yes" ; then
|
||||
if test -z "$prefix" ; then
|
||||
prefix="/c/Program Files/Qemu"
|
||||
fi
|
||||
mandir="$prefix"
|
||||
datadir="$prefix"
|
||||
docdir="$prefix"
|
||||
bindir="$prefix"
|
||||
else
|
||||
if test -z "$prefix" ; then
|
||||
prefix="/usr/local"
|
||||
fi
|
||||
mandir="$prefix/share/man"
|
||||
datadir="$prefix/share/qemu"
|
||||
docdir="$prefix/share/doc/qemu"
|
||||
bindir="$prefix/bin"
|
||||
fi
|
||||
|
||||
echo "Creating config.mak and config.h"
|
||||
# kqemu support
|
||||
if test $kqemu = "yes" ; then
|
||||
# test if the source code is installed
|
||||
if test '!' -f "kqemu/Makefile" ; then
|
||||
kqemu="no"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Linux specific kqemu configuration
|
||||
if test $kqemu = "yes" -a $linux = "yes" ; then
|
||||
# find the kernel path
|
||||
if test -z "$kernel_path" ; then
|
||||
kernel_version=`uname -r`
|
||||
kernel_path="/lib/modules/$kernel_version/build"
|
||||
if test '!' -d "$kernel_path/include" ; then
|
||||
kernel_path="/usr/src/linux"
|
||||
if test '!' -d "$kernel_path/include" ; then
|
||||
echo "Could not find kernel includes in /lib/modules or /usr/src/linux - cannot build the kqemu module"
|
||||
kqemu="no"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "# Automatically generated by configure - do not modify" > config.mak
|
||||
echo "/* Automatically generated by configure - do not modify */" > $TMPH
|
||||
if test $kqemu = "yes" ; then
|
||||
|
||||
echo "prefix=$prefix" >> config.mak
|
||||
echo "#define CONFIG_QEMU_PREFIX \"$prefix\"" >> $TMPH
|
||||
echo "MAKE=$make" >> config.mak
|
||||
echo "CC=$cc" >> config.mak
|
||||
echo "GCC_MAJOR=$gcc_major" >> config.mak
|
||||
echo "HOST_CC=$host_cc" >> config.mak
|
||||
echo "AR=$ar" >> config.mak
|
||||
echo "STRIP=$strip -s -R .comment -R .note" >> config.mak
|
||||
echo "CFLAGS=$CFLAGS" >> config.mak
|
||||
echo "LDFLAGS=$LDFLAGS" >> config.mak
|
||||
if test "$cpu" = "x86" ; then
|
||||
echo "ARCH=i386" >> config.mak
|
||||
# test that the kernel config is present
|
||||
if test '!' -f "$kernel_path/Makefile" ; then
|
||||
echo "No Makefile file present in $kernel_path - kqemu cannot be built"
|
||||
kqemu="no"
|
||||
fi
|
||||
|
||||
# find build system (2.6 or legacy)
|
||||
kbuild26="yes"
|
||||
if grep -q "PATCHLEVEL = 4" $kernel_path/Makefile ; then
|
||||
kbuild26="no"
|
||||
fi
|
||||
|
||||
fi # kqemu
|
||||
|
||||
fi # kqemu and linux
|
||||
|
||||
|
||||
echo "Install prefix $prefix"
|
||||
echo "BIOS directory $datadir"
|
||||
echo "binary directory $bindir"
|
||||
if test "$mingw32" = "no" ; then
|
||||
echo "Manual directory $mandir"
|
||||
echo "ELF interp prefix $interp_prefix"
|
||||
fi
|
||||
echo "Source path $source_path"
|
||||
echo "C compiler $cc"
|
||||
echo "make $make"
|
||||
echo "host CPU $cpu"
|
||||
echo "host big endian $bigendian"
|
||||
echo "target list $target_list"
|
||||
echo "gprof enabled $gprof"
|
||||
echo "static build $static"
|
||||
if test "$darwin" = "yes" ; then
|
||||
echo "Cocoa support $cocoa"
|
||||
fi
|
||||
echo "SDL support $sdl"
|
||||
if test "$sdl" != "no" ; then
|
||||
echo "SDL static link $sdl_static"
|
||||
fi
|
||||
echo "mingw32 support $mingw32"
|
||||
echo "Adlib support $adlib"
|
||||
echo -n "FMOD support $fmod"
|
||||
if test $fmod = "yes"; then
|
||||
echo -n " (lib='$fmod_lib' include='$fmod_inc')"
|
||||
fi
|
||||
echo ""
|
||||
echo "kqemu support $kqemu"
|
||||
if test $kqemu = "yes" -a $linux = "yes" ; then
|
||||
echo ""
|
||||
echo "KQEMU Linux module configuration:"
|
||||
echo "kernel sources $kernel_path"
|
||||
echo -n "kbuild type "
|
||||
if test $kbuild26 = "yes"; then
|
||||
echo "2.6"
|
||||
else
|
||||
echo "2.4"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $sdl_too_old = "yes"; then
|
||||
echo "-> Your SDL version is too old - please upgrade to have SDL support"
|
||||
fi
|
||||
#if test "$sdl_static" = "no"; then
|
||||
# echo "WARNING: cannot compile statically with SDL - qemu-fast won't have a graphical output"
|
||||
#fi
|
||||
|
||||
config_mak="config-host.mak"
|
||||
config_h="config-host.h"
|
||||
|
||||
#echo "Creating $config_mak and $config_h"
|
||||
|
||||
echo "# Automatically generated by configure - do not modify" > $config_mak
|
||||
echo "/* Automatically generated by configure - do not modify */" > $config_h
|
||||
|
||||
echo "prefix=$prefix" >> $config_mak
|
||||
echo "bindir=$bindir" >> $config_mak
|
||||
echo "mandir=$mandir" >> $config_mak
|
||||
echo "datadir=$datadir" >> $config_mak
|
||||
echo "docdir=$docdir" >> $config_mak
|
||||
echo "#define CONFIG_QEMU_SHAREDIR \"$datadir\"" >> $config_h
|
||||
echo "MAKE=$make" >> $config_mak
|
||||
echo "CC=$cc" >> $config_mak
|
||||
if test "$have_gcc3_options" = "yes" ; then
|
||||
echo "HAVE_GCC3_OPTIONS=yes" >> $config_mak
|
||||
fi
|
||||
echo "HOST_CC=$host_cc" >> $config_mak
|
||||
echo "AR=$ar" >> $config_mak
|
||||
echo "STRIP=$strip -s -R .comment -R .note" >> $config_mak
|
||||
echo "CFLAGS=$CFLAGS" >> $config_mak
|
||||
echo "LDFLAGS=$LDFLAGS" >> $config_mak
|
||||
echo "EXESUF=$EXESUF" >> $config_mak
|
||||
if test "$cpu" = "i386" ; then
|
||||
echo "ARCH=i386" >> $config_mak
|
||||
echo "#define HOST_I386 1" >> $config_h
|
||||
elif test "$cpu" = "x86_64" ; then
|
||||
echo "ARCH=x86_64" >> $config_mak
|
||||
echo "#define HOST_X86_64 1" >> $config_h
|
||||
elif test "$cpu" = "armv4b" ; then
|
||||
echo "ARCH=arm" >> $config_mak
|
||||
echo "#define HOST_ARM 1" >> $config_h
|
||||
elif test "$cpu" = "armv4l" ; then
|
||||
echo "ARCH=arm" >> config.mak
|
||||
echo "ARCH=arm" >> $config_mak
|
||||
echo "#define HOST_ARM 1" >> $config_h
|
||||
elif test "$cpu" = "powerpc" ; then
|
||||
echo "ARCH=ppc" >> config.mak
|
||||
echo "ARCH=ppc" >> $config_mak
|
||||
echo "#define HOST_PPC 1" >> $config_h
|
||||
elif test "$cpu" = "mips" ; then
|
||||
echo "ARCH=mips" >> config.mak
|
||||
echo "ARCH=mips" >> $config_mak
|
||||
echo "#define HOST_MIPS 1" >> $config_h
|
||||
elif test "$cpu" = "s390" ; then
|
||||
echo "ARCH=s390" >> $config_mak
|
||||
echo "#define HOST_S390 1" >> $config_h
|
||||
elif test "$cpu" = "alpha" ; then
|
||||
echo "ARCH=alpha" >> $config_mak
|
||||
echo "#define HOST_ALPHA 1" >> $config_h
|
||||
elif test "$cpu" = "sparc" ; then
|
||||
echo "ARCH=sparc" >> $config_mak
|
||||
echo "#define HOST_SPARC 1" >> $config_h
|
||||
elif test "$cpu" = "sparc64" ; then
|
||||
echo "ARCH=sparc64" >> $config_mak
|
||||
echo "#define HOST_SPARC64 1" >> $config_h
|
||||
elif test "$cpu" = "ia64" ; then
|
||||
echo "ARCH=ia64" >> $config_mak
|
||||
echo "#define HOST_IA64 1" >> $config_h
|
||||
elif test "$cpu" = "m68k" ; then
|
||||
echo "ARCH=m68k" >> $config_mak
|
||||
echo "#define HOST_M68K 1" >> $config_h
|
||||
else
|
||||
echo "Unsupported CPU"
|
||||
exit 1
|
||||
fi
|
||||
if test "$bigendian" = "yes" ; then
|
||||
echo "WORDS_BIGENDIAN=yes" >> config.mak
|
||||
echo "#define WORDS_BIGENDIAN 1" >> $TMPH
|
||||
echo "WORDS_BIGENDIAN=yes" >> $config_mak
|
||||
echo "#define WORDS_BIGENDIAN 1" >> $config_h
|
||||
fi
|
||||
if test "$mingw32" = "yes" ; then
|
||||
echo "CONFIG_WIN32=yes" >> $config_mak
|
||||
echo "#define CONFIG_WIN32 1" >> $config_h
|
||||
elif test -f "/usr/include/byteswap.h" ; then
|
||||
echo "#define HAVE_BYTESWAP_H 1" >> $config_h
|
||||
fi
|
||||
if test "$darwin" = "yes" ; then
|
||||
echo "CONFIG_DARWIN=yes" >> $config_mak
|
||||
echo "#define CONFIG_DARWIN 1" >> $config_h
|
||||
fi
|
||||
if test "$gdbstub" = "yes" ; then
|
||||
echo "CONFIG_GDBSTUB=yes" >> $config_mak
|
||||
echo "#define CONFIG_GDBSTUB 1" >> $config_h
|
||||
fi
|
||||
if test "$gprof" = "yes" ; then
|
||||
echo "TARGET_GPROF=yes" >> config.mak
|
||||
echo "#define HAVE_GPROF 1" >> $TMPH
|
||||
echo "TARGET_GPROF=yes" >> $config_mak
|
||||
echo "#define HAVE_GPROF 1" >> $config_h
|
||||
fi
|
||||
echo -n "VERSION=" >>config.mak
|
||||
head $source_path/VERSION >>config.mak
|
||||
echo "" >>config.mak
|
||||
echo -n "#define QEMU_VERSION \"" >> $TMPH
|
||||
head $source_path/VERSION >> $TMPH
|
||||
echo "\"" >> $TMPH
|
||||
if test "$network" = "yes" ; then
|
||||
echo "#define CONFIG_NETWORK 1" >> $TMPH
|
||||
echo "CONFIG_NETWORK=yes" >> config.mak
|
||||
if test "$static" = "yes" ; then
|
||||
echo "CONFIG_STATIC=yes" >> $config_mak
|
||||
echo "#define CONFIG_STATIC 1" >> $config_h
|
||||
fi
|
||||
if test "$slirp" = "yes" ; then
|
||||
echo "CONFIG_SLIRP=yes" >> $config_mak
|
||||
echo "#define CONFIG_SLIRP 1" >> $config_h
|
||||
fi
|
||||
if test "$adlib" = "yes" ; then
|
||||
echo "CONFIG_ADLIB=yes" >> $config_mak
|
||||
echo "#define CONFIG_ADLIB 1" >> $config_h
|
||||
fi
|
||||
if test "$oss" = "yes" ; then
|
||||
echo "CONFIG_OSS=yes" >> $config_mak
|
||||
echo "#define CONFIG_OSS 1" >> $config_h
|
||||
fi
|
||||
if test "$fmod" = "yes" ; then
|
||||
echo "CONFIG_FMOD=yes" >> $config_mak
|
||||
echo "CONFIG_FMOD_LIB=$fmod_lib" >> $config_mak
|
||||
echo "CONFIG_FMOD_INC=$fmod_inc" >> $config_mak
|
||||
echo "#define CONFIG_FMOD 1" >> $config_h
|
||||
fi
|
||||
echo -n "VERSION=" >>$config_mak
|
||||
head $source_path/VERSION >>$config_mak
|
||||
echo "" >>$config_mak
|
||||
echo -n "#define QEMU_VERSION \"" >> $config_h
|
||||
head $source_path/VERSION >> $config_h
|
||||
echo "\"" >> $config_h
|
||||
|
||||
if test $kqemu = "yes" ; then
|
||||
echo "CONFIG_KQEMU=yes" >> $config_mak
|
||||
if test $linux = "yes" ; then
|
||||
echo "KERNEL_PATH=$kernel_path" >> $config_mak
|
||||
if test $kbuild26 = "yes" ; then
|
||||
echo "CONFIG_KBUILD26=yes" >> $config_mak
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
echo "SRC_PATH=$source_path" >> $config_mak
|
||||
echo "TARGET_DIRS=$target_list" >> $config_mak
|
||||
|
||||
# XXX: suppress that
|
||||
if [ "$bsd" = "yes" ] ; then
|
||||
echo "#define O_LARGEFILE 0" >> $config_h
|
||||
echo "#define MAP_ANONYMOUS MAP_ANON" >> $config_h
|
||||
echo "#define _BSD 1" >> $config_h
|
||||
fi
|
||||
|
||||
for target in $target_list; do
|
||||
|
||||
target_dir="$target"
|
||||
config_mak=$target_dir/config.mak
|
||||
config_h=$target_dir/config.h
|
||||
target_cpu=`echo $target | cut -d '-' -f 1`
|
||||
target_bigendian="no"
|
||||
[ "$target_cpu" = "armeb" ] && target_bigendian=yes
|
||||
[ "$target_cpu" = "sparc" ] && target_bigendian=yes
|
||||
[ "$target_cpu" = "sparc64" ] && target_bigendian=yes
|
||||
[ "$target_cpu" = "ppc" ] && target_bigendian=yes
|
||||
target_softmmu="no"
|
||||
if expr $target : '.*-softmmu' > /dev/null ; then
|
||||
target_softmmu="yes"
|
||||
fi
|
||||
target_user_only="no"
|
||||
if expr $target : '.*-user' > /dev/null ; then
|
||||
target_user_only="yes"
|
||||
fi
|
||||
|
||||
#echo "Creating $config_mak, $config_h and $target_dir/Makefile"
|
||||
|
||||
mkdir -p $target_dir
|
||||
mkdir -p $target_dir/fpu
|
||||
if test "$target" = "arm-user" -o "$target" = "armeb-user" ; then
|
||||
mkdir -p $target_dir/nwfpe
|
||||
fi
|
||||
if test "$target_user_only" = "no" ; then
|
||||
mkdir -p $target_dir/slirp
|
||||
fi
|
||||
|
||||
ln -sf $source_path/Makefile.target $target_dir/Makefile
|
||||
|
||||
echo "# Automatically generated by configure - do not modify" > $config_mak
|
||||
echo "/* Automatically generated by configure - do not modify */" > $config_h
|
||||
|
||||
|
||||
echo "include ../config-host.mak" >> $config_mak
|
||||
echo "#include \"../config-host.h\"" >> $config_h
|
||||
|
||||
interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_cpu/g"`
|
||||
echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix1\"" >> $config_h
|
||||
|
||||
if test "$target_cpu" = "i386" ; then
|
||||
echo "TARGET_ARCH=i386" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"i386\"" >> $config_h
|
||||
echo "#define TARGET_I386 1" >> $config_h
|
||||
if test $kqemu = "yes" -a "$target_softmmu" = "yes" -a $cpu = "i386" ; then
|
||||
echo "#define USE_KQEMU 1" >> $config_h
|
||||
fi
|
||||
elif test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" ; then
|
||||
echo "TARGET_ARCH=arm" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"arm\"" >> $config_h
|
||||
echo "#define TARGET_ARM 1" >> $config_h
|
||||
elif test "$target_cpu" = "sparc" ; then
|
||||
echo "TARGET_ARCH=sparc" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"sparc\"" >> $config_h
|
||||
echo "#define TARGET_SPARC 1" >> $config_h
|
||||
elif test "$target_cpu" = "sparc64" ; then
|
||||
echo "TARGET_ARCH=sparc64" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"sparc64\"" >> $config_h
|
||||
echo "#define TARGET_SPARC 1" >> $config_h
|
||||
echo "#define TARGET_SPARC64 1" >> $config_h
|
||||
elif test "$target_cpu" = "ppc" ; then
|
||||
echo "TARGET_ARCH=ppc" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"ppc\"" >> $config_h
|
||||
echo "#define TARGET_PPC 1" >> $config_h
|
||||
elif test "$target_cpu" = "x86_64" ; then
|
||||
echo "TARGET_ARCH=x86_64" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"x86_64\"" >> $config_h
|
||||
echo "#define TARGET_I386 1" >> $config_h
|
||||
echo "#define TARGET_X86_64 1" >> $config_h
|
||||
if test $kqemu = "yes" -a "$target_softmmu" = "yes" -a $cpu = "x86_64" ; then
|
||||
echo "#define USE_KQEMU 1" >> $config_h
|
||||
fi
|
||||
else
|
||||
echo "Unsupported target CPU"
|
||||
exit 1
|
||||
fi
|
||||
if test "$target_bigendian" = "yes" ; then
|
||||
echo "TARGET_WORDS_BIGENDIAN=yes" >> $config_mak
|
||||
echo "#define TARGET_WORDS_BIGENDIAN 1" >> $config_h
|
||||
fi
|
||||
if test "$target_softmmu" = "yes" ; then
|
||||
echo "CONFIG_SOFTMMU=yes" >> $config_mak
|
||||
echo "#define CONFIG_SOFTMMU 1" >> $config_h
|
||||
fi
|
||||
if test "$target_user_only" = "yes" ; then
|
||||
echo "CONFIG_USER_ONLY=yes" >> $config_mak
|
||||
echo "#define CONFIG_USER_ONLY 1" >> $config_h
|
||||
fi
|
||||
|
||||
if test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" ; then
|
||||
echo "CONFIG_SOFTFLOAT=yes" >> $config_mak
|
||||
echo "#define CONFIG_SOFTFLOAT 1" >> $config_h
|
||||
fi
|
||||
# sdl defines
|
||||
|
||||
if test "$target_user_only" = "no"; then
|
||||
if test "$target_softmmu" = "no" -o "$static" = "yes"; then
|
||||
sdl1=$sdl_static
|
||||
else
|
||||
sdl1=$sdl
|
||||
fi
|
||||
if test "$sdl1" = "yes" ; then
|
||||
echo "#define CONFIG_SDL 1" >> $config_h
|
||||
echo "CONFIG_SDL=yes" >> $config_mak
|
||||
if test "$target_softmmu" = "no" -o "$static" = "yes"; then
|
||||
echo "SDL_LIBS=$sdl_static_libs" >> $config_mak
|
||||
else
|
||||
echo "SDL_LIBS=`$sdl_config --libs`" >> $config_mak
|
||||
fi
|
||||
echo -n "SDL_CFLAGS=`$sdl_config --cflags`" >> $config_mak
|
||||
if [ "${aa}" = "yes" ] ; then
|
||||
echo -n " `aalib-config --cflags`" >> $config_mak ;
|
||||
fi
|
||||
echo "" >> $config_mak
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$cocoa" = "yes" ; then
|
||||
echo "#define CONFIG_COCOA 1" >> $config_h
|
||||
echo "CONFIG_COCOA=yes" >> $config_mak
|
||||
fi
|
||||
|
||||
done # for target in $targets
|
||||
|
||||
# build tree in object directory if source path is different from current one
|
||||
if test "$source_path_used" = "yes" ; then
|
||||
@@ -246,13 +721,5 @@ if test "$source_path_used" = "yes" ; then
|
||||
ln -sf $source_path/$f $f
|
||||
done
|
||||
fi
|
||||
echo "SRC_PATH=$source_path" >> config.mak
|
||||
|
||||
diff $TMPH config.h >/dev/null 2>&1
|
||||
if test $? -ne 0 ; then
|
||||
mv -f $TMPH config.h
|
||||
else
|
||||
echo "config.h is unchanged"
|
||||
fi
|
||||
|
||||
rm -f $TMPH
|
||||
rm -f $TMPO $TMPC $TMPE $TMPS
|
||||
@@ -0,0 +1,731 @@
|
||||
/*
|
||||
* QEMU graphical console
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
#define DEFAULT_BACKSCROLL 512
|
||||
#define MAX_CONSOLES 12
|
||||
|
||||
#define RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
|
||||
#define RGB(r, g, b) RGBA(r, g, b, 0xff)
|
||||
|
||||
typedef struct TextCell {
|
||||
uint8_t ch;
|
||||
uint8_t bgcol:4;
|
||||
uint8_t fgcol:4;
|
||||
} TextCell;
|
||||
|
||||
#define MAX_ESC_PARAMS 3
|
||||
|
||||
enum TTYState {
|
||||
TTY_STATE_NORM,
|
||||
TTY_STATE_ESC,
|
||||
TTY_STATE_CSI,
|
||||
};
|
||||
|
||||
struct TextConsole {
|
||||
int text_console; /* true if text console */
|
||||
DisplayState *ds;
|
||||
int g_width, g_height;
|
||||
int width;
|
||||
int height;
|
||||
int total_height;
|
||||
int backscroll_height;
|
||||
int fgcol;
|
||||
int bgcol;
|
||||
int x, y;
|
||||
int y_displayed;
|
||||
int y_base;
|
||||
TextCell *cells;
|
||||
|
||||
enum TTYState state;
|
||||
int esc_params[MAX_ESC_PARAMS];
|
||||
int nb_esc_params;
|
||||
|
||||
/* kbd read handler */
|
||||
IOReadHandler *fd_read;
|
||||
void *fd_opaque;
|
||||
};
|
||||
|
||||
static TextConsole *active_console;
|
||||
static TextConsole *consoles[MAX_CONSOLES];
|
||||
static int nb_consoles = 0;
|
||||
|
||||
/* convert a RGBA color to a color index usable in graphic primitives */
|
||||
static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
|
||||
{
|
||||
unsigned int r, g, b, color;
|
||||
|
||||
switch(ds->depth) {
|
||||
#if 0
|
||||
case 8:
|
||||
r = (rgba >> 16) & 0xff;
|
||||
g = (rgba >> 8) & 0xff;
|
||||
b = (rgba) & 0xff;
|
||||
color = (rgb_to_index[r] * 6 * 6) +
|
||||
(rgb_to_index[g] * 6) +
|
||||
(rgb_to_index[b]);
|
||||
break;
|
||||
#endif
|
||||
case 15:
|
||||
r = (rgba >> 16) & 0xff;
|
||||
g = (rgba >> 8) & 0xff;
|
||||
b = (rgba) & 0xff;
|
||||
color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
|
||||
break;
|
||||
case 16:
|
||||
r = (rgba >> 16) & 0xff;
|
||||
g = (rgba >> 8) & 0xff;
|
||||
b = (rgba) & 0xff;
|
||||
color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
|
||||
break;
|
||||
case 32:
|
||||
default:
|
||||
color = rgba;
|
||||
break;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
static void vga_fill_rect (DisplayState *ds,
|
||||
int posx, int posy, int width, int height, uint32_t color)
|
||||
{
|
||||
uint8_t *d, *d1;
|
||||
int x, y, bpp;
|
||||
|
||||
bpp = (ds->depth + 7) >> 3;
|
||||
d1 = ds->data +
|
||||
ds->linesize * posy + bpp * posx;
|
||||
for (y = 0; y < height; y++) {
|
||||
d = d1;
|
||||
switch(bpp) {
|
||||
case 1:
|
||||
for (x = 0; x < width; x++) {
|
||||
*((uint8_t *)d) = color;
|
||||
d++;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for (x = 0; x < width; x++) {
|
||||
*((uint16_t *)d) = color;
|
||||
d += 2;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
for (x = 0; x < width; x++) {
|
||||
*((uint32_t *)d) = color;
|
||||
d += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
d1 += ds->linesize;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
|
||||
static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
|
||||
{
|
||||
const uint8_t *s;
|
||||
uint8_t *d;
|
||||
int wb, y, bpp;
|
||||
|
||||
bpp = (ds->depth + 7) >> 3;
|
||||
wb = w * bpp;
|
||||
if (yd <= ys) {
|
||||
s = ds->data +
|
||||
ds->linesize * ys + bpp * xs;
|
||||
d = ds->data +
|
||||
ds->linesize * yd + bpp * xd;
|
||||
for (y = 0; y < h; y++) {
|
||||
memmove(d, s, wb);
|
||||
d += ds->linesize;
|
||||
s += ds->linesize;
|
||||
}
|
||||
} else {
|
||||
s = ds->data +
|
||||
ds->linesize * (ys + h - 1) + bpp * xs;
|
||||
d = ds->data +
|
||||
ds->linesize * (yd + h - 1) + bpp * xd;
|
||||
for (y = 0; y < h; y++) {
|
||||
memmove(d, s, wb);
|
||||
d -= ds->linesize;
|
||||
s -= ds->linesize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* basic char display */
|
||||
|
||||
#define FONT_HEIGHT 16
|
||||
#define FONT_WIDTH 8
|
||||
|
||||
#include "vgafont.h"
|
||||
|
||||
#define cbswap_32(__x) \
|
||||
((uint32_t)( \
|
||||
(((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define PAT(x) x
|
||||
#else
|
||||
#define PAT(x) cbswap_32(x)
|
||||
#endif
|
||||
|
||||
static const uint32_t dmask16[16] = {
|
||||
PAT(0x00000000),
|
||||
PAT(0x000000ff),
|
||||
PAT(0x0000ff00),
|
||||
PAT(0x0000ffff),
|
||||
PAT(0x00ff0000),
|
||||
PAT(0x00ff00ff),
|
||||
PAT(0x00ffff00),
|
||||
PAT(0x00ffffff),
|
||||
PAT(0xff000000),
|
||||
PAT(0xff0000ff),
|
||||
PAT(0xff00ff00),
|
||||
PAT(0xff00ffff),
|
||||
PAT(0xffff0000),
|
||||
PAT(0xffff00ff),
|
||||
PAT(0xffffff00),
|
||||
PAT(0xffffffff),
|
||||
};
|
||||
|
||||
static const uint32_t dmask4[4] = {
|
||||
PAT(0x00000000),
|
||||
PAT(0x0000ffff),
|
||||
PAT(0xffff0000),
|
||||
PAT(0xffffffff),
|
||||
};
|
||||
|
||||
static uint32_t color_table[8];
|
||||
|
||||
static const uint32_t color_table_rgb[8] = {
|
||||
RGB(0x00, 0x00, 0x00),
|
||||
RGB(0xff, 0x00, 0x00),
|
||||
RGB(0x00, 0xff, 0x00),
|
||||
RGB(0xff, 0xff, 0x00),
|
||||
RGB(0x00, 0x00, 0xff),
|
||||
RGB(0xff, 0x00, 0xff),
|
||||
RGB(0x00, 0xff, 0xff),
|
||||
RGB(0xff, 0xff, 0xff),
|
||||
};
|
||||
|
||||
static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
|
||||
{
|
||||
switch(ds->depth) {
|
||||
case 8:
|
||||
col |= col << 8;
|
||||
col |= col << 16;
|
||||
break;
|
||||
case 15:
|
||||
case 16:
|
||||
col |= col << 16;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
|
||||
unsigned int fgcol, unsigned int bgcol)
|
||||
{
|
||||
uint8_t *d;
|
||||
const uint8_t *font_ptr;
|
||||
unsigned int font_data, linesize, xorcol, bpp;
|
||||
int i;
|
||||
|
||||
bpp = (ds->depth + 7) >> 3;
|
||||
d = ds->data +
|
||||
ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
|
||||
linesize = ds->linesize;
|
||||
font_ptr = vgafont16 + FONT_HEIGHT * ch;
|
||||
xorcol = bgcol ^ fgcol;
|
||||
switch(ds->depth) {
|
||||
case 8:
|
||||
for(i = 0; i < FONT_HEIGHT; i++) {
|
||||
font_data = *font_ptr++;
|
||||
((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
|
||||
d += linesize;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
case 15:
|
||||
for(i = 0; i < FONT_HEIGHT; i++) {
|
||||
font_data = *font_ptr++;
|
||||
((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
|
||||
d += linesize;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
for(i = 0; i < FONT_HEIGHT; i++) {
|
||||
font_data = *font_ptr++;
|
||||
((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
|
||||
d += linesize;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void text_console_resize(TextConsole *s)
|
||||
{
|
||||
TextCell *cells, *c, *c1;
|
||||
int w1, x, y, last_width;
|
||||
|
||||
last_width = s->width;
|
||||
s->width = s->g_width / FONT_WIDTH;
|
||||
s->height = s->g_height / FONT_HEIGHT;
|
||||
|
||||
w1 = last_width;
|
||||
if (s->width < w1)
|
||||
w1 = s->width;
|
||||
|
||||
cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
|
||||
for(y = 0; y < s->total_height; y++) {
|
||||
c = &cells[y * s->width];
|
||||
if (w1 > 0) {
|
||||
c1 = &s->cells[y * last_width];
|
||||
for(x = 0; x < w1; x++) {
|
||||
*c++ = *c1++;
|
||||
}
|
||||
}
|
||||
for(x = w1; x < s->width; x++) {
|
||||
c->ch = ' ';
|
||||
c->fgcol = 7;
|
||||
c->bgcol = 0;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
free(s->cells);
|
||||
s->cells = cells;
|
||||
}
|
||||
|
||||
static void update_xy(TextConsole *s, int x, int y)
|
||||
{
|
||||
TextCell *c;
|
||||
int y1, y2;
|
||||
|
||||
if (s == active_console) {
|
||||
y1 = (s->y_base + y) % s->total_height;
|
||||
y2 = y1 - s->y_displayed;
|
||||
if (y2 < 0)
|
||||
y2 += s->total_height;
|
||||
if (y2 < s->height) {
|
||||
c = &s->cells[y1 * s->width + x];
|
||||
vga_putcharxy(s->ds, x, y2, c->ch,
|
||||
color_table[c->fgcol], color_table[c->bgcol]);
|
||||
dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
|
||||
FONT_WIDTH, FONT_HEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void console_show_cursor(TextConsole *s, int show)
|
||||
{
|
||||
TextCell *c;
|
||||
int y, y1;
|
||||
|
||||
if (s == active_console) {
|
||||
y1 = (s->y_base + s->y) % s->total_height;
|
||||
y = y1 - s->y_displayed;
|
||||
if (y < 0)
|
||||
y += s->total_height;
|
||||
if (y < s->height) {
|
||||
c = &s->cells[y1 * s->width + s->x];
|
||||
if (show) {
|
||||
vga_putcharxy(s->ds, s->x, y, c->ch,
|
||||
color_table[0], color_table[7]);
|
||||
} else {
|
||||
vga_putcharxy(s->ds, s->x, y, c->ch,
|
||||
color_table[c->fgcol], color_table[c->bgcol]);
|
||||
}
|
||||
dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT,
|
||||
FONT_WIDTH, FONT_HEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void console_refresh(TextConsole *s)
|
||||
{
|
||||
TextCell *c;
|
||||
int x, y, y1;
|
||||
|
||||
if (s != active_console)
|
||||
return;
|
||||
|
||||
vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
|
||||
color_table[0]);
|
||||
y1 = s->y_displayed;
|
||||
for(y = 0; y < s->height; y++) {
|
||||
c = s->cells + y1 * s->width;
|
||||
for(x = 0; x < s->width; x++) {
|
||||
vga_putcharxy(s->ds, x, y, c->ch,
|
||||
color_table[c->fgcol], color_table[c->bgcol]);
|
||||
c++;
|
||||
}
|
||||
if (++y1 == s->total_height)
|
||||
y1 = 0;
|
||||
}
|
||||
dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
|
||||
console_show_cursor(s, 1);
|
||||
}
|
||||
|
||||
static void console_scroll(int ydelta)
|
||||
{
|
||||
TextConsole *s;
|
||||
int i, y1;
|
||||
|
||||
s = active_console;
|
||||
if (!s || !s->text_console)
|
||||
return;
|
||||
|
||||
if (ydelta > 0) {
|
||||
for(i = 0; i < ydelta; i++) {
|
||||
if (s->y_displayed == s->y_base)
|
||||
break;
|
||||
if (++s->y_displayed == s->total_height)
|
||||
s->y_displayed = 0;
|
||||
}
|
||||
} else {
|
||||
ydelta = -ydelta;
|
||||
i = s->backscroll_height;
|
||||
if (i > s->total_height - s->height)
|
||||
i = s->total_height - s->height;
|
||||
y1 = s->y_base - i;
|
||||
if (y1 < 0)
|
||||
y1 += s->total_height;
|
||||
for(i = 0; i < ydelta; i++) {
|
||||
if (s->y_displayed == y1)
|
||||
break;
|
||||
if (--s->y_displayed < 0)
|
||||
s->y_displayed = s->total_height - 1;
|
||||
}
|
||||
}
|
||||
console_refresh(s);
|
||||
}
|
||||
|
||||
static void console_put_lf(TextConsole *s)
|
||||
{
|
||||
TextCell *c;
|
||||
int x, y1;
|
||||
|
||||
s->x = 0;
|
||||
s->y++;
|
||||
if (s->y >= s->height) {
|
||||
s->y = s->height - 1;
|
||||
|
||||
if (s->y_displayed == s->y_base) {
|
||||
if (++s->y_displayed == s->total_height)
|
||||
s->y_displayed = 0;
|
||||
}
|
||||
if (++s->y_base == s->total_height)
|
||||
s->y_base = 0;
|
||||
if (s->backscroll_height < s->total_height)
|
||||
s->backscroll_height++;
|
||||
y1 = (s->y_base + s->height - 1) % s->total_height;
|
||||
c = &s->cells[y1 * s->width];
|
||||
for(x = 0; x < s->width; x++) {
|
||||
c->ch = ' ';
|
||||
c->fgcol = s->fgcol;
|
||||
c->bgcol = s->bgcol;
|
||||
c++;
|
||||
}
|
||||
if (s == active_console && s->y_displayed == s->y_base) {
|
||||
vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
|
||||
s->width * FONT_WIDTH,
|
||||
(s->height - 1) * FONT_HEIGHT);
|
||||
vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
|
||||
s->width * FONT_WIDTH, FONT_HEIGHT,
|
||||
color_table[s->bgcol]);
|
||||
dpy_update(s->ds, 0, 0,
|
||||
s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void console_putchar(TextConsole *s, int ch)
|
||||
{
|
||||
TextCell *c;
|
||||
int y1, i, x;
|
||||
|
||||
switch(s->state) {
|
||||
case TTY_STATE_NORM:
|
||||
switch(ch) {
|
||||
case '\r':
|
||||
s->x = 0;
|
||||
break;
|
||||
case '\n':
|
||||
console_put_lf(s);
|
||||
break;
|
||||
case 27:
|
||||
s->state = TTY_STATE_ESC;
|
||||
break;
|
||||
default:
|
||||
y1 = (s->y_base + s->y) % s->total_height;
|
||||
c = &s->cells[y1 * s->width + s->x];
|
||||
c->ch = ch;
|
||||
c->fgcol = s->fgcol;
|
||||
c->bgcol = s->bgcol;
|
||||
update_xy(s, s->x, s->y);
|
||||
s->x++;
|
||||
if (s->x >= s->width)
|
||||
console_put_lf(s);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TTY_STATE_ESC:
|
||||
if (ch == '[') {
|
||||
for(i=0;i<MAX_ESC_PARAMS;i++)
|
||||
s->esc_params[i] = 0;
|
||||
s->nb_esc_params = 0;
|
||||
s->state = TTY_STATE_CSI;
|
||||
} else {
|
||||
s->state = TTY_STATE_NORM;
|
||||
}
|
||||
break;
|
||||
case TTY_STATE_CSI:
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
if (s->nb_esc_params < MAX_ESC_PARAMS) {
|
||||
s->esc_params[s->nb_esc_params] =
|
||||
s->esc_params[s->nb_esc_params] * 10 + ch - '0';
|
||||
}
|
||||
} else {
|
||||
s->nb_esc_params++;
|
||||
if (ch == ';')
|
||||
break;
|
||||
s->state = TTY_STATE_NORM;
|
||||
switch(ch) {
|
||||
case 'D':
|
||||
if (s->x > 0)
|
||||
s->x--;
|
||||
break;
|
||||
case 'C':
|
||||
if (s->x < (s->width - 1))
|
||||
s->x++;
|
||||
break;
|
||||
case 'K':
|
||||
/* clear to eol */
|
||||
y1 = (s->y_base + s->y) % s->total_height;
|
||||
for(x = s->x; x < s->width; x++) {
|
||||
c = &s->cells[y1 * s->width + x];
|
||||
c->ch = ' ';
|
||||
c->fgcol = s->fgcol;
|
||||
c->bgcol = s->bgcol;
|
||||
c++;
|
||||
update_xy(s, x, s->y);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void console_select(unsigned int index)
|
||||
{
|
||||
TextConsole *s;
|
||||
|
||||
if (index >= MAX_CONSOLES)
|
||||
return;
|
||||
s = consoles[index];
|
||||
if (s) {
|
||||
active_console = s;
|
||||
if (s->text_console) {
|
||||
if (s->g_width != s->ds->width ||
|
||||
s->g_height != s->ds->height) {
|
||||
s->g_width = s->ds->width;
|
||||
s->g_height = s->ds->height;
|
||||
text_console_resize(s);
|
||||
}
|
||||
console_refresh(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
TextConsole *s = chr->opaque;
|
||||
int i;
|
||||
|
||||
console_show_cursor(s, 0);
|
||||
for(i = 0; i < len; i++) {
|
||||
console_putchar(s, buf[i]);
|
||||
}
|
||||
console_show_cursor(s, 1);
|
||||
return len;
|
||||
}
|
||||
|
||||
static void console_chr_add_read_handler(CharDriverState *chr,
|
||||
IOCanRWHandler *fd_can_read,
|
||||
IOReadHandler *fd_read, void *opaque)
|
||||
{
|
||||
TextConsole *s = chr->opaque;
|
||||
s->fd_read = fd_read;
|
||||
s->fd_opaque = opaque;
|
||||
}
|
||||
|
||||
static void console_send_event(CharDriverState *chr, int event)
|
||||
{
|
||||
TextConsole *s = chr->opaque;
|
||||
int i;
|
||||
|
||||
if (event == CHR_EVENT_FOCUS) {
|
||||
for(i = 0; i < nb_consoles; i++) {
|
||||
if (consoles[i] == s) {
|
||||
console_select(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* called when an ascii key is pressed */
|
||||
void kbd_put_keysym(int keysym)
|
||||
{
|
||||
TextConsole *s;
|
||||
uint8_t buf[16], *q;
|
||||
int c;
|
||||
|
||||
s = active_console;
|
||||
if (!s || !s->text_console)
|
||||
return;
|
||||
|
||||
switch(keysym) {
|
||||
case QEMU_KEY_CTRL_UP:
|
||||
console_scroll(-1);
|
||||
break;
|
||||
case QEMU_KEY_CTRL_DOWN:
|
||||
console_scroll(1);
|
||||
break;
|
||||
case QEMU_KEY_CTRL_PAGEUP:
|
||||
console_scroll(-10);
|
||||
break;
|
||||
case QEMU_KEY_CTRL_PAGEDOWN:
|
||||
console_scroll(10);
|
||||
break;
|
||||
default:
|
||||
if (s->fd_read) {
|
||||
/* convert the QEMU keysym to VT100 key string */
|
||||
q = buf;
|
||||
if (keysym >= 0xe100 && keysym <= 0xe11f) {
|
||||
*q++ = '\033';
|
||||
*q++ = '[';
|
||||
c = keysym - 0xe100;
|
||||
if (c >= 10)
|
||||
*q++ = '0' + (c / 10);
|
||||
*q++ = '0' + (c % 10);
|
||||
*q++ = '~';
|
||||
} else if (keysym >= 0xe120 && keysym <= 0xe17f) {
|
||||
*q++ = '\033';
|
||||
*q++ = '[';
|
||||
*q++ = keysym & 0xff;
|
||||
} else {
|
||||
*q++ = keysym;
|
||||
}
|
||||
s->fd_read(s->fd_opaque, buf, q - buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TextConsole *graphic_console_init(DisplayState *ds)
|
||||
{
|
||||
TextConsole *s;
|
||||
|
||||
if (nb_consoles >= MAX_CONSOLES)
|
||||
return NULL;
|
||||
s = qemu_mallocz(sizeof(TextConsole));
|
||||
if (!s) {
|
||||
return NULL;
|
||||
}
|
||||
if (!active_console)
|
||||
active_console = s;
|
||||
s->ds = ds;
|
||||
consoles[nb_consoles++] = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
int is_active_console(TextConsole *s)
|
||||
{
|
||||
return s == active_console;
|
||||
}
|
||||
|
||||
CharDriverState *text_console_init(DisplayState *ds)
|
||||
{
|
||||
CharDriverState *chr;
|
||||
TextConsole *s;
|
||||
int i;
|
||||
static int color_inited;
|
||||
|
||||
chr = qemu_mallocz(sizeof(CharDriverState));
|
||||
if (!chr)
|
||||
return NULL;
|
||||
s = graphic_console_init(ds);
|
||||
if (!s) {
|
||||
free(chr);
|
||||
return NULL;
|
||||
}
|
||||
s->text_console = 1;
|
||||
chr->opaque = s;
|
||||
chr->chr_write = console_puts;
|
||||
chr->chr_add_read_handler = console_chr_add_read_handler;
|
||||
chr->chr_send_event = console_send_event;
|
||||
|
||||
if (!color_inited) {
|
||||
color_inited = 1;
|
||||
for(i = 0; i < 8; i++) {
|
||||
color_table[i] = col_expand(s->ds,
|
||||
vga_get_color(s->ds, color_table_rgb[i]));
|
||||
}
|
||||
}
|
||||
s->y_displayed = 0;
|
||||
s->y_base = 0;
|
||||
s->total_height = DEFAULT_BACKSCROLL;
|
||||
s->x = 0;
|
||||
s->y = 0;
|
||||
s->fgcol = 7;
|
||||
s->bgcol = 0;
|
||||
s->g_width = s->ds->width;
|
||||
s->g_height = s->ds->height;
|
||||
text_console_resize(s);
|
||||
|
||||
return chr;
|
||||
}
|
||||
@@ -0,0 +1,764 @@
|
||||
/*
|
||||
* defines common to all virtual CPUs
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef CPU_ALL_H
|
||||
#define CPU_ALL_H
|
||||
|
||||
#if defined(__arm__) || defined(__sparc__)
|
||||
#define WORDS_ALIGNED
|
||||
#endif
|
||||
|
||||
/* some important defines:
|
||||
*
|
||||
* WORDS_ALIGNED : if defined, the host cpu can only make word aligned
|
||||
* memory accesses.
|
||||
*
|
||||
* WORDS_BIGENDIAN : if defined, the host cpu is big endian and
|
||||
* otherwise little endian.
|
||||
*
|
||||
* (TARGET_WORDS_ALIGNED : same for target cpu (not supported yet))
|
||||
*
|
||||
* TARGET_WORDS_BIGENDIAN : same for target cpu
|
||||
*/
|
||||
|
||||
#include "bswap.h"
|
||||
|
||||
#if defined(WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
|
||||
#define BSWAP_NEEDED
|
||||
#endif
|
||||
|
||||
#ifdef BSWAP_NEEDED
|
||||
|
||||
static inline uint16_t tswap16(uint16_t s)
|
||||
{
|
||||
return bswap16(s);
|
||||
}
|
||||
|
||||
static inline uint32_t tswap32(uint32_t s)
|
||||
{
|
||||
return bswap32(s);
|
||||
}
|
||||
|
||||
static inline uint64_t tswap64(uint64_t s)
|
||||
{
|
||||
return bswap64(s);
|
||||
}
|
||||
|
||||
static inline void tswap16s(uint16_t *s)
|
||||
{
|
||||
*s = bswap16(*s);
|
||||
}
|
||||
|
||||
static inline void tswap32s(uint32_t *s)
|
||||
{
|
||||
*s = bswap32(*s);
|
||||
}
|
||||
|
||||
static inline void tswap64s(uint64_t *s)
|
||||
{
|
||||
*s = bswap64(*s);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline uint16_t tswap16(uint16_t s)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline uint32_t tswap32(uint32_t s)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline uint64_t tswap64(uint64_t s)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline void tswap16s(uint16_t *s)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void tswap32s(uint32_t *s)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void tswap64s(uint64_t *s)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if TARGET_LONG_SIZE == 4
|
||||
#define tswapl(s) tswap32(s)
|
||||
#define tswapls(s) tswap32s((uint32_t *)(s))
|
||||
#define bswaptls(s) bswap32s(s)
|
||||
#else
|
||||
#define tswapl(s) tswap64(s)
|
||||
#define tswapls(s) tswap64s((uint64_t *)(s))
|
||||
#define bswaptls(s) bswap64s(s)
|
||||
#endif
|
||||
|
||||
/* NOTE: arm FPA is horrible as double 32 bit words are stored in big
|
||||
endian ! */
|
||||
typedef union {
|
||||
float64 d;
|
||||
#if defined(WORDS_BIGENDIAN) \
|
||||
|| (defined(__arm__) && !defined(__VFP_FP__) && !defined(CONFIG_SOFTFLOAT))
|
||||
struct {
|
||||
uint32_t upper;
|
||||
uint32_t lower;
|
||||
} l;
|
||||
#else
|
||||
struct {
|
||||
uint32_t lower;
|
||||
uint32_t upper;
|
||||
} l;
|
||||
#endif
|
||||
uint64_t ll;
|
||||
} CPU_DoubleU;
|
||||
|
||||
/* CPU memory access without any memory or io remapping */
|
||||
|
||||
/*
|
||||
* the generic syntax for the memory accesses is:
|
||||
*
|
||||
* load: ld{type}{sign}{size}{endian}_{access_type}(ptr)
|
||||
*
|
||||
* store: st{type}{size}{endian}_{access_type}(ptr, val)
|
||||
*
|
||||
* type is:
|
||||
* (empty): integer access
|
||||
* f : float access
|
||||
*
|
||||
* sign is:
|
||||
* (empty): for floats or 32 bit size
|
||||
* u : unsigned
|
||||
* s : signed
|
||||
*
|
||||
* size is:
|
||||
* b: 8 bits
|
||||
* w: 16 bits
|
||||
* l: 32 bits
|
||||
* q: 64 bits
|
||||
*
|
||||
* endian is:
|
||||
* (empty): target cpu endianness or 8 bit access
|
||||
* r : reversed target cpu endianness (not implemented yet)
|
||||
* be : big endian (not implemented yet)
|
||||
* le : little endian (not implemented yet)
|
||||
*
|
||||
* access_type is:
|
||||
* raw : host memory access
|
||||
* user : user mode access using soft MMU
|
||||
* kernel : kernel mode access using soft MMU
|
||||
*/
|
||||
static inline int ldub_p(void *ptr)
|
||||
{
|
||||
return *(uint8_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldsb_p(void *ptr)
|
||||
{
|
||||
return *(int8_t *)ptr;
|
||||
}
|
||||
|
||||
static inline void stb_p(void *ptr, int v)
|
||||
{
|
||||
*(uint8_t *)ptr = v;
|
||||
}
|
||||
|
||||
/* NOTE: on arm, putting 2 in /proc/sys/debug/alignment so that the
|
||||
kernel handles unaligned load/stores may give better results, but
|
||||
it is a system wide setting : bad */
|
||||
#if !defined(TARGET_WORDS_BIGENDIAN) && (defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED))
|
||||
|
||||
/* conservative code for little endian unaligned accesses */
|
||||
static inline int lduw_p(void *ptr)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
int val;
|
||||
__asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
|
||||
return val;
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
return p[0] | (p[1] << 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int ldsw_p(void *ptr)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
int val;
|
||||
__asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
|
||||
return (int16_t)val;
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
return (int16_t)(p[0] | (p[1] << 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int ldl_p(void *ptr)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
int val;
|
||||
__asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (ptr));
|
||||
return val;
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t ldq_p(void *ptr)
|
||||
{
|
||||
uint8_t *p = ptr;
|
||||
uint32_t v1, v2;
|
||||
v1 = ldl_p(p);
|
||||
v2 = ldl_p(p + 4);
|
||||
return v1 | ((uint64_t)v2 << 32);
|
||||
}
|
||||
|
||||
static inline void stw_p(void *ptr, int v)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
__asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr));
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
p[0] = v;
|
||||
p[1] = v >> 8;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void stl_p(void *ptr, int v)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
__asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr));
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
p[0] = v;
|
||||
p[1] = v >> 8;
|
||||
p[2] = v >> 16;
|
||||
p[3] = v >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void stq_p(void *ptr, uint64_t v)
|
||||
{
|
||||
uint8_t *p = ptr;
|
||||
stl_p(p, (uint32_t)v);
|
||||
stl_p(p + 4, v >> 32);
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float32 ldfl_p(void *ptr)
|
||||
{
|
||||
union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.i = ldl_p(ptr);
|
||||
return u.f;
|
||||
}
|
||||
|
||||
static inline void stfl_p(void *ptr, float32 v)
|
||||
{
|
||||
union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.f = v;
|
||||
stl_p(ptr, u.i);
|
||||
}
|
||||
|
||||
static inline float64 ldfq_p(void *ptr)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.l.lower = ldl_p(ptr);
|
||||
u.l.upper = ldl_p(ptr + 4);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static inline void stfq_p(void *ptr, float64 v)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.d = v;
|
||||
stl_p(ptr, u.l.lower);
|
||||
stl_p(ptr + 4, u.l.upper);
|
||||
}
|
||||
|
||||
#elif defined(TARGET_WORDS_BIGENDIAN) && (!defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED))
|
||||
|
||||
static inline int lduw_p(void *ptr)
|
||||
{
|
||||
#if defined(__i386__)
|
||||
int val;
|
||||
asm volatile ("movzwl %1, %0\n"
|
||||
"xchgb %b0, %h0\n"
|
||||
: "=q" (val)
|
||||
: "m" (*(uint16_t *)ptr));
|
||||
return val;
|
||||
#else
|
||||
uint8_t *b = (uint8_t *) ptr;
|
||||
return ((b[0] << 8) | b[1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int ldsw_p(void *ptr)
|
||||
{
|
||||
#if defined(__i386__)
|
||||
int val;
|
||||
asm volatile ("movzwl %1, %0\n"
|
||||
"xchgb %b0, %h0\n"
|
||||
: "=q" (val)
|
||||
: "m" (*(uint16_t *)ptr));
|
||||
return (int16_t)val;
|
||||
#else
|
||||
uint8_t *b = (uint8_t *) ptr;
|
||||
return (int16_t)((b[0] << 8) | b[1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int ldl_p(void *ptr)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
int val;
|
||||
asm volatile ("movl %1, %0\n"
|
||||
"bswap %0\n"
|
||||
: "=r" (val)
|
||||
: "m" (*(uint32_t *)ptr));
|
||||
return val;
|
||||
#else
|
||||
uint8_t *b = (uint8_t *) ptr;
|
||||
return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t ldq_p(void *ptr)
|
||||
{
|
||||
uint32_t a,b;
|
||||
a = ldl_p(ptr);
|
||||
b = ldl_p(ptr+4);
|
||||
return (((uint64_t)a<<32)|b);
|
||||
}
|
||||
|
||||
static inline void stw_p(void *ptr, int v)
|
||||
{
|
||||
#if defined(__i386__)
|
||||
asm volatile ("xchgb %b0, %h0\n"
|
||||
"movw %w0, %1\n"
|
||||
: "=q" (v)
|
||||
: "m" (*(uint16_t *)ptr), "0" (v));
|
||||
#else
|
||||
uint8_t *d = (uint8_t *) ptr;
|
||||
d[0] = v >> 8;
|
||||
d[1] = v;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void stl_p(void *ptr, int v)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
asm volatile ("bswap %0\n"
|
||||
"movl %0, %1\n"
|
||||
: "=r" (v)
|
||||
: "m" (*(uint32_t *)ptr), "0" (v));
|
||||
#else
|
||||
uint8_t *d = (uint8_t *) ptr;
|
||||
d[0] = v >> 24;
|
||||
d[1] = v >> 16;
|
||||
d[2] = v >> 8;
|
||||
d[3] = v;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void stq_p(void *ptr, uint64_t v)
|
||||
{
|
||||
stl_p(ptr, v >> 32);
|
||||
stl_p(ptr + 4, v);
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float32 ldfl_p(void *ptr)
|
||||
{
|
||||
union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.i = ldl_p(ptr);
|
||||
return u.f;
|
||||
}
|
||||
|
||||
static inline void stfl_p(void *ptr, float32 v)
|
||||
{
|
||||
union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.f = v;
|
||||
stl_p(ptr, u.i);
|
||||
}
|
||||
|
||||
static inline float64 ldfq_p(void *ptr)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.l.upper = ldl_p(ptr);
|
||||
u.l.lower = ldl_p(ptr + 4);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static inline void stfq_p(void *ptr, float64 v)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.d = v;
|
||||
stl_p(ptr, u.l.upper);
|
||||
stl_p(ptr + 4, u.l.lower);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int lduw_p(void *ptr)
|
||||
{
|
||||
return *(uint16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldsw_p(void *ptr)
|
||||
{
|
||||
return *(int16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldl_p(void *ptr)
|
||||
{
|
||||
return *(uint32_t *)ptr;
|
||||
}
|
||||
|
||||
static inline uint64_t ldq_p(void *ptr)
|
||||
{
|
||||
return *(uint64_t *)ptr;
|
||||
}
|
||||
|
||||
static inline void stw_p(void *ptr, int v)
|
||||
{
|
||||
*(uint16_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stl_p(void *ptr, int v)
|
||||
{
|
||||
*(uint32_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stq_p(void *ptr, uint64_t v)
|
||||
{
|
||||
*(uint64_t *)ptr = v;
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float32 ldfl_p(void *ptr)
|
||||
{
|
||||
return *(float32 *)ptr;
|
||||
}
|
||||
|
||||
static inline float64 ldfq_p(void *ptr)
|
||||
{
|
||||
return *(float64 *)ptr;
|
||||
}
|
||||
|
||||
static inline void stfl_p(void *ptr, float32 v)
|
||||
{
|
||||
*(float32 *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stfq_p(void *ptr, float64 v)
|
||||
{
|
||||
*(float64 *)ptr = v;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* MMU memory access macros */
|
||||
|
||||
/* NOTE: we use double casts if pointers and target_ulong have
|
||||
different sizes */
|
||||
#define ldub_raw(p) ldub_p((uint8_t *)(long)(p))
|
||||
#define ldsb_raw(p) ldsb_p((uint8_t *)(long)(p))
|
||||
#define lduw_raw(p) lduw_p((uint8_t *)(long)(p))
|
||||
#define ldsw_raw(p) ldsw_p((uint8_t *)(long)(p))
|
||||
#define ldl_raw(p) ldl_p((uint8_t *)(long)(p))
|
||||
#define ldq_raw(p) ldq_p((uint8_t *)(long)(p))
|
||||
#define ldfl_raw(p) ldfl_p((uint8_t *)(long)(p))
|
||||
#define ldfq_raw(p) ldfq_p((uint8_t *)(long)(p))
|
||||
#define stb_raw(p, v) stb_p((uint8_t *)(long)(p), v)
|
||||
#define stw_raw(p, v) stw_p((uint8_t *)(long)(p), v)
|
||||
#define stl_raw(p, v) stl_p((uint8_t *)(long)(p), v)
|
||||
#define stq_raw(p, v) stq_p((uint8_t *)(long)(p), v)
|
||||
#define stfl_raw(p, v) stfl_p((uint8_t *)(long)(p), v)
|
||||
#define stfq_raw(p, v) stfq_p((uint8_t *)(long)(p), v)
|
||||
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
|
||||
/* if user mode, no other memory access functions */
|
||||
#define ldub(p) ldub_raw(p)
|
||||
#define ldsb(p) ldsb_raw(p)
|
||||
#define lduw(p) lduw_raw(p)
|
||||
#define ldsw(p) ldsw_raw(p)
|
||||
#define ldl(p) ldl_raw(p)
|
||||
#define ldq(p) ldq_raw(p)
|
||||
#define ldfl(p) ldfl_raw(p)
|
||||
#define ldfq(p) ldfq_raw(p)
|
||||
#define stb(p, v) stb_raw(p, v)
|
||||
#define stw(p, v) stw_raw(p, v)
|
||||
#define stl(p, v) stl_raw(p, v)
|
||||
#define stq(p, v) stq_raw(p, v)
|
||||
#define stfl(p, v) stfl_raw(p, v)
|
||||
#define stfq(p, v) stfq_raw(p, v)
|
||||
|
||||
#define ldub_code(p) ldub_raw(p)
|
||||
#define ldsb_code(p) ldsb_raw(p)
|
||||
#define lduw_code(p) lduw_raw(p)
|
||||
#define ldsw_code(p) ldsw_raw(p)
|
||||
#define ldl_code(p) ldl_raw(p)
|
||||
|
||||
#define ldub_kernel(p) ldub_raw(p)
|
||||
#define ldsb_kernel(p) ldsb_raw(p)
|
||||
#define lduw_kernel(p) lduw_raw(p)
|
||||
#define ldsw_kernel(p) ldsw_raw(p)
|
||||
#define ldl_kernel(p) ldl_raw(p)
|
||||
#define ldfl_kernel(p) ldfl_raw(p)
|
||||
#define ldfq_kernel(p) ldfq_raw(p)
|
||||
#define stb_kernel(p, v) stb_raw(p, v)
|
||||
#define stw_kernel(p, v) stw_raw(p, v)
|
||||
#define stl_kernel(p, v) stl_raw(p, v)
|
||||
#define stq_kernel(p, v) stq_raw(p, v)
|
||||
#define stfl_kernel(p, v) stfl_raw(p, v)
|
||||
#define stfq_kernel(p, vt) stfq_raw(p, v)
|
||||
|
||||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
|
||||
/* page related stuff */
|
||||
|
||||
#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS)
|
||||
#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
|
||||
#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
|
||||
|
||||
extern unsigned long qemu_real_host_page_size;
|
||||
extern unsigned long qemu_host_page_bits;
|
||||
extern unsigned long qemu_host_page_size;
|
||||
extern unsigned long qemu_host_page_mask;
|
||||
|
||||
#define HOST_PAGE_ALIGN(addr) (((addr) + qemu_host_page_size - 1) & qemu_host_page_mask)
|
||||
|
||||
/* same as PROT_xxx */
|
||||
#define PAGE_READ 0x0001
|
||||
#define PAGE_WRITE 0x0002
|
||||
#define PAGE_EXEC 0x0004
|
||||
#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC)
|
||||
#define PAGE_VALID 0x0008
|
||||
/* original state of the write flag (used when tracking self-modifying
|
||||
code */
|
||||
#define PAGE_WRITE_ORG 0x0010
|
||||
|
||||
void page_dump(FILE *f);
|
||||
int page_get_flags(unsigned long address);
|
||||
void page_set_flags(unsigned long start, unsigned long end, int flags);
|
||||
void page_unprotect_range(uint8_t *data, unsigned long data_size);
|
||||
|
||||
#define SINGLE_CPU_DEFINES
|
||||
#ifdef SINGLE_CPU_DEFINES
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
|
||||
#define CPUState CPUX86State
|
||||
#define cpu_init cpu_x86_init
|
||||
#define cpu_exec cpu_x86_exec
|
||||
#define cpu_gen_code cpu_x86_gen_code
|
||||
#define cpu_signal_handler cpu_x86_signal_handler
|
||||
|
||||
#elif defined(TARGET_ARM)
|
||||
|
||||
#define CPUState CPUARMState
|
||||
#define cpu_init cpu_arm_init
|
||||
#define cpu_exec cpu_arm_exec
|
||||
#define cpu_gen_code cpu_arm_gen_code
|
||||
#define cpu_signal_handler cpu_arm_signal_handler
|
||||
|
||||
#elif defined(TARGET_SPARC)
|
||||
|
||||
#define CPUState CPUSPARCState
|
||||
#define cpu_init cpu_sparc_init
|
||||
#define cpu_exec cpu_sparc_exec
|
||||
#define cpu_gen_code cpu_sparc_gen_code
|
||||
#define cpu_signal_handler cpu_sparc_signal_handler
|
||||
|
||||
#elif defined(TARGET_PPC)
|
||||
|
||||
#define CPUState CPUPPCState
|
||||
#define cpu_init cpu_ppc_init
|
||||
#define cpu_exec cpu_ppc_exec
|
||||
#define cpu_gen_code cpu_ppc_gen_code
|
||||
#define cpu_signal_handler cpu_ppc_signal_handler
|
||||
|
||||
#else
|
||||
|
||||
#error unsupported target CPU
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* SINGLE_CPU_DEFINES */
|
||||
|
||||
void cpu_dump_state(CPUState *env, FILE *f,
|
||||
int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
|
||||
int flags);
|
||||
|
||||
void cpu_abort(CPUState *env, const char *fmt, ...);
|
||||
extern CPUState *cpu_single_env;
|
||||
extern int code_copy_enabled;
|
||||
|
||||
#define CPU_INTERRUPT_EXIT 0x01 /* wants exit from main loop */
|
||||
#define CPU_INTERRUPT_HARD 0x02 /* hardware interrupt pending */
|
||||
#define CPU_INTERRUPT_EXITTB 0x04 /* exit the current TB (use for x86 a20 case) */
|
||||
#define CPU_INTERRUPT_TIMER 0x08 /* internal timer exception pending */
|
||||
void cpu_interrupt(CPUState *s, int mask);
|
||||
void cpu_reset_interrupt(CPUState *env, int mask);
|
||||
|
||||
int cpu_breakpoint_insert(CPUState *env, target_ulong pc);
|
||||
int cpu_breakpoint_remove(CPUState *env, target_ulong pc);
|
||||
void cpu_single_step(CPUState *env, int enabled);
|
||||
void cpu_reset(CPUState *s);
|
||||
|
||||
/* Return the physical page corresponding to a virtual one. Use it
|
||||
only for debugging because no protection checks are done. Return -1
|
||||
if no page found. */
|
||||
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr);
|
||||
|
||||
#define CPU_LOG_TB_OUT_ASM (1 << 0)
|
||||
#define CPU_LOG_TB_IN_ASM (1 << 1)
|
||||
#define CPU_LOG_TB_OP (1 << 2)
|
||||
#define CPU_LOG_TB_OP_OPT (1 << 3)
|
||||
#define CPU_LOG_INT (1 << 4)
|
||||
#define CPU_LOG_EXEC (1 << 5)
|
||||
#define CPU_LOG_PCALL (1 << 6)
|
||||
#define CPU_LOG_IOPORT (1 << 7)
|
||||
#define CPU_LOG_TB_CPU (1 << 8)
|
||||
|
||||
/* define log items */
|
||||
typedef struct CPULogItem {
|
||||
int mask;
|
||||
const char *name;
|
||||
const char *help;
|
||||
} CPULogItem;
|
||||
|
||||
extern CPULogItem cpu_log_items[];
|
||||
|
||||
void cpu_set_log(int log_flags);
|
||||
void cpu_set_log_filename(const char *filename);
|
||||
int cpu_str_to_log_mask(const char *str);
|
||||
|
||||
/* IO ports API */
|
||||
|
||||
/* NOTE: as these functions may be even used when there is an isa
|
||||
brige on non x86 targets, we always defined them */
|
||||
#ifndef NO_CPU_IO_DEFS
|
||||
void cpu_outb(CPUState *env, int addr, int val);
|
||||
void cpu_outw(CPUState *env, int addr, int val);
|
||||
void cpu_outl(CPUState *env, int addr, int val);
|
||||
int cpu_inb(CPUState *env, int addr);
|
||||
int cpu_inw(CPUState *env, int addr);
|
||||
int cpu_inl(CPUState *env, int addr);
|
||||
#endif
|
||||
|
||||
/* memory API */
|
||||
|
||||
extern int phys_ram_size;
|
||||
extern int phys_ram_fd;
|
||||
extern uint8_t *phys_ram_base;
|
||||
extern uint8_t *phys_ram_dirty;
|
||||
|
||||
/* physical memory access */
|
||||
#define IO_MEM_NB_ENTRIES 256
|
||||
#define TLB_INVALID_MASK (1 << 3)
|
||||
#define IO_MEM_SHIFT 4
|
||||
|
||||
#define IO_MEM_RAM (0 << IO_MEM_SHIFT) /* hardcoded offset */
|
||||
#define IO_MEM_ROM (1 << IO_MEM_SHIFT) /* hardcoded offset */
|
||||
#define IO_MEM_UNASSIGNED (2 << IO_MEM_SHIFT)
|
||||
#define IO_MEM_CODE (3 << IO_MEM_SHIFT) /* used internally, never use directly */
|
||||
#define IO_MEM_NOTDIRTY (4 << IO_MEM_SHIFT) /* used internally, never use directly */
|
||||
|
||||
typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value);
|
||||
typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr);
|
||||
|
||||
void cpu_register_physical_memory(target_phys_addr_t start_addr,
|
||||
unsigned long size,
|
||||
unsigned long phys_offset);
|
||||
int cpu_register_io_memory(int io_index,
|
||||
CPUReadMemoryFunc **mem_read,
|
||||
CPUWriteMemoryFunc **mem_write,
|
||||
void *opaque);
|
||||
CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index);
|
||||
CPUReadMemoryFunc **cpu_get_io_memory_read(int io_index);
|
||||
|
||||
void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
|
||||
int len, int is_write);
|
||||
static inline void cpu_physical_memory_read(target_phys_addr_t addr,
|
||||
uint8_t *buf, int len)
|
||||
{
|
||||
cpu_physical_memory_rw(addr, buf, len, 0);
|
||||
}
|
||||
static inline void cpu_physical_memory_write(target_phys_addr_t addr,
|
||||
const uint8_t *buf, int len)
|
||||
{
|
||||
cpu_physical_memory_rw(addr, (uint8_t *)buf, len, 1);
|
||||
}
|
||||
uint32_t ldl_phys(target_phys_addr_t addr);
|
||||
void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val);
|
||||
void stl_phys(target_phys_addr_t addr, uint32_t val);
|
||||
|
||||
int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
|
||||
uint8_t *buf, int len, int is_write);
|
||||
|
||||
#define VGA_DIRTY_FLAG 0x01
|
||||
|
||||
/* read dirty bit (return 0 or 1) */
|
||||
static inline int cpu_physical_memory_is_dirty(target_ulong addr)
|
||||
{
|
||||
return phys_ram_dirty[addr >> TARGET_PAGE_BITS] == 0xff;
|
||||
}
|
||||
|
||||
static inline int cpu_physical_memory_get_dirty(target_ulong addr,
|
||||
int dirty_flags)
|
||||
{
|
||||
return phys_ram_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags;
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_set_dirty(target_ulong addr)
|
||||
{
|
||||
phys_ram_dirty[addr >> TARGET_PAGE_BITS] = 0xff;
|
||||
}
|
||||
|
||||
void cpu_physical_memory_reset_dirty(target_ulong start, target_ulong end,
|
||||
int dirty_flags);
|
||||
|
||||
void dump_exec_info(FILE *f,
|
||||
int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
|
||||
|
||||
#endif /* CPU_ALL_H */
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* common defines for all CPUs
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef CPU_DEFS_H
|
||||
#define CPU_DEFS_H
|
||||
|
||||
#include "config.h"
|
||||
#include <setjmp.h>
|
||||
#include <inttypes.h>
|
||||
#include "osdep.h"
|
||||
|
||||
#ifndef TARGET_LONG_BITS
|
||||
#error TARGET_LONG_BITS must be defined before including this header
|
||||
#endif
|
||||
|
||||
#if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__)
|
||||
#define HOST_LONG_BITS 64
|
||||
#else
|
||||
#define HOST_LONG_BITS 32
|
||||
#endif
|
||||
|
||||
#ifndef TARGET_PHYS_ADDR_BITS
|
||||
#if TARGET_LONG_BITS >= HOST_LONG_BITS
|
||||
#define TARGET_PHYS_ADDR_BITS TARGET_LONG_BITS
|
||||
#else
|
||||
#define TARGET_PHYS_ADDR_BITS HOST_LONG_BITS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8)
|
||||
|
||||
/* target_ulong is the type of a virtual address */
|
||||
#if TARGET_LONG_SIZE == 4
|
||||
typedef int32_t target_long;
|
||||
typedef uint32_t target_ulong;
|
||||
#define TARGET_FMT_lx "%08x"
|
||||
#elif TARGET_LONG_SIZE == 8
|
||||
typedef int64_t target_long;
|
||||
typedef uint64_t target_ulong;
|
||||
#define TARGET_FMT_lx "%016llx"
|
||||
#else
|
||||
#error TARGET_LONG_SIZE undefined
|
||||
#endif
|
||||
|
||||
/* target_phys_addr_t is the type of a physical address (its size can
|
||||
be different from 'target_ulong'). We have sizeof(target_phys_addr)
|
||||
= max(sizeof(unsigned long),
|
||||
sizeof(size_of_target_physical_address)) because we must pass a
|
||||
host pointer to memory operations in some cases */
|
||||
|
||||
#if TARGET_PHYS_ADDR_BITS == 32
|
||||
typedef uint32_t target_phys_addr_t;
|
||||
#elif TARGET_PHYS_ADDR_BITS == 64
|
||||
typedef uint64_t target_phys_addr_t;
|
||||
#else
|
||||
#error TARGET_PHYS_ADDR_BITS undefined
|
||||
#endif
|
||||
|
||||
#define HOST_LONG_SIZE (HOST_LONG_BITS / 8)
|
||||
|
||||
#define EXCP_INTERRUPT 256 /* async interruption */
|
||||
#define EXCP_HLT 257 /* hlt instruction reached */
|
||||
#define EXCP_DEBUG 258 /* cpu stopped after a breakpoint or singlestep */
|
||||
|
||||
#define MAX_BREAKPOINTS 32
|
||||
|
||||
#define CPU_TLB_SIZE 256
|
||||
|
||||
typedef struct CPUTLBEntry {
|
||||
/* bit 31 to TARGET_PAGE_BITS : virtual address
|
||||
bit TARGET_PAGE_BITS-1..IO_MEM_SHIFT : if non zero, memory io
|
||||
zone number
|
||||
bit 3 : indicates that the entry is invalid
|
||||
bit 2..0 : zero
|
||||
*/
|
||||
target_ulong address;
|
||||
/* addend to virtual address to get physical address */
|
||||
target_phys_addr_t addend;
|
||||
} CPUTLBEntry;
|
||||
|
||||
#endif
|
||||
+1187
File diff suppressed because it is too large.
Load diff
-434
@@ -1,434 +0,0 @@
|
||||
/*
|
||||
* i386 virtual CPU header
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef CPU_I386_H
|
||||
#define CPU_I386_H
|
||||
|
||||
#include "config.h"
|
||||
#include <setjmp.h>
|
||||
|
||||
#define R_EAX 0
|
||||
#define R_ECX 1
|
||||
#define R_EDX 2
|
||||
#define R_EBX 3
|
||||
#define R_ESP 4
|
||||
#define R_EBP 5
|
||||
#define R_ESI 6
|
||||
#define R_EDI 7
|
||||
|
||||
#define R_AL 0
|
||||
#define R_CL 1
|
||||
#define R_DL 2
|
||||
#define R_BL 3
|
||||
#define R_AH 4
|
||||
#define R_CH 5
|
||||
#define R_DH 6
|
||||
#define R_BH 7
|
||||
|
||||
#define R_ES 0
|
||||
#define R_CS 1
|
||||
#define R_SS 2
|
||||
#define R_DS 3
|
||||
#define R_FS 4
|
||||
#define R_GS 5
|
||||
|
||||
/* eflags masks */
|
||||
#define CC_C 0x0001
|
||||
#define CC_P 0x0004
|
||||
#define CC_A 0x0010
|
||||
#define CC_Z 0x0040
|
||||
#define CC_S 0x0080
|
||||
#define CC_O 0x0800
|
||||
|
||||
#define TF_MASK 0x00000100
|
||||
#define IF_MASK 0x00000200
|
||||
#define DF_MASK 0x00000400
|
||||
#define IOPL_MASK 0x00003000
|
||||
#define NT_MASK 0x00004000
|
||||
#define RF_MASK 0x00010000
|
||||
#define VM_MASK 0x00020000
|
||||
#define AC_MASK 0x00040000
|
||||
#define VIF_MASK 0x00080000
|
||||
#define VIP_MASK 0x00100000
|
||||
#define ID_MASK 0x00200000
|
||||
|
||||
#define EXCP00_DIVZ 1
|
||||
#define EXCP01_SSTP 2
|
||||
#define EXCP02_NMI 3
|
||||
#define EXCP03_INT3 4
|
||||
#define EXCP04_INTO 5
|
||||
#define EXCP05_BOUND 6
|
||||
#define EXCP06_ILLOP 7
|
||||
#define EXCP07_PREX 8
|
||||
#define EXCP08_DBLE 9
|
||||
#define EXCP09_XERR 10
|
||||
#define EXCP0A_TSS 11
|
||||
#define EXCP0B_NOSEG 12
|
||||
#define EXCP0C_STACK 13
|
||||
#define EXCP0D_GPF 14
|
||||
#define EXCP0E_PAGE 15
|
||||
#define EXCP10_COPR 17
|
||||
#define EXCP11_ALGN 18
|
||||
#define EXCP12_MCHK 19
|
||||
|
||||
#define EXCP_INTERRUPT 256 /* async interruption */
|
||||
|
||||
enum {
|
||||
CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
|
||||
CC_OP_EFLAGS, /* all cc are explicitely computed, CC_SRC = flags */
|
||||
CC_OP_MUL, /* modify all flags, C, O = (CC_SRC != 0) */
|
||||
|
||||
CC_OP_ADDB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
|
||||
CC_OP_ADDW,
|
||||
CC_OP_ADDL,
|
||||
|
||||
CC_OP_ADCB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
|
||||
CC_OP_ADCW,
|
||||
CC_OP_ADCL,
|
||||
|
||||
CC_OP_SUBB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
|
||||
CC_OP_SUBW,
|
||||
CC_OP_SUBL,
|
||||
|
||||
CC_OP_SBBB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
|
||||
CC_OP_SBBW,
|
||||
CC_OP_SBBL,
|
||||
|
||||
CC_OP_LOGICB, /* modify all flags, CC_DST = res */
|
||||
CC_OP_LOGICW,
|
||||
CC_OP_LOGICL,
|
||||
|
||||
CC_OP_INCB, /* modify all flags except, CC_DST = res, CC_SRC = C */
|
||||
CC_OP_INCW,
|
||||
CC_OP_INCL,
|
||||
|
||||
CC_OP_DECB, /* modify all flags except, CC_DST = res, CC_SRC = C */
|
||||
CC_OP_DECW,
|
||||
CC_OP_DECL,
|
||||
|
||||
CC_OP_SHLB, /* modify all flags, CC_DST = res, CC_SRC.lsb = C */
|
||||
CC_OP_SHLW,
|
||||
CC_OP_SHLL,
|
||||
|
||||
CC_OP_SARB, /* modify all flags, CC_DST = res, CC_SRC.lsb = C */
|
||||
CC_OP_SARW,
|
||||
CC_OP_SARL,
|
||||
|
||||
CC_OP_NB,
|
||||
};
|
||||
|
||||
#ifdef __i386__
|
||||
#define USE_X86LDOUBLE
|
||||
#endif
|
||||
|
||||
#ifdef USE_X86LDOUBLE
|
||||
typedef long double CPU86_LDouble;
|
||||
#else
|
||||
typedef double CPU86_LDouble;
|
||||
#endif
|
||||
|
||||
typedef struct SegmentCache {
|
||||
uint8_t *base;
|
||||
unsigned long limit;
|
||||
uint8_t seg_32bit;
|
||||
} SegmentCache;
|
||||
|
||||
typedef struct SegmentDescriptorTable {
|
||||
uint8_t *base;
|
||||
unsigned long limit;
|
||||
/* this is the returned base when reading the register, just to
|
||||
avoid that the emulated program modifies it */
|
||||
unsigned long emu_base;
|
||||
} SegmentDescriptorTable;
|
||||
|
||||
typedef struct CPUX86State {
|
||||
/* standard registers */
|
||||
uint32_t regs[8];
|
||||
uint32_t eip;
|
||||
uint32_t eflags; /* eflags register. During CPU emulation, CC
|
||||
flags and DF are set to zero because they are
|
||||
store elsewhere */
|
||||
|
||||
/* emulator internal eflags handling */
|
||||
uint32_t cc_src;
|
||||
uint32_t cc_dst;
|
||||
uint32_t cc_op;
|
||||
int32_t df; /* D flag : 1 if D = 0, -1 if D = 1 */
|
||||
|
||||
/* FPU state */
|
||||
unsigned int fpstt; /* top of stack index */
|
||||
unsigned int fpus;
|
||||
unsigned int fpuc;
|
||||
uint8_t fptags[8]; /* 0 = valid, 1 = empty */
|
||||
CPU86_LDouble fpregs[8];
|
||||
|
||||
/* emulator internal variables */
|
||||
CPU86_LDouble ft0;
|
||||
|
||||
/* segments */
|
||||
uint32_t segs[6]; /* selector values */
|
||||
SegmentCache seg_cache[6]; /* info taken from LDT/GDT */
|
||||
SegmentDescriptorTable gdt;
|
||||
SegmentDescriptorTable ldt;
|
||||
SegmentDescriptorTable idt;
|
||||
|
||||
/* exception/interrupt handling */
|
||||
jmp_buf jmp_env;
|
||||
int exception_index;
|
||||
int interrupt_request;
|
||||
|
||||
/* user data */
|
||||
void *opaque;
|
||||
} CPUX86State;
|
||||
|
||||
/* all CPU memory access use these macros */
|
||||
static inline int ldub(void *ptr)
|
||||
{
|
||||
return *(uint8_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldsb(void *ptr)
|
||||
{
|
||||
return *(int8_t *)ptr;
|
||||
}
|
||||
|
||||
static inline void stb(void *ptr, int v)
|
||||
{
|
||||
*(uint8_t *)ptr = v;
|
||||
}
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
|
||||
/* conservative code for little endian unaligned accesses */
|
||||
static inline int lduw(void *ptr)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
int val;
|
||||
__asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
|
||||
return val;
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
return p[0] | (p[1] << 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int ldsw(void *ptr)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
int val;
|
||||
__asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
|
||||
return (int16_t)val;
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
return (int16_t)(p[0] | (p[1] << 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int ldl(void *ptr)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
int val;
|
||||
__asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (ptr));
|
||||
return val;
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t ldq(void *ptr)
|
||||
{
|
||||
uint8_t *p = ptr;
|
||||
uint32_t v1, v2;
|
||||
v1 = ldl(p);
|
||||
v2 = ldl(p + 4);
|
||||
return v1 | ((uint64_t)v2 << 32);
|
||||
}
|
||||
|
||||
static inline void stw(void *ptr, int v)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
__asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr));
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
p[0] = v;
|
||||
p[1] = v >> 8;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void stl(void *ptr, int v)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
__asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr));
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
p[0] = v;
|
||||
p[1] = v >> 8;
|
||||
p[2] = v >> 16;
|
||||
p[3] = v >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void stq(void *ptr, uint64_t v)
|
||||
{
|
||||
uint8_t *p = ptr;
|
||||
stl(p, (uint32_t)v);
|
||||
stl(p + 4, v >> 32);
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float ldfl(void *ptr)
|
||||
{
|
||||
union {
|
||||
float f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.i = ldl(ptr);
|
||||
return u.f;
|
||||
}
|
||||
|
||||
static inline double ldfq(void *ptr)
|
||||
{
|
||||
union {
|
||||
double d;
|
||||
uint64_t i;
|
||||
} u;
|
||||
u.i = ldq(ptr);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static inline void stfl(void *ptr, float v)
|
||||
{
|
||||
union {
|
||||
float f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.f = v;
|
||||
stl(ptr, u.i);
|
||||
}
|
||||
|
||||
static inline void stfq(void *ptr, double v)
|
||||
{
|
||||
union {
|
||||
double d;
|
||||
uint64_t i;
|
||||
} u;
|
||||
u.d = v;
|
||||
stq(ptr, u.i);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int lduw(void *ptr)
|
||||
{
|
||||
return *(uint16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldsw(void *ptr)
|
||||
{
|
||||
return *(int16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldl(void *ptr)
|
||||
{
|
||||
return *(uint32_t *)ptr;
|
||||
}
|
||||
|
||||
static inline uint64_t ldq(void *ptr)
|
||||
{
|
||||
return *(uint64_t *)ptr;
|
||||
}
|
||||
|
||||
static inline void stw(void *ptr, int v)
|
||||
{
|
||||
*(uint16_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stl(void *ptr, int v)
|
||||
{
|
||||
*(uint32_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stq(void *ptr, uint64_t v)
|
||||
{
|
||||
*(uint64_t *)ptr = v;
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float ldfl(void *ptr)
|
||||
{
|
||||
return *(float *)ptr;
|
||||
}
|
||||
|
||||
static inline double ldfq(void *ptr)
|
||||
{
|
||||
return *(double *)ptr;
|
||||
}
|
||||
|
||||
static inline void stfl(void *ptr, float v)
|
||||
{
|
||||
*(float *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stfq(void *ptr, double v)
|
||||
{
|
||||
*(double *)ptr = v;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef IN_OP_I386
|
||||
void cpu_x86_outb(int addr, int val);
|
||||
void cpu_x86_outw(int addr, int val);
|
||||
void cpu_x86_outl(int addr, int val);
|
||||
int cpu_x86_inb(int addr);
|
||||
int cpu_x86_inw(int addr);
|
||||
int cpu_x86_inl(int addr);
|
||||
#endif
|
||||
|
||||
CPUX86State *cpu_x86_init(void);
|
||||
int cpu_x86_exec(CPUX86State *s);
|
||||
void cpu_x86_interrupt(CPUX86State *s);
|
||||
void cpu_x86_close(CPUX86State *s);
|
||||
|
||||
/* needed to load some predefinied segment registers */
|
||||
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector);
|
||||
|
||||
/* you can call this signal handler from your SIGBUS and SIGSEGV
|
||||
signal handlers to inform the virtual CPU of exceptions. non zero
|
||||
is returned if the signal was handled by the virtual CPU. */
|
||||
struct siginfo;
|
||||
int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
|
||||
void *puc);
|
||||
|
||||
/* internal functions */
|
||||
|
||||
#define GEN_FLAG_CODE32_SHIFT 0
|
||||
#define GEN_FLAG_ADDSEG_SHIFT 1
|
||||
#define GEN_FLAG_SS32_SHIFT 2
|
||||
#define GEN_FLAG_VM_SHIFT 3
|
||||
#define GEN_FLAG_ST_SHIFT 4
|
||||
|
||||
int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
|
||||
int *gen_code_size_ptr,
|
||||
uint8_t *pc_start, uint8_t *cs_base, int flags);
|
||||
void cpu_x86_tblocks_init(void);
|
||||
|
||||
#endif /* CPU_I386_H */
|
||||
@@ -9,9 +9,166 @@
|
||||
#ifndef DIS_ASM_H
|
||||
#define DIS_ASM_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "bfd.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
#define PARAMS(x) x
|
||||
typedef void *PTR;
|
||||
typedef uint64_t bfd_vma;
|
||||
typedef int64_t bfd_signed_vma;
|
||||
typedef uint8_t bfd_byte;
|
||||
#define sprintf_vma(s,x) sprintf (s, "%0" PRIx64, x)
|
||||
|
||||
#define BFD64
|
||||
|
||||
enum bfd_flavour {
|
||||
bfd_target_unknown_flavour,
|
||||
bfd_target_aout_flavour,
|
||||
bfd_target_coff_flavour,
|
||||
bfd_target_ecoff_flavour,
|
||||
bfd_target_elf_flavour,
|
||||
bfd_target_ieee_flavour,
|
||||
bfd_target_nlm_flavour,
|
||||
bfd_target_oasys_flavour,
|
||||
bfd_target_tekhex_flavour,
|
||||
bfd_target_srec_flavour,
|
||||
bfd_target_ihex_flavour,
|
||||
bfd_target_som_flavour,
|
||||
bfd_target_os9k_flavour,
|
||||
bfd_target_versados_flavour,
|
||||
bfd_target_msdos_flavour,
|
||||
bfd_target_evax_flavour
|
||||
};
|
||||
|
||||
enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN };
|
||||
|
||||
enum bfd_architecture
|
||||
{
|
||||
bfd_arch_unknown, /* File arch not known */
|
||||
bfd_arch_obscure, /* Arch known, not one of these */
|
||||
bfd_arch_m68k, /* Motorola 68xxx */
|
||||
#define bfd_mach_m68000 1
|
||||
#define bfd_mach_m68008 2
|
||||
#define bfd_mach_m68010 3
|
||||
#define bfd_mach_m68020 4
|
||||
#define bfd_mach_m68030 5
|
||||
#define bfd_mach_m68040 6
|
||||
#define bfd_mach_m68060 7
|
||||
bfd_arch_vax, /* DEC Vax */
|
||||
bfd_arch_i960, /* Intel 960 */
|
||||
/* The order of the following is important.
|
||||
lower number indicates a machine type that
|
||||
only accepts a subset of the instructions
|
||||
available to machines with higher numbers.
|
||||
The exception is the "ca", which is
|
||||
incompatible with all other machines except
|
||||
"core". */
|
||||
|
||||
#define bfd_mach_i960_core 1
|
||||
#define bfd_mach_i960_ka_sa 2
|
||||
#define bfd_mach_i960_kb_sb 3
|
||||
#define bfd_mach_i960_mc 4
|
||||
#define bfd_mach_i960_xa 5
|
||||
#define bfd_mach_i960_ca 6
|
||||
#define bfd_mach_i960_jx 7
|
||||
#define bfd_mach_i960_hx 8
|
||||
|
||||
bfd_arch_a29k, /* AMD 29000 */
|
||||
bfd_arch_sparc, /* SPARC */
|
||||
#define bfd_mach_sparc 1
|
||||
/* The difference between v8plus and v9 is that v9 is a true 64 bit env. */
|
||||
#define bfd_mach_sparc_sparclet 2
|
||||
#define bfd_mach_sparc_sparclite 3
|
||||
#define bfd_mach_sparc_v8plus 4
|
||||
#define bfd_mach_sparc_v8plusa 5 /* with ultrasparc add'ns. */
|
||||
#define bfd_mach_sparc_sparclite_le 6
|
||||
#define bfd_mach_sparc_v9 7
|
||||
#define bfd_mach_sparc_v9a 8 /* with ultrasparc add'ns. */
|
||||
#define bfd_mach_sparc_v8plusb 9 /* with cheetah add'ns. */
|
||||
#define bfd_mach_sparc_v9b 10 /* with cheetah add'ns. */
|
||||
/* Nonzero if MACH has the v9 instruction set. */
|
||||
#define bfd_mach_sparc_v9_p(mach) \
|
||||
((mach) >= bfd_mach_sparc_v8plus && (mach) <= bfd_mach_sparc_v9b \
|
||||
&& (mach) != bfd_mach_sparc_sparclite_le)
|
||||
bfd_arch_mips, /* MIPS Rxxxx */
|
||||
#define bfd_mach_mips3000 3000
|
||||
#define bfd_mach_mips3900 3900
|
||||
#define bfd_mach_mips4000 4000
|
||||
#define bfd_mach_mips4010 4010
|
||||
#define bfd_mach_mips4100 4100
|
||||
#define bfd_mach_mips4300 4300
|
||||
#define bfd_mach_mips4400 4400
|
||||
#define bfd_mach_mips4600 4600
|
||||
#define bfd_mach_mips4650 4650
|
||||
#define bfd_mach_mips5000 5000
|
||||
#define bfd_mach_mips6000 6000
|
||||
#define bfd_mach_mips8000 8000
|
||||
#define bfd_mach_mips10000 10000
|
||||
#define bfd_mach_mips16 16
|
||||
bfd_arch_i386, /* Intel 386 */
|
||||
#define bfd_mach_i386_i386 0
|
||||
#define bfd_mach_i386_i8086 1
|
||||
#define bfd_mach_i386_i386_intel_syntax 2
|
||||
#define bfd_mach_x86_64 3
|
||||
#define bfd_mach_x86_64_intel_syntax 4
|
||||
bfd_arch_we32k, /* AT&T WE32xxx */
|
||||
bfd_arch_tahoe, /* CCI/Harris Tahoe */
|
||||
bfd_arch_i860, /* Intel 860 */
|
||||
bfd_arch_romp, /* IBM ROMP PC/RT */
|
||||
bfd_arch_alliant, /* Alliant */
|
||||
bfd_arch_convex, /* Convex */
|
||||
bfd_arch_m88k, /* Motorola 88xxx */
|
||||
bfd_arch_pyramid, /* Pyramid Technology */
|
||||
bfd_arch_h8300, /* Hitachi H8/300 */
|
||||
#define bfd_mach_h8300 1
|
||||
#define bfd_mach_h8300h 2
|
||||
#define bfd_mach_h8300s 3
|
||||
bfd_arch_powerpc, /* PowerPC */
|
||||
bfd_arch_rs6000, /* IBM RS/6000 */
|
||||
bfd_arch_hppa, /* HP PA RISC */
|
||||
bfd_arch_d10v, /* Mitsubishi D10V */
|
||||
bfd_arch_z8k, /* Zilog Z8000 */
|
||||
#define bfd_mach_z8001 1
|
||||
#define bfd_mach_z8002 2
|
||||
bfd_arch_h8500, /* Hitachi H8/500 */
|
||||
bfd_arch_sh, /* Hitachi SH */
|
||||
#define bfd_mach_sh 0
|
||||
#define bfd_mach_sh3 0x30
|
||||
#define bfd_mach_sh3e 0x3e
|
||||
#define bfd_mach_sh4 0x40
|
||||
bfd_arch_alpha, /* Dec Alpha */
|
||||
bfd_arch_arm, /* Advanced Risc Machines ARM */
|
||||
#define bfd_mach_arm_2 1
|
||||
#define bfd_mach_arm_2a 2
|
||||
#define bfd_mach_arm_3 3
|
||||
#define bfd_mach_arm_3M 4
|
||||
#define bfd_mach_arm_4 5
|
||||
#define bfd_mach_arm_4T 6
|
||||
bfd_arch_ns32k, /* National Semiconductors ns32000 */
|
||||
bfd_arch_w65, /* WDC 65816 */
|
||||
bfd_arch_tic30, /* Texas Instruments TMS320C30 */
|
||||
bfd_arch_v850, /* NEC V850 */
|
||||
#define bfd_mach_v850 0
|
||||
bfd_arch_arc, /* Argonaut RISC Core */
|
||||
#define bfd_mach_arc_base 0
|
||||
bfd_arch_m32r, /* Mitsubishi M32R/D */
|
||||
#define bfd_mach_m32r 0 /* backwards compatibility */
|
||||
bfd_arch_mn10200, /* Matsushita MN10200 */
|
||||
bfd_arch_mn10300, /* Matsushita MN10300 */
|
||||
bfd_arch_last
|
||||
};
|
||||
|
||||
typedef struct symbol_cache_entry
|
||||
{
|
||||
const char *name;
|
||||
union
|
||||
{
|
||||
PTR p;
|
||||
bfd_vma i;
|
||||
} udata;
|
||||
} asymbol;
|
||||
|
||||
typedef int (*fprintf_ftype) PARAMS((FILE*, const char*, ...));
|
||||
|
||||
@@ -134,6 +291,9 @@ typedef struct disassemble_info {
|
||||
zero if unknown. */
|
||||
bfd_vma target2; /* Second target address for dref2 */
|
||||
|
||||
/* Command line options specific to the target disassembler. */
|
||||
char * disassembler_options;
|
||||
|
||||
} disassemble_info;
|
||||
|
||||
|
||||
@@ -154,8 +314,7 @@ extern int print_insn_h8300s PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_h8500 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_alpha PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern disassembler_ftype arc_get_disassembler PARAMS ((int, int));
|
||||
extern int print_insn_big_arm PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_arm PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_arm PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_sparc PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_big_a29k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_a29k PARAMS ((bfd_vma, disassemble_info*));
|
||||
@@ -175,9 +334,12 @@ extern int print_insn_w65 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_d10v PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_v850 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_tic30 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_ppc PARAMS ((bfd_vma, disassemble_info*));
|
||||
|
||||
#if 0
|
||||
/* Fetch the disassembler for a given BFD, if that support is available. */
|
||||
extern disassembler_ftype disassembler PARAMS ((bfd *));
|
||||
#endif
|
||||
|
||||
|
||||
/* This block of definitions is for particular callers who read instructions
|
||||
@@ -233,6 +395,15 @@ extern int generic_symbol_at_address
|
||||
(INFO).bytes_per_line = 0, \
|
||||
(INFO).bytes_per_chunk = 0, \
|
||||
(INFO).display_endian = BFD_ENDIAN_UNKNOWN, \
|
||||
(INFO).disassembler_options = NULL, \
|
||||
(INFO).insn_info_valid = 0
|
||||
|
||||
#define _(x) x
|
||||
|
||||
/* from libbfd */
|
||||
|
||||
bfd_vma bfd_getl32 (const bfd_byte *addr);
|
||||
bfd_vma bfd_getb32 (const bfd_byte *addr);
|
||||
typedef enum bfd_boolean {false, true} boolean;
|
||||
|
||||
#endif /* ! defined (DIS_ASM_H) */
|
||||
@@ -1,79 +0,0 @@
|
||||
/* Disassemble from a buffer, for GNU.
|
||||
Copyright (C) 1993, 1994 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "dis-asm.h"
|
||||
#include <errno.h>
|
||||
|
||||
/* Get LENGTH bytes from info's buffer, at target address memaddr.
|
||||
Transfer them to myaddr. */
|
||||
int
|
||||
buffer_read_memory (memaddr, myaddr, length, info)
|
||||
bfd_vma memaddr;
|
||||
bfd_byte *myaddr;
|
||||
int length;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
if (memaddr < info->buffer_vma
|
||||
|| memaddr + length > info->buffer_vma + info->buffer_length)
|
||||
/* Out of bounds. Use EIO because GDB uses it. */
|
||||
return EIO;
|
||||
memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Print an error message. We can assume that this is in response to
|
||||
an error return from buffer_read_memory. */
|
||||
void
|
||||
perror_memory (status, memaddr, info)
|
||||
int status;
|
||||
bfd_vma memaddr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
if (status != EIO)
|
||||
/* Can't happen. */
|
||||
(*info->fprintf_func) (info->stream, "Unknown error %d\n", status);
|
||||
else
|
||||
/* Actually, address between memaddr and memaddr + len was
|
||||
out of bounds. */
|
||||
(*info->fprintf_func) (info->stream,
|
||||
"Address 0x%x is out of bounds.\n", memaddr);
|
||||
}
|
||||
|
||||
/* This could be in a separate file, to save miniscule amounts of space
|
||||
in statically linked executables. */
|
||||
|
||||
/* Just print the address is hex. This is included for completeness even
|
||||
though both GDB and objdump provide their own (to print symbolic
|
||||
addresses). */
|
||||
|
||||
void
|
||||
generic_print_address (addr, info)
|
||||
bfd_vma addr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
(*info->fprintf_func) (info->stream, "0x%x", addr);
|
||||
}
|
||||
|
||||
/* Just return the given address. */
|
||||
|
||||
int
|
||||
generic_symbol_at_address (addr, info)
|
||||
bfd_vma addr;
|
||||
struct disassemble_info * info;
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/* General "disassemble this chunk" code. Used for debugging. */
|
||||
#include "config.h"
|
||||
#include "dis-asm.h"
|
||||
#include "elf.h"
|
||||
#include <errno.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "exec-all.h"
|
||||
#include "disas.h"
|
||||
|
||||
/* Filled in by elfload.c. Simplistic, but will do for now. */
|
||||
struct syminfo *syminfos = NULL;
|
||||
|
||||
/* Get LENGTH bytes from info's buffer, at target address memaddr.
|
||||
Transfer them to myaddr. */
|
||||
int
|
||||
buffer_read_memory (memaddr, myaddr, length, info)
|
||||
bfd_vma memaddr;
|
||||
bfd_byte *myaddr;
|
||||
int length;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
if (memaddr < info->buffer_vma
|
||||
|| memaddr + length > info->buffer_vma + info->buffer_length)
|
||||
/* Out of bounds. Use EIO because GDB uses it. */
|
||||
return EIO;
|
||||
memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get LENGTH bytes from info's buffer, at target address memaddr.
|
||||
Transfer them to myaddr. */
|
||||
static int
|
||||
target_read_memory (bfd_vma memaddr,
|
||||
bfd_byte *myaddr,
|
||||
int length,
|
||||
struct disassemble_info *info)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < length; i++) {
|
||||
myaddr[i] = ldub_code(memaddr + i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Print an error message. We can assume that this is in response to
|
||||
an error return from buffer_read_memory. */
|
||||
void
|
||||
perror_memory (status, memaddr, info)
|
||||
int status;
|
||||
bfd_vma memaddr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
if (status != EIO)
|
||||
/* Can't happen. */
|
||||
(*info->fprintf_func) (info->stream, "Unknown error %d\n", status);
|
||||
else
|
||||
/* Actually, address between memaddr and memaddr + len was
|
||||
out of bounds. */
|
||||
(*info->fprintf_func) (info->stream,
|
||||
"Address 0x%llx is out of bounds.\n", memaddr);
|
||||
}
|
||||
|
||||
/* This could be in a separate file, to save miniscule amounts of space
|
||||
in statically linked executables. */
|
||||
|
||||
/* Just print the address is hex. This is included for completeness even
|
||||
though both GDB and objdump provide their own (to print symbolic
|
||||
addresses). */
|
||||
|
||||
void
|
||||
generic_print_address (addr, info)
|
||||
bfd_vma addr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
(*info->fprintf_func) (info->stream, "0x%llx", addr);
|
||||
}
|
||||
|
||||
/* Just return the given address. */
|
||||
|
||||
int
|
||||
generic_symbol_at_address (addr, info)
|
||||
bfd_vma addr;
|
||||
struct disassemble_info * info;
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
bfd_vma bfd_getl32 (const bfd_byte *addr)
|
||||
{
|
||||
unsigned long v;
|
||||
|
||||
v = (unsigned long) addr[0];
|
||||
v |= (unsigned long) addr[1] << 8;
|
||||
v |= (unsigned long) addr[2] << 16;
|
||||
v |= (unsigned long) addr[3] << 24;
|
||||
return (bfd_vma) v;
|
||||
}
|
||||
|
||||
bfd_vma bfd_getb32 (const bfd_byte *addr)
|
||||
{
|
||||
unsigned long v;
|
||||
|
||||
v = (unsigned long) addr[0] << 24;
|
||||
v |= (unsigned long) addr[1] << 16;
|
||||
v |= (unsigned long) addr[2] << 8;
|
||||
v |= (unsigned long) addr[3];
|
||||
return (bfd_vma) v;
|
||||
}
|
||||
|
||||
#ifdef TARGET_ARM
|
||||
static int
|
||||
print_insn_thumb1(bfd_vma pc, disassemble_info *info)
|
||||
{
|
||||
return print_insn_arm(pc | 1, info);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Disassemble this for me please... (debugging). 'flags' has teh following
|
||||
values:
|
||||
i386 - nonzero means 16 bit code
|
||||
arm - nonzero means thumb code
|
||||
other targets - unused
|
||||
*/
|
||||
void target_disas(FILE *out, target_ulong code, target_ulong size, int flags)
|
||||
{
|
||||
target_ulong pc;
|
||||
int count;
|
||||
struct disassemble_info disasm_info;
|
||||
int (*print_insn)(bfd_vma pc, disassemble_info *info);
|
||||
|
||||
INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf);
|
||||
|
||||
disasm_info.read_memory_func = target_read_memory;
|
||||
disasm_info.buffer_vma = code;
|
||||
disasm_info.buffer_length = size;
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
disasm_info.endian = BFD_ENDIAN_BIG;
|
||||
#else
|
||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||
#endif
|
||||
#if defined(TARGET_I386)
|
||||
if (flags == 2)
|
||||
disasm_info.mach = bfd_mach_x86_64;
|
||||
else if (flags == 1)
|
||||
disasm_info.mach = bfd_mach_i386_i8086;
|
||||
else
|
||||
disasm_info.mach = bfd_mach_i386_i386;
|
||||
print_insn = print_insn_i386;
|
||||
#elif defined(TARGET_ARM)
|
||||
if (flags)
|
||||
print_insn = print_insn_thumb1;
|
||||
else
|
||||
print_insn = print_insn_arm;
|
||||
#elif defined(TARGET_SPARC)
|
||||
print_insn = print_insn_sparc;
|
||||
#elif defined(TARGET_PPC)
|
||||
if (cpu_single_env->msr[MSR_LE])
|
||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||
print_insn = print_insn_ppc;
|
||||
#else
|
||||
fprintf(out, "0x" TARGET_FMT_lx
|
||||
": Asm output not supported on this arch\n", code);
|
||||
return;
|
||||
#endif
|
||||
|
||||
for (pc = code; pc < code + size; pc += count) {
|
||||
fprintf(out, "0x" TARGET_FMT_lx ": ", pc);
|
||||
count = print_insn(pc, &disasm_info);
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
uint8_t b;
|
||||
fprintf(out, " {");
|
||||
for(i = 0; i < count; i++) {
|
||||
target_read_memory(pc + i, &b, 1, &disasm_info);
|
||||
fprintf(out, " %02x", b);
|
||||
}
|
||||
fprintf(out, " }");
|
||||
}
|
||||
#endif
|
||||
fprintf(out, "\n");
|
||||
if (count < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disassemble this for me please... (debugging). */
|
||||
void disas(FILE *out, void *code, unsigned long size)
|
||||
{
|
||||
unsigned long pc;
|
||||
int count;
|
||||
struct disassemble_info disasm_info;
|
||||
int (*print_insn)(bfd_vma pc, disassemble_info *info);
|
||||
|
||||
INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf);
|
||||
|
||||
disasm_info.buffer = code;
|
||||
disasm_info.buffer_vma = (unsigned long)code;
|
||||
disasm_info.buffer_length = size;
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
disasm_info.endian = BFD_ENDIAN_BIG;
|
||||
#else
|
||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||
#endif
|
||||
#if defined(__i386__)
|
||||
disasm_info.mach = bfd_mach_i386_i386;
|
||||
print_insn = print_insn_i386;
|
||||
#elif defined(__x86_64__)
|
||||
disasm_info.mach = bfd_mach_x86_64;
|
||||
print_insn = print_insn_i386;
|
||||
#elif defined(__powerpc__)
|
||||
print_insn = print_insn_ppc;
|
||||
#elif defined(__alpha__)
|
||||
print_insn = print_insn_alpha;
|
||||
#elif defined(__sparc__)
|
||||
print_insn = print_insn_sparc;
|
||||
#elif defined(__arm__)
|
||||
print_insn = print_insn_arm;
|
||||
#else
|
||||
fprintf(out, "0x%lx: Asm output not supported on this arch\n",
|
||||
(long) code);
|
||||
return;
|
||||
#endif
|
||||
for (pc = (unsigned long)code; pc < (unsigned long)code + size; pc += count) {
|
||||
fprintf(out, "0x%08lx: ", pc);
|
||||
#ifdef __arm__
|
||||
/* since data are included in the code, it is better to
|
||||
display code data too */
|
||||
if (is_host) {
|
||||
fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc));
|
||||
}
|
||||
#endif
|
||||
count = print_insn(pc, &disasm_info);
|
||||
fprintf(out, "\n");
|
||||
if (count < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Look up symbol for debugging purpose. Returns "" if unknown. */
|
||||
const char *lookup_symbol(target_ulong orig_addr)
|
||||
{
|
||||
unsigned int i;
|
||||
/* Hack, because we know this is x86. */
|
||||
Elf32_Sym *sym;
|
||||
struct syminfo *s;
|
||||
|
||||
for (s = syminfos; s; s = s->next) {
|
||||
sym = s->disas_symtab;
|
||||
for (i = 0; i < s->disas_num_syms; i++) {
|
||||
if (sym[i].st_shndx == SHN_UNDEF
|
||||
|| sym[i].st_shndx >= SHN_LORESERVE)
|
||||
continue;
|
||||
|
||||
if (ELF_ST_TYPE(sym[i].st_info) != STT_FUNC)
|
||||
continue;
|
||||
|
||||
if (orig_addr >= sym[i].st_value
|
||||
&& orig_addr < sym[i].st_value + sym[i].st_size)
|
||||
return s->disas_strtab + sym[i].st_name;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
void term_vprintf(const char *fmt, va_list ap);
|
||||
void term_printf(const char *fmt, ...);
|
||||
|
||||
static int monitor_disas_is_physical;
|
||||
|
||||
static int
|
||||
monitor_read_memory (memaddr, myaddr, length, info)
|
||||
bfd_vma memaddr;
|
||||
bfd_byte *myaddr;
|
||||
int length;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
if (monitor_disas_is_physical) {
|
||||
cpu_physical_memory_rw(memaddr, myaddr, length, 0);
|
||||
} else {
|
||||
cpu_memory_rw_debug(cpu_single_env, memaddr,myaddr, length, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int monitor_fprintf(FILE *stream, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
term_vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void monitor_disas(target_ulong pc, int nb_insn, int is_physical, int flags)
|
||||
{
|
||||
int count, i;
|
||||
struct disassemble_info disasm_info;
|
||||
int (*print_insn)(bfd_vma pc, disassemble_info *info);
|
||||
|
||||
INIT_DISASSEMBLE_INFO(disasm_info, NULL, monitor_fprintf);
|
||||
|
||||
monitor_disas_is_physical = is_physical;
|
||||
disasm_info.read_memory_func = monitor_read_memory;
|
||||
|
||||
disasm_info.buffer_vma = pc;
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
disasm_info.endian = BFD_ENDIAN_BIG;
|
||||
#else
|
||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||
#endif
|
||||
#if defined(TARGET_I386)
|
||||
if (flags == 2)
|
||||
disasm_info.mach = bfd_mach_x86_64;
|
||||
else if (flags == 1)
|
||||
disasm_info.mach = bfd_mach_i386_i8086;
|
||||
else
|
||||
disasm_info.mach = bfd_mach_i386_i386;
|
||||
print_insn = print_insn_i386;
|
||||
#elif defined(TARGET_ARM)
|
||||
print_insn = print_insn_arm;
|
||||
#elif defined(TARGET_SPARC)
|
||||
print_insn = print_insn_sparc;
|
||||
#elif defined(TARGET_PPC)
|
||||
print_insn = print_insn_ppc;
|
||||
#else
|
||||
term_printf("0x" TARGET_FMT_lx
|
||||
": Asm output not supported on this arch\n", pc);
|
||||
return;
|
||||
#endif
|
||||
|
||||
for(i = 0; i < nb_insn; i++) {
|
||||
term_printf("0x" TARGET_FMT_lx ": ", pc);
|
||||
count = print_insn(pc, &disasm_info);
|
||||
term_printf("\n");
|
||||
if (count < 0)
|
||||
break;
|
||||
pc += count;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef _QEMU_DISAS_H
|
||||
#define _QEMU_DISAS_H
|
||||
|
||||
/* Disassemble this for me please... (debugging). */
|
||||
void disas(FILE *out, void *code, unsigned long size);
|
||||
void target_disas(FILE *out, target_ulong code, target_ulong size, int flags);
|
||||
void monitor_disas(target_ulong pc, int nb_insn, int is_physical, int flags);
|
||||
|
||||
/* Look up symbol for debugging purpose. Returns "" if unknown. */
|
||||
const char *lookup_symbol(target_ulong orig_addr);
|
||||
|
||||
/* Filled in by elfload.c. Simplistic, but will do for now. */
|
||||
extern struct syminfo {
|
||||
unsigned int disas_num_syms;
|
||||
void *disas_symtab;
|
||||
const char *disas_strtab;
|
||||
struct syminfo *next;
|
||||
} *syminfos;
|
||||
|
||||
#endif /* _QEMU_DISAS_H */
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* dyngen defines for micro operation code
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#if !defined(__DYNGEN_EXEC_H__)
|
||||
#define __DYNGEN_EXEC_H__
|
||||
|
||||
/* NOTE: standard headers should be used with special care at this
|
||||
point because host CPU registers are used as global variables. Some
|
||||
host headers do not allow that. */
|
||||
#include <stddef.h>
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
/* XXX may be done for all 64 bits targets ? */
|
||||
#if defined (__x86_64__) || defined(__ia64)
|
||||
typedef unsigned long uint64_t;
|
||||
#else
|
||||
typedef unsigned long long uint64_t;
|
||||
#endif
|
||||
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
#if defined (__x86_64__) || defined(__ia64)
|
||||
typedef signed long int64_t;
|
||||
#else
|
||||
typedef signed long long int64_t;
|
||||
#endif
|
||||
|
||||
#define INT8_MIN (-128)
|
||||
#define INT16_MIN (-32767-1)
|
||||
#define INT32_MIN (-2147483647-1)
|
||||
#define INT64_MIN (-(int64_t)(9223372036854775807)-1)
|
||||
#define INT8_MAX (127)
|
||||
#define INT16_MAX (32767)
|
||||
#define INT32_MAX (2147483647)
|
||||
#define INT64_MAX ((int64_t)(9223372036854775807))
|
||||
#define UINT8_MAX (255)
|
||||
#define UINT16_MAX (65535)
|
||||
#define UINT32_MAX (4294967295U)
|
||||
#define UINT64_MAX ((uint64_t)(18446744073709551615))
|
||||
|
||||
typedef struct FILE FILE;
|
||||
extern int fprintf(FILE *, const char *, ...);
|
||||
extern int printf(const char *, ...);
|
||||
#undef NULL
|
||||
#define NULL 0
|
||||
|
||||
#ifdef __i386__
|
||||
#define AREG0 "ebp"
|
||||
#define AREG1 "ebx"
|
||||
#define AREG2 "esi"
|
||||
#define AREG3 "edi"
|
||||
#endif
|
||||
#ifdef __x86_64__
|
||||
#define AREG0 "rbp"
|
||||
#define AREG1 "rbx"
|
||||
#define AREG2 "r12"
|
||||
#define AREG3 "r13"
|
||||
//#define AREG4 "r14"
|
||||
//#define AREG5 "r15"
|
||||
#endif
|
||||
#ifdef __powerpc__
|
||||
#define AREG0 "r27"
|
||||
#define AREG1 "r24"
|
||||
#define AREG2 "r25"
|
||||
#define AREG3 "r26"
|
||||
/* XXX: suppress this hack */
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#define AREG4 "r16"
|
||||
#define AREG5 "r17"
|
||||
#define AREG6 "r18"
|
||||
#define AREG7 "r19"
|
||||
#define AREG8 "r20"
|
||||
#define AREG9 "r21"
|
||||
#define AREG10 "r22"
|
||||
#define AREG11 "r23"
|
||||
#endif
|
||||
#define USE_INT_TO_FLOAT_HELPERS
|
||||
#define BUGGY_GCC_DIV64
|
||||
#endif
|
||||
#ifdef __arm__
|
||||
#define AREG0 "r7"
|
||||
#define AREG1 "r4"
|
||||
#define AREG2 "r5"
|
||||
#define AREG3 "r6"
|
||||
#endif
|
||||
#ifdef __mips__
|
||||
#define AREG0 "s3"
|
||||
#define AREG1 "s0"
|
||||
#define AREG2 "s1"
|
||||
#define AREG3 "s2"
|
||||
#endif
|
||||
#ifdef __sparc__
|
||||
#define AREG0 "g6"
|
||||
#define AREG1 "g1"
|
||||
#define AREG2 "g2"
|
||||
#define AREG3 "g3"
|
||||
#define AREG4 "l0"
|
||||
#define AREG5 "l1"
|
||||
#define AREG6 "l2"
|
||||
#define AREG7 "l3"
|
||||
#define AREG8 "l4"
|
||||
#define AREG9 "l5"
|
||||
#define AREG10 "l6"
|
||||
#define AREG11 "l7"
|
||||
#define USE_FP_CONVERT
|
||||
#endif
|
||||
#ifdef __s390__
|
||||
#define AREG0 "r10"
|
||||
#define AREG1 "r7"
|
||||
#define AREG2 "r8"
|
||||
#define AREG3 "r9"
|
||||
#endif
|
||||
#ifdef __alpha__
|
||||
/* Note $15 is the frame pointer, so anything in op-i386.c that would
|
||||
require a frame pointer, like alloca, would probably loose. */
|
||||
#define AREG0 "$15"
|
||||
#define AREG1 "$9"
|
||||
#define AREG2 "$10"
|
||||
#define AREG3 "$11"
|
||||
#define AREG4 "$12"
|
||||
#define AREG5 "$13"
|
||||
#define AREG6 "$14"
|
||||
#endif
|
||||
#ifdef __mc68000
|
||||
#define AREG0 "%a5"
|
||||
#define AREG1 "%a4"
|
||||
#define AREG2 "%d7"
|
||||
#define AREG3 "%d6"
|
||||
#define AREG4 "%d5"
|
||||
#endif
|
||||
#ifdef __ia64__
|
||||
#define AREG0 "r7"
|
||||
#define AREG1 "r4"
|
||||
#define AREG2 "r5"
|
||||
#define AREG3 "r6"
|
||||
#endif
|
||||
|
||||
/* force GCC to generate only one epilog at the end of the function */
|
||||
#define FORCE_RET() asm volatile ("");
|
||||
|
||||
#ifndef OPPROTO
|
||||
#define OPPROTO
|
||||
#endif
|
||||
|
||||
#define xglue(x, y) x ## y
|
||||
#define glue(x, y) xglue(x, y)
|
||||
#define stringify(s) tostring(s)
|
||||
#define tostring(s) #s
|
||||
|
||||
#ifdef __alpha__
|
||||
/* the symbols are considered non exported so a br immediate is generated */
|
||||
#define __hidden __attribute__((visibility("hidden")))
|
||||
#else
|
||||
#define __hidden
|
||||
#endif
|
||||
|
||||
#if defined(__alpha__)
|
||||
/* Suggested by Richard Henderson. This will result in code like
|
||||
ldah $0,__op_param1($29) !gprelhigh
|
||||
lda $0,__op_param1($0) !gprellow
|
||||
We can then conveniently change $29 to $31 and adapt the offsets to
|
||||
emit the appropriate constant. */
|
||||
extern int __op_param1 __hidden;
|
||||
extern int __op_param2 __hidden;
|
||||
extern int __op_param3 __hidden;
|
||||
#define PARAM1 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param1)); _r; })
|
||||
#define PARAM2 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param2)); _r; })
|
||||
#define PARAM3 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param3)); _r; })
|
||||
#else
|
||||
#if defined(__APPLE__)
|
||||
static int __op_param1, __op_param2, __op_param3;
|
||||
#else
|
||||
extern int __op_param1, __op_param2, __op_param3;
|
||||
#endif
|
||||
#define PARAM1 ((long)(&__op_param1))
|
||||
#define PARAM2 ((long)(&__op_param2))
|
||||
#define PARAM3 ((long)(&__op_param3))
|
||||
#endif /* !defined(__alpha__) */
|
||||
|
||||
extern int __op_jmp0, __op_jmp1, __op_jmp2, __op_jmp3;
|
||||
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
#define ASM_NAME(x) "_" #x
|
||||
#else
|
||||
#define ASM_NAME(x) #x
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
#define EXIT_TB() asm volatile ("ret")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("jmp " ASM_NAME(__op_gen_label) #n)
|
||||
#endif
|
||||
#ifdef __x86_64__
|
||||
#define EXIT_TB() asm volatile ("ret")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("jmp " ASM_NAME(__op_gen_label) #n)
|
||||
#endif
|
||||
#ifdef __powerpc__
|
||||
#define EXIT_TB() asm volatile ("blr")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n)
|
||||
#endif
|
||||
#ifdef __s390__
|
||||
#define EXIT_TB() asm volatile ("br %r14")
|
||||
#endif
|
||||
#ifdef __alpha__
|
||||
#define EXIT_TB() asm volatile ("ret")
|
||||
#endif
|
||||
#ifdef __ia64__
|
||||
#define EXIT_TB() asm volatile ("br.ret.sptk.many b0;;")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("br.sptk.many " \
|
||||
ASM_NAME(__op_gen_label) #n)
|
||||
#endif
|
||||
#ifdef __sparc__
|
||||
#define EXIT_TB() asm volatile ("jmpl %i0 + 8, %g0\n" \
|
||||
"nop")
|
||||
#endif
|
||||
#ifdef __arm__
|
||||
#define EXIT_TB() asm volatile ("b exec_loop")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n)
|
||||
#endif
|
||||
#ifdef __mc68000
|
||||
#define EXIT_TB() asm volatile ("rts")
|
||||
#endif
|
||||
|
||||
#endif /* !defined(__DYNGEN_EXEC_H__) */
|
||||
@@ -0,0 +1,9 @@
|
||||
static inline int gen_new_label(void)
|
||||
{
|
||||
return nb_gen_labels++;
|
||||
}
|
||||
|
||||
static inline void gen_set_label(int n)
|
||||
{
|
||||
gen_labels[n] = gen_opc_ptr - gen_opc_buf;
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
* dyngen helpers
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
int __op_param1, __op_param2, __op_param3;
|
||||
int __op_gen_label1, __op_gen_label2, __op_gen_label3;
|
||||
int __op_jmp0, __op_jmp1, __op_jmp2, __op_jmp3;
|
||||
|
||||
#ifdef __i386__
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __x86_64__
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __s390__
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __ia64__
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
while (start < stop) {
|
||||
asm volatile ("fc %0" :: "r"(start));
|
||||
start += 32;
|
||||
}
|
||||
asm volatile (";;sync.i;;srlz.i;;");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __powerpc__
|
||||
|
||||
#define MIN_CACHE_LINE_SIZE 8 /* conservative value */
|
||||
|
||||
static void inline flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
unsigned long p;
|
||||
|
||||
p = start & ~(MIN_CACHE_LINE_SIZE - 1);
|
||||
stop = (stop + MIN_CACHE_LINE_SIZE - 1) & ~(MIN_CACHE_LINE_SIZE - 1);
|
||||
|
||||
for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
|
||||
asm volatile ("dcbst 0,%0" : : "r"(p) : "memory");
|
||||
}
|
||||
asm volatile ("sync" : : : "memory");
|
||||
for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
|
||||
asm volatile ("icbi 0,%0" : : "r"(p) : "memory");
|
||||
}
|
||||
asm volatile ("sync" : : : "memory");
|
||||
asm volatile ("isync" : : : "memory");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __alpha__
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
asm ("imb");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __sparc__
|
||||
|
||||
static void inline flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
unsigned long p;
|
||||
|
||||
p = start & ~(8UL - 1UL);
|
||||
stop = (stop + (8UL - 1UL)) & ~(8UL - 1UL);
|
||||
|
||||
for (; p < stop; p += 8)
|
||||
__asm__ __volatile__("flush\t%0" : : "r" (p));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __arm__
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
register unsigned long _beg __asm ("a1") = start;
|
||||
register unsigned long _end __asm ("a2") = stop;
|
||||
register unsigned long _flg __asm ("a3") = 0;
|
||||
__asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __mc68000
|
||||
#include <asm/cachectl.h>
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
cacheflush(start,FLUSH_SCOPE_LINE,FLUSH_CACHE_BOTH,stop-start+16);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __alpha__
|
||||
|
||||
register int gp asm("$29");
|
||||
|
||||
static inline void immediate_ldah(void *p, int val) {
|
||||
uint32_t *dest = p;
|
||||
long high = ((val >> 16) + ((val >> 15) & 1)) & 0xffff;
|
||||
|
||||
*dest &= ~0xffff;
|
||||
*dest |= high;
|
||||
*dest |= 31 << 16;
|
||||
}
|
||||
static inline void immediate_lda(void *dest, int val) {
|
||||
*(uint16_t *) dest = val;
|
||||
}
|
||||
void fix_bsr(void *p, int offset) {
|
||||
uint32_t *dest = p;
|
||||
*dest &= ~((1 << 21) - 1);
|
||||
*dest |= (offset >> 2) & ((1 << 21) - 1);
|
||||
}
|
||||
|
||||
#endif /* __alpha__ */
|
||||
|
||||
#ifdef __arm__
|
||||
|
||||
#define MAX_OP_SIZE (128 * 4) /* in bytes */
|
||||
/* max size of the code that can be generated without calling arm_flush_ldr */
|
||||
#define MAX_FRAG_SIZE (1024 * 4)
|
||||
//#define MAX_FRAG_SIZE (135 * 4) /* for testing */
|
||||
|
||||
typedef struct LDREntry {
|
||||
uint8_t *ptr;
|
||||
uint32_t *data_ptr;
|
||||
} LDREntry;
|
||||
|
||||
static LDREntry arm_ldr_table[1024];
|
||||
static uint32_t arm_data_table[1024];
|
||||
|
||||
extern char exec_loop;
|
||||
|
||||
static inline void arm_reloc_pc24(uint32_t *ptr, uint32_t insn, int val)
|
||||
{
|
||||
*ptr = (insn & ~0xffffff) | ((insn + ((val - (int)ptr) >> 2)) & 0xffffff);
|
||||
}
|
||||
|
||||
static uint8_t *arm_flush_ldr(uint8_t *gen_code_ptr,
|
||||
LDREntry *ldr_start, LDREntry *ldr_end,
|
||||
uint32_t *data_start, uint32_t *data_end,
|
||||
int gen_jmp)
|
||||
{
|
||||
LDREntry *le;
|
||||
uint32_t *ptr;
|
||||
int offset, data_size, target;
|
||||
uint8_t *data_ptr;
|
||||
uint32_t insn;
|
||||
|
||||
data_size = (uint8_t *)data_end - (uint8_t *)data_start;
|
||||
|
||||
if (gen_jmp) {
|
||||
/* generate branch to skip the data */
|
||||
if (data_size == 0)
|
||||
return gen_code_ptr;
|
||||
target = (long)gen_code_ptr + data_size + 4;
|
||||
arm_reloc_pc24((uint32_t *)gen_code_ptr, 0xeafffffe, target);
|
||||
gen_code_ptr += 4;
|
||||
}
|
||||
|
||||
/* copy the data */
|
||||
data_ptr = gen_code_ptr;
|
||||
memcpy(gen_code_ptr, data_start, data_size);
|
||||
gen_code_ptr += data_size;
|
||||
|
||||
/* patch the ldr to point to the data */
|
||||
for(le = ldr_start; le < ldr_end; le++) {
|
||||
ptr = (uint32_t *)le->ptr;
|
||||
offset = ((unsigned long)(le->data_ptr) - (unsigned long)data_start) +
|
||||
(unsigned long)data_ptr -
|
||||
(unsigned long)ptr - 8;
|
||||
insn = *ptr & ~(0xfff | 0x00800000);
|
||||
if (offset < 0) {
|
||||
offset = - offset;
|
||||
} else {
|
||||
insn |= 0x00800000;
|
||||
}
|
||||
if (offset > 0xfff) {
|
||||
fprintf(stderr, "Error ldr offset\n");
|
||||
abort();
|
||||
}
|
||||
insn |= offset;
|
||||
*ptr = insn;
|
||||
}
|
||||
return gen_code_ptr;
|
||||
}
|
||||
|
||||
#endif /* __arm__ */
|
||||
|
||||
#ifdef __ia64
|
||||
|
||||
|
||||
/* Patch instruction with "val" where "mask" has 1 bits. */
|
||||
static inline void ia64_patch (uint64_t insn_addr, uint64_t mask, uint64_t val)
|
||||
{
|
||||
uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) (insn_addr & -16);
|
||||
# define insn_mask ((1UL << 41) - 1)
|
||||
unsigned long shift;
|
||||
|
||||
b0 = b[0]; b1 = b[1];
|
||||
shift = 5 + 41 * (insn_addr % 16); /* 5 template, 3 x 41-bit insns */
|
||||
if (shift >= 64) {
|
||||
m1 = mask << (shift - 64);
|
||||
v1 = val << (shift - 64);
|
||||
} else {
|
||||
m0 = mask << shift; m1 = mask >> (64 - shift);
|
||||
v0 = val << shift; v1 = val >> (64 - shift);
|
||||
b[0] = (b0 & ~m0) | (v0 & m0);
|
||||
}
|
||||
b[1] = (b1 & ~m1) | (v1 & m1);
|
||||
}
|
||||
|
||||
static inline void ia64_patch_imm60 (uint64_t insn_addr, uint64_t val)
|
||||
{
|
||||
ia64_patch(insn_addr,
|
||||
0x011ffffe000UL,
|
||||
( ((val & 0x0800000000000000UL) >> 23) /* bit 59 -> 36 */
|
||||
| ((val & 0x00000000000fffffUL) << 13) /* bit 0 -> 13 */));
|
||||
ia64_patch(insn_addr - 1, 0x1fffffffffcUL, val >> 18);
|
||||
}
|
||||
|
||||
static inline void ia64_imm64 (void *insn, uint64_t val)
|
||||
{
|
||||
/* Ignore the slot number of the relocation; GCC and Intel
|
||||
toolchains differed for some time on whether IMM64 relocs are
|
||||
against slot 1 (Intel) or slot 2 (GCC). */
|
||||
uint64_t insn_addr = (uint64_t) insn & ~3UL;
|
||||
|
||||
ia64_patch(insn_addr + 2,
|
||||
0x01fffefe000UL,
|
||||
( ((val & 0x8000000000000000UL) >> 27) /* bit 63 -> 36 */
|
||||
| ((val & 0x0000000000200000UL) << 0) /* bit 21 -> 21 */
|
||||
| ((val & 0x00000000001f0000UL) << 6) /* bit 16 -> 22 */
|
||||
| ((val & 0x000000000000ff80UL) << 20) /* bit 7 -> 27 */
|
||||
| ((val & 0x000000000000007fUL) << 13) /* bit 0 -> 13 */)
|
||||
);
|
||||
ia64_patch(insn_addr + 1, 0x1ffffffffffUL, val >> 22);
|
||||
}
|
||||
|
||||
static inline void ia64_imm60b (void *insn, uint64_t val)
|
||||
{
|
||||
/* Ignore the slot number of the relocation; GCC and Intel
|
||||
toolchains differed for some time on whether IMM64 relocs are
|
||||
against slot 1 (Intel) or slot 2 (GCC). */
|
||||
uint64_t insn_addr = (uint64_t) insn & ~3UL;
|
||||
|
||||
if (val + ((uint64_t) 1 << 59) >= (1UL << 60))
|
||||
fprintf(stderr, "%s: value %ld out of IMM60 range\n",
|
||||
__FUNCTION__, (int64_t) val);
|
||||
ia64_patch_imm60(insn_addr + 2, val);
|
||||
}
|
||||
|
||||
static inline void ia64_imm22 (void *insn, uint64_t val)
|
||||
{
|
||||
if (val + (1 << 21) >= (1 << 22))
|
||||
fprintf(stderr, "%s: value %li out of IMM22 range\n",
|
||||
__FUNCTION__, (int64_t)val);
|
||||
ia64_patch((uint64_t) insn, 0x01fffcfe000UL,
|
||||
( ((val & 0x200000UL) << 15) /* bit 21 -> 36 */
|
||||
| ((val & 0x1f0000UL) << 6) /* bit 16 -> 22 */
|
||||
| ((val & 0x00ff80UL) << 20) /* bit 7 -> 27 */
|
||||
| ((val & 0x00007fUL) << 13) /* bit 0 -> 13 */));
|
||||
}
|
||||
|
||||
/* Like ia64_imm22(), but also clear bits 20-21. For addl, this has
|
||||
the effect of turning "addl rX=imm22,rY" into "addl
|
||||
rX=imm22,r0". */
|
||||
static inline void ia64_imm22_r0 (void *insn, uint64_t val)
|
||||
{
|
||||
if (val + (1 << 21) >= (1 << 22))
|
||||
fprintf(stderr, "%s: value %li out of IMM22 range\n",
|
||||
__FUNCTION__, (int64_t)val);
|
||||
ia64_patch((uint64_t) insn, 0x01fffcfe000UL | (0x3UL << 20),
|
||||
( ((val & 0x200000UL) << 15) /* bit 21 -> 36 */
|
||||
| ((val & 0x1f0000UL) << 6) /* bit 16 -> 22 */
|
||||
| ((val & 0x00ff80UL) << 20) /* bit 7 -> 27 */
|
||||
| ((val & 0x00007fUL) << 13) /* bit 0 -> 13 */));
|
||||
}
|
||||
|
||||
static inline void ia64_imm21b (void *insn, uint64_t val)
|
||||
{
|
||||
if (val + (1 << 20) >= (1 << 21))
|
||||
fprintf(stderr, "%s: value %li out of IMM21b range\n",
|
||||
__FUNCTION__, (int64_t)val);
|
||||
ia64_patch((uint64_t) insn, 0x11ffffe000UL,
|
||||
( ((val & 0x100000UL) << 16) /* bit 20 -> 36 */
|
||||
| ((val & 0x0fffffUL) << 13) /* bit 0 -> 13 */));
|
||||
}
|
||||
|
||||
static inline void ia64_nop_b (void *insn)
|
||||
{
|
||||
ia64_patch((uint64_t) insn, (1UL << 41) - 1, 2UL << 37);
|
||||
}
|
||||
|
||||
static inline void ia64_ldxmov(void *insn, uint64_t val)
|
||||
{
|
||||
if (val + (1 << 21) < (1 << 22))
|
||||
ia64_patch((uint64_t) insn, 0x1fff80fe000UL, 8UL << 37);
|
||||
}
|
||||
|
||||
static inline int ia64_patch_ltoff(void *insn, uint64_t val,
|
||||
int relaxable)
|
||||
{
|
||||
if (relaxable && (val + (1 << 21) < (1 << 22))) {
|
||||
ia64_imm22_r0(insn, val);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ia64_fixup {
|
||||
struct ia64_fixup *next;
|
||||
void *addr; /* address that needs to be patched */
|
||||
long value;
|
||||
};
|
||||
|
||||
#define IA64_PLT(insn, plt_index) \
|
||||
do { \
|
||||
struct ia64_fixup *fixup = alloca(sizeof(*fixup)); \
|
||||
fixup->next = plt_fixes; \
|
||||
plt_fixes = fixup; \
|
||||
fixup->addr = (insn); \
|
||||
fixup->value = (plt_index); \
|
||||
plt_offset[(plt_index)] = 1; \
|
||||
} while (0)
|
||||
|
||||
#define IA64_LTOFF(insn, val, relaxable) \
|
||||
do { \
|
||||
if (ia64_patch_ltoff(insn, val, relaxable)) { \
|
||||
struct ia64_fixup *fixup = alloca(sizeof(*fixup)); \
|
||||
fixup->next = ltoff_fixes; \
|
||||
ltoff_fixes = fixup; \
|
||||
fixup->addr = (insn); \
|
||||
fixup->value = (val); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static inline void ia64_apply_fixes (uint8_t **gen_code_pp,
|
||||
struct ia64_fixup *ltoff_fixes,
|
||||
uint64_t gp,
|
||||
struct ia64_fixup *plt_fixes,
|
||||
int num_plts,
|
||||
unsigned long *plt_target,
|
||||
unsigned int *plt_offset)
|
||||
{
|
||||
static const uint8_t plt_bundle[] = {
|
||||
0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, /* nop 0; movl r1=GP */
|
||||
0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x60,
|
||||
|
||||
0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, /* nop 0; brl IP */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0
|
||||
};
|
||||
uint8_t *gen_code_ptr = *gen_code_pp, *plt_start, *got_start, *vp;
|
||||
struct ia64_fixup *fixup;
|
||||
unsigned int offset = 0;
|
||||
struct fdesc {
|
||||
long ip;
|
||||
long gp;
|
||||
} *fdesc;
|
||||
int i;
|
||||
|
||||
if (plt_fixes) {
|
||||
plt_start = gen_code_ptr;
|
||||
|
||||
for (i = 0; i < num_plts; ++i) {
|
||||
if (plt_offset[i]) {
|
||||
plt_offset[i] = offset;
|
||||
offset += sizeof(plt_bundle);
|
||||
|
||||
fdesc = (struct fdesc *) plt_target[i];
|
||||
memcpy(gen_code_ptr, plt_bundle, sizeof(plt_bundle));
|
||||
ia64_imm64 (gen_code_ptr + 0x02, fdesc->gp);
|
||||
ia64_imm60b(gen_code_ptr + 0x12,
|
||||
(fdesc->ip - (long) (gen_code_ptr + 0x10)) >> 4);
|
||||
gen_code_ptr += sizeof(plt_bundle);
|
||||
}
|
||||
}
|
||||
|
||||
for (fixup = plt_fixes; fixup; fixup = fixup->next)
|
||||
ia64_imm21b(fixup->addr,
|
||||
((long) plt_start + plt_offset[fixup->value]
|
||||
- ((long) fixup->addr & ~0xf)) >> 4);
|
||||
}
|
||||
|
||||
got_start = gen_code_ptr;
|
||||
|
||||
/* First, create the GOT: */
|
||||
for (fixup = ltoff_fixes; fixup; fixup = fixup->next) {
|
||||
/* first check if we already have this value in the GOT: */
|
||||
for (vp = got_start; vp < gen_code_ptr; ++vp)
|
||||
if (*(uint64_t *) vp == fixup->value)
|
||||
break;
|
||||
if (vp == gen_code_ptr) {
|
||||
/* Nope, we need to put the value in the GOT: */
|
||||
*(uint64_t *) vp = fixup->value;
|
||||
gen_code_ptr += 8;
|
||||
}
|
||||
ia64_imm22(fixup->addr, (long) vp - gp);
|
||||
}
|
||||
*gen_code_pp = gen_code_ptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
+619
@@ -0,0 +1,619 @@
|
||||
/*
|
||||
* internal execution defines for qemu
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* allow to see translation results - the slowdown should be negligible, so we leave it */
|
||||
#define DEBUG_DISAS
|
||||
|
||||
#ifndef glue
|
||||
#define xglue(x, y) x ## y
|
||||
#define glue(x, y) xglue(x, y)
|
||||
#define stringify(s) tostring(s)
|
||||
#define tostring(s) #s
|
||||
#endif
|
||||
|
||||
#if GCC_MAJOR < 3
|
||||
#define __builtin_expect(x, n) (x)
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
#define REGPARM(n) __attribute((regparm(n)))
|
||||
#else
|
||||
#define REGPARM(n)
|
||||
#endif
|
||||
|
||||
/* is_jmp field values */
|
||||
#define DISAS_NEXT 0 /* next instruction can be analyzed */
|
||||
#define DISAS_JUMP 1 /* only pc was modified dynamically */
|
||||
#define DISAS_UPDATE 2 /* cpu state was modified dynamically */
|
||||
#define DISAS_TB_JUMP 3 /* only pc was modified statically */
|
||||
|
||||
struct TranslationBlock;
|
||||
|
||||
/* XXX: make safe guess about sizes */
|
||||
#define MAX_OP_PER_INSTR 32
|
||||
#define OPC_BUF_SIZE 512
|
||||
#define OPC_MAX_SIZE (OPC_BUF_SIZE - MAX_OP_PER_INSTR)
|
||||
|
||||
#define OPPARAM_BUF_SIZE (OPC_BUF_SIZE * 3)
|
||||
|
||||
extern uint16_t gen_opc_buf[OPC_BUF_SIZE];
|
||||
extern uint32_t gen_opparam_buf[OPPARAM_BUF_SIZE];
|
||||
extern long gen_labels[OPC_BUF_SIZE];
|
||||
extern int nb_gen_labels;
|
||||
extern target_ulong gen_opc_pc[OPC_BUF_SIZE];
|
||||
extern target_ulong gen_opc_npc[OPC_BUF_SIZE];
|
||||
extern uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
|
||||
extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
|
||||
extern target_ulong gen_opc_jump_pc[2];
|
||||
|
||||
typedef void (GenOpFunc)(void);
|
||||
typedef void (GenOpFunc1)(long);
|
||||
typedef void (GenOpFunc2)(long, long);
|
||||
typedef void (GenOpFunc3)(long, long, long);
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
|
||||
void optimize_flags_init(void);
|
||||
|
||||
#endif
|
||||
|
||||
extern FILE *logfile;
|
||||
extern int loglevel;
|
||||
|
||||
int gen_intermediate_code(CPUState *env, struct TranslationBlock *tb);
|
||||
int gen_intermediate_code_pc(CPUState *env, struct TranslationBlock *tb);
|
||||
void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf);
|
||||
int cpu_gen_code(CPUState *env, struct TranslationBlock *tb,
|
||||
int max_code_size, int *gen_code_size_ptr);
|
||||
int cpu_restore_state(struct TranslationBlock *tb,
|
||||
CPUState *env, unsigned long searched_pc,
|
||||
void *puc);
|
||||
int cpu_gen_code_copy(CPUState *env, struct TranslationBlock *tb,
|
||||
int max_code_size, int *gen_code_size_ptr);
|
||||
int cpu_restore_state_copy(struct TranslationBlock *tb,
|
||||
CPUState *env, unsigned long searched_pc,
|
||||
void *puc);
|
||||
void cpu_resume_from_signal(CPUState *env1, void *puc);
|
||||
void cpu_exec_init(void);
|
||||
int page_unprotect(unsigned long address, unsigned long pc, void *puc);
|
||||
void tb_invalidate_phys_page_range(target_ulong start, target_ulong end,
|
||||
int is_cpu_write_access);
|
||||
void tb_invalidate_page_range(target_ulong start, target_ulong end);
|
||||
void tlb_flush_page(CPUState *env, target_ulong addr);
|
||||
void tlb_flush(CPUState *env, int flush_global);
|
||||
int tlb_set_page(CPUState *env, target_ulong vaddr,
|
||||
target_phys_addr_t paddr, int prot,
|
||||
int is_user, int is_softmmu);
|
||||
|
||||
#define CODE_GEN_MAX_SIZE 65536
|
||||
#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
|
||||
|
||||
#define CODE_GEN_HASH_BITS 15
|
||||
#define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS)
|
||||
|
||||
#define CODE_GEN_PHYS_HASH_BITS 15
|
||||
#define CODE_GEN_PHYS_HASH_SIZE (1 << CODE_GEN_PHYS_HASH_BITS)
|
||||
|
||||
/* maximum total translate dcode allocated */
|
||||
|
||||
/* NOTE: the translated code area cannot be too big because on some
|
||||
archs the range of "fast" function calls is limited. Here is a
|
||||
summary of the ranges:
|
||||
|
||||
i386 : signed 32 bits
|
||||
arm : signed 26 bits
|
||||
ppc : signed 24 bits
|
||||
sparc : signed 32 bits
|
||||
alpha : signed 23 bits
|
||||
*/
|
||||
|
||||
#if defined(__alpha__)
|
||||
#define CODE_GEN_BUFFER_SIZE (2 * 1024 * 1024)
|
||||
#elif defined(__ia64)
|
||||
#define CODE_GEN_BUFFER_SIZE (4 * 1024 * 1024) /* range of addl */
|
||||
#elif defined(__powerpc__)
|
||||
#define CODE_GEN_BUFFER_SIZE (6 * 1024 * 1024)
|
||||
#else
|
||||
#define CODE_GEN_BUFFER_SIZE (8 * 1024 * 1024)
|
||||
#endif
|
||||
|
||||
//#define CODE_GEN_BUFFER_SIZE (128 * 1024)
|
||||
|
||||
/* estimated block size for TB allocation */
|
||||
/* XXX: use a per code average code fragment size and modulate it
|
||||
according to the host CPU */
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
#define CODE_GEN_AVG_BLOCK_SIZE 128
|
||||
#else
|
||||
#define CODE_GEN_AVG_BLOCK_SIZE 64
|
||||
#endif
|
||||
|
||||
#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / CODE_GEN_AVG_BLOCK_SIZE)
|
||||
|
||||
#if defined(__powerpc__)
|
||||
#define USE_DIRECT_JUMP
|
||||
#endif
|
||||
#if defined(__i386__) && !defined(_WIN32)
|
||||
#define USE_DIRECT_JUMP
|
||||
#endif
|
||||
|
||||
typedef struct TranslationBlock {
|
||||
target_ulong pc; /* simulated PC corresponding to this block (EIP + CS base) */
|
||||
target_ulong cs_base; /* CS base for this block */
|
||||
unsigned int flags; /* flags defining in which context the code was generated */
|
||||
uint16_t size; /* size of target code for this block (1 <=
|
||||
size <= TARGET_PAGE_SIZE) */
|
||||
uint16_t cflags; /* compile flags */
|
||||
#define CF_CODE_COPY 0x0001 /* block was generated in code copy mode */
|
||||
#define CF_TB_FP_USED 0x0002 /* fp ops are used in the TB */
|
||||
#define CF_FP_USED 0x0004 /* fp ops are used in the TB or in a chained TB */
|
||||
#define CF_SINGLE_INSN 0x0008 /* compile only a single instruction */
|
||||
|
||||
uint8_t *tc_ptr; /* pointer to the translated code */
|
||||
struct TranslationBlock *hash_next; /* next matching tb for virtual address */
|
||||
/* next matching tb for physical address. */
|
||||
struct TranslationBlock *phys_hash_next;
|
||||
/* first and second physical page containing code. The lower bit
|
||||
of the pointer tells the index in page_next[] */
|
||||
struct TranslationBlock *page_next[2];
|
||||
target_ulong page_addr[2];
|
||||
|
||||
/* the following data are used to directly call another TB from
|
||||
the code of this one. */
|
||||
uint16_t tb_next_offset[2]; /* offset of original jump target */
|
||||
#ifdef USE_DIRECT_JUMP
|
||||
uint16_t tb_jmp_offset[4]; /* offset of jump instruction */
|
||||
#else
|
||||
uint32_t tb_next[2]; /* address of jump generated code */
|
||||
#endif
|
||||
/* list of TBs jumping to this one. This is a circular list using
|
||||
the two least significant bits of the pointers to tell what is
|
||||
the next pointer: 0 = jmp_next[0], 1 = jmp_next[1], 2 =
|
||||
jmp_first */
|
||||
struct TranslationBlock *jmp_next[2];
|
||||
struct TranslationBlock *jmp_first;
|
||||
} TranslationBlock;
|
||||
|
||||
static inline unsigned int tb_hash_func(target_ulong pc)
|
||||
{
|
||||
return pc & (CODE_GEN_HASH_SIZE - 1);
|
||||
}
|
||||
|
||||
static inline unsigned int tb_phys_hash_func(unsigned long pc)
|
||||
{
|
||||
return pc & (CODE_GEN_PHYS_HASH_SIZE - 1);
|
||||
}
|
||||
|
||||
TranslationBlock *tb_alloc(target_ulong pc);
|
||||
void tb_flush(CPUState *env);
|
||||
void tb_link(TranslationBlock *tb);
|
||||
void tb_link_phys(TranslationBlock *tb,
|
||||
target_ulong phys_pc, target_ulong phys_page2);
|
||||
|
||||
extern TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE];
|
||||
extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
|
||||
|
||||
extern uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
|
||||
extern uint8_t *code_gen_ptr;
|
||||
|
||||
/* find a translation block in the translation cache. If not found,
|
||||
return NULL and the pointer to the last element of the list in pptb */
|
||||
static inline TranslationBlock *tb_find(TranslationBlock ***pptb,
|
||||
target_ulong pc,
|
||||
target_ulong cs_base,
|
||||
unsigned int flags)
|
||||
{
|
||||
TranslationBlock **ptb, *tb;
|
||||
unsigned int h;
|
||||
|
||||
h = tb_hash_func(pc);
|
||||
ptb = &tb_hash[h];
|
||||
for(;;) {
|
||||
tb = *ptb;
|
||||
if (!tb)
|
||||
break;
|
||||
if (tb->pc == pc && tb->cs_base == cs_base && tb->flags == flags)
|
||||
return tb;
|
||||
ptb = &tb->hash_next;
|
||||
}
|
||||
*pptb = ptb;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
#if defined(USE_DIRECT_JUMP)
|
||||
|
||||
#if defined(__powerpc__)
|
||||
static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
|
||||
{
|
||||
uint32_t val, *ptr;
|
||||
|
||||
/* patch the branch destination */
|
||||
ptr = (uint32_t *)jmp_addr;
|
||||
val = *ptr;
|
||||
val = (val & ~0x03fffffc) | ((addr - jmp_addr) & 0x03fffffc);
|
||||
*ptr = val;
|
||||
/* flush icache */
|
||||
asm volatile ("dcbst 0,%0" : : "r"(ptr) : "memory");
|
||||
asm volatile ("sync" : : : "memory");
|
||||
asm volatile ("icbi 0,%0" : : "r"(ptr) : "memory");
|
||||
asm volatile ("sync" : : : "memory");
|
||||
asm volatile ("isync" : : : "memory");
|
||||
}
|
||||
#elif defined(__i386__)
|
||||
static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
|
||||
{
|
||||
/* patch the branch destination */
|
||||
*(uint32_t *)jmp_addr = addr - (jmp_addr + 4);
|
||||
/* no need to flush icache explicitely */
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void tb_set_jmp_target(TranslationBlock *tb,
|
||||
int n, unsigned long addr)
|
||||
{
|
||||
unsigned long offset;
|
||||
|
||||
offset = tb->tb_jmp_offset[n];
|
||||
tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr);
|
||||
offset = tb->tb_jmp_offset[n + 2];
|
||||
if (offset != 0xffff)
|
||||
tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* set the jump target */
|
||||
static inline void tb_set_jmp_target(TranslationBlock *tb,
|
||||
int n, unsigned long addr)
|
||||
{
|
||||
tb->tb_next[n] = addr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline void tb_add_jump(TranslationBlock *tb, int n,
|
||||
TranslationBlock *tb_next)
|
||||
{
|
||||
/* NOTE: this test is only needed for thread safety */
|
||||
if (!tb->jmp_next[n]) {
|
||||
/* patch the native jump address */
|
||||
tb_set_jmp_target(tb, n, (unsigned long)tb_next->tc_ptr);
|
||||
|
||||
/* add in TB jmp circular list */
|
||||
tb->jmp_next[n] = tb_next->jmp_first;
|
||||
tb_next->jmp_first = (TranslationBlock *)((long)(tb) | (n));
|
||||
}
|
||||
}
|
||||
|
||||
TranslationBlock *tb_find_pc(unsigned long pc_ptr);
|
||||
|
||||
#ifndef offsetof
|
||||
#define offsetof(type, field) ((size_t) &((type *)0)->field)
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define ASM_DATA_SECTION ".section \".data\"\n"
|
||||
#define ASM_PREVIOUS_SECTION ".section .text\n"
|
||||
#elif defined(__APPLE__)
|
||||
#define ASM_DATA_SECTION ".data\n"
|
||||
#define ASM_PREVIOUS_SECTION ".text\n"
|
||||
#else
|
||||
#define ASM_DATA_SECTION ".section \".data\"\n"
|
||||
#define ASM_PREVIOUS_SECTION ".previous\n"
|
||||
#endif
|
||||
|
||||
#if defined(__powerpc__)
|
||||
|
||||
/* we patch the jump instruction directly */
|
||||
#define GOTO_TB(opname, tbparam, n)\
|
||||
do {\
|
||||
asm volatile (ASM_DATA_SECTION\
|
||||
ASM_NAME(__op_label) #n "." ASM_NAME(opname) ":\n"\
|
||||
".long 1f\n"\
|
||||
ASM_PREVIOUS_SECTION \
|
||||
"b " ASM_NAME(__op_jmp) #n "\n"\
|
||||
"1:\n");\
|
||||
} while (0)
|
||||
|
||||
#elif defined(__i386__) && defined(USE_DIRECT_JUMP)
|
||||
|
||||
/* we patch the jump instruction directly */
|
||||
#define GOTO_TB(opname, tbparam, n)\
|
||||
do {\
|
||||
asm volatile (".section .data\n"\
|
||||
ASM_NAME(__op_label) #n "." ASM_NAME(opname) ":\n"\
|
||||
".long 1f\n"\
|
||||
ASM_PREVIOUS_SECTION \
|
||||
"jmp " ASM_NAME(__op_jmp) #n "\n"\
|
||||
"1:\n");\
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
/* jump to next block operations (more portable code, does not need
|
||||
cache flushing, but slower because of indirect jump) */
|
||||
#define GOTO_TB(opname, tbparam, n)\
|
||||
do {\
|
||||
static void __attribute__((unused)) *dummy ## n = &&dummy_label ## n;\
|
||||
static void __attribute__((unused)) *__op_label ## n = &&label ## n;\
|
||||
goto *(void *)(((TranslationBlock *)tbparam)->tb_next[n]);\
|
||||
label ## n: ;\
|
||||
dummy_label ## n: ;\
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
/* XXX: will be suppressed */
|
||||
#define JUMP_TB(opname, tbparam, n, eip)\
|
||||
do {\
|
||||
GOTO_TB(opname, tbparam, n);\
|
||||
T0 = (long)(tbparam) + (n);\
|
||||
EIP = (int32_t)eip;\
|
||||
EXIT_TB();\
|
||||
} while (0)
|
||||
|
||||
extern CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
|
||||
extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
|
||||
extern void *io_mem_opaque[IO_MEM_NB_ENTRIES];
|
||||
|
||||
#ifdef __powerpc__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
__asm__ __volatile__ (
|
||||
"0: lwarx %0,0,%1\n"
|
||||
" xor. %0,%3,%0\n"
|
||||
" bne 1f\n"
|
||||
" stwcx. %2,0,%1\n"
|
||||
" bne- 0b\n"
|
||||
"1: "
|
||||
: "=&r" (ret)
|
||||
: "r" (p), "r" (1), "r" (0)
|
||||
: "cr0", "memory");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
long int readval = 0;
|
||||
|
||||
__asm__ __volatile__ ("lock; cmpxchgl %2, %0"
|
||||
: "+m" (*p), "+a" (readval)
|
||||
: "r" (1)
|
||||
: "cc");
|
||||
return readval;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __x86_64__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
long int readval = 0;
|
||||
|
||||
__asm__ __volatile__ ("lock; cmpxchgl %2, %0"
|
||||
: "+m" (*p), "+a" (readval)
|
||||
: "r" (1)
|
||||
: "cc");
|
||||
return readval;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __s390__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
|
||||
__asm__ __volatile__ ("0: cs %0,%1,0(%2)\n"
|
||||
" jl 0b"
|
||||
: "=&d" (ret)
|
||||
: "r" (1), "a" (p), "0" (*p)
|
||||
: "cc", "memory" );
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __alpha__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
unsigned long one;
|
||||
|
||||
__asm__ __volatile__ ("0: mov 1,%2\n"
|
||||
" ldl_l %0,%1\n"
|
||||
" stl_c %2,%1\n"
|
||||
" beq %2,1f\n"
|
||||
".subsection 2\n"
|
||||
"1: br 0b\n"
|
||||
".previous"
|
||||
: "=r" (ret), "=m" (*p), "=r" (one)
|
||||
: "m" (*p));
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __sparc__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
|
||||
__asm__ __volatile__("ldstub [%1], %0"
|
||||
: "=r" (ret)
|
||||
: "r" (p)
|
||||
: "memory");
|
||||
|
||||
return (ret ? 1 : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __arm__
|
||||
static inline int testandset (int *spinlock)
|
||||
{
|
||||
register unsigned int ret;
|
||||
__asm__ __volatile__("swp %0, %1, [%2]"
|
||||
: "=r"(ret)
|
||||
: "0"(1), "r"(spinlock));
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __mc68000
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
char ret;
|
||||
__asm__ __volatile__("tas %1; sne %0"
|
||||
: "=r" (ret)
|
||||
: "m" (p)
|
||||
: "cc","memory");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __ia64
|
||||
#include <ia64intrin.h>
|
||||
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
return __sync_lock_test_and_set (p, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef int spinlock_t;
|
||||
|
||||
#define SPIN_LOCK_UNLOCKED 0
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
static inline void spin_lock(spinlock_t *lock)
|
||||
{
|
||||
while (testandset(lock));
|
||||
}
|
||||
|
||||
static inline void spin_unlock(spinlock_t *lock)
|
||||
{
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
static inline int spin_trylock(spinlock_t *lock)
|
||||
{
|
||||
return !testandset(lock);
|
||||
}
|
||||
#else
|
||||
static inline void spin_lock(spinlock_t *lock)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void spin_unlock(spinlock_t *lock)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int spin_trylock(spinlock_t *lock)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern spinlock_t tb_lock;
|
||||
|
||||
extern int tb_invalidated_flag;
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
void tlb_fill(target_ulong addr, int is_write, int is_user,
|
||||
void *retaddr);
|
||||
|
||||
#define ACCESS_TYPE 3
|
||||
#define MEMSUFFIX _code
|
||||
#define env cpu_single_env
|
||||
|
||||
#define DATA_SIZE 1
|
||||
#include "softmmu_header.h"
|
||||
|
||||
#define DATA_SIZE 2
|
||||
#include "softmmu_header.h"
|
||||
|
||||
#define DATA_SIZE 4
|
||||
#include "softmmu_header.h"
|
||||
|
||||
#define DATA_SIZE 8
|
||||
#include "softmmu_header.h"
|
||||
|
||||
#undef ACCESS_TYPE
|
||||
#undef MEMSUFFIX
|
||||
#undef env
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr)
|
||||
{
|
||||
return addr;
|
||||
}
|
||||
#else
|
||||
/* NOTE: this function can trigger an exception */
|
||||
/* NOTE2: the returned address is not exactly the physical address: it
|
||||
is the offset relative to phys_ram_base */
|
||||
/* XXX: i386 target specific */
|
||||
static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr)
|
||||
{
|
||||
int is_user, index, pd;
|
||||
|
||||
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
#if defined(TARGET_I386)
|
||||
is_user = ((env->hflags & HF_CPL_MASK) == 3);
|
||||
#elif defined (TARGET_PPC)
|
||||
is_user = msr_pr;
|
||||
#elif defined (TARGET_SPARC)
|
||||
is_user = (env->psrs == 0);
|
||||
#else
|
||||
#error "Unimplemented !"
|
||||
#endif
|
||||
if (__builtin_expect(env->tlb_read[is_user][index].address !=
|
||||
(addr & TARGET_PAGE_MASK), 0)) {
|
||||
ldub_code(addr);
|
||||
}
|
||||
pd = env->tlb_read[is_user][index].address & ~TARGET_PAGE_MASK;
|
||||
if (pd > IO_MEM_ROM) {
|
||||
cpu_abort(env, "Trying to execute code outside RAM or ROM at 0x%08lx\n", addr);
|
||||
}
|
||||
return addr + env->tlb_read[is_user][index].addend - (unsigned long)phys_ram_base;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef USE_KQEMU
|
||||
int kqemu_init(CPUState *env);
|
||||
int kqemu_cpu_exec(CPUState *env);
|
||||
void kqemu_flush_page(CPUState *env, target_ulong addr);
|
||||
void kqemu_flush(CPUState *env, int global);
|
||||
|
||||
static inline int kqemu_is_ok(CPUState *env)
|
||||
{
|
||||
return(env->kqemu_enabled &&
|
||||
(env->hflags & HF_CPL_MASK) == 3 &&
|
||||
(env->eflags & IOPL_MASK) != IOPL_MASK &&
|
||||
(env->cr[0] & CR0_PE_MASK) &&
|
||||
(env->eflags & IF_MASK) &&
|
||||
!(env->eflags & VM_MASK));
|
||||
}
|
||||
|
||||
#endif
|
||||
-500
@@ -1,500 +0,0 @@
|
||||
/*
|
||||
* i386 emulator main execution loop
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "exec-i386.h"
|
||||
|
||||
//#define DEBUG_EXEC
|
||||
#define DEBUG_FLUSH
|
||||
//#define DEBUG_SIGNAL
|
||||
|
||||
/* main execution loop */
|
||||
|
||||
/* maximum total translate dcode allocated */
|
||||
#define CODE_GEN_BUFFER_SIZE (2048 * 1024)
|
||||
//#define CODE_GEN_BUFFER_SIZE (128 * 1024)
|
||||
#define CODE_GEN_MAX_SIZE 65536
|
||||
#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
|
||||
|
||||
/* threshold to flush the translated code buffer */
|
||||
#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE)
|
||||
|
||||
#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64)
|
||||
#define CODE_GEN_HASH_BITS 15
|
||||
#define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS)
|
||||
|
||||
typedef struct TranslationBlock {
|
||||
unsigned long pc; /* simulated PC corresponding to this block (EIP + CS base) */
|
||||
unsigned long cs_base; /* CS base for this block */
|
||||
unsigned int flags; /* flags defining in which context the code was generated */
|
||||
uint8_t *tc_ptr; /* pointer to the translated code */
|
||||
struct TranslationBlock *hash_next; /* next matching block */
|
||||
} TranslationBlock;
|
||||
|
||||
TranslationBlock tbs[CODE_GEN_MAX_BLOCKS];
|
||||
TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE];
|
||||
int nb_tbs;
|
||||
|
||||
uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
|
||||
uint8_t *code_gen_ptr;
|
||||
|
||||
/* thread support */
|
||||
|
||||
#ifdef __powerpc__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
__asm__ __volatile__ (
|
||||
"0: lwarx %0,0,%1 ;"
|
||||
" xor. %0,%3,%0;"
|
||||
" bne 1f;"
|
||||
" stwcx. %2,0,%1;"
|
||||
" bne- 0b;"
|
||||
"1: "
|
||||
: "=&r" (ret)
|
||||
: "r" (p), "r" (1), "r" (0)
|
||||
: "cr0", "memory");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
char ret;
|
||||
long int readval;
|
||||
|
||||
__asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0"
|
||||
: "=q" (ret), "=m" (*p), "=a" (readval)
|
||||
: "r" (1), "m" (*p), "a" (0)
|
||||
: "memory");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int global_cpu_lock = 0;
|
||||
|
||||
void cpu_lock(void)
|
||||
{
|
||||
while (testandset(&global_cpu_lock));
|
||||
}
|
||||
|
||||
void cpu_unlock(void)
|
||||
{
|
||||
global_cpu_lock = 0;
|
||||
}
|
||||
|
||||
/* exception support */
|
||||
/* NOTE: not static to force relocation generation by GCC */
|
||||
void raise_exception(int exception_index)
|
||||
{
|
||||
/* NOTE: the register at this point must be saved by hand because
|
||||
longjmp restore them */
|
||||
#ifdef reg_EAX
|
||||
env->regs[R_EAX] = EAX;
|
||||
#endif
|
||||
#ifdef reg_ECX
|
||||
env->regs[R_ECX] = ECX;
|
||||
#endif
|
||||
#ifdef reg_EDX
|
||||
env->regs[R_EDX] = EDX;
|
||||
#endif
|
||||
#ifdef reg_EBX
|
||||
env->regs[R_EBX] = EBX;
|
||||
#endif
|
||||
#ifdef reg_ESP
|
||||
env->regs[R_ESP] = ESP;
|
||||
#endif
|
||||
#ifdef reg_EBP
|
||||
env->regs[R_EBP] = EBP;
|
||||
#endif
|
||||
#ifdef reg_ESI
|
||||
env->regs[R_ESI] = ESI;
|
||||
#endif
|
||||
#ifdef reg_EDI
|
||||
env->regs[R_EDI] = EDI;
|
||||
#endif
|
||||
env->exception_index = exception_index;
|
||||
longjmp(env->jmp_env, 1);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_EXEC)
|
||||
static const char *cc_op_str[] = {
|
||||
"DYNAMIC",
|
||||
"EFLAGS",
|
||||
"MUL",
|
||||
"ADDB",
|
||||
"ADDW",
|
||||
"ADDL",
|
||||
"ADCB",
|
||||
"ADCW",
|
||||
"ADCL",
|
||||
"SUBB",
|
||||
"SUBW",
|
||||
"SUBL",
|
||||
"SBBB",
|
||||
"SBBW",
|
||||
"SBBL",
|
||||
"LOGICB",
|
||||
"LOGICW",
|
||||
"LOGICL",
|
||||
"INCB",
|
||||
"INCW",
|
||||
"INCL",
|
||||
"DECB",
|
||||
"DECW",
|
||||
"DECL",
|
||||
"SHLB",
|
||||
"SHLW",
|
||||
"SHLL",
|
||||
"SARB",
|
||||
"SARW",
|
||||
"SARL",
|
||||
};
|
||||
|
||||
static void cpu_x86_dump_state(FILE *f)
|
||||
{
|
||||
int eflags;
|
||||
eflags = cc_table[CC_OP].compute_all();
|
||||
eflags |= (DF & DIRECTION_FLAG);
|
||||
fprintf(f,
|
||||
"EAX=%08x EBX=%08X ECX=%08x EDX=%08x\n"
|
||||
"ESI=%08x EDI=%08X EBP=%08x ESP=%08x\n"
|
||||
"CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n"
|
||||
"EIP=%08x\n",
|
||||
env->regs[R_EAX], env->regs[R_EBX], env->regs[R_ECX], env->regs[R_EDX],
|
||||
env->regs[R_ESI], env->regs[R_EDI], env->regs[R_EBP], env->regs[R_ESP],
|
||||
env->cc_src, env->cc_dst, cc_op_str[env->cc_op],
|
||||
eflags & DIRECTION_FLAG ? 'D' : '-',
|
||||
eflags & CC_O ? 'O' : '-',
|
||||
eflags & CC_S ? 'S' : '-',
|
||||
eflags & CC_Z ? 'Z' : '-',
|
||||
eflags & CC_A ? 'A' : '-',
|
||||
eflags & CC_P ? 'P' : '-',
|
||||
eflags & CC_C ? 'C' : '-',
|
||||
env->eip);
|
||||
#if 1
|
||||
fprintf(f, "ST0=%f ST1=%f ST2=%f ST3=%f\n",
|
||||
(double)ST0, (double)ST1, (double)ST(2), (double)ST(3));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void cpu_x86_tblocks_init(void)
|
||||
{
|
||||
if (!code_gen_ptr) {
|
||||
code_gen_ptr = code_gen_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/* flush all the translation blocks */
|
||||
static void tb_flush(void)
|
||||
{
|
||||
int i;
|
||||
#ifdef DEBUG_FLUSH
|
||||
printf("gemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n",
|
||||
code_gen_ptr - code_gen_buffer,
|
||||
nb_tbs,
|
||||
(code_gen_ptr - code_gen_buffer) / nb_tbs);
|
||||
#endif
|
||||
nb_tbs = 0;
|
||||
for(i = 0;i < CODE_GEN_HASH_SIZE; i++)
|
||||
tb_hash[i] = NULL;
|
||||
code_gen_ptr = code_gen_buffer;
|
||||
/* XXX: flush processor icache at this point */
|
||||
}
|
||||
|
||||
/* find a translation block in the translation cache. If not found,
|
||||
return NULL and the pointer to the last element of the list in pptb */
|
||||
static inline TranslationBlock *tb_find(TranslationBlock ***pptb,
|
||||
unsigned long pc,
|
||||
unsigned long cs_base,
|
||||
unsigned int flags)
|
||||
{
|
||||
TranslationBlock **ptb, *tb;
|
||||
unsigned int h;
|
||||
|
||||
h = pc & (CODE_GEN_HASH_SIZE - 1);
|
||||
ptb = &tb_hash[h];
|
||||
for(;;) {
|
||||
tb = *ptb;
|
||||
if (!tb)
|
||||
break;
|
||||
if (tb->pc == pc && tb->cs_base == cs_base && tb->flags == flags)
|
||||
return tb;
|
||||
ptb = &tb->hash_next;
|
||||
}
|
||||
*pptb = ptb;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* allocate a new translation block. flush the translation buffer if
|
||||
too many translation blocks or too much generated code */
|
||||
static inline TranslationBlock *tb_alloc(void)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
if (nb_tbs >= CODE_GEN_MAX_BLOCKS ||
|
||||
(code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE)
|
||||
tb_flush();
|
||||
tb = &tbs[nb_tbs++];
|
||||
return tb;
|
||||
}
|
||||
|
||||
int cpu_x86_exec(CPUX86State *env1)
|
||||
{
|
||||
int saved_T0, saved_T1, saved_A0;
|
||||
CPUX86State *saved_env;
|
||||
#ifdef reg_EAX
|
||||
int saved_EAX;
|
||||
#endif
|
||||
#ifdef reg_ECX
|
||||
int saved_ECX;
|
||||
#endif
|
||||
#ifdef reg_EDX
|
||||
int saved_EDX;
|
||||
#endif
|
||||
#ifdef reg_EBX
|
||||
int saved_EBX;
|
||||
#endif
|
||||
#ifdef reg_ESP
|
||||
int saved_ESP;
|
||||
#endif
|
||||
#ifdef reg_EBP
|
||||
int saved_EBP;
|
||||
#endif
|
||||
#ifdef reg_ESI
|
||||
int saved_ESI;
|
||||
#endif
|
||||
#ifdef reg_EDI
|
||||
int saved_EDI;
|
||||
#endif
|
||||
int code_gen_size, ret;
|
||||
void (*gen_func)(void);
|
||||
TranslationBlock *tb, **ptb;
|
||||
uint8_t *tc_ptr, *cs_base, *pc;
|
||||
unsigned int flags;
|
||||
|
||||
/* first we save global registers */
|
||||
saved_T0 = T0;
|
||||
saved_T1 = T1;
|
||||
saved_A0 = A0;
|
||||
saved_env = env;
|
||||
env = env1;
|
||||
#ifdef reg_EAX
|
||||
saved_EAX = EAX;
|
||||
EAX = env->regs[R_EAX];
|
||||
#endif
|
||||
#ifdef reg_ECX
|
||||
saved_ECX = ECX;
|
||||
ECX = env->regs[R_ECX];
|
||||
#endif
|
||||
#ifdef reg_EDX
|
||||
saved_EDX = EDX;
|
||||
EDX = env->regs[R_EDX];
|
||||
#endif
|
||||
#ifdef reg_EBX
|
||||
saved_EBX = EBX;
|
||||
EBX = env->regs[R_EBX];
|
||||
#endif
|
||||
#ifdef reg_ESP
|
||||
saved_ESP = ESP;
|
||||
ESP = env->regs[R_ESP];
|
||||
#endif
|
||||
#ifdef reg_EBP
|
||||
saved_EBP = EBP;
|
||||
EBP = env->regs[R_EBP];
|
||||
#endif
|
||||
#ifdef reg_ESI
|
||||
saved_ESI = ESI;
|
||||
ESI = env->regs[R_ESI];
|
||||
#endif
|
||||
#ifdef reg_EDI
|
||||
saved_EDI = EDI;
|
||||
EDI = env->regs[R_EDI];
|
||||
#endif
|
||||
|
||||
/* put eflags in CPU temporary format */
|
||||
CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||
DF = 1 - (2 * ((env->eflags >> 10) & 1));
|
||||
CC_OP = CC_OP_EFLAGS;
|
||||
env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||
env->interrupt_request = 0;
|
||||
|
||||
/* prepare setjmp context for exception handling */
|
||||
if (setjmp(env->jmp_env) == 0) {
|
||||
for(;;) {
|
||||
if (env->interrupt_request) {
|
||||
raise_exception(EXCP_INTERRUPT);
|
||||
}
|
||||
#ifdef DEBUG_EXEC
|
||||
if (loglevel) {
|
||||
cpu_x86_dump_state(logfile);
|
||||
}
|
||||
#endif
|
||||
/* we compute the CPU state. We assume it will not
|
||||
change during the whole generated block. */
|
||||
flags = env->seg_cache[R_CS].seg_32bit << GEN_FLAG_CODE32_SHIFT;
|
||||
flags |= env->seg_cache[R_SS].seg_32bit << GEN_FLAG_SS32_SHIFT;
|
||||
flags |= (((unsigned long)env->seg_cache[R_DS].base |
|
||||
(unsigned long)env->seg_cache[R_ES].base |
|
||||
(unsigned long)env->seg_cache[R_SS].base) != 0) <<
|
||||
GEN_FLAG_ADDSEG_SHIFT;
|
||||
flags |= (env->eflags & VM_MASK) >> (17 - GEN_FLAG_VM_SHIFT);
|
||||
cs_base = env->seg_cache[R_CS].base;
|
||||
pc = cs_base + env->eip;
|
||||
tb = tb_find(&ptb, (unsigned long)pc, (unsigned long)cs_base,
|
||||
flags);
|
||||
if (!tb) {
|
||||
/* if no translated code available, then translate it now */
|
||||
/* XXX: very inefficient: we lock all the cpus when
|
||||
generating code */
|
||||
cpu_lock();
|
||||
tc_ptr = code_gen_ptr;
|
||||
ret = cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE,
|
||||
&code_gen_size, pc, cs_base, flags);
|
||||
/* if invalid instruction, signal it */
|
||||
if (ret != 0) {
|
||||
cpu_unlock();
|
||||
raise_exception(EXCP06_ILLOP);
|
||||
}
|
||||
tb = tb_alloc();
|
||||
*ptb = tb;
|
||||
tb->pc = (unsigned long)pc;
|
||||
tb->cs_base = (unsigned long)cs_base;
|
||||
tb->flags = flags;
|
||||
tb->tc_ptr = tc_ptr;
|
||||
tb->hash_next = NULL;
|
||||
code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
|
||||
cpu_unlock();
|
||||
}
|
||||
/* execute the generated code */
|
||||
tc_ptr = tb->tc_ptr;
|
||||
gen_func = (void *)tc_ptr;
|
||||
gen_func();
|
||||
}
|
||||
}
|
||||
ret = env->exception_index;
|
||||
|
||||
/* restore flags in standard format */
|
||||
env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
|
||||
|
||||
/* restore global registers */
|
||||
#ifdef reg_EAX
|
||||
EAX = saved_EAX;
|
||||
#endif
|
||||
#ifdef reg_ECX
|
||||
ECX = saved_ECX;
|
||||
#endif
|
||||
#ifdef reg_EDX
|
||||
EDX = saved_EDX;
|
||||
#endif
|
||||
#ifdef reg_EBX
|
||||
EBX = saved_EBX;
|
||||
#endif
|
||||
#ifdef reg_ESP
|
||||
ESP = saved_ESP;
|
||||
#endif
|
||||
#ifdef reg_EBP
|
||||
EBP = saved_EBP;
|
||||
#endif
|
||||
#ifdef reg_ESI
|
||||
ESI = saved_ESI;
|
||||
#endif
|
||||
#ifdef reg_EDI
|
||||
EDI = saved_EDI;
|
||||
#endif
|
||||
T0 = saved_T0;
|
||||
T1 = saved_T1;
|
||||
A0 = saved_A0;
|
||||
env = saved_env;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cpu_x86_interrupt(CPUX86State *s)
|
||||
{
|
||||
s->interrupt_request = 1;
|
||||
}
|
||||
|
||||
|
||||
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
|
||||
{
|
||||
CPUX86State *saved_env;
|
||||
|
||||
saved_env = env;
|
||||
env = s;
|
||||
load_seg(seg_reg, selector);
|
||||
env = saved_env;
|
||||
}
|
||||
|
||||
#undef EAX
|
||||
#undef ECX
|
||||
#undef EDX
|
||||
#undef EBX
|
||||
#undef ESP
|
||||
#undef EBP
|
||||
#undef ESI
|
||||
#undef EDI
|
||||
#undef EIP
|
||||
#include <signal.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
static inline int handle_cpu_signal(unsigned long pc,
|
||||
sigset_t *old_set)
|
||||
{
|
||||
#ifdef DEBUG_SIGNAL
|
||||
printf("gemu: SIGSEGV pc=0x%08lx oldset=0x%08lx\n",
|
||||
pc, *(unsigned long *)old_set);
|
||||
#endif
|
||||
if (pc >= (unsigned long)code_gen_buffer &&
|
||||
pc < (unsigned long)code_gen_buffer + CODE_GEN_BUFFER_SIZE) {
|
||||
/* the PC is inside the translated code. It means that we have
|
||||
a virtual CPU fault */
|
||||
/* we restore the process signal mask as the sigreturn should
|
||||
do it */
|
||||
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||
/* XXX: need to compute virtual pc position by retranslating
|
||||
code. The rest of the CPU state should be correct. */
|
||||
raise_exception(EXCP0D_GPF);
|
||||
/* never comes here */
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
|
||||
void *puc)
|
||||
{
|
||||
#if defined(__i386__)
|
||||
struct ucontext *uc = puc;
|
||||
unsigned long pc;
|
||||
sigset_t *pold_set;
|
||||
|
||||
#ifndef REG_EIP
|
||||
/* for glibc 2.1 */
|
||||
#define REG_EIP EIP
|
||||
#endif
|
||||
pc = uc->uc_mcontext.gregs[REG_EIP];
|
||||
pold_set = &uc->uc_sigmask;
|
||||
return handle_cpu_signal(pc, pold_set);
|
||||
#else
|
||||
#warning No CPU specific signal handler: cannot handle target SIGSEGV events
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
-164
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* i386 execution defines
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef signed long long int64_t;
|
||||
|
||||
#define bswap32(x) \
|
||||
({ \
|
||||
uint32_t __x = (x); \
|
||||
((uint32_t)( \
|
||||
(((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \
|
||||
})
|
||||
|
||||
#define NULL 0
|
||||
#include <fenv.h>
|
||||
|
||||
typedef struct FILE FILE;
|
||||
extern FILE *logfile;
|
||||
extern int loglevel;
|
||||
extern int fprintf(FILE *, const char *, ...);
|
||||
extern int printf(const char *, ...);
|
||||
|
||||
#ifdef __i386__
|
||||
register unsigned int T0 asm("ebx");
|
||||
register unsigned int T1 asm("esi");
|
||||
register unsigned int A0 asm("edi");
|
||||
register struct CPUX86State *env asm("ebp");
|
||||
#endif
|
||||
#ifdef __powerpc__
|
||||
register unsigned int EAX asm("r16");
|
||||
register unsigned int ECX asm("r17");
|
||||
register unsigned int EDX asm("r18");
|
||||
register unsigned int EBX asm("r19");
|
||||
register unsigned int ESP asm("r20");
|
||||
register unsigned int EBP asm("r21");
|
||||
register unsigned int ESI asm("r22");
|
||||
register unsigned int EDI asm("r23");
|
||||
register unsigned int T0 asm("r24");
|
||||
register unsigned int T1 asm("r25");
|
||||
register unsigned int A0 asm("r26");
|
||||
register struct CPUX86State *env asm("r27");
|
||||
#define USE_INT_TO_FLOAT_HELPERS
|
||||
#define reg_EAX
|
||||
#define reg_ECX
|
||||
#define reg_EDX
|
||||
#define reg_EBX
|
||||
#define reg_ESP
|
||||
#define reg_EBP
|
||||
#define reg_ESI
|
||||
#define reg_EDI
|
||||
#endif
|
||||
#ifdef __arm__
|
||||
register unsigned int T0 asm("r4");
|
||||
register unsigned int T1 asm("r5");
|
||||
register unsigned int A0 asm("r6");
|
||||
register struct CPUX86State *env asm("r7");
|
||||
#endif
|
||||
#ifdef __mips__
|
||||
register unsigned int T0 asm("s0");
|
||||
register unsigned int T1 asm("s1");
|
||||
register unsigned int A0 asm("s2");
|
||||
register struct CPUX86State *env asm("s3");
|
||||
#endif
|
||||
#ifdef __sparc__
|
||||
register unsigned int T0 asm("l0");
|
||||
register unsigned int T1 asm("l1");
|
||||
register unsigned int A0 asm("l2");
|
||||
register struct CPUX86State *env asm("l3");
|
||||
#endif
|
||||
|
||||
/* force GCC to generate only one epilog at the end of the function */
|
||||
#define FORCE_RET() asm volatile ("");
|
||||
|
||||
#ifndef OPPROTO
|
||||
#define OPPROTO
|
||||
#endif
|
||||
|
||||
#define xglue(x, y) x ## y
|
||||
#define glue(x, y) xglue(x, y)
|
||||
|
||||
#ifndef reg_EAX
|
||||
#define EAX (env->regs[R_EAX])
|
||||
#endif
|
||||
#ifndef reg_ECX
|
||||
#define ECX (env->regs[R_ECX])
|
||||
#endif
|
||||
#ifndef reg_EDX
|
||||
#define EDX (env->regs[R_EDX])
|
||||
#endif
|
||||
#ifndef reg_EBX
|
||||
#define EBX (env->regs[R_EBX])
|
||||
#endif
|
||||
#ifndef reg_ESP
|
||||
#define ESP (env->regs[R_ESP])
|
||||
#endif
|
||||
#ifndef reg_EBP
|
||||
#define EBP (env->regs[R_EBP])
|
||||
#endif
|
||||
#ifndef reg_ESI
|
||||
#define ESI (env->regs[R_ESI])
|
||||
#endif
|
||||
#ifndef reg_EDI
|
||||
#define EDI (env->regs[R_EDI])
|
||||
#endif
|
||||
#define EIP (env->eip)
|
||||
#define DF (env->df)
|
||||
|
||||
#define CC_SRC (env->cc_src)
|
||||
#define CC_DST (env->cc_dst)
|
||||
#define CC_OP (env->cc_op)
|
||||
|
||||
/* float macros */
|
||||
#define FT0 (env->ft0)
|
||||
#define ST0 (env->fpregs[env->fpstt])
|
||||
#define ST(n) (env->fpregs[(env->fpstt + (n)) & 7])
|
||||
#define ST1 ST(1)
|
||||
|
||||
extern int __op_param1, __op_param2, __op_param3;
|
||||
#define PARAM1 ((long)(&__op_param1))
|
||||
#define PARAM2 ((long)(&__op_param2))
|
||||
#define PARAM3 ((long)(&__op_param3))
|
||||
|
||||
#include "cpu-i386.h"
|
||||
|
||||
typedef struct CCTable {
|
||||
int (*compute_all)(void); /* return all the flags */
|
||||
int (*compute_c)(void); /* return the C flag */
|
||||
} CCTable;
|
||||
|
||||
extern CCTable cc_table[];
|
||||
|
||||
void load_seg(int seg_reg, int selector);
|
||||
void cpu_lock(void);
|
||||
void cpu_unlock(void);
|
||||
void raise_exception(int exception_index);
|
||||
|
||||
void OPPROTO op_movl_eflags_T0(void);
|
||||
void OPPROTO op_movl_T0_eflags(void);
|
||||
@@ -0,0 +1,720 @@
|
||||
|
||||
/*============================================================================
|
||||
|
||||
This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
|
||||
Arithmetic Package, Release 2b.
|
||||
|
||||
Written by John R. Hauser. This work was made possible in part by the
|
||||
International Computer Science Institute, located at Suite 600, 1947 Center
|
||||
Street, Berkeley, California 94704. Funding was partially provided by the
|
||||
National Science Foundation under grant MIP-9311980. The original version
|
||||
of this code was written as part of a project to build a fixed-point vector
|
||||
processor in collaboration with the University of California at Berkeley,
|
||||
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
|
||||
is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
|
||||
arithmetic/SoftFloat.html'.
|
||||
|
||||
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
|
||||
been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
|
||||
RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
|
||||
AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
|
||||
COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
|
||||
EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
|
||||
INSTITUTE (possibly via similar legal notice) AGAINST ALL LOSSES, COSTS, OR
|
||||
OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
|
||||
|
||||
Derivative works are acceptable, even for commercial purposes, so long as
|
||||
(1) the source code for the derivative work includes prominent notice that
|
||||
the work is derivative, and (2) the source code includes prominent notice with
|
||||
these four paragraphs for those parts of this code that are retained.
|
||||
|
||||
=============================================================================*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts `a' right by the number of bits given in `count'. If any nonzero
|
||||
| bits are shifted off, they are ``jammed'' into the least significant bit of
|
||||
| the result by setting the least significant bit to 1. The value of `count'
|
||||
| can be arbitrarily large; in particular, if `count' is greater than 32, the
|
||||
| result will be either 0 or 1, depending on whether `a' is zero or nonzero.
|
||||
| The result is stored in the location pointed to by `zPtr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr )
|
||||
{
|
||||
bits32 z;
|
||||
|
||||
if ( count == 0 ) {
|
||||
z = a;
|
||||
}
|
||||
else if ( count < 32 ) {
|
||||
z = ( a>>count ) | ( ( a<<( ( - count ) & 31 ) ) != 0 );
|
||||
}
|
||||
else {
|
||||
z = ( a != 0 );
|
||||
}
|
||||
*zPtr = z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts `a' right by the number of bits given in `count'. If any nonzero
|
||||
| bits are shifted off, they are ``jammed'' into the least significant bit of
|
||||
| the result by setting the least significant bit to 1. The value of `count'
|
||||
| can be arbitrarily large; in particular, if `count' is greater than 64, the
|
||||
| result will be either 0 or 1, depending on whether `a' is zero or nonzero.
|
||||
| The result is stored in the location pointed to by `zPtr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr )
|
||||
{
|
||||
bits64 z;
|
||||
|
||||
if ( count == 0 ) {
|
||||
z = a;
|
||||
}
|
||||
else if ( count < 64 ) {
|
||||
z = ( a>>count ) | ( ( a<<( ( - count ) & 63 ) ) != 0 );
|
||||
}
|
||||
else {
|
||||
z = ( a != 0 );
|
||||
}
|
||||
*zPtr = z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64
|
||||
| _plus_ the number of bits given in `count'. The shifted result is at most
|
||||
| 64 nonzero bits; this is stored at the location pointed to by `z0Ptr'. The
|
||||
| bits shifted off form a second 64-bit result as follows: The _last_ bit
|
||||
| shifted off is the most-significant bit of the extra result, and the other
|
||||
| 63 bits of the extra result are all zero if and only if _all_but_the_last_
|
||||
| bits shifted off were all zero. This extra result is stored in the location
|
||||
| pointed to by `z1Ptr'. The value of `count' can be arbitrarily large.
|
||||
| (This routine makes more sense if `a0' and `a1' are considered to form
|
||||
| a fixed-point value with binary point between `a0' and `a1'. This fixed-
|
||||
| point value is shifted right by the number of bits given in `count', and
|
||||
| the integer part of the result is returned at the location pointed to by
|
||||
| `z0Ptr'. The fractional part of the result may be slightly corrupted as
|
||||
| described above, and is returned at the location pointed to by `z1Ptr'.)
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
shift64ExtraRightJamming(
|
||||
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
bits64 z0, z1;
|
||||
int8 negCount = ( - count ) & 63;
|
||||
|
||||
if ( count == 0 ) {
|
||||
z1 = a1;
|
||||
z0 = a0;
|
||||
}
|
||||
else if ( count < 64 ) {
|
||||
z1 = ( a0<<negCount ) | ( a1 != 0 );
|
||||
z0 = a0>>count;
|
||||
}
|
||||
else {
|
||||
if ( count == 64 ) {
|
||||
z1 = a0 | ( a1 != 0 );
|
||||
}
|
||||
else {
|
||||
z1 = ( ( a0 | a1 ) != 0 );
|
||||
}
|
||||
z0 = 0;
|
||||
}
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
|
||||
| number of bits given in `count'. Any bits shifted off are lost. The value
|
||||
| of `count' can be arbitrarily large; in particular, if `count' is greater
|
||||
| than 128, the result will be 0. The result is broken into two 64-bit pieces
|
||||
| which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
shift128Right(
|
||||
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
bits64 z0, z1;
|
||||
int8 negCount = ( - count ) & 63;
|
||||
|
||||
if ( count == 0 ) {
|
||||
z1 = a1;
|
||||
z0 = a0;
|
||||
}
|
||||
else if ( count < 64 ) {
|
||||
z1 = ( a0<<negCount ) | ( a1>>count );
|
||||
z0 = a0>>count;
|
||||
}
|
||||
else {
|
||||
z1 = ( count < 64 ) ? ( a0>>( count & 63 ) ) : 0;
|
||||
z0 = 0;
|
||||
}
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
|
||||
| number of bits given in `count'. If any nonzero bits are shifted off, they
|
||||
| are ``jammed'' into the least significant bit of the result by setting the
|
||||
| least significant bit to 1. The value of `count' can be arbitrarily large;
|
||||
| in particular, if `count' is greater than 128, the result will be either
|
||||
| 0 or 1, depending on whether the concatenation of `a0' and `a1' is zero or
|
||||
| nonzero. The result is broken into two 64-bit pieces which are stored at
|
||||
| the locations pointed to by `z0Ptr' and `z1Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
shift128RightJamming(
|
||||
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
bits64 z0, z1;
|
||||
int8 negCount = ( - count ) & 63;
|
||||
|
||||
if ( count == 0 ) {
|
||||
z1 = a1;
|
||||
z0 = a0;
|
||||
}
|
||||
else if ( count < 64 ) {
|
||||
z1 = ( a0<<negCount ) | ( a1>>count ) | ( ( a1<<negCount ) != 0 );
|
||||
z0 = a0>>count;
|
||||
}
|
||||
else {
|
||||
if ( count == 64 ) {
|
||||
z1 = a0 | ( a1 != 0 );
|
||||
}
|
||||
else if ( count < 128 ) {
|
||||
z1 = ( a0>>( count & 63 ) ) | ( ( ( a0<<negCount ) | a1 ) != 0 );
|
||||
}
|
||||
else {
|
||||
z1 = ( ( a0 | a1 ) != 0 );
|
||||
}
|
||||
z0 = 0;
|
||||
}
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' right
|
||||
| by 64 _plus_ the number of bits given in `count'. The shifted result is
|
||||
| at most 128 nonzero bits; these are broken into two 64-bit pieces which are
|
||||
| stored at the locations pointed to by `z0Ptr' and `z1Ptr'. The bits shifted
|
||||
| off form a third 64-bit result as follows: The _last_ bit shifted off is
|
||||
| the most-significant bit of the extra result, and the other 63 bits of the
|
||||
| extra result are all zero if and only if _all_but_the_last_ bits shifted off
|
||||
| were all zero. This extra result is stored in the location pointed to by
|
||||
| `z2Ptr'. The value of `count' can be arbitrarily large.
|
||||
| (This routine makes more sense if `a0', `a1', and `a2' are considered
|
||||
| to form a fixed-point value with binary point between `a1' and `a2'. This
|
||||
| fixed-point value is shifted right by the number of bits given in `count',
|
||||
| and the integer part of the result is returned at the locations pointed to
|
||||
| by `z0Ptr' and `z1Ptr'. The fractional part of the result may be slightly
|
||||
| corrupted as described above, and is returned at the location pointed to by
|
||||
| `z2Ptr'.)
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
shift128ExtraRightJamming(
|
||||
bits64 a0,
|
||||
bits64 a1,
|
||||
bits64 a2,
|
||||
int16 count,
|
||||
bits64 *z0Ptr,
|
||||
bits64 *z1Ptr,
|
||||
bits64 *z2Ptr
|
||||
)
|
||||
{
|
||||
bits64 z0, z1, z2;
|
||||
int8 negCount = ( - count ) & 63;
|
||||
|
||||
if ( count == 0 ) {
|
||||
z2 = a2;
|
||||
z1 = a1;
|
||||
z0 = a0;
|
||||
}
|
||||
else {
|
||||
if ( count < 64 ) {
|
||||
z2 = a1<<negCount;
|
||||
z1 = ( a0<<negCount ) | ( a1>>count );
|
||||
z0 = a0>>count;
|
||||
}
|
||||
else {
|
||||
if ( count == 64 ) {
|
||||
z2 = a1;
|
||||
z1 = a0;
|
||||
}
|
||||
else {
|
||||
a2 |= a1;
|
||||
if ( count < 128 ) {
|
||||
z2 = a0<<negCount;
|
||||
z1 = a0>>( count & 63 );
|
||||
}
|
||||
else {
|
||||
z2 = ( count == 128 ) ? a0 : ( a0 != 0 );
|
||||
z1 = 0;
|
||||
}
|
||||
}
|
||||
z0 = 0;
|
||||
}
|
||||
z2 |= ( a2 != 0 );
|
||||
}
|
||||
*z2Ptr = z2;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the
|
||||
| number of bits given in `count'. Any bits shifted off are lost. The value
|
||||
| of `count' must be less than 64. The result is broken into two 64-bit
|
||||
| pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
shortShift128Left(
|
||||
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
|
||||
*z1Ptr = a1<<count;
|
||||
*z0Ptr =
|
||||
( count == 0 ) ? a0 : ( a0<<count ) | ( a1>>( ( - count ) & 63 ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left
|
||||
| by the number of bits given in `count'. Any bits shifted off are lost.
|
||||
| The value of `count' must be less than 64. The result is broken into three
|
||||
| 64-bit pieces which are stored at the locations pointed to by `z0Ptr',
|
||||
| `z1Ptr', and `z2Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
shortShift192Left(
|
||||
bits64 a0,
|
||||
bits64 a1,
|
||||
bits64 a2,
|
||||
int16 count,
|
||||
bits64 *z0Ptr,
|
||||
bits64 *z1Ptr,
|
||||
bits64 *z2Ptr
|
||||
)
|
||||
{
|
||||
bits64 z0, z1, z2;
|
||||
int8 negCount;
|
||||
|
||||
z2 = a2<<count;
|
||||
z1 = a1<<count;
|
||||
z0 = a0<<count;
|
||||
if ( 0 < count ) {
|
||||
negCount = ( ( - count ) & 63 );
|
||||
z1 |= a2>>negCount;
|
||||
z0 |= a1>>negCount;
|
||||
}
|
||||
*z2Ptr = z2;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit
|
||||
| value formed by concatenating `b0' and `b1'. Addition is modulo 2^128, so
|
||||
| any carry out is lost. The result is broken into two 64-bit pieces which
|
||||
| are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
add128(
|
||||
bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
bits64 z1;
|
||||
|
||||
z1 = a1 + b1;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = a0 + b0 + ( z1 < a1 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the
|
||||
| 192-bit value formed by concatenating `b0', `b1', and `b2'. Addition is
|
||||
| modulo 2^192, so any carry out is lost. The result is broken into three
|
||||
| 64-bit pieces which are stored at the locations pointed to by `z0Ptr',
|
||||
| `z1Ptr', and `z2Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
add192(
|
||||
bits64 a0,
|
||||
bits64 a1,
|
||||
bits64 a2,
|
||||
bits64 b0,
|
||||
bits64 b1,
|
||||
bits64 b2,
|
||||
bits64 *z0Ptr,
|
||||
bits64 *z1Ptr,
|
||||
bits64 *z2Ptr
|
||||
)
|
||||
{
|
||||
bits64 z0, z1, z2;
|
||||
int8 carry0, carry1;
|
||||
|
||||
z2 = a2 + b2;
|
||||
carry1 = ( z2 < a2 );
|
||||
z1 = a1 + b1;
|
||||
carry0 = ( z1 < a1 );
|
||||
z0 = a0 + b0;
|
||||
z1 += carry1;
|
||||
z0 += ( z1 < carry1 );
|
||||
z0 += carry0;
|
||||
*z2Ptr = z2;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the
|
||||
| 128-bit value formed by concatenating `a0' and `a1'. Subtraction is modulo
|
||||
| 2^128, so any borrow out (carry out) is lost. The result is broken into two
|
||||
| 64-bit pieces which are stored at the locations pointed to by `z0Ptr' and
|
||||
| `z1Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
sub128(
|
||||
bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
|
||||
*z1Ptr = a1 - b1;
|
||||
*z0Ptr = a0 - b0 - ( a1 < b1 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2'
|
||||
| from the 192-bit value formed by concatenating `a0', `a1', and `a2'.
|
||||
| Subtraction is modulo 2^192, so any borrow out (carry out) is lost. The
|
||||
| result is broken into three 64-bit pieces which are stored at the locations
|
||||
| pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
sub192(
|
||||
bits64 a0,
|
||||
bits64 a1,
|
||||
bits64 a2,
|
||||
bits64 b0,
|
||||
bits64 b1,
|
||||
bits64 b2,
|
||||
bits64 *z0Ptr,
|
||||
bits64 *z1Ptr,
|
||||
bits64 *z2Ptr
|
||||
)
|
||||
{
|
||||
bits64 z0, z1, z2;
|
||||
int8 borrow0, borrow1;
|
||||
|
||||
z2 = a2 - b2;
|
||||
borrow1 = ( a2 < b2 );
|
||||
z1 = a1 - b1;
|
||||
borrow0 = ( a1 < b1 );
|
||||
z0 = a0 - b0;
|
||||
z0 -= ( z1 < borrow1 );
|
||||
z1 -= borrow1;
|
||||
z0 -= borrow0;
|
||||
*z2Ptr = z2;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Multiplies `a' by `b' to obtain a 128-bit product. The product is broken
|
||||
| into two 64-bit pieces which are stored at the locations pointed to by
|
||||
| `z0Ptr' and `z1Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
bits32 aHigh, aLow, bHigh, bLow;
|
||||
bits64 z0, zMiddleA, zMiddleB, z1;
|
||||
|
||||
aLow = a;
|
||||
aHigh = a>>32;
|
||||
bLow = b;
|
||||
bHigh = b>>32;
|
||||
z1 = ( (bits64) aLow ) * bLow;
|
||||
zMiddleA = ( (bits64) aLow ) * bHigh;
|
||||
zMiddleB = ( (bits64) aHigh ) * bLow;
|
||||
z0 = ( (bits64) aHigh ) * bHigh;
|
||||
zMiddleA += zMiddleB;
|
||||
z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 );
|
||||
zMiddleA <<= 32;
|
||||
z1 += zMiddleA;
|
||||
z0 += ( z1 < zMiddleA );
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Multiplies the 128-bit value formed by concatenating `a0' and `a1' by
|
||||
| `b' to obtain a 192-bit product. The product is broken into three 64-bit
|
||||
| pieces which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and
|
||||
| `z2Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
mul128By64To192(
|
||||
bits64 a0,
|
||||
bits64 a1,
|
||||
bits64 b,
|
||||
bits64 *z0Ptr,
|
||||
bits64 *z1Ptr,
|
||||
bits64 *z2Ptr
|
||||
)
|
||||
{
|
||||
bits64 z0, z1, z2, more1;
|
||||
|
||||
mul64To128( a1, b, &z1, &z2 );
|
||||
mul64To128( a0, b, &z0, &more1 );
|
||||
add128( z0, more1, 0, z1, &z0, &z1 );
|
||||
*z2Ptr = z2;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the
|
||||
| 128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit
|
||||
| product. The product is broken into four 64-bit pieces which are stored at
|
||||
| the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
mul128To256(
|
||||
bits64 a0,
|
||||
bits64 a1,
|
||||
bits64 b0,
|
||||
bits64 b1,
|
||||
bits64 *z0Ptr,
|
||||
bits64 *z1Ptr,
|
||||
bits64 *z2Ptr,
|
||||
bits64 *z3Ptr
|
||||
)
|
||||
{
|
||||
bits64 z0, z1, z2, z3;
|
||||
bits64 more1, more2;
|
||||
|
||||
mul64To128( a1, b1, &z2, &z3 );
|
||||
mul64To128( a1, b0, &z1, &more2 );
|
||||
add128( z1, more2, 0, z2, &z1, &z2 );
|
||||
mul64To128( a0, b0, &z0, &more1 );
|
||||
add128( z0, more1, 0, z1, &z0, &z1 );
|
||||
mul64To128( a0, b1, &more1, &more2 );
|
||||
add128( more1, more2, 0, z2, &more1, &z2 );
|
||||
add128( z0, z1, 0, more1, &z0, &z1 );
|
||||
*z3Ptr = z3;
|
||||
*z2Ptr = z2;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns an approximation to the 64-bit integer quotient obtained by dividing
|
||||
| `b' into the 128-bit value formed by concatenating `a0' and `a1'. The
|
||||
| divisor `b' must be at least 2^63. If q is the exact quotient truncated
|
||||
| toward zero, the approximation returned lies between q and q + 2 inclusive.
|
||||
| If the exact quotient q is larger than 64 bits, the maximum positive 64-bit
|
||||
| unsigned integer is returned.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b )
|
||||
{
|
||||
bits64 b0, b1;
|
||||
bits64 rem0, rem1, term0, term1;
|
||||
bits64 z;
|
||||
|
||||
if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF );
|
||||
b0 = b>>32;
|
||||
z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32;
|
||||
mul64To128( b, z, &term0, &term1 );
|
||||
sub128( a0, a1, term0, term1, &rem0, &rem1 );
|
||||
while ( ( (sbits64) rem0 ) < 0 ) {
|
||||
z -= LIT64( 0x100000000 );
|
||||
b1 = b<<32;
|
||||
add128( rem0, rem1, b0, b1, &rem0, &rem1 );
|
||||
}
|
||||
rem0 = ( rem0<<32 ) | ( rem1>>32 );
|
||||
z |= ( b0<<32 <= rem0 ) ? 0xFFFFFFFF : rem0 / b0;
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns an approximation to the square root of the 32-bit significand given
|
||||
| by `a'. Considered as an integer, `a' must be at least 2^31. If bit 0 of
|
||||
| `aExp' (the least significant bit) is 1, the integer returned approximates
|
||||
| 2^31*sqrt(`a'/2^31), where `a' is considered an integer. If bit 0 of `aExp'
|
||||
| is 0, the integer returned approximates 2^31*sqrt(`a'/2^30). In either
|
||||
| case, the approximation returned lies strictly within +/-2 of the exact
|
||||
| value.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static bits32 estimateSqrt32( int16 aExp, bits32 a )
|
||||
{
|
||||
static const bits16 sqrtOddAdjustments[] = {
|
||||
0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0,
|
||||
0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67
|
||||
};
|
||||
static const bits16 sqrtEvenAdjustments[] = {
|
||||
0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E,
|
||||
0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002
|
||||
};
|
||||
int8 index;
|
||||
bits32 z;
|
||||
|
||||
index = ( a>>27 ) & 15;
|
||||
if ( aExp & 1 ) {
|
||||
z = 0x4000 + ( a>>17 ) - sqrtOddAdjustments[ index ];
|
||||
z = ( ( a / z )<<14 ) + ( z<<15 );
|
||||
a >>= 1;
|
||||
}
|
||||
else {
|
||||
z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ index ];
|
||||
z = a / z + z;
|
||||
z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 );
|
||||
if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 );
|
||||
}
|
||||
return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the number of leading 0 bits before the most-significant 1 bit of
|
||||
| `a'. If `a' is zero, 32 is returned.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static int8 countLeadingZeros32( bits32 a )
|
||||
{
|
||||
static const int8 countLeadingZerosHigh[] = {
|
||||
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
int8 shiftCount;
|
||||
|
||||
shiftCount = 0;
|
||||
if ( a < 0x10000 ) {
|
||||
shiftCount += 16;
|
||||
a <<= 16;
|
||||
}
|
||||
if ( a < 0x1000000 ) {
|
||||
shiftCount += 8;
|
||||
a <<= 8;
|
||||
}
|
||||
shiftCount += countLeadingZerosHigh[ a>>24 ];
|
||||
return shiftCount;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the number of leading 0 bits before the most-significant 1 bit of
|
||||
| `a'. If `a' is zero, 64 is returned.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static int8 countLeadingZeros64( bits64 a )
|
||||
{
|
||||
int8 shiftCount;
|
||||
|
||||
shiftCount = 0;
|
||||
if ( a < ( (bits64) 1 )<<32 ) {
|
||||
shiftCount += 32;
|
||||
}
|
||||
else {
|
||||
a >>= 32;
|
||||
}
|
||||
shiftCount += countLeadingZeros32( a );
|
||||
return shiftCount;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1'
|
||||
| is equal to the 128-bit value formed by concatenating `b0' and `b1'.
|
||||
| Otherwise, returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
|
||||
{
|
||||
|
||||
return ( a0 == b0 ) && ( a1 == b1 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
|
||||
| than or equal to the 128-bit value formed by concatenating `b0' and `b1'.
|
||||
| Otherwise, returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
|
||||
{
|
||||
|
||||
return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
|
||||
| than the 128-bit value formed by concatenating `b0' and `b1'. Otherwise,
|
||||
| returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
|
||||
{
|
||||
|
||||
return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is
|
||||
| not equal to the 128-bit value formed by concatenating `b0' and `b1'.
|
||||
| Otherwise, returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
|
||||
{
|
||||
|
||||
return ( a0 != b0 ) || ( a1 != b1 );
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
/* Native implementation of soft float functions. Only a single status
|
||||
context is supported */
|
||||
#include "softfloat.h"
|
||||
#include <math.h>
|
||||
|
||||
void set_float_rounding_mode(int val STATUS_PARAM)
|
||||
{
|
||||
STATUS(float_rounding_mode) = val;
|
||||
#if defined(_BSD) && !defined(__APPLE__)
|
||||
fpsetround(val);
|
||||
#elif defined(__arm__)
|
||||
/* nothing to do */
|
||||
#else
|
||||
fesetround(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
void set_floatx80_rounding_precision(int val STATUS_PARAM)
|
||||
{
|
||||
STATUS(floatx80_rounding_precision) = val;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_BSD)
|
||||
#define lrint(d) ((int32_t)rint(d))
|
||||
#define llrint(d) ((int64_t)rint(d))
|
||||
#endif
|
||||
|
||||
#if defined(__powerpc__)
|
||||
|
||||
/* correct (but slow) PowerPC rint() (glibc version is incorrect) */
|
||||
double qemu_rint(double x)
|
||||
{
|
||||
double y = 4503599627370496.0;
|
||||
if (fabs(x) >= y)
|
||||
return x;
|
||||
if (x < 0)
|
||||
y = -y;
|
||||
y = (x + y) - y;
|
||||
if (y == 0.0)
|
||||
y = copysign(y, x);
|
||||
return y;
|
||||
}
|
||||
|
||||
#define rint qemu_rint
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE integer-to-floating-point conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 int32_to_float32(int v STATUS_PARAM)
|
||||
{
|
||||
return (float32)v;
|
||||
}
|
||||
|
||||
float64 int32_to_float64(int v STATUS_PARAM)
|
||||
{
|
||||
return (float64)v;
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
floatx80 int32_to_floatx80(int v STATUS_PARAM)
|
||||
{
|
||||
return (floatx80)v;
|
||||
}
|
||||
#endif
|
||||
float32 int64_to_float32( int64_t v STATUS_PARAM)
|
||||
{
|
||||
return (float32)v;
|
||||
}
|
||||
float64 int64_to_float64( int64_t v STATUS_PARAM)
|
||||
{
|
||||
return (float64)v;
|
||||
}
|
||||
#ifdef FLOATX80
|
||||
floatx80 int64_to_floatx80( int64_t v STATUS_PARAM)
|
||||
{
|
||||
return (floatx80)v;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float32_to_int32( float32 a STATUS_PARAM)
|
||||
{
|
||||
return lrintf(a);
|
||||
}
|
||||
int float32_to_int32_round_to_zero( float32 a STATUS_PARAM)
|
||||
{
|
||||
return (int)a;
|
||||
}
|
||||
int64_t float32_to_int64( float32 a STATUS_PARAM)
|
||||
{
|
||||
return llrintf(a);
|
||||
}
|
||||
|
||||
int64_t float32_to_int64_round_to_zero( float32 a STATUS_PARAM)
|
||||
{
|
||||
return (int64_t)a;
|
||||
}
|
||||
|
||||
float64 float32_to_float64( float32 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#ifdef FLOATX80
|
||||
floatx80 float32_to_floatx80( float32 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 float32_round_to_int( float32 a STATUS_PARAM)
|
||||
{
|
||||
return rintf(a);
|
||||
}
|
||||
|
||||
float32 float32_rem( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return remainderf(a, b);
|
||||
}
|
||||
|
||||
float32 float32_sqrt( float32 a STATUS_PARAM)
|
||||
{
|
||||
return sqrtf(a);
|
||||
}
|
||||
char float32_compare( float32 a, float32 b STATUS_PARAM )
|
||||
{
|
||||
if (a < b) {
|
||||
return -1;
|
||||
} else if (a == b) {
|
||||
return 0;
|
||||
} else if (a > b) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
char float32_compare_quiet( float32 a, float32 b STATUS_PARAM )
|
||||
{
|
||||
if (isless(a, b)) {
|
||||
return -1;
|
||||
} else if (a == b) {
|
||||
return 0;
|
||||
} else if (isgreater(a, b)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
char float32_is_signaling_nan( float32 a1)
|
||||
{
|
||||
float32u u;
|
||||
uint32_t a;
|
||||
u.f = a1;
|
||||
a = u.i;
|
||||
return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float64_to_int32( float64 a STATUS_PARAM)
|
||||
{
|
||||
return lrint(a);
|
||||
}
|
||||
int float64_to_int32_round_to_zero( float64 a STATUS_PARAM)
|
||||
{
|
||||
return (int)a;
|
||||
}
|
||||
int64_t float64_to_int64( float64 a STATUS_PARAM)
|
||||
{
|
||||
return llrint(a);
|
||||
}
|
||||
int64_t float64_to_int64_round_to_zero( float64 a STATUS_PARAM)
|
||||
{
|
||||
return (int64_t)a;
|
||||
}
|
||||
float32 float64_to_float32( float64 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#ifdef FLOATX80
|
||||
floatx80 float64_to_floatx80( float64 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 float64_to_float128( float64 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float64 float64_round_to_int( float64 a STATUS_PARAM )
|
||||
{
|
||||
#if defined(__arm__)
|
||||
switch(STATUS(float_rounding_mode)) {
|
||||
default:
|
||||
case float_round_nearest_even:
|
||||
asm("rndd %0, %1" : "=f" (a) : "f"(a));
|
||||
break;
|
||||
case float_round_down:
|
||||
asm("rnddm %0, %1" : "=f" (a) : "f"(a));
|
||||
break;
|
||||
case float_round_up:
|
||||
asm("rnddp %0, %1" : "=f" (a) : "f"(a));
|
||||
break;
|
||||
case float_round_to_zero:
|
||||
asm("rnddz %0, %1" : "=f" (a) : "f"(a));
|
||||
break;
|
||||
}
|
||||
#else
|
||||
return rint(a);
|
||||
#endif
|
||||
}
|
||||
|
||||
float64 float64_rem( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return remainder(a, b);
|
||||
}
|
||||
|
||||
float64 float64_sqrt( float64 a STATUS_PARAM)
|
||||
{
|
||||
return sqrt(a);
|
||||
}
|
||||
char float64_compare( float64 a, float64 b STATUS_PARAM )
|
||||
{
|
||||
if (a < b) {
|
||||
return -1;
|
||||
} else if (a == b) {
|
||||
return 0;
|
||||
} else if (a > b) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
char float64_compare_quiet( float64 a, float64 b STATUS_PARAM )
|
||||
{
|
||||
if (isless(a, b)) {
|
||||
return -1;
|
||||
} else if (a == b) {
|
||||
return 0;
|
||||
} else if (isgreater(a, b)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
char float64_is_signaling_nan( float64 a1)
|
||||
{
|
||||
float64u u;
|
||||
uint64_t a;
|
||||
u.f = a1;
|
||||
a = u.i;
|
||||
return
|
||||
( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
|
||||
&& ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
|
||||
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int floatx80_to_int32( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return lrintl(a);
|
||||
}
|
||||
int floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return (int)a;
|
||||
}
|
||||
int64_t floatx80_to_int64( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return llrintl(a);
|
||||
}
|
||||
int64_t floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return (int64_t)a;
|
||||
}
|
||||
float32 floatx80_to_float32( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
float64 floatx80_to_float64( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return rintl(a);
|
||||
}
|
||||
floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return remainderl(a, b);
|
||||
}
|
||||
floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return sqrtl(a);
|
||||
}
|
||||
char floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
{
|
||||
if (a < b) {
|
||||
return -1;
|
||||
} else if (a == b) {
|
||||
return 0;
|
||||
} else if (a > b) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
char floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
{
|
||||
if (isless(a, b)) {
|
||||
return -1;
|
||||
} else if (a == b) {
|
||||
return 0;
|
||||
} else if (isgreater(a, b)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
char floatx80_is_signaling_nan( floatx80 a1)
|
||||
{
|
||||
floatx80u u;
|
||||
u.f = a1;
|
||||
return ( ( u.i.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( u.i.low<<1 );
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,332 @@
|
||||
/* Native implementation of soft float functions */
|
||||
#include <math.h>
|
||||
#if defined(_BSD) && !defined(__APPLE__)
|
||||
#include <ieeefp.h>
|
||||
#else
|
||||
#include <fenv.h>
|
||||
#endif
|
||||
|
||||
typedef float float32;
|
||||
typedef double float64;
|
||||
#ifdef FLOATX80
|
||||
typedef long double floatx80;
|
||||
#endif
|
||||
|
||||
typedef union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} float32u;
|
||||
typedef union {
|
||||
float64 f;
|
||||
uint64_t i;
|
||||
} float64u;
|
||||
#ifdef FLOATX80
|
||||
typedef union {
|
||||
floatx80 f;
|
||||
struct {
|
||||
uint64_t low;
|
||||
uint16_t high;
|
||||
} i;
|
||||
} floatx80u;
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point rounding mode.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#if defined(_BSD) && !defined(__APPLE__)
|
||||
enum {
|
||||
float_round_nearest_even = FP_RN,
|
||||
float_round_down = FE_RM,
|
||||
float_round_up = FE_RP,
|
||||
float_round_to_zero = FE_RZ
|
||||
};
|
||||
#elif defined(__arm__)
|
||||
enum {
|
||||
float_round_nearest_even = 0,
|
||||
float_round_down = 1,
|
||||
float_round_up = 2,
|
||||
float_round_to_zero = 3
|
||||
};
|
||||
#else
|
||||
enum {
|
||||
float_round_nearest_even = FE_TONEAREST,
|
||||
float_round_down = FE_DOWNWARD,
|
||||
float_round_up = FE_UPWARD,
|
||||
float_round_to_zero = FE_TOWARDZERO
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct float_status {
|
||||
signed char float_rounding_mode;
|
||||
#ifdef FLOATX80
|
||||
signed char floatx80_rounding_precision;
|
||||
#endif
|
||||
} float_status;
|
||||
|
||||
void set_float_rounding_mode(int val STATUS_PARAM);
|
||||
#ifdef FLOATX80
|
||||
void set_floatx80_rounding_precision(int val STATUS_PARAM);
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE integer-to-floating-point conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 int32_to_float32( int STATUS_PARAM);
|
||||
float64 int32_to_float64( int STATUS_PARAM);
|
||||
#ifdef FLOATX80
|
||||
floatx80 int32_to_floatx80( int STATUS_PARAM);
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 int32_to_float128( int STATUS_PARAM);
|
||||
#endif
|
||||
float32 int64_to_float32( int64_t STATUS_PARAM);
|
||||
float64 int64_to_float64( int64_t STATUS_PARAM);
|
||||
#ifdef FLOATX80
|
||||
floatx80 int64_to_floatx80( int64_t STATUS_PARAM);
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 int64_to_float128( int64_t STATUS_PARAM);
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float32_to_int32( float32 STATUS_PARAM);
|
||||
int float32_to_int32_round_to_zero( float32 STATUS_PARAM);
|
||||
int64_t float32_to_int64( float32 STATUS_PARAM);
|
||||
int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM);
|
||||
float64 float32_to_float64( float32 STATUS_PARAM);
|
||||
#ifdef FLOATX80
|
||||
floatx80 float32_to_floatx80( float32 STATUS_PARAM);
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 float32_to_float128( float32 STATUS_PARAM);
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 float32_round_to_int( float32 STATUS_PARAM);
|
||||
INLINE float32 float32_add( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
INLINE float32 float32_sub( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
INLINE float32 float32_mul( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
INLINE float32 float32_div( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a / b;
|
||||
}
|
||||
float32 float32_rem( float32, float32 STATUS_PARAM);
|
||||
float32 float32_sqrt( float32 STATUS_PARAM);
|
||||
INLINE char float32_eq( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
INLINE char float32_le( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b;
|
||||
}
|
||||
INLINE char float32_lt( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
INLINE char float32_eq_signaling( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b && a >= b;
|
||||
}
|
||||
INLINE char float32_le_quiet( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return islessequal(a, b);
|
||||
}
|
||||
INLINE char float32_lt_quiet( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return isless(a, b);
|
||||
}
|
||||
INLINE char float32_unordered( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return isunordered(a, b);
|
||||
|
||||
}
|
||||
char float32_compare( float32, float32 STATUS_PARAM );
|
||||
char float32_compare_quiet( float32, float32 STATUS_PARAM );
|
||||
char float32_is_signaling_nan( float32 );
|
||||
|
||||
INLINE float32 float32_abs(float32 a)
|
||||
{
|
||||
return fabsf(a);
|
||||
}
|
||||
|
||||
INLINE float32 float32_chs(float32 a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float64_to_int32( float64 STATUS_PARAM );
|
||||
int float64_to_int32_round_to_zero( float64 STATUS_PARAM );
|
||||
int64_t float64_to_int64( float64 STATUS_PARAM );
|
||||
int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM );
|
||||
float32 float64_to_float32( float64 STATUS_PARAM );
|
||||
#ifdef FLOATX80
|
||||
floatx80 float64_to_floatx80( float64 STATUS_PARAM );
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 float64_to_float128( float64 STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float64 float64_round_to_int( float64 STATUS_PARAM );
|
||||
INLINE float64 float64_add( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
INLINE float64 float64_sub( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
INLINE float64 float64_mul( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
INLINE float64 float64_div( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a / b;
|
||||
}
|
||||
float64 float64_rem( float64, float64 STATUS_PARAM );
|
||||
float64 float64_sqrt( float64 STATUS_PARAM );
|
||||
INLINE char float64_eq( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
INLINE char float64_le( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b;
|
||||
}
|
||||
INLINE char float64_lt( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
INLINE char float64_eq_signaling( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b && a >= b;
|
||||
}
|
||||
INLINE char float64_le_quiet( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return islessequal(a, b);
|
||||
}
|
||||
INLINE char float64_lt_quiet( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return isless(a, b);
|
||||
|
||||
}
|
||||
INLINE char float64_unordered( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return isunordered(a, b);
|
||||
|
||||
}
|
||||
char float64_compare( float64, float64 STATUS_PARAM );
|
||||
char float64_compare_quiet( float64, float64 STATUS_PARAM );
|
||||
char float64_is_signaling_nan( float64 );
|
||||
|
||||
INLINE float64 float64_abs(float64 a)
|
||||
{
|
||||
return fabs(a);
|
||||
}
|
||||
|
||||
INLINE float64 float64_chs(float64 a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int floatx80_to_int32( floatx80 STATUS_PARAM );
|
||||
int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM );
|
||||
int64_t floatx80_to_int64( floatx80 STATUS_PARAM);
|
||||
int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM);
|
||||
float32 floatx80_to_float32( floatx80 STATUS_PARAM );
|
||||
float64 floatx80_to_float64( floatx80 STATUS_PARAM );
|
||||
#ifdef FLOAT128
|
||||
float128 floatx80_to_float128( floatx80 STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM );
|
||||
INLINE floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
INLINE floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
INLINE floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
INLINE floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a / b;
|
||||
}
|
||||
floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_sqrt( floatx80 STATUS_PARAM );
|
||||
INLINE char floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
INLINE char floatx80_le( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b;
|
||||
}
|
||||
INLINE char floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
INLINE char floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b && a >= b;
|
||||
}
|
||||
INLINE char floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return islessequal(a, b);
|
||||
}
|
||||
INLINE char floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return isless(a, b);
|
||||
|
||||
}
|
||||
INLINE char floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return isunordered(a, b);
|
||||
|
||||
}
|
||||
char floatx80_compare( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_is_signaling_nan( floatx80 );
|
||||
|
||||
INLINE floatx80 floatx80_abs(floatx80 a)
|
||||
{
|
||||
return fabsl(a);
|
||||
}
|
||||
|
||||
INLINE floatx80 floatx80_chs(floatx80 a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,464 @@
|
||||
|
||||
/*============================================================================
|
||||
|
||||
This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
|
||||
Arithmetic Package, Release 2b.
|
||||
|
||||
Written by John R. Hauser. This work was made possible in part by the
|
||||
International Computer Science Institute, located at Suite 600, 1947 Center
|
||||
Street, Berkeley, California 94704. Funding was partially provided by the
|
||||
National Science Foundation under grant MIP-9311980. The original version
|
||||
of this code was written as part of a project to build a fixed-point vector
|
||||
processor in collaboration with the University of California at Berkeley,
|
||||
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
|
||||
is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
|
||||
arithmetic/SoftFloat.html'.
|
||||
|
||||
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
|
||||
been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
|
||||
RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
|
||||
AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
|
||||
COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
|
||||
EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
|
||||
INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
|
||||
OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
|
||||
|
||||
Derivative works are acceptable, even for commercial purposes, so long as
|
||||
(1) the source code for the derivative work includes prominent notice that
|
||||
the work is derivative, and (2) the source code includes prominent notice with
|
||||
these four paragraphs for those parts of this code that are retained.
|
||||
|
||||
=============================================================================*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Underflow tininess-detection mode, statically initialized to default value.
|
||||
| (The declaration in `softfloat.h' must match the `int8' type here.)
|
||||
*----------------------------------------------------------------------------*/
|
||||
int8 float_detect_tininess = float_tininess_after_rounding;
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Raises the exceptions specified by `flags'. Floating-point traps can be
|
||||
| defined here if desired. It is currently not possible for such a trap
|
||||
| to substitute a result value. If traps are not implemented, this routine
|
||||
| should be simply `float_exception_flags |= flags;'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
void float_raise( int8 flags STATUS_PARAM )
|
||||
{
|
||||
|
||||
STATUS(float_exception_flags) |= flags;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Internal canonical NaN format.
|
||||
*----------------------------------------------------------------------------*/
|
||||
typedef struct {
|
||||
flag sign;
|
||||
bits64 high, low;
|
||||
} commonNaNT;
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| The pattern for a default generated single-precision NaN.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#define float32_default_nan 0xFFC00000
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the single-precision floating-point value `a' is a NaN;
|
||||
| otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float32_is_nan( float32 a )
|
||||
{
|
||||
|
||||
return ( 0xFF000000 < (bits32) ( a<<1 ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the single-precision floating-point value `a' is a signaling
|
||||
| NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float32_is_signaling_nan( float32 a )
|
||||
{
|
||||
|
||||
return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the single-precision floating-point NaN
|
||||
| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
|
||||
| exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static commonNaNT float32ToCommonNaN( float32 a STATUS_PARAM )
|
||||
{
|
||||
commonNaNT z;
|
||||
|
||||
if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR );
|
||||
z.sign = a>>31;
|
||||
z.low = 0;
|
||||
z.high = ( (bits64) a )<<41;
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the canonical NaN `a' to the single-
|
||||
| precision floating-point format.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float32 commonNaNToFloat32( commonNaNT a )
|
||||
{
|
||||
|
||||
return ( ( (bits32) a.sign )<<31 ) | 0x7FC00000 | ( a.high>>41 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Takes two single-precision floating-point values `a' and `b', one of which
|
||||
| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
|
||||
| signaling NaN, the invalid exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
|
||||
|
||||
aIsNaN = float32_is_nan( a );
|
||||
aIsSignalingNaN = float32_is_signaling_nan( a );
|
||||
bIsNaN = float32_is_nan( b );
|
||||
bIsSignalingNaN = float32_is_signaling_nan( b );
|
||||
a |= 0x00400000;
|
||||
b |= 0x00400000;
|
||||
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
if ( aIsSignalingNaN ) {
|
||||
if ( bIsSignalingNaN ) goto returnLargerSignificand;
|
||||
return bIsNaN ? b : a;
|
||||
}
|
||||
else if ( aIsNaN ) {
|
||||
if ( bIsSignalingNaN | ! bIsNaN ) return a;
|
||||
returnLargerSignificand:
|
||||
if ( (bits32) ( a<<1 ) < (bits32) ( b<<1 ) ) return b;
|
||||
if ( (bits32) ( b<<1 ) < (bits32) ( a<<1 ) ) return a;
|
||||
return ( a < b ) ? a : b;
|
||||
}
|
||||
else {
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| The pattern for a default generated double-precision NaN.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#define float64_default_nan LIT64( 0xFFF8000000000000 )
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the double-precision floating-point value `a' is a NaN;
|
||||
| otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float64_is_nan( float64 a )
|
||||
{
|
||||
|
||||
return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the double-precision floating-point value `a' is a signaling
|
||||
| NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float64_is_signaling_nan( float64 a )
|
||||
{
|
||||
|
||||
return
|
||||
( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
|
||||
&& ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the double-precision floating-point NaN
|
||||
| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
|
||||
| exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static commonNaNT float64ToCommonNaN( float64 a STATUS_PARAM)
|
||||
{
|
||||
commonNaNT z;
|
||||
|
||||
if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
z.sign = a>>63;
|
||||
z.low = 0;
|
||||
z.high = a<<12;
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the canonical NaN `a' to the double-
|
||||
| precision floating-point format.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float64 commonNaNToFloat64( commonNaNT a )
|
||||
{
|
||||
|
||||
return
|
||||
( ( (bits64) a.sign )<<63 )
|
||||
| LIT64( 0x7FF8000000000000 )
|
||||
| ( a.high>>12 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Takes two double-precision floating-point values `a' and `b', one of which
|
||||
| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
|
||||
| signaling NaN, the invalid exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
|
||||
|
||||
aIsNaN = float64_is_nan( a );
|
||||
aIsSignalingNaN = float64_is_signaling_nan( a );
|
||||
bIsNaN = float64_is_nan( b );
|
||||
bIsSignalingNaN = float64_is_signaling_nan( b );
|
||||
a |= LIT64( 0x0008000000000000 );
|
||||
b |= LIT64( 0x0008000000000000 );
|
||||
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
if ( aIsSignalingNaN ) {
|
||||
if ( bIsSignalingNaN ) goto returnLargerSignificand;
|
||||
return bIsNaN ? b : a;
|
||||
}
|
||||
else if ( aIsNaN ) {
|
||||
if ( bIsSignalingNaN | ! bIsNaN ) return a;
|
||||
returnLargerSignificand:
|
||||
if ( (bits64) ( a<<1 ) < (bits64) ( b<<1 ) ) return b;
|
||||
if ( (bits64) ( b<<1 ) < (bits64) ( a<<1 ) ) return a;
|
||||
return ( a < b ) ? a : b;
|
||||
}
|
||||
else {
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| The pattern for a default generated extended double-precision NaN. The
|
||||
| `high' and `low' values hold the most- and least-significant bits,
|
||||
| respectively.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#define floatx80_default_nan_high 0xFFFF
|
||||
#define floatx80_default_nan_low LIT64( 0xC000000000000000 )
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the extended double-precision floating-point value `a' is a
|
||||
| NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag floatx80_is_nan( floatx80 a )
|
||||
{
|
||||
|
||||
return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the extended double-precision floating-point value `a' is a
|
||||
| signaling NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag floatx80_is_signaling_nan( floatx80 a )
|
||||
{
|
||||
bits64 aLow;
|
||||
|
||||
aLow = a.low & ~ LIT64( 0x4000000000000000 );
|
||||
return
|
||||
( ( a.high & 0x7FFF ) == 0x7FFF )
|
||||
&& (bits64) ( aLow<<1 )
|
||||
&& ( a.low == aLow );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the extended double-precision floating-
|
||||
| point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the
|
||||
| invalid exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static commonNaNT floatx80ToCommonNaN( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
commonNaNT z;
|
||||
|
||||
if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
z.sign = a.high>>15;
|
||||
z.low = 0;
|
||||
z.high = a.low<<1;
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the canonical NaN `a' to the extended
|
||||
| double-precision floating-point format.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static floatx80 commonNaNToFloatx80( commonNaNT a )
|
||||
{
|
||||
floatx80 z;
|
||||
|
||||
z.low = LIT64( 0xC000000000000000 ) | ( a.high>>1 );
|
||||
z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF;
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Takes two extended double-precision floating-point values `a' and `b', one
|
||||
| of which is a NaN, and returns the appropriate NaN result. If either `a' or
|
||||
| `b' is a signaling NaN, the invalid exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
|
||||
|
||||
aIsNaN = floatx80_is_nan( a );
|
||||
aIsSignalingNaN = floatx80_is_signaling_nan( a );
|
||||
bIsNaN = floatx80_is_nan( b );
|
||||
bIsSignalingNaN = floatx80_is_signaling_nan( b );
|
||||
a.low |= LIT64( 0xC000000000000000 );
|
||||
b.low |= LIT64( 0xC000000000000000 );
|
||||
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
if ( aIsSignalingNaN ) {
|
||||
if ( bIsSignalingNaN ) goto returnLargerSignificand;
|
||||
return bIsNaN ? b : a;
|
||||
}
|
||||
else if ( aIsNaN ) {
|
||||
if ( bIsSignalingNaN | ! bIsNaN ) return a;
|
||||
returnLargerSignificand:
|
||||
if ( a.low < b.low ) return b;
|
||||
if ( b.low < a.low ) return a;
|
||||
return ( a.high < b.high ) ? a : b;
|
||||
}
|
||||
else {
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef FLOAT128
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| The pattern for a default generated quadruple-precision NaN. The `high' and
|
||||
| `low' values hold the most- and least-significant bits, respectively.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#define float128_default_nan_high LIT64( 0xFFFF800000000000 )
|
||||
#define float128_default_nan_low LIT64( 0x0000000000000000 )
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the quadruple-precision floating-point value `a' is a NaN;
|
||||
| otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float128_is_nan( float128 a )
|
||||
{
|
||||
|
||||
return
|
||||
( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) )
|
||||
&& ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the quadruple-precision floating-point value `a' is a
|
||||
| signaling NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float128_is_signaling_nan( float128 a )
|
||||
{
|
||||
|
||||
return
|
||||
( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE )
|
||||
&& ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the quadruple-precision floating-point NaN
|
||||
| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
|
||||
| exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static commonNaNT float128ToCommonNaN( float128 a STATUS_PARAM)
|
||||
{
|
||||
commonNaNT z;
|
||||
|
||||
if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
z.sign = a.high>>63;
|
||||
shortShift128Left( a.high, a.low, 16, &z.high, &z.low );
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the canonical NaN `a' to the quadruple-
|
||||
| precision floating-point format.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float128 commonNaNToFloat128( commonNaNT a )
|
||||
{
|
||||
float128 z;
|
||||
|
||||
shift128Right( a.high, a.low, 16, &z.high, &z.low );
|
||||
z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF800000000000 );
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Takes two quadruple-precision floating-point values `a' and `b', one of
|
||||
| which is a NaN, and returns the appropriate NaN result. If either `a' or
|
||||
| `b' is a signaling NaN, the invalid exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM)
|
||||
{
|
||||
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
|
||||
|
||||
aIsNaN = float128_is_nan( a );
|
||||
aIsSignalingNaN = float128_is_signaling_nan( a );
|
||||
bIsNaN = float128_is_nan( b );
|
||||
bIsSignalingNaN = float128_is_signaling_nan( b );
|
||||
a.high |= LIT64( 0x0000800000000000 );
|
||||
b.high |= LIT64( 0x0000800000000000 );
|
||||
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
if ( aIsSignalingNaN ) {
|
||||
if ( bIsSignalingNaN ) goto returnLargerSignificand;
|
||||
return bIsNaN ? b : a;
|
||||
}
|
||||
else if ( aIsNaN ) {
|
||||
if ( bIsSignalingNaN | ! bIsNaN ) return a;
|
||||
returnLargerSignificand:
|
||||
if ( lt128( a.high<<1, a.low, b.high<<1, b.low ) ) return b;
|
||||
if ( lt128( b.high<<1, b.low, a.high<<1, a.low ) ) return a;
|
||||
return ( a.high < b.high ) ? a : b;
|
||||
}
|
||||
else {
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+5320
File diff suppressed because it is too large.
Load diff
+398
@@ -0,0 +1,398 @@
|
||||
/*============================================================================
|
||||
|
||||
This C header file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic
|
||||
Package, Release 2b.
|
||||
|
||||
Written by John R. Hauser. This work was made possible in part by the
|
||||
International Computer Science Institute, located at Suite 600, 1947 Center
|
||||
Street, Berkeley, California 94704. Funding was partially provided by the
|
||||
National Science Foundation under grant MIP-9311980. The original version
|
||||
of this code was written as part of a project to build a fixed-point vector
|
||||
processor in collaboration with the University of California at Berkeley,
|
||||
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
|
||||
is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
|
||||
arithmetic/SoftFloat.html'.
|
||||
|
||||
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
|
||||
been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
|
||||
RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
|
||||
AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
|
||||
COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
|
||||
EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
|
||||
INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
|
||||
OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
|
||||
|
||||
Derivative works are acceptable, even for commercial purposes, so long as
|
||||
(1) the source code for the derivative work includes prominent notice that
|
||||
the work is derivative, and (2) the source code includes prominent notice with
|
||||
these four paragraphs for those parts of this code that are retained.
|
||||
|
||||
=============================================================================*/
|
||||
|
||||
#ifndef SOFTFLOAT_H
|
||||
#define SOFTFLOAT_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "config.h"
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Each of the following `typedef's defines the most convenient type that holds
|
||||
| integers of at least as many bits as specified. For example, `uint8' should
|
||||
| be the most convenient type that can hold unsigned integers of as many as
|
||||
| 8 bits. The `flag' type must be able to hold either a 0 or 1. For most
|
||||
| implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed
|
||||
| to the same as `int'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
typedef char flag;
|
||||
typedef uint8_t uint8;
|
||||
typedef int8_t int8;
|
||||
typedef int uint16;
|
||||
typedef int int16;
|
||||
typedef unsigned int uint32;
|
||||
typedef signed int int32;
|
||||
typedef uint64_t uint64;
|
||||
typedef int64_t int64;
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Each of the following `typedef's defines a type that holds integers
|
||||
| of _exactly_ the number of bits specified. For instance, for most
|
||||
| implementation of C, `bits16' and `sbits16' should be `typedef'ed to
|
||||
| `unsigned short int' and `signed short int' (or `short int'), respectively.
|
||||
*----------------------------------------------------------------------------*/
|
||||
typedef uint8_t bits8;
|
||||
typedef int8_t sbits8;
|
||||
typedef uint16_t bits16;
|
||||
typedef int16_t sbits16;
|
||||
typedef uint32_t bits32;
|
||||
typedef int32_t sbits32;
|
||||
typedef uint64_t bits64;
|
||||
typedef int64_t sbits64;
|
||||
|
||||
#define LIT64( a ) a##LL
|
||||
#define INLINE static inline
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| The macro `FLOATX80' must be defined to enable the extended double-precision
|
||||
| floating-point format `floatx80'. If this macro is not defined, the
|
||||
| `floatx80' type will not be defined, and none of the functions that either
|
||||
| input or output the `floatx80' type will be defined. The same applies to
|
||||
| the `FLOAT128' macro and the quadruple-precision format `float128'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#ifdef CONFIG_SOFTFLOAT
|
||||
/* bit exact soft float support */
|
||||
#define FLOATX80
|
||||
#define FLOAT128
|
||||
#else
|
||||
/* native float support */
|
||||
#if (defined(__i386__) || defined(__x86_64__)) && !defined(_BSD)
|
||||
#define FLOATX80
|
||||
#endif
|
||||
#endif /* !CONFIG_SOFTFLOAT */
|
||||
|
||||
#define STATUS_PARAM , float_status *status
|
||||
#define STATUS(field) status->field
|
||||
#define STATUS_VAR , status
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point ordering relations
|
||||
*----------------------------------------------------------------------------*/
|
||||
enum {
|
||||
float_relation_less = -1,
|
||||
float_relation_equal = 0,
|
||||
float_relation_greater = 1,
|
||||
float_relation_unordered = 2
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SOFTFLOAT
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point types.
|
||||
*----------------------------------------------------------------------------*/
|
||||
typedef uint32_t float32;
|
||||
typedef uint64_t float64;
|
||||
#ifdef FLOATX80
|
||||
typedef struct {
|
||||
uint64_t low;
|
||||
uint16_t high;
|
||||
} floatx80;
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
typedef struct {
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
uint64_t high, low;
|
||||
#else
|
||||
uint64_t low, high;
|
||||
#endif
|
||||
} float128;
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point underflow tininess-detection mode.
|
||||
*----------------------------------------------------------------------------*/
|
||||
enum {
|
||||
float_tininess_after_rounding = 0,
|
||||
float_tininess_before_rounding = 1
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point rounding mode.
|
||||
*----------------------------------------------------------------------------*/
|
||||
enum {
|
||||
float_round_nearest_even = 0,
|
||||
float_round_down = 1,
|
||||
float_round_up = 2,
|
||||
float_round_to_zero = 3
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point exception flags.
|
||||
*----------------------------------------------------------------------------*/
|
||||
enum {
|
||||
float_flag_invalid = 1,
|
||||
float_flag_divbyzero = 4,
|
||||
float_flag_overflow = 8,
|
||||
float_flag_underflow = 16,
|
||||
float_flag_inexact = 32
|
||||
};
|
||||
|
||||
typedef struct float_status {
|
||||
signed char float_detect_tininess;
|
||||
signed char float_rounding_mode;
|
||||
signed char float_exception_flags;
|
||||
#ifdef FLOATX80
|
||||
signed char floatx80_rounding_precision;
|
||||
#endif
|
||||
} float_status;
|
||||
|
||||
void set_float_rounding_mode(int val STATUS_PARAM);
|
||||
void set_float_exception_flags(int val STATUS_PARAM);
|
||||
INLINE int get_float_exception_flags(float_status *status)
|
||||
{
|
||||
return STATUS(float_exception_flags);
|
||||
}
|
||||
#ifdef FLOATX80
|
||||
void set_floatx80_rounding_precision(int val STATUS_PARAM);
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Routine to raise any or all of the software IEC/IEEE floating-point
|
||||
| exception flags.
|
||||
*----------------------------------------------------------------------------*/
|
||||
void float_raise( signed char STATUS_PARAM);
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE integer-to-floating-point conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 int32_to_float32( int STATUS_PARAM );
|
||||
float64 int32_to_float64( int STATUS_PARAM );
|
||||
float32 uint32_to_float32( unsigned int STATUS_PARAM );
|
||||
float64 uint32_to_float64( unsigned int STATUS_PARAM );
|
||||
#ifdef FLOATX80
|
||||
floatx80 int32_to_floatx80( int STATUS_PARAM );
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 int32_to_float128( int STATUS_PARAM );
|
||||
#endif
|
||||
float32 int64_to_float32( int64_t STATUS_PARAM );
|
||||
float64 int64_to_float64( int64_t STATUS_PARAM );
|
||||
#ifdef FLOATX80
|
||||
floatx80 int64_to_floatx80( int64_t STATUS_PARAM );
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 int64_to_float128( int64_t STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float32_to_int32( float32 STATUS_PARAM );
|
||||
int float32_to_int32_round_to_zero( float32 STATUS_PARAM );
|
||||
unsigned int float32_to_uint32( float32 STATUS_PARAM );
|
||||
unsigned int float32_to_uint32_round_to_zero( float32 STATUS_PARAM );
|
||||
int64_t float32_to_int64( float32 STATUS_PARAM );
|
||||
int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM );
|
||||
float64 float32_to_float64( float32 STATUS_PARAM );
|
||||
#ifdef FLOATX80
|
||||
floatx80 float32_to_floatx80( float32 STATUS_PARAM );
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 float32_to_float128( float32 STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 float32_round_to_int( float32 STATUS_PARAM );
|
||||
float32 float32_add( float32, float32 STATUS_PARAM );
|
||||
float32 float32_sub( float32, float32 STATUS_PARAM );
|
||||
float32 float32_mul( float32, float32 STATUS_PARAM );
|
||||
float32 float32_div( float32, float32 STATUS_PARAM );
|
||||
float32 float32_rem( float32, float32 STATUS_PARAM );
|
||||
float32 float32_sqrt( float32 STATUS_PARAM );
|
||||
char float32_eq( float32, float32 STATUS_PARAM );
|
||||
char float32_le( float32, float32 STATUS_PARAM );
|
||||
char float32_lt( float32, float32 STATUS_PARAM );
|
||||
char float32_eq_signaling( float32, float32 STATUS_PARAM );
|
||||
char float32_le_quiet( float32, float32 STATUS_PARAM );
|
||||
char float32_lt_quiet( float32, float32 STATUS_PARAM );
|
||||
char float32_compare( float32, float32 STATUS_PARAM );
|
||||
char float32_compare_quiet( float32, float32 STATUS_PARAM );
|
||||
char float32_is_signaling_nan( float32 );
|
||||
|
||||
INLINE float32 float32_abs(float32 a)
|
||||
{
|
||||
return a & 0x7fffffff;
|
||||
}
|
||||
|
||||
INLINE float32 float32_chs(float32 a)
|
||||
{
|
||||
return a ^ 0x80000000;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float64_to_int32( float64 STATUS_PARAM );
|
||||
int float64_to_int32_round_to_zero( float64 STATUS_PARAM );
|
||||
unsigned int float64_to_uint32( float64 STATUS_PARAM );
|
||||
unsigned int float64_to_uint32_round_to_zero( float64 STATUS_PARAM );
|
||||
int64_t float64_to_int64( float64 STATUS_PARAM );
|
||||
int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM );
|
||||
float32 float64_to_float32( float64 STATUS_PARAM );
|
||||
#ifdef FLOATX80
|
||||
floatx80 float64_to_floatx80( float64 STATUS_PARAM );
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 float64_to_float128( float64 STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float64 float64_round_to_int( float64 STATUS_PARAM );
|
||||
float64 float64_add( float64, float64 STATUS_PARAM );
|
||||
float64 float64_sub( float64, float64 STATUS_PARAM );
|
||||
float64 float64_mul( float64, float64 STATUS_PARAM );
|
||||
float64 float64_div( float64, float64 STATUS_PARAM );
|
||||
float64 float64_rem( float64, float64 STATUS_PARAM );
|
||||
float64 float64_sqrt( float64 STATUS_PARAM );
|
||||
char float64_eq( float64, float64 STATUS_PARAM );
|
||||
char float64_le( float64, float64 STATUS_PARAM );
|
||||
char float64_lt( float64, float64 STATUS_PARAM );
|
||||
char float64_eq_signaling( float64, float64 STATUS_PARAM );
|
||||
char float64_le_quiet( float64, float64 STATUS_PARAM );
|
||||
char float64_lt_quiet( float64, float64 STATUS_PARAM );
|
||||
char float64_compare( float64, float64 STATUS_PARAM );
|
||||
char float64_compare_quiet( float64, float64 STATUS_PARAM );
|
||||
char float64_is_signaling_nan( float64 );
|
||||
|
||||
INLINE float64 float64_abs(float64 a)
|
||||
{
|
||||
return a & 0x7fffffffffffffffLL;
|
||||
}
|
||||
|
||||
INLINE float64 float64_chs(float64 a)
|
||||
{
|
||||
return a ^ 0x8000000000000000LL;
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int floatx80_to_int32( floatx80 STATUS_PARAM );
|
||||
int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM );
|
||||
int64_t floatx80_to_int64( floatx80 STATUS_PARAM );
|
||||
int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM );
|
||||
float32 floatx80_to_float32( floatx80 STATUS_PARAM );
|
||||
float64 floatx80_to_float64( floatx80 STATUS_PARAM );
|
||||
#ifdef FLOAT128
|
||||
float128 floatx80_to_float128( floatx80 STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_add( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_sub( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_mul( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_div( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_sqrt( floatx80 STATUS_PARAM );
|
||||
char floatx80_eq( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_le( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_lt( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_eq_signaling( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_is_signaling_nan( floatx80 );
|
||||
|
||||
INLINE floatx80 floatx80_abs(floatx80 a)
|
||||
{
|
||||
a.high &= 0x7fff;
|
||||
return a;
|
||||
}
|
||||
|
||||
INLINE floatx80 floatx80_chs(floatx80 a)
|
||||
{
|
||||
a.high ^= 0x8000;
|
||||
return a;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef FLOAT128
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE quadruple-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float128_to_int32( float128 STATUS_PARAM );
|
||||
int float128_to_int32_round_to_zero( float128 STATUS_PARAM );
|
||||
int64_t float128_to_int64( float128 STATUS_PARAM );
|
||||
int64_t float128_to_int64_round_to_zero( float128 STATUS_PARAM );
|
||||
float32 float128_to_float32( float128 STATUS_PARAM );
|
||||
float64 float128_to_float64( float128 STATUS_PARAM );
|
||||
#ifdef FLOATX80
|
||||
floatx80 float128_to_floatx80( float128 STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE quadruple-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float128 float128_round_to_int( float128 STATUS_PARAM );
|
||||
float128 float128_add( float128, float128 STATUS_PARAM );
|
||||
float128 float128_sub( float128, float128 STATUS_PARAM );
|
||||
float128 float128_mul( float128, float128 STATUS_PARAM );
|
||||
float128 float128_div( float128, float128 STATUS_PARAM );
|
||||
float128 float128_rem( float128, float128 STATUS_PARAM );
|
||||
float128 float128_sqrt( float128 STATUS_PARAM );
|
||||
char float128_eq( float128, float128 STATUS_PARAM );
|
||||
char float128_le( float128, float128 STATUS_PARAM );
|
||||
char float128_lt( float128, float128 STATUS_PARAM );
|
||||
char float128_eq_signaling( float128, float128 STATUS_PARAM );
|
||||
char float128_le_quiet( float128, float128 STATUS_PARAM );
|
||||
char float128_lt_quiet( float128, float128 STATUS_PARAM );
|
||||
char float128_is_signaling_nan( float128 );
|
||||
|
||||
INLINE float128 float128_abs(float128 a)
|
||||
{
|
||||
a.high &= 0x7fffffffffffffffLL;
|
||||
return a;
|
||||
}
|
||||
|
||||
INLINE float128 float128_chs(float128 a)
|
||||
{
|
||||
a.high ^= 0x8000000000000000LL;
|
||||
return a;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#else /* CONFIG_SOFTFLOAT */
|
||||
|
||||
#include "softfloat-native.h"
|
||||
|
||||
#endif /* !CONFIG_SOFTFLOAT */
|
||||
|
||||
#endif /* !SOFTFLOAT_H */
|
||||
@@ -0,0 +1,792 @@
|
||||
/*
|
||||
* gdb server stub
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "qemu.h"
|
||||
#else
|
||||
#include "vl.h"
|
||||
#endif
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <signal.h>
|
||||
|
||||
//#define DEBUG_GDB
|
||||
|
||||
enum RSState {
|
||||
RS_IDLE,
|
||||
RS_GETLINE,
|
||||
RS_CHKSUM1,
|
||||
RS_CHKSUM2,
|
||||
};
|
||||
/* XXX: This is not thread safe. Do we care? */
|
||||
static int gdbserver_fd = -1;
|
||||
|
||||
typedef struct GDBState {
|
||||
enum RSState state; /* parsing state */
|
||||
int fd;
|
||||
char line_buf[4096];
|
||||
int line_buf_index;
|
||||
int line_csum;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
int running_state;
|
||||
#endif
|
||||
} GDBState;
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* XXX: remove this hack. */
|
||||
static GDBState gdbserver_state;
|
||||
#endif
|
||||
|
||||
static int get_char(GDBState *s)
|
||||
{
|
||||
uint8_t ch;
|
||||
int ret;
|
||||
|
||||
for(;;) {
|
||||
ret = read(s->fd, &ch, 1);
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
return -1;
|
||||
} else if (ret == 0) {
|
||||
return -1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
static void put_buffer(GDBState *s, const uint8_t *buf, int len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
while (len > 0) {
|
||||
ret = write(s->fd, buf, len);
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
return;
|
||||
} else {
|
||||
buf += ret;
|
||||
len -= ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline int fromhex(int v)
|
||||
{
|
||||
if (v >= '0' && v <= '9')
|
||||
return v - '0';
|
||||
else if (v >= 'A' && v <= 'F')
|
||||
return v - 'A' + 10;
|
||||
else if (v >= 'a' && v <= 'f')
|
||||
return v - 'a' + 10;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int tohex(int v)
|
||||
{
|
||||
if (v < 10)
|
||||
return v + '0';
|
||||
else
|
||||
return v - 10 + 'a';
|
||||
}
|
||||
|
||||
static void memtohex(char *buf, const uint8_t *mem, int len)
|
||||
{
|
||||
int i, c;
|
||||
char *q;
|
||||
q = buf;
|
||||
for(i = 0; i < len; i++) {
|
||||
c = mem[i];
|
||||
*q++ = tohex(c >> 4);
|
||||
*q++ = tohex(c & 0xf);
|
||||
}
|
||||
*q = '\0';
|
||||
}
|
||||
|
||||
static void hextomem(uint8_t *mem, const char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < len; i++) {
|
||||
mem[i] = (fromhex(buf[0]) << 4) | fromhex(buf[1]);
|
||||
buf += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* return -1 if error, 0 if OK */
|
||||
static int put_packet(GDBState *s, char *buf)
|
||||
{
|
||||
char buf1[3];
|
||||
int len, csum, ch, i;
|
||||
|
||||
#ifdef DEBUG_GDB
|
||||
printf("reply='%s'\n", buf);
|
||||
#endif
|
||||
|
||||
for(;;) {
|
||||
buf1[0] = '$';
|
||||
put_buffer(s, buf1, 1);
|
||||
len = strlen(buf);
|
||||
put_buffer(s, buf, len);
|
||||
csum = 0;
|
||||
for(i = 0; i < len; i++) {
|
||||
csum += buf[i];
|
||||
}
|
||||
buf1[0] = '#';
|
||||
buf1[1] = tohex((csum >> 4) & 0xf);
|
||||
buf1[2] = tohex((csum) & 0xf);
|
||||
|
||||
put_buffer(s, buf1, 3);
|
||||
|
||||
ch = get_char(s);
|
||||
if (ch < 0)
|
||||
return -1;
|
||||
if (ch == '+')
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
|
||||
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
{
|
||||
uint32_t *registers = (uint32_t *)mem_buf;
|
||||
int i, fpus;
|
||||
|
||||
for(i = 0; i < 8; i++) {
|
||||
registers[i] = env->regs[i];
|
||||
}
|
||||
registers[8] = env->eip;
|
||||
registers[9] = env->eflags;
|
||||
registers[10] = env->segs[R_CS].selector;
|
||||
registers[11] = env->segs[R_SS].selector;
|
||||
registers[12] = env->segs[R_DS].selector;
|
||||
registers[13] = env->segs[R_ES].selector;
|
||||
registers[14] = env->segs[R_FS].selector;
|
||||
registers[15] = env->segs[R_GS].selector;
|
||||
/* XXX: convert floats */
|
||||
for(i = 0; i < 8; i++) {
|
||||
memcpy(mem_buf + 16 * 4 + i * 10, &env->fpregs[i], 10);
|
||||
}
|
||||
registers[36] = env->fpuc;
|
||||
fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
|
||||
registers[37] = fpus;
|
||||
registers[38] = 0; /* XXX: convert tags */
|
||||
registers[39] = 0; /* fiseg */
|
||||
registers[40] = 0; /* fioff */
|
||||
registers[41] = 0; /* foseg */
|
||||
registers[42] = 0; /* fooff */
|
||||
registers[43] = 0; /* fop */
|
||||
|
||||
for(i = 0; i < 16; i++)
|
||||
tswapls(®isters[i]);
|
||||
for(i = 36; i < 44; i++)
|
||||
tswapls(®isters[i]);
|
||||
return 44 * 4;
|
||||
}
|
||||
|
||||
static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
{
|
||||
uint32_t *registers = (uint32_t *)mem_buf;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 8; i++) {
|
||||
env->regs[i] = tswapl(registers[i]);
|
||||
}
|
||||
env->eip = tswapl(registers[8]);
|
||||
env->eflags = tswapl(registers[9]);
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#define LOAD_SEG(index, sreg)\
|
||||
if (tswapl(registers[index]) != env->segs[sreg].selector)\
|
||||
cpu_x86_load_seg(env, sreg, tswapl(registers[index]));
|
||||
LOAD_SEG(10, R_CS);
|
||||
LOAD_SEG(11, R_SS);
|
||||
LOAD_SEG(12, R_DS);
|
||||
LOAD_SEG(13, R_ES);
|
||||
LOAD_SEG(14, R_FS);
|
||||
LOAD_SEG(15, R_GS);
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif defined (TARGET_PPC)
|
||||
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
{
|
||||
uint32_t *registers = (uint32_t *)mem_buf, tmp;
|
||||
int i;
|
||||
|
||||
/* fill in gprs */
|
||||
for(i = 0; i < 32; i++) {
|
||||
registers[i] = tswapl(env->gpr[i]);
|
||||
}
|
||||
/* fill in fprs */
|
||||
for (i = 0; i < 32; i++) {
|
||||
registers[(i * 2) + 32] = tswapl(*((uint32_t *)&env->fpr[i]));
|
||||
registers[(i * 2) + 33] = tswapl(*((uint32_t *)&env->fpr[i] + 1));
|
||||
}
|
||||
/* nip, msr, ccr, lnk, ctr, xer, mq */
|
||||
registers[96] = tswapl(env->nip);
|
||||
registers[97] = tswapl(_load_msr(env));
|
||||
tmp = 0;
|
||||
for (i = 0; i < 8; i++)
|
||||
tmp |= env->crf[i] << (32 - ((i + 1) * 4));
|
||||
registers[98] = tswapl(tmp);
|
||||
registers[99] = tswapl(env->lr);
|
||||
registers[100] = tswapl(env->ctr);
|
||||
registers[101] = tswapl(_load_xer(env));
|
||||
registers[102] = 0;
|
||||
|
||||
return 103 * 4;
|
||||
}
|
||||
|
||||
static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
{
|
||||
uint32_t *registers = (uint32_t *)mem_buf;
|
||||
int i;
|
||||
|
||||
/* fill in gprs */
|
||||
for (i = 0; i < 32; i++) {
|
||||
env->gpr[i] = tswapl(registers[i]);
|
||||
}
|
||||
/* fill in fprs */
|
||||
for (i = 0; i < 32; i++) {
|
||||
*((uint32_t *)&env->fpr[i]) = tswapl(registers[(i * 2) + 32]);
|
||||
*((uint32_t *)&env->fpr[i] + 1) = tswapl(registers[(i * 2) + 33]);
|
||||
}
|
||||
/* nip, msr, ccr, lnk, ctr, xer, mq */
|
||||
env->nip = tswapl(registers[96]);
|
||||
_store_msr(env, tswapl(registers[97]));
|
||||
registers[98] = tswapl(registers[98]);
|
||||
for (i = 0; i < 8; i++)
|
||||
env->crf[i] = (registers[98] >> (32 - ((i + 1) * 4))) & 0xF;
|
||||
env->lr = tswapl(registers[99]);
|
||||
env->ctr = tswapl(registers[100]);
|
||||
_store_xer(env, tswapl(registers[101]));
|
||||
}
|
||||
#elif defined (TARGET_SPARC)
|
||||
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
{
|
||||
uint32_t *registers = (uint32_t *)mem_buf, tmp;
|
||||
int i;
|
||||
|
||||
/* fill in g0..g7 */
|
||||
for(i = 0; i < 7; i++) {
|
||||
registers[i] = tswapl(env->gregs[i]);
|
||||
}
|
||||
/* fill in register window */
|
||||
for(i = 0; i < 24; i++) {
|
||||
registers[i + 8] = tswapl(env->regwptr[i]);
|
||||
}
|
||||
/* fill in fprs */
|
||||
for (i = 0; i < 32; i++) {
|
||||
registers[i + 32] = tswapl(*((uint32_t *)&env->fpr[i]));
|
||||
}
|
||||
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
|
||||
registers[64] = tswapl(env->y);
|
||||
tmp = GET_PSR(env);
|
||||
registers[65] = tswapl(tmp);
|
||||
registers[66] = tswapl(env->wim);
|
||||
registers[67] = tswapl(env->tbr);
|
||||
registers[68] = tswapl(env->pc);
|
||||
registers[69] = tswapl(env->npc);
|
||||
registers[70] = tswapl(env->fsr);
|
||||
registers[71] = 0; /* csr */
|
||||
registers[72] = 0;
|
||||
|
||||
return 73 * 4;
|
||||
}
|
||||
|
||||
static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
{
|
||||
uint32_t *registers = (uint32_t *)mem_buf;
|
||||
int i;
|
||||
|
||||
/* fill in g0..g7 */
|
||||
for(i = 0; i < 7; i++) {
|
||||
env->gregs[i] = tswapl(registers[i]);
|
||||
}
|
||||
/* fill in register window */
|
||||
for(i = 0; i < 24; i++) {
|
||||
env->regwptr[i] = tswapl(registers[i]);
|
||||
}
|
||||
/* fill in fprs */
|
||||
for (i = 0; i < 32; i++) {
|
||||
*((uint32_t *)&env->fpr[i]) = tswapl(registers[i + 32]);
|
||||
}
|
||||
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
|
||||
env->y = tswapl(registers[64]);
|
||||
PUT_PSR(env, tswapl(registers[65]));
|
||||
env->wim = tswapl(registers[66]);
|
||||
env->tbr = tswapl(registers[67]);
|
||||
env->pc = tswapl(registers[68]);
|
||||
env->npc = tswapl(registers[69]);
|
||||
env->fsr = tswapl(registers[70]);
|
||||
}
|
||||
#elif defined (TARGET_ARM)
|
||||
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
{
|
||||
int i;
|
||||
uint8_t *ptr;
|
||||
|
||||
ptr = mem_buf;
|
||||
/* 16 core integer registers (4 bytes each). */
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
*(uint32_t *)ptr = tswapl(env->regs[i]);
|
||||
ptr += 4;
|
||||
}
|
||||
/* 8 FPA registers (12 bytes each), FPS (4 bytes).
|
||||
Not yet implemented. */
|
||||
memset (ptr, 0, 8 * 12 + 4);
|
||||
ptr += 8 * 12 + 4;
|
||||
/* CPSR (4 bytes). */
|
||||
*(uint32_t *)ptr = tswapl (env->cpsr);
|
||||
ptr += 4;
|
||||
|
||||
return ptr - mem_buf;
|
||||
}
|
||||
|
||||
static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
{
|
||||
int i;
|
||||
uint8_t *ptr;
|
||||
|
||||
ptr = mem_buf;
|
||||
/* Core integer registers. */
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
env->regs[i] = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
}
|
||||
/* Ignore FPA regs and scr. */
|
||||
ptr += 8 * 12 + 4;
|
||||
env->cpsr = tswapl(*(uint32_t *)ptr);
|
||||
}
|
||||
#else
|
||||
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
|
||||
{
|
||||
const char *p;
|
||||
int ch, reg_size, type;
|
||||
char buf[4096];
|
||||
uint8_t mem_buf[2000];
|
||||
uint32_t *registers;
|
||||
uint32_t addr, len;
|
||||
|
||||
#ifdef DEBUG_GDB
|
||||
printf("command='%s'\n", line_buf);
|
||||
#endif
|
||||
p = line_buf;
|
||||
ch = *p++;
|
||||
switch(ch) {
|
||||
case '?':
|
||||
/* TODO: Make this return the correct value for user-mode. */
|
||||
snprintf(buf, sizeof(buf), "S%02x", SIGTRAP);
|
||||
put_packet(s, buf);
|
||||
break;
|
||||
case 'c':
|
||||
if (*p != '\0') {
|
||||
addr = strtoul(p, (char **)&p, 16);
|
||||
#if defined(TARGET_I386)
|
||||
env->eip = addr;
|
||||
#elif defined (TARGET_PPC)
|
||||
env->nip = addr;
|
||||
#elif defined (TARGET_SPARC)
|
||||
env->pc = addr;
|
||||
env->npc = addr + 4;
|
||||
#endif
|
||||
}
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
s->running_state = 1;
|
||||
#else
|
||||
vm_start();
|
||||
#endif
|
||||
return RS_IDLE;
|
||||
case 's':
|
||||
if (*p != '\0') {
|
||||
addr = strtoul(p, (char **)&p, 16);
|
||||
#if defined(TARGET_I386)
|
||||
env->eip = addr;
|
||||
#elif defined (TARGET_PPC)
|
||||
env->nip = addr;
|
||||
#elif defined (TARGET_SPARC)
|
||||
env->pc = addr;
|
||||
env->npc = addr + 4;
|
||||
#endif
|
||||
}
|
||||
cpu_single_step(env, 1);
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
s->running_state = 1;
|
||||
#else
|
||||
vm_start();
|
||||
#endif
|
||||
return RS_IDLE;
|
||||
case 'g':
|
||||
reg_size = cpu_gdb_read_registers(env, mem_buf);
|
||||
memtohex(buf, mem_buf, reg_size);
|
||||
put_packet(s, buf);
|
||||
break;
|
||||
case 'G':
|
||||
registers = (void *)mem_buf;
|
||||
len = strlen(p) / 2;
|
||||
hextomem((uint8_t *)registers, p, len);
|
||||
cpu_gdb_write_registers(env, mem_buf, len);
|
||||
put_packet(s, "OK");
|
||||
break;
|
||||
case 'm':
|
||||
addr = strtoul(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
len = strtoul(p, NULL, 16);
|
||||
if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0)
|
||||
memset(mem_buf, 0, len);
|
||||
memtohex(buf, mem_buf, len);
|
||||
put_packet(s, buf);
|
||||
break;
|
||||
case 'M':
|
||||
addr = strtoul(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
len = strtoul(p, (char **)&p, 16);
|
||||
if (*p == ':')
|
||||
p++;
|
||||
hextomem(mem_buf, p, len);
|
||||
if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0)
|
||||
put_packet(s, "E14");
|
||||
else
|
||||
put_packet(s, "OK");
|
||||
break;
|
||||
case 'Z':
|
||||
type = strtoul(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
addr = strtoul(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
len = strtoul(p, (char **)&p, 16);
|
||||
if (type == 0 || type == 1) {
|
||||
if (cpu_breakpoint_insert(env, addr) < 0)
|
||||
goto breakpoint_error;
|
||||
put_packet(s, "OK");
|
||||
} else {
|
||||
breakpoint_error:
|
||||
put_packet(s, "E22");
|
||||
}
|
||||
break;
|
||||
case 'z':
|
||||
type = strtoul(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
addr = strtoul(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
len = strtoul(p, (char **)&p, 16);
|
||||
if (type == 0 || type == 1) {
|
||||
cpu_breakpoint_remove(env, addr);
|
||||
put_packet(s, "OK");
|
||||
} else {
|
||||
goto breakpoint_error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// unknown_command:
|
||||
/* put empty packet */
|
||||
buf[0] = '\0';
|
||||
put_packet(s, buf);
|
||||
break;
|
||||
}
|
||||
return RS_IDLE;
|
||||
}
|
||||
|
||||
extern void tb_flush(CPUState *env);
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static void gdb_vm_stopped(void *opaque, int reason)
|
||||
{
|
||||
GDBState *s = opaque;
|
||||
char buf[256];
|
||||
int ret;
|
||||
|
||||
/* disable single step if it was enable */
|
||||
cpu_single_step(cpu_single_env, 0);
|
||||
|
||||
if (reason == EXCP_DEBUG) {
|
||||
tb_flush(cpu_single_env);
|
||||
ret = SIGTRAP;
|
||||
}
|
||||
else
|
||||
ret = 0;
|
||||
snprintf(buf, sizeof(buf), "S%02x", ret);
|
||||
put_packet(s, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void gdb_read_byte(GDBState *s, CPUState *env, int ch)
|
||||
{
|
||||
int i, csum;
|
||||
char reply[1];
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (vm_running) {
|
||||
/* when the CPU is running, we cannot do anything except stop
|
||||
it when receiving a char */
|
||||
vm_stop(EXCP_INTERRUPT);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
switch(s->state) {
|
||||
case RS_IDLE:
|
||||
if (ch == '$') {
|
||||
s->line_buf_index = 0;
|
||||
s->state = RS_GETLINE;
|
||||
}
|
||||
break;
|
||||
case RS_GETLINE:
|
||||
if (ch == '#') {
|
||||
s->state = RS_CHKSUM1;
|
||||
} else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
|
||||
s->state = RS_IDLE;
|
||||
} else {
|
||||
s->line_buf[s->line_buf_index++] = ch;
|
||||
}
|
||||
break;
|
||||
case RS_CHKSUM1:
|
||||
s->line_buf[s->line_buf_index] = '\0';
|
||||
s->line_csum = fromhex(ch) << 4;
|
||||
s->state = RS_CHKSUM2;
|
||||
break;
|
||||
case RS_CHKSUM2:
|
||||
s->line_csum |= fromhex(ch);
|
||||
csum = 0;
|
||||
for(i = 0; i < s->line_buf_index; i++) {
|
||||
csum += s->line_buf[i];
|
||||
}
|
||||
if (s->line_csum != (csum & 0xff)) {
|
||||
reply[0] = '-';
|
||||
put_buffer(s, reply, 1);
|
||||
s->state = RS_IDLE;
|
||||
} else {
|
||||
reply[0] = '+';
|
||||
put_buffer(s, reply, 1);
|
||||
s->state = gdb_handle_packet(s, env, s->line_buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
int
|
||||
gdb_handlesig (CPUState *env, int sig)
|
||||
{
|
||||
GDBState *s;
|
||||
char buf[256];
|
||||
int n;
|
||||
|
||||
if (gdbserver_fd < 0)
|
||||
return sig;
|
||||
|
||||
s = &gdbserver_state;
|
||||
|
||||
/* disable single step if it was enabled */
|
||||
cpu_single_step(env, 0);
|
||||
tb_flush(env);
|
||||
|
||||
if (sig != 0)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "S%02x", sig);
|
||||
put_packet(s, buf);
|
||||
}
|
||||
|
||||
sig = 0;
|
||||
s->state = RS_IDLE;
|
||||
s->running_state = 0;
|
||||
while (s->running_state == 0) {
|
||||
n = read (s->fd, buf, 256);
|
||||
if (n > 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
gdb_read_byte (s, env, buf[i]);
|
||||
}
|
||||
else if (n == 0 || errno != EAGAIN)
|
||||
{
|
||||
/* XXX: Connection closed. Should probably wait for annother
|
||||
connection before continuing. */
|
||||
return sig;
|
||||
}
|
||||
}
|
||||
return sig;
|
||||
}
|
||||
|
||||
/* Tell the remote gdb that the process has exited. */
|
||||
void gdb_exit(CPUState *env, int code)
|
||||
{
|
||||
GDBState *s;
|
||||
char buf[4];
|
||||
|
||||
if (gdbserver_fd < 0)
|
||||
return;
|
||||
|
||||
s = &gdbserver_state;
|
||||
|
||||
snprintf(buf, sizeof(buf), "W%02x", code);
|
||||
put_packet(s, buf);
|
||||
}
|
||||
|
||||
#else
|
||||
static int gdb_can_read(void *opaque)
|
||||
{
|
||||
return 256;
|
||||
}
|
||||
|
||||
static void gdb_read(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
GDBState *s = opaque;
|
||||
int i;
|
||||
if (size == 0) {
|
||||
/* end of connection */
|
||||
qemu_del_vm_stop_handler(gdb_vm_stopped, s);
|
||||
qemu_del_fd_read_handler(s->fd);
|
||||
qemu_free(s);
|
||||
vm_start();
|
||||
} else {
|
||||
for(i = 0; i < size; i++)
|
||||
gdb_read_byte(s, cpu_single_env, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void gdb_accept(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
GDBState *s;
|
||||
struct sockaddr_in sockaddr;
|
||||
socklen_t len;
|
||||
int val, fd;
|
||||
|
||||
for(;;) {
|
||||
len = sizeof(sockaddr);
|
||||
fd = accept(gdbserver_fd, (struct sockaddr *)&sockaddr, &len);
|
||||
if (fd < 0 && errno != EINTR) {
|
||||
perror("accept");
|
||||
return;
|
||||
} else if (fd >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* set short latency */
|
||||
val = 1;
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
s = &gdbserver_state;
|
||||
memset (s, 0, sizeof (GDBState));
|
||||
#else
|
||||
s = qemu_mallocz(sizeof(GDBState));
|
||||
if (!s) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
s->fd = fd;
|
||||
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
/* stop the VM */
|
||||
vm_stop(EXCP_INTERRUPT);
|
||||
|
||||
/* start handling I/O */
|
||||
qemu_add_fd_read_handler(s->fd, gdb_can_read, gdb_read, s);
|
||||
/* when the VM is stopped, the following callback is called */
|
||||
qemu_add_vm_stop_handler(gdb_vm_stopped, s);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int gdbserver_open(int port)
|
||||
{
|
||||
struct sockaddr_in sockaddr;
|
||||
int fd, val, ret;
|
||||
|
||||
fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* allow fast reuse */
|
||||
val = 1;
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
|
||||
|
||||
sockaddr.sin_family = AF_INET;
|
||||
sockaddr.sin_port = htons(port);
|
||||
sockaddr.sin_addr.s_addr = 0;
|
||||
ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
|
||||
if (ret < 0) {
|
||||
perror("bind");
|
||||
return -1;
|
||||
}
|
||||
ret = listen(fd, 0);
|
||||
if (ret < 0) {
|
||||
perror("listen");
|
||||
return -1;
|
||||
}
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
return fd;
|
||||
}
|
||||
|
||||
int gdbserver_start(int port)
|
||||
{
|
||||
gdbserver_fd = gdbserver_open(port);
|
||||
if (gdbserver_fd < 0)
|
||||
return -1;
|
||||
/* accept connections */
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
gdb_accept (NULL, NULL, 0);
|
||||
#else
|
||||
qemu_add_fd_read_handler(gdbserver_fd, NULL, gdb_accept, NULL);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef GDBSTUB_H
|
||||
#define GDBSTUB_H
|
||||
|
||||
#define DEFAULT_GDBSTUB_PORT 1234
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
int gdb_handlesig (CPUState *, int);
|
||||
void gdb_exit(CPUState *, int);
|
||||
#endif
|
||||
int gdbserver_start(int);
|
||||
|
||||
#endif
|
||||
Whitespace-only changes.
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* QEMU ADB support
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
/* ADB commands */
|
||||
#define ADB_BUSRESET 0x00
|
||||
#define ADB_FLUSH 0x01
|
||||
#define ADB_WRITEREG 0x08
|
||||
#define ADB_READREG 0x0c
|
||||
|
||||
/* ADB device commands */
|
||||
#define ADB_CMD_SELF_TEST 0xff
|
||||
#define ADB_CMD_CHANGE_ID 0xfe
|
||||
#define ADB_CMD_CHANGE_ID_AND_ACT 0xfd
|
||||
#define ADB_CMD_CHANGE_ID_AND_ENABLE 0x00
|
||||
|
||||
/* ADB default device IDs (upper 4 bits of ADB command byte) */
|
||||
#define ADB_DONGLE 1
|
||||
#define ADB_KEYBOARD 2
|
||||
#define ADB_MOUSE 3
|
||||
#define ADB_TABLET 4
|
||||
#define ADB_MODEM 5
|
||||
#define ADB_MISC 7
|
||||
|
||||
/* error codes */
|
||||
#define ADB_RET_NOTPRESENT (-2)
|
||||
|
||||
int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
|
||||
{
|
||||
ADBDevice *d;
|
||||
int devaddr, cmd, i;
|
||||
|
||||
cmd = buf[0] & 0xf;
|
||||
if (cmd == ADB_BUSRESET) {
|
||||
for(i = 0; i < s->nb_devices; i++) {
|
||||
d = &s->devices[i];
|
||||
if (d->devreset) {
|
||||
d->devreset(d);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
devaddr = buf[0] >> 4;
|
||||
for(i = 0; i < s->nb_devices; i++) {
|
||||
d = &s->devices[i];
|
||||
if (d->devaddr == devaddr) {
|
||||
return d->devreq(d, obuf, buf, len);
|
||||
}
|
||||
}
|
||||
return ADB_RET_NOTPRESENT;
|
||||
}
|
||||
|
||||
/* XXX: move that to cuda ? */
|
||||
int adb_poll(ADBBusState *s, uint8_t *obuf)
|
||||
{
|
||||
ADBDevice *d;
|
||||
int olen, i;
|
||||
uint8_t buf[1];
|
||||
|
||||
olen = 0;
|
||||
for(i = 0; i < s->nb_devices; i++) {
|
||||
if (s->poll_index >= s->nb_devices)
|
||||
s->poll_index = 0;
|
||||
d = &s->devices[s->poll_index];
|
||||
buf[0] = ADB_READREG | (d->devaddr << 4);
|
||||
olen = adb_request(s, obuf + 1, buf, 1);
|
||||
/* if there is data, we poll again the same device */
|
||||
if (olen > 0) {
|
||||
obuf[0] = buf[0];
|
||||
olen++;
|
||||
break;
|
||||
}
|
||||
s->poll_index++;
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
|
||||
ADBDevice *adb_register_device(ADBBusState *s, int devaddr,
|
||||
ADBDeviceRequest *devreq,
|
||||
ADBDeviceReset *devreset,
|
||||
void *opaque)
|
||||
{
|
||||
ADBDevice *d;
|
||||
if (s->nb_devices >= MAX_ADB_DEVICES)
|
||||
return NULL;
|
||||
d = &s->devices[s->nb_devices++];
|
||||
d->bus = s;
|
||||
d->devaddr = devaddr;
|
||||
d->devreq = devreq;
|
||||
d->devreset = devreset;
|
||||
d->opaque = opaque;
|
||||
return d;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* Keyboard ADB device */
|
||||
|
||||
typedef struct KBDState {
|
||||
uint8_t data[128];
|
||||
int rptr, wptr, count;
|
||||
} KBDState;
|
||||
|
||||
static const uint8_t pc_to_adb_keycode[256] = {
|
||||
0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48,
|
||||
12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54, 0, 1,
|
||||
2, 3, 5, 4, 38, 40, 37, 41, 39, 50, 56, 42, 6, 7, 8, 9,
|
||||
11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96,
|
||||
97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83,
|
||||
84, 85, 82, 65, 0, 0, 10,103,111, 0, 0,110, 81, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 94, 0, 93, 0, 0, 0, 0, 0, 0,104,102, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76,125, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 75, 0, 0,124, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,115, 62,116, 0, 59, 0, 60, 0,119,
|
||||
61,121,114,117, 0, 0, 0, 0, 0, 0, 0, 55,126, 0,127, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
static void adb_kbd_put_keycode(void *opaque, int keycode)
|
||||
{
|
||||
ADBDevice *d = opaque;
|
||||
KBDState *s = d->opaque;
|
||||
|
||||
if (s->count < sizeof(s->data)) {
|
||||
s->data[s->wptr] = keycode;
|
||||
if (++s->wptr == sizeof(s->data))
|
||||
s->wptr = 0;
|
||||
s->count++;
|
||||
}
|
||||
}
|
||||
|
||||
static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf)
|
||||
{
|
||||
static int ext_keycode;
|
||||
KBDState *s = d->opaque;
|
||||
int adb_keycode, keycode;
|
||||
int olen;
|
||||
|
||||
olen = 0;
|
||||
for(;;) {
|
||||
if (s->count == 0)
|
||||
break;
|
||||
keycode = s->data[s->rptr];
|
||||
if (++s->rptr == sizeof(s->data))
|
||||
s->rptr = 0;
|
||||
s->count--;
|
||||
|
||||
if (keycode == 0xe0) {
|
||||
ext_keycode = 1;
|
||||
} else {
|
||||
if (ext_keycode)
|
||||
adb_keycode = pc_to_adb_keycode[keycode | 0x80];
|
||||
else
|
||||
adb_keycode = pc_to_adb_keycode[keycode & 0x7f];
|
||||
obuf[0] = adb_keycode | (keycode & 0x80);
|
||||
/* NOTE: could put a second keycode if needed */
|
||||
obuf[1] = 0xff;
|
||||
olen = 2;
|
||||
ext_keycode = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
|
||||
static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
|
||||
const uint8_t *buf, int len)
|
||||
{
|
||||
KBDState *s = d->opaque;
|
||||
int cmd, reg, olen;
|
||||
|
||||
if ((buf[0] & 0x0f) == ADB_FLUSH) {
|
||||
/* flush keyboard fifo */
|
||||
s->wptr = s->rptr = s->count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd = buf[0] & 0xc;
|
||||
reg = buf[0] & 0x3;
|
||||
olen = 0;
|
||||
switch(cmd) {
|
||||
case ADB_WRITEREG:
|
||||
switch(reg) {
|
||||
case 2:
|
||||
/* LED status */
|
||||
break;
|
||||
case 3:
|
||||
switch(buf[2]) {
|
||||
case ADB_CMD_SELF_TEST:
|
||||
break;
|
||||
case ADB_CMD_CHANGE_ID:
|
||||
case ADB_CMD_CHANGE_ID_AND_ACT:
|
||||
case ADB_CMD_CHANGE_ID_AND_ENABLE:
|
||||
d->devaddr = buf[1] & 0xf;
|
||||
break;
|
||||
default:
|
||||
/* XXX: check this */
|
||||
d->devaddr = buf[1] & 0xf;
|
||||
d->handler = buf[2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ADB_READREG:
|
||||
switch(reg) {
|
||||
case 0:
|
||||
olen = adb_kbd_poll(d, obuf);
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
obuf[0] = 0x00; /* XXX: check this */
|
||||
obuf[1] = 0x07; /* led status */
|
||||
olen = 2;
|
||||
break;
|
||||
case 3:
|
||||
obuf[0] = d->handler;
|
||||
obuf[1] = d->devaddr;
|
||||
olen = 2;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
|
||||
static int adb_kbd_reset(ADBDevice *d)
|
||||
{
|
||||
KBDState *s = d->opaque;
|
||||
|
||||
d->handler = 1;
|
||||
d->devaddr = ADB_KEYBOARD;
|
||||
memset(s, 0, sizeof(KBDState));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void adb_kbd_init(ADBBusState *bus)
|
||||
{
|
||||
ADBDevice *d;
|
||||
KBDState *s;
|
||||
s = qemu_mallocz(sizeof(KBDState));
|
||||
d = adb_register_device(bus, ADB_KEYBOARD, adb_kbd_request,
|
||||
adb_kbd_reset, s);
|
||||
adb_kbd_reset(d);
|
||||
qemu_add_kbd_event_handler(adb_kbd_put_keycode, d);
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* Mouse ADB device */
|
||||
|
||||
typedef struct MouseState {
|
||||
int buttons_state, last_buttons_state;
|
||||
int dx, dy, dz;
|
||||
} MouseState;
|
||||
|
||||
static void adb_mouse_event(void *opaque,
|
||||
int dx1, int dy1, int dz1, int buttons_state)
|
||||
{
|
||||
ADBDevice *d = opaque;
|
||||
MouseState *s = d->opaque;
|
||||
|
||||
s->dx += dx1;
|
||||
s->dy += dy1;
|
||||
s->dz += dz1;
|
||||
s->buttons_state = buttons_state;
|
||||
}
|
||||
|
||||
|
||||
static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf)
|
||||
{
|
||||
MouseState *s = d->opaque;
|
||||
int dx, dy;
|
||||
|
||||
if (s->last_buttons_state == s->buttons_state &&
|
||||
s->dx == 0 && s->dy == 0)
|
||||
return 0;
|
||||
|
||||
dx = s->dx;
|
||||
if (dx < -63)
|
||||
dx = -63;
|
||||
else if (dx > 63)
|
||||
dx = 63;
|
||||
|
||||
dy = s->dy;
|
||||
if (dy < -63)
|
||||
dy = -63;
|
||||
else if (dy > 63)
|
||||
dy = 63;
|
||||
|
||||
s->dx -= dx;
|
||||
s->dy -= dy;
|
||||
s->last_buttons_state = s->buttons_state;
|
||||
|
||||
dx &= 0x7f;
|
||||
dy &= 0x7f;
|
||||
|
||||
if (!(s->buttons_state & MOUSE_EVENT_LBUTTON))
|
||||
dy |= 0x80;
|
||||
if (!(s->buttons_state & MOUSE_EVENT_RBUTTON))
|
||||
dx |= 0x80;
|
||||
|
||||
obuf[0] = dy;
|
||||
obuf[1] = dx;
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
|
||||
const uint8_t *buf, int len)
|
||||
{
|
||||
MouseState *s = d->opaque;
|
||||
int cmd, reg, olen;
|
||||
|
||||
if ((buf[0] & 0x0f) == ADB_FLUSH) {
|
||||
/* flush mouse fifo */
|
||||
s->buttons_state = s->last_buttons_state;
|
||||
s->dx = 0;
|
||||
s->dy = 0;
|
||||
s->dz = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd = buf[0] & 0xc;
|
||||
reg = buf[0] & 0x3;
|
||||
olen = 0;
|
||||
switch(cmd) {
|
||||
case ADB_WRITEREG:
|
||||
switch(reg) {
|
||||
case 2:
|
||||
break;
|
||||
case 3:
|
||||
switch(buf[2]) {
|
||||
case ADB_CMD_SELF_TEST:
|
||||
break;
|
||||
case ADB_CMD_CHANGE_ID:
|
||||
case ADB_CMD_CHANGE_ID_AND_ACT:
|
||||
case ADB_CMD_CHANGE_ID_AND_ENABLE:
|
||||
d->devaddr = buf[1] & 0xf;
|
||||
break;
|
||||
default:
|
||||
/* XXX: check this */
|
||||
d->devaddr = buf[1] & 0xf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ADB_READREG:
|
||||
switch(reg) {
|
||||
case 0:
|
||||
olen = adb_mouse_poll(d, obuf);
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 3:
|
||||
obuf[0] = d->handler;
|
||||
obuf[1] = d->devaddr;
|
||||
olen = 2;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
|
||||
static int adb_mouse_reset(ADBDevice *d)
|
||||
{
|
||||
MouseState *s = d->opaque;
|
||||
|
||||
d->handler = 2;
|
||||
d->devaddr = ADB_MOUSE;
|
||||
memset(s, 0, sizeof(MouseState));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void adb_mouse_init(ADBBusState *bus)
|
||||
{
|
||||
ADBDevice *d;
|
||||
MouseState *s;
|
||||
|
||||
s = qemu_mallocz(sizeof(MouseState));
|
||||
d = adb_register_device(bus, ADB_MOUSE, adb_mouse_request,
|
||||
adb_mouse_reset, s);
|
||||
adb_mouse_reset(d);
|
||||
qemu_add_mouse_event_handler(adb_mouse_event, d);
|
||||
}
|
||||
+313
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* QEMU Adlib emulation
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
#define dolog(...) AUD_log ("adlib", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
#ifdef USE_YMF262
|
||||
#define HAS_YMF262 1
|
||||
#include "ymf262.h"
|
||||
void YMF262UpdateOneQEMU(int which, INT16 *dst, int length);
|
||||
#define SHIFT 2
|
||||
#else
|
||||
#include "fmopl.h"
|
||||
#define SHIFT 1
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define small_delay() Sleep (1)
|
||||
#else
|
||||
#define small_delay() usleep (1)
|
||||
#endif
|
||||
|
||||
#define IO_READ_PROTO(name) \
|
||||
uint32_t name (void *opaque, uint32_t nport)
|
||||
#define IO_WRITE_PROTO(name) \
|
||||
void name (void *opaque, uint32_t nport, uint32_t val)
|
||||
|
||||
static struct {
|
||||
int port;
|
||||
int freq;
|
||||
} conf = {0x220, 44100};
|
||||
|
||||
typedef struct {
|
||||
int enabled;
|
||||
int active;
|
||||
int cparam;
|
||||
int64_t ticks;
|
||||
int bufpos;
|
||||
int16_t *mixbuf;
|
||||
double interval;
|
||||
QEMUTimer *ts, *opl_ts;
|
||||
SWVoice *voice;
|
||||
int left, pos, samples, bytes_per_second, old_free;
|
||||
int refcount;
|
||||
#ifndef USE_YMF262
|
||||
FM_OPL *opl;
|
||||
#endif
|
||||
} AdlibState;
|
||||
|
||||
static AdlibState adlib;
|
||||
|
||||
static IO_WRITE_PROTO(adlib_write)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
int a = nport & 3;
|
||||
int status;
|
||||
|
||||
s->ticks = qemu_get_clock (vm_clock);
|
||||
s->active = 1;
|
||||
AUD_enable (s->voice, 1);
|
||||
|
||||
#ifdef USE_YMF262
|
||||
status = YMF262Write (0, a, val);
|
||||
#else
|
||||
status = OPLWrite (s->opl, a, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
static IO_READ_PROTO(adlib_read)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
uint8_t data;
|
||||
int a = nport & 3;
|
||||
|
||||
#ifdef USE_YMF262
|
||||
(void) s;
|
||||
data = YMF262Read (0, a);
|
||||
#else
|
||||
data = OPLRead (s->opl, a);
|
||||
#endif
|
||||
return data;
|
||||
}
|
||||
|
||||
static void OPL_timer (void *opaque)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
#ifdef USE_YMF262
|
||||
YMF262TimerOver (s->cparam >> 1, s->cparam & 1);
|
||||
#else
|
||||
OPLTimerOver (s->opl, s->cparam);
|
||||
#endif
|
||||
qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);
|
||||
}
|
||||
|
||||
static void YMF262TimerHandler (int c, double interval_Sec)
|
||||
{
|
||||
AdlibState *s = &adlib;
|
||||
if (interval_Sec == 0.0) {
|
||||
qemu_del_timer (s->opl_ts);
|
||||
return;
|
||||
}
|
||||
s->cparam = c;
|
||||
s->interval = ticks_per_sec * interval_Sec;
|
||||
qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);
|
||||
small_delay ();
|
||||
}
|
||||
|
||||
static int write_audio (AdlibState *s, int samples)
|
||||
{
|
||||
int net = 0;
|
||||
int ss = samples;
|
||||
while (samples) {
|
||||
int nbytes = samples << SHIFT;
|
||||
int wbytes = AUD_write (s->voice,
|
||||
s->mixbuf + (s->pos << (SHIFT - 1)),
|
||||
nbytes);
|
||||
int wsampl = wbytes >> SHIFT;
|
||||
samples -= wsampl;
|
||||
s->pos = (s->pos + wsampl) % s->samples;
|
||||
net += wsampl;
|
||||
if (!wbytes)
|
||||
break;
|
||||
}
|
||||
if (net > ss) {
|
||||
dolog ("WARNING: net > ss\n");
|
||||
}
|
||||
return net;
|
||||
}
|
||||
|
||||
static void timer (void *opaque)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
int elapsed, samples, net = 0;
|
||||
|
||||
if (s->refcount)
|
||||
dolog ("refcount=%d\n", s->refcount);
|
||||
|
||||
s->refcount += 1;
|
||||
if (!(s->active && s->enabled))
|
||||
goto reset;
|
||||
|
||||
AUD_run ();
|
||||
|
||||
while (s->left) {
|
||||
int written = write_audio (s, s->left);
|
||||
net += written;
|
||||
if (!written)
|
||||
goto reset2;
|
||||
s->left -= written;
|
||||
}
|
||||
s->pos = 0;
|
||||
|
||||
elapsed = AUD_calc_elapsed (s->voice);
|
||||
if (!elapsed)
|
||||
goto reset2;
|
||||
|
||||
/* elapsed = AUD_get_free (s->voice); */
|
||||
samples = elapsed >> SHIFT;
|
||||
if (!samples)
|
||||
goto reset2;
|
||||
|
||||
samples = audio_MIN (samples, s->samples - s->pos);
|
||||
if (s->left)
|
||||
dolog ("left=%d samples=%d elapsed=%d free=%d\n",
|
||||
s->left, samples, elapsed, AUD_get_free (s->voice));
|
||||
|
||||
if (!samples)
|
||||
goto reset2;
|
||||
|
||||
#ifdef USE_YMF262
|
||||
YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
|
||||
#else
|
||||
YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
|
||||
#endif
|
||||
|
||||
while (samples) {
|
||||
int written = write_audio (s, samples);
|
||||
net += written;
|
||||
if (!written)
|
||||
break;
|
||||
samples -= written;
|
||||
}
|
||||
if (!samples)
|
||||
s->pos = 0;
|
||||
s->left = samples;
|
||||
|
||||
reset2:
|
||||
AUD_adjust (s->voice, net << SHIFT);
|
||||
reset:
|
||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + ticks_per_sec / 1024);
|
||||
s->refcount -= 1;
|
||||
}
|
||||
|
||||
static void Adlib_fini (AdlibState *s)
|
||||
{
|
||||
#ifdef USE_YMF262
|
||||
YMF262Shutdown ();
|
||||
#else
|
||||
if (s->opl) {
|
||||
OPLDestroy (s->opl);
|
||||
s->opl = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s->opl_ts)
|
||||
qemu_free_timer (s->opl_ts);
|
||||
|
||||
if (s->ts)
|
||||
qemu_free_timer (s->ts);
|
||||
|
||||
#define maybe_free(p) if (p) qemu_free (p)
|
||||
maybe_free (s->mixbuf);
|
||||
#undef maybe_free
|
||||
|
||||
s->active = 0;
|
||||
s->enabled = 0;
|
||||
}
|
||||
|
||||
void Adlib_init (void)
|
||||
{
|
||||
AdlibState *s = &adlib;
|
||||
|
||||
memset (s, 0, sizeof (*s));
|
||||
|
||||
#ifdef USE_YMF262
|
||||
if (YMF262Init (1, 14318180, conf.freq)) {
|
||||
dolog ("YMF262Init %d failed\n", conf.freq);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
YMF262SetTimerHandler (0, YMF262TimerHandler, 0);
|
||||
s->enabled = 1;
|
||||
}
|
||||
#else
|
||||
s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq);
|
||||
if (!s->opl) {
|
||||
dolog ("OPLCreate %d failed\n", conf.freq);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
OPLSetTimerHandler (s->opl, YMF262TimerHandler, 0);
|
||||
s->enabled = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
s->opl_ts = qemu_new_timer (vm_clock, OPL_timer, s);
|
||||
if (!s->opl_ts) {
|
||||
dolog ("Can not get timer for adlib emulation\n");
|
||||
Adlib_fini (s);
|
||||
return;
|
||||
}
|
||||
|
||||
s->ts = qemu_new_timer (vm_clock, timer, s);
|
||||
if (!s->opl_ts) {
|
||||
dolog ("Can not get timer for adlib emulation\n");
|
||||
Adlib_fini (s);
|
||||
return;
|
||||
}
|
||||
|
||||
s->voice = AUD_open (s->voice, "adlib", conf.freq, SHIFT, AUD_FMT_S16);
|
||||
if (!s->voice) {
|
||||
Adlib_fini (s);
|
||||
return;
|
||||
}
|
||||
|
||||
s->bytes_per_second = conf.freq << SHIFT;
|
||||
s->samples = AUD_get_buffer_size (s->voice) >> SHIFT;
|
||||
s->mixbuf = qemu_mallocz (s->samples << SHIFT);
|
||||
|
||||
if (!s->mixbuf) {
|
||||
dolog ("not enough memory for adlib mixing buffer (%d)\n",
|
||||
s->samples << SHIFT);
|
||||
Adlib_fini (s);
|
||||
return;
|
||||
}
|
||||
register_ioport_read (0x388, 4, 1, adlib_read, s);
|
||||
register_ioport_write (0x388, 4, 1, adlib_write, s);
|
||||
|
||||
register_ioport_read (conf.port, 4, 1, adlib_read, s);
|
||||
register_ioport_write (conf.port, 4, 1, adlib_write, s);
|
||||
|
||||
register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
|
||||
register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
|
||||
|
||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* APIC support
|
||||
*
|
||||
* Copyright (c) 2004-2005 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_APIC
|
||||
|
||||
/* APIC Local Vector Table */
|
||||
#define APIC_LVT_TIMER 0
|
||||
#define APIC_LVT_THERMAL 1
|
||||
#define APIC_LVT_PERFORM 2
|
||||
#define APIC_LVT_LINT0 3
|
||||
#define APIC_LVT_LINT1 4
|
||||
#define APIC_LVT_ERROR 5
|
||||
#define APIC_LVT_NB 6
|
||||
|
||||
/* APIC delivery modes */
|
||||
#define APIC_DM_FIXED 0
|
||||
#define APIC_DM_LOWPRI 1
|
||||
#define APIC_DM_SMI 2
|
||||
#define APIC_DM_NMI 4
|
||||
#define APIC_DM_INIT 5
|
||||
#define APIC_DM_SIPI 6
|
||||
#define APIC_DM_EXTINT 7
|
||||
|
||||
#define APIC_TRIGGER_EDGE 0
|
||||
#define APIC_TRIGGER_LEVEL 1
|
||||
|
||||
#define APIC_LVT_TIMER_PERIODIC (1<<17)
|
||||
#define APIC_LVT_MASKED (1<<16)
|
||||
#define APIC_LVT_LEVEL_TRIGGER (1<<15)
|
||||
#define APIC_LVT_REMOTE_IRR (1<<14)
|
||||
#define APIC_INPUT_POLARITY (1<<13)
|
||||
#define APIC_SEND_PENDING (1<<12)
|
||||
|
||||
#define ESR_ILLEGAL_ADDRESS (1 << 7)
|
||||
|
||||
#define APIC_SV_ENABLE (1 << 8)
|
||||
|
||||
typedef struct APICState {
|
||||
CPUState *cpu_env;
|
||||
uint32_t apicbase;
|
||||
uint8_t id;
|
||||
uint8_t tpr;
|
||||
uint32_t spurious_vec;
|
||||
uint32_t isr[8]; /* in service register */
|
||||
uint32_t tmr[8]; /* trigger mode register */
|
||||
uint32_t irr[8]; /* interrupt request register */
|
||||
uint32_t lvt[APIC_LVT_NB];
|
||||
uint32_t esr; /* error register */
|
||||
uint32_t icr[2];
|
||||
|
||||
uint32_t divide_conf;
|
||||
int count_shift;
|
||||
uint32_t initial_count;
|
||||
int64_t initial_count_load_time, next_time;
|
||||
QEMUTimer *timer;
|
||||
} APICState;
|
||||
|
||||
static int apic_io_memory;
|
||||
|
||||
void cpu_set_apic_base(CPUState *env, uint64_t val)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
#ifdef DEBUG_APIC
|
||||
printf("cpu_set_apic_base: %016llx\n", val);
|
||||
#endif
|
||||
s->apicbase = (val & 0xfffff000) |
|
||||
(s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
|
||||
/* if disabled, cannot be enabled again */
|
||||
if (!(val & MSR_IA32_APICBASE_ENABLE)) {
|
||||
s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
|
||||
env->cpuid_features &= ~CPUID_APIC;
|
||||
s->spurious_vec &= ~APIC_SV_ENABLE;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t cpu_get_apic_base(CPUState *env)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
#ifdef DEBUG_APIC
|
||||
printf("cpu_get_apic_base: %016llx\n", (uint64_t)s->apicbase);
|
||||
#endif
|
||||
return s->apicbase;
|
||||
}
|
||||
|
||||
void cpu_set_apic_tpr(CPUX86State *env, uint8_t val)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
s->tpr = (val & 0x0f) << 4;
|
||||
}
|
||||
|
||||
uint8_t cpu_get_apic_tpr(CPUX86State *env)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
return s->tpr >> 4;
|
||||
}
|
||||
|
||||
/* return -1 if no bit is set */
|
||||
static int get_highest_priority_int(uint32_t *tab)
|
||||
{
|
||||
int i;
|
||||
for(i = 0;i < 8; i++) {
|
||||
if (tab[i] != 0) {
|
||||
return i * 32 + ffs(tab[i]) - 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void set_bit(uint32_t *tab, int index)
|
||||
{
|
||||
int i, mask;
|
||||
i = index >> 5;
|
||||
mask = 1 << (index & 0x1f);
|
||||
tab[i] |= mask;
|
||||
}
|
||||
|
||||
static inline void reset_bit(uint32_t *tab, int index)
|
||||
{
|
||||
int i, mask;
|
||||
i = index >> 5;
|
||||
mask = 1 << (index & 0x1f);
|
||||
tab[i] &= ~mask;
|
||||
}
|
||||
|
||||
static int apic_get_ppr(APICState *s)
|
||||
{
|
||||
int tpr, isrv, ppr;
|
||||
|
||||
tpr = (s->tpr >> 4);
|
||||
isrv = get_highest_priority_int(s->isr);
|
||||
if (isrv < 0)
|
||||
isrv = 0;
|
||||
isrv >>= 4;
|
||||
if (tpr >= isrv)
|
||||
ppr = s->tpr;
|
||||
else
|
||||
ppr = isrv << 4;
|
||||
return ppr;
|
||||
}
|
||||
|
||||
/* signal the CPU if an irq is pending */
|
||||
static void apic_update_irq(APICState *s)
|
||||
{
|
||||
int irrv, isrv;
|
||||
irrv = get_highest_priority_int(s->irr);
|
||||
if (irrv < 0)
|
||||
return;
|
||||
isrv = get_highest_priority_int(s->isr);
|
||||
/* if the pending irq has less priority, we do not make a new request */
|
||||
if (isrv >= 0 && irrv >= isrv)
|
||||
return;
|
||||
cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
|
||||
static void apic_set_irq(APICState *s, int vector_num, int trigger_mode)
|
||||
{
|
||||
set_bit(s->irr, vector_num);
|
||||
if (trigger_mode)
|
||||
set_bit(s->tmr, vector_num);
|
||||
else
|
||||
reset_bit(s->tmr, vector_num);
|
||||
apic_update_irq(s);
|
||||
}
|
||||
|
||||
static void apic_eoi(APICState *s)
|
||||
{
|
||||
int isrv;
|
||||
isrv = get_highest_priority_int(s->isr);
|
||||
if (isrv < 0)
|
||||
return;
|
||||
reset_bit(s->isr, isrv);
|
||||
apic_update_irq(s);
|
||||
}
|
||||
|
||||
int apic_get_interrupt(CPUState *env)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
int intno;
|
||||
|
||||
/* if the APIC is installed or enabled, we let the 8259 handle the
|
||||
IRQs */
|
||||
if (!s)
|
||||
return -1;
|
||||
if (!(s->spurious_vec & APIC_SV_ENABLE))
|
||||
return -1;
|
||||
|
||||
/* XXX: spurious IRQ handling */
|
||||
intno = get_highest_priority_int(s->irr);
|
||||
if (intno < 0)
|
||||
return -1;
|
||||
reset_bit(s->irr, intno);
|
||||
set_bit(s->isr, intno);
|
||||
apic_update_irq(s);
|
||||
return intno;
|
||||
}
|
||||
|
||||
static uint32_t apic_get_current_count(APICState *s)
|
||||
{
|
||||
int64_t d;
|
||||
uint32_t val;
|
||||
d = (qemu_get_clock(vm_clock) - s->initial_count_load_time) >>
|
||||
s->count_shift;
|
||||
if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
|
||||
/* periodic */
|
||||
val = s->initial_count - (d % (s->initial_count + 1));
|
||||
} else {
|
||||
if (d >= s->initial_count)
|
||||
val = 0;
|
||||
else
|
||||
val = s->initial_count - d;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void apic_timer_update(APICState *s, int64_t current_time)
|
||||
{
|
||||
int64_t next_time, d;
|
||||
|
||||
if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) {
|
||||
d = (current_time - s->initial_count_load_time) >>
|
||||
s->count_shift;
|
||||
if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
|
||||
d = ((d / (s->initial_count + 1)) + 1) * (s->initial_count + 1);
|
||||
} else {
|
||||
if (d >= s->initial_count)
|
||||
goto no_timer;
|
||||
d = s->initial_count + 1;
|
||||
}
|
||||
next_time = s->initial_count_load_time + (d << s->count_shift);
|
||||
qemu_mod_timer(s->timer, next_time);
|
||||
s->next_time = next_time;
|
||||
} else {
|
||||
no_timer:
|
||||
qemu_del_timer(s->timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void apic_timer(void *opaque)
|
||||
{
|
||||
APICState *s = opaque;
|
||||
|
||||
if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) {
|
||||
apic_set_irq(s, s->lvt[APIC_LVT_TIMER] & 0xff, APIC_TRIGGER_EDGE);
|
||||
}
|
||||
apic_timer_update(s, s->next_time);
|
||||
}
|
||||
|
||||
static uint32_t apic_mem_readb(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t apic_mem_readw(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apic_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
}
|
||||
|
||||
static void apic_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
}
|
||||
|
||||
static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
CPUState *env;
|
||||
APICState *s;
|
||||
uint32_t val;
|
||||
int index;
|
||||
|
||||
env = cpu_single_env;
|
||||
if (!env)
|
||||
return 0;
|
||||
s = env->apic_state;
|
||||
|
||||
index = (addr >> 4) & 0xff;
|
||||
switch(index) {
|
||||
case 0x02: /* id */
|
||||
val = s->id << 24;
|
||||
break;
|
||||
case 0x03: /* version */
|
||||
val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */
|
||||
break;
|
||||
case 0x08:
|
||||
val = s->tpr;
|
||||
break;
|
||||
case 0x0a:
|
||||
/* ppr */
|
||||
val = apic_get_ppr(s);
|
||||
break;
|
||||
case 0x0f:
|
||||
val = s->spurious_vec;
|
||||
break;
|
||||
case 0x10 ... 0x17:
|
||||
val = s->isr[index & 7];
|
||||
break;
|
||||
case 0x18 ... 0x1f:
|
||||
val = s->tmr[index & 7];
|
||||
break;
|
||||
case 0x20 ... 0x27:
|
||||
val = s->irr[index & 7];
|
||||
break;
|
||||
case 0x28:
|
||||
val = s->esr;
|
||||
break;
|
||||
case 0x32 ... 0x37:
|
||||
val = s->lvt[index - 0x32];
|
||||
break;
|
||||
case 0x30:
|
||||
case 0x31:
|
||||
val = s->icr[index & 1];
|
||||
break;
|
||||
case 0x38:
|
||||
val = s->initial_count;
|
||||
break;
|
||||
case 0x39:
|
||||
val = apic_get_current_count(s);
|
||||
break;
|
||||
case 0x3e:
|
||||
val = s->divide_conf;
|
||||
break;
|
||||
default:
|
||||
s->esr |= ESR_ILLEGAL_ADDRESS;
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_APIC
|
||||
printf("APIC read: %08x = %08x\n", (uint32_t)addr, val);
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
CPUState *env;
|
||||
APICState *s;
|
||||
int index;
|
||||
|
||||
env = cpu_single_env;
|
||||
if (!env)
|
||||
return;
|
||||
s = env->apic_state;
|
||||
|
||||
#ifdef DEBUG_APIC
|
||||
printf("APIC write: %08x = %08x\n", (uint32_t)addr, val);
|
||||
#endif
|
||||
|
||||
index = (addr >> 4) & 0xff;
|
||||
switch(index) {
|
||||
case 0x02:
|
||||
s->id = (val >> 24);
|
||||
break;
|
||||
case 0x08:
|
||||
s->tpr = val;
|
||||
break;
|
||||
case 0x0b: /* EOI */
|
||||
apic_eoi(s);
|
||||
break;
|
||||
case 0x0f:
|
||||
s->spurious_vec = val & 0x1ff;
|
||||
break;
|
||||
case 0x30:
|
||||
case 0x31:
|
||||
s->icr[index & 1] = val;
|
||||
break;
|
||||
case 0x32 ... 0x37:
|
||||
{
|
||||
int n = index - 0x32;
|
||||
s->lvt[n] = val;
|
||||
if (n == APIC_LVT_TIMER)
|
||||
apic_timer_update(s, qemu_get_clock(vm_clock));
|
||||
}
|
||||
break;
|
||||
case 0x38:
|
||||
s->initial_count = val;
|
||||
s->initial_count_load_time = qemu_get_clock(vm_clock);
|
||||
apic_timer_update(s, s->initial_count_load_time);
|
||||
break;
|
||||
case 0x3e:
|
||||
{
|
||||
int v;
|
||||
s->divide_conf = val & 0xb;
|
||||
v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4);
|
||||
s->count_shift = (v + 1) & 7;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
s->esr |= ESR_ILLEGAL_ADDRESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static CPUReadMemoryFunc *apic_mem_read[3] = {
|
||||
apic_mem_readb,
|
||||
apic_mem_readw,
|
||||
apic_mem_readl,
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *apic_mem_write[3] = {
|
||||
apic_mem_writeb,
|
||||
apic_mem_writew,
|
||||
apic_mem_writel,
|
||||
};
|
||||
|
||||
int apic_init(CPUState *env)
|
||||
{
|
||||
APICState *s;
|
||||
int i;
|
||||
|
||||
s = malloc(sizeof(APICState));
|
||||
if (!s)
|
||||
return -1;
|
||||
memset(s, 0, sizeof(*s));
|
||||
env->apic_state = s;
|
||||
s->cpu_env = env;
|
||||
s->apicbase = 0xfee00000 |
|
||||
MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE;
|
||||
for(i = 0; i < APIC_LVT_NB; i++)
|
||||
s->lvt[i] = 1 << 16; /* mask LVT */
|
||||
s->spurious_vec = 0xff;
|
||||
|
||||
if (apic_io_memory == 0) {
|
||||
/* NOTE: the APIC is directly connected to the CPU - it is not
|
||||
on the global memory bus. */
|
||||
apic_io_memory = cpu_register_io_memory(0, apic_mem_read,
|
||||
apic_mem_write, NULL);
|
||||
cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000, apic_io_memory);
|
||||
}
|
||||
s->timer = qemu_new_timer(vm_clock, apic_timer, s);
|
||||
return 0;
|
||||
}
|
||||
+3118
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* QEMU Cirrus CLGD 54xx VGA Emulator.
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
static void
|
||||
glue(cirrus_bitblt_rop_fwd_, ROP_NAME)(CirrusVGAState *s,
|
||||
uint8_t *dst,const uint8_t *src,
|
||||
int dstpitch,int srcpitch,
|
||||
int bltwidth,int bltheight)
|
||||
{
|
||||
int x,y;
|
||||
dstpitch -= bltwidth;
|
||||
srcpitch -= bltwidth;
|
||||
for (y = 0; y < bltheight; y++) {
|
||||
for (x = 0; x < bltwidth; x++) {
|
||||
ROP_OP(*dst, *src);
|
||||
dst++;
|
||||
src++;
|
||||
}
|
||||
dst += dstpitch;
|
||||
src += srcpitch;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
glue(cirrus_bitblt_rop_bkwd_, ROP_NAME)(CirrusVGAState *s,
|
||||
uint8_t *dst,const uint8_t *src,
|
||||
int dstpitch,int srcpitch,
|
||||
int bltwidth,int bltheight)
|
||||
{
|
||||
int x,y;
|
||||
dstpitch += bltwidth;
|
||||
srcpitch += bltwidth;
|
||||
for (y = 0; y < bltheight; y++) {
|
||||
for (x = 0; x < bltwidth; x++) {
|
||||
ROP_OP(*dst, *src);
|
||||
dst--;
|
||||
src--;
|
||||
}
|
||||
dst += dstpitch;
|
||||
src += srcpitch;
|
||||
}
|
||||
}
|
||||
|
||||
#define DEPTH 8
|
||||
#include "cirrus_vga_rop2.h"
|
||||
|
||||
#define DEPTH 16
|
||||
#include "cirrus_vga_rop2.h"
|
||||
|
||||
#define DEPTH 24
|
||||
#include "cirrus_vga_rop2.h"
|
||||
|
||||
#define DEPTH 32
|
||||
#include "cirrus_vga_rop2.h"
|
||||
|
||||
#undef ROP_NAME
|
||||
#undef ROP_OP
|
||||
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* QEMU Cirrus CLGD 54xx VGA Emulator.
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if DEPTH == 8
|
||||
#define PUTPIXEL() ROP_OP(d[0], col)
|
||||
#elif DEPTH == 16
|
||||
#define PUTPIXEL() ROP_OP(((uint16_t *)d)[0], col);
|
||||
#elif DEPTH == 24
|
||||
#define PUTPIXEL() ROP_OP(d[0], col); \
|
||||
ROP_OP(d[1], (col >> 8)); \
|
||||
ROP_OP(d[2], (col >> 16))
|
||||
#elif DEPTH == 32
|
||||
#define PUTPIXEL() ROP_OP(((uint32_t *)d)[0], col)
|
||||
#else
|
||||
#error unsupported DEPTH
|
||||
#endif
|
||||
|
||||
static void
|
||||
glue(glue(glue(cirrus_patternfill_, ROP_NAME), _),DEPTH)
|
||||
(CirrusVGAState * s, uint8_t * dst,
|
||||
const uint8_t * src,
|
||||
int dstpitch, int srcpitch,
|
||||
int bltwidth, int bltheight)
|
||||
{
|
||||
uint8_t *d;
|
||||
int x, y, pattern_y, pattern_pitch, pattern_x;
|
||||
unsigned int col;
|
||||
const uint8_t *src1;
|
||||
#if DEPTH == 24
|
||||
int skipleft = s->gr[0x2f] & 0x1f;
|
||||
#else
|
||||
int skipleft = (s->gr[0x2f] & 0x07) * (DEPTH / 8);
|
||||
#endif
|
||||
|
||||
#if DEPTH == 8
|
||||
pattern_pitch = 8;
|
||||
#elif DEPTH == 16
|
||||
pattern_pitch = 16;
|
||||
#else
|
||||
pattern_pitch = 32;
|
||||
#endif
|
||||
pattern_y = s->cirrus_blt_srcaddr & 7;
|
||||
pattern_x = skipleft;
|
||||
for(y = 0; y < bltheight; y++) {
|
||||
d = dst + skipleft;
|
||||
src1 = src + pattern_y * pattern_pitch;
|
||||
for (x = skipleft; x < bltwidth; x += (DEPTH / 8)) {
|
||||
#if DEPTH == 8
|
||||
col = src1[pattern_x];
|
||||
pattern_x = (pattern_x + 1) & 7;
|
||||
#elif DEPTH == 16
|
||||
col = ((uint16_t *)(src1 + pattern_x))[0];
|
||||
pattern_x = (pattern_x + 2) & 15;
|
||||
#elif DEPTH == 24
|
||||
{
|
||||
const uint8_t *src2 = src1 + pattern_x * 3;
|
||||
col = src2[0] | (src2[1] << 8) | (src2[2] << 16);
|
||||
pattern_x = (pattern_x + 1) & 7;
|
||||
}
|
||||
#else
|
||||
col = ((uint32_t *)(src1 + pattern_x))[0];
|
||||
pattern_x = (pattern_x + 4) & 31;
|
||||
#endif
|
||||
PUTPIXEL();
|
||||
d += (DEPTH / 8);
|
||||
}
|
||||
pattern_y = (pattern_y + 1) & 7;
|
||||
dst += dstpitch;
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: srcpitch is ignored */
|
||||
static void
|
||||
glue(glue(glue(cirrus_colorexpand_transp_, ROP_NAME), _),DEPTH)
|
||||
(CirrusVGAState * s, uint8_t * dst,
|
||||
const uint8_t * src,
|
||||
int dstpitch, int srcpitch,
|
||||
int bltwidth, int bltheight)
|
||||
{
|
||||
uint8_t *d;
|
||||
int x, y;
|
||||
unsigned bits, bits_xor;
|
||||
unsigned int col;
|
||||
unsigned bitmask;
|
||||
unsigned index;
|
||||
#if DEPTH == 24
|
||||
int dstskipleft = s->gr[0x2f] & 0x1f;
|
||||
int srcskipleft = dstskipleft / 3;
|
||||
#else
|
||||
int srcskipleft = s->gr[0x2f] & 0x07;
|
||||
int dstskipleft = srcskipleft * (DEPTH / 8);
|
||||
#endif
|
||||
|
||||
if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) {
|
||||
bits_xor = 0xff;
|
||||
col = s->cirrus_blt_bgcol;
|
||||
} else {
|
||||
bits_xor = 0x00;
|
||||
col = s->cirrus_blt_fgcol;
|
||||
}
|
||||
|
||||
for(y = 0; y < bltheight; y++) {
|
||||
bitmask = 0x80 >> srcskipleft;
|
||||
bits = *src++ ^ bits_xor;
|
||||
d = dst + dstskipleft;
|
||||
for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
|
||||
if ((bitmask & 0xff) == 0) {
|
||||
bitmask = 0x80;
|
||||
bits = *src++ ^ bits_xor;
|
||||
}
|
||||
index = (bits & bitmask);
|
||||
if (index) {
|
||||
PUTPIXEL();
|
||||
}
|
||||
d += (DEPTH / 8);
|
||||
bitmask >>= 1;
|
||||
}
|
||||
dst += dstpitch;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
glue(glue(glue(cirrus_colorexpand_, ROP_NAME), _),DEPTH)
|
||||
(CirrusVGAState * s, uint8_t * dst,
|
||||
const uint8_t * src,
|
||||
int dstpitch, int srcpitch,
|
||||
int bltwidth, int bltheight)
|
||||
{
|
||||
uint32_t colors[2];
|
||||
uint8_t *d;
|
||||
int x, y;
|
||||
unsigned bits;
|
||||
unsigned int col;
|
||||
unsigned bitmask;
|
||||
int srcskipleft = s->gr[0x2f] & 0x07;
|
||||
int dstskipleft = srcskipleft * (DEPTH / 8);
|
||||
|
||||
colors[0] = s->cirrus_blt_bgcol;
|
||||
colors[1] = s->cirrus_blt_fgcol;
|
||||
for(y = 0; y < bltheight; y++) {
|
||||
bitmask = 0x80 >> srcskipleft;
|
||||
bits = *src++;
|
||||
d = dst + dstskipleft;
|
||||
for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
|
||||
if ((bitmask & 0xff) == 0) {
|
||||
bitmask = 0x80;
|
||||
bits = *src++;
|
||||
}
|
||||
col = colors[!!(bits & bitmask)];
|
||||
PUTPIXEL();
|
||||
d += (DEPTH / 8);
|
||||
bitmask >>= 1;
|
||||
}
|
||||
dst += dstpitch;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
glue(glue(glue(cirrus_colorexpand_pattern_transp_, ROP_NAME), _),DEPTH)
|
||||
(CirrusVGAState * s, uint8_t * dst,
|
||||
const uint8_t * src,
|
||||
int dstpitch, int srcpitch,
|
||||
int bltwidth, int bltheight)
|
||||
{
|
||||
uint8_t *d;
|
||||
int x, y, bitpos, pattern_y;
|
||||
unsigned int bits, bits_xor;
|
||||
unsigned int col;
|
||||
#if DEPTH == 24
|
||||
int dstskipleft = s->gr[0x2f] & 0x1f;
|
||||
int srcskipleft = dstskipleft / 3;
|
||||
#else
|
||||
int srcskipleft = s->gr[0x2f] & 0x07;
|
||||
int dstskipleft = srcskipleft * (DEPTH / 8);
|
||||
#endif
|
||||
|
||||
if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) {
|
||||
bits_xor = 0xff;
|
||||
col = s->cirrus_blt_bgcol;
|
||||
} else {
|
||||
bits_xor = 0x00;
|
||||
col = s->cirrus_blt_fgcol;
|
||||
}
|
||||
pattern_y = s->cirrus_blt_srcaddr & 7;
|
||||
|
||||
for(y = 0; y < bltheight; y++) {
|
||||
bits = src[pattern_y] ^ bits_xor;
|
||||
bitpos = 7 - srcskipleft;
|
||||
d = dst + dstskipleft;
|
||||
for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
|
||||
if ((bits >> bitpos) & 1) {
|
||||
PUTPIXEL();
|
||||
}
|
||||
d += (DEPTH / 8);
|
||||
bitpos = (bitpos - 1) & 7;
|
||||
}
|
||||
pattern_y = (pattern_y + 1) & 7;
|
||||
dst += dstpitch;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
glue(glue(glue(cirrus_colorexpand_pattern_, ROP_NAME), _),DEPTH)
|
||||
(CirrusVGAState * s, uint8_t * dst,
|
||||
const uint8_t * src,
|
||||
int dstpitch, int srcpitch,
|
||||
int bltwidth, int bltheight)
|
||||
{
|
||||
uint32_t colors[2];
|
||||
uint8_t *d;
|
||||
int x, y, bitpos, pattern_y;
|
||||
unsigned int bits;
|
||||
unsigned int col;
|
||||
int srcskipleft = s->gr[0x2f] & 0x07;
|
||||
int dstskipleft = srcskipleft * (DEPTH / 8);
|
||||
|
||||
colors[0] = s->cirrus_blt_bgcol;
|
||||
colors[1] = s->cirrus_blt_fgcol;
|
||||
pattern_y = s->cirrus_blt_srcaddr & 7;
|
||||
|
||||
for(y = 0; y < bltheight; y++) {
|
||||
bits = src[pattern_y];
|
||||
bitpos = 7 - srcskipleft;
|
||||
d = dst + dstskipleft;
|
||||
for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
|
||||
col = colors[(bits >> bitpos) & 1];
|
||||
PUTPIXEL();
|
||||
d += (DEPTH / 8);
|
||||
bitpos = (bitpos - 1) & 7;
|
||||
}
|
||||
pattern_y = (pattern_y + 1) & 7;
|
||||
dst += dstpitch;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
glue(glue(glue(cirrus_fill_, ROP_NAME), _),DEPTH)
|
||||
(CirrusVGAState *s,
|
||||
uint8_t *dst, int dst_pitch,
|
||||
int width, int height)
|
||||
{
|
||||
uint8_t *d, *d1;
|
||||
uint32_t col;
|
||||
int x, y;
|
||||
|
||||
col = s->cirrus_blt_fgcol;
|
||||
|
||||
d1 = dst;
|
||||
for(y = 0; y < height; y++) {
|
||||
d = d1;
|
||||
for(x = 0; x < width; x += (DEPTH / 8)) {
|
||||
PUTPIXEL();
|
||||
d += (DEPTH / 8);
|
||||
}
|
||||
d1 += dst_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
#undef DEPTH
|
||||
#undef PUTPIXEL
|
||||
@@ -0,0 +1,626 @@
|
||||
/*
|
||||
* QEMU CUDA support
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_CUDA
|
||||
//#define DEBUG_CUDA_PACKET
|
||||
|
||||
/* Bits in B data register: all active low */
|
||||
#define TREQ 0x08 /* Transfer request (input) */
|
||||
#define TACK 0x10 /* Transfer acknowledge (output) */
|
||||
#define TIP 0x20 /* Transfer in progress (output) */
|
||||
|
||||
/* Bits in ACR */
|
||||
#define SR_CTRL 0x1c /* Shift register control bits */
|
||||
#define SR_EXT 0x0c /* Shift on external clock */
|
||||
#define SR_OUT 0x10 /* Shift out if 1 */
|
||||
|
||||
/* Bits in IFR and IER */
|
||||
#define IER_SET 0x80 /* set bits in IER */
|
||||
#define IER_CLR 0 /* clear bits in IER */
|
||||
#define SR_INT 0x04 /* Shift register full/empty */
|
||||
#define T1_INT 0x40 /* Timer 1 interrupt */
|
||||
|
||||
/* Bits in ACR */
|
||||
#define T1MODE 0xc0 /* Timer 1 mode */
|
||||
#define T1MODE_CONT 0x40 /* continuous interrupts */
|
||||
|
||||
/* commands (1st byte) */
|
||||
#define ADB_PACKET 0
|
||||
#define CUDA_PACKET 1
|
||||
#define ERROR_PACKET 2
|
||||
#define TIMER_PACKET 3
|
||||
#define POWER_PACKET 4
|
||||
#define MACIIC_PACKET 5
|
||||
#define PMU_PACKET 6
|
||||
|
||||
|
||||
/* CUDA commands (2nd byte) */
|
||||
#define CUDA_WARM_START 0x0
|
||||
#define CUDA_AUTOPOLL 0x1
|
||||
#define CUDA_GET_6805_ADDR 0x2
|
||||
#define CUDA_GET_TIME 0x3
|
||||
#define CUDA_GET_PRAM 0x7
|
||||
#define CUDA_SET_6805_ADDR 0x8
|
||||
#define CUDA_SET_TIME 0x9
|
||||
#define CUDA_POWERDOWN 0xa
|
||||
#define CUDA_POWERUP_TIME 0xb
|
||||
#define CUDA_SET_PRAM 0xc
|
||||
#define CUDA_MS_RESET 0xd
|
||||
#define CUDA_SEND_DFAC 0xe
|
||||
#define CUDA_BATTERY_SWAP_SENSE 0x10
|
||||
#define CUDA_RESET_SYSTEM 0x11
|
||||
#define CUDA_SET_IPL 0x12
|
||||
#define CUDA_FILE_SERVER_FLAG 0x13
|
||||
#define CUDA_SET_AUTO_RATE 0x14
|
||||
#define CUDA_GET_AUTO_RATE 0x16
|
||||
#define CUDA_SET_DEVICE_LIST 0x19
|
||||
#define CUDA_GET_DEVICE_LIST 0x1a
|
||||
#define CUDA_SET_ONE_SECOND_MODE 0x1b
|
||||
#define CUDA_SET_POWER_MESSAGES 0x21
|
||||
#define CUDA_GET_SET_IIC 0x22
|
||||
#define CUDA_WAKEUP 0x23
|
||||
#define CUDA_TIMER_TICKLE 0x24
|
||||
#define CUDA_COMBINED_FORMAT_IIC 0x25
|
||||
|
||||
#define CUDA_TIMER_FREQ (4700000 / 6)
|
||||
#define CUDA_ADB_POLL_FREQ 50
|
||||
|
||||
/* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */
|
||||
#define RTC_OFFSET 2082844800
|
||||
|
||||
typedef struct CUDATimer {
|
||||
unsigned int latch;
|
||||
uint16_t counter_value; /* counter value at load time */
|
||||
int64_t load_time;
|
||||
int64_t next_irq_time;
|
||||
QEMUTimer *timer;
|
||||
} CUDATimer;
|
||||
|
||||
typedef struct CUDAState {
|
||||
/* cuda registers */
|
||||
uint8_t b; /* B-side data */
|
||||
uint8_t a; /* A-side data */
|
||||
uint8_t dirb; /* B-side direction (1=output) */
|
||||
uint8_t dira; /* A-side direction (1=output) */
|
||||
uint8_t sr; /* Shift register */
|
||||
uint8_t acr; /* Auxiliary control register */
|
||||
uint8_t pcr; /* Peripheral control register */
|
||||
uint8_t ifr; /* Interrupt flag register */
|
||||
uint8_t ier; /* Interrupt enable register */
|
||||
uint8_t anh; /* A-side data, no handshake */
|
||||
|
||||
CUDATimer timers[2];
|
||||
|
||||
uint8_t last_b; /* last value of B register */
|
||||
uint8_t last_acr; /* last value of B register */
|
||||
|
||||
int data_in_size;
|
||||
int data_in_index;
|
||||
int data_out_index;
|
||||
|
||||
int irq;
|
||||
openpic_t *openpic;
|
||||
uint8_t autopoll;
|
||||
uint8_t data_in[128];
|
||||
uint8_t data_out[16];
|
||||
QEMUTimer *adb_poll_timer;
|
||||
} CUDAState;
|
||||
|
||||
static CUDAState cuda_state;
|
||||
ADBBusState adb_bus;
|
||||
|
||||
static void cuda_update(CUDAState *s);
|
||||
static void cuda_receive_packet_from_host(CUDAState *s,
|
||||
const uint8_t *data, int len);
|
||||
static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
|
||||
int64_t current_time);
|
||||
|
||||
static void cuda_update_irq(CUDAState *s)
|
||||
{
|
||||
if (s->ifr & s->ier & (SR_INT | T1_INT)) {
|
||||
openpic_set_irq(s->openpic, s->irq, 1);
|
||||
} else {
|
||||
openpic_set_irq(s->openpic, s->irq, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int get_counter(CUDATimer *s)
|
||||
{
|
||||
int64_t d;
|
||||
unsigned int counter;
|
||||
|
||||
d = muldiv64(qemu_get_clock(vm_clock) - s->load_time,
|
||||
CUDA_TIMER_FREQ, ticks_per_sec);
|
||||
if (d <= s->counter_value) {
|
||||
counter = d;
|
||||
} else {
|
||||
counter = s->latch - 1 - ((d - s->counter_value) % s->latch);
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val)
|
||||
{
|
||||
#ifdef DEBUG_CUDA
|
||||
printf("cuda: T%d.counter=%d\n",
|
||||
1 + (ti->timer == NULL), val);
|
||||
#endif
|
||||
ti->load_time = qemu_get_clock(vm_clock);
|
||||
ti->counter_value = val;
|
||||
cuda_timer_update(s, ti, ti->load_time);
|
||||
}
|
||||
|
||||
static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time)
|
||||
{
|
||||
int64_t d, next_time, base;
|
||||
/* current counter value */
|
||||
d = muldiv64(current_time - s->load_time,
|
||||
CUDA_TIMER_FREQ, ticks_per_sec);
|
||||
if (d < s->counter_value) {
|
||||
next_time = s->counter_value + 1;
|
||||
} else
|
||||
{
|
||||
base = ((d - s->counter_value + 1) / s->latch);
|
||||
base = (base * s->latch) + s->counter_value;
|
||||
next_time = base + s->latch;
|
||||
}
|
||||
#if 0
|
||||
#ifdef DEBUG_CUDA
|
||||
printf("latch=%d counter=%lld delta_next=%lld\n",
|
||||
s->latch, d, next_time - d);
|
||||
#endif
|
||||
#endif
|
||||
next_time = muldiv64(next_time, ticks_per_sec, CUDA_TIMER_FREQ) +
|
||||
s->load_time;
|
||||
if (next_time <= current_time)
|
||||
next_time = current_time + 1;
|
||||
return next_time;
|
||||
}
|
||||
|
||||
static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
|
||||
int64_t current_time)
|
||||
{
|
||||
if (!ti->timer)
|
||||
return;
|
||||
if ((s->acr & T1MODE) != T1MODE_CONT) {
|
||||
qemu_del_timer(ti->timer);
|
||||
} else {
|
||||
ti->next_irq_time = get_next_irq_time(ti, current_time);
|
||||
qemu_mod_timer(ti->timer, ti->next_irq_time);
|
||||
}
|
||||
}
|
||||
|
||||
static void cuda_timer1(void *opaque)
|
||||
{
|
||||
CUDAState *s = opaque;
|
||||
CUDATimer *ti = &s->timers[0];
|
||||
|
||||
cuda_timer_update(s, ti, ti->next_irq_time);
|
||||
s->ifr |= T1_INT;
|
||||
cuda_update_irq(s);
|
||||
}
|
||||
|
||||
static uint32_t cuda_readb(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
CUDAState *s = opaque;
|
||||
uint32_t val;
|
||||
|
||||
addr = (addr >> 9) & 0xf;
|
||||
switch(addr) {
|
||||
case 0:
|
||||
val = s->b;
|
||||
break;
|
||||
case 1:
|
||||
val = s->a;
|
||||
break;
|
||||
case 2:
|
||||
val = s->dirb;
|
||||
break;
|
||||
case 3:
|
||||
val = s->dira;
|
||||
break;
|
||||
case 4:
|
||||
val = get_counter(&s->timers[0]) & 0xff;
|
||||
s->ifr &= ~T1_INT;
|
||||
cuda_update_irq(s);
|
||||
break;
|
||||
case 5:
|
||||
val = get_counter(&s->timers[0]) >> 8;
|
||||
s->ifr &= ~T1_INT;
|
||||
cuda_update_irq(s);
|
||||
break;
|
||||
case 6:
|
||||
val = s->timers[0].latch & 0xff;
|
||||
break;
|
||||
case 7:
|
||||
val = (s->timers[0].latch >> 8) & 0xff;
|
||||
break;
|
||||
case 8:
|
||||
val = get_counter(&s->timers[1]) & 0xff;
|
||||
break;
|
||||
case 9:
|
||||
val = get_counter(&s->timers[1]) >> 8;
|
||||
break;
|
||||
case 10:
|
||||
val = s->sr;
|
||||
s->ifr &= ~SR_INT;
|
||||
cuda_update_irq(s);
|
||||
break;
|
||||
case 11:
|
||||
val = s->acr;
|
||||
break;
|
||||
case 12:
|
||||
val = s->pcr;
|
||||
break;
|
||||
case 13:
|
||||
val = s->ifr;
|
||||
break;
|
||||
case 14:
|
||||
val = s->ier;
|
||||
break;
|
||||
default:
|
||||
case 15:
|
||||
val = s->anh;
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_CUDA
|
||||
if (addr != 13 || val != 0)
|
||||
printf("cuda: read: reg=0x%x val=%02x\n", addr, val);
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
static void cuda_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
CUDAState *s = opaque;
|
||||
|
||||
addr = (addr >> 9) & 0xf;
|
||||
#ifdef DEBUG_CUDA
|
||||
printf("cuda: write: reg=0x%x val=%02x\n", addr, val);
|
||||
#endif
|
||||
|
||||
switch(addr) {
|
||||
case 0:
|
||||
s->b = val;
|
||||
cuda_update(s);
|
||||
break;
|
||||
case 1:
|
||||
s->a = val;
|
||||
break;
|
||||
case 2:
|
||||
s->dirb = val;
|
||||
break;
|
||||
case 3:
|
||||
s->dira = val;
|
||||
break;
|
||||
case 4:
|
||||
val = val | (get_counter(&s->timers[0]) & 0xff00);
|
||||
set_counter(s, &s->timers[0], val);
|
||||
break;
|
||||
case 5:
|
||||
val = (val << 8) | (get_counter(&s->timers[0]) & 0xff);
|
||||
set_counter(s, &s->timers[0], val);
|
||||
break;
|
||||
case 6:
|
||||
s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
|
||||
cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
|
||||
break;
|
||||
case 7:
|
||||
s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
|
||||
cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
|
||||
break;
|
||||
case 8:
|
||||
val = val | (get_counter(&s->timers[1]) & 0xff00);
|
||||
set_counter(s, &s->timers[1], val);
|
||||
break;
|
||||
case 9:
|
||||
val = (val << 8) | (get_counter(&s->timers[1]) & 0xff);
|
||||
set_counter(s, &s->timers[1], val);
|
||||
break;
|
||||
case 10:
|
||||
s->sr = val;
|
||||
break;
|
||||
case 11:
|
||||
s->acr = val;
|
||||
cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
|
||||
cuda_update(s);
|
||||
break;
|
||||
case 12:
|
||||
s->pcr = val;
|
||||
break;
|
||||
case 13:
|
||||
/* reset bits */
|
||||
s->ifr &= ~val;
|
||||
cuda_update_irq(s);
|
||||
break;
|
||||
case 14:
|
||||
if (val & IER_SET) {
|
||||
/* set bits */
|
||||
s->ier |= val & 0x7f;
|
||||
} else {
|
||||
/* reset bits */
|
||||
s->ier &= ~val;
|
||||
}
|
||||
cuda_update_irq(s);
|
||||
break;
|
||||
default:
|
||||
case 15:
|
||||
s->anh = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: TIP and TREQ are negated */
|
||||
static void cuda_update(CUDAState *s)
|
||||
{
|
||||
int packet_received, len;
|
||||
|
||||
packet_received = 0;
|
||||
if (!(s->b & TIP)) {
|
||||
/* transfer requested from host */
|
||||
|
||||
if (s->acr & SR_OUT) {
|
||||
/* data output */
|
||||
if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
|
||||
if (s->data_out_index < sizeof(s->data_out)) {
|
||||
#ifdef DEBUG_CUDA
|
||||
printf("cuda: send: %02x\n", s->sr);
|
||||
#endif
|
||||
s->data_out[s->data_out_index++] = s->sr;
|
||||
s->ifr |= SR_INT;
|
||||
cuda_update_irq(s);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (s->data_in_index < s->data_in_size) {
|
||||
/* data input */
|
||||
if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
|
||||
s->sr = s->data_in[s->data_in_index++];
|
||||
#ifdef DEBUG_CUDA
|
||||
printf("cuda: recv: %02x\n", s->sr);
|
||||
#endif
|
||||
/* indicate end of transfer */
|
||||
if (s->data_in_index >= s->data_in_size) {
|
||||
s->b = (s->b | TREQ);
|
||||
}
|
||||
s->ifr |= SR_INT;
|
||||
cuda_update_irq(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* no transfer requested: handle sync case */
|
||||
if ((s->last_b & TIP) && (s->b & TACK) != (s->last_b & TACK)) {
|
||||
/* update TREQ state each time TACK change state */
|
||||
if (s->b & TACK)
|
||||
s->b = (s->b | TREQ);
|
||||
else
|
||||
s->b = (s->b & ~TREQ);
|
||||
s->ifr |= SR_INT;
|
||||
cuda_update_irq(s);
|
||||
} else {
|
||||
if (!(s->last_b & TIP)) {
|
||||
/* handle end of host to cuda transfert */
|
||||
packet_received = (s->data_out_index > 0);
|
||||
/* always an IRQ at the end of transfert */
|
||||
s->ifr |= SR_INT;
|
||||
cuda_update_irq(s);
|
||||
}
|
||||
/* signal if there is data to read */
|
||||
if (s->data_in_index < s->data_in_size) {
|
||||
s->b = (s->b & ~TREQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s->last_acr = s->acr;
|
||||
s->last_b = s->b;
|
||||
|
||||
/* NOTE: cuda_receive_packet_from_host() can call cuda_update()
|
||||
recursively */
|
||||
if (packet_received) {
|
||||
len = s->data_out_index;
|
||||
s->data_out_index = 0;
|
||||
cuda_receive_packet_from_host(s, s->data_out, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void cuda_send_packet_to_host(CUDAState *s,
|
||||
const uint8_t *data, int len)
|
||||
{
|
||||
#ifdef DEBUG_CUDA_PACKET
|
||||
{
|
||||
int i;
|
||||
printf("cuda_send_packet_to_host:\n");
|
||||
for(i = 0; i < len; i++)
|
||||
printf(" %02x", data[i]);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
memcpy(s->data_in, data, len);
|
||||
s->data_in_size = len;
|
||||
s->data_in_index = 0;
|
||||
cuda_update(s);
|
||||
s->ifr |= SR_INT;
|
||||
cuda_update_irq(s);
|
||||
}
|
||||
|
||||
static void cuda_adb_poll(void *opaque)
|
||||
{
|
||||
CUDAState *s = opaque;
|
||||
uint8_t obuf[ADB_MAX_OUT_LEN + 2];
|
||||
int olen;
|
||||
|
||||
olen = adb_poll(&adb_bus, obuf + 2);
|
||||
if (olen > 0) {
|
||||
obuf[0] = ADB_PACKET;
|
||||
obuf[1] = 0x40; /* polled data */
|
||||
cuda_send_packet_to_host(s, obuf, olen + 2);
|
||||
}
|
||||
qemu_mod_timer(s->adb_poll_timer,
|
||||
qemu_get_clock(vm_clock) +
|
||||
(ticks_per_sec / CUDA_ADB_POLL_FREQ));
|
||||
}
|
||||
|
||||
static void cuda_receive_packet(CUDAState *s,
|
||||
const uint8_t *data, int len)
|
||||
{
|
||||
uint8_t obuf[16];
|
||||
int ti, autopoll;
|
||||
|
||||
switch(data[0]) {
|
||||
case CUDA_AUTOPOLL:
|
||||
autopoll = (data[1] != 0);
|
||||
if (autopoll != s->autopoll) {
|
||||
s->autopoll = autopoll;
|
||||
if (autopoll) {
|
||||
qemu_mod_timer(s->adb_poll_timer,
|
||||
qemu_get_clock(vm_clock) +
|
||||
(ticks_per_sec / CUDA_ADB_POLL_FREQ));
|
||||
} else {
|
||||
qemu_del_timer(s->adb_poll_timer);
|
||||
}
|
||||
}
|
||||
obuf[0] = CUDA_PACKET;
|
||||
obuf[1] = data[1];
|
||||
cuda_send_packet_to_host(s, obuf, 2);
|
||||
break;
|
||||
case CUDA_GET_TIME:
|
||||
case CUDA_SET_TIME:
|
||||
/* XXX: add time support ? */
|
||||
ti = time(NULL) + RTC_OFFSET;
|
||||
obuf[0] = CUDA_PACKET;
|
||||
obuf[1] = 0;
|
||||
obuf[2] = 0;
|
||||
obuf[3] = ti >> 24;
|
||||
obuf[4] = ti >> 16;
|
||||
obuf[5] = ti >> 8;
|
||||
obuf[6] = ti;
|
||||
cuda_send_packet_to_host(s, obuf, 7);
|
||||
break;
|
||||
case CUDA_FILE_SERVER_FLAG:
|
||||
case CUDA_SET_DEVICE_LIST:
|
||||
case CUDA_SET_AUTO_RATE:
|
||||
case CUDA_SET_POWER_MESSAGES:
|
||||
obuf[0] = CUDA_PACKET;
|
||||
obuf[1] = 0;
|
||||
cuda_send_packet_to_host(s, obuf, 2);
|
||||
break;
|
||||
case CUDA_POWERDOWN:
|
||||
obuf[0] = CUDA_PACKET;
|
||||
obuf[1] = 0;
|
||||
cuda_send_packet_to_host(s, obuf, 2);
|
||||
qemu_system_shutdown_request();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void cuda_receive_packet_from_host(CUDAState *s,
|
||||
const uint8_t *data, int len)
|
||||
{
|
||||
#ifdef DEBUG_CUDA_PACKET
|
||||
{
|
||||
int i;
|
||||
printf("cuda_receive_packet_to_host:\n");
|
||||
for(i = 0; i < len; i++)
|
||||
printf(" %02x", data[i]);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
switch(data[0]) {
|
||||
case ADB_PACKET:
|
||||
{
|
||||
uint8_t obuf[ADB_MAX_OUT_LEN + 2];
|
||||
int olen;
|
||||
olen = adb_request(&adb_bus, obuf + 2, data + 1, len - 1);
|
||||
if (olen > 0) {
|
||||
obuf[0] = ADB_PACKET;
|
||||
obuf[1] = 0x00;
|
||||
} else {
|
||||
/* error */
|
||||
obuf[0] = ADB_PACKET;
|
||||
obuf[1] = -olen;
|
||||
olen = 0;
|
||||
}
|
||||
cuda_send_packet_to_host(s, obuf, olen + 2);
|
||||
}
|
||||
break;
|
||||
case CUDA_PACKET:
|
||||
cuda_receive_packet(s, data + 1, len - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void cuda_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
}
|
||||
|
||||
static void cuda_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
}
|
||||
|
||||
static uint32_t cuda_readw (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t cuda_readl (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc *cuda_write[] = {
|
||||
&cuda_writeb,
|
||||
&cuda_writew,
|
||||
&cuda_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *cuda_read[] = {
|
||||
&cuda_readb,
|
||||
&cuda_readw,
|
||||
&cuda_readl,
|
||||
};
|
||||
|
||||
int cuda_init(openpic_t *openpic, int irq)
|
||||
{
|
||||
CUDAState *s = &cuda_state;
|
||||
int cuda_mem_index;
|
||||
|
||||
s->openpic = openpic;
|
||||
s->irq = irq;
|
||||
|
||||
s->timers[0].timer = qemu_new_timer(vm_clock, cuda_timer1, s);
|
||||
s->timers[0].latch = 0x10000;
|
||||
set_counter(s, &s->timers[0], 0xffff);
|
||||
s->timers[1].latch = 0x10000;
|
||||
s->ier = T1_INT | SR_INT;
|
||||
set_counter(s, &s->timers[1], 0xffff);
|
||||
|
||||
s->adb_poll_timer = qemu_new_timer(vm_clock, cuda_adb_poll, s);
|
||||
cuda_mem_index = cpu_register_io_memory(0, cuda_read, cuda_write, s);
|
||||
return cuda_mem_index;
|
||||
}
|
||||
@@ -0,0 +1,535 @@
|
||||
/*
|
||||
* QEMU DMA emulation
|
||||
*
|
||||
* Copyright (c) 2003-2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
/* #define DEBUG_DMA */
|
||||
|
||||
#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__)
|
||||
#ifdef DEBUG_DMA
|
||||
#define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__)
|
||||
#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__)
|
||||
#define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__)
|
||||
#else
|
||||
#define lwarn(...)
|
||||
#define linfo(...)
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
#define LENOFA(a) ((int) (sizeof(a)/sizeof(a[0])))
|
||||
|
||||
struct dma_regs {
|
||||
int now[2];
|
||||
uint16_t base[2];
|
||||
uint8_t mode;
|
||||
uint8_t page;
|
||||
uint8_t pageh;
|
||||
uint8_t dack;
|
||||
uint8_t eop;
|
||||
DMA_transfer_handler transfer_handler;
|
||||
void *opaque;
|
||||
};
|
||||
|
||||
#define ADDR 0
|
||||
#define COUNT 1
|
||||
|
||||
static struct dma_cont {
|
||||
uint8_t status;
|
||||
uint8_t command;
|
||||
uint8_t mask;
|
||||
uint8_t flip_flop;
|
||||
int dshift;
|
||||
struct dma_regs regs[4];
|
||||
} dma_controllers[2];
|
||||
|
||||
enum {
|
||||
CMD_MEMORY_TO_MEMORY = 0x01,
|
||||
CMD_FIXED_ADDRESS = 0x02,
|
||||
CMD_BLOCK_CONTROLLER = 0x04,
|
||||
CMD_COMPRESSED_TIME = 0x08,
|
||||
CMD_CYCLIC_PRIORITY = 0x10,
|
||||
CMD_EXTENDED_WRITE = 0x20,
|
||||
CMD_LOW_DREQ = 0x40,
|
||||
CMD_LOW_DACK = 0x80,
|
||||
CMD_NOT_SUPPORTED = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS
|
||||
| CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE
|
||||
| CMD_LOW_DREQ | CMD_LOW_DACK
|
||||
|
||||
};
|
||||
|
||||
static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
|
||||
|
||||
static void write_page (void *opaque, uint32_t nport, uint32_t data)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int ichan;
|
||||
|
||||
ichan = channels[nport & 7];
|
||||
if (-1 == ichan) {
|
||||
dolog ("invalid channel %#x %#x\n", nport, data);
|
||||
return;
|
||||
}
|
||||
d->regs[ichan].page = data;
|
||||
}
|
||||
|
||||
static void write_pageh (void *opaque, uint32_t nport, uint32_t data)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int ichan;
|
||||
|
||||
ichan = channels[nport & 7];
|
||||
if (-1 == ichan) {
|
||||
dolog ("invalid channel %#x %#x\n", nport, data);
|
||||
return;
|
||||
}
|
||||
d->regs[ichan].pageh = data;
|
||||
}
|
||||
|
||||
static uint32_t read_page (void *opaque, uint32_t nport)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int ichan;
|
||||
|
||||
ichan = channels[nport & 7];
|
||||
if (-1 == ichan) {
|
||||
dolog ("invalid channel read %#x\n", nport);
|
||||
return 0;
|
||||
}
|
||||
return d->regs[ichan].page;
|
||||
}
|
||||
|
||||
static uint32_t read_pageh (void *opaque, uint32_t nport)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int ichan;
|
||||
|
||||
ichan = channels[nport & 7];
|
||||
if (-1 == ichan) {
|
||||
dolog ("invalid channel read %#x\n", nport);
|
||||
return 0;
|
||||
}
|
||||
return d->regs[ichan].pageh;
|
||||
}
|
||||
|
||||
static inline void init_chan (struct dma_cont *d, int ichan)
|
||||
{
|
||||
struct dma_regs *r;
|
||||
|
||||
r = d->regs + ichan;
|
||||
r->now[ADDR] = r->base[ADDR] << d->dshift;
|
||||
r->now[COUNT] = 0;
|
||||
}
|
||||
|
||||
static inline int getff (struct dma_cont *d)
|
||||
{
|
||||
int ff;
|
||||
|
||||
ff = d->flip_flop;
|
||||
d->flip_flop = !ff;
|
||||
return ff;
|
||||
}
|
||||
|
||||
static uint32_t read_chan (void *opaque, uint32_t nport)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int ichan, nreg, iport, ff, val, dir;
|
||||
struct dma_regs *r;
|
||||
|
||||
iport = (nport >> d->dshift) & 0x0f;
|
||||
ichan = iport >> 1;
|
||||
nreg = iport & 1;
|
||||
r = d->regs + ichan;
|
||||
|
||||
dir = ((r->mode >> 5) & 1) ? -1 : 1;
|
||||
ff = getff (d);
|
||||
if (nreg)
|
||||
val = (r->base[COUNT] << d->dshift) - r->now[COUNT];
|
||||
else
|
||||
val = r->now[ADDR] + r->now[COUNT] * dir;
|
||||
|
||||
ldebug ("read_chan %#x -> %d\n", iport, val);
|
||||
return (val >> (d->dshift + (ff << 3))) & 0xff;
|
||||
}
|
||||
|
||||
static void write_chan (void *opaque, uint32_t nport, uint32_t data)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int iport, ichan, nreg;
|
||||
struct dma_regs *r;
|
||||
|
||||
iport = (nport >> d->dshift) & 0x0f;
|
||||
ichan = iport >> 1;
|
||||
nreg = iport & 1;
|
||||
r = d->regs + ichan;
|
||||
if (getff (d)) {
|
||||
r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00);
|
||||
init_chan (d, ichan);
|
||||
} else {
|
||||
r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_cont (void *opaque, uint32_t nport, uint32_t data)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int iport, ichan = 0;
|
||||
|
||||
iport = (nport >> d->dshift) & 0x0f;
|
||||
switch (iport) {
|
||||
case 0x08: /* command */
|
||||
if ((data != 0) && (data & CMD_NOT_SUPPORTED)) {
|
||||
dolog ("command %#x not supported\n", data);
|
||||
return;
|
||||
}
|
||||
d->command = data;
|
||||
break;
|
||||
|
||||
case 0x09:
|
||||
ichan = data & 3;
|
||||
if (data & 4) {
|
||||
d->status |= 1 << (ichan + 4);
|
||||
}
|
||||
else {
|
||||
d->status &= ~(1 << (ichan + 4));
|
||||
}
|
||||
d->status &= ~(1 << ichan);
|
||||
break;
|
||||
|
||||
case 0x0a: /* single mask */
|
||||
if (data & 4)
|
||||
d->mask |= 1 << (data & 3);
|
||||
else
|
||||
d->mask &= ~(1 << (data & 3));
|
||||
break;
|
||||
|
||||
case 0x0b: /* mode */
|
||||
{
|
||||
ichan = data & 3;
|
||||
#ifdef DEBUG_DMA
|
||||
{
|
||||
int op, ai, dir, opmode;
|
||||
op = (data >> 2) & 3;
|
||||
ai = (data >> 4) & 1;
|
||||
dir = (data >> 5) & 1;
|
||||
opmode = (data >> 6) & 3;
|
||||
|
||||
linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n",
|
||||
ichan, op, ai, dir, opmode);
|
||||
}
|
||||
#endif
|
||||
d->regs[ichan].mode = data;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0c: /* clear flip flop */
|
||||
d->flip_flop = 0;
|
||||
break;
|
||||
|
||||
case 0x0d: /* reset */
|
||||
d->flip_flop = 0;
|
||||
d->mask = ~0;
|
||||
d->status = 0;
|
||||
d->command = 0;
|
||||
break;
|
||||
|
||||
case 0x0e: /* clear mask for all channels */
|
||||
d->mask = 0;
|
||||
break;
|
||||
|
||||
case 0x0f: /* write mask for all channels */
|
||||
d->mask = data;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("unknown iport %#x\n", iport);
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DMA
|
||||
if (0xc != iport) {
|
||||
linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n",
|
||||
nport, ichan, data);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t read_cont (void *opaque, uint32_t nport)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int iport, val;
|
||||
|
||||
iport = (nport >> d->dshift) & 0x0f;
|
||||
switch (iport) {
|
||||
case 0x08: /* status */
|
||||
val = d->status;
|
||||
d->status &= 0xf0;
|
||||
break;
|
||||
case 0x0f: /* mask */
|
||||
val = d->mask;
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
int DMA_get_channel_mode (int nchan)
|
||||
{
|
||||
return dma_controllers[nchan > 3].regs[nchan & 3].mode;
|
||||
}
|
||||
|
||||
void DMA_hold_DREQ (int nchan)
|
||||
{
|
||||
int ncont, ichan;
|
||||
|
||||
ncont = nchan > 3;
|
||||
ichan = nchan & 3;
|
||||
linfo ("held cont=%d chan=%d\n", ncont, ichan);
|
||||
dma_controllers[ncont].status |= 1 << (ichan + 4);
|
||||
}
|
||||
|
||||
void DMA_release_DREQ (int nchan)
|
||||
{
|
||||
int ncont, ichan;
|
||||
|
||||
ncont = nchan > 3;
|
||||
ichan = nchan & 3;
|
||||
linfo ("released cont=%d chan=%d\n", ncont, ichan);
|
||||
dma_controllers[ncont].status &= ~(1 << (ichan + 4));
|
||||
}
|
||||
|
||||
static void channel_run (int ncont, int ichan)
|
||||
{
|
||||
int n;
|
||||
struct dma_regs *r = &dma_controllers[ncont].regs[ichan];
|
||||
#ifdef DEBUG_DMA
|
||||
int dir, opmode;
|
||||
|
||||
dir = (r->mode >> 5) & 1;
|
||||
opmode = (r->mode >> 6) & 3;
|
||||
|
||||
if (dir) {
|
||||
dolog ("DMA in address decrement mode\n");
|
||||
}
|
||||
if (opmode != 1) {
|
||||
dolog ("DMA not in single mode select %#x\n", opmode);
|
||||
}
|
||||
#endif
|
||||
|
||||
r = dma_controllers[ncont].regs + ichan;
|
||||
n = r->transfer_handler (r->opaque, ichan + (ncont << 2),
|
||||
r->now[COUNT], (r->base[COUNT] + 1) << ncont);
|
||||
r->now[COUNT] = n;
|
||||
ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
|
||||
}
|
||||
|
||||
void DMA_run (void)
|
||||
{
|
||||
struct dma_cont *d;
|
||||
int icont, ichan;
|
||||
|
||||
d = dma_controllers;
|
||||
|
||||
for (icont = 0; icont < 2; icont++, d++) {
|
||||
for (ichan = 0; ichan < 4; ichan++) {
|
||||
int mask;
|
||||
|
||||
mask = 1 << ichan;
|
||||
|
||||
if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4))))
|
||||
channel_run (icont, ichan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DMA_register_channel (int nchan,
|
||||
DMA_transfer_handler transfer_handler,
|
||||
void *opaque)
|
||||
{
|
||||
struct dma_regs *r;
|
||||
int ichan, ncont;
|
||||
|
||||
ncont = nchan > 3;
|
||||
ichan = nchan & 3;
|
||||
|
||||
r = dma_controllers[ncont].regs + ichan;
|
||||
r->transfer_handler = transfer_handler;
|
||||
r->opaque = opaque;
|
||||
}
|
||||
|
||||
int DMA_read_memory (int nchan, void *buf, int pos, int len)
|
||||
{
|
||||
struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
|
||||
target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
|
||||
|
||||
if (r->mode & 0x20) {
|
||||
int i;
|
||||
uint8_t *p = buf;
|
||||
|
||||
cpu_physical_memory_read (addr - pos - len, buf, len);
|
||||
/* What about 16bit transfers? */
|
||||
for (i = 0; i < len >> 1; i++) {
|
||||
uint8_t b = p[len - i - 1];
|
||||
p[i] = b;
|
||||
}
|
||||
}
|
||||
else
|
||||
cpu_physical_memory_read (addr + pos, buf, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int DMA_write_memory (int nchan, void *buf, int pos, int len)
|
||||
{
|
||||
struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
|
||||
target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
|
||||
|
||||
if (r->mode & 0x20) {
|
||||
int i;
|
||||
uint8_t *p = buf;
|
||||
|
||||
cpu_physical_memory_write (addr - pos - len, buf, len);
|
||||
/* What about 16bit transfers? */
|
||||
for (i = 0; i < len; i++) {
|
||||
uint8_t b = p[len - i - 1];
|
||||
p[i] = b;
|
||||
}
|
||||
}
|
||||
else
|
||||
cpu_physical_memory_write (addr + pos, buf, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* request the emulator to transfer a new DMA memory block ASAP */
|
||||
void DMA_schedule(int nchan)
|
||||
{
|
||||
cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
|
||||
}
|
||||
|
||||
static void dma_reset(void *opaque)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
write_cont (d, (0x0d << d->dshift), 0);
|
||||
}
|
||||
|
||||
/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
|
||||
static void dma_init2(struct dma_cont *d, int base, int dshift,
|
||||
int page_base, int pageh_base)
|
||||
{
|
||||
const static int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 };
|
||||
int i;
|
||||
|
||||
d->dshift = dshift;
|
||||
for (i = 0; i < 8; i++) {
|
||||
register_ioport_write (base + (i << dshift), 1, 1, write_chan, d);
|
||||
register_ioport_read (base + (i << dshift), 1, 1, read_chan, d);
|
||||
}
|
||||
for (i = 0; i < LENOFA (page_port_list); i++) {
|
||||
register_ioport_write (page_base + page_port_list[i], 1, 1,
|
||||
write_page, d);
|
||||
register_ioport_read (page_base + page_port_list[i], 1, 1,
|
||||
read_page, d);
|
||||
if (pageh_base >= 0) {
|
||||
register_ioport_write (pageh_base + page_port_list[i], 1, 1,
|
||||
write_pageh, d);
|
||||
register_ioport_read (pageh_base + page_port_list[i], 1, 1,
|
||||
read_pageh, d);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 8; i++) {
|
||||
register_ioport_write (base + ((i + 8) << dshift), 1, 1,
|
||||
write_cont, d);
|
||||
register_ioport_read (base + ((i + 8) << dshift), 1, 1,
|
||||
read_cont, d);
|
||||
}
|
||||
qemu_register_reset(dma_reset, d);
|
||||
dma_reset(d);
|
||||
}
|
||||
|
||||
static void dma_save (QEMUFile *f, void *opaque)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int i;
|
||||
|
||||
/* qemu_put_8s (f, &d->status); */
|
||||
qemu_put_8s (f, &d->command);
|
||||
qemu_put_8s (f, &d->mask);
|
||||
qemu_put_8s (f, &d->flip_flop);
|
||||
qemu_put_be32s (f, &d->dshift);
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
struct dma_regs *r = &d->regs[i];
|
||||
qemu_put_be32s (f, &r->now[0]);
|
||||
qemu_put_be32s (f, &r->now[1]);
|
||||
qemu_put_be16s (f, &r->base[0]);
|
||||
qemu_put_be16s (f, &r->base[1]);
|
||||
qemu_put_8s (f, &r->mode);
|
||||
qemu_put_8s (f, &r->page);
|
||||
qemu_put_8s (f, &r->pageh);
|
||||
qemu_put_8s (f, &r->dack);
|
||||
qemu_put_8s (f, &r->eop);
|
||||
}
|
||||
}
|
||||
|
||||
static int dma_load (QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int i;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* qemu_get_8s (f, &d->status); */
|
||||
qemu_get_8s (f, &d->command);
|
||||
qemu_get_8s (f, &d->mask);
|
||||
qemu_get_8s (f, &d->flip_flop);
|
||||
qemu_get_be32s (f, &d->dshift);
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
struct dma_regs *r = &d->regs[i];
|
||||
qemu_get_be32s (f, &r->now[0]);
|
||||
qemu_get_be32s (f, &r->now[1]);
|
||||
qemu_get_be16s (f, &r->base[0]);
|
||||
qemu_get_be16s (f, &r->base[1]);
|
||||
qemu_get_8s (f, &r->mode);
|
||||
qemu_get_8s (f, &r->page);
|
||||
qemu_get_8s (f, &r->pageh);
|
||||
qemu_get_8s (f, &r->dack);
|
||||
qemu_get_8s (f, &r->eop);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DMA_init (int high_page_enable)
|
||||
{
|
||||
dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
|
||||
high_page_enable ? 0x480 : -1);
|
||||
dma_init2(&dma_controllers[1], 0xc0, 1, 0x88,
|
||||
high_page_enable ? 0x488 : -1);
|
||||
register_savevm ("dma", 0, 1, dma_save, dma_load, &dma_controllers[0]);
|
||||
register_savevm ("dma", 1, 1, dma_save, dma_load, &dma_controllers[1]);
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* QEMU ESP emulation
|
||||
*
|
||||
* Copyright (c) 2005 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
/* debug ESP card */
|
||||
//#define DEBUG_ESP
|
||||
|
||||
#ifdef DEBUG_ESP
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { printf("ESP: " fmt , ##args); } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, args...)
|
||||
#endif
|
||||
|
||||
#define ESPDMA_REGS 4
|
||||
#define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1)
|
||||
#define ESP_MAXREG 0x3f
|
||||
|
||||
typedef struct ESPState {
|
||||
BlockDriverState **bd;
|
||||
uint8_t rregs[ESP_MAXREG];
|
||||
uint8_t wregs[ESP_MAXREG];
|
||||
int irq;
|
||||
uint32_t espdmaregs[ESPDMA_REGS];
|
||||
uint32_t ti_size;
|
||||
int ti_dir;
|
||||
uint8_t ti_buf[65536];
|
||||
} ESPState;
|
||||
|
||||
#define STAT_DO 0x00
|
||||
#define STAT_DI 0x01
|
||||
#define STAT_CD 0x02
|
||||
#define STAT_ST 0x03
|
||||
#define STAT_MI 0x06
|
||||
#define STAT_MO 0x07
|
||||
|
||||
#define STAT_TC 0x10
|
||||
#define STAT_IN 0x80
|
||||
|
||||
#define INTR_FC 0x08
|
||||
#define INTR_BS 0x10
|
||||
#define INTR_DC 0x20
|
||||
|
||||
#define SEQ_0 0x0
|
||||
#define SEQ_CD 0x4
|
||||
|
||||
static void handle_satn(ESPState *s)
|
||||
{
|
||||
uint8_t buf[32];
|
||||
uint32_t dmaptr, dmalen;
|
||||
unsigned int i;
|
||||
int64_t nb_sectors;
|
||||
int target;
|
||||
|
||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
||||
DPRINTF("Select with ATN at %8.8x len %d\n", dmaptr, dmalen);
|
||||
DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r');
|
||||
cpu_physical_memory_read(dmaptr, buf, dmalen);
|
||||
for (i = 0; i < dmalen; i++) {
|
||||
DPRINTF("Command %2.2x\n", buf[i]);
|
||||
}
|
||||
s->ti_dir = 0;
|
||||
s->ti_size = 0;
|
||||
target = s->wregs[4] & 7;
|
||||
|
||||
if (target > 4 || !s->bd[target]) { // No such drive
|
||||
s->rregs[4] = STAT_IN;
|
||||
s->rregs[5] = INTR_DC;
|
||||
s->rregs[6] = SEQ_0;
|
||||
s->espdmaregs[0] |= 1;
|
||||
pic_set_irq(s->irq, 1);
|
||||
return;
|
||||
}
|
||||
switch (buf[1]) {
|
||||
case 0x0:
|
||||
DPRINTF("Test Unit Ready (len %d)\n", buf[5]);
|
||||
break;
|
||||
case 0x12:
|
||||
DPRINTF("Inquiry (len %d)\n", buf[5]);
|
||||
memset(s->ti_buf, 0, 36);
|
||||
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) {
|
||||
s->ti_buf[0] = 5;
|
||||
memcpy(&s->ti_buf[16], "QEMU CDROM ", 16);
|
||||
} else {
|
||||
s->ti_buf[0] = 0;
|
||||
memcpy(&s->ti_buf[16], "QEMU HARDDISK ", 16);
|
||||
}
|
||||
memcpy(&s->ti_buf[8], "QEMU ", 8);
|
||||
s->ti_buf[2] = 1;
|
||||
s->ti_buf[3] = 2;
|
||||
s->ti_dir = 1;
|
||||
s->ti_size = 36;
|
||||
break;
|
||||
case 0x1a:
|
||||
DPRINTF("Mode Sense(6) (page %d, len %d)\n", buf[3], buf[5]);
|
||||
break;
|
||||
case 0x25:
|
||||
DPRINTF("Read Capacity (len %d)\n", buf[5]);
|
||||
memset(s->ti_buf, 0, 8);
|
||||
bdrv_get_geometry(s->bd[target], &nb_sectors);
|
||||
s->ti_buf[0] = (nb_sectors >> 24) & 0xff;
|
||||
s->ti_buf[1] = (nb_sectors >> 16) & 0xff;
|
||||
s->ti_buf[2] = (nb_sectors >> 8) & 0xff;
|
||||
s->ti_buf[3] = nb_sectors & 0xff;
|
||||
s->ti_buf[4] = 0;
|
||||
s->ti_buf[5] = 0;
|
||||
s->ti_buf[6] = 2;
|
||||
s->ti_buf[7] = 0;
|
||||
s->ti_dir = 1;
|
||||
s->ti_size = 8;
|
||||
break;
|
||||
case 0x28:
|
||||
{
|
||||
int64_t offset, len;
|
||||
|
||||
offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
|
||||
len = (buf[8] << 8) | buf[9];
|
||||
DPRINTF("Read (10) (offset %lld len %lld)\n", offset, len);
|
||||
bdrv_read(s->bd[target], offset, s->ti_buf, len);
|
||||
s->ti_dir = 1;
|
||||
s->ti_size = len * 512;
|
||||
break;
|
||||
}
|
||||
case 0x2a:
|
||||
{
|
||||
int64_t offset, len;
|
||||
|
||||
offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
|
||||
len = (buf[8] << 8) | buf[9];
|
||||
DPRINTF("Write (10) (offset %lld len %lld)\n", offset, len);
|
||||
bdrv_write(s->bd[target], offset, s->ti_buf, len);
|
||||
s->ti_dir = 0;
|
||||
s->ti_size = len * 512;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DPRINTF("Unknown command (%2.2x)\n", buf[1]);
|
||||
break;
|
||||
}
|
||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_DI;
|
||||
s->rregs[5] = INTR_BS | INTR_FC;
|
||||
s->rregs[6] = SEQ_CD;
|
||||
s->espdmaregs[0] |= 1;
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
|
||||
static void dma_write(ESPState *s, const uint8_t *buf, uint32_t len)
|
||||
{
|
||||
uint32_t dmaptr, dmalen;
|
||||
|
||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
||||
DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r');
|
||||
cpu_physical_memory_write(dmaptr, buf, len);
|
||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
|
||||
s->rregs[5] = INTR_BS | INTR_FC;
|
||||
s->rregs[6] = SEQ_CD;
|
||||
s->espdmaregs[0] |= 1;
|
||||
pic_set_irq(s->irq, 1);
|
||||
|
||||
}
|
||||
static const uint8_t okbuf[] = {0, 0};
|
||||
|
||||
static void handle_ti(ESPState *s)
|
||||
{
|
||||
uint32_t dmaptr, dmalen;
|
||||
unsigned int i;
|
||||
|
||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
||||
DPRINTF("Transfer Information at %8.8x len %d\n", dmaptr, dmalen);
|
||||
DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r');
|
||||
for (i = 0; i < s->ti_size; i++) {
|
||||
dmaptr = iommu_translate(s->espdmaregs[1] + i);
|
||||
if (s->ti_dir)
|
||||
cpu_physical_memory_write(dmaptr, &s->ti_buf[i], 1);
|
||||
else
|
||||
cpu_physical_memory_read(dmaptr, &s->ti_buf[i], 1);
|
||||
}
|
||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
|
||||
s->rregs[5] = INTR_BS;
|
||||
s->rregs[6] = 0;
|
||||
s->espdmaregs[0] |= 1;
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
|
||||
static void esp_reset(void *opaque)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
memset(s->rregs, 0, ESP_MAXREG);
|
||||
s->rregs[0x0e] = 0x4; // Indicate fas100a
|
||||
memset(s->espdmaregs, 0, ESPDMA_REGS * 4);
|
||||
}
|
||||
|
||||
static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr & ESP_MAXREG) >> 2;
|
||||
switch (saddr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]);
|
||||
return s->rregs[saddr];
|
||||
}
|
||||
|
||||
static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr & ESP_MAXREG) >> 2;
|
||||
DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], val);
|
||||
switch (saddr) {
|
||||
case 3:
|
||||
// Command
|
||||
switch(val & 0x7f) {
|
||||
case 0:
|
||||
DPRINTF("NOP (%2.2x)\n", val);
|
||||
break;
|
||||
case 1:
|
||||
DPRINTF("Flush FIFO (%2.2x)\n", val);
|
||||
s->rregs[6] = 0;
|
||||
s->rregs[5] = INTR_FC;
|
||||
break;
|
||||
case 2:
|
||||
DPRINTF("Chip reset (%2.2x)\n", val);
|
||||
esp_reset(s);
|
||||
break;
|
||||
case 3:
|
||||
DPRINTF("Bus reset (%2.2x)\n", val);
|
||||
break;
|
||||
case 0x10:
|
||||
handle_ti(s);
|
||||
break;
|
||||
case 0x11:
|
||||
DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val);
|
||||
dma_write(s, okbuf, 2);
|
||||
break;
|
||||
case 0x12:
|
||||
DPRINTF("Message Accepted (%2.2x)\n", val);
|
||||
dma_write(s, okbuf, 2);
|
||||
s->rregs[5] = INTR_DC;
|
||||
s->rregs[6] = 0;
|
||||
break;
|
||||
case 0x1a:
|
||||
DPRINTF("Set ATN (%2.2x)\n", val);
|
||||
break;
|
||||
case 0x42:
|
||||
handle_satn(s);
|
||||
break;
|
||||
case 0x43:
|
||||
DPRINTF("Set ATN & stop (%2.2x)\n", val);
|
||||
handle_satn(s);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("Unhandled command (%2.2x)\n", val);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4 ... 7:
|
||||
case 9 ... 0xf:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
s->wregs[saddr] = val;
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *esp_mem_read[3] = {
|
||||
esp_mem_readb,
|
||||
esp_mem_readb,
|
||||
esp_mem_readb,
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *esp_mem_write[3] = {
|
||||
esp_mem_writeb,
|
||||
esp_mem_writeb,
|
||||
esp_mem_writeb,
|
||||
};
|
||||
|
||||
static uint32_t espdma_mem_readl(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr & ESPDMA_MAXADDR) >> 2;
|
||||
DPRINTF("read dmareg[%d]: 0x%2.2x\n", saddr, s->espdmaregs[saddr]);
|
||||
return s->espdmaregs[saddr];
|
||||
}
|
||||
|
||||
static void espdma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr & ESPDMA_MAXADDR) >> 2;
|
||||
DPRINTF("write dmareg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->espdmaregs[saddr], val);
|
||||
switch (saddr) {
|
||||
case 0:
|
||||
if (!(val & 0x10))
|
||||
pic_set_irq(s->irq, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
s->espdmaregs[saddr] = val;
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *espdma_mem_read[3] = {
|
||||
espdma_mem_readl,
|
||||
espdma_mem_readl,
|
||||
espdma_mem_readl,
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *espdma_mem_write[3] = {
|
||||
espdma_mem_writel,
|
||||
espdma_mem_writel,
|
||||
espdma_mem_writel,
|
||||
};
|
||||
|
||||
static void esp_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
unsigned int i;
|
||||
|
||||
qemu_put_buffer(f, s->rregs, ESP_MAXREG);
|
||||
qemu_put_buffer(f, s->wregs, ESP_MAXREG);
|
||||
qemu_put_be32s(f, &s->irq);
|
||||
for (i = 0; i < ESPDMA_REGS; i++)
|
||||
qemu_put_be32s(f, &s->espdmaregs[i]);
|
||||
}
|
||||
|
||||
static int esp_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
unsigned int i;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
qemu_get_buffer(f, s->rregs, ESP_MAXREG);
|
||||
qemu_get_buffer(f, s->wregs, ESP_MAXREG);
|
||||
qemu_get_be32s(f, &s->irq);
|
||||
for (i = 0; i < ESPDMA_REGS; i++)
|
||||
qemu_get_be32s(f, &s->espdmaregs[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdaddr)
|
||||
{
|
||||
ESPState *s;
|
||||
int esp_io_memory, espdma_io_memory;
|
||||
|
||||
s = qemu_mallocz(sizeof(ESPState));
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
s->bd = bd;
|
||||
s->irq = irq;
|
||||
|
||||
esp_io_memory = cpu_register_io_memory(0, esp_mem_read, esp_mem_write, s);
|
||||
cpu_register_physical_memory(espaddr, ESP_MAXREG*4, esp_io_memory);
|
||||
|
||||
espdma_io_memory = cpu_register_io_memory(0, espdma_mem_read, espdma_mem_write, s);
|
||||
cpu_register_physical_memory(espdaddr, 16, espdma_io_memory);
|
||||
|
||||
esp_reset(s);
|
||||
|
||||
register_savevm("esp", espaddr, 1, esp_save, esp_load, s);
|
||||
qemu_register_reset(esp_reset, s);
|
||||
}
|
||||
|
||||
+1390
File diff suppressed because it is too large.
Load diff
+174
@@ -0,0 +1,174 @@
|
||||
#ifndef __FMOPL_H_
|
||||
#define __FMOPL_H_
|
||||
|
||||
/* --- select emulation chips --- */
|
||||
#define BUILD_YM3812 (HAS_YM3812)
|
||||
//#define BUILD_YM3526 (HAS_YM3526)
|
||||
//#define BUILD_Y8950 (HAS_Y8950)
|
||||
|
||||
/* --- system optimize --- */
|
||||
/* select bit size of output : 8 or 16 */
|
||||
#define OPL_OUTPUT_BIT 16
|
||||
|
||||
/* compiler dependence */
|
||||
#ifndef OSD_CPU_H
|
||||
#define OSD_CPU_H
|
||||
typedef unsigned char UINT8; /* unsigned 8bit */
|
||||
typedef unsigned short UINT16; /* unsigned 16bit */
|
||||
typedef unsigned int UINT32; /* unsigned 32bit */
|
||||
typedef signed char INT8; /* signed 8bit */
|
||||
typedef signed short INT16; /* signed 16bit */
|
||||
typedef signed int INT32; /* signed 32bit */
|
||||
#endif
|
||||
|
||||
#if (OPL_OUTPUT_BIT==16)
|
||||
typedef INT16 OPLSAMPLE;
|
||||
#endif
|
||||
#if (OPL_OUTPUT_BIT==8)
|
||||
typedef unsigned char OPLSAMPLE;
|
||||
#endif
|
||||
|
||||
|
||||
#if BUILD_Y8950
|
||||
#include "ymdeltat.h"
|
||||
#endif
|
||||
|
||||
typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
|
||||
typedef void (*OPL_IRQHANDLER)(int param,int irq);
|
||||
typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
|
||||
typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
|
||||
typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
|
||||
|
||||
/* !!!!! here is private section , do not access there member direct !!!!! */
|
||||
|
||||
#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
|
||||
#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
|
||||
#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
|
||||
#define OPL_TYPE_IO 0x08 /* I/O port */
|
||||
|
||||
/* Saving is necessary for member of the 'R' mark for suspend/resume */
|
||||
/* ---------- OPL one of slot ---------- */
|
||||
typedef struct fm_opl_slot {
|
||||
INT32 TL; /* total level :TL << 8 */
|
||||
INT32 TLL; /* adjusted now TL */
|
||||
UINT8 KSR; /* key scale rate :(shift down bit) */
|
||||
INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */
|
||||
INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */
|
||||
INT32 SL; /* sustin level :SL_TALBE[SL] */
|
||||
INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */
|
||||
UINT8 ksl; /* keyscale level :(shift down bits) */
|
||||
UINT8 ksr; /* key scale rate :kcode>>KSR */
|
||||
UINT32 mul; /* multiple :ML_TABLE[ML] */
|
||||
UINT32 Cnt; /* frequency count : */
|
||||
UINT32 Incr; /* frequency step : */
|
||||
/* envelope generator state */
|
||||
UINT8 eg_typ; /* envelope type flag */
|
||||
UINT8 evm; /* envelope phase */
|
||||
INT32 evc; /* envelope counter */
|
||||
INT32 eve; /* envelope counter end point */
|
||||
INT32 evs; /* envelope counter step */
|
||||
INT32 evsa; /* envelope step for AR :AR[ksr] */
|
||||
INT32 evsd; /* envelope step for DR :DR[ksr] */
|
||||
INT32 evsr; /* envelope step for RR :RR[ksr] */
|
||||
/* LFO */
|
||||
UINT8 ams; /* ams flag */
|
||||
UINT8 vib; /* vibrate flag */
|
||||
/* wave selector */
|
||||
INT32 **wavetable;
|
||||
}OPL_SLOT;
|
||||
|
||||
/* ---------- OPL one of channel ---------- */
|
||||
typedef struct fm_opl_channel {
|
||||
OPL_SLOT SLOT[2];
|
||||
UINT8 CON; /* connection type */
|
||||
UINT8 FB; /* feed back :(shift down bit) */
|
||||
INT32 *connect1; /* slot1 output pointer */
|
||||
INT32 *connect2; /* slot2 output pointer */
|
||||
INT32 op1_out[2]; /* slot1 output for selfeedback */
|
||||
/* phase generator state */
|
||||
UINT32 block_fnum; /* block+fnum : */
|
||||
UINT8 kcode; /* key code : KeyScaleCode */
|
||||
UINT32 fc; /* Freq. Increment base */
|
||||
UINT32 ksl_base; /* KeyScaleLevel Base step */
|
||||
UINT8 keyon; /* key on/off flag */
|
||||
} OPL_CH;
|
||||
|
||||
/* OPL state */
|
||||
typedef struct fm_opl_f {
|
||||
UINT8 type; /* chip type */
|
||||
int clock; /* master clock (Hz) */
|
||||
int rate; /* sampling rate (Hz) */
|
||||
double freqbase; /* frequency base */
|
||||
double TimerBase; /* Timer base time (==sampling time) */
|
||||
UINT8 address; /* address register */
|
||||
UINT8 status; /* status flag */
|
||||
UINT8 statusmask; /* status mask */
|
||||
UINT32 mode; /* Reg.08 : CSM , notesel,etc. */
|
||||
/* Timer */
|
||||
int T[2]; /* timer counter */
|
||||
UINT8 st[2]; /* timer enable */
|
||||
/* FM channel slots */
|
||||
OPL_CH *P_CH; /* pointer of CH */
|
||||
int max_ch; /* maximum channel */
|
||||
/* Rythm sention */
|
||||
UINT8 rythm; /* Rythm mode , key flag */
|
||||
#if BUILD_Y8950
|
||||
/* Delta-T ADPCM unit (Y8950) */
|
||||
YM_DELTAT *deltat; /* DELTA-T ADPCM */
|
||||
#endif
|
||||
/* Keyboard / I/O interface unit (Y8950) */
|
||||
UINT8 portDirection;
|
||||
UINT8 portLatch;
|
||||
OPL_PORTHANDLER_R porthandler_r;
|
||||
OPL_PORTHANDLER_W porthandler_w;
|
||||
int port_param;
|
||||
OPL_PORTHANDLER_R keyboardhandler_r;
|
||||
OPL_PORTHANDLER_W keyboardhandler_w;
|
||||
int keyboard_param;
|
||||
/* time tables */
|
||||
INT32 AR_TABLE[75]; /* atttack rate tables */
|
||||
INT32 DR_TABLE[75]; /* decay rate tables */
|
||||
UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */
|
||||
/* LFO */
|
||||
INT32 *ams_table;
|
||||
INT32 *vib_table;
|
||||
INT32 amsCnt;
|
||||
INT32 amsIncr;
|
||||
INT32 vibCnt;
|
||||
INT32 vibIncr;
|
||||
/* wave selector enable flag */
|
||||
UINT8 wavesel;
|
||||
/* external event callback handler */
|
||||
OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
|
||||
int TimerParam; /* TIMER parameter */
|
||||
OPL_IRQHANDLER IRQHandler; /* IRQ handler */
|
||||
int IRQParam; /* IRQ parameter */
|
||||
OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
|
||||
int UpdateParam; /* stream update parameter */
|
||||
} FM_OPL;
|
||||
|
||||
/* ---------- Generic interface section ---------- */
|
||||
#define OPL_TYPE_YM3526 (0)
|
||||
#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
|
||||
#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
|
||||
|
||||
FM_OPL *OPLCreate(int type, int clock, int rate);
|
||||
void OPLDestroy(FM_OPL *OPL);
|
||||
void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
|
||||
void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param);
|
||||
void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param);
|
||||
/* Y8950 port handlers */
|
||||
void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param);
|
||||
void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param);
|
||||
|
||||
void OPLResetChip(FM_OPL *OPL);
|
||||
int OPLWrite(FM_OPL *OPL,int a,int v);
|
||||
unsigned char OPLRead(FM_OPL *OPL,int a);
|
||||
int OPLTimerOver(FM_OPL *OPL,int c);
|
||||
|
||||
/* YM3626/YM3812 local section */
|
||||
void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
|
||||
|
||||
void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
|
||||
|
||||
#endif
|
||||
+470
@@ -0,0 +1,470 @@
|
||||
/*
|
||||
* QEMU 8253/8254 interval timer emulation
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_PIT
|
||||
|
||||
#define RW_STATE_LSB 1
|
||||
#define RW_STATE_MSB 2
|
||||
#define RW_STATE_WORD0 3
|
||||
#define RW_STATE_WORD1 4
|
||||
|
||||
typedef struct PITChannelState {
|
||||
int count; /* can be 65536 */
|
||||
uint16_t latched_count;
|
||||
uint8_t count_latched;
|
||||
uint8_t status_latched;
|
||||
uint8_t status;
|
||||
uint8_t read_state;
|
||||
uint8_t write_state;
|
||||
uint8_t write_latch;
|
||||
uint8_t rw_mode;
|
||||
uint8_t mode;
|
||||
uint8_t bcd; /* not supported */
|
||||
uint8_t gate; /* timer start */
|
||||
int64_t count_load_time;
|
||||
/* irq handling */
|
||||
int64_t next_transition_time;
|
||||
QEMUTimer *irq_timer;
|
||||
int irq;
|
||||
} PITChannelState;
|
||||
|
||||
struct PITState {
|
||||
PITChannelState channels[3];
|
||||
};
|
||||
|
||||
static PITState pit_state;
|
||||
|
||||
static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
|
||||
|
||||
static int pit_get_count(PITChannelState *s)
|
||||
{
|
||||
uint64_t d;
|
||||
int counter;
|
||||
|
||||
d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ, ticks_per_sec);
|
||||
switch(s->mode) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 4:
|
||||
case 5:
|
||||
counter = (s->count - d) & 0xffff;
|
||||
break;
|
||||
case 3:
|
||||
/* XXX: may be incorrect for odd counts */
|
||||
counter = s->count - ((2 * d) % s->count);
|
||||
break;
|
||||
default:
|
||||
counter = s->count - (d % s->count);
|
||||
break;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
/* get pit output bit */
|
||||
static int pit_get_out1(PITChannelState *s, int64_t current_time)
|
||||
{
|
||||
uint64_t d;
|
||||
int out;
|
||||
|
||||
d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
|
||||
switch(s->mode) {
|
||||
default:
|
||||
case 0:
|
||||
out = (d >= s->count);
|
||||
break;
|
||||
case 1:
|
||||
out = (d < s->count);
|
||||
break;
|
||||
case 2:
|
||||
if ((d % s->count) == 0 && d != 0)
|
||||
out = 1;
|
||||
else
|
||||
out = 0;
|
||||
break;
|
||||
case 3:
|
||||
out = (d % s->count) < ((s->count + 1) >> 1);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
out = (d == s->count);
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int pit_get_out(PITState *pit, int channel, int64_t current_time)
|
||||
{
|
||||
PITChannelState *s = &pit->channels[channel];
|
||||
return pit_get_out1(s, current_time);
|
||||
}
|
||||
|
||||
/* return -1 if no transition will occur. */
|
||||
static int64_t pit_get_next_transition_time(PITChannelState *s,
|
||||
int64_t current_time)
|
||||
{
|
||||
uint64_t d, next_time, base;
|
||||
int period2;
|
||||
|
||||
d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
|
||||
switch(s->mode) {
|
||||
default:
|
||||
case 0:
|
||||
case 1:
|
||||
if (d < s->count)
|
||||
next_time = s->count;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
case 2:
|
||||
base = (d / s->count) * s->count;
|
||||
if ((d - base) == 0 && d != 0)
|
||||
next_time = base + s->count;
|
||||
else
|
||||
next_time = base + s->count + 1;
|
||||
break;
|
||||
case 3:
|
||||
base = (d / s->count) * s->count;
|
||||
period2 = ((s->count + 1) >> 1);
|
||||
if ((d - base) < period2)
|
||||
next_time = base + period2;
|
||||
else
|
||||
next_time = base + s->count;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
if (d < s->count)
|
||||
next_time = s->count;
|
||||
else if (d == s->count)
|
||||
next_time = s->count + 1;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
/* convert to timer units */
|
||||
next_time = s->count_load_time + muldiv64(next_time, ticks_per_sec, PIT_FREQ);
|
||||
/* fix potential rounding problems */
|
||||
/* XXX: better solution: use a clock at PIT_FREQ Hz */
|
||||
if (next_time <= current_time)
|
||||
next_time = current_time + 1;
|
||||
return next_time;
|
||||
}
|
||||
|
||||
/* val must be 0 or 1 */
|
||||
void pit_set_gate(PITState *pit, int channel, int val)
|
||||
{
|
||||
PITChannelState *s = &pit->channels[channel];
|
||||
|
||||
switch(s->mode) {
|
||||
default:
|
||||
case 0:
|
||||
case 4:
|
||||
/* XXX: just disable/enable counting */
|
||||
break;
|
||||
case 1:
|
||||
case 5:
|
||||
if (s->gate < val) {
|
||||
/* restart counting on rising edge */
|
||||
s->count_load_time = qemu_get_clock(vm_clock);
|
||||
pit_irq_timer_update(s, s->count_load_time);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
if (s->gate < val) {
|
||||
/* restart counting on rising edge */
|
||||
s->count_load_time = qemu_get_clock(vm_clock);
|
||||
pit_irq_timer_update(s, s->count_load_time);
|
||||
}
|
||||
/* XXX: disable/enable counting */
|
||||
break;
|
||||
}
|
||||
s->gate = val;
|
||||
}
|
||||
|
||||
int pit_get_gate(PITState *pit, int channel)
|
||||
{
|
||||
PITChannelState *s = &pit->channels[channel];
|
||||
return s->gate;
|
||||
}
|
||||
|
||||
static inline void pit_load_count(PITChannelState *s, int val)
|
||||
{
|
||||
if (val == 0)
|
||||
val = 0x10000;
|
||||
s->count_load_time = qemu_get_clock(vm_clock);
|
||||
s->count = val;
|
||||
pit_irq_timer_update(s, s->count_load_time);
|
||||
}
|
||||
|
||||
/* if already latched, do not latch again */
|
||||
static void pit_latch_count(PITChannelState *s)
|
||||
{
|
||||
if (!s->count_latched) {
|
||||
s->latched_count = pit_get_count(s);
|
||||
s->count_latched = s->rw_mode;
|
||||
}
|
||||
}
|
||||
|
||||
static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
PITState *pit = opaque;
|
||||
int channel, access;
|
||||
PITChannelState *s;
|
||||
|
||||
addr &= 3;
|
||||
if (addr == 3) {
|
||||
channel = val >> 6;
|
||||
if (channel == 3) {
|
||||
/* read back command */
|
||||
for(channel = 0; channel < 3; channel++) {
|
||||
s = &pit->channels[channel];
|
||||
if (val & (2 << channel)) {
|
||||
if (!(val & 0x20)) {
|
||||
pit_latch_count(s);
|
||||
}
|
||||
if (!(val & 0x10) && !s->status_latched) {
|
||||
/* status latch */
|
||||
/* XXX: add BCD and null count */
|
||||
s->status = (pit_get_out1(s, qemu_get_clock(vm_clock)) << 7) |
|
||||
(s->rw_mode << 4) |
|
||||
(s->mode << 1) |
|
||||
s->bcd;
|
||||
s->status_latched = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = &pit->channels[channel];
|
||||
access = (val >> 4) & 3;
|
||||
if (access == 0) {
|
||||
pit_latch_count(s);
|
||||
} else {
|
||||
s->rw_mode = access;
|
||||
s->read_state = access;
|
||||
s->write_state = access;
|
||||
|
||||
s->mode = (val >> 1) & 7;
|
||||
s->bcd = val & 1;
|
||||
/* XXX: update irq timer ? */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = &pit->channels[addr];
|
||||
switch(s->write_state) {
|
||||
default:
|
||||
case RW_STATE_LSB:
|
||||
pit_load_count(s, val);
|
||||
break;
|
||||
case RW_STATE_MSB:
|
||||
pit_load_count(s, val << 8);
|
||||
break;
|
||||
case RW_STATE_WORD0:
|
||||
s->write_latch = val;
|
||||
s->write_state = RW_STATE_WORD1;
|
||||
break;
|
||||
case RW_STATE_WORD1:
|
||||
pit_load_count(s, s->write_latch | (val << 8));
|
||||
s->write_state = RW_STATE_WORD0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
PITState *pit = opaque;
|
||||
int ret, count;
|
||||
PITChannelState *s;
|
||||
|
||||
addr &= 3;
|
||||
s = &pit->channels[addr];
|
||||
if (s->status_latched) {
|
||||
s->status_latched = 0;
|
||||
ret = s->status;
|
||||
} else if (s->count_latched) {
|
||||
switch(s->count_latched) {
|
||||
default:
|
||||
case RW_STATE_LSB:
|
||||
ret = s->latched_count & 0xff;
|
||||
s->count_latched = 0;
|
||||
break;
|
||||
case RW_STATE_MSB:
|
||||
ret = s->latched_count >> 8;
|
||||
s->count_latched = 0;
|
||||
break;
|
||||
case RW_STATE_WORD0:
|
||||
ret = s->latched_count & 0xff;
|
||||
s->count_latched = RW_STATE_MSB;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(s->read_state) {
|
||||
default:
|
||||
case RW_STATE_LSB:
|
||||
count = pit_get_count(s);
|
||||
ret = count & 0xff;
|
||||
break;
|
||||
case RW_STATE_MSB:
|
||||
count = pit_get_count(s);
|
||||
ret = (count >> 8) & 0xff;
|
||||
break;
|
||||
case RW_STATE_WORD0:
|
||||
count = pit_get_count(s);
|
||||
ret = count & 0xff;
|
||||
s->read_state = RW_STATE_WORD1;
|
||||
break;
|
||||
case RW_STATE_WORD1:
|
||||
count = pit_get_count(s);
|
||||
ret = (count >> 8) & 0xff;
|
||||
s->read_state = RW_STATE_WORD0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
|
||||
{
|
||||
int64_t expire_time;
|
||||
int irq_level;
|
||||
|
||||
if (!s->irq_timer)
|
||||
return;
|
||||
expire_time = pit_get_next_transition_time(s, current_time);
|
||||
irq_level = pit_get_out1(s, current_time);
|
||||
pic_set_irq(s->irq, irq_level);
|
||||
#ifdef DEBUG_PIT
|
||||
printf("irq_level=%d next_delay=%f\n",
|
||||
irq_level,
|
||||
(double)(expire_time - current_time) / ticks_per_sec);
|
||||
#endif
|
||||
s->next_transition_time = expire_time;
|
||||
if (expire_time != -1)
|
||||
qemu_mod_timer(s->irq_timer, expire_time);
|
||||
else
|
||||
qemu_del_timer(s->irq_timer);
|
||||
}
|
||||
|
||||
static void pit_irq_timer(void *opaque)
|
||||
{
|
||||
PITChannelState *s = opaque;
|
||||
|
||||
pit_irq_timer_update(s, s->next_transition_time);
|
||||
}
|
||||
|
||||
static void pit_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
PITState *pit = opaque;
|
||||
PITChannelState *s;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 3; i++) {
|
||||
s = &pit->channels[i];
|
||||
qemu_put_be32s(f, &s->count);
|
||||
qemu_put_be16s(f, &s->latched_count);
|
||||
qemu_put_8s(f, &s->count_latched);
|
||||
qemu_put_8s(f, &s->status_latched);
|
||||
qemu_put_8s(f, &s->status);
|
||||
qemu_put_8s(f, &s->read_state);
|
||||
qemu_put_8s(f, &s->write_state);
|
||||
qemu_put_8s(f, &s->write_latch);
|
||||
qemu_put_8s(f, &s->rw_mode);
|
||||
qemu_put_8s(f, &s->mode);
|
||||
qemu_put_8s(f, &s->bcd);
|
||||
qemu_put_8s(f, &s->gate);
|
||||
qemu_put_be64s(f, &s->count_load_time);
|
||||
if (s->irq_timer) {
|
||||
qemu_put_be64s(f, &s->next_transition_time);
|
||||
qemu_put_timer(f, s->irq_timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int pit_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
PITState *pit = opaque;
|
||||
PITChannelState *s;
|
||||
int i;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
for(i = 0; i < 3; i++) {
|
||||
s = &pit->channels[i];
|
||||
qemu_get_be32s(f, &s->count);
|
||||
qemu_get_be16s(f, &s->latched_count);
|
||||
qemu_get_8s(f, &s->count_latched);
|
||||
qemu_get_8s(f, &s->status_latched);
|
||||
qemu_get_8s(f, &s->status);
|
||||
qemu_get_8s(f, &s->read_state);
|
||||
qemu_get_8s(f, &s->write_state);
|
||||
qemu_get_8s(f, &s->write_latch);
|
||||
qemu_get_8s(f, &s->rw_mode);
|
||||
qemu_get_8s(f, &s->mode);
|
||||
qemu_get_8s(f, &s->bcd);
|
||||
qemu_get_8s(f, &s->gate);
|
||||
qemu_get_be64s(f, &s->count_load_time);
|
||||
if (s->irq_timer) {
|
||||
qemu_get_be64s(f, &s->next_transition_time);
|
||||
qemu_get_timer(f, s->irq_timer);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pit_reset(void *opaque)
|
||||
{
|
||||
PITState *pit = opaque;
|
||||
PITChannelState *s;
|
||||
int i;
|
||||
|
||||
for(i = 0;i < 3; i++) {
|
||||
s = &pit->channels[i];
|
||||
s->mode = 3;
|
||||
s->gate = (i != 2);
|
||||
pit_load_count(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
PITState *pit_init(int base, int irq)
|
||||
{
|
||||
PITState *pit = &pit_state;
|
||||
PITChannelState *s;
|
||||
|
||||
s = &pit->channels[0];
|
||||
/* the timer 0 is connected to an IRQ */
|
||||
s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s);
|
||||
s->irq = irq;
|
||||
|
||||
register_savevm("i8254", base, 1, pit_save, pit_load, pit);
|
||||
|
||||
qemu_register_reset(pit_reset, pit);
|
||||
register_ioport_write(base, 4, 1, pit_ioport_write, pit);
|
||||
register_ioport_read(base, 3, 1, pit_ioport_read, pit);
|
||||
|
||||
pit_reset(pit);
|
||||
|
||||
return pit;
|
||||
}
|
||||
+521
@@ -0,0 +1,521 @@
|
||||
/*
|
||||
* QEMU 8259 interrupt controller emulation
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
/* debug PIC */
|
||||
//#define DEBUG_PIC
|
||||
|
||||
//#define DEBUG_IRQ_LATENCY
|
||||
//#define DEBUG_IRQ_COUNT
|
||||
|
||||
typedef struct PicState {
|
||||
uint8_t last_irr; /* edge detection */
|
||||
uint8_t irr; /* interrupt request register */
|
||||
uint8_t imr; /* interrupt mask register */
|
||||
uint8_t isr; /* interrupt service register */
|
||||
uint8_t priority_add; /* highest irq priority */
|
||||
uint8_t irq_base;
|
||||
uint8_t read_reg_select;
|
||||
uint8_t poll;
|
||||
uint8_t special_mask;
|
||||
uint8_t init_state;
|
||||
uint8_t auto_eoi;
|
||||
uint8_t rotate_on_auto_eoi;
|
||||
uint8_t special_fully_nested_mode;
|
||||
uint8_t init4; /* true if 4 byte init */
|
||||
uint8_t elcr; /* PIIX edge/trigger selection*/
|
||||
uint8_t elcr_mask;
|
||||
} PicState;
|
||||
|
||||
/* 0 is master pic, 1 is slave pic */
|
||||
static PicState pics[2];
|
||||
|
||||
#if defined(DEBUG_PIC) || defined (DEBUG_IRQ_COUNT)
|
||||
static int irq_level[16];
|
||||
#endif
|
||||
#ifdef DEBUG_IRQ_COUNT
|
||||
static uint64_t irq_count[16];
|
||||
#endif
|
||||
|
||||
/* set irq level. If an edge is detected, then the IRR is set to 1 */
|
||||
static inline void pic_set_irq1(PicState *s, int irq, int level)
|
||||
{
|
||||
int mask;
|
||||
mask = 1 << irq;
|
||||
if (s->elcr & mask) {
|
||||
/* level triggered */
|
||||
if (level) {
|
||||
s->irr |= mask;
|
||||
s->last_irr |= mask;
|
||||
} else {
|
||||
s->irr &= ~mask;
|
||||
s->last_irr &= ~mask;
|
||||
}
|
||||
} else {
|
||||
/* edge triggered */
|
||||
if (level) {
|
||||
if ((s->last_irr & mask) == 0)
|
||||
s->irr |= mask;
|
||||
s->last_irr |= mask;
|
||||
} else {
|
||||
s->last_irr &= ~mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return the highest priority found in mask (highest = smallest
|
||||
number). Return 8 if no irq */
|
||||
static inline int get_priority(PicState *s, int mask)
|
||||
{
|
||||
int priority;
|
||||
if (mask == 0)
|
||||
return 8;
|
||||
priority = 0;
|
||||
while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0)
|
||||
priority++;
|
||||
return priority;
|
||||
}
|
||||
|
||||
/* return the pic wanted interrupt. return -1 if none */
|
||||
static int pic_get_irq(PicState *s)
|
||||
{
|
||||
int mask, cur_priority, priority;
|
||||
|
||||
mask = s->irr & ~s->imr;
|
||||
priority = get_priority(s, mask);
|
||||
if (priority == 8)
|
||||
return -1;
|
||||
/* compute current priority. If special fully nested mode on the
|
||||
master, the IRQ coming from the slave is not taken into account
|
||||
for the priority computation. */
|
||||
mask = s->isr;
|
||||
if (s->special_fully_nested_mode && s == &pics[0])
|
||||
mask &= ~(1 << 2);
|
||||
cur_priority = get_priority(s, mask);
|
||||
if (priority < cur_priority) {
|
||||
/* higher priority found: an irq should be generated */
|
||||
return (priority + s->priority_add) & 7;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* raise irq to CPU if necessary. must be called every time the active
|
||||
irq may change */
|
||||
static void pic_update_irq(void)
|
||||
{
|
||||
int irq2, irq;
|
||||
|
||||
/* first look at slave pic */
|
||||
irq2 = pic_get_irq(&pics[1]);
|
||||
if (irq2 >= 0) {
|
||||
/* if irq request by slave pic, signal master PIC */
|
||||
pic_set_irq1(&pics[0], 2, 1);
|
||||
pic_set_irq1(&pics[0], 2, 0);
|
||||
}
|
||||
/* look at requested irq */
|
||||
irq = pic_get_irq(&pics[0]);
|
||||
if (irq >= 0) {
|
||||
#if defined(DEBUG_PIC)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < 2; i++) {
|
||||
printf("pic%d: imr=%x irr=%x padd=%d\n",
|
||||
i, pics[i].imr, pics[i].irr, pics[i].priority_add);
|
||||
|
||||
}
|
||||
}
|
||||
printf("pic: cpu_interrupt\n");
|
||||
#endif
|
||||
cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_IRQ_LATENCY
|
||||
int64_t irq_time[16];
|
||||
#endif
|
||||
|
||||
void pic_set_irq(int irq, int level)
|
||||
{
|
||||
#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
|
||||
if (level != irq_level[irq]) {
|
||||
#if defined(DEBUG_PIC)
|
||||
printf("pic_set_irq: irq=%d level=%d\n", irq, level);
|
||||
#endif
|
||||
irq_level[irq] = level;
|
||||
#ifdef DEBUG_IRQ_COUNT
|
||||
if (level == 1)
|
||||
irq_count[irq]++;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#ifdef DEBUG_IRQ_LATENCY
|
||||
if (level) {
|
||||
irq_time[irq] = qemu_get_clock(vm_clock);
|
||||
}
|
||||
#endif
|
||||
pic_set_irq1(&pics[irq >> 3], irq & 7, level);
|
||||
pic_update_irq();
|
||||
}
|
||||
|
||||
/* acknowledge interrupt 'irq' */
|
||||
static inline void pic_intack(PicState *s, int irq)
|
||||
{
|
||||
if (s->auto_eoi) {
|
||||
if (s->rotate_on_auto_eoi)
|
||||
s->priority_add = (irq + 1) & 7;
|
||||
} else {
|
||||
s->isr |= (1 << irq);
|
||||
}
|
||||
/* We don't clear a level sensitive interrupt here */
|
||||
if (!(s->elcr & (1 << irq)))
|
||||
s->irr &= ~(1 << irq);
|
||||
}
|
||||
|
||||
int cpu_get_pic_interrupt(CPUState *env)
|
||||
{
|
||||
int irq, irq2, intno;
|
||||
|
||||
#ifdef TARGET_X86_64
|
||||
intno = apic_get_interrupt(env);
|
||||
if (intno >= 0) {
|
||||
/* set irq request if a PIC irq is still pending */
|
||||
/* XXX: improve that */
|
||||
pic_update_irq();
|
||||
return intno;
|
||||
}
|
||||
#endif
|
||||
/* read the irq from the PIC */
|
||||
|
||||
irq = pic_get_irq(&pics[0]);
|
||||
if (irq >= 0) {
|
||||
pic_intack(&pics[0], irq);
|
||||
if (irq == 2) {
|
||||
irq2 = pic_get_irq(&pics[1]);
|
||||
if (irq2 >= 0) {
|
||||
pic_intack(&pics[1], irq2);
|
||||
} else {
|
||||
/* spurious IRQ on slave controller */
|
||||
irq2 = 7;
|
||||
}
|
||||
intno = pics[1].irq_base + irq2;
|
||||
irq = irq2 + 8;
|
||||
} else {
|
||||
intno = pics[0].irq_base + irq;
|
||||
}
|
||||
} else {
|
||||
/* spurious IRQ on host controller */
|
||||
irq = 7;
|
||||
intno = pics[0].irq_base + irq;
|
||||
}
|
||||
pic_update_irq();
|
||||
|
||||
#ifdef DEBUG_IRQ_LATENCY
|
||||
printf("IRQ%d latency=%0.3fus\n",
|
||||
irq,
|
||||
(double)(qemu_get_clock(vm_clock) - irq_time[irq]) * 1000000.0 / ticks_per_sec);
|
||||
#endif
|
||||
#if defined(DEBUG_PIC)
|
||||
printf("pic_interrupt: irq=%d\n", irq);
|
||||
#endif
|
||||
return intno;
|
||||
}
|
||||
|
||||
static void pic_reset(void *opaque)
|
||||
{
|
||||
PicState *s = opaque;
|
||||
int tmp;
|
||||
|
||||
tmp = s->elcr_mask;
|
||||
memset(s, 0, sizeof(PicState));
|
||||
s->elcr_mask = tmp;
|
||||
}
|
||||
|
||||
static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
PicState *s = opaque;
|
||||
int priority, cmd, irq;
|
||||
|
||||
#ifdef DEBUG_PIC
|
||||
printf("pic_write: addr=0x%02x val=0x%02x\n", addr, val);
|
||||
#endif
|
||||
addr &= 1;
|
||||
if (addr == 0) {
|
||||
if (val & 0x10) {
|
||||
/* init */
|
||||
pic_reset(s);
|
||||
/* deassert a pending interrupt */
|
||||
cpu_reset_interrupt(cpu_single_env, CPU_INTERRUPT_HARD);
|
||||
|
||||
s->init_state = 1;
|
||||
s->init4 = val & 1;
|
||||
if (val & 0x02)
|
||||
hw_error("single mode not supported");
|
||||
if (val & 0x08)
|
||||
hw_error("level sensitive irq not supported");
|
||||
} else if (val & 0x08) {
|
||||
if (val & 0x04)
|
||||
s->poll = 1;
|
||||
if (val & 0x02)
|
||||
s->read_reg_select = val & 1;
|
||||
if (val & 0x40)
|
||||
s->special_mask = (val >> 5) & 1;
|
||||
} else {
|
||||
cmd = val >> 5;
|
||||
switch(cmd) {
|
||||
case 0:
|
||||
case 4:
|
||||
s->rotate_on_auto_eoi = cmd >> 2;
|
||||
break;
|
||||
case 1: /* end of interrupt */
|
||||
case 5:
|
||||
priority = get_priority(s, s->isr);
|
||||
if (priority != 8) {
|
||||
irq = (priority + s->priority_add) & 7;
|
||||
s->isr &= ~(1 << irq);
|
||||
if (cmd == 5)
|
||||
s->priority_add = (irq + 1) & 7;
|
||||
pic_update_irq();
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
irq = val & 7;
|
||||
s->isr &= ~(1 << irq);
|
||||
pic_update_irq();
|
||||
break;
|
||||
case 6:
|
||||
s->priority_add = (val + 1) & 7;
|
||||
pic_update_irq();
|
||||
break;
|
||||
case 7:
|
||||
irq = val & 7;
|
||||
s->isr &= ~(1 << irq);
|
||||
s->priority_add = (irq + 1) & 7;
|
||||
pic_update_irq();
|
||||
break;
|
||||
default:
|
||||
/* no operation */
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch(s->init_state) {
|
||||
case 0:
|
||||
/* normal mode */
|
||||
s->imr = val;
|
||||
pic_update_irq();
|
||||
break;
|
||||
case 1:
|
||||
s->irq_base = val & 0xf8;
|
||||
s->init_state = 2;
|
||||
break;
|
||||
case 2:
|
||||
if (s->init4) {
|
||||
s->init_state = 3;
|
||||
} else {
|
||||
s->init_state = 0;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
s->special_fully_nested_mode = (val >> 4) & 1;
|
||||
s->auto_eoi = (val >> 1) & 1;
|
||||
s->init_state = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t pic_poll_read (PicState *s, uint32_t addr1)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pic_get_irq(s);
|
||||
if (ret >= 0) {
|
||||
if (addr1 >> 7) {
|
||||
pics[0].isr &= ~(1 << 2);
|
||||
pics[0].irr &= ~(1 << 2);
|
||||
}
|
||||
s->irr &= ~(1 << ret);
|
||||
s->isr &= ~(1 << ret);
|
||||
if (addr1 >> 7 || ret != 2)
|
||||
pic_update_irq();
|
||||
} else {
|
||||
ret = 0x07;
|
||||
pic_update_irq();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t pic_ioport_read(void *opaque, uint32_t addr1)
|
||||
{
|
||||
PicState *s = opaque;
|
||||
unsigned int addr;
|
||||
int ret;
|
||||
|
||||
addr = addr1;
|
||||
addr &= 1;
|
||||
if (s->poll) {
|
||||
ret = pic_poll_read(s, addr1);
|
||||
s->poll = 0;
|
||||
} else {
|
||||
if (addr == 0) {
|
||||
if (s->read_reg_select)
|
||||
ret = s->isr;
|
||||
else
|
||||
ret = s->irr;
|
||||
} else {
|
||||
ret = s->imr;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_PIC
|
||||
printf("pic_read: addr=0x%02x val=0x%02x\n", addr1, ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* memory mapped interrupt status */
|
||||
uint32_t pic_intack_read(CPUState *env)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pic_poll_read(&pics[0], 0x00);
|
||||
if (ret == 2)
|
||||
ret = pic_poll_read(&pics[1], 0x80) + 8;
|
||||
/* Prepare for ISR read */
|
||||
pics[0].read_reg_select = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void elcr_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
PicState *s = opaque;
|
||||
s->elcr = val & s->elcr_mask;
|
||||
}
|
||||
|
||||
static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1)
|
||||
{
|
||||
PicState *s = opaque;
|
||||
return s->elcr;
|
||||
}
|
||||
|
||||
static void pic_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
PicState *s = opaque;
|
||||
|
||||
qemu_put_8s(f, &s->last_irr);
|
||||
qemu_put_8s(f, &s->irr);
|
||||
qemu_put_8s(f, &s->imr);
|
||||
qemu_put_8s(f, &s->isr);
|
||||
qemu_put_8s(f, &s->priority_add);
|
||||
qemu_put_8s(f, &s->irq_base);
|
||||
qemu_put_8s(f, &s->read_reg_select);
|
||||
qemu_put_8s(f, &s->poll);
|
||||
qemu_put_8s(f, &s->special_mask);
|
||||
qemu_put_8s(f, &s->init_state);
|
||||
qemu_put_8s(f, &s->auto_eoi);
|
||||
qemu_put_8s(f, &s->rotate_on_auto_eoi);
|
||||
qemu_put_8s(f, &s->special_fully_nested_mode);
|
||||
qemu_put_8s(f, &s->init4);
|
||||
qemu_put_8s(f, &s->elcr);
|
||||
}
|
||||
|
||||
static int pic_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
PicState *s = opaque;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
qemu_get_8s(f, &s->last_irr);
|
||||
qemu_get_8s(f, &s->irr);
|
||||
qemu_get_8s(f, &s->imr);
|
||||
qemu_get_8s(f, &s->isr);
|
||||
qemu_get_8s(f, &s->priority_add);
|
||||
qemu_get_8s(f, &s->irq_base);
|
||||
qemu_get_8s(f, &s->read_reg_select);
|
||||
qemu_get_8s(f, &s->poll);
|
||||
qemu_get_8s(f, &s->special_mask);
|
||||
qemu_get_8s(f, &s->init_state);
|
||||
qemu_get_8s(f, &s->auto_eoi);
|
||||
qemu_get_8s(f, &s->rotate_on_auto_eoi);
|
||||
qemu_get_8s(f, &s->special_fully_nested_mode);
|
||||
qemu_get_8s(f, &s->init4);
|
||||
qemu_get_8s(f, &s->elcr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: add generic master/slave system */
|
||||
static void pic_init1(int io_addr, int elcr_addr, PicState *s)
|
||||
{
|
||||
register_ioport_write(io_addr, 2, 1, pic_ioport_write, s);
|
||||
register_ioport_read(io_addr, 2, 1, pic_ioport_read, s);
|
||||
if (elcr_addr >= 0) {
|
||||
register_ioport_write(elcr_addr, 1, 1, elcr_ioport_write, s);
|
||||
register_ioport_read(elcr_addr, 1, 1, elcr_ioport_read, s);
|
||||
}
|
||||
register_savevm("i8259", io_addr, 1, pic_save, pic_load, s);
|
||||
qemu_register_reset(pic_reset, s);
|
||||
}
|
||||
|
||||
void pic_info(void)
|
||||
{
|
||||
int i;
|
||||
PicState *s;
|
||||
|
||||
for(i=0;i<2;i++) {
|
||||
s = &pics[i];
|
||||
term_printf("pic%d: irr=%02x imr=%02x isr=%02x hprio=%d irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
|
||||
i, s->irr, s->imr, s->isr, s->priority_add,
|
||||
s->irq_base, s->read_reg_select, s->elcr,
|
||||
s->special_fully_nested_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void irq_info(void)
|
||||
{
|
||||
#ifndef DEBUG_IRQ_COUNT
|
||||
term_printf("irq statistic code not compiled.\n");
|
||||
#else
|
||||
int i;
|
||||
int64_t count;
|
||||
|
||||
term_printf("IRQ statistics:\n");
|
||||
for (i = 0; i < 16; i++) {
|
||||
count = irq_count[i];
|
||||
if (count > 0)
|
||||
term_printf("%2d: %lld\n", i, count);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void pic_init(void)
|
||||
{
|
||||
pic_init1(0x20, 0x4d0, &pics[0]);
|
||||
pic_init1(0xa0, 0x4d1, &pics[1]);
|
||||
pics[0].elcr_mask = 0xf8;
|
||||
pics[1].elcr_mask = 0xde;
|
||||
}
|
||||
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* QEMU SPARC iommu emulation
|
||||
*
|
||||
* Copyright (c) 2003-2005 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
/* debug iommu */
|
||||
//#define DEBUG_IOMMU
|
||||
|
||||
#ifdef DEBUG_IOMMU
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { printf("IOMMU: " fmt , ##args); } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, args...)
|
||||
#endif
|
||||
|
||||
#define IOMMU_NREGS (3*4096)
|
||||
#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */
|
||||
#define IOMMU_CTRL_VERS 0x0f000000 /* Version */
|
||||
#define IOMMU_CTRL_RNGE 0x0000001c /* Mapping RANGE */
|
||||
#define IOMMU_RNGE_16MB 0x00000000 /* 0xff000000 -> 0xffffffff */
|
||||
#define IOMMU_RNGE_32MB 0x00000004 /* 0xfe000000 -> 0xffffffff */
|
||||
#define IOMMU_RNGE_64MB 0x00000008 /* 0xfc000000 -> 0xffffffff */
|
||||
#define IOMMU_RNGE_128MB 0x0000000c /* 0xf8000000 -> 0xffffffff */
|
||||
#define IOMMU_RNGE_256MB 0x00000010 /* 0xf0000000 -> 0xffffffff */
|
||||
#define IOMMU_RNGE_512MB 0x00000014 /* 0xe0000000 -> 0xffffffff */
|
||||
#define IOMMU_RNGE_1GB 0x00000018 /* 0xc0000000 -> 0xffffffff */
|
||||
#define IOMMU_RNGE_2GB 0x0000001c /* 0x80000000 -> 0xffffffff */
|
||||
#define IOMMU_CTRL_ENAB 0x00000001 /* IOMMU Enable */
|
||||
|
||||
/* The format of an iopte in the page tables */
|
||||
#define IOPTE_PAGE 0x07ffff00 /* Physical page number (PA[30:12]) */
|
||||
#define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or Viking/MXCC) */
|
||||
#define IOPTE_WRITE 0x00000004 /* Writeable */
|
||||
#define IOPTE_VALID 0x00000002 /* IOPTE is valid */
|
||||
#define IOPTE_WAZ 0x00000001 /* Write as zeros */
|
||||
|
||||
#define PAGE_SHIFT 12
|
||||
#define PAGE_SIZE (1 << PAGE_SHIFT)
|
||||
#define PAGE_MASK (PAGE_SIZE - 1)
|
||||
|
||||
typedef struct IOMMUState {
|
||||
uint32_t addr;
|
||||
uint32_t regs[IOMMU_NREGS];
|
||||
uint32_t iostart;
|
||||
} IOMMUState;
|
||||
|
||||
static uint32_t iommu_mem_readw(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
IOMMUState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr - s->addr) >> 2;
|
||||
switch (saddr) {
|
||||
default:
|
||||
DPRINTF("read reg[%d] = %x\n", saddr, s->regs[saddr]);
|
||||
return s->regs[saddr];
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iommu_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
IOMMUState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr - s->addr) >> 2;
|
||||
DPRINTF("write reg[%d] = %x\n", saddr, val);
|
||||
switch (saddr) {
|
||||
case 0:
|
||||
switch (val & IOMMU_CTRL_RNGE) {
|
||||
case IOMMU_RNGE_16MB:
|
||||
s->iostart = 0xff000000;
|
||||
break;
|
||||
case IOMMU_RNGE_32MB:
|
||||
s->iostart = 0xfe000000;
|
||||
break;
|
||||
case IOMMU_RNGE_64MB:
|
||||
s->iostart = 0xfc000000;
|
||||
break;
|
||||
case IOMMU_RNGE_128MB:
|
||||
s->iostart = 0xf8000000;
|
||||
break;
|
||||
case IOMMU_RNGE_256MB:
|
||||
s->iostart = 0xf0000000;
|
||||
break;
|
||||
case IOMMU_RNGE_512MB:
|
||||
s->iostart = 0xe0000000;
|
||||
break;
|
||||
case IOMMU_RNGE_1GB:
|
||||
s->iostart = 0xc0000000;
|
||||
break;
|
||||
default:
|
||||
case IOMMU_RNGE_2GB:
|
||||
s->iostart = 0x80000000;
|
||||
break;
|
||||
}
|
||||
DPRINTF("iostart = %x\n", s->iostart);
|
||||
/* Fall through */
|
||||
default:
|
||||
s->regs[saddr] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *iommu_mem_read[3] = {
|
||||
iommu_mem_readw,
|
||||
iommu_mem_readw,
|
||||
iommu_mem_readw,
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *iommu_mem_write[3] = {
|
||||
iommu_mem_writew,
|
||||
iommu_mem_writew,
|
||||
iommu_mem_writew,
|
||||
};
|
||||
|
||||
uint32_t iommu_translate_local(void *opaque, uint32_t addr)
|
||||
{
|
||||
IOMMUState *s = opaque;
|
||||
uint32_t iopte, pa, tmppte;
|
||||
|
||||
iopte = s->regs[1] << 4;
|
||||
addr &= ~s->iostart;
|
||||
iopte += (addr >> (PAGE_SHIFT - 2)) & ~3;
|
||||
cpu_physical_memory_read(iopte, (void *) &pa, 4);
|
||||
bswap32s(&pa);
|
||||
tmppte = pa;
|
||||
pa = ((pa & IOPTE_PAGE) << 4) + (addr & PAGE_MASK);
|
||||
DPRINTF("xlate dva %x => pa %x (iopte[%x] = %x)\n", addr, pa, iopte, tmppte);
|
||||
return pa;
|
||||
}
|
||||
|
||||
static void iommu_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
IOMMUState *s = opaque;
|
||||
int i;
|
||||
|
||||
qemu_put_be32s(f, &s->addr);
|
||||
for (i = 0; i < IOMMU_NREGS; i++)
|
||||
qemu_put_be32s(f, &s->regs[i]);
|
||||
qemu_put_be32s(f, &s->iostart);
|
||||
}
|
||||
|
||||
static int iommu_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
IOMMUState *s = opaque;
|
||||
int i;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
qemu_get_be32s(f, &s->addr);
|
||||
for (i = 0; i < IOMMU_NREGS; i++)
|
||||
qemu_put_be32s(f, &s->regs[i]);
|
||||
qemu_get_be32s(f, &s->iostart);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iommu_reset(void *opaque)
|
||||
{
|
||||
IOMMUState *s = opaque;
|
||||
|
||||
memset(s->regs, 0, IOMMU_NREGS * 4);
|
||||
s->iostart = 0;
|
||||
}
|
||||
|
||||
void *iommu_init(uint32_t addr)
|
||||
{
|
||||
IOMMUState *s;
|
||||
int iommu_io_memory;
|
||||
|
||||
s = qemu_mallocz(sizeof(IOMMUState));
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
s->addr = addr;
|
||||
|
||||
iommu_io_memory = cpu_register_io_memory(0, iommu_mem_read, iommu_mem_write, s);
|
||||
cpu_register_physical_memory(addr, IOMMU_NREGS * 4, iommu_io_memory);
|
||||
|
||||
register_savevm("iommu", addr, 1, iommu_save, iommu_load, s);
|
||||
qemu_register_reset(iommu_reset, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
+469
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* QEMU Lance emulation
|
||||
*
|
||||
* Copyright (c) 2003-2005 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
/* debug LANCE card */
|
||||
//#define DEBUG_LANCE
|
||||
|
||||
#ifdef DEBUG_LANCE
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { printf("LANCE: " fmt , ##args); } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, args...)
|
||||
#endif
|
||||
|
||||
#ifndef LANCE_LOG_TX_BUFFERS
|
||||
#define LANCE_LOG_TX_BUFFERS 4
|
||||
#define LANCE_LOG_RX_BUFFERS 4
|
||||
#endif
|
||||
|
||||
#define LE_CSR0 0
|
||||
#define LE_CSR1 1
|
||||
#define LE_CSR2 2
|
||||
#define LE_CSR3 3
|
||||
#define LE_NREGS (LE_CSR3 + 1)
|
||||
#define LE_MAXREG LE_CSR3
|
||||
|
||||
#define LE_RDP 0
|
||||
#define LE_RAP 1
|
||||
|
||||
#define LE_MO_PROM 0x8000 /* Enable promiscuous mode */
|
||||
|
||||
#define LE_C0_ERR 0x8000 /* Error: set if BAB, SQE, MISS or ME is set */
|
||||
#define LE_C0_BABL 0x4000 /* BAB: Babble: tx timeout. */
|
||||
#define LE_C0_CERR 0x2000 /* SQE: Signal quality error */
|
||||
#define LE_C0_MISS 0x1000 /* MISS: Missed a packet */
|
||||
#define LE_C0_MERR 0x0800 /* ME: Memory error */
|
||||
#define LE_C0_RINT 0x0400 /* Received interrupt */
|
||||
#define LE_C0_TINT 0x0200 /* Transmitter Interrupt */
|
||||
#define LE_C0_IDON 0x0100 /* IFIN: Init finished. */
|
||||
#define LE_C0_INTR 0x0080 /* Interrupt or error */
|
||||
#define LE_C0_INEA 0x0040 /* Interrupt enable */
|
||||
#define LE_C0_RXON 0x0020 /* Receiver on */
|
||||
#define LE_C0_TXON 0x0010 /* Transmitter on */
|
||||
#define LE_C0_TDMD 0x0008 /* Transmitter demand */
|
||||
#define LE_C0_STOP 0x0004 /* Stop the card */
|
||||
#define LE_C0_STRT 0x0002 /* Start the card */
|
||||
#define LE_C0_INIT 0x0001 /* Init the card */
|
||||
|
||||
#define LE_C3_BSWP 0x4 /* SWAP */
|
||||
#define LE_C3_ACON 0x2 /* ALE Control */
|
||||
#define LE_C3_BCON 0x1 /* Byte control */
|
||||
|
||||
/* Receive message descriptor 1 */
|
||||
#define LE_R1_OWN 0x80 /* Who owns the entry */
|
||||
#define LE_R1_ERR 0x40 /* Error: if FRA, OFL, CRC or BUF is set */
|
||||
#define LE_R1_FRA 0x20 /* FRA: Frame error */
|
||||
#define LE_R1_OFL 0x10 /* OFL: Frame overflow */
|
||||
#define LE_R1_CRC 0x08 /* CRC error */
|
||||
#define LE_R1_BUF 0x04 /* BUF: Buffer error */
|
||||
#define LE_R1_SOP 0x02 /* Start of packet */
|
||||
#define LE_R1_EOP 0x01 /* End of packet */
|
||||
#define LE_R1_POK 0x03 /* Packet is complete: SOP + EOP */
|
||||
|
||||
#define LE_T1_OWN 0x80 /* Lance owns the packet */
|
||||
#define LE_T1_ERR 0x40 /* Error summary */
|
||||
#define LE_T1_EMORE 0x10 /* Error: more than one retry needed */
|
||||
#define LE_T1_EONE 0x08 /* Error: one retry needed */
|
||||
#define LE_T1_EDEF 0x04 /* Error: deferred */
|
||||
#define LE_T1_SOP 0x02 /* Start of packet */
|
||||
#define LE_T1_EOP 0x01 /* End of packet */
|
||||
#define LE_T1_POK 0x03 /* Packet is complete: SOP + EOP */
|
||||
|
||||
#define LE_T3_BUF 0x8000 /* Buffer error */
|
||||
#define LE_T3_UFL 0x4000 /* Error underflow */
|
||||
#define LE_T3_LCOL 0x1000 /* Error late collision */
|
||||
#define LE_T3_CLOS 0x0800 /* Error carrier loss */
|
||||
#define LE_T3_RTY 0x0400 /* Error retry */
|
||||
#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry counter */
|
||||
|
||||
#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS))
|
||||
#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
|
||||
#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29)
|
||||
|
||||
#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS))
|
||||
#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
|
||||
#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)
|
||||
|
||||
#define PKT_BUF_SZ 1544
|
||||
#define RX_BUFF_SIZE PKT_BUF_SZ
|
||||
#define TX_BUFF_SIZE PKT_BUF_SZ
|
||||
|
||||
struct lance_rx_desc {
|
||||
unsigned short rmd0; /* low address of packet */
|
||||
unsigned char rmd1_bits; /* descriptor bits */
|
||||
unsigned char rmd1_hadr; /* high address of packet */
|
||||
short length; /* This length is 2s complement (negative)!
|
||||
* Buffer length
|
||||
*/
|
||||
unsigned short mblength; /* This is the actual number of bytes received */
|
||||
};
|
||||
|
||||
struct lance_tx_desc {
|
||||
unsigned short tmd0; /* low address of packet */
|
||||
unsigned char tmd1_bits; /* descriptor bits */
|
||||
unsigned char tmd1_hadr; /* high address of packet */
|
||||
short length; /* Length is 2s complement (negative)! */
|
||||
unsigned short misc;
|
||||
};
|
||||
|
||||
/* The LANCE initialization block, described in databook. */
|
||||
/* On the Sparc, this block should be on a DMA region */
|
||||
struct lance_init_block {
|
||||
unsigned short mode; /* Pre-set mode (reg. 15) */
|
||||
unsigned char phys_addr[6]; /* Physical ethernet address */
|
||||
unsigned filter[2]; /* Multicast filter. */
|
||||
|
||||
/* Receive and transmit ring base, along with extra bits. */
|
||||
unsigned short rx_ptr; /* receive descriptor addr */
|
||||
unsigned short rx_len; /* receive len and high addr */
|
||||
unsigned short tx_ptr; /* transmit descriptor addr */
|
||||
unsigned short tx_len; /* transmit len and high addr */
|
||||
|
||||
/* The Tx and Rx ring entries must aligned on 8-byte boundaries. */
|
||||
struct lance_rx_desc brx_ring[RX_RING_SIZE];
|
||||
struct lance_tx_desc btx_ring[TX_RING_SIZE];
|
||||
|
||||
char tx_buf [TX_RING_SIZE][TX_BUFF_SIZE];
|
||||
char pad[2]; /* align rx_buf for copy_and_sum(). */
|
||||
char rx_buf [RX_RING_SIZE][RX_BUFF_SIZE];
|
||||
};
|
||||
|
||||
#define LEDMA_REGS 4
|
||||
#define LEDMA_MAXADDR (LEDMA_REGS * 4 - 1)
|
||||
|
||||
typedef struct LANCEState {
|
||||
NetDriverState *nd;
|
||||
uint32_t leptr;
|
||||
uint16_t addr;
|
||||
uint16_t regs[LE_NREGS];
|
||||
uint8_t phys[6]; /* mac address */
|
||||
int irq;
|
||||
unsigned int rxptr, txptr;
|
||||
uint32_t ledmaregs[LEDMA_REGS];
|
||||
} LANCEState;
|
||||
|
||||
static void lance_send(void *opaque);
|
||||
|
||||
static void lance_reset(void *opaque)
|
||||
{
|
||||
LANCEState *s = opaque;
|
||||
memcpy(s->phys, s->nd->macaddr, 6);
|
||||
s->rxptr = 0;
|
||||
s->txptr = 0;
|
||||
memset(s->regs, 0, LE_NREGS * 2);
|
||||
s->regs[LE_CSR0] = LE_C0_STOP;
|
||||
memset(s->ledmaregs, 0, LEDMA_REGS * 4);
|
||||
}
|
||||
|
||||
static uint32_t lance_mem_readw(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
LANCEState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = addr & LE_MAXREG;
|
||||
switch (saddr >> 1) {
|
||||
case LE_RDP:
|
||||
DPRINTF("read dreg[%d] = %4.4x\n", s->addr, s->regs[s->addr]);
|
||||
return s->regs[s->addr];
|
||||
case LE_RAP:
|
||||
DPRINTF("read areg = %4.4x\n", s->addr);
|
||||
return s->addr;
|
||||
default:
|
||||
DPRINTF("read unknown(%d)\n", saddr>>1);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lance_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
LANCEState *s = opaque;
|
||||
uint32_t saddr;
|
||||
uint16_t reg;
|
||||
|
||||
saddr = addr & LE_MAXREG;
|
||||
switch (saddr >> 1) {
|
||||
case LE_RDP:
|
||||
DPRINTF("write dreg[%d] = %4.4x\n", s->addr, val);
|
||||
switch(s->addr) {
|
||||
case LE_CSR0:
|
||||
if (val & LE_C0_STOP) {
|
||||
s->regs[LE_CSR0] = LE_C0_STOP;
|
||||
break;
|
||||
}
|
||||
|
||||
reg = s->regs[LE_CSR0];
|
||||
|
||||
// 1 = clear for some bits
|
||||
reg &= ~(val & 0x7f00);
|
||||
|
||||
// generated bits
|
||||
reg &= ~(LE_C0_ERR | LE_C0_INTR);
|
||||
if (reg & 0x7100)
|
||||
reg |= LE_C0_ERR;
|
||||
if (reg & 0x7f00)
|
||||
reg |= LE_C0_INTR;
|
||||
|
||||
// direct bit
|
||||
reg &= ~LE_C0_INEA;
|
||||
reg |= val & LE_C0_INEA;
|
||||
|
||||
// exclusive bits
|
||||
if (val & LE_C0_INIT) {
|
||||
reg |= LE_C0_IDON | LE_C0_INIT;
|
||||
reg &= ~LE_C0_STOP;
|
||||
}
|
||||
else if (val & LE_C0_STRT) {
|
||||
reg |= LE_C0_STRT | LE_C0_RXON | LE_C0_TXON;
|
||||
reg &= ~LE_C0_STOP;
|
||||
}
|
||||
|
||||
s->regs[LE_CSR0] = reg;
|
||||
break;
|
||||
case LE_CSR1:
|
||||
s->leptr = (s->leptr & 0xffff0000) | (val & 0xffff);
|
||||
s->regs[s->addr] = val;
|
||||
break;
|
||||
case LE_CSR2:
|
||||
s->leptr = (s->leptr & 0xffff) | ((val & 0xffff) << 16);
|
||||
s->regs[s->addr] = val;
|
||||
break;
|
||||
case LE_CSR3:
|
||||
s->regs[s->addr] = val;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case LE_RAP:
|
||||
DPRINTF("write areg = %4.4x\n", val);
|
||||
if (val < LE_NREGS)
|
||||
s->addr = val;
|
||||
break;
|
||||
default:
|
||||
DPRINTF("write unknown(%d) = %4.4x\n", saddr>>1, val);
|
||||
break;
|
||||
}
|
||||
lance_send(s);
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *lance_mem_read[3] = {
|
||||
lance_mem_readw,
|
||||
lance_mem_readw,
|
||||
lance_mem_readw,
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *lance_mem_write[3] = {
|
||||
lance_mem_writew,
|
||||
lance_mem_writew,
|
||||
lance_mem_writew,
|
||||
};
|
||||
|
||||
|
||||
/* return the max buffer size if the LANCE can receive more data */
|
||||
static int lance_can_receive(void *opaque)
|
||||
{
|
||||
LANCEState *s = opaque;
|
||||
uint32_t dmaptr = s->leptr + s->ledmaregs[3];
|
||||
struct lance_init_block *ib;
|
||||
int i;
|
||||
uint8_t temp8;
|
||||
|
||||
if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP)
|
||||
return 0;
|
||||
|
||||
ib = (void *) iommu_translate(dmaptr);
|
||||
|
||||
for (i = 0; i < RX_RING_SIZE; i++) {
|
||||
cpu_physical_memory_read((uint32_t)&ib->brx_ring[i].rmd1_bits, (void *) &temp8, 1);
|
||||
if (temp8 == (LE_R1_OWN)) {
|
||||
DPRINTF("can receive %d\n", RX_BUFF_SIZE);
|
||||
return RX_BUFF_SIZE;
|
||||
}
|
||||
}
|
||||
DPRINTF("cannot receive\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MIN_BUF_SIZE 60
|
||||
|
||||
static void lance_receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
LANCEState *s = opaque;
|
||||
uint32_t dmaptr = s->leptr + s->ledmaregs[3];
|
||||
struct lance_init_block *ib;
|
||||
unsigned int i, old_rxptr;
|
||||
uint16_t temp16;
|
||||
uint8_t temp8;
|
||||
|
||||
DPRINTF("receive size %d\n", size);
|
||||
if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP)
|
||||
return;
|
||||
|
||||
ib = (void *) iommu_translate(dmaptr);
|
||||
|
||||
old_rxptr = s->rxptr;
|
||||
for (i = s->rxptr; i != ((old_rxptr - 1) & RX_RING_MOD_MASK); i = (i + 1) & RX_RING_MOD_MASK) {
|
||||
cpu_physical_memory_read((uint32_t)&ib->brx_ring[i].rmd1_bits, (void *) &temp8, 1);
|
||||
if (temp8 == (LE_R1_OWN)) {
|
||||
s->rxptr = (s->rxptr + 1) & RX_RING_MOD_MASK;
|
||||
temp16 = size + 4;
|
||||
bswap16s(&temp16);
|
||||
cpu_physical_memory_write((uint32_t)&ib->brx_ring[i].mblength, (void *) &temp16, 2);
|
||||
cpu_physical_memory_write((uint32_t)&ib->rx_buf[i], buf, size);
|
||||
temp8 = LE_R1_POK;
|
||||
cpu_physical_memory_write((uint32_t)&ib->brx_ring[i].rmd1_bits, (void *) &temp8, 1);
|
||||
s->regs[LE_CSR0] |= LE_C0_RINT | LE_C0_INTR;
|
||||
if (s->regs[LE_CSR0] & LE_C0_INEA)
|
||||
pic_set_irq(s->irq, 1);
|
||||
DPRINTF("got packet, len %d\n", size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lance_send(void *opaque)
|
||||
{
|
||||
LANCEState *s = opaque;
|
||||
uint32_t dmaptr = s->leptr + s->ledmaregs[3];
|
||||
struct lance_init_block *ib;
|
||||
unsigned int i, old_txptr;
|
||||
uint16_t temp16;
|
||||
uint8_t temp8;
|
||||
char pkt_buf[PKT_BUF_SZ];
|
||||
|
||||
DPRINTF("sending packet? (csr0 %4.4x)\n", s->regs[LE_CSR0]);
|
||||
if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP)
|
||||
return;
|
||||
|
||||
ib = (void *) iommu_translate(dmaptr);
|
||||
|
||||
DPRINTF("sending packet? (dmaptr %8.8x) (ib %p) (btx_ring %p)\n", dmaptr, ib, &ib->btx_ring);
|
||||
old_txptr = s->txptr;
|
||||
for (i = s->txptr; i != ((old_txptr - 1) & TX_RING_MOD_MASK); i = (i + 1) & TX_RING_MOD_MASK) {
|
||||
cpu_physical_memory_read((uint32_t)&ib->btx_ring[i].tmd1_bits, (void *) &temp8, 1);
|
||||
if (temp8 == (LE_T1_POK|LE_T1_OWN)) {
|
||||
cpu_physical_memory_read((uint32_t)&ib->btx_ring[i].length, (void *) &temp16, 2);
|
||||
bswap16s(&temp16);
|
||||
temp16 = (~temp16) + 1;
|
||||
cpu_physical_memory_read((uint32_t)&ib->tx_buf[i], pkt_buf, temp16);
|
||||
DPRINTF("sending packet, len %d\n", temp16);
|
||||
qemu_send_packet(s->nd, pkt_buf, temp16);
|
||||
temp8 = LE_T1_POK;
|
||||
cpu_physical_memory_write((uint32_t)&ib->btx_ring[i].tmd1_bits, (void *) &temp8, 1);
|
||||
s->txptr = (s->txptr + 1) & TX_RING_MOD_MASK;
|
||||
s->regs[LE_CSR0] |= LE_C0_TINT | LE_C0_INTR;
|
||||
}
|
||||
}
|
||||
if ((s->regs[LE_CSR0] & LE_C0_INTR) && (s->regs[LE_CSR0] & LE_C0_INEA))
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
|
||||
static uint32_t ledma_mem_readl(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
LANCEState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr & LEDMA_MAXADDR) >> 2;
|
||||
return s->ledmaregs[saddr];
|
||||
}
|
||||
|
||||
static void ledma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
LANCEState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr & LEDMA_MAXADDR) >> 2;
|
||||
s->ledmaregs[saddr] = val;
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *ledma_mem_read[3] = {
|
||||
ledma_mem_readl,
|
||||
ledma_mem_readl,
|
||||
ledma_mem_readl,
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *ledma_mem_write[3] = {
|
||||
ledma_mem_writel,
|
||||
ledma_mem_writel,
|
||||
ledma_mem_writel,
|
||||
};
|
||||
|
||||
static void lance_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
LANCEState *s = opaque;
|
||||
int i;
|
||||
|
||||
qemu_put_be32s(f, &s->leptr);
|
||||
qemu_put_be16s(f, &s->addr);
|
||||
for (i = 0; i < LE_NREGS; i ++)
|
||||
qemu_put_be16s(f, &s->regs[i]);
|
||||
qemu_put_buffer(f, s->phys, 6);
|
||||
qemu_put_be32s(f, &s->irq);
|
||||
for (i = 0; i < LEDMA_REGS; i ++)
|
||||
qemu_put_be32s(f, &s->ledmaregs[i]);
|
||||
}
|
||||
|
||||
static int lance_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
LANCEState *s = opaque;
|
||||
int i;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
qemu_get_be32s(f, &s->leptr);
|
||||
qemu_get_be16s(f, &s->addr);
|
||||
for (i = 0; i < LE_NREGS; i ++)
|
||||
qemu_get_be16s(f, &s->regs[i]);
|
||||
qemu_get_buffer(f, s->phys, 6);
|
||||
qemu_get_be32s(f, &s->irq);
|
||||
for (i = 0; i < LEDMA_REGS; i ++)
|
||||
qemu_get_be32s(f, &s->ledmaregs[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lance_init(NetDriverState *nd, int irq, uint32_t leaddr, uint32_t ledaddr)
|
||||
{
|
||||
LANCEState *s;
|
||||
int lance_io_memory, ledma_io_memory;
|
||||
|
||||
s = qemu_mallocz(sizeof(LANCEState));
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
s->nd = nd;
|
||||
s->irq = irq;
|
||||
|
||||
lance_io_memory = cpu_register_io_memory(0, lance_mem_read, lance_mem_write, s);
|
||||
cpu_register_physical_memory(leaddr, 4, lance_io_memory);
|
||||
|
||||
ledma_io_memory = cpu_register_io_memory(0, ledma_mem_read, ledma_mem_write, s);
|
||||
cpu_register_physical_memory(ledaddr, 16, ledma_io_memory);
|
||||
|
||||
lance_reset(s);
|
||||
qemu_add_read_packet(nd, lance_can_receive, lance_receive, s);
|
||||
register_savevm("lance", leaddr, 1, lance_save, lance_load, s);
|
||||
qemu_register_reset(lance_reset, s);
|
||||
}
|
||||
|
||||
+356
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* QEMU M48T08 NVRAM emulation for Sparc platform
|
||||
*
|
||||
* Copyright (c) 2003-2004 Jocelyn Mayer
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
#include "m48t08.h"
|
||||
|
||||
//#define DEBUG_NVRAM
|
||||
|
||||
#if defined(DEBUG_NVRAM)
|
||||
#define NVRAM_PRINTF(fmt, args...) do { printf(fmt , ##args); } while (0)
|
||||
#else
|
||||
#define NVRAM_PRINTF(fmt, args...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define NVRAM_MAX_MEM 0x1ff0
|
||||
#define NVRAM_MAXADDR 0x1fff
|
||||
|
||||
struct m48t08_t {
|
||||
/* RTC management */
|
||||
time_t time_offset;
|
||||
time_t stop_time;
|
||||
/* NVRAM storage */
|
||||
uint8_t *buffer;
|
||||
};
|
||||
|
||||
/* Fake timer functions */
|
||||
/* Generic helpers for BCD */
|
||||
static inline uint8_t toBCD (uint8_t value)
|
||||
{
|
||||
return (((value / 10) % 10) << 4) | (value % 10);
|
||||
}
|
||||
|
||||
static inline uint8_t fromBCD (uint8_t BCD)
|
||||
{
|
||||
return ((BCD >> 4) * 10) + (BCD & 0x0F);
|
||||
}
|
||||
|
||||
/* RTC management helpers */
|
||||
static void get_time (m48t08_t *NVRAM, struct tm *tm)
|
||||
{
|
||||
time_t t;
|
||||
|
||||
t = time(NULL) + NVRAM->time_offset;
|
||||
#ifdef _WIN32
|
||||
memcpy(tm,localtime(&t),sizeof(*tm));
|
||||
#else
|
||||
localtime_r (&t, tm) ;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void set_time (m48t08_t *NVRAM, struct tm *tm)
|
||||
{
|
||||
time_t now, new_time;
|
||||
|
||||
new_time = mktime(tm);
|
||||
now = time(NULL);
|
||||
NVRAM->time_offset = new_time - now;
|
||||
}
|
||||
|
||||
/* Direct access to NVRAM */
|
||||
void m48t08_write (m48t08_t *NVRAM, uint32_t addr, uint8_t val)
|
||||
{
|
||||
struct tm tm;
|
||||
int tmp;
|
||||
|
||||
addr &= NVRAM_MAXADDR;
|
||||
switch (addr) {
|
||||
case 0x1FF8:
|
||||
/* control */
|
||||
NVRAM->buffer[0x1FF8] = (val & ~0xA0) | 0x90;
|
||||
break;
|
||||
case 0x1FF9:
|
||||
/* seconds (BCD) */
|
||||
tmp = fromBCD(val & 0x7F);
|
||||
if (tmp >= 0 && tmp <= 59) {
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_sec = tmp;
|
||||
set_time(NVRAM, &tm);
|
||||
}
|
||||
if ((val & 0x80) ^ (NVRAM->buffer[0x1FF9] & 0x80)) {
|
||||
if (val & 0x80) {
|
||||
NVRAM->stop_time = time(NULL);
|
||||
} else {
|
||||
NVRAM->time_offset += NVRAM->stop_time - time(NULL);
|
||||
NVRAM->stop_time = 0;
|
||||
}
|
||||
}
|
||||
NVRAM->buffer[0x1FF9] = val & 0x80;
|
||||
break;
|
||||
case 0x1FFA:
|
||||
/* minutes (BCD) */
|
||||
tmp = fromBCD(val & 0x7F);
|
||||
if (tmp >= 0 && tmp <= 59) {
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_min = tmp;
|
||||
set_time(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
case 0x1FFB:
|
||||
/* hours (BCD) */
|
||||
tmp = fromBCD(val & 0x3F);
|
||||
if (tmp >= 0 && tmp <= 23) {
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_hour = tmp;
|
||||
set_time(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
case 0x1FFC:
|
||||
/* day of the week / century */
|
||||
tmp = fromBCD(val & 0x07);
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_wday = tmp;
|
||||
set_time(NVRAM, &tm);
|
||||
NVRAM->buffer[0x1FFC] = val & 0x40;
|
||||
break;
|
||||
case 0x1FFD:
|
||||
/* date */
|
||||
tmp = fromBCD(val & 0x1F);
|
||||
if (tmp != 0) {
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_mday = tmp;
|
||||
set_time(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
case 0x1FFE:
|
||||
/* month */
|
||||
tmp = fromBCD(val & 0x1F);
|
||||
if (tmp >= 1 && tmp <= 12) {
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_mon = tmp - 1;
|
||||
set_time(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
case 0x1FFF:
|
||||
/* year */
|
||||
tmp = fromBCD(val);
|
||||
if (tmp >= 0 && tmp <= 99) {
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_year = fromBCD(val);
|
||||
set_time(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NVRAM->buffer[addr] = val & 0xFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t m48t08_read (m48t08_t *NVRAM, uint32_t addr)
|
||||
{
|
||||
struct tm tm;
|
||||
uint8_t retval = 0xFF;
|
||||
|
||||
addr &= NVRAM_MAXADDR;
|
||||
switch (addr) {
|
||||
case 0x1FF8:
|
||||
/* control */
|
||||
goto do_read;
|
||||
case 0x1FF9:
|
||||
/* seconds (BCD) */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = (NVRAM->buffer[0x1FF9] & 0x80) | toBCD(tm.tm_sec);
|
||||
break;
|
||||
case 0x1FFA:
|
||||
/* minutes (BCD) */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = toBCD(tm.tm_min);
|
||||
break;
|
||||
case 0x1FFB:
|
||||
/* hours (BCD) */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = toBCD(tm.tm_hour);
|
||||
break;
|
||||
case 0x1FFC:
|
||||
/* day of the week / century */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = NVRAM->buffer[0x1FFC] | tm.tm_wday;
|
||||
break;
|
||||
case 0x1FFD:
|
||||
/* date */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = toBCD(tm.tm_mday);
|
||||
break;
|
||||
case 0x1FFE:
|
||||
/* month */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = toBCD(tm.tm_mon + 1);
|
||||
break;
|
||||
case 0x1FFF:
|
||||
/* year */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = toBCD(tm.tm_year);
|
||||
break;
|
||||
default:
|
||||
do_read:
|
||||
retval = NVRAM->buffer[addr];
|
||||
break;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
m48t08_t *NVRAM = opaque;
|
||||
|
||||
m48t08_write(NVRAM, addr, value);
|
||||
}
|
||||
|
||||
static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
m48t08_t *NVRAM = opaque;
|
||||
|
||||
m48t08_write(NVRAM, addr, value);
|
||||
m48t08_write(NVRAM, addr + 1, value >> 8);
|
||||
}
|
||||
|
||||
static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
m48t08_t *NVRAM = opaque;
|
||||
|
||||
m48t08_write(NVRAM, addr, value);
|
||||
m48t08_write(NVRAM, addr + 1, value >> 8);
|
||||
m48t08_write(NVRAM, addr + 2, value >> 16);
|
||||
m48t08_write(NVRAM, addr + 3, value >> 24);
|
||||
}
|
||||
|
||||
static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
m48t08_t *NVRAM = opaque;
|
||||
uint32_t retval = 0;
|
||||
|
||||
retval = m48t08_read(NVRAM, addr);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
m48t08_t *NVRAM = opaque;
|
||||
uint32_t retval = 0;
|
||||
|
||||
retval = m48t08_read(NVRAM, addr) << 8;
|
||||
retval |= m48t08_read(NVRAM, addr + 1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
m48t08_t *NVRAM = opaque;
|
||||
uint32_t retval = 0;
|
||||
|
||||
retval = m48t08_read(NVRAM, addr) << 24;
|
||||
retval |= m48t08_read(NVRAM, addr + 1) << 16;
|
||||
retval |= m48t08_read(NVRAM, addr + 2) << 8;
|
||||
retval |= m48t08_read(NVRAM, addr + 3);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc *nvram_write[] = {
|
||||
&nvram_writeb,
|
||||
&nvram_writew,
|
||||
&nvram_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *nvram_read[] = {
|
||||
&nvram_readb,
|
||||
&nvram_readw,
|
||||
&nvram_readl,
|
||||
};
|
||||
|
||||
static void nvram_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
m48t08_t *s = opaque;
|
||||
|
||||
qemu_put_be32s(f, (uint32_t *)&s->time_offset);
|
||||
qemu_put_be32s(f, (uint32_t *)&s->stop_time);
|
||||
qemu_put_buffer(f, s->buffer, 0x2000);
|
||||
}
|
||||
|
||||
static int nvram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
m48t08_t *s = opaque;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
qemu_get_be32s(f, (uint32_t *)&s->time_offset);
|
||||
qemu_get_be32s(f, (uint32_t *)&s->stop_time);
|
||||
qemu_get_buffer(f, s->buffer, 0x2000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void m48t08_reset(void *opaque)
|
||||
{
|
||||
m48t08_t *s = opaque;
|
||||
|
||||
s->time_offset = 0;
|
||||
s->stop_time = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Initialisation routine */
|
||||
m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size)
|
||||
{
|
||||
m48t08_t *s;
|
||||
int mem_index;
|
||||
|
||||
s = qemu_mallocz(sizeof(m48t08_t));
|
||||
if (!s)
|
||||
return NULL;
|
||||
s->buffer = qemu_mallocz(size);
|
||||
if (!s->buffer) {
|
||||
qemu_free(s);
|
||||
return NULL;
|
||||
}
|
||||
if (mem_base != 0) {
|
||||
mem_index = cpu_register_io_memory(0, nvram_read, nvram_write, s);
|
||||
cpu_register_physical_memory(mem_base, 0x2000, mem_index);
|
||||
}
|
||||
|
||||
register_savevm("nvram", mem_base, 1, nvram_save, nvram_load, s);
|
||||
qemu_register_reset(m48t08_reset, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
#if 0
|
||||
struct idprom
|
||||
{
|
||||
unsigned char id_format; /* Format identifier (always 0x01) */
|
||||
unsigned char id_machtype; /* Machine type */
|
||||
unsigned char id_ethaddr[6]; /* Hardware ethernet address */
|
||||
long id_date; /* Date of manufacture */
|
||||
unsigned int id_sernum:24; /* Unique serial number */
|
||||
unsigned char id_cksum; /* Checksum - xor of the data bytes */
|
||||
unsigned char reserved[16];
|
||||
};
|
||||
#endif
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#if !defined (__M48T08_H__)
|
||||
#define __M48T08_H__
|
||||
|
||||
typedef struct m48t08_t m48t08_t;
|
||||
|
||||
void m48t08_write (m48t08_t *NVRAM, uint32_t addr, uint8_t val);
|
||||
uint8_t m48t08_read (m48t08_t *NVRAM, uint32_t addr);
|
||||
m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size);
|
||||
|
||||
#endif /* !defined (__M48T08_H__) */
|
||||
+602
@@ -0,0 +1,602 @@
|
||||
/*
|
||||
* QEMU M48T59 NVRAM emulation for PPC PREP platform
|
||||
*
|
||||
* Copyright (c) 2003-2004 Jocelyn Mayer
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
#include "m48t59.h"
|
||||
|
||||
//#define DEBUG_NVRAM
|
||||
|
||||
#if defined(DEBUG_NVRAM)
|
||||
#define NVRAM_PRINTF(fmt, args...) do { printf(fmt , ##args); } while (0)
|
||||
#else
|
||||
#define NVRAM_PRINTF(fmt, args...) do { } while (0)
|
||||
#endif
|
||||
|
||||
struct m48t59_t {
|
||||
/* Hardware parameters */
|
||||
int IRQ;
|
||||
int mem_index;
|
||||
uint32_t mem_base;
|
||||
uint32_t io_base;
|
||||
uint16_t size;
|
||||
/* RTC management */
|
||||
time_t time_offset;
|
||||
time_t stop_time;
|
||||
/* Alarm & watchdog */
|
||||
time_t alarm;
|
||||
struct QEMUTimer *alrm_timer;
|
||||
struct QEMUTimer *wd_timer;
|
||||
/* NVRAM storage */
|
||||
uint8_t lock;
|
||||
uint16_t addr;
|
||||
uint8_t *buffer;
|
||||
};
|
||||
|
||||
/* Fake timer functions */
|
||||
/* Generic helpers for BCD */
|
||||
static inline uint8_t toBCD (uint8_t value)
|
||||
{
|
||||
return (((value / 10) % 10) << 4) | (value % 10);
|
||||
}
|
||||
|
||||
static inline uint8_t fromBCD (uint8_t BCD)
|
||||
{
|
||||
return ((BCD >> 4) * 10) + (BCD & 0x0F);
|
||||
}
|
||||
|
||||
/* RTC management helpers */
|
||||
static void get_time (m48t59_t *NVRAM, struct tm *tm)
|
||||
{
|
||||
time_t t;
|
||||
|
||||
t = time(NULL) + NVRAM->time_offset;
|
||||
#ifdef _WIN32
|
||||
memcpy(tm,localtime(&t),sizeof(*tm));
|
||||
#else
|
||||
localtime_r (&t, tm) ;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void set_time (m48t59_t *NVRAM, struct tm *tm)
|
||||
{
|
||||
time_t now, new_time;
|
||||
|
||||
new_time = mktime(tm);
|
||||
now = time(NULL);
|
||||
NVRAM->time_offset = new_time - now;
|
||||
}
|
||||
|
||||
/* Alarm management */
|
||||
static void alarm_cb (void *opaque)
|
||||
{
|
||||
struct tm tm, tm_now;
|
||||
uint64_t next_time;
|
||||
m48t59_t *NVRAM = opaque;
|
||||
|
||||
pic_set_irq(NVRAM->IRQ, 1);
|
||||
if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 &&
|
||||
(NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
|
||||
(NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
|
||||
(NVRAM->buffer[0x1FF2] & 0x80) == 0) {
|
||||
/* Repeat once a month */
|
||||
get_time(NVRAM, &tm_now);
|
||||
memcpy(&tm, &tm_now, sizeof(struct tm));
|
||||
tm.tm_mon++;
|
||||
if (tm.tm_mon == 13) {
|
||||
tm.tm_mon = 1;
|
||||
tm.tm_year++;
|
||||
}
|
||||
next_time = mktime(&tm);
|
||||
} else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
|
||||
(NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
|
||||
(NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
|
||||
(NVRAM->buffer[0x1FF2] & 0x80) == 0) {
|
||||
/* Repeat once a day */
|
||||
next_time = 24 * 60 * 60 + mktime(&tm_now);
|
||||
} else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
|
||||
(NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
|
||||
(NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
|
||||
(NVRAM->buffer[0x1FF2] & 0x80) == 0) {
|
||||
/* Repeat once an hour */
|
||||
next_time = 60 * 60 + mktime(&tm_now);
|
||||
} else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
|
||||
(NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
|
||||
(NVRAM->buffer[0x1FF3] & 0x80) != 0 &&
|
||||
(NVRAM->buffer[0x1FF2] & 0x80) == 0) {
|
||||
/* Repeat once a minute */
|
||||
next_time = 60 + mktime(&tm_now);
|
||||
} else {
|
||||
/* Repeat once a second */
|
||||
next_time = 1 + mktime(&tm_now);
|
||||
}
|
||||
qemu_mod_timer(NVRAM->alrm_timer, next_time * 1000);
|
||||
pic_set_irq(NVRAM->IRQ, 0);
|
||||
}
|
||||
|
||||
|
||||
static void get_alarm (m48t59_t *NVRAM, struct tm *tm)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
memcpy(tm,localtime(&NVRAM->alarm),sizeof(*tm));
|
||||
#else
|
||||
localtime_r (&NVRAM->alarm, tm);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void set_alarm (m48t59_t *NVRAM, struct tm *tm)
|
||||
{
|
||||
NVRAM->alarm = mktime(tm);
|
||||
if (NVRAM->alrm_timer != NULL) {
|
||||
qemu_del_timer(NVRAM->alrm_timer);
|
||||
NVRAM->alrm_timer = NULL;
|
||||
}
|
||||
if (NVRAM->alarm - time(NULL) > 0)
|
||||
qemu_mod_timer(NVRAM->alrm_timer, NVRAM->alarm * 1000);
|
||||
}
|
||||
|
||||
/* Watchdog management */
|
||||
static void watchdog_cb (void *opaque)
|
||||
{
|
||||
m48t59_t *NVRAM = opaque;
|
||||
|
||||
NVRAM->buffer[0x1FF0] |= 0x80;
|
||||
if (NVRAM->buffer[0x1FF7] & 0x80) {
|
||||
NVRAM->buffer[0x1FF7] = 0x00;
|
||||
NVRAM->buffer[0x1FFC] &= ~0x40;
|
||||
/* May it be a hw CPU Reset instead ? */
|
||||
qemu_system_reset_request();
|
||||
} else {
|
||||
pic_set_irq(NVRAM->IRQ, 1);
|
||||
pic_set_irq(NVRAM->IRQ, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_up_watchdog (m48t59_t *NVRAM, uint8_t value)
|
||||
{
|
||||
uint64_t interval; /* in 1/16 seconds */
|
||||
|
||||
if (NVRAM->wd_timer != NULL) {
|
||||
qemu_del_timer(NVRAM->wd_timer);
|
||||
NVRAM->wd_timer = NULL;
|
||||
}
|
||||
NVRAM->buffer[0x1FF0] &= ~0x80;
|
||||
if (value != 0) {
|
||||
interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F);
|
||||
qemu_mod_timer(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) +
|
||||
((interval * 1000) >> 4));
|
||||
}
|
||||
}
|
||||
|
||||
/* Direct access to NVRAM */
|
||||
void m48t59_write (m48t59_t *NVRAM, uint32_t val)
|
||||
{
|
||||
struct tm tm;
|
||||
int tmp;
|
||||
|
||||
if (NVRAM->addr > 0x1FF8 && NVRAM->addr < 0x2000)
|
||||
NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, NVRAM->addr, val);
|
||||
switch (NVRAM->addr) {
|
||||
case 0x1FF0:
|
||||
/* flags register : read-only */
|
||||
break;
|
||||
case 0x1FF1:
|
||||
/* unused */
|
||||
break;
|
||||
case 0x1FF2:
|
||||
/* alarm seconds */
|
||||
tmp = fromBCD(val & 0x7F);
|
||||
if (tmp >= 0 && tmp <= 59) {
|
||||
get_alarm(NVRAM, &tm);
|
||||
tm.tm_sec = tmp;
|
||||
NVRAM->buffer[0x1FF2] = val;
|
||||
set_alarm(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
case 0x1FF3:
|
||||
/* alarm minutes */
|
||||
tmp = fromBCD(val & 0x7F);
|
||||
if (tmp >= 0 && tmp <= 59) {
|
||||
get_alarm(NVRAM, &tm);
|
||||
tm.tm_min = tmp;
|
||||
NVRAM->buffer[0x1FF3] = val;
|
||||
set_alarm(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
case 0x1FF4:
|
||||
/* alarm hours */
|
||||
tmp = fromBCD(val & 0x3F);
|
||||
if (tmp >= 0 && tmp <= 23) {
|
||||
get_alarm(NVRAM, &tm);
|
||||
tm.tm_hour = tmp;
|
||||
NVRAM->buffer[0x1FF4] = val;
|
||||
set_alarm(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
case 0x1FF5:
|
||||
/* alarm date */
|
||||
tmp = fromBCD(val & 0x1F);
|
||||
if (tmp != 0) {
|
||||
get_alarm(NVRAM, &tm);
|
||||
tm.tm_mday = tmp;
|
||||
NVRAM->buffer[0x1FF5] = val;
|
||||
set_alarm(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
case 0x1FF6:
|
||||
/* interrupts */
|
||||
NVRAM->buffer[0x1FF6] = val;
|
||||
break;
|
||||
case 0x1FF7:
|
||||
/* watchdog */
|
||||
NVRAM->buffer[0x1FF7] = val;
|
||||
set_up_watchdog(NVRAM, val);
|
||||
break;
|
||||
case 0x1FF8:
|
||||
/* control */
|
||||
NVRAM->buffer[0x1FF8] = (val & ~0xA0) | 0x90;
|
||||
break;
|
||||
case 0x1FF9:
|
||||
/* seconds (BCD) */
|
||||
tmp = fromBCD(val & 0x7F);
|
||||
if (tmp >= 0 && tmp <= 59) {
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_sec = tmp;
|
||||
set_time(NVRAM, &tm);
|
||||
}
|
||||
if ((val & 0x80) ^ (NVRAM->buffer[0x1FF9] & 0x80)) {
|
||||
if (val & 0x80) {
|
||||
NVRAM->stop_time = time(NULL);
|
||||
} else {
|
||||
NVRAM->time_offset += NVRAM->stop_time - time(NULL);
|
||||
NVRAM->stop_time = 0;
|
||||
}
|
||||
}
|
||||
NVRAM->buffer[0x1FF9] = val & 0x80;
|
||||
break;
|
||||
case 0x1FFA:
|
||||
/* minutes (BCD) */
|
||||
tmp = fromBCD(val & 0x7F);
|
||||
if (tmp >= 0 && tmp <= 59) {
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_min = tmp;
|
||||
set_time(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
case 0x1FFB:
|
||||
/* hours (BCD) */
|
||||
tmp = fromBCD(val & 0x3F);
|
||||
if (tmp >= 0 && tmp <= 23) {
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_hour = tmp;
|
||||
set_time(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
case 0x1FFC:
|
||||
/* day of the week / century */
|
||||
tmp = fromBCD(val & 0x07);
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_wday = tmp;
|
||||
set_time(NVRAM, &tm);
|
||||
NVRAM->buffer[0x1FFC] = val & 0x40;
|
||||
break;
|
||||
case 0x1FFD:
|
||||
/* date */
|
||||
tmp = fromBCD(val & 0x1F);
|
||||
if (tmp != 0) {
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_mday = tmp;
|
||||
set_time(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
case 0x1FFE:
|
||||
/* month */
|
||||
tmp = fromBCD(val & 0x1F);
|
||||
if (tmp >= 1 && tmp <= 12) {
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_mon = tmp - 1;
|
||||
set_time(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
case 0x1FFF:
|
||||
/* year */
|
||||
tmp = fromBCD(val);
|
||||
if (tmp >= 0 && tmp <= 99) {
|
||||
get_time(NVRAM, &tm);
|
||||
tm.tm_year = fromBCD(val);
|
||||
set_time(NVRAM, &tm);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Check lock registers state */
|
||||
if (NVRAM->addr >= 0x20 && NVRAM->addr <= 0x2F && (NVRAM->lock & 1))
|
||||
break;
|
||||
if (NVRAM->addr >= 0x30 && NVRAM->addr <= 0x3F && (NVRAM->lock & 2))
|
||||
break;
|
||||
if (NVRAM->addr < 0x1FF0 ||
|
||||
(NVRAM->addr > 0x1FFF && NVRAM->addr < NVRAM->size)) {
|
||||
NVRAM->buffer[NVRAM->addr] = val & 0xFF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t m48t59_read (m48t59_t *NVRAM)
|
||||
{
|
||||
struct tm tm;
|
||||
uint32_t retval = 0xFF;
|
||||
|
||||
switch (NVRAM->addr) {
|
||||
case 0x1FF0:
|
||||
/* flags register */
|
||||
goto do_read;
|
||||
case 0x1FF1:
|
||||
/* unused */
|
||||
retval = 0;
|
||||
break;
|
||||
case 0x1FF2:
|
||||
/* alarm seconds */
|
||||
goto do_read;
|
||||
case 0x1FF3:
|
||||
/* alarm minutes */
|
||||
goto do_read;
|
||||
case 0x1FF4:
|
||||
/* alarm hours */
|
||||
goto do_read;
|
||||
case 0x1FF5:
|
||||
/* alarm date */
|
||||
goto do_read;
|
||||
case 0x1FF6:
|
||||
/* interrupts */
|
||||
goto do_read;
|
||||
case 0x1FF7:
|
||||
/* A read resets the watchdog */
|
||||
set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]);
|
||||
goto do_read;
|
||||
case 0x1FF8:
|
||||
/* control */
|
||||
goto do_read;
|
||||
case 0x1FF9:
|
||||
/* seconds (BCD) */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = (NVRAM->buffer[0x1FF9] & 0x80) | toBCD(tm.tm_sec);
|
||||
break;
|
||||
case 0x1FFA:
|
||||
/* minutes (BCD) */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = toBCD(tm.tm_min);
|
||||
break;
|
||||
case 0x1FFB:
|
||||
/* hours (BCD) */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = toBCD(tm.tm_hour);
|
||||
break;
|
||||
case 0x1FFC:
|
||||
/* day of the week / century */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = NVRAM->buffer[0x1FFC] | tm.tm_wday;
|
||||
break;
|
||||
case 0x1FFD:
|
||||
/* date */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = toBCD(tm.tm_mday);
|
||||
break;
|
||||
case 0x1FFE:
|
||||
/* month */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = toBCD(tm.tm_mon + 1);
|
||||
break;
|
||||
case 0x1FFF:
|
||||
/* year */
|
||||
get_time(NVRAM, &tm);
|
||||
retval = toBCD(tm.tm_year);
|
||||
break;
|
||||
default:
|
||||
/* Check lock registers state */
|
||||
if (NVRAM->addr >= 0x20 && NVRAM->addr <= 0x2F && (NVRAM->lock & 1))
|
||||
break;
|
||||
if (NVRAM->addr >= 0x30 && NVRAM->addr <= 0x3F && (NVRAM->lock & 2))
|
||||
break;
|
||||
if (NVRAM->addr < 0x1FF0 ||
|
||||
(NVRAM->addr > 0x1FFF && NVRAM->addr < NVRAM->size)) {
|
||||
do_read:
|
||||
retval = NVRAM->buffer[NVRAM->addr];
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (NVRAM->addr > 0x1FF9 && NVRAM->addr < 0x2000)
|
||||
NVRAM_PRINTF("0x%08x <= 0x%08x\n", NVRAM->addr, retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void m48t59_set_addr (m48t59_t *NVRAM, uint32_t addr)
|
||||
{
|
||||
NVRAM->addr = addr;
|
||||
}
|
||||
|
||||
void m48t59_toggle_lock (m48t59_t *NVRAM, int lock)
|
||||
{
|
||||
NVRAM->lock ^= 1 << lock;
|
||||
}
|
||||
|
||||
/* IO access to NVRAM */
|
||||
static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
m48t59_t *NVRAM = opaque;
|
||||
|
||||
addr -= NVRAM->io_base;
|
||||
NVRAM_PRINTF("0x%08x => 0x%08x\n", addr, val);
|
||||
switch (addr) {
|
||||
case 0:
|
||||
NVRAM->addr &= ~0x00FF;
|
||||
NVRAM->addr |= val;
|
||||
break;
|
||||
case 1:
|
||||
NVRAM->addr &= ~0xFF00;
|
||||
NVRAM->addr |= val << 8;
|
||||
break;
|
||||
case 3:
|
||||
m48t59_write(NVRAM, val);
|
||||
NVRAM->addr = 0x0000;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t NVRAM_readb (void *opaque, uint32_t addr)
|
||||
{
|
||||
m48t59_t *NVRAM = opaque;
|
||||
uint32_t retval;
|
||||
|
||||
addr -= NVRAM->io_base;
|
||||
switch (addr) {
|
||||
case 3:
|
||||
retval = m48t59_read(NVRAM);
|
||||
break;
|
||||
default:
|
||||
retval = -1;
|
||||
break;
|
||||
}
|
||||
NVRAM_PRINTF("0x%08x <= 0x%08x\n", addr, retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
m48t59_t *NVRAM = opaque;
|
||||
|
||||
addr -= NVRAM->mem_base;
|
||||
if (addr < 0x1FF0)
|
||||
NVRAM->buffer[addr] = value;
|
||||
}
|
||||
|
||||
static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
m48t59_t *NVRAM = opaque;
|
||||
|
||||
addr -= NVRAM->mem_base;
|
||||
if (addr < 0x1FF0) {
|
||||
NVRAM->buffer[addr] = value >> 8;
|
||||
NVRAM->buffer[addr + 1] = value;
|
||||
}
|
||||
}
|
||||
|
||||
static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
m48t59_t *NVRAM = opaque;
|
||||
|
||||
addr -= NVRAM->mem_base;
|
||||
if (addr < 0x1FF0) {
|
||||
NVRAM->buffer[addr] = value >> 24;
|
||||
NVRAM->buffer[addr + 1] = value >> 16;
|
||||
NVRAM->buffer[addr + 2] = value >> 8;
|
||||
NVRAM->buffer[addr + 3] = value;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
m48t59_t *NVRAM = opaque;
|
||||
uint32_t retval = 0;
|
||||
|
||||
addr -= NVRAM->mem_base;
|
||||
if (addr < 0x1FF0)
|
||||
retval = NVRAM->buffer[addr];
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
m48t59_t *NVRAM = opaque;
|
||||
uint32_t retval = 0;
|
||||
|
||||
addr -= NVRAM->mem_base;
|
||||
if (addr < 0x1FF0) {
|
||||
retval = NVRAM->buffer[addr] << 8;
|
||||
retval |= NVRAM->buffer[addr + 1];
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
m48t59_t *NVRAM = opaque;
|
||||
uint32_t retval = 0;
|
||||
|
||||
addr -= NVRAM->mem_base;
|
||||
if (addr < 0x1FF0) {
|
||||
retval = NVRAM->buffer[addr] << 24;
|
||||
retval |= NVRAM->buffer[addr + 1] << 16;
|
||||
retval |= NVRAM->buffer[addr + 2] << 8;
|
||||
retval |= NVRAM->buffer[addr + 3];
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc *nvram_write[] = {
|
||||
&nvram_writeb,
|
||||
&nvram_writew,
|
||||
&nvram_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *nvram_read[] = {
|
||||
&nvram_readb,
|
||||
&nvram_readw,
|
||||
&nvram_readl,
|
||||
};
|
||||
/* Initialisation routine */
|
||||
m48t59_t *m48t59_init (int IRQ, uint32_t mem_base,
|
||||
uint32_t io_base, uint16_t size)
|
||||
{
|
||||
m48t59_t *s;
|
||||
|
||||
s = qemu_mallocz(sizeof(m48t59_t));
|
||||
if (!s)
|
||||
return NULL;
|
||||
s->buffer = qemu_mallocz(size);
|
||||
if (!s->buffer) {
|
||||
qemu_free(s);
|
||||
return NULL;
|
||||
}
|
||||
s->IRQ = IRQ;
|
||||
s->size = size;
|
||||
s->mem_base = mem_base;
|
||||
s->io_base = io_base;
|
||||
s->addr = 0;
|
||||
register_ioport_read(io_base, 0x04, 1, NVRAM_readb, s);
|
||||
register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, s);
|
||||
if (mem_base != 0) {
|
||||
s->mem_index = cpu_register_io_memory(0, nvram_read, nvram_write, s);
|
||||
cpu_register_physical_memory(mem_base, 0x4000, s->mem_index);
|
||||
}
|
||||
s->alrm_timer = qemu_new_timer(vm_clock, &alarm_cb, s);
|
||||
s->wd_timer = qemu_new_timer(vm_clock, &watchdog_cb, s);
|
||||
s->lock = 0;
|
||||
|
||||
return s;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
#if !defined (__M48T59_H__)
|
||||
#define __M48T59_H__
|
||||
|
||||
typedef struct m48t59_t m48t59_t;
|
||||
|
||||
void m48t59_write (m48t59_t *NVRAM, uint32_t val);
|
||||
uint32_t m48t59_read (m48t59_t *NVRAM);
|
||||
void m48t59_set_addr (m48t59_t *NVRAM, uint32_t addr);
|
||||
void m48t59_toggle_lock (m48t59_t *NVRAM, int lock);
|
||||
m48t59_t *m48t59_init (int IRQ, uint32_t io_base,
|
||||
uint32_t mem_base, uint16_t size);
|
||||
|
||||
#endif /* !defined (__M48T59_H__) */
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
#include "vl.h"
|
||||
#include "disas.h"
|
||||
#include "exec-all.h"
|
||||
|
||||
struct exec
|
||||
{
|
||||
uint32_t a_info; /* Use macros N_MAGIC, etc for access */
|
||||
uint32_t a_text; /* length of text, in bytes */
|
||||
uint32_t a_data; /* length of data, in bytes */
|
||||
uint32_t a_bss; /* length of uninitialized data area, in bytes */
|
||||
uint32_t a_syms; /* length of symbol table data in file, in bytes */
|
||||
uint32_t a_entry; /* start address */
|
||||
uint32_t a_trsize; /* length of relocation info for text, in bytes */
|
||||
uint32_t a_drsize; /* length of relocation info for data, in bytes */
|
||||
};
|
||||
|
||||
#ifdef BSWAP_NEEDED
|
||||
static void bswap_ahdr(struct exec *e)
|
||||
{
|
||||
bswap32s(&e->a_info);
|
||||
bswap32s(&e->a_text);
|
||||
bswap32s(&e->a_data);
|
||||
bswap32s(&e->a_bss);
|
||||
bswap32s(&e->a_syms);
|
||||
bswap32s(&e->a_entry);
|
||||
bswap32s(&e->a_trsize);
|
||||
bswap32s(&e->a_drsize);
|
||||
}
|
||||
#else
|
||||
#define bswap_ahdr(x) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define N_MAGIC(exec) ((exec).a_info & 0xffff)
|
||||
#define OMAGIC 0407
|
||||
#define NMAGIC 0410
|
||||
#define ZMAGIC 0413
|
||||
#define QMAGIC 0314
|
||||
#define _N_HDROFF(x) (1024 - sizeof (struct exec))
|
||||
#define N_TXTOFF(x) \
|
||||
(N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \
|
||||
(N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec)))
|
||||
#define N_TXTADDR(x) (N_MAGIC(x) == QMAGIC ? TARGET_PAGE_SIZE : 0)
|
||||
#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text)
|
||||
#define _N_SEGMENT_ROUND(x) (((x) + TARGET_PAGE_SIZE - 1) & ~(TARGET_PAGE_SIZE - 1))
|
||||
|
||||
#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text)
|
||||
|
||||
#define N_DATADDR(x) \
|
||||
(N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \
|
||||
: (_N_SEGMENT_ROUND (_N_TXTENDADDR(x))))
|
||||
|
||||
|
||||
#define ELF_CLASS ELFCLASS32
|
||||
#define ELF_DATA ELFDATA2MSB
|
||||
#define ELF_ARCH EM_SPARC
|
||||
|
||||
#include "elf.h"
|
||||
|
||||
#ifdef BSWAP_NEEDED
|
||||
static void bswap_ehdr(Elf32_Ehdr *ehdr)
|
||||
{
|
||||
bswap16s(&ehdr->e_type); /* Object file type */
|
||||
bswap16s(&ehdr->e_machine); /* Architecture */
|
||||
bswap32s(&ehdr->e_version); /* Object file version */
|
||||
bswap32s(&ehdr->e_entry); /* Entry point virtual address */
|
||||
bswap32s(&ehdr->e_phoff); /* Program header table file offset */
|
||||
bswap32s(&ehdr->e_shoff); /* Section header table file offset */
|
||||
bswap32s(&ehdr->e_flags); /* Processor-specific flags */
|
||||
bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */
|
||||
bswap16s(&ehdr->e_phentsize); /* Program header table entry size */
|
||||
bswap16s(&ehdr->e_phnum); /* Program header table entry count */
|
||||
bswap16s(&ehdr->e_shentsize); /* Section header table entry size */
|
||||
bswap16s(&ehdr->e_shnum); /* Section header table entry count */
|
||||
bswap16s(&ehdr->e_shstrndx); /* Section header string table index */
|
||||
}
|
||||
|
||||
static void bswap_phdr(Elf32_Phdr *phdr)
|
||||
{
|
||||
bswap32s(&phdr->p_type); /* Segment type */
|
||||
bswap32s(&phdr->p_offset); /* Segment file offset */
|
||||
bswap32s(&phdr->p_vaddr); /* Segment virtual address */
|
||||
bswap32s(&phdr->p_paddr); /* Segment physical address */
|
||||
bswap32s(&phdr->p_filesz); /* Segment size in file */
|
||||
bswap32s(&phdr->p_memsz); /* Segment size in memory */
|
||||
bswap32s(&phdr->p_flags); /* Segment flags */
|
||||
bswap32s(&phdr->p_align); /* Segment alignment */
|
||||
}
|
||||
|
||||
static void bswap_shdr(Elf32_Shdr *shdr)
|
||||
{
|
||||
bswap32s(&shdr->sh_name);
|
||||
bswap32s(&shdr->sh_type);
|
||||
bswap32s(&shdr->sh_flags);
|
||||
bswap32s(&shdr->sh_addr);
|
||||
bswap32s(&shdr->sh_offset);
|
||||
bswap32s(&shdr->sh_size);
|
||||
bswap32s(&shdr->sh_link);
|
||||
bswap32s(&shdr->sh_info);
|
||||
bswap32s(&shdr->sh_addralign);
|
||||
bswap32s(&shdr->sh_entsize);
|
||||
}
|
||||
|
||||
static void bswap_sym(Elf32_Sym *sym)
|
||||
{
|
||||
bswap32s(&sym->st_name);
|
||||
bswap32s(&sym->st_value);
|
||||
bswap32s(&sym->st_size);
|
||||
bswap16s(&sym->st_shndx);
|
||||
}
|
||||
#else
|
||||
#define bswap_ehdr(e) do { } while (0)
|
||||
#define bswap_phdr(e) do { } while (0)
|
||||
#define bswap_shdr(e) do { } while (0)
|
||||
#define bswap_sym(e) do { } while (0)
|
||||
#endif
|
||||
|
||||
static int find_phdr(struct elfhdr *ehdr, int fd, struct elf_phdr *phdr, uint32_t type)
|
||||
{
|
||||
int i, retval;
|
||||
|
||||
retval = lseek(fd, ehdr->e_phoff, SEEK_SET);
|
||||
if (retval < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
retval = read(fd, phdr, sizeof(*phdr));
|
||||
if (retval < 0)
|
||||
return -1;
|
||||
bswap_phdr(phdr);
|
||||
if (phdr->p_type == type)
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *find_shdr(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, uint32_t type)
|
||||
{
|
||||
int i, retval;
|
||||
|
||||
retval = lseek(fd, ehdr->e_shoff, SEEK_SET);
|
||||
if (retval < 0)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < ehdr->e_shnum; i++) {
|
||||
retval = read(fd, shdr, sizeof(*shdr));
|
||||
if (retval < 0)
|
||||
return NULL;
|
||||
bswap_shdr(shdr);
|
||||
if (shdr->sh_type == type)
|
||||
return qemu_malloc(shdr->sh_size);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *find_strtab(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = lseek(fd, ehdr->e_shoff + sizeof(struct elf_shdr) * symtab->sh_link, SEEK_SET);
|
||||
if (retval < 0)
|
||||
return NULL;
|
||||
|
||||
retval = read(fd, shdr, sizeof(*shdr));
|
||||
if (retval < 0)
|
||||
return NULL;
|
||||
bswap_shdr(shdr);
|
||||
if (shdr->sh_type == SHT_STRTAB)
|
||||
return qemu_malloc(shdr->sh_size);;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int read_program(int fd, struct elf_phdr *phdr, void *dst, uint32_t entry)
|
||||
{
|
||||
int retval;
|
||||
retval = lseek(fd, phdr->p_offset + entry - phdr->p_vaddr, SEEK_SET);
|
||||
if (retval < 0)
|
||||
return -1;
|
||||
return read(fd, dst, phdr->p_filesz);
|
||||
}
|
||||
|
||||
static int read_section(int fd, struct elf_shdr *s, void *dst)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = lseek(fd, s->sh_offset, SEEK_SET);
|
||||
if (retval < 0)
|
||||
return -1;
|
||||
retval = read(fd, dst, s->sh_size);
|
||||
if (retval < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *process_section(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, uint32_t type)
|
||||
{
|
||||
void *dst;
|
||||
|
||||
dst = find_shdr(ehdr, fd, shdr, type);
|
||||
if (!dst)
|
||||
goto error;
|
||||
|
||||
if (read_section(fd, shdr, dst))
|
||||
goto error;
|
||||
return dst;
|
||||
error:
|
||||
qemu_free(dst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *process_strtab(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab)
|
||||
{
|
||||
void *dst;
|
||||
|
||||
dst = find_strtab(ehdr, fd, shdr, symtab);
|
||||
if (!dst)
|
||||
goto error;
|
||||
|
||||
if (read_section(fd, shdr, dst))
|
||||
goto error;
|
||||
return dst;
|
||||
error:
|
||||
qemu_free(dst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void load_symbols(struct elfhdr *ehdr, int fd)
|
||||
{
|
||||
struct elf_shdr symtab, strtab;
|
||||
struct elf_sym *syms;
|
||||
struct syminfo *s;
|
||||
int nsyms, i;
|
||||
char *str;
|
||||
|
||||
/* Symbol table */
|
||||
syms = process_section(ehdr, fd, &symtab, SHT_SYMTAB);
|
||||
if (!syms)
|
||||
return;
|
||||
|
||||
nsyms = symtab.sh_size / sizeof(struct elf_sym);
|
||||
for (i = 0; i < nsyms; i++)
|
||||
bswap_sym(&syms[i]);
|
||||
|
||||
/* String table */
|
||||
str = process_strtab(ehdr, fd, &strtab, &symtab);
|
||||
if (!str)
|
||||
goto error_freesyms;
|
||||
|
||||
/* Commit */
|
||||
s = qemu_mallocz(sizeof(*s));
|
||||
s->disas_symtab = syms;
|
||||
s->disas_num_syms = nsyms;
|
||||
s->disas_strtab = str;
|
||||
s->next = syminfos;
|
||||
syminfos = s;
|
||||
return;
|
||||
error_freesyms:
|
||||
qemu_free(syms);
|
||||
return;
|
||||
}
|
||||
|
||||
int load_elf(const char *filename, uint8_t *addr)
|
||||
{
|
||||
struct elfhdr ehdr;
|
||||
struct elf_phdr phdr;
|
||||
int retval, fd;
|
||||
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
goto error;
|
||||
|
||||
retval = read(fd, &ehdr, sizeof(ehdr));
|
||||
if (retval < 0)
|
||||
goto error;
|
||||
|
||||
bswap_ehdr(&ehdr);
|
||||
|
||||
if (ehdr.e_ident[0] != 0x7f || ehdr.e_ident[1] != 'E'
|
||||
|| ehdr.e_ident[2] != 'L' || ehdr.e_ident[3] != 'F'
|
||||
|| (ehdr.e_machine != EM_SPARC
|
||||
&& ehdr.e_machine != EM_SPARC32PLUS))
|
||||
goto error;
|
||||
|
||||
if (find_phdr(&ehdr, fd, &phdr, PT_LOAD))
|
||||
goto error;
|
||||
retval = read_program(fd, &phdr, addr, ehdr.e_entry);
|
||||
if (retval < 0)
|
||||
goto error;
|
||||
|
||||
load_symbols(&ehdr, fd);
|
||||
|
||||
close(fd);
|
||||
return retval;
|
||||
error:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int load_aout(const char *filename, uint8_t *addr)
|
||||
{
|
||||
int fd, size, ret;
|
||||
struct exec e;
|
||||
uint32_t magic;
|
||||
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
size = read(fd, &e, sizeof(e));
|
||||
if (size < 0)
|
||||
goto fail;
|
||||
|
||||
bswap_ahdr(&e);
|
||||
|
||||
magic = N_MAGIC(e);
|
||||
switch (magic) {
|
||||
case ZMAGIC:
|
||||
case QMAGIC:
|
||||
case OMAGIC:
|
||||
lseek(fd, N_TXTOFF(e), SEEK_SET);
|
||||
size = read(fd, addr, e.a_text + e.a_data);
|
||||
if (size < 0)
|
||||
goto fail;
|
||||
break;
|
||||
case NMAGIC:
|
||||
lseek(fd, N_TXTOFF(e), SEEK_SET);
|
||||
size = read(fd, addr, e.a_text);
|
||||
if (size < 0)
|
||||
goto fail;
|
||||
ret = read(fd, addr + N_DATADDR(e), e.a_data);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
size += ret;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
close(fd);
|
||||
return size;
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* QEMU MC146818 RTC emulation
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_CMOS
|
||||
|
||||
#define RTC_SECONDS 0
|
||||
#define RTC_SECONDS_ALARM 1
|
||||
#define RTC_MINUTES 2
|
||||
#define RTC_MINUTES_ALARM 3
|
||||
#define RTC_HOURS 4
|
||||
#define RTC_HOURS_ALARM 5
|
||||
#define RTC_ALARM_DONT_CARE 0xC0
|
||||
|
||||
#define RTC_DAY_OF_WEEK 6
|
||||
#define RTC_DAY_OF_MONTH 7
|
||||
#define RTC_MONTH 8
|
||||
#define RTC_YEAR 9
|
||||
|
||||
#define RTC_REG_A 10
|
||||
#define RTC_REG_B 11
|
||||
#define RTC_REG_C 12
|
||||
#define RTC_REG_D 13
|
||||
|
||||
#define REG_A_UIP 0x80
|
||||
|
||||
#define REG_B_SET 0x80
|
||||
#define REG_B_PIE 0x40
|
||||
#define REG_B_AIE 0x20
|
||||
#define REG_B_UIE 0x10
|
||||
|
||||
struct RTCState {
|
||||
uint8_t cmos_data[128];
|
||||
uint8_t cmos_index;
|
||||
struct tm current_tm;
|
||||
int irq;
|
||||
/* periodic timer */
|
||||
QEMUTimer *periodic_timer;
|
||||
int64_t next_periodic_time;
|
||||
/* second update */
|
||||
int64_t next_second_time;
|
||||
QEMUTimer *second_timer;
|
||||
QEMUTimer *second_timer2;
|
||||
};
|
||||
|
||||
static void rtc_set_time(RTCState *s);
|
||||
static void rtc_copy_date(RTCState *s);
|
||||
|
||||
static void rtc_timer_update(RTCState *s, int64_t current_time)
|
||||
{
|
||||
int period_code, period;
|
||||
int64_t cur_clock, next_irq_clock;
|
||||
|
||||
period_code = s->cmos_data[RTC_REG_A] & 0x0f;
|
||||
if (period_code != 0 &&
|
||||
(s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
|
||||
if (period_code <= 2)
|
||||
period_code += 7;
|
||||
/* period in 32 Khz cycles */
|
||||
period = 1 << (period_code - 1);
|
||||
/* compute 32 khz clock */
|
||||
cur_clock = muldiv64(current_time, 32768, ticks_per_sec);
|
||||
next_irq_clock = (cur_clock & ~(period - 1)) + period;
|
||||
s->next_periodic_time = muldiv64(next_irq_clock, ticks_per_sec, 32768) + 1;
|
||||
qemu_mod_timer(s->periodic_timer, s->next_periodic_time);
|
||||
} else {
|
||||
qemu_del_timer(s->periodic_timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void rtc_periodic_timer(void *opaque)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
|
||||
rtc_timer_update(s, s->next_periodic_time);
|
||||
s->cmos_data[RTC_REG_C] |= 0xc0;
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
|
||||
static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
|
||||
if ((addr & 1) == 0) {
|
||||
s->cmos_index = data & 0x7f;
|
||||
} else {
|
||||
#ifdef DEBUG_CMOS
|
||||
printf("cmos: write index=0x%02x val=0x%02x\n",
|
||||
s->cmos_index, data);
|
||||
#endif
|
||||
switch(s->cmos_index) {
|
||||
case RTC_SECONDS_ALARM:
|
||||
case RTC_MINUTES_ALARM:
|
||||
case RTC_HOURS_ALARM:
|
||||
/* XXX: not supported */
|
||||
s->cmos_data[s->cmos_index] = data;
|
||||
break;
|
||||
case RTC_SECONDS:
|
||||
case RTC_MINUTES:
|
||||
case RTC_HOURS:
|
||||
case RTC_DAY_OF_WEEK:
|
||||
case RTC_DAY_OF_MONTH:
|
||||
case RTC_MONTH:
|
||||
case RTC_YEAR:
|
||||
s->cmos_data[s->cmos_index] = data;
|
||||
/* if in set mode, do not update the time */
|
||||
if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
|
||||
rtc_set_time(s);
|
||||
}
|
||||
break;
|
||||
case RTC_REG_A:
|
||||
/* UIP bit is read only */
|
||||
s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
|
||||
(s->cmos_data[RTC_REG_A] & REG_A_UIP);
|
||||
rtc_timer_update(s, qemu_get_clock(vm_clock));
|
||||
break;
|
||||
case RTC_REG_B:
|
||||
if (data & REG_B_SET) {
|
||||
/* set mode: reset UIP mode */
|
||||
s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
|
||||
data &= ~REG_B_UIE;
|
||||
} else {
|
||||
/* if disabling set mode, update the time */
|
||||
if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
|
||||
rtc_set_time(s);
|
||||
}
|
||||
}
|
||||
s->cmos_data[RTC_REG_B] = data;
|
||||
rtc_timer_update(s, qemu_get_clock(vm_clock));
|
||||
break;
|
||||
case RTC_REG_C:
|
||||
case RTC_REG_D:
|
||||
/* cannot write to them */
|
||||
break;
|
||||
default:
|
||||
s->cmos_data[s->cmos_index] = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline int to_bcd(RTCState *s, int a)
|
||||
{
|
||||
if (s->cmos_data[RTC_REG_B] & 0x04) {
|
||||
return a;
|
||||
} else {
|
||||
return ((a / 10) << 4) | (a % 10);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int from_bcd(RTCState *s, int a)
|
||||
{
|
||||
if (s->cmos_data[RTC_REG_B] & 0x04) {
|
||||
return a;
|
||||
} else {
|
||||
return ((a >> 4) * 10) + (a & 0x0f);
|
||||
}
|
||||
}
|
||||
|
||||
static void rtc_set_time(RTCState *s)
|
||||
{
|
||||
struct tm *tm = &s->current_tm;
|
||||
|
||||
tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
|
||||
tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]);
|
||||
tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
|
||||
if (!(s->cmos_data[RTC_REG_B] & 0x02) &&
|
||||
(s->cmos_data[RTC_HOURS] & 0x80)) {
|
||||
tm->tm_hour += 12;
|
||||
}
|
||||
tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
|
||||
tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
|
||||
tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
|
||||
tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100;
|
||||
}
|
||||
|
||||
static void rtc_copy_date(RTCState *s)
|
||||
{
|
||||
const struct tm *tm = &s->current_tm;
|
||||
|
||||
s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
|
||||
s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
|
||||
if (s->cmos_data[RTC_REG_B] & 0x02) {
|
||||
/* 24 hour format */
|
||||
s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
|
||||
} else {
|
||||
/* 12 hour format */
|
||||
s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);
|
||||
if (tm->tm_hour >= 12)
|
||||
s->cmos_data[RTC_HOURS] |= 0x80;
|
||||
}
|
||||
s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);
|
||||
s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);
|
||||
s->cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);
|
||||
s->cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);
|
||||
}
|
||||
|
||||
/* month is between 0 and 11. */
|
||||
static int get_days_in_month(int month, int year)
|
||||
{
|
||||
static const int days_tab[12] = {
|
||||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||||
};
|
||||
int d;
|
||||
if ((unsigned )month >= 12)
|
||||
return 31;
|
||||
d = days_tab[month];
|
||||
if (month == 1) {
|
||||
if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
|
||||
d++;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/* update 'tm' to the next second */
|
||||
static void rtc_next_second(struct tm *tm)
|
||||
{
|
||||
int days_in_month;
|
||||
|
||||
tm->tm_sec++;
|
||||
if ((unsigned)tm->tm_sec >= 60) {
|
||||
tm->tm_sec = 0;
|
||||
tm->tm_min++;
|
||||
if ((unsigned)tm->tm_min >= 60) {
|
||||
tm->tm_min = 0;
|
||||
tm->tm_hour++;
|
||||
if ((unsigned)tm->tm_hour >= 24) {
|
||||
tm->tm_hour = 0;
|
||||
/* next day */
|
||||
tm->tm_wday++;
|
||||
if ((unsigned)tm->tm_wday >= 7)
|
||||
tm->tm_wday = 0;
|
||||
days_in_month = get_days_in_month(tm->tm_mon,
|
||||
tm->tm_year + 1900);
|
||||
tm->tm_mday++;
|
||||
if (tm->tm_mday < 1) {
|
||||
tm->tm_mday = 1;
|
||||
} else if (tm->tm_mday > days_in_month) {
|
||||
tm->tm_mday = 1;
|
||||
tm->tm_mon++;
|
||||
if (tm->tm_mon >= 12) {
|
||||
tm->tm_mon = 0;
|
||||
tm->tm_year++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void rtc_update_second(void *opaque)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
int64_t delay;
|
||||
|
||||
/* if the oscillator is not in normal operation, we do not update */
|
||||
if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
|
||||
s->next_second_time += ticks_per_sec;
|
||||
qemu_mod_timer(s->second_timer, s->next_second_time);
|
||||
} else {
|
||||
rtc_next_second(&s->current_tm);
|
||||
|
||||
if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
|
||||
/* update in progress bit */
|
||||
s->cmos_data[RTC_REG_A] |= REG_A_UIP;
|
||||
}
|
||||
/* should be 244 us = 8 / 32768 seconds, but currently the
|
||||
timers do not have the necessary resolution. */
|
||||
delay = (ticks_per_sec * 1) / 100;
|
||||
if (delay < 1)
|
||||
delay = 1;
|
||||
qemu_mod_timer(s->second_timer2,
|
||||
s->next_second_time + delay);
|
||||
}
|
||||
}
|
||||
|
||||
static void rtc_update_second2(void *opaque)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
|
||||
if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
|
||||
rtc_copy_date(s);
|
||||
}
|
||||
|
||||
/* check alarm */
|
||||
if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
|
||||
if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
|
||||
s->cmos_data[RTC_SECONDS_ALARM] == s->current_tm.tm_sec) &&
|
||||
((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
|
||||
s->cmos_data[RTC_MINUTES_ALARM] == s->current_tm.tm_mon) &&
|
||||
((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
|
||||
s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) {
|
||||
|
||||
s->cmos_data[RTC_REG_C] |= 0xa0;
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* update ended interrupt */
|
||||
if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
|
||||
s->cmos_data[RTC_REG_C] |= 0x90;
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
|
||||
/* clear update in progress bit */
|
||||
s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
|
||||
|
||||
s->next_second_time += ticks_per_sec;
|
||||
qemu_mod_timer(s->second_timer, s->next_second_time);
|
||||
}
|
||||
|
||||
static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
int ret;
|
||||
if ((addr & 1) == 0) {
|
||||
return 0xff;
|
||||
} else {
|
||||
switch(s->cmos_index) {
|
||||
case RTC_SECONDS:
|
||||
case RTC_MINUTES:
|
||||
case RTC_HOURS:
|
||||
case RTC_DAY_OF_WEEK:
|
||||
case RTC_DAY_OF_MONTH:
|
||||
case RTC_MONTH:
|
||||
case RTC_YEAR:
|
||||
ret = s->cmos_data[s->cmos_index];
|
||||
break;
|
||||
case RTC_REG_A:
|
||||
ret = s->cmos_data[s->cmos_index];
|
||||
break;
|
||||
case RTC_REG_C:
|
||||
ret = s->cmos_data[s->cmos_index];
|
||||
pic_set_irq(s->irq, 0);
|
||||
s->cmos_data[RTC_REG_C] = 0x00;
|
||||
break;
|
||||
default:
|
||||
ret = s->cmos_data[s->cmos_index];
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_CMOS
|
||||
printf("cmos: read index=0x%02x val=0x%02x\n",
|
||||
s->cmos_index, ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void rtc_set_memory(RTCState *s, int addr, int val)
|
||||
{
|
||||
if (addr >= 0 && addr <= 127)
|
||||
s->cmos_data[addr] = val;
|
||||
}
|
||||
|
||||
void rtc_set_date(RTCState *s, const struct tm *tm)
|
||||
{
|
||||
s->current_tm = *tm;
|
||||
rtc_copy_date(s);
|
||||
}
|
||||
|
||||
static void rtc_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
|
||||
qemu_put_buffer(f, s->cmos_data, 128);
|
||||
qemu_put_8s(f, &s->cmos_index);
|
||||
|
||||
qemu_put_be32s(f, &s->current_tm.tm_sec);
|
||||
qemu_put_be32s(f, &s->current_tm.tm_min);
|
||||
qemu_put_be32s(f, &s->current_tm.tm_hour);
|
||||
qemu_put_be32s(f, &s->current_tm.tm_wday);
|
||||
qemu_put_be32s(f, &s->current_tm.tm_mday);
|
||||
qemu_put_be32s(f, &s->current_tm.tm_mon);
|
||||
qemu_put_be32s(f, &s->current_tm.tm_year);
|
||||
|
||||
qemu_put_timer(f, s->periodic_timer);
|
||||
qemu_put_be64s(f, &s->next_periodic_time);
|
||||
|
||||
qemu_put_be64s(f, &s->next_second_time);
|
||||
qemu_put_timer(f, s->second_timer);
|
||||
qemu_put_timer(f, s->second_timer2);
|
||||
}
|
||||
|
||||
static int rtc_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
qemu_get_buffer(f, s->cmos_data, 128);
|
||||
qemu_get_8s(f, &s->cmos_index);
|
||||
|
||||
qemu_get_be32s(f, &s->current_tm.tm_sec);
|
||||
qemu_get_be32s(f, &s->current_tm.tm_min);
|
||||
qemu_get_be32s(f, &s->current_tm.tm_hour);
|
||||
qemu_get_be32s(f, &s->current_tm.tm_wday);
|
||||
qemu_get_be32s(f, &s->current_tm.tm_mday);
|
||||
qemu_get_be32s(f, &s->current_tm.tm_mon);
|
||||
qemu_get_be32s(f, &s->current_tm.tm_year);
|
||||
|
||||
qemu_get_timer(f, s->periodic_timer);
|
||||
qemu_get_be64s(f, &s->next_periodic_time);
|
||||
|
||||
qemu_get_be64s(f, &s->next_second_time);
|
||||
qemu_get_timer(f, s->second_timer);
|
||||
qemu_get_timer(f, s->second_timer2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
RTCState *rtc_init(int base, int irq)
|
||||
{
|
||||
RTCState *s;
|
||||
|
||||
s = qemu_mallocz(sizeof(RTCState));
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
s->irq = irq;
|
||||
s->cmos_data[RTC_REG_A] = 0x26;
|
||||
s->cmos_data[RTC_REG_B] = 0x02;
|
||||
s->cmos_data[RTC_REG_C] = 0x00;
|
||||
s->cmos_data[RTC_REG_D] = 0x80;
|
||||
|
||||
s->periodic_timer = qemu_new_timer(vm_clock,
|
||||
rtc_periodic_timer, s);
|
||||
s->second_timer = qemu_new_timer(vm_clock,
|
||||
rtc_update_second, s);
|
||||
s->second_timer2 = qemu_new_timer(vm_clock,
|
||||
rtc_update_second2, s);
|
||||
|
||||
s->next_second_time = qemu_get_clock(vm_clock) + (ticks_per_sec * 99) / 100;
|
||||
qemu_mod_timer(s->second_timer2, s->next_second_time);
|
||||
|
||||
register_ioport_write(base, 2, 1, cmos_ioport_write, s);
|
||||
register_ioport_read(base, 2, 1, cmos_ioport_read, s);
|
||||
|
||||
register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
+692
@@ -0,0 +1,692 @@
|
||||
/*
|
||||
* QEMU NE2000 emulation
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
/* debug NE2000 card */
|
||||
//#define DEBUG_NE2000
|
||||
|
||||
#define MAX_ETH_FRAME_SIZE 1514
|
||||
|
||||
#define E8390_CMD 0x00 /* The command register (for all pages) */
|
||||
/* Page 0 register offsets. */
|
||||
#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
|
||||
#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
|
||||
#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
|
||||
#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
|
||||
#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
|
||||
#define EN0_TSR 0x04 /* Transmit status reg RD */
|
||||
#define EN0_TPSR 0x04 /* Transmit starting page WR */
|
||||
#define EN0_NCR 0x05 /* Number of collision reg RD */
|
||||
#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
|
||||
#define EN0_FIFO 0x06 /* FIFO RD */
|
||||
#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
|
||||
#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
|
||||
#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
|
||||
#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
|
||||
#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
|
||||
#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
|
||||
#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
|
||||
#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
|
||||
#define EN0_RSR 0x0c /* rx status reg RD */
|
||||
#define EN0_RXCR 0x0c /* RX configuration reg WR */
|
||||
#define EN0_TXCR 0x0d /* TX configuration reg WR */
|
||||
#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
|
||||
#define EN0_DCFG 0x0e /* Data configuration reg WR */
|
||||
#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
|
||||
#define EN0_IMR 0x0f /* Interrupt mask reg WR */
|
||||
#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
|
||||
|
||||
#define EN1_PHYS 0x11
|
||||
#define EN1_CURPAG 0x17
|
||||
#define EN1_MULT 0x18
|
||||
|
||||
/* Register accessed at EN_CMD, the 8390 base addr. */
|
||||
#define E8390_STOP 0x01 /* Stop and reset the chip */
|
||||
#define E8390_START 0x02 /* Start the chip, clear reset */
|
||||
#define E8390_TRANS 0x04 /* Transmit a frame */
|
||||
#define E8390_RREAD 0x08 /* Remote read */
|
||||
#define E8390_RWRITE 0x10 /* Remote write */
|
||||
#define E8390_NODMA 0x20 /* Remote DMA */
|
||||
#define E8390_PAGE0 0x00 /* Select page chip registers */
|
||||
#define E8390_PAGE1 0x40 /* using the two high-order bits */
|
||||
#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
|
||||
|
||||
/* Bits in EN0_ISR - Interrupt status register */
|
||||
#define ENISR_RX 0x01 /* Receiver, no error */
|
||||
#define ENISR_TX 0x02 /* Transmitter, no error */
|
||||
#define ENISR_RX_ERR 0x04 /* Receiver, with error */
|
||||
#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
|
||||
#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
|
||||
#define ENISR_COUNTERS 0x20 /* Counters need emptying */
|
||||
#define ENISR_RDC 0x40 /* remote dma complete */
|
||||
#define ENISR_RESET 0x80 /* Reset completed */
|
||||
#define ENISR_ALL 0x3f /* Interrupts we will enable */
|
||||
|
||||
/* Bits in received packet status byte and EN0_RSR*/
|
||||
#define ENRSR_RXOK 0x01 /* Received a good packet */
|
||||
#define ENRSR_CRC 0x02 /* CRC error */
|
||||
#define ENRSR_FAE 0x04 /* frame alignment error */
|
||||
#define ENRSR_FO 0x08 /* FIFO overrun */
|
||||
#define ENRSR_MPA 0x10 /* missed pkt */
|
||||
#define ENRSR_PHY 0x20 /* physical/multicast address */
|
||||
#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
|
||||
#define ENRSR_DEF 0x80 /* deferring */
|
||||
|
||||
/* Transmitted packet status, EN0_TSR. */
|
||||
#define ENTSR_PTX 0x01 /* Packet transmitted without error */
|
||||
#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */
|
||||
#define ENTSR_COL 0x04 /* The transmit collided at least once. */
|
||||
#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */
|
||||
#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
|
||||
#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */
|
||||
#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
|
||||
#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */
|
||||
|
||||
#define NE2000_PMEM_SIZE (32*1024)
|
||||
#define NE2000_PMEM_START (16*1024)
|
||||
#define NE2000_PMEM_END (NE2000_PMEM_SIZE+NE2000_PMEM_START)
|
||||
#define NE2000_MEM_SIZE NE2000_PMEM_END
|
||||
|
||||
typedef struct NE2000State {
|
||||
uint8_t cmd;
|
||||
uint32_t start;
|
||||
uint32_t stop;
|
||||
uint8_t boundary;
|
||||
uint8_t tsr;
|
||||
uint8_t tpsr;
|
||||
uint16_t tcnt;
|
||||
uint16_t rcnt;
|
||||
uint32_t rsar;
|
||||
uint8_t rsr;
|
||||
uint8_t isr;
|
||||
uint8_t dcfg;
|
||||
uint8_t imr;
|
||||
uint8_t phys[6]; /* mac address */
|
||||
uint8_t curpag;
|
||||
uint8_t mult[8]; /* multicast mask array */
|
||||
int irq;
|
||||
PCIDevice *pci_dev;
|
||||
NetDriverState *nd;
|
||||
uint8_t mem[NE2000_MEM_SIZE];
|
||||
} NE2000State;
|
||||
|
||||
static void ne2000_reset(NE2000State *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
s->isr = ENISR_RESET;
|
||||
memcpy(s->mem, s->nd->macaddr, 6);
|
||||
s->mem[14] = 0x57;
|
||||
s->mem[15] = 0x57;
|
||||
|
||||
/* duplicate prom data */
|
||||
for(i = 15;i >= 0; i--) {
|
||||
s->mem[2 * i] = s->mem[i];
|
||||
s->mem[2 * i + 1] = s->mem[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void ne2000_update_irq(NE2000State *s)
|
||||
{
|
||||
int isr;
|
||||
isr = s->isr & s->imr;
|
||||
#if defined(DEBUG_NE2000)
|
||||
printf("NE2000: Set IRQ line %d to %d (%02x %02x)\n",
|
||||
s->irq, isr ? 1 : 0, s->isr, s->imr);
|
||||
#endif
|
||||
if (s->irq == 16) {
|
||||
/* PCI irq */
|
||||
pci_set_irq(s->pci_dev, 0, (isr != 0));
|
||||
} else {
|
||||
/* ISA irq */
|
||||
pic_set_irq(s->irq, (isr != 0));
|
||||
}
|
||||
}
|
||||
|
||||
/* return the max buffer size if the NE2000 can receive more data */
|
||||
static int ne2000_can_receive(void *opaque)
|
||||
{
|
||||
NE2000State *s = opaque;
|
||||
int avail, index, boundary;
|
||||
|
||||
if (s->cmd & E8390_STOP)
|
||||
return 0;
|
||||
index = s->curpag << 8;
|
||||
boundary = s->boundary << 8;
|
||||
if (index < boundary)
|
||||
avail = boundary - index;
|
||||
else
|
||||
avail = (s->stop - s->start) - (index - boundary);
|
||||
if (avail < (MAX_ETH_FRAME_SIZE + 4))
|
||||
return 0;
|
||||
return MAX_ETH_FRAME_SIZE;
|
||||
}
|
||||
|
||||
#define MIN_BUF_SIZE 60
|
||||
|
||||
static void ne2000_receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
NE2000State *s = opaque;
|
||||
uint8_t *p;
|
||||
int total_len, next, avail, len, index;
|
||||
uint8_t buf1[60];
|
||||
|
||||
#if defined(DEBUG_NE2000)
|
||||
printf("NE2000: received len=%d\n", size);
|
||||
#endif
|
||||
|
||||
/* if too small buffer, then expand it */
|
||||
if (size < MIN_BUF_SIZE) {
|
||||
memcpy(buf1, buf, size);
|
||||
memset(buf1 + size, 0, MIN_BUF_SIZE - size);
|
||||
buf = buf1;
|
||||
size = MIN_BUF_SIZE;
|
||||
}
|
||||
|
||||
index = s->curpag << 8;
|
||||
/* 4 bytes for header */
|
||||
total_len = size + 4;
|
||||
/* address for next packet (4 bytes for CRC) */
|
||||
next = index + ((total_len + 4 + 255) & ~0xff);
|
||||
if (next >= s->stop)
|
||||
next -= (s->stop - s->start);
|
||||
/* prepare packet header */
|
||||
p = s->mem + index;
|
||||
s->rsr = ENRSR_RXOK; /* receive status */
|
||||
/* XXX: check this */
|
||||
if (buf[0] & 0x01)
|
||||
s->rsr |= ENRSR_PHY;
|
||||
p[0] = s->rsr;
|
||||
p[1] = next >> 8;
|
||||
p[2] = total_len;
|
||||
p[3] = total_len >> 8;
|
||||
index += 4;
|
||||
|
||||
/* write packet data */
|
||||
while (size > 0) {
|
||||
avail = s->stop - index;
|
||||
len = size;
|
||||
if (len > avail)
|
||||
len = avail;
|
||||
memcpy(s->mem + index, buf, len);
|
||||
buf += len;
|
||||
index += len;
|
||||
if (index == s->stop)
|
||||
index = s->start;
|
||||
size -= len;
|
||||
}
|
||||
s->curpag = next >> 8;
|
||||
|
||||
/* now we can signal we have receive something */
|
||||
s->isr |= ENISR_RX;
|
||||
ne2000_update_irq(s);
|
||||
}
|
||||
|
||||
static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
NE2000State *s = opaque;
|
||||
int offset, page, index;
|
||||
|
||||
addr &= 0xf;
|
||||
#ifdef DEBUG_NE2000
|
||||
printf("NE2000: write addr=0x%x val=0x%02x\n", addr, val);
|
||||
#endif
|
||||
if (addr == E8390_CMD) {
|
||||
/* control register */
|
||||
s->cmd = val;
|
||||
if (val & E8390_START) {
|
||||
s->isr &= ~ENISR_RESET;
|
||||
/* test specific case: zero length transfert */
|
||||
if ((val & (E8390_RREAD | E8390_RWRITE)) &&
|
||||
s->rcnt == 0) {
|
||||
s->isr |= ENISR_RDC;
|
||||
ne2000_update_irq(s);
|
||||
}
|
||||
if (val & E8390_TRANS) {
|
||||
index = (s->tpsr << 8);
|
||||
/* XXX: next 2 lines are a hack to make netware 3.11 work */
|
||||
if (index >= NE2000_PMEM_END)
|
||||
index -= NE2000_PMEM_SIZE;
|
||||
/* fail safe: check range on the transmitted length */
|
||||
if (index + s->tcnt <= NE2000_PMEM_END) {
|
||||
qemu_send_packet(s->nd, s->mem + index, s->tcnt);
|
||||
}
|
||||
/* signal end of transfert */
|
||||
s->tsr = ENTSR_PTX;
|
||||
s->isr |= ENISR_TX;
|
||||
s->cmd &= ~E8390_TRANS;
|
||||
ne2000_update_irq(s);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
page = s->cmd >> 6;
|
||||
offset = addr | (page << 4);
|
||||
switch(offset) {
|
||||
case EN0_STARTPG:
|
||||
s->start = val << 8;
|
||||
break;
|
||||
case EN0_STOPPG:
|
||||
s->stop = val << 8;
|
||||
break;
|
||||
case EN0_BOUNDARY:
|
||||
s->boundary = val;
|
||||
break;
|
||||
case EN0_IMR:
|
||||
s->imr = val;
|
||||
ne2000_update_irq(s);
|
||||
break;
|
||||
case EN0_TPSR:
|
||||
s->tpsr = val;
|
||||
break;
|
||||
case EN0_TCNTLO:
|
||||
s->tcnt = (s->tcnt & 0xff00) | val;
|
||||
break;
|
||||
case EN0_TCNTHI:
|
||||
s->tcnt = (s->tcnt & 0x00ff) | (val << 8);
|
||||
break;
|
||||
case EN0_RSARLO:
|
||||
s->rsar = (s->rsar & 0xff00) | val;
|
||||
break;
|
||||
case EN0_RSARHI:
|
||||
s->rsar = (s->rsar & 0x00ff) | (val << 8);
|
||||
break;
|
||||
case EN0_RCNTLO:
|
||||
s->rcnt = (s->rcnt & 0xff00) | val;
|
||||
break;
|
||||
case EN0_RCNTHI:
|
||||
s->rcnt = (s->rcnt & 0x00ff) | (val << 8);
|
||||
break;
|
||||
case EN0_DCFG:
|
||||
s->dcfg = val;
|
||||
break;
|
||||
case EN0_ISR:
|
||||
s->isr &= ~(val & 0x7f);
|
||||
ne2000_update_irq(s);
|
||||
break;
|
||||
case EN1_PHYS ... EN1_PHYS + 5:
|
||||
s->phys[offset - EN1_PHYS] = val;
|
||||
break;
|
||||
case EN1_CURPAG:
|
||||
s->curpag = val;
|
||||
break;
|
||||
case EN1_MULT ... EN1_MULT + 7:
|
||||
s->mult[offset - EN1_MULT] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
NE2000State *s = opaque;
|
||||
int offset, page, ret;
|
||||
|
||||
addr &= 0xf;
|
||||
if (addr == E8390_CMD) {
|
||||
ret = s->cmd;
|
||||
} else {
|
||||
page = s->cmd >> 6;
|
||||
offset = addr | (page << 4);
|
||||
switch(offset) {
|
||||
case EN0_TSR:
|
||||
ret = s->tsr;
|
||||
break;
|
||||
case EN0_BOUNDARY:
|
||||
ret = s->boundary;
|
||||
break;
|
||||
case EN0_ISR:
|
||||
ret = s->isr;
|
||||
break;
|
||||
case EN0_RSARLO:
|
||||
ret = s->rsar & 0x00ff;
|
||||
break;
|
||||
case EN0_RSARHI:
|
||||
ret = s->rsar >> 8;
|
||||
break;
|
||||
case EN1_PHYS ... EN1_PHYS + 5:
|
||||
ret = s->phys[offset - EN1_PHYS];
|
||||
break;
|
||||
case EN1_CURPAG:
|
||||
ret = s->curpag;
|
||||
break;
|
||||
case EN1_MULT ... EN1_MULT + 7:
|
||||
ret = s->mult[offset - EN1_MULT];
|
||||
break;
|
||||
case EN0_RSR:
|
||||
ret = s->rsr;
|
||||
break;
|
||||
default:
|
||||
ret = 0x00;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_NE2000
|
||||
printf("NE2000: read addr=0x%x val=%02x\n", addr, ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr,
|
||||
uint32_t val)
|
||||
{
|
||||
if (addr < 32 ||
|
||||
(addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
|
||||
s->mem[addr] = val;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr,
|
||||
uint32_t val)
|
||||
{
|
||||
addr &= ~1; /* XXX: check exact behaviour if not even */
|
||||
if (addr < 32 ||
|
||||
(addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
|
||||
*(uint16_t *)(s->mem + addr) = cpu_to_le16(val);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr,
|
||||
uint32_t val)
|
||||
{
|
||||
addr &= ~1; /* XXX: check exact behaviour if not even */
|
||||
if (addr < 32 ||
|
||||
(addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
|
||||
cpu_to_le32wu((uint32_t *)(s->mem + addr), val);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr)
|
||||
{
|
||||
if (addr < 32 ||
|
||||
(addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
|
||||
return s->mem[addr];
|
||||
} else {
|
||||
return 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr)
|
||||
{
|
||||
addr &= ~1; /* XXX: check exact behaviour if not even */
|
||||
if (addr < 32 ||
|
||||
(addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
|
||||
return le16_to_cpu(*(uint16_t *)(s->mem + addr));
|
||||
} else {
|
||||
return 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr)
|
||||
{
|
||||
addr &= ~1; /* XXX: check exact behaviour if not even */
|
||||
if (addr < 32 ||
|
||||
(addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
|
||||
return le32_to_cpupu((uint32_t *)(s->mem + addr));
|
||||
} else {
|
||||
return 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ne2000_dma_update(NE2000State *s, int len)
|
||||
{
|
||||
s->rsar += len;
|
||||
/* wrap */
|
||||
/* XXX: check what to do if rsar > stop */
|
||||
if (s->rsar == s->stop)
|
||||
s->rsar = s->start;
|
||||
|
||||
if (s->rcnt <= len) {
|
||||
s->rcnt = 0;
|
||||
/* signal end of transfert */
|
||||
s->isr |= ENISR_RDC;
|
||||
ne2000_update_irq(s);
|
||||
} else {
|
||||
s->rcnt -= len;
|
||||
}
|
||||
}
|
||||
|
||||
static void ne2000_asic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
NE2000State *s = opaque;
|
||||
|
||||
#ifdef DEBUG_NE2000
|
||||
printf("NE2000: asic write val=0x%04x\n", val);
|
||||
#endif
|
||||
if (s->rcnt == 0)
|
||||
return;
|
||||
if (s->dcfg & 0x01) {
|
||||
/* 16 bit access */
|
||||
ne2000_mem_writew(s, s->rsar, val);
|
||||
ne2000_dma_update(s, 2);
|
||||
} else {
|
||||
/* 8 bit access */
|
||||
ne2000_mem_writeb(s, s->rsar, val);
|
||||
ne2000_dma_update(s, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t ne2000_asic_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
NE2000State *s = opaque;
|
||||
int ret;
|
||||
|
||||
if (s->dcfg & 0x01) {
|
||||
/* 16 bit access */
|
||||
ret = ne2000_mem_readw(s, s->rsar);
|
||||
ne2000_dma_update(s, 2);
|
||||
} else {
|
||||
/* 8 bit access */
|
||||
ret = ne2000_mem_readb(s, s->rsar);
|
||||
ne2000_dma_update(s, 1);
|
||||
}
|
||||
#ifdef DEBUG_NE2000
|
||||
printf("NE2000: asic read val=0x%04x\n", ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ne2000_asic_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
NE2000State *s = opaque;
|
||||
|
||||
#ifdef DEBUG_NE2000
|
||||
printf("NE2000: asic writel val=0x%04x\n", val);
|
||||
#endif
|
||||
if (s->rcnt == 0)
|
||||
return;
|
||||
/* 32 bit access */
|
||||
ne2000_mem_writel(s, s->rsar, val);
|
||||
ne2000_dma_update(s, 4);
|
||||
}
|
||||
|
||||
static uint32_t ne2000_asic_ioport_readl(void *opaque, uint32_t addr)
|
||||
{
|
||||
NE2000State *s = opaque;
|
||||
int ret;
|
||||
|
||||
/* 32 bit access */
|
||||
ret = ne2000_mem_readl(s, s->rsar);
|
||||
ne2000_dma_update(s, 4);
|
||||
#ifdef DEBUG_NE2000
|
||||
printf("NE2000: asic readl val=0x%04x\n", ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ne2000_reset_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
/* nothing to do (end of reset pulse) */
|
||||
}
|
||||
|
||||
static uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
NE2000State *s = opaque;
|
||||
ne2000_reset(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ne2000_save(QEMUFile* f,void* opaque)
|
||||
{
|
||||
NE2000State* s=(NE2000State*)opaque;
|
||||
|
||||
qemu_put_8s(f, &s->cmd);
|
||||
qemu_put_be32s(f, &s->start);
|
||||
qemu_put_be32s(f, &s->stop);
|
||||
qemu_put_8s(f, &s->boundary);
|
||||
qemu_put_8s(f, &s->tsr);
|
||||
qemu_put_8s(f, &s->tpsr);
|
||||
qemu_put_be16s(f, &s->tcnt);
|
||||
qemu_put_be16s(f, &s->rcnt);
|
||||
qemu_put_be32s(f, &s->rsar);
|
||||
qemu_put_8s(f, &s->rsr);
|
||||
qemu_put_8s(f, &s->isr);
|
||||
qemu_put_8s(f, &s->dcfg);
|
||||
qemu_put_8s(f, &s->imr);
|
||||
qemu_put_buffer(f, s->phys, 6);
|
||||
qemu_put_8s(f, &s->curpag);
|
||||
qemu_put_buffer(f, s->mult, 8);
|
||||
qemu_put_be32s(f, &s->irq);
|
||||
qemu_put_buffer(f, s->mem, NE2000_MEM_SIZE);
|
||||
}
|
||||
|
||||
static int ne2000_load(QEMUFile* f,void* opaque,int version_id)
|
||||
{
|
||||
NE2000State* s=(NE2000State*)opaque;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
qemu_get_8s(f, &s->cmd);
|
||||
qemu_get_be32s(f, &s->start);
|
||||
qemu_get_be32s(f, &s->stop);
|
||||
qemu_get_8s(f, &s->boundary);
|
||||
qemu_get_8s(f, &s->tsr);
|
||||
qemu_get_8s(f, &s->tpsr);
|
||||
qemu_get_be16s(f, &s->tcnt);
|
||||
qemu_get_be16s(f, &s->rcnt);
|
||||
qemu_get_be32s(f, &s->rsar);
|
||||
qemu_get_8s(f, &s->rsr);
|
||||
qemu_get_8s(f, &s->isr);
|
||||
qemu_get_8s(f, &s->dcfg);
|
||||
qemu_get_8s(f, &s->imr);
|
||||
qemu_get_buffer(f, s->phys, 6);
|
||||
qemu_get_8s(f, &s->curpag);
|
||||
qemu_get_buffer(f, s->mult, 8);
|
||||
qemu_get_be32s(f, &s->irq);
|
||||
qemu_get_buffer(f, s->mem, NE2000_MEM_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void isa_ne2000_init(int base, int irq, NetDriverState *nd)
|
||||
{
|
||||
NE2000State *s;
|
||||
|
||||
s = qemu_mallocz(sizeof(NE2000State));
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
register_ioport_write(base, 16, 1, ne2000_ioport_write, s);
|
||||
register_ioport_read(base, 16, 1, ne2000_ioport_read, s);
|
||||
|
||||
register_ioport_write(base + 0x10, 1, 1, ne2000_asic_ioport_write, s);
|
||||
register_ioport_read(base + 0x10, 1, 1, ne2000_asic_ioport_read, s);
|
||||
register_ioport_write(base + 0x10, 2, 2, ne2000_asic_ioport_write, s);
|
||||
register_ioport_read(base + 0x10, 2, 2, ne2000_asic_ioport_read, s);
|
||||
|
||||
register_ioport_write(base + 0x1f, 1, 1, ne2000_reset_ioport_write, s);
|
||||
register_ioport_read(base + 0x1f, 1, 1, ne2000_reset_ioport_read, s);
|
||||
s->irq = irq;
|
||||
s->nd = nd;
|
||||
|
||||
ne2000_reset(s);
|
||||
|
||||
qemu_add_read_packet(nd, ne2000_can_receive, ne2000_receive, s);
|
||||
|
||||
register_savevm("ne2000", 0, 1, ne2000_save, ne2000_load, s);
|
||||
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* PCI NE2000 definitions */
|
||||
|
||||
typedef struct PCINE2000State {
|
||||
PCIDevice dev;
|
||||
NE2000State ne2000;
|
||||
} PCINE2000State;
|
||||
|
||||
static void ne2000_map(PCIDevice *pci_dev, int region_num,
|
||||
uint32_t addr, uint32_t size, int type)
|
||||
{
|
||||
PCINE2000State *d = (PCINE2000State *)pci_dev;
|
||||
NE2000State *s = &d->ne2000;
|
||||
|
||||
register_ioport_write(addr, 16, 1, ne2000_ioport_write, s);
|
||||
register_ioport_read(addr, 16, 1, ne2000_ioport_read, s);
|
||||
|
||||
register_ioport_write(addr + 0x10, 1, 1, ne2000_asic_ioport_write, s);
|
||||
register_ioport_read(addr + 0x10, 1, 1, ne2000_asic_ioport_read, s);
|
||||
register_ioport_write(addr + 0x10, 2, 2, ne2000_asic_ioport_write, s);
|
||||
register_ioport_read(addr + 0x10, 2, 2, ne2000_asic_ioport_read, s);
|
||||
register_ioport_write(addr + 0x10, 4, 4, ne2000_asic_ioport_writel, s);
|
||||
register_ioport_read(addr + 0x10, 4, 4, ne2000_asic_ioport_readl, s);
|
||||
|
||||
register_ioport_write(addr + 0x1f, 1, 1, ne2000_reset_ioport_write, s);
|
||||
register_ioport_read(addr + 0x1f, 1, 1, ne2000_reset_ioport_read, s);
|
||||
}
|
||||
|
||||
void pci_ne2000_init(PCIBus *bus, NetDriverState *nd)
|
||||
{
|
||||
PCINE2000State *d;
|
||||
NE2000State *s;
|
||||
uint8_t *pci_conf;
|
||||
|
||||
d = (PCINE2000State *)pci_register_device(bus,
|
||||
"NE2000", sizeof(PCINE2000State),
|
||||
-1,
|
||||
NULL, NULL);
|
||||
pci_conf = d->dev.config;
|
||||
pci_conf[0x00] = 0xec; // Realtek 8029
|
||||
pci_conf[0x01] = 0x10;
|
||||
pci_conf[0x02] = 0x29;
|
||||
pci_conf[0x03] = 0x80;
|
||||
pci_conf[0x0a] = 0x00; // ethernet network controller
|
||||
pci_conf[0x0b] = 0x02;
|
||||
pci_conf[0x0e] = 0x00; // header_type
|
||||
pci_conf[0x3d] = 1; // interrupt pin 0
|
||||
|
||||
pci_register_io_region(&d->dev, 0, 0x100,
|
||||
PCI_ADDRESS_SPACE_IO, ne2000_map);
|
||||
s = &d->ne2000;
|
||||
s->irq = 16; // PCI interrupt
|
||||
s->pci_dev = (PCIDevice *)d;
|
||||
s->nd = nd;
|
||||
ne2000_reset(s);
|
||||
qemu_add_read_packet(nd, ne2000_can_receive, ne2000_receive, s);
|
||||
|
||||
/* XXX: instance number ? */
|
||||
register_savevm("ne2000", 0, 1, ne2000_save, ne2000_load, s);
|
||||
register_savevm("ne2000_pci", 0, 1, generic_pci_save, generic_pci_load,
|
||||
&d->dev);
|
||||
}
|
||||
+1023
File diff suppressed because it is too large.
Load diff
+183
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* QEMU Parallel PORT emulation
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_PARALLEL
|
||||
|
||||
/*
|
||||
* These are the definitions for the Printer Status Register
|
||||
*/
|
||||
#define PARA_STS_BUSY 0x80 /* Busy complement */
|
||||
#define PARA_STS_ACK 0x40 /* Acknowledge */
|
||||
#define PARA_STS_PAPER 0x20 /* Out of paper */
|
||||
#define PARA_STS_ONLINE 0x10 /* Online */
|
||||
#define PARA_STS_ERROR 0x08 /* Error complement */
|
||||
|
||||
/*
|
||||
* These are the definitions for the Printer Control Register
|
||||
*/
|
||||
#define PARA_CTR_INTEN 0x10 /* IRQ Enable */
|
||||
#define PARA_CTR_SELECT 0x08 /* Select In complement */
|
||||
#define PARA_CTR_INIT 0x04 /* Initialize Printer complement */
|
||||
#define PARA_CTR_AUTOLF 0x02 /* Auto linefeed complement */
|
||||
#define PARA_CTR_STROBE 0x01 /* Strobe complement */
|
||||
|
||||
struct ParallelState {
|
||||
uint8_t data;
|
||||
uint8_t status; /* read only register */
|
||||
uint8_t control;
|
||||
int irq;
|
||||
int irq_pending;
|
||||
CharDriverState *chr;
|
||||
};
|
||||
|
||||
static void parallel_update_irq(ParallelState *s)
|
||||
{
|
||||
if (s->irq_pending)
|
||||
pic_set_irq(s->irq, 1);
|
||||
else
|
||||
pic_set_irq(s->irq, 0);
|
||||
}
|
||||
|
||||
static void parallel_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
ParallelState *s = opaque;
|
||||
|
||||
addr &= 7;
|
||||
#ifdef DEBUG_PARALLEL
|
||||
printf("parallel: write addr=0x%02x val=0x%02x\n", addr, val);
|
||||
#endif
|
||||
switch(addr) {
|
||||
case 0:
|
||||
s->data = val;
|
||||
parallel_update_irq(s);
|
||||
break;
|
||||
case 2:
|
||||
if ((val & PARA_CTR_INIT) == 0 ) {
|
||||
s->status = PARA_STS_BUSY;
|
||||
s->status |= PARA_STS_ACK;
|
||||
s->status |= PARA_STS_ONLINE;
|
||||
s->status |= PARA_STS_ERROR;
|
||||
}
|
||||
else if (val & PARA_CTR_SELECT) {
|
||||
if (val & PARA_CTR_STROBE) {
|
||||
s->status &= ~PARA_STS_BUSY;
|
||||
if ((s->control & PARA_CTR_STROBE) == 0)
|
||||
qemu_chr_write(s->chr, &s->data, 1);
|
||||
} else {
|
||||
if (s->control & PARA_CTR_INTEN) {
|
||||
s->irq_pending = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
parallel_update_irq(s);
|
||||
s->control = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t parallel_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
ParallelState *s = opaque;
|
||||
uint32_t ret = 0xff;
|
||||
|
||||
addr &= 7;
|
||||
switch(addr) {
|
||||
case 0:
|
||||
ret = s->data;
|
||||
break;
|
||||
case 1:
|
||||
ret = s->status;
|
||||
s->irq_pending = 0;
|
||||
if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) {
|
||||
/* XXX Fixme: wait 5 microseconds */
|
||||
if (s->status & PARA_STS_ACK)
|
||||
s->status &= ~PARA_STS_ACK;
|
||||
else {
|
||||
/* XXX Fixme: wait 5 microseconds */
|
||||
s->status |= PARA_STS_ACK;
|
||||
s->status |= PARA_STS_BUSY;
|
||||
}
|
||||
}
|
||||
parallel_update_irq(s);
|
||||
break;
|
||||
case 2:
|
||||
ret = s->control;
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_PARALLEL
|
||||
printf("parallel: read addr=0x%02x val=0x%02x\n", addr, ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parallel_can_receive(ParallelState *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parallel_receive_byte(ParallelState *s, int ch)
|
||||
{
|
||||
}
|
||||
|
||||
static int parallel_can_receive1(void *opaque)
|
||||
{
|
||||
ParallelState *s = opaque;
|
||||
return parallel_can_receive(s);
|
||||
}
|
||||
|
||||
static void parallel_receive1(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
ParallelState *s = opaque;
|
||||
parallel_receive_byte(s, buf[0]);
|
||||
}
|
||||
|
||||
static void parallel_event(void *opaque, int event)
|
||||
{
|
||||
}
|
||||
|
||||
/* If fd is zero, it means that the parallel device uses the console */
|
||||
ParallelState *parallel_init(int base, int irq, CharDriverState *chr)
|
||||
{
|
||||
ParallelState *s;
|
||||
|
||||
s = qemu_mallocz(sizeof(ParallelState));
|
||||
if (!s)
|
||||
return NULL;
|
||||
s->irq = irq;
|
||||
s->data = 0;
|
||||
s->status = PARA_STS_BUSY;
|
||||
s->status |= PARA_STS_ACK;
|
||||
s->status |= PARA_STS_ONLINE;
|
||||
s->status |= PARA_STS_ERROR;
|
||||
s->control = PARA_CTR_SELECT;
|
||||
s->control |= PARA_CTR_INIT;
|
||||
|
||||
register_ioport_write(base, 8, 1, parallel_ioport_write, s);
|
||||
register_ioport_read(base, 8, 1, parallel_ioport_read, s);
|
||||
s->chr = chr;
|
||||
qemu_chr_add_read_handler(chr, parallel_can_receive1, parallel_receive1, s);
|
||||
qemu_chr_add_event_handler(chr, parallel_event);
|
||||
return s;
|
||||
}
|
||||
@@ -0,0 +1,597 @@
|
||||
/*
|
||||
* QEMU PC System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
/* output Bochs bios info messages */
|
||||
//#define DEBUG_BIOS
|
||||
|
||||
#define BIOS_FILENAME "bios.bin"
|
||||
#define VGABIOS_FILENAME "vgabios.bin"
|
||||
#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
|
||||
#define LINUX_BOOT_FILENAME "linux_boot.bin"
|
||||
|
||||
#define KERNEL_LOAD_ADDR 0x00100000
|
||||
#define INITRD_LOAD_ADDR 0x00400000
|
||||
#define KERNEL_PARAMS_ADDR 0x00090000
|
||||
#define KERNEL_CMDLINE_ADDR 0x00099000
|
||||
|
||||
int speaker_data_on;
|
||||
int dummy_refresh_clock;
|
||||
static fdctrl_t *floppy_controller;
|
||||
static RTCState *rtc_state;
|
||||
static PITState *pit;
|
||||
|
||||
static void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
|
||||
{
|
||||
}
|
||||
|
||||
/* MSDOS compatibility mode FPU exception support */
|
||||
/* XXX: add IGNNE support */
|
||||
void cpu_set_ferr(CPUX86State *s)
|
||||
{
|
||||
pic_set_irq(13, 1);
|
||||
}
|
||||
|
||||
static void ioportF0_write(void *opaque, uint32_t addr, uint32_t data)
|
||||
{
|
||||
pic_set_irq(13, 0);
|
||||
}
|
||||
|
||||
/* TSC handling */
|
||||
|
||||
uint64_t cpu_get_tsc(CPUX86State *env)
|
||||
{
|
||||
return qemu_get_clock(vm_clock);
|
||||
}
|
||||
|
||||
/* PC cmos mappings */
|
||||
|
||||
#define REG_EQUIPMENT_BYTE 0x14
|
||||
#define REG_IBM_CENTURY_BYTE 0x32
|
||||
#define REG_IBM_PS2_CENTURY_BYTE 0x37
|
||||
|
||||
|
||||
static inline int to_bcd(RTCState *s, int a)
|
||||
{
|
||||
return ((a / 10) << 4) | (a % 10);
|
||||
}
|
||||
|
||||
static int cmos_get_fd_drive_type(int fd0)
|
||||
{
|
||||
int val;
|
||||
|
||||
switch (fd0) {
|
||||
case 0:
|
||||
/* 1.44 Mb 3"5 drive */
|
||||
val = 4;
|
||||
break;
|
||||
case 1:
|
||||
/* 2.88 Mb 3"5 drive */
|
||||
val = 5;
|
||||
break;
|
||||
case 2:
|
||||
/* 1.2 Mb 5"5 drive */
|
||||
val = 2;
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd)
|
||||
{
|
||||
RTCState *s = rtc_state;
|
||||
int cylinders, heads, sectors;
|
||||
bdrv_get_geometry_hint(hd, &cylinders, &heads, §ors);
|
||||
rtc_set_memory(s, type_ofs, 47);
|
||||
rtc_set_memory(s, info_ofs, cylinders);
|
||||
rtc_set_memory(s, info_ofs + 1, cylinders >> 8);
|
||||
rtc_set_memory(s, info_ofs + 2, heads);
|
||||
rtc_set_memory(s, info_ofs + 3, 0xff);
|
||||
rtc_set_memory(s, info_ofs + 4, 0xff);
|
||||
rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3));
|
||||
rtc_set_memory(s, info_ofs + 6, cylinders);
|
||||
rtc_set_memory(s, info_ofs + 7, cylinders >> 8);
|
||||
rtc_set_memory(s, info_ofs + 8, sectors);
|
||||
}
|
||||
|
||||
/* hd_table must contain 4 block drivers */
|
||||
static void cmos_init(int ram_size, int boot_device, BlockDriverState **hd_table)
|
||||
{
|
||||
RTCState *s = rtc_state;
|
||||
int val;
|
||||
int fd0, fd1, nb;
|
||||
time_t ti;
|
||||
struct tm *tm;
|
||||
int i;
|
||||
|
||||
/* set the CMOS date */
|
||||
time(&ti);
|
||||
if (rtc_utc)
|
||||
tm = gmtime(&ti);
|
||||
else
|
||||
tm = localtime(&ti);
|
||||
rtc_set_date(s, tm);
|
||||
|
||||
val = to_bcd(s, (tm->tm_year / 100) + 19);
|
||||
rtc_set_memory(s, REG_IBM_CENTURY_BYTE, val);
|
||||
rtc_set_memory(s, REG_IBM_PS2_CENTURY_BYTE, val);
|
||||
|
||||
/* various important CMOS locations needed by PC/Bochs bios */
|
||||
|
||||
/* memory size */
|
||||
val = 640; /* base memory in K */
|
||||
rtc_set_memory(s, 0x15, val);
|
||||
rtc_set_memory(s, 0x16, val >> 8);
|
||||
|
||||
val = (ram_size / 1024) - 1024;
|
||||
if (val > 65535)
|
||||
val = 65535;
|
||||
rtc_set_memory(s, 0x17, val);
|
||||
rtc_set_memory(s, 0x18, val >> 8);
|
||||
rtc_set_memory(s, 0x30, val);
|
||||
rtc_set_memory(s, 0x31, val >> 8);
|
||||
|
||||
if (ram_size > (16 * 1024 * 1024))
|
||||
val = (ram_size / 65536) - ((16 * 1024 * 1024) / 65536);
|
||||
else
|
||||
val = 0;
|
||||
if (val > 65535)
|
||||
val = 65535;
|
||||
rtc_set_memory(s, 0x34, val);
|
||||
rtc_set_memory(s, 0x35, val >> 8);
|
||||
|
||||
switch(boot_device) {
|
||||
case 'a':
|
||||
case 'b':
|
||||
rtc_set_memory(s, 0x3d, 0x01); /* floppy boot */
|
||||
break;
|
||||
default:
|
||||
case 'c':
|
||||
rtc_set_memory(s, 0x3d, 0x02); /* hard drive boot */
|
||||
break;
|
||||
case 'd':
|
||||
rtc_set_memory(s, 0x3d, 0x03); /* CD-ROM boot */
|
||||
break;
|
||||
}
|
||||
|
||||
/* floppy type */
|
||||
|
||||
fd0 = fdctrl_get_drive_type(floppy_controller, 0);
|
||||
fd1 = fdctrl_get_drive_type(floppy_controller, 1);
|
||||
|
||||
val = (cmos_get_fd_drive_type(fd0) << 4) | cmos_get_fd_drive_type(fd1);
|
||||
rtc_set_memory(s, 0x10, val);
|
||||
|
||||
val = 0;
|
||||
nb = 0;
|
||||
if (fd0 < 3)
|
||||
nb++;
|
||||
if (fd1 < 3)
|
||||
nb++;
|
||||
switch (nb) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
val |= 0x01; /* 1 drive, ready for boot */
|
||||
break;
|
||||
case 2:
|
||||
val |= 0x41; /* 2 drives, ready for boot */
|
||||
break;
|
||||
}
|
||||
val |= 0x02; /* FPU is there */
|
||||
val |= 0x04; /* PS/2 mouse installed */
|
||||
rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
|
||||
|
||||
/* hard drives */
|
||||
|
||||
rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0));
|
||||
if (hd_table[0])
|
||||
cmos_init_hd(0x19, 0x1b, hd_table[0]);
|
||||
if (hd_table[1])
|
||||
cmos_init_hd(0x1a, 0x24, hd_table[1]);
|
||||
|
||||
val = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (hd_table[i]) {
|
||||
int cylinders, heads, sectors, translation;
|
||||
/* NOTE: bdrv_get_geometry_hint() returns the physical
|
||||
geometry. It is always such that: 1 <= sects <= 63, 1
|
||||
<= heads <= 16, 1 <= cylinders <= 16383. The BIOS
|
||||
geometry can be different if a translation is done. */
|
||||
translation = bdrv_get_translation_hint(hd_table[i]);
|
||||
if (translation == BIOS_ATA_TRANSLATION_AUTO) {
|
||||
bdrv_get_geometry_hint(hd_table[i], &cylinders, &heads, §ors);
|
||||
if (cylinders <= 1024 && heads <= 16 && sectors <= 63) {
|
||||
/* No translation. */
|
||||
translation = 0;
|
||||
} else {
|
||||
/* LBA translation. */
|
||||
translation = 1;
|
||||
}
|
||||
} else {
|
||||
translation--;
|
||||
}
|
||||
val |= translation << (i * 2);
|
||||
}
|
||||
}
|
||||
rtc_set_memory(s, 0x39, val);
|
||||
|
||||
/* Disable check of 0x55AA signature on the last two bytes of
|
||||
first sector of disk. XXX: make it the default ? */
|
||||
// rtc_set_memory(s, 0x38, 1);
|
||||
}
|
||||
|
||||
static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
speaker_data_on = (val >> 1) & 1;
|
||||
pit_set_gate(pit, 2, val & 1);
|
||||
}
|
||||
|
||||
static uint32_t speaker_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
int out;
|
||||
out = pit_get_out(pit, 2, qemu_get_clock(vm_clock));
|
||||
dummy_refresh_clock ^= 1;
|
||||
return (speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) |
|
||||
(dummy_refresh_clock << 4);
|
||||
}
|
||||
|
||||
static void ioport92_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
cpu_x86_set_a20(cpu_single_env, (val >> 1) & 1);
|
||||
/* XXX: bit 0 is fast reset */
|
||||
}
|
||||
|
||||
static uint32_t ioport92_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
return ((cpu_single_env->a20_mask >> 20) & 1) << 1;
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* Bochs BIOS debug ports */
|
||||
|
||||
void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
static const char shutdown_str[8] = "Shutdown";
|
||||
static int shutdown_index = 0;
|
||||
|
||||
switch(addr) {
|
||||
/* Bochs BIOS messages */
|
||||
case 0x400:
|
||||
case 0x401:
|
||||
fprintf(stderr, "BIOS panic at rombios.c, line %d\n", val);
|
||||
exit(1);
|
||||
case 0x402:
|
||||
case 0x403:
|
||||
#ifdef DEBUG_BIOS
|
||||
fprintf(stderr, "%c", val);
|
||||
#endif
|
||||
break;
|
||||
case 0x8900:
|
||||
/* same as Bochs power off */
|
||||
if (val == shutdown_str[shutdown_index]) {
|
||||
shutdown_index++;
|
||||
if (shutdown_index == 8) {
|
||||
shutdown_index = 0;
|
||||
qemu_system_shutdown_request();
|
||||
}
|
||||
} else {
|
||||
shutdown_index = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
/* LGPL'ed VGA BIOS messages */
|
||||
case 0x501:
|
||||
case 0x502:
|
||||
fprintf(stderr, "VGA BIOS panic, line %d\n", val);
|
||||
exit(1);
|
||||
case 0x500:
|
||||
case 0x503:
|
||||
#ifdef DEBUG_BIOS
|
||||
fprintf(stderr, "%c", val);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void bochs_bios_init(void)
|
||||
{
|
||||
register_ioport_write(0x400, 1, 2, bochs_bios_write, NULL);
|
||||
register_ioport_write(0x401, 1, 2, bochs_bios_write, NULL);
|
||||
register_ioport_write(0x402, 1, 1, bochs_bios_write, NULL);
|
||||
register_ioport_write(0x403, 1, 1, bochs_bios_write, NULL);
|
||||
register_ioport_write(0x8900, 1, 1, bochs_bios_write, NULL);
|
||||
|
||||
register_ioport_write(0x501, 1, 2, bochs_bios_write, NULL);
|
||||
register_ioport_write(0x502, 1, 2, bochs_bios_write, NULL);
|
||||
register_ioport_write(0x500, 1, 1, bochs_bios_write, NULL);
|
||||
register_ioport_write(0x503, 1, 1, bochs_bios_write, NULL);
|
||||
}
|
||||
|
||||
|
||||
int load_kernel(const char *filename, uint8_t *addr,
|
||||
uint8_t *real_addr)
|
||||
{
|
||||
int fd, size;
|
||||
int setup_sects;
|
||||
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
/* load 16 bit code */
|
||||
if (read(fd, real_addr, 512) != 512)
|
||||
goto fail;
|
||||
setup_sects = real_addr[0x1F1];
|
||||
if (!setup_sects)
|
||||
setup_sects = 4;
|
||||
if (read(fd, real_addr + 512, setup_sects * 512) !=
|
||||
setup_sects * 512)
|
||||
goto fail;
|
||||
|
||||
/* load 32 bit code */
|
||||
size = read(fd, addr, 16 * 1024 * 1024);
|
||||
if (size < 0)
|
||||
goto fail;
|
||||
close(fd);
|
||||
return size;
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const int ide_iobase[2] = { 0x1f0, 0x170 };
|
||||
static const int ide_iobase2[2] = { 0x3f6, 0x376 };
|
||||
static const int ide_irq[2] = { 14, 15 };
|
||||
|
||||
#define NE2000_NB_MAX 6
|
||||
|
||||
static int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 };
|
||||
static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
|
||||
|
||||
static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
|
||||
static int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
|
||||
|
||||
static int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
|
||||
static int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
|
||||
|
||||
/* PC hardware initialisation */
|
||||
void pc_init(int ram_size, int vga_ram_size, int boot_device,
|
||||
DisplayState *ds, const char **fd_filename, int snapshot,
|
||||
const char *kernel_filename, const char *kernel_cmdline,
|
||||
const char *initrd_filename)
|
||||
{
|
||||
char buf[1024];
|
||||
int ret, linux_boot, initrd_size, i, nb_nics1;
|
||||
unsigned long bios_offset, vga_bios_offset;
|
||||
int bios_size, isa_bios_size;
|
||||
PCIBus *pci_bus;
|
||||
|
||||
linux_boot = (kernel_filename != NULL);
|
||||
|
||||
/* allocate RAM */
|
||||
cpu_register_physical_memory(0, ram_size, 0);
|
||||
|
||||
/* BIOS load */
|
||||
bios_offset = ram_size + vga_ram_size;
|
||||
vga_bios_offset = bios_offset + 256 * 1024;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME);
|
||||
bios_size = get_image_size(buf);
|
||||
if (bios_size <= 0 ||
|
||||
(bios_size % 65536) != 0 ||
|
||||
bios_size > (256 * 1024)) {
|
||||
goto bios_error;
|
||||
}
|
||||
ret = load_image(buf, phys_ram_base + bios_offset);
|
||||
if (ret != bios_size) {
|
||||
bios_error:
|
||||
fprintf(stderr, "qemu: could not load PC bios '%s'\n", buf);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* VGA BIOS load */
|
||||
if (cirrus_vga_enabled) {
|
||||
snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_CIRRUS_FILENAME);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_FILENAME);
|
||||
}
|
||||
ret = load_image(buf, phys_ram_base + vga_bios_offset);
|
||||
|
||||
/* setup basic memory access */
|
||||
cpu_register_physical_memory(0xc0000, 0x10000,
|
||||
vga_bios_offset | IO_MEM_ROM);
|
||||
|
||||
/* map the last 128KB of the BIOS in ISA space */
|
||||
isa_bios_size = bios_size;
|
||||
if (isa_bios_size > (128 * 1024))
|
||||
isa_bios_size = 128 * 1024;
|
||||
cpu_register_physical_memory(0xd0000, (192 * 1024) - isa_bios_size,
|
||||
IO_MEM_UNASSIGNED);
|
||||
cpu_register_physical_memory(0x100000 - isa_bios_size,
|
||||
isa_bios_size,
|
||||
(bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);
|
||||
/* map all the bios at the top of memory */
|
||||
cpu_register_physical_memory((uint32_t)(-bios_size),
|
||||
bios_size, bios_offset | IO_MEM_ROM);
|
||||
|
||||
bochs_bios_init();
|
||||
|
||||
if (linux_boot) {
|
||||
uint8_t bootsect[512];
|
||||
uint8_t old_bootsect[512];
|
||||
|
||||
if (bs_table[0] == NULL) {
|
||||
fprintf(stderr, "A disk image must be given for 'hda' when booting a Linux kernel\n");
|
||||
exit(1);
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "%s/%s", bios_dir, LINUX_BOOT_FILENAME);
|
||||
ret = load_image(buf, bootsect);
|
||||
if (ret != sizeof(bootsect)) {
|
||||
fprintf(stderr, "qemu: could not load linux boot sector '%s'\n",
|
||||
buf);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (bdrv_read(bs_table[0], 0, old_bootsect, 1) >= 0) {
|
||||
/* copy the MSDOS partition table */
|
||||
memcpy(bootsect + 0x1be, old_bootsect + 0x1be, 0x40);
|
||||
}
|
||||
|
||||
bdrv_set_boot_sector(bs_table[0], bootsect, sizeof(bootsect));
|
||||
|
||||
/* now we can load the kernel */
|
||||
ret = load_kernel(kernel_filename,
|
||||
phys_ram_base + KERNEL_LOAD_ADDR,
|
||||
phys_ram_base + KERNEL_PARAMS_ADDR);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||
kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* load initrd */
|
||||
initrd_size = 0;
|
||||
if (initrd_filename) {
|
||||
initrd_size = load_image(initrd_filename, phys_ram_base + INITRD_LOAD_ADDR);
|
||||
if (initrd_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (initrd_size > 0) {
|
||||
stl_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x218, INITRD_LOAD_ADDR);
|
||||
stl_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x21c, initrd_size);
|
||||
}
|
||||
pstrcpy(phys_ram_base + KERNEL_CMDLINE_ADDR, 4096,
|
||||
kernel_cmdline);
|
||||
stw_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x20, 0xA33F);
|
||||
stw_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x22,
|
||||
KERNEL_CMDLINE_ADDR - KERNEL_PARAMS_ADDR);
|
||||
/* loader type */
|
||||
stw_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x210, 0x01);
|
||||
}
|
||||
|
||||
if (pci_enabled) {
|
||||
pci_bus = i440fx_init();
|
||||
piix3_init(pci_bus);
|
||||
} else {
|
||||
pci_bus = NULL;
|
||||
}
|
||||
|
||||
/* init basic PC hardware */
|
||||
register_ioport_write(0x80, 1, 1, ioport80_write, NULL);
|
||||
|
||||
register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
|
||||
|
||||
if (cirrus_vga_enabled) {
|
||||
if (pci_enabled) {
|
||||
pci_cirrus_vga_init(pci_bus,
|
||||
ds, phys_ram_base + ram_size, ram_size,
|
||||
vga_ram_size);
|
||||
} else {
|
||||
isa_cirrus_vga_init(ds, phys_ram_base + ram_size, ram_size,
|
||||
vga_ram_size);
|
||||
}
|
||||
} else {
|
||||
vga_initialize(pci_bus, ds, phys_ram_base + ram_size, ram_size,
|
||||
vga_ram_size);
|
||||
}
|
||||
|
||||
rtc_state = rtc_init(0x70, 8);
|
||||
register_ioport_read(0x61, 1, 1, speaker_ioport_read, NULL);
|
||||
register_ioport_write(0x61, 1, 1, speaker_ioport_write, NULL);
|
||||
|
||||
register_ioport_read(0x92, 1, 1, ioport92_read, NULL);
|
||||
register_ioport_write(0x92, 1, 1, ioport92_write, NULL);
|
||||
|
||||
if (pci_enabled)
|
||||
apic_init(cpu_single_env);
|
||||
pic_init();
|
||||
pit = pit_init(0x40, 0);
|
||||
|
||||
for(i = 0; i < MAX_SERIAL_PORTS; i++) {
|
||||
if (serial_hds[i]) {
|
||||
serial_init(serial_io[i], serial_irq[i], serial_hds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
|
||||
if (parallel_hds[i]) {
|
||||
parallel_init(parallel_io[i], parallel_irq[i], parallel_hds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (pci_enabled) {
|
||||
for(i = 0; i < nb_nics; i++) {
|
||||
pci_ne2000_init(pci_bus, &nd_table[i]);
|
||||
}
|
||||
pci_piix3_ide_init(pci_bus, bs_table);
|
||||
} else {
|
||||
nb_nics1 = nb_nics;
|
||||
if (nb_nics1 > NE2000_NB_MAX)
|
||||
nb_nics1 = NE2000_NB_MAX;
|
||||
for(i = 0; i < nb_nics1; i++) {
|
||||
isa_ne2000_init(ne2000_io[i], ne2000_irq[i], &nd_table[i]);
|
||||
}
|
||||
|
||||
for(i = 0; i < 2; i++) {
|
||||
isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i],
|
||||
bs_table[2 * i], bs_table[2 * i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
kbd_init();
|
||||
DMA_init(0);
|
||||
|
||||
if (audio_enabled) {
|
||||
AUD_init();
|
||||
#ifdef USE_SB16
|
||||
if (sb16_enabled)
|
||||
SB16_init ();
|
||||
#endif
|
||||
#ifdef CONFIG_ADLIB
|
||||
if (adlib_enabled)
|
||||
Adlib_init ();
|
||||
#endif
|
||||
#ifdef USE_GUS
|
||||
if (gus_enabled)
|
||||
GUS_init ();
|
||||
#endif
|
||||
}
|
||||
|
||||
floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table);
|
||||
|
||||
cmos_init(ram_size, boot_device, bs_table);
|
||||
|
||||
/* must be done after all PCI devices are instanciated */
|
||||
/* XXX: should be done in the Bochs BIOS */
|
||||
if (pci_enabled) {
|
||||
pci_bios_init();
|
||||
}
|
||||
}
|
||||
+705
@@ -0,0 +1,705 @@
|
||||
/*
|
||||
* QEMU PC keyboard emulation
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
/* debug PC keyboard */
|
||||
//#define DEBUG_KBD
|
||||
|
||||
/* debug PC keyboard : only mouse */
|
||||
//#define DEBUG_MOUSE
|
||||
|
||||
/* Keyboard Controller Commands */
|
||||
#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
|
||||
#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
|
||||
#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
|
||||
#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
|
||||
#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
|
||||
#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
|
||||
#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
|
||||
#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
|
||||
#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
|
||||
#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
|
||||
#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */
|
||||
#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */
|
||||
#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */
|
||||
#define KBD_CCMD_WRITE_OBUF 0xD2
|
||||
#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
|
||||
initiated by the auxiliary device */
|
||||
#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
|
||||
#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */
|
||||
#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */
|
||||
#define KBD_CCMD_RESET 0xFE
|
||||
|
||||
/* Keyboard Commands */
|
||||
#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
|
||||
#define KBD_CMD_ECHO 0xEE
|
||||
#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */
|
||||
#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
|
||||
#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
|
||||
#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */
|
||||
#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */
|
||||
#define KBD_CMD_RESET 0xFF /* Reset */
|
||||
|
||||
/* Keyboard Replies */
|
||||
#define KBD_REPLY_POR 0xAA /* Power on reset */
|
||||
#define KBD_REPLY_ACK 0xFA /* Command ACK */
|
||||
#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
|
||||
|
||||
/* Status Register Bits */
|
||||
#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
|
||||
#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
|
||||
#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
|
||||
#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
|
||||
#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
|
||||
#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
|
||||
#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
|
||||
#define KBD_STAT_PERR 0x80 /* Parity error */
|
||||
|
||||
/* Controller Mode Register Bits */
|
||||
#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
|
||||
#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
|
||||
#define KBD_MODE_SYS 0x04 /* The system flag (?) */
|
||||
#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
|
||||
#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
|
||||
#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
|
||||
#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
|
||||
#define KBD_MODE_RFU 0x80
|
||||
|
||||
/* Mouse Commands */
|
||||
#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
|
||||
#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
|
||||
#define AUX_SET_RES 0xE8 /* Set resolution */
|
||||
#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
|
||||
#define AUX_SET_STREAM 0xEA /* Set stream mode */
|
||||
#define AUX_POLL 0xEB /* Poll */
|
||||
#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */
|
||||
#define AUX_SET_WRAP 0xEE /* Set wrap mode */
|
||||
#define AUX_SET_REMOTE 0xF0 /* Set remote mode */
|
||||
#define AUX_GET_TYPE 0xF2 /* Get type */
|
||||
#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
|
||||
#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
|
||||
#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
|
||||
#define AUX_SET_DEFAULT 0xF6
|
||||
#define AUX_RESET 0xFF /* Reset aux device */
|
||||
#define AUX_ACK 0xFA /* Command byte ACK. */
|
||||
|
||||
#define MOUSE_STATUS_REMOTE 0x40
|
||||
#define MOUSE_STATUS_ENABLED 0x20
|
||||
#define MOUSE_STATUS_SCALE21 0x10
|
||||
|
||||
#define KBD_QUEUE_SIZE 256
|
||||
|
||||
typedef struct {
|
||||
uint8_t aux[KBD_QUEUE_SIZE];
|
||||
uint8_t data[KBD_QUEUE_SIZE];
|
||||
int rptr, wptr, count;
|
||||
} KBDQueue;
|
||||
|
||||
typedef struct KBDState {
|
||||
KBDQueue queue;
|
||||
uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
|
||||
uint8_t status;
|
||||
uint8_t mode;
|
||||
/* keyboard state */
|
||||
int kbd_write_cmd;
|
||||
int scan_enabled;
|
||||
/* mouse state */
|
||||
int mouse_write_cmd;
|
||||
uint8_t mouse_status;
|
||||
uint8_t mouse_resolution;
|
||||
uint8_t mouse_sample_rate;
|
||||
uint8_t mouse_wrap;
|
||||
uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
|
||||
uint8_t mouse_detect_state;
|
||||
int mouse_dx; /* current values, needed for 'poll' mode */
|
||||
int mouse_dy;
|
||||
int mouse_dz;
|
||||
uint8_t mouse_buttons;
|
||||
} KBDState;
|
||||
|
||||
KBDState kbd_state;
|
||||
|
||||
/* update irq and KBD_STAT_[MOUSE_]OBF */
|
||||
/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
|
||||
incorrect, but it avoids having to simulate exact delays */
|
||||
static void kbd_update_irq(KBDState *s)
|
||||
{
|
||||
KBDQueue *q = &s->queue;
|
||||
int irq12_level, irq1_level;
|
||||
|
||||
irq1_level = 0;
|
||||
irq12_level = 0;
|
||||
s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
|
||||
if (q->count != 0) {
|
||||
s->status |= KBD_STAT_OBF;
|
||||
if (q->aux[q->rptr]) {
|
||||
s->status |= KBD_STAT_MOUSE_OBF;
|
||||
if (s->mode & KBD_MODE_MOUSE_INT)
|
||||
irq12_level = 1;
|
||||
} else {
|
||||
if ((s->mode & KBD_MODE_KBD_INT) &&
|
||||
!(s->mode & KBD_MODE_DISABLE_KBD))
|
||||
irq1_level = 1;
|
||||
}
|
||||
}
|
||||
pic_set_irq(1, irq1_level);
|
||||
pic_set_irq(12, irq12_level);
|
||||
}
|
||||
|
||||
static void kbd_queue(KBDState *s, int b, int aux)
|
||||
{
|
||||
KBDQueue *q = &s->queue;
|
||||
|
||||
#if defined(DEBUG_MOUSE) || defined(DEBUG_KBD)
|
||||
if (aux)
|
||||
printf("mouse event: 0x%02x\n", b);
|
||||
#ifdef DEBUG_KBD
|
||||
else
|
||||
printf("kbd event: 0x%02x\n", b);
|
||||
#endif
|
||||
#endif
|
||||
if (q->count >= KBD_QUEUE_SIZE)
|
||||
return;
|
||||
q->aux[q->wptr] = aux;
|
||||
q->data[q->wptr] = b;
|
||||
if (++q->wptr == KBD_QUEUE_SIZE)
|
||||
q->wptr = 0;
|
||||
q->count++;
|
||||
kbd_update_irq(s);
|
||||
}
|
||||
|
||||
static void pc_kbd_put_keycode(void *opaque, int keycode)
|
||||
{
|
||||
KBDState *s = opaque;
|
||||
kbd_queue(s, keycode, 0);
|
||||
}
|
||||
|
||||
static uint32_t kbd_read_status(void *opaque, uint32_t addr)
|
||||
{
|
||||
KBDState *s = opaque;
|
||||
int val;
|
||||
val = s->status;
|
||||
#if defined(DEBUG_KBD)
|
||||
printf("kbd: read status=0x%02x\n", val);
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
KBDState *s = opaque;
|
||||
|
||||
#ifdef DEBUG_KBD
|
||||
printf("kbd: write cmd=0x%02x\n", val);
|
||||
#endif
|
||||
switch(val) {
|
||||
case KBD_CCMD_READ_MODE:
|
||||
kbd_queue(s, s->mode, 0);
|
||||
break;
|
||||
case KBD_CCMD_WRITE_MODE:
|
||||
case KBD_CCMD_WRITE_OBUF:
|
||||
case KBD_CCMD_WRITE_AUX_OBUF:
|
||||
case KBD_CCMD_WRITE_MOUSE:
|
||||
case KBD_CCMD_WRITE_OUTPORT:
|
||||
s->write_cmd = val;
|
||||
break;
|
||||
case KBD_CCMD_MOUSE_DISABLE:
|
||||
s->mode |= KBD_MODE_DISABLE_MOUSE;
|
||||
break;
|
||||
case KBD_CCMD_MOUSE_ENABLE:
|
||||
s->mode &= ~KBD_MODE_DISABLE_MOUSE;
|
||||
break;
|
||||
case KBD_CCMD_TEST_MOUSE:
|
||||
kbd_queue(s, 0x00, 0);
|
||||
break;
|
||||
case KBD_CCMD_SELF_TEST:
|
||||
s->status |= KBD_STAT_SELFTEST;
|
||||
kbd_queue(s, 0x55, 0);
|
||||
break;
|
||||
case KBD_CCMD_KBD_TEST:
|
||||
kbd_queue(s, 0x00, 0);
|
||||
break;
|
||||
case KBD_CCMD_KBD_DISABLE:
|
||||
s->mode |= KBD_MODE_DISABLE_KBD;
|
||||
kbd_update_irq(s);
|
||||
break;
|
||||
case KBD_CCMD_KBD_ENABLE:
|
||||
s->mode &= ~KBD_MODE_DISABLE_KBD;
|
||||
kbd_update_irq(s);
|
||||
break;
|
||||
case KBD_CCMD_READ_INPORT:
|
||||
kbd_queue(s, 0x00, 0);
|
||||
break;
|
||||
case KBD_CCMD_READ_OUTPORT:
|
||||
/* XXX: check that */
|
||||
#ifdef TARGET_I386
|
||||
val = 0x01 | (((cpu_single_env->a20_mask >> 20) & 1) << 1);
|
||||
#else
|
||||
val = 0x01;
|
||||
#endif
|
||||
if (s->status & KBD_STAT_OBF)
|
||||
val |= 0x10;
|
||||
if (s->status & KBD_STAT_MOUSE_OBF)
|
||||
val |= 0x20;
|
||||
kbd_queue(s, val, 0);
|
||||
break;
|
||||
#ifdef TARGET_I386
|
||||
case KBD_CCMD_ENABLE_A20:
|
||||
cpu_x86_set_a20(cpu_single_env, 1);
|
||||
break;
|
||||
case KBD_CCMD_DISABLE_A20:
|
||||
cpu_x86_set_a20(cpu_single_env, 0);
|
||||
break;
|
||||
#endif
|
||||
case KBD_CCMD_RESET:
|
||||
qemu_system_reset_request();
|
||||
break;
|
||||
case 0xff:
|
||||
/* ignore that - I don't know what is its use */
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t kbd_read_data(void *opaque, uint32_t addr)
|
||||
{
|
||||
KBDState *s = opaque;
|
||||
KBDQueue *q;
|
||||
int val, index, aux;
|
||||
|
||||
q = &s->queue;
|
||||
if (q->count == 0) {
|
||||
/* NOTE: if no data left, we return the last keyboard one
|
||||
(needed for EMM386) */
|
||||
/* XXX: need a timer to do things correctly */
|
||||
index = q->rptr - 1;
|
||||
if (index < 0)
|
||||
index = KBD_QUEUE_SIZE - 1;
|
||||
val = q->data[index];
|
||||
} else {
|
||||
aux = q->aux[q->rptr];
|
||||
val = q->data[q->rptr];
|
||||
if (++q->rptr == KBD_QUEUE_SIZE)
|
||||
q->rptr = 0;
|
||||
q->count--;
|
||||
/* reading deasserts IRQ */
|
||||
if (aux)
|
||||
pic_set_irq(12, 0);
|
||||
else
|
||||
pic_set_irq(1, 0);
|
||||
}
|
||||
/* reassert IRQs if data left */
|
||||
kbd_update_irq(s);
|
||||
#ifdef DEBUG_KBD
|
||||
printf("kbd: read data=0x%02x\n", val);
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
static void kbd_reset_keyboard(KBDState *s)
|
||||
{
|
||||
s->scan_enabled = 1;
|
||||
}
|
||||
|
||||
static void kbd_write_keyboard(KBDState *s, int val)
|
||||
{
|
||||
switch(s->kbd_write_cmd) {
|
||||
default:
|
||||
case -1:
|
||||
switch(val) {
|
||||
case 0x00:
|
||||
kbd_queue(s, KBD_REPLY_ACK, 0);
|
||||
break;
|
||||
case 0x05:
|
||||
kbd_queue(s, KBD_REPLY_RESEND, 0);
|
||||
break;
|
||||
case KBD_CMD_GET_ID:
|
||||
kbd_queue(s, KBD_REPLY_ACK, 0);
|
||||
kbd_queue(s, 0xab, 0);
|
||||
kbd_queue(s, 0x83, 0);
|
||||
break;
|
||||
case KBD_CMD_ECHO:
|
||||
kbd_queue(s, KBD_CMD_ECHO, 0);
|
||||
break;
|
||||
case KBD_CMD_ENABLE:
|
||||
s->scan_enabled = 1;
|
||||
kbd_queue(s, KBD_REPLY_ACK, 0);
|
||||
break;
|
||||
case KBD_CMD_SET_LEDS:
|
||||
case KBD_CMD_SET_RATE:
|
||||
s->kbd_write_cmd = val;
|
||||
kbd_queue(s, KBD_REPLY_ACK, 0);
|
||||
break;
|
||||
case KBD_CMD_RESET_DISABLE:
|
||||
kbd_reset_keyboard(s);
|
||||
s->scan_enabled = 0;
|
||||
kbd_queue(s, KBD_REPLY_ACK, 0);
|
||||
break;
|
||||
case KBD_CMD_RESET_ENABLE:
|
||||
kbd_reset_keyboard(s);
|
||||
s->scan_enabled = 1;
|
||||
kbd_queue(s, KBD_REPLY_ACK, 0);
|
||||
break;
|
||||
case KBD_CMD_RESET:
|
||||
kbd_reset_keyboard(s);
|
||||
kbd_queue(s, KBD_REPLY_ACK, 0);
|
||||
kbd_queue(s, KBD_REPLY_POR, 0);
|
||||
break;
|
||||
default:
|
||||
kbd_queue(s, KBD_REPLY_ACK, 0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case KBD_CMD_SET_LEDS:
|
||||
kbd_queue(s, KBD_REPLY_ACK, 0);
|
||||
s->kbd_write_cmd = -1;
|
||||
break;
|
||||
case KBD_CMD_SET_RATE:
|
||||
kbd_queue(s, KBD_REPLY_ACK, 0);
|
||||
s->kbd_write_cmd = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void kbd_mouse_send_packet(KBDState *s)
|
||||
{
|
||||
unsigned int b;
|
||||
int dx1, dy1, dz1;
|
||||
|
||||
dx1 = s->mouse_dx;
|
||||
dy1 = s->mouse_dy;
|
||||
dz1 = s->mouse_dz;
|
||||
/* XXX: increase range to 8 bits ? */
|
||||
if (dx1 > 127)
|
||||
dx1 = 127;
|
||||
else if (dx1 < -127)
|
||||
dx1 = -127;
|
||||
if (dy1 > 127)
|
||||
dy1 = 127;
|
||||
else if (dy1 < -127)
|
||||
dy1 = -127;
|
||||
b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
|
||||
kbd_queue(s, b, 1);
|
||||
kbd_queue(s, dx1 & 0xff, 1);
|
||||
kbd_queue(s, dy1 & 0xff, 1);
|
||||
/* extra byte for IMPS/2 or IMEX */
|
||||
switch(s->mouse_type) {
|
||||
default:
|
||||
break;
|
||||
case 3:
|
||||
if (dz1 > 127)
|
||||
dz1 = 127;
|
||||
else if (dz1 < -127)
|
||||
dz1 = -127;
|
||||
kbd_queue(s, dz1 & 0xff, 1);
|
||||
break;
|
||||
case 4:
|
||||
if (dz1 > 7)
|
||||
dz1 = 7;
|
||||
else if (dz1 < -7)
|
||||
dz1 = -7;
|
||||
b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
|
||||
kbd_queue(s, b, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
/* update deltas */
|
||||
s->mouse_dx -= dx1;
|
||||
s->mouse_dy -= dy1;
|
||||
s->mouse_dz -= dz1;
|
||||
}
|
||||
|
||||
static void pc_kbd_mouse_event(void *opaque,
|
||||
int dx, int dy, int dz, int buttons_state)
|
||||
{
|
||||
KBDState *s = opaque;
|
||||
|
||||
/* check if deltas are recorded when disabled */
|
||||
if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
|
||||
return;
|
||||
|
||||
s->mouse_dx += dx;
|
||||
s->mouse_dy -= dy;
|
||||
s->mouse_dz += dz;
|
||||
/* XXX: SDL sometimes generates nul events: we delete them */
|
||||
if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
|
||||
s->mouse_buttons == buttons_state)
|
||||
return;
|
||||
s->mouse_buttons = buttons_state;
|
||||
|
||||
if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
|
||||
(s->queue.count < (KBD_QUEUE_SIZE - 16))) {
|
||||
for(;;) {
|
||||
/* if not remote, send event. Multiple events are sent if
|
||||
too big deltas */
|
||||
kbd_mouse_send_packet(s);
|
||||
if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void kbd_write_mouse(KBDState *s, int val)
|
||||
{
|
||||
#ifdef DEBUG_MOUSE
|
||||
printf("kbd: write mouse 0x%02x\n", val);
|
||||
#endif
|
||||
switch(s->mouse_write_cmd) {
|
||||
default:
|
||||
case -1:
|
||||
/* mouse command */
|
||||
if (s->mouse_wrap) {
|
||||
if (val == AUX_RESET_WRAP) {
|
||||
s->mouse_wrap = 0;
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
return;
|
||||
} else if (val != AUX_RESET) {
|
||||
kbd_queue(s, val, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch(val) {
|
||||
case AUX_SET_SCALE11:
|
||||
s->mouse_status &= ~MOUSE_STATUS_SCALE21;
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
break;
|
||||
case AUX_SET_SCALE21:
|
||||
s->mouse_status |= MOUSE_STATUS_SCALE21;
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
break;
|
||||
case AUX_SET_STREAM:
|
||||
s->mouse_status &= ~MOUSE_STATUS_REMOTE;
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
break;
|
||||
case AUX_SET_WRAP:
|
||||
s->mouse_wrap = 1;
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
break;
|
||||
case AUX_SET_REMOTE:
|
||||
s->mouse_status |= MOUSE_STATUS_REMOTE;
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
break;
|
||||
case AUX_GET_TYPE:
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
kbd_queue(s, s->mouse_type, 1);
|
||||
break;
|
||||
case AUX_SET_RES:
|
||||
case AUX_SET_SAMPLE:
|
||||
s->mouse_write_cmd = val;
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
break;
|
||||
case AUX_GET_SCALE:
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
kbd_queue(s, s->mouse_status, 1);
|
||||
kbd_queue(s, s->mouse_resolution, 1);
|
||||
kbd_queue(s, s->mouse_sample_rate, 1);
|
||||
break;
|
||||
case AUX_POLL:
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
kbd_mouse_send_packet(s);
|
||||
break;
|
||||
case AUX_ENABLE_DEV:
|
||||
s->mouse_status |= MOUSE_STATUS_ENABLED;
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
break;
|
||||
case AUX_DISABLE_DEV:
|
||||
s->mouse_status &= ~MOUSE_STATUS_ENABLED;
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
break;
|
||||
case AUX_SET_DEFAULT:
|
||||
s->mouse_sample_rate = 100;
|
||||
s->mouse_resolution = 2;
|
||||
s->mouse_status = 0;
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
break;
|
||||
case AUX_RESET:
|
||||
s->mouse_sample_rate = 100;
|
||||
s->mouse_resolution = 2;
|
||||
s->mouse_status = 0;
|
||||
s->mouse_type = 0;
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
kbd_queue(s, 0xaa, 1);
|
||||
kbd_queue(s, s->mouse_type, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AUX_SET_SAMPLE:
|
||||
s->mouse_sample_rate = val;
|
||||
/* detect IMPS/2 or IMEX */
|
||||
switch(s->mouse_detect_state) {
|
||||
default:
|
||||
case 0:
|
||||
if (val == 200)
|
||||
s->mouse_detect_state = 1;
|
||||
break;
|
||||
case 1:
|
||||
if (val == 100)
|
||||
s->mouse_detect_state = 2;
|
||||
else if (val == 200)
|
||||
s->mouse_detect_state = 3;
|
||||
else
|
||||
s->mouse_detect_state = 0;
|
||||
break;
|
||||
case 2:
|
||||
if (val == 80)
|
||||
s->mouse_type = 3; /* IMPS/2 */
|
||||
s->mouse_detect_state = 0;
|
||||
break;
|
||||
case 3:
|
||||
if (val == 80)
|
||||
s->mouse_type = 4; /* IMEX */
|
||||
s->mouse_detect_state = 0;
|
||||
break;
|
||||
}
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
s->mouse_write_cmd = -1;
|
||||
break;
|
||||
case AUX_SET_RES:
|
||||
s->mouse_resolution = val;
|
||||
kbd_queue(s, AUX_ACK, 1);
|
||||
s->mouse_write_cmd = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
KBDState *s = opaque;
|
||||
|
||||
#ifdef DEBUG_KBD
|
||||
printf("kbd: write data=0x%02x\n", val);
|
||||
#endif
|
||||
|
||||
switch(s->write_cmd) {
|
||||
case 0:
|
||||
kbd_write_keyboard(s, val);
|
||||
break;
|
||||
case KBD_CCMD_WRITE_MODE:
|
||||
s->mode = val;
|
||||
kbd_update_irq(s);
|
||||
break;
|
||||
case KBD_CCMD_WRITE_OBUF:
|
||||
kbd_queue(s, val, 0);
|
||||
break;
|
||||
case KBD_CCMD_WRITE_AUX_OBUF:
|
||||
kbd_queue(s, val, 1);
|
||||
break;
|
||||
case KBD_CCMD_WRITE_OUTPORT:
|
||||
#ifdef TARGET_I386
|
||||
cpu_x86_set_a20(cpu_single_env, (val >> 1) & 1);
|
||||
#endif
|
||||
if (!(val & 1)) {
|
||||
qemu_system_reset_request();
|
||||
}
|
||||
break;
|
||||
case KBD_CCMD_WRITE_MOUSE:
|
||||
kbd_write_mouse(s, val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
s->write_cmd = 0;
|
||||
}
|
||||
|
||||
static void kbd_reset(void *opaque)
|
||||
{
|
||||
KBDState *s = opaque;
|
||||
KBDQueue *q;
|
||||
|
||||
s->kbd_write_cmd = -1;
|
||||
s->mouse_write_cmd = -1;
|
||||
s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
|
||||
s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
|
||||
q = &s->queue;
|
||||
q->rptr = 0;
|
||||
q->wptr = 0;
|
||||
q->count = 0;
|
||||
}
|
||||
|
||||
static void kbd_save(QEMUFile* f, void* opaque)
|
||||
{
|
||||
KBDState *s = (KBDState*)opaque;
|
||||
|
||||
qemu_put_8s(f, &s->write_cmd);
|
||||
qemu_put_8s(f, &s->status);
|
||||
qemu_put_8s(f, &s->mode);
|
||||
qemu_put_be32s(f, &s->kbd_write_cmd);
|
||||
qemu_put_be32s(f, &s->scan_enabled);
|
||||
qemu_put_be32s(f, &s->mouse_write_cmd);
|
||||
qemu_put_8s(f, &s->mouse_status);
|
||||
qemu_put_8s(f, &s->mouse_resolution);
|
||||
qemu_put_8s(f, &s->mouse_sample_rate);
|
||||
qemu_put_8s(f, &s->mouse_wrap);
|
||||
qemu_put_8s(f, &s->mouse_type);
|
||||
qemu_put_8s(f, &s->mouse_detect_state);
|
||||
qemu_put_be32s(f, &s->mouse_dx);
|
||||
qemu_put_be32s(f, &s->mouse_dy);
|
||||
qemu_put_be32s(f, &s->mouse_dz);
|
||||
qemu_put_8s(f, &s->mouse_buttons);
|
||||
}
|
||||
|
||||
static int kbd_load(QEMUFile* f, void* opaque, int version_id)
|
||||
{
|
||||
KBDState *s = (KBDState*)opaque;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
qemu_get_8s(f, &s->write_cmd);
|
||||
qemu_get_8s(f, &s->status);
|
||||
qemu_get_8s(f, &s->mode);
|
||||
qemu_get_be32s(f, &s->kbd_write_cmd);
|
||||
qemu_get_be32s(f, &s->scan_enabled);
|
||||
qemu_get_be32s(f, &s->mouse_write_cmd);
|
||||
qemu_get_8s(f, &s->mouse_status);
|
||||
qemu_get_8s(f, &s->mouse_resolution);
|
||||
qemu_get_8s(f, &s->mouse_sample_rate);
|
||||
qemu_get_8s(f, &s->mouse_wrap);
|
||||
qemu_get_8s(f, &s->mouse_type);
|
||||
qemu_get_8s(f, &s->mouse_detect_state);
|
||||
qemu_get_be32s(f, &s->mouse_dx);
|
||||
qemu_get_be32s(f, &s->mouse_dy);
|
||||
qemu_get_be32s(f, &s->mouse_dz);
|
||||
qemu_get_8s(f, &s->mouse_buttons);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kbd_init(void)
|
||||
{
|
||||
KBDState *s = &kbd_state;
|
||||
|
||||
kbd_reset(s);
|
||||
register_savevm("pckbd", 0, 1, kbd_save, kbd_load, s);
|
||||
register_ioport_read(0x60, 1, 1, kbd_read_data, s);
|
||||
register_ioport_write(0x60, 1, 1, kbd_write_data, s);
|
||||
register_ioport_read(0x64, 1, 1, kbd_read_status, s);
|
||||
register_ioport_write(0x64, 1, 1, kbd_write_command, s);
|
||||
|
||||
qemu_add_kbd_event_handler(pc_kbd_put_keycode, s);
|
||||
qemu_add_mouse_event_handler(pc_kbd_mouse_event, s);
|
||||
qemu_register_reset(kbd_reset, s);
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* QEMU generic PPC hardware System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2004 Jocelyn Mayer
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
#include "m48t59.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* PPC time base and decrementer emulation */
|
||||
//#define DEBUG_TB
|
||||
|
||||
struct ppc_tb_t {
|
||||
/* Time base management */
|
||||
int64_t tb_offset; /* Compensation */
|
||||
uint32_t tb_freq; /* TB frequency */
|
||||
/* Decrementer management */
|
||||
uint64_t decr_next; /* Tick for next decr interrupt */
|
||||
struct QEMUTimer *decr_timer;
|
||||
};
|
||||
|
||||
static inline uint64_t cpu_ppc_get_tb (ppc_tb_t *tb_env)
|
||||
{
|
||||
/* TB time in tb periods */
|
||||
return muldiv64(qemu_get_clock(vm_clock) + tb_env->tb_offset,
|
||||
tb_env->tb_freq, ticks_per_sec);
|
||||
}
|
||||
|
||||
uint32_t cpu_ppc_load_tbl (CPUState *env)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
uint64_t tb;
|
||||
|
||||
tb = cpu_ppc_get_tb(tb_env);
|
||||
#ifdef DEBUG_TB
|
||||
{
|
||||
static int last_time;
|
||||
int now;
|
||||
now = time(NULL);
|
||||
if (last_time != now) {
|
||||
last_time = now;
|
||||
printf("%s: tb=0x%016lx %d %08lx\n",
|
||||
__func__, tb, now, tb_env->tb_offset);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return tb & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
uint32_t cpu_ppc_load_tbu (CPUState *env)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
uint64_t tb;
|
||||
|
||||
tb = cpu_ppc_get_tb(tb_env);
|
||||
#ifdef DEBUG_TB
|
||||
printf("%s: tb=0x%016lx\n", __func__, tb);
|
||||
#endif
|
||||
return tb >> 32;
|
||||
}
|
||||
|
||||
static void cpu_ppc_store_tb (ppc_tb_t *tb_env, uint64_t value)
|
||||
{
|
||||
tb_env->tb_offset = muldiv64(value, ticks_per_sec, tb_env->tb_freq)
|
||||
- qemu_get_clock(vm_clock);
|
||||
#ifdef DEBUG_TB
|
||||
printf("%s: tb=0x%016lx offset=%08x\n", __func__, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
|
||||
cpu_ppc_store_tb(tb_env,
|
||||
((uint64_t)value << 32) | cpu_ppc_load_tbl(env));
|
||||
}
|
||||
|
||||
void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
|
||||
cpu_ppc_store_tb(tb_env,
|
||||
((uint64_t)cpu_ppc_load_tbu(env) << 32) | value);
|
||||
}
|
||||
|
||||
uint32_t cpu_ppc_load_decr (CPUState *env)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
uint32_t decr;
|
||||
|
||||
decr = muldiv64(tb_env->decr_next - qemu_get_clock(vm_clock),
|
||||
tb_env->tb_freq, ticks_per_sec);
|
||||
#if defined(DEBUG_TB)
|
||||
printf("%s: 0x%08x\n", __func__, decr);
|
||||
#endif
|
||||
|
||||
return decr;
|
||||
}
|
||||
|
||||
/* When decrementer expires,
|
||||
* all we need to do is generate or queue a CPU exception
|
||||
*/
|
||||
static inline void cpu_ppc_decr_excp (CPUState *env)
|
||||
{
|
||||
/* Raise it */
|
||||
#ifdef DEBUG_TB
|
||||
printf("raise decrementer exception\n");
|
||||
#endif
|
||||
cpu_interrupt(env, CPU_INTERRUPT_TIMER);
|
||||
}
|
||||
|
||||
static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr,
|
||||
uint32_t value, int is_excp)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
uint64_t now, next;
|
||||
|
||||
#ifdef DEBUG_TB
|
||||
printf("%s: 0x%08x => 0x%08x\n", __func__, decr, value);
|
||||
#endif
|
||||
now = qemu_get_clock(vm_clock);
|
||||
next = now + muldiv64(value, ticks_per_sec, tb_env->tb_freq);
|
||||
if (is_excp)
|
||||
next += tb_env->decr_next - now;
|
||||
if (next == now)
|
||||
next++;
|
||||
tb_env->decr_next = next;
|
||||
/* Adjust timer */
|
||||
qemu_mod_timer(tb_env->decr_timer, next);
|
||||
/* If we set a negative value and the decrementer was positive,
|
||||
* raise an exception.
|
||||
*/
|
||||
if ((value & 0x80000000) && !(decr & 0x80000000))
|
||||
cpu_ppc_decr_excp(env);
|
||||
}
|
||||
|
||||
void cpu_ppc_store_decr (CPUState *env, uint32_t value)
|
||||
{
|
||||
_cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0);
|
||||
}
|
||||
|
||||
static void cpu_ppc_decr_cb (void *opaque)
|
||||
{
|
||||
_cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1);
|
||||
}
|
||||
|
||||
/* Set up (once) timebase frequency (in Hz) */
|
||||
ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq)
|
||||
{
|
||||
ppc_tb_t *tb_env;
|
||||
|
||||
tb_env = qemu_mallocz(sizeof(ppc_tb_t));
|
||||
if (tb_env == NULL)
|
||||
return NULL;
|
||||
env->tb_env = tb_env;
|
||||
if (tb_env->tb_freq == 0 || 1) {
|
||||
tb_env->tb_freq = freq;
|
||||
/* Create new timer */
|
||||
tb_env->decr_timer =
|
||||
qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env);
|
||||
/* There is a bug in 2.4 kernels:
|
||||
* if a decrementer exception is pending when it enables msr_ee,
|
||||
* it's not ready to handle it...
|
||||
*/
|
||||
_cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0);
|
||||
}
|
||||
|
||||
return tb_env;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*****************************************************************************/
|
||||
/* Handle system reset (for now, just stop emulation) */
|
||||
void cpu_ppc_reset (CPUState *env)
|
||||
{
|
||||
printf("Reset asked... Stop emulation\n");
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void PPC_io_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
cpu_outb(NULL, addr & 0xffff, value);
|
||||
}
|
||||
|
||||
static uint32_t PPC_io_readb (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
uint32_t ret = cpu_inb(NULL, addr & 0xffff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void PPC_io_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
value = bswap16(value);
|
||||
#endif
|
||||
cpu_outw(NULL, addr & 0xffff, value);
|
||||
}
|
||||
|
||||
static uint32_t PPC_io_readw (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
uint32_t ret = cpu_inw(NULL, addr & 0xffff);
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
ret = bswap16(ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void PPC_io_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
value = bswap32(value);
|
||||
#endif
|
||||
cpu_outl(NULL, addr & 0xffff, value);
|
||||
}
|
||||
|
||||
static uint32_t PPC_io_readl (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
uint32_t ret = cpu_inl(NULL, addr & 0xffff);
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
ret = bswap32(ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
CPUWriteMemoryFunc *PPC_io_write[] = {
|
||||
&PPC_io_writeb,
|
||||
&PPC_io_writew,
|
||||
&PPC_io_writel,
|
||||
};
|
||||
|
||||
CPUReadMemoryFunc *PPC_io_read[] = {
|
||||
&PPC_io_readb,
|
||||
&PPC_io_readw,
|
||||
&PPC_io_readl,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Debug port */
|
||||
void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
addr &= 0xF;
|
||||
switch (addr) {
|
||||
case 0:
|
||||
printf("%c", val);
|
||||
break;
|
||||
case 1:
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
break;
|
||||
case 2:
|
||||
printf("Set loglevel to %04x\n", val);
|
||||
cpu_set_log(val | 0x100);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* NVRAM helpers */
|
||||
void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value)
|
||||
{
|
||||
m48t59_set_addr(nvram, addr);
|
||||
m48t59_write(nvram, value);
|
||||
}
|
||||
|
||||
uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr)
|
||||
{
|
||||
m48t59_set_addr(nvram, addr);
|
||||
return m48t59_read(nvram);
|
||||
}
|
||||
|
||||
void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value)
|
||||
{
|
||||
m48t59_set_addr(nvram, addr);
|
||||
m48t59_write(nvram, value >> 8);
|
||||
m48t59_set_addr(nvram, addr + 1);
|
||||
m48t59_write(nvram, value & 0xFF);
|
||||
}
|
||||
|
||||
uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr)
|
||||
{
|
||||
uint16_t tmp;
|
||||
|
||||
m48t59_set_addr(nvram, addr);
|
||||
tmp = m48t59_read(nvram) << 8;
|
||||
m48t59_set_addr(nvram, addr + 1);
|
||||
tmp |= m48t59_read(nvram);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value)
|
||||
{
|
||||
m48t59_set_addr(nvram, addr);
|
||||
m48t59_write(nvram, value >> 24);
|
||||
m48t59_set_addr(nvram, addr + 1);
|
||||
m48t59_write(nvram, (value >> 16) & 0xFF);
|
||||
m48t59_set_addr(nvram, addr + 2);
|
||||
m48t59_write(nvram, (value >> 8) & 0xFF);
|
||||
m48t59_set_addr(nvram, addr + 3);
|
||||
m48t59_write(nvram, value & 0xFF);
|
||||
}
|
||||
|
||||
uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr)
|
||||
{
|
||||
uint32_t tmp;
|
||||
|
||||
m48t59_set_addr(nvram, addr);
|
||||
tmp = m48t59_read(nvram) << 24;
|
||||
m48t59_set_addr(nvram, addr + 1);
|
||||
tmp |= m48t59_read(nvram) << 16;
|
||||
m48t59_set_addr(nvram, addr + 2);
|
||||
tmp |= m48t59_read(nvram) << 8;
|
||||
m48t59_set_addr(nvram, addr + 3);
|
||||
tmp |= m48t59_read(nvram);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void NVRAM_set_string (m48t59_t *nvram, uint32_t addr,
|
||||
const unsigned char *str, uint32_t max)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max && str[i] != '\0'; i++) {
|
||||
m48t59_set_addr(nvram, addr + i);
|
||||
m48t59_write(nvram, str[i]);
|
||||
}
|
||||
m48t59_set_addr(nvram, addr + max - 1);
|
||||
m48t59_write(nvram, '\0');
|
||||
}
|
||||
|
||||
int NVRAM_get_string (m48t59_t *nvram, uint8_t *dst, uint16_t addr, int max)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(dst, 0, max);
|
||||
for (i = 0; i < max; i++) {
|
||||
dst[i] = NVRAM_get_byte(nvram, addr + i);
|
||||
if (dst[i] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value)
|
||||
{
|
||||
uint16_t tmp;
|
||||
uint16_t pd, pd1, pd2;
|
||||
|
||||
tmp = prev >> 8;
|
||||
pd = prev ^ value;
|
||||
pd1 = pd & 0x000F;
|
||||
pd2 = ((pd >> 4) & 0x000F) ^ pd1;
|
||||
tmp ^= (pd1 << 3) | (pd1 << 8);
|
||||
tmp ^= pd2 | (pd2 << 7) | (pd2 << 12);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
uint16_t NVRAM_compute_crc (m48t59_t *nvram, uint32_t start, uint32_t count)
|
||||
{
|
||||
uint32_t i;
|
||||
uint16_t crc = 0xFFFF;
|
||||
int odd;
|
||||
|
||||
odd = count & 1;
|
||||
count &= ~1;
|
||||
for (i = 0; i != count; i++) {
|
||||
crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i));
|
||||
}
|
||||
if (odd) {
|
||||
crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
#define CMDLINE_ADDR 0x017ff000
|
||||
|
||||
int PPC_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size,
|
||||
const unsigned char *arch,
|
||||
uint32_t RAM_size, int boot_device,
|
||||
uint32_t kernel_image, uint32_t kernel_size,
|
||||
const char *cmdline,
|
||||
uint32_t initrd_image, uint32_t initrd_size,
|
||||
uint32_t NVRAM_image,
|
||||
int width, int height, int depth)
|
||||
{
|
||||
uint16_t crc;
|
||||
|
||||
/* Set parameters for Open Hack'Ware BIOS */
|
||||
NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16);
|
||||
NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */
|
||||
NVRAM_set_word(nvram, 0x14, NVRAM_size);
|
||||
NVRAM_set_string(nvram, 0x20, arch, 16);
|
||||
NVRAM_set_lword(nvram, 0x30, RAM_size);
|
||||
NVRAM_set_byte(nvram, 0x34, boot_device);
|
||||
NVRAM_set_lword(nvram, 0x38, kernel_image);
|
||||
NVRAM_set_lword(nvram, 0x3C, kernel_size);
|
||||
if (cmdline) {
|
||||
/* XXX: put the cmdline in NVRAM too ? */
|
||||
strcpy(phys_ram_base + CMDLINE_ADDR, cmdline);
|
||||
NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR);
|
||||
NVRAM_set_lword(nvram, 0x44, strlen(cmdline));
|
||||
} else {
|
||||
NVRAM_set_lword(nvram, 0x40, 0);
|
||||
NVRAM_set_lword(nvram, 0x44, 0);
|
||||
}
|
||||
NVRAM_set_lword(nvram, 0x48, initrd_image);
|
||||
NVRAM_set_lword(nvram, 0x4C, initrd_size);
|
||||
NVRAM_set_lword(nvram, 0x50, NVRAM_image);
|
||||
|
||||
NVRAM_set_word(nvram, 0x54, width);
|
||||
NVRAM_set_word(nvram, 0x56, height);
|
||||
NVRAM_set_word(nvram, 0x58, depth);
|
||||
crc = NVRAM_compute_crc(nvram, 0x00, 0xF8);
|
||||
NVRAM_set_word(nvram, 0xFC, crc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
void ppc_init (int ram_size, int vga_ram_size, int boot_device,
|
||||
DisplayState *ds, const char **fd_filename, int snapshot,
|
||||
const char *kernel_filename, const char *kernel_cmdline,
|
||||
const char *initrd_filename)
|
||||
{
|
||||
if (prep_enabled) {
|
||||
ppc_prep_init(ram_size, vga_ram_size, boot_device, ds, fd_filename,
|
||||
snapshot, kernel_filename, kernel_cmdline,
|
||||
initrd_filename);
|
||||
} else {
|
||||
ppc_chrp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename,
|
||||
snapshot, kernel_filename, kernel_cmdline,
|
||||
initrd_filename);
|
||||
}
|
||||
/* Special port to get debug messages from Open-Firmware */
|
||||
register_ioport_write(0x0F00, 4, 1, &PPC_debug_write, NULL);
|
||||
}
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* QEMU PPC CHRP/PMAC hardware System Emulator
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
#define BIOS_FILENAME "ppc_rom.bin"
|
||||
#define NVRAM_SIZE 0x2000
|
||||
|
||||
#define KERNEL_LOAD_ADDR 0x01000000
|
||||
#define INITRD_LOAD_ADDR 0x01800000
|
||||
|
||||
/* MacIO devices (mapped inside the MacIO address space): CUDA, DBDMA,
|
||||
NVRAM (not implemented). */
|
||||
|
||||
static int dbdma_mem_index;
|
||||
static int cuda_mem_index;
|
||||
static int ide0_mem_index;
|
||||
static int ide1_mem_index;
|
||||
static int openpic_mem_index;
|
||||
|
||||
/* DBDMA: currently no op - should suffice right now */
|
||||
|
||||
static void dbdma_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
printf("%s: 0x%08x <= 0x%08x\n", __func__, addr, value);
|
||||
}
|
||||
|
||||
static void dbdma_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
}
|
||||
|
||||
static void dbdma_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
}
|
||||
|
||||
static uint32_t dbdma_readb (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
printf("%s: 0x%08x => 0x00000000\n", __func__, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t dbdma_readw (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t dbdma_readl (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc *dbdma_write[] = {
|
||||
&dbdma_writeb,
|
||||
&dbdma_writew,
|
||||
&dbdma_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *dbdma_read[] = {
|
||||
&dbdma_readb,
|
||||
&dbdma_readw,
|
||||
&dbdma_readl,
|
||||
};
|
||||
|
||||
static void macio_map(PCIDevice *pci_dev, int region_num,
|
||||
uint32_t addr, uint32_t size, int type)
|
||||
{
|
||||
cpu_register_physical_memory(addr + 0x08000, 0x1000, dbdma_mem_index);
|
||||
cpu_register_physical_memory(addr + 0x16000, 0x2000, cuda_mem_index);
|
||||
cpu_register_physical_memory(addr + 0x1f000, 0x1000, ide0_mem_index);
|
||||
cpu_register_physical_memory(addr + 0x20000, 0x1000, ide1_mem_index);
|
||||
cpu_register_physical_memory(addr + 0x40000, 0x40000, openpic_mem_index);
|
||||
}
|
||||
|
||||
static void macio_init(PCIBus *bus)
|
||||
{
|
||||
PCIDevice *d;
|
||||
|
||||
d = pci_register_device(bus, "macio", sizeof(PCIDevice),
|
||||
-1, NULL, NULL);
|
||||
/* Note: this code is strongly inspirated from the corresponding code
|
||||
in PearPC */
|
||||
d->config[0x00] = 0x6b; // vendor_id
|
||||
d->config[0x01] = 0x10;
|
||||
d->config[0x02] = 0x22;
|
||||
d->config[0x03] = 0x00;
|
||||
|
||||
d->config[0x0a] = 0x00; // class_sub = pci2pci
|
||||
d->config[0x0b] = 0xff; // class_base = bridge
|
||||
d->config[0x0e] = 0x00; // header_type
|
||||
|
||||
d->config[0x3d] = 0x01; // interrupt on pin 1
|
||||
|
||||
dbdma_mem_index = cpu_register_io_memory(0, dbdma_read, dbdma_write, NULL);
|
||||
|
||||
pci_register_io_region(d, 0, 0x80000,
|
||||
PCI_ADDRESS_SPACE_MEM, macio_map);
|
||||
}
|
||||
|
||||
/* PowerPC PREP hardware initialisation */
|
||||
void ppc_chrp_init(int ram_size, int vga_ram_size, int boot_device,
|
||||
DisplayState *ds, const char **fd_filename, int snapshot,
|
||||
const char *kernel_filename, const char *kernel_cmdline,
|
||||
const char *initrd_filename)
|
||||
{
|
||||
char buf[1024];
|
||||
openpic_t *openpic;
|
||||
m48t59_t *nvram;
|
||||
int PPC_io_memory;
|
||||
int ret, linux_boot, i;
|
||||
unsigned long bios_offset;
|
||||
uint32_t kernel_base, kernel_size, initrd_base, initrd_size;
|
||||
PCIBus *pci_bus;
|
||||
|
||||
linux_boot = (kernel_filename != NULL);
|
||||
|
||||
/* allocate RAM */
|
||||
cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
|
||||
|
||||
/* allocate and load BIOS */
|
||||
bios_offset = ram_size + vga_ram_size;
|
||||
snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME);
|
||||
ret = load_image(buf, phys_ram_base + bios_offset);
|
||||
if (ret != BIOS_SIZE) {
|
||||
fprintf(stderr, "qemu: could not load PPC PREP bios '%s'\n", buf);
|
||||
exit(1);
|
||||
}
|
||||
cpu_register_physical_memory((uint32_t)(-BIOS_SIZE),
|
||||
BIOS_SIZE, bios_offset | IO_MEM_ROM);
|
||||
cpu_single_env->nip = 0xfffffffc;
|
||||
|
||||
if (linux_boot) {
|
||||
kernel_base = KERNEL_LOAD_ADDR;
|
||||
/* now we can load the kernel */
|
||||
kernel_size = load_image(kernel_filename, phys_ram_base + kernel_base);
|
||||
if (kernel_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||
kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
/* load initrd */
|
||||
if (initrd_filename) {
|
||||
initrd_base = INITRD_LOAD_ADDR;
|
||||
initrd_size = load_image(initrd_filename,
|
||||
phys_ram_base + initrd_base);
|
||||
if (initrd_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
boot_device = 'm';
|
||||
} else {
|
||||
kernel_base = 0;
|
||||
kernel_size = 0;
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
/* Register CPU as a 74x/75x */
|
||||
cpu_ppc_register(cpu_single_env, 0x00080000);
|
||||
/* Set time-base frequency to 100 Mhz */
|
||||
cpu_ppc_tb_init(cpu_single_env, 100UL * 1000UL * 1000UL);
|
||||
|
||||
isa_mem_base = 0x80000000;
|
||||
pci_bus = pci_pmac_init();
|
||||
|
||||
/* Register 8 MB of ISA IO space */
|
||||
PPC_io_memory = cpu_register_io_memory(0, PPC_io_read, PPC_io_write, NULL);
|
||||
cpu_register_physical_memory(0xF2000000, 0x00800000, PPC_io_memory);
|
||||
|
||||
/* init basic PC hardware */
|
||||
vga_initialize(pci_bus, ds, phys_ram_base + ram_size, ram_size,
|
||||
vga_ram_size);
|
||||
openpic = openpic_init(NULL, &openpic_mem_index, 1);
|
||||
pci_pmac_set_openpic(pci_bus, openpic);
|
||||
|
||||
/* XXX: suppress that */
|
||||
pic_init();
|
||||
|
||||
/* XXX: use Mac Serial port */
|
||||
serial_init(0x3f8, 4, serial_hds[0]);
|
||||
|
||||
for(i = 0; i < nb_nics; i++) {
|
||||
pci_ne2000_init(pci_bus, &nd_table[i]);
|
||||
}
|
||||
|
||||
ide0_mem_index = pmac_ide_init(&bs_table[0], openpic, 0x13);
|
||||
ide1_mem_index = pmac_ide_init(&bs_table[2], openpic, 0x14);
|
||||
|
||||
/* cuda also initialize ADB */
|
||||
cuda_mem_index = cuda_init(openpic, 0x19);
|
||||
|
||||
adb_kbd_init(&adb_bus);
|
||||
adb_mouse_init(&adb_bus);
|
||||
|
||||
macio_init(pci_bus);
|
||||
|
||||
nvram = m48t59_init(8, 0xFFF04000, 0x0074, NVRAM_SIZE);
|
||||
|
||||
if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8)
|
||||
graphic_depth = 15;
|
||||
|
||||
PPC_NVRAM_set_params(nvram, NVRAM_SIZE, "CHRP", ram_size, boot_device,
|
||||
kernel_base, kernel_size,
|
||||
kernel_cmdline,
|
||||
initrd_base, initrd_size,
|
||||
/* XXX: need an option to load a NVRAM image */
|
||||
0,
|
||||
graphic_width, graphic_height, graphic_depth);
|
||||
/* No PCI init: the BIOS will do it */
|
||||
}
|
||||
+653
@@ -0,0 +1,653 @@
|
||||
/*
|
||||
* QEMU PPC PREP hardware System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2004 Jocelyn Mayer
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
//#define HARD_DEBUG_PPC_IO
|
||||
//#define DEBUG_PPC_IO
|
||||
|
||||
#define BIOS_FILENAME "ppc_rom.bin"
|
||||
#define KERNEL_LOAD_ADDR 0x01000000
|
||||
#define INITRD_LOAD_ADDR 0x01800000
|
||||
|
||||
extern int loglevel;
|
||||
extern FILE *logfile;
|
||||
|
||||
#if defined (HARD_DEBUG_PPC_IO) && !defined (DEBUG_PPC_IO)
|
||||
#define DEBUG_PPC_IO
|
||||
#endif
|
||||
|
||||
#if defined (HARD_DEBUG_PPC_IO)
|
||||
#define PPC_IO_DPRINTF(fmt, args...) \
|
||||
do { \
|
||||
if (loglevel & CPU_LOG_IOPORT) { \
|
||||
fprintf(logfile, "%s: " fmt, __func__ , ##args); \
|
||||
} else { \
|
||||
printf("%s : " fmt, __func__ , ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
#elif defined (DEBUG_PPC_IO)
|
||||
#define PPC_IO_DPRINTF(fmt, args...) \
|
||||
do { \
|
||||
if (loglevel & CPU_LOG_IOPORT) { \
|
||||
fprintf(logfile, "%s: " fmt, __func__ , ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define PPC_IO_DPRINTF(fmt, args...) do { } while (0)
|
||||
#endif
|
||||
|
||||
/* Constants for devices init */
|
||||
static const int ide_iobase[2] = { 0x1f0, 0x170 };
|
||||
static const int ide_iobase2[2] = { 0x3f6, 0x376 };
|
||||
static const int ide_irq[2] = { 13, 13 };
|
||||
|
||||
#define NE2000_NB_MAX 6
|
||||
|
||||
static uint32_t ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 };
|
||||
static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
|
||||
|
||||
//static PITState *pit;
|
||||
|
||||
/* ISA IO ports bridge */
|
||||
#define PPC_IO_BASE 0x80000000
|
||||
|
||||
/* Speaker port 0x61 */
|
||||
int speaker_data_on;
|
||||
int dummy_refresh_clock;
|
||||
|
||||
static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
#if 0
|
||||
speaker_data_on = (val >> 1) & 1;
|
||||
pit_set_gate(pit, 2, val & 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t speaker_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
#if 0
|
||||
int out;
|
||||
out = pit_get_out(pit, 2, qemu_get_clock(vm_clock));
|
||||
dummy_refresh_clock ^= 1;
|
||||
return (speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) |
|
||||
(dummy_refresh_clock << 4);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PCI intack register */
|
||||
/* Read-only register (?) */
|
||||
static void _PPC_intack_write (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
// printf("%s: 0x%08x => 0x%08x\n", __func__, addr, value);
|
||||
}
|
||||
|
||||
static inline uint32_t _PPC_intack_read (target_phys_addr_t addr)
|
||||
{
|
||||
uint32_t retval = 0;
|
||||
|
||||
if (addr == 0xBFFFFFF0)
|
||||
retval = pic_intack_read(NULL);
|
||||
// printf("%s: 0x%08x <= %d\n", __func__, addr, retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static uint32_t PPC_intack_readb (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
return _PPC_intack_read(addr);
|
||||
}
|
||||
|
||||
static uint32_t PPC_intack_readw (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
return bswap16(_PPC_intack_read(addr));
|
||||
#else
|
||||
return _PPC_intack_read(addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t PPC_intack_readl (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
return bswap32(_PPC_intack_read(addr));
|
||||
#else
|
||||
return _PPC_intack_read(addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc *PPC_intack_write[] = {
|
||||
&_PPC_intack_write,
|
||||
&_PPC_intack_write,
|
||||
&_PPC_intack_write,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *PPC_intack_read[] = {
|
||||
&PPC_intack_readb,
|
||||
&PPC_intack_readw,
|
||||
&PPC_intack_readl,
|
||||
};
|
||||
|
||||
/* PowerPC control and status registers */
|
||||
#if 0 // Not used
|
||||
static struct {
|
||||
/* IDs */
|
||||
uint32_t veni_devi;
|
||||
uint32_t revi;
|
||||
/* Control and status */
|
||||
uint32_t gcsr;
|
||||
uint32_t xcfr;
|
||||
uint32_t ct32;
|
||||
uint32_t mcsr;
|
||||
/* General purpose registers */
|
||||
uint32_t gprg[6];
|
||||
/* Exceptions */
|
||||
uint32_t feen;
|
||||
uint32_t fest;
|
||||
uint32_t fema;
|
||||
uint32_t fecl;
|
||||
uint32_t eeen;
|
||||
uint32_t eest;
|
||||
uint32_t eecl;
|
||||
uint32_t eeint;
|
||||
uint32_t eemck0;
|
||||
uint32_t eemck1;
|
||||
/* Error diagnostic */
|
||||
} XCSR;
|
||||
|
||||
static void PPC_XCSR_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
printf("%s: 0x%08lx => 0x%08x\n", __func__, (long)addr, value);
|
||||
}
|
||||
|
||||
static void PPC_XCSR_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
value = bswap16(value);
|
||||
#endif
|
||||
printf("%s: 0x%08lx => 0x%08x\n", __func__, (long)addr, value);
|
||||
}
|
||||
|
||||
static void PPC_XCSR_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
value = bswap32(value);
|
||||
#endif
|
||||
printf("%s: 0x%08lx => 0x%08x\n", __func__, (long)addr, value);
|
||||
}
|
||||
|
||||
static uint32_t PPC_XCSR_readb (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
uint32_t retval = 0;
|
||||
|
||||
printf("%s: 0x%08lx <= %d\n", __func__, (long)addr, retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static uint32_t PPC_XCSR_readw (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
uint32_t retval = 0;
|
||||
|
||||
printf("%s: 0x%08lx <= %d\n", __func__, (long)addr, retval);
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
retval = bswap16(retval);
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static uint32_t PPC_XCSR_readl (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
uint32_t retval = 0;
|
||||
|
||||
printf("%s: 0x%08lx <= %d\n", __func__, (long)addr, retval);
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
retval = bswap32(retval);
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc *PPC_XCSR_write[] = {
|
||||
&PPC_XCSR_writeb,
|
||||
&PPC_XCSR_writew,
|
||||
&PPC_XCSR_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *PPC_XCSR_read[] = {
|
||||
&PPC_XCSR_readb,
|
||||
&PPC_XCSR_readw,
|
||||
&PPC_XCSR_readl,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Fake super-io ports for PREP platform (Intel 82378ZB) */
|
||||
typedef struct sysctrl_t {
|
||||
m48t59_t *nvram;
|
||||
uint8_t state;
|
||||
uint8_t syscontrol;
|
||||
uint8_t fake_io[2];
|
||||
int contiguous_map;
|
||||
} sysctrl_t;
|
||||
|
||||
enum {
|
||||
STATE_HARDFILE = 0x01,
|
||||
};
|
||||
|
||||
static sysctrl_t *sysctrl;
|
||||
|
||||
static void PREP_io_write (void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
sysctrl_t *sysctrl = opaque;
|
||||
|
||||
PPC_IO_DPRINTF("0x%08lx => 0x%08x\n", (long)addr - PPC_IO_BASE, val);
|
||||
sysctrl->fake_io[addr - 0x0398] = val;
|
||||
}
|
||||
|
||||
static uint32_t PREP_io_read (void *opaque, uint32_t addr)
|
||||
{
|
||||
sysctrl_t *sysctrl = opaque;
|
||||
|
||||
PPC_IO_DPRINTF("0x%08lx <= 0x%08x\n", (long)addr - PPC_IO_BASE,
|
||||
sysctrl->fake_io[addr - 0x0398]);
|
||||
return sysctrl->fake_io[addr - 0x0398];
|
||||
}
|
||||
|
||||
static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
sysctrl_t *sysctrl = opaque;
|
||||
|
||||
PPC_IO_DPRINTF("0x%08lx => 0x%08x\n", (long)addr - PPC_IO_BASE, val);
|
||||
switch (addr) {
|
||||
case 0x0092:
|
||||
/* Special port 92 */
|
||||
/* Check soft reset asked */
|
||||
if (val & 0x01) {
|
||||
// cpu_interrupt(cpu_single_env, CPU_INTERRUPT_RESET);
|
||||
}
|
||||
/* Check LE mode */
|
||||
if (val & 0x02) {
|
||||
printf("Little Endian mode isn't supported (yet ?)\n");
|
||||
abort();
|
||||
}
|
||||
break;
|
||||
case 0x0800:
|
||||
/* Motorola CPU configuration register : read-only */
|
||||
break;
|
||||
case 0x0802:
|
||||
/* Motorola base module feature register : read-only */
|
||||
break;
|
||||
case 0x0803:
|
||||
/* Motorola base module status register : read-only */
|
||||
break;
|
||||
case 0x0808:
|
||||
/* Hardfile light register */
|
||||
if (val & 1)
|
||||
sysctrl->state |= STATE_HARDFILE;
|
||||
else
|
||||
sysctrl->state &= ~STATE_HARDFILE;
|
||||
break;
|
||||
case 0x0810:
|
||||
/* Password protect 1 register */
|
||||
if (sysctrl->nvram != NULL)
|
||||
m48t59_toggle_lock(sysctrl->nvram, 1);
|
||||
break;
|
||||
case 0x0812:
|
||||
/* Password protect 2 register */
|
||||
if (sysctrl->nvram != NULL)
|
||||
m48t59_toggle_lock(sysctrl->nvram, 2);
|
||||
break;
|
||||
case 0x0814:
|
||||
/* L2 invalidate register */
|
||||
// tlb_flush(cpu_single_env, 1);
|
||||
break;
|
||||
case 0x081C:
|
||||
/* system control register */
|
||||
sysctrl->syscontrol = val & 0x0F;
|
||||
break;
|
||||
case 0x0850:
|
||||
/* I/O map type register */
|
||||
sysctrl->contiguous_map = val & 0x01;
|
||||
break;
|
||||
default:
|
||||
printf("ERROR: unaffected IO port write: %04lx => %02x\n",
|
||||
(long)addr, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr)
|
||||
{
|
||||
sysctrl_t *sysctrl = opaque;
|
||||
uint32_t retval = 0xFF;
|
||||
|
||||
switch (addr) {
|
||||
case 0x0092:
|
||||
/* Special port 92 */
|
||||
retval = 0x00;
|
||||
break;
|
||||
case 0x0800:
|
||||
/* Motorola CPU configuration register */
|
||||
retval = 0xEF; /* MPC750 */
|
||||
break;
|
||||
case 0x0802:
|
||||
/* Motorola Base module feature register */
|
||||
retval = 0xAD; /* No ESCC, PMC slot neither ethernet */
|
||||
break;
|
||||
case 0x0803:
|
||||
/* Motorola base module status register */
|
||||
retval = 0xE0; /* Standard MPC750 */
|
||||
break;
|
||||
case 0x080C:
|
||||
/* Equipment present register:
|
||||
* no L2 cache
|
||||
* no upgrade processor
|
||||
* no cards in PCI slots
|
||||
* SCSI fuse is bad
|
||||
*/
|
||||
retval = 0x3C;
|
||||
break;
|
||||
case 0x0810:
|
||||
/* Motorola base module extended feature register */
|
||||
retval = 0x39; /* No USB, CF and PCI bridge. NVRAM present */
|
||||
break;
|
||||
case 0x0814:
|
||||
/* L2 invalidate: don't care */
|
||||
break;
|
||||
case 0x0818:
|
||||
/* Keylock */
|
||||
retval = 0x00;
|
||||
break;
|
||||
case 0x081C:
|
||||
/* system control register
|
||||
* 7 - 6 / 1 - 0: L2 cache enable
|
||||
*/
|
||||
retval = sysctrl->syscontrol;
|
||||
break;
|
||||
case 0x0823:
|
||||
/* */
|
||||
retval = 0x03; /* no L2 cache */
|
||||
break;
|
||||
case 0x0850:
|
||||
/* I/O map type register */
|
||||
retval = sysctrl->contiguous_map;
|
||||
break;
|
||||
default:
|
||||
printf("ERROR: unaffected IO port: %04lx read\n", (long)addr);
|
||||
break;
|
||||
}
|
||||
PPC_IO_DPRINTF("0x%08lx <= 0x%08x\n", (long)addr - PPC_IO_BASE, retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static inline target_phys_addr_t prep_IO_address (sysctrl_t *sysctrl,
|
||||
target_phys_addr_t addr)
|
||||
{
|
||||
if (sysctrl->contiguous_map == 0) {
|
||||
/* 64 KB contiguous space for IOs */
|
||||
addr &= 0xFFFF;
|
||||
} else {
|
||||
/* 8 MB non-contiguous space for IOs */
|
||||
addr = (addr & 0x1F) | ((addr & 0x007FFF000) >> 7);
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void PPC_prep_io_writeb (void *opaque, target_phys_addr_t addr,
|
||||
uint32_t value)
|
||||
{
|
||||
sysctrl_t *sysctrl = opaque;
|
||||
|
||||
addr = prep_IO_address(sysctrl, addr);
|
||||
cpu_outb(NULL, addr, value);
|
||||
}
|
||||
|
||||
static uint32_t PPC_prep_io_readb (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
sysctrl_t *sysctrl = opaque;
|
||||
uint32_t ret;
|
||||
|
||||
addr = prep_IO_address(sysctrl, addr);
|
||||
ret = cpu_inb(NULL, addr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void PPC_prep_io_writew (void *opaque, target_phys_addr_t addr,
|
||||
uint32_t value)
|
||||
{
|
||||
sysctrl_t *sysctrl = opaque;
|
||||
|
||||
addr = prep_IO_address(sysctrl, addr);
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
value = bswap16(value);
|
||||
#endif
|
||||
PPC_IO_DPRINTF("0x%08lx => 0x%08x\n", (long)addr, value);
|
||||
cpu_outw(NULL, addr, value);
|
||||
}
|
||||
|
||||
static uint32_t PPC_prep_io_readw (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
sysctrl_t *sysctrl = opaque;
|
||||
uint32_t ret;
|
||||
|
||||
addr = prep_IO_address(sysctrl, addr);
|
||||
ret = cpu_inw(NULL, addr);
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
ret = bswap16(ret);
|
||||
#endif
|
||||
PPC_IO_DPRINTF("0x%08lx <= 0x%08x\n", (long)addr, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void PPC_prep_io_writel (void *opaque, target_phys_addr_t addr,
|
||||
uint32_t value)
|
||||
{
|
||||
sysctrl_t *sysctrl = opaque;
|
||||
|
||||
addr = prep_IO_address(sysctrl, addr);
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
value = bswap32(value);
|
||||
#endif
|
||||
PPC_IO_DPRINTF("0x%08lx => 0x%08x\n", (long)addr, value);
|
||||
cpu_outl(NULL, addr, value);
|
||||
}
|
||||
|
||||
static uint32_t PPC_prep_io_readl (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
sysctrl_t *sysctrl = opaque;
|
||||
uint32_t ret;
|
||||
|
||||
addr = prep_IO_address(sysctrl, addr);
|
||||
ret = cpu_inl(NULL, addr);
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
ret = bswap32(ret);
|
||||
#endif
|
||||
PPC_IO_DPRINTF("0x%08lx <= 0x%08x\n", (long)addr, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CPUWriteMemoryFunc *PPC_prep_io_write[] = {
|
||||
&PPC_prep_io_writeb,
|
||||
&PPC_prep_io_writew,
|
||||
&PPC_prep_io_writel,
|
||||
};
|
||||
|
||||
CPUReadMemoryFunc *PPC_prep_io_read[] = {
|
||||
&PPC_prep_io_readb,
|
||||
&PPC_prep_io_readw,
|
||||
&PPC_prep_io_readl,
|
||||
};
|
||||
|
||||
extern CPUPPCState *global_env;
|
||||
|
||||
#define NVRAM_SIZE 0x2000
|
||||
|
||||
/* PowerPC PREP hardware initialisation */
|
||||
void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device,
|
||||
DisplayState *ds, const char **fd_filename, int snapshot,
|
||||
const char *kernel_filename, const char *kernel_cmdline,
|
||||
const char *initrd_filename)
|
||||
{
|
||||
char buf[1024];
|
||||
m48t59_t *nvram;
|
||||
int PPC_io_memory;
|
||||
int ret, linux_boot, i, nb_nics1;
|
||||
unsigned long bios_offset;
|
||||
uint32_t kernel_base, kernel_size, initrd_base, initrd_size;
|
||||
PCIBus *pci_bus;
|
||||
|
||||
sysctrl = qemu_mallocz(sizeof(sysctrl_t));
|
||||
if (sysctrl == NULL)
|
||||
return;
|
||||
|
||||
linux_boot = (kernel_filename != NULL);
|
||||
|
||||
/* allocate RAM */
|
||||
cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
|
||||
|
||||
/* allocate and load BIOS */
|
||||
bios_offset = ram_size + vga_ram_size;
|
||||
snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME);
|
||||
ret = load_image(buf, phys_ram_base + bios_offset);
|
||||
if (ret != BIOS_SIZE) {
|
||||
fprintf(stderr, "qemu: could not load PPC PREP bios '%s'\n", buf);
|
||||
exit(1);
|
||||
}
|
||||
cpu_register_physical_memory((uint32_t)(-BIOS_SIZE),
|
||||
BIOS_SIZE, bios_offset | IO_MEM_ROM);
|
||||
cpu_single_env->nip = 0xfffffffc;
|
||||
|
||||
if (linux_boot) {
|
||||
kernel_base = KERNEL_LOAD_ADDR;
|
||||
/* now we can load the kernel */
|
||||
kernel_size = load_image(kernel_filename, phys_ram_base + kernel_base);
|
||||
if (kernel_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||
kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
/* load initrd */
|
||||
if (initrd_filename) {
|
||||
initrd_base = INITRD_LOAD_ADDR;
|
||||
initrd_size = load_image(initrd_filename,
|
||||
phys_ram_base + initrd_base);
|
||||
if (initrd_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
boot_device = 'm';
|
||||
} else {
|
||||
kernel_base = 0;
|
||||
kernel_size = 0;
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
|
||||
/* Register CPU as a 604 */
|
||||
cpu_ppc_register(cpu_single_env, 0x00040000);
|
||||
/* Set time-base frequency to 100 Mhz */
|
||||
cpu_ppc_tb_init(cpu_single_env, 100UL * 1000UL * 1000UL);
|
||||
|
||||
isa_mem_base = 0xc0000000;
|
||||
pci_bus = pci_prep_init();
|
||||
// pci_bus = i440fx_init();
|
||||
/* Register 8 MB of ISA IO space (needed for non-contiguous map) */
|
||||
PPC_io_memory = cpu_register_io_memory(0, PPC_prep_io_read,
|
||||
PPC_prep_io_write, sysctrl);
|
||||
cpu_register_physical_memory(0x80000000, 0x00800000, PPC_io_memory);
|
||||
|
||||
/* init basic PC hardware */
|
||||
vga_initialize(pci_bus, ds, phys_ram_base + ram_size, ram_size,
|
||||
vga_ram_size);
|
||||
rtc_init(0x70, 8);
|
||||
// openpic = openpic_init(0x00000000, 0xF0000000, 1);
|
||||
// pic_init(openpic);
|
||||
pic_init();
|
||||
// pit = pit_init(0x40, 0);
|
||||
|
||||
serial_init(0x3f8, 4, serial_hds[0]);
|
||||
nb_nics1 = nb_nics;
|
||||
if (nb_nics1 > NE2000_NB_MAX)
|
||||
nb_nics1 = NE2000_NB_MAX;
|
||||
for(i = 0; i < nb_nics1; i++) {
|
||||
isa_ne2000_init(ne2000_io[i], ne2000_irq[i], &nd_table[i]);
|
||||
}
|
||||
|
||||
for(i = 0; i < 2; i++) {
|
||||
isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i],
|
||||
bs_table[2 * i], bs_table[2 * i + 1]);
|
||||
}
|
||||
kbd_init();
|
||||
DMA_init(1);
|
||||
// AUD_init();
|
||||
// SB16_init();
|
||||
|
||||
fdctrl_init(6, 2, 0, 0x3f0, fd_table);
|
||||
|
||||
/* Register speaker port */
|
||||
register_ioport_read(0x61, 1, 1, speaker_ioport_read, NULL);
|
||||
register_ioport_write(0x61, 1, 1, speaker_ioport_write, NULL);
|
||||
/* Register fake IO ports for PREP */
|
||||
register_ioport_read(0x398, 2, 1, &PREP_io_read, sysctrl);
|
||||
register_ioport_write(0x398, 2, 1, &PREP_io_write, sysctrl);
|
||||
/* System control ports */
|
||||
register_ioport_read(0x0092, 0x01, 1, &PREP_io_800_readb, sysctrl);
|
||||
register_ioport_write(0x0092, 0x01, 1, &PREP_io_800_writeb, sysctrl);
|
||||
register_ioport_read(0x0800, 0x52, 1, &PREP_io_800_readb, sysctrl);
|
||||
register_ioport_write(0x0800, 0x52, 1, &PREP_io_800_writeb, sysctrl);
|
||||
/* PCI intack location */
|
||||
PPC_io_memory = cpu_register_io_memory(0, PPC_intack_read,
|
||||
PPC_intack_write, NULL);
|
||||
cpu_register_physical_memory(0xBFFFFFF0, 0x4, PPC_io_memory);
|
||||
/* PowerPC control and status register group */
|
||||
#if 0
|
||||
PPC_io_memory = cpu_register_io_memory(0, PPC_XCSR_read, PPC_XCSR_write, NULL);
|
||||
cpu_register_physical_memory(0xFEFF0000, 0x1000, PPC_io_memory);
|
||||
#endif
|
||||
|
||||
nvram = m48t59_init(8, 0, 0x0074, NVRAM_SIZE);
|
||||
if (nvram == NULL)
|
||||
return;
|
||||
sysctrl->nvram = nvram;
|
||||
|
||||
/* Initialise NVRAM */
|
||||
PPC_NVRAM_set_params(nvram, NVRAM_SIZE, "PREP", ram_size, boot_device,
|
||||
kernel_base, kernel_size,
|
||||
kernel_cmdline,
|
||||
initrd_base, initrd_size,
|
||||
/* XXX: need an option to load a NVRAM image */
|
||||
0,
|
||||
graphic_width, graphic_height, graphic_depth);
|
||||
}
|
||||
Loaded 100 of 331 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user