Compare commits
1796 Commits
v0.1.6
...
release_0_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bebe39c387 | ||
|
|
215cf0bed8 | ||
|
|
691fce48a3 | ||
|
|
8dbca8dd8a | ||
|
|
68cae3d8c1 | ||
|
|
c91fde65f4 | ||
|
|
f5d2a381d1 | ||
|
|
e553272d75 | ||
|
|
3aee288bc8 | ||
|
|
bdbd7676fd | ||
|
|
38cfa06cbd | ||
|
|
e035649ea3 | ||
|
|
29e3055c37 | ||
|
|
06d9f2f7d4 | ||
|
|
eade0f192e | ||
|
|
07de1eaa9d | ||
|
|
4f552e3b9a | ||
|
|
773f2cdd3c | ||
|
|
fedc54adaa | ||
|
|
4b6ccfdec9 | ||
|
|
7d510b8c0c | ||
|
|
38954dca9f | ||
|
|
6ca957f08f | ||
|
|
f354832878 | ||
|
|
45a8f3ca06 | ||
|
|
debc70650a | ||
|
|
985d1742db | ||
|
|
3f423c9c8f | ||
|
|
7f881e5674 | ||
|
|
24236869fb | ||
|
|
a46e4035e2 | ||
|
|
4dbe19e181 | ||
|
|
a1e7547389 | ||
|
|
7918bf476b | ||
|
|
1640695026 | ||
|
|
008a8818d9 | ||
|
|
27c7ca7e77 | ||
|
|
fdf9b3e831 | ||
|
|
66a93e0f47 | ||
|
|
9ee3c02942 | ||
|
|
94ac515889 | ||
|
|
3598ecb620 | ||
|
|
a8ca632cc0 | ||
|
|
ec530c81ef | ||
|
|
96b74a0221 | ||
|
|
c2ff060fd4 | ||
|
|
467d409f7e | ||
|
|
fd06c37550 | ||
|
|
52328140e2 | ||
|
|
135e73c5f7 | ||
|
|
72899afc5d | ||
|
|
56bebe70bd | ||
|
|
fd4a43e4e2 | ||
|
|
ad1a5b7853 | ||
|
|
6c3ee14ff3 | ||
|
|
ba6526df38 | ||
|
|
1b2b0af50d | ||
|
|
e774a278d8 | ||
|
|
fa7cf687ac | ||
|
|
465e983875 | ||
|
|
b854608e0c | ||
|
|
f4e15b4b4b | ||
|
|
bbeb7b5cbd | ||
|
|
f9ebe432db | ||
|
|
cc8ae6de58 | ||
|
|
d0ecd2aaf9 | ||
|
|
b37837317f | ||
|
|
5fe141fd30 | ||
|
|
ce2f4b3cb9 | ||
|
|
cd7dd10f09 | ||
|
|
76e050c2e6 | ||
|
|
da2414e933 | ||
|
|
a7350fa109 | ||
|
|
132ea32fae | ||
|
|
d7ce493a38 | ||
|
|
6a8826434f | ||
|
|
a03a60532a | ||
|
|
9c03850684 | ||
|
|
99773bd4b4 | ||
|
|
2483668940 | ||
|
|
d4b8f0396a | ||
|
|
8606e5b450 | ||
|
|
b1a550a0da | ||
|
|
0f8134bfd6 | ||
|
|
ad06484063 | ||
|
|
115defd163 | ||
|
|
ffcdb539de | ||
|
|
210fe0be2a | ||
|
|
09b26c5ec0 | ||
|
|
6a15fd12ca | ||
|
|
3e749fe1f7 | ||
|
|
f331110f35 | ||
|
|
1236cab73d | ||
|
|
358bf29e80 | ||
|
|
cdbdb648b7 | ||
|
|
95219897ff | ||
|
|
07435f7462 | ||
|
|
e3f4e2a4b0 | ||
|
|
706cd4b547 | ||
|
|
c2f07f81a2 | ||
|
|
af5db58e8b | ||
|
|
7783e9f002 | ||
|
|
33698e5ffc | ||
|
|
894244f6ca | ||
|
|
1298fe6316 | ||
|
|
307b0c24de | ||
|
|
61b9415691 | ||
|
|
89ba1a738e | ||
|
|
53a5960aad | ||
|
|
26f69dc09f | ||
|
|
cad25d69ad | ||
|
|
0a8e90f401 | ||
|
|
8637c67fc5 | ||
|
|
d80cfb3f70 | ||
|
|
19b045dec9 | ||
|
|
b55669bf57 | ||
|
|
f5ba07d399 | ||
|
|
ce5c37c2a4 | ||
|
|
5b31187812 | ||
|
|
38ca0f6dee | ||
|
|
09c56b842e | ||
|
|
ecd78a0ac7 | ||
|
|
4e9aec746e | ||
|
|
56b194039e | ||
|
|
98c1b82b6c | ||
|
|
6d6f7c288d | ||
|
|
d2ec1774eb | ||
|
|
38260998a2 | ||
|
|
647c593038 | ||
|
|
9540a78b90 | ||
|
|
e10c2bfb73 | ||
|
|
023e9351d0 | ||
|
|
ed96ca3571 | ||
|
|
40f137e1ea | ||
|
|
4081fccf14 | ||
|
|
be9d365723 | ||
|
|
242011157f | ||
|
|
2ae23e7504 | ||
|
|
3b7f5d479c | ||
|
|
1247c5f7be | ||
|
|
e0b3073f53 | ||
|
|
29517134c6 | ||
|
|
ce4defa062 | ||
|
|
b88a38324b | ||
|
|
89bfc105d0 | ||
|
|
f32fc64851 | ||
|
|
f1c85677fc | ||
|
|
5f1ce9487c | ||
|
|
05c2a3e731 | ||
|
|
f94f5d717c | ||
|
|
3aa22b4b53 | ||
|
|
af2f67333f | ||
|
|
bdd5003ae5 | ||
|
|
a41b2ff2dd | ||
|
|
d861b05ea3 | ||
|
|
191abaa2f0 | ||
|
|
3442e8964e | ||
|
|
e89f07d384 | ||
|
|
06c949e62a | ||
|
|
0240ded8bb | ||
|
|
0fd14b72ac | ||
|
|
7fb843f8cc | ||
|
|
9445880298 | ||
|
|
8147cfca56 | ||
|
|
28a5c9c8b2 | ||
|
|
acff9df6a8 | ||
|
|
039af320d9 | ||
|
|
fd1dff4b41 | ||
|
|
ff3fbb307d | ||
|
|
90dc3b395f | ||
|
|
85b2c68832 | ||
|
|
48c2f068e4 | ||
|
|
1538800276 | ||
|
|
6a36d84e10 | ||
|
|
3f9f3aa1ca | ||
|
|
31febb71f4 | ||
|
|
2d7a3b9d7b | ||
|
|
7c206a754a | ||
|
|
2efc32658e | ||
|
|
91fc211974 | ||
|
|
2c6cadd49e | ||
|
|
a046433a16 | ||
|
|
95389c8681 | ||
|
|
1658b44bf5 | ||
|
|
183b4a3806 | ||
|
|
5198cfd927 | ||
|
|
68998c5de3 | ||
|
|
6d7e63262c | ||
|
|
3d830459b1 | ||
|
|
87022ff52b | ||
|
|
cd072e01d8 | ||
|
|
d3e9db933f | ||
|
|
01dbbdf1e5 | ||
|
|
76b3030c56 | ||
|
|
265d349776 | ||
|
|
dbf2c23a60 | ||
|
|
56902eee82 | ||
|
|
9f25f11fe5 | ||
|
|
909a8762ee | ||
|
|
c20eb47362 | ||
|
|
01f5e596ed | ||
|
|
ea31eb5b0c | ||
|
|
cc4adeef98 | ||
|
|
6900e84b20 | ||
|
|
ba3c64fb47 | ||
|
|
b9788fc4c4 | ||
|
|
227671c93b | ||
|
|
f881a0d4f6 | ||
|
|
4ad40f366f | ||
|
|
6810e15490 | ||
|
|
a64d4718f1 | ||
|
|
2d7272a588 | ||
|
|
30d6cb8479 | ||
|
|
6f970bd90e | ||
|
|
89984cd2e5 | ||
|
|
ee0971849e | ||
|
|
80337b66a8 | ||
|
|
54ca9095f0 | ||
|
|
56c8f68f1d | ||
|
|
c960bde13c | ||
|
|
148f50581b | ||
|
|
4b4f782c78 | ||
|
|
84b7b8e778 | ||
|
|
5cf3839607 | ||
|
|
5732fd2779 | ||
|
|
649ea05a2c | ||
|
|
7664728bdf | ||
|
|
50443c98e4 | ||
|
|
6f5a9f7e56 | ||
|
|
4a38940da0 | ||
|
|
048f6b4df7 | ||
|
|
eeef26cd42 | ||
|
|
cc9442b9fc | ||
|
|
15338fd765 | ||
|
|
79639d423f | ||
|
|
9332f9dafa | ||
|
|
e8ebb8a8d7 | ||
|
|
b5ff1b3127 | ||
|
|
0e43e99c04 | ||
|
|
98699967b8 | ||
|
|
daa579632d | ||
|
|
e80e1cc4b1 | ||
|
|
f24e5695e5 | ||
|
|
7668a27f1d | ||
|
|
e5d13e2f64 | ||
|
|
2023a2c836 | ||
|
|
5a1e3cfcb0 | ||
|
|
d2ac63e03e | ||
|
|
ad49ff9de3 | ||
|
|
15a7644956 | ||
|
|
8dd69b8f2c | ||
|
|
089af99118 | ||
|
|
3476562d36 | ||
|
|
59b8ad81c4 | ||
|
|
c68ea7043f | ||
|
|
173d6cfe51 | ||
|
|
0e1fd3694e | ||
|
|
e0fd87812f | ||
|
|
6a00d60127 | ||
|
|
f0aca8227f | ||
|
|
e59c11393b | ||
|
|
32d448c470 | ||
|
|
571ec3d68d | ||
|
|
5e941d4b51 | ||
|
|
509035303d | ||
|
|
546754dc1d | ||
|
|
8a40a180d3 | ||
|
|
313adae905 | ||
|
|
a316d3353c | ||
|
|
6e256c935c | ||
|
|
c1942362bc | ||
|
|
0b7a4a9711 | ||
|
|
2df3b95dbb | ||
|
|
5e9ab4c493 | ||
|
|
7e89463d4a | ||
|
|
41d03949e1 | ||
|
|
7c9d8e07e1 | ||
|
|
868bfe2b2b | ||
|
|
9e61bde56a | ||
|
|
4787c71d17 | ||
|
|
541e084426 | ||
|
|
e7cad33853 | ||
|
|
575b5dc4dc | ||
|
|
b41cffbeb4 | ||
|
|
0bd4885002 | ||
|
|
946fc94733 | ||
|
|
a0d01ed9ea | ||
|
|
e57a8c0eef | ||
|
|
2122c51a9c | ||
|
|
f8d179e33d | ||
|
|
3f87bf6959 | ||
|
|
2531fc7bc0 | ||
|
|
3dbbdc2555 | ||
|
|
48024e4a48 | ||
|
|
b389dbfb58 | ||
|
|
a594cfbf3e | ||
|
|
8738a8d079 | ||
|
|
c0fe3827ea | ||
|
|
f04308e452 | ||
|
|
59ae540c3d | ||
|
|
92414fdca0 | ||
|
|
bb36d4708b | ||
|
|
1aff381f59 | ||
|
|
9903da21e3 | ||
|
|
c53be33474 | ||
|
|
d5d11eac6c | ||
|
|
24741ef3de | ||
|
|
74c33bed31 | ||
|
|
afce2927aa | ||
|
|
bd6ea3c8f3 | ||
|
|
02aab46a36 | ||
|
|
aab3309407 | ||
|
|
05f3fb8de3 | ||
|
|
6f5f11a5bc | ||
|
|
1d14ffa97e | ||
|
|
3b0d4f61c9 | ||
|
|
87f48e6a1a | ||
|
|
b3ecf620de | ||
|
|
a9049a07bb | ||
|
|
bb3911a609 | ||
|
|
4e3b1ea1b8 | ||
|
|
4f6200f03b | ||
|
|
aea3ce4c8d | ||
|
|
f69a86955e | ||
|
|
7b936c0c42 | ||
|
|
819385c58b | ||
|
|
48b2c19353 | ||
|
|
1983a3956c | ||
|
|
c0b24a1dd6 | ||
|
|
de75815006 | ||
|
|
7fa98e2a7a | ||
|
|
8289336c98 | ||
|
|
a332e112b7 | ||
|
|
ca0d1734b4 | ||
|
|
aa0bc6b68c | ||
|
|
1c213d1976 | ||
|
|
a7c15abbb1 | ||
|
|
df5f895699 | ||
|
|
fc8dc06020 | ||
|
|
f23db1692b | ||
|
|
3f20e1ddf2 | ||
|
|
75913b727e | ||
|
|
ecada8a2dd | ||
|
|
1e8a7cfd11 | ||
|
|
0bccf03d6f | ||
|
|
89353a790b | ||
|
|
7ebab69910 | ||
|
|
697584ab24 | ||
|
|
c96a29cdef | ||
|
|
4b7df22f91 | ||
|
|
2c8e030185 | ||
|
|
81eea5ebb6 | ||
|
|
5e6ad6f90e | ||
|
|
aa06297340 | ||
|
|
3a7d929e62 | ||
|
|
04c504cc4f | ||
|
|
ff7b8f5b0f | ||
|
|
6688bc6d04 | ||
|
|
bc3fc8dac0 | ||
|
|
1f3358c87d | ||
|
|
92510b8cf5 | ||
|
|
6cc721cf42 | ||
|
|
e99f906055 | ||
|
|
f5a8510c7c | ||
|
|
93856aac7b | ||
|
|
5cedb46460 | ||
|
|
0f4c64157f | ||
|
|
90cb949352 | ||
|
|
db6e6ed77e | ||
|
|
57e4c06ed7 | ||
|
|
09d459a1db | ||
|
|
108c49b8a2 | ||
|
|
90f18422d9 | ||
|
|
9529397248 | ||
|
|
a2458627f9 | ||
|
|
b1fc0348b1 | ||
|
|
45bbbb466c | ||
|
|
d592d3033d | ||
|
|
1ff5c1a68e | ||
|
|
e5d80f94c5 | ||
|
|
8f091a5960 | ||
|
|
2efbe911d3 | ||
|
|
667f38b167 | ||
|
|
8346901560 | ||
|
|
b7c7b18129 | ||
|
|
9835236910 | ||
|
|
e573335624 | ||
|
|
4e588a4d0e | ||
|
|
a368741bf2 | ||
|
|
61271e5c2d | ||
|
|
aefce9af41 | ||
|
|
ee5bbe38b1 | ||
|
|
e37e863f5e | ||
|
|
fdabc366bd | ||
|
|
2157fa0682 | ||
|
|
d24b15a8d8 | ||
|
|
9d0a8e6f8f | ||
|
|
51a36cb2cb | ||
|
|
33d084399c | ||
|
|
e0727e17f3 | ||
|
|
97067eb5bc | ||
|
|
4157a66212 | ||
|
|
0289b2c1df | ||
|
|
d5295253b0 | ||
|
|
fb3444b86c | ||
|
|
2be0071f22 | ||
|
|
f68c781c2d | ||
|
|
fa296b0fb4 | ||
|
|
3fc6c082e3 | ||
|
|
2f636b458f | ||
|
|
3de388f676 | ||
|
|
73133662c6 | ||
|
|
bf82d81801 | ||
|
|
b195775fef | ||
|
|
1b351e5291 | ||
|
|
d325856010 | ||
|
|
0d8aca8c67 | ||
|
|
8549850891 | ||
|
|
7a962d3087 | ||
|
|
e1d9a50836 | ||
|
|
568b600d85 | ||
|
|
bc2c390907 | ||
|
|
9827e95c78 | ||
|
|
51e11d9e6c | ||
|
|
90b37806ba | ||
|
|
0699b54839 | ||
|
|
9d1d106a3d | ||
|
|
ae022501f2 | ||
|
|
899abcf513 | ||
|
|
dfae6487c0 | ||
|
|
de12d6369b | ||
|
|
bc9ed47b12 | ||
|
|
9fb63ac281 | ||
|
|
6af0bf9c7c | ||
|
|
6643d27ea0 | ||
|
|
3475187dd8 | ||
|
|
8979b2277d | ||
|
|
97ccc689e6 | ||
|
|
c98baaac2f | ||
|
|
101c593562 | ||
|
|
b685369795 | ||
|
|
3f1a88f450 | ||
|
|
a84eaf0c9b | ||
|
|
6e20a45f53 | ||
|
|
cadae95f33 | ||
|
|
e68b9b2b10 | ||
|
|
c0e564d53b | ||
|
|
384d887691 | ||
|
|
5457c8ceeb | ||
|
|
0aa6a4a250 | ||
|
|
938828a263 | ||
|
|
b5ff2d6e2d | ||
|
|
54fa5af546 | ||
|
|
cc1daa40f1 | ||
|
|
2d61879305 | ||
|
|
7c48011b45 | ||
|
|
8dd4983c4e | ||
|
|
71be0fc3eb | ||
|
|
30aec8768f | ||
|
|
8993433789 | ||
|
|
d094807b9b | ||
|
|
6d506e6dc2 | ||
|
|
43ef9eb267 | ||
|
|
e4cf1adc80 | ||
|
|
72cc6cfeef | ||
|
|
bc380d1719 | ||
|
|
ff8263a951 | ||
|
|
04d81be884 | ||
|
|
2d5262f991 | ||
|
|
a09db21f71 | ||
|
|
b671f9ed2d | ||
|
|
de167e416f | ||
|
|
712e78744e | ||
|
|
7c35359cbf | ||
|
|
d37282add1 | ||
|
|
a343df1659 | ||
|
|
98ff7d30f2 | ||
|
|
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 |
29
.cvsignore
Normal file
29
.cvsignore
Normal file
@@ -0,0 +1,29 @@
|
||||
arm-user
|
||||
arm-softmmu
|
||||
armeb-user
|
||||
config-host.*
|
||||
dyngen
|
||||
i386
|
||||
i386-softmmu
|
||||
i386-user
|
||||
ppc-softmmu
|
||||
ppc64-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
|
||||
mips-softmmu
|
||||
mipsel-softmmu
|
||||
mips-user
|
||||
mipsel-user
|
||||
sh4-user
|
||||
sh4-softmmu
|
||||
222
COPYING.LIB
222
COPYING.LIB
@@ -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!
|
||||
|
||||
|
||||
|
||||
316
Changelog
316
Changelog
@@ -1,3 +1,319 @@
|
||||
version 0.8.1:
|
||||
|
||||
- USB tablet support (Brad Campbell, Anthony Liguori)
|
||||
- win32 host serial support (Kazu)
|
||||
- PC speaker support (Joachim Henke)
|
||||
- IDE LBA48 support (Jens Axboe)
|
||||
- SSE3 support
|
||||
- Solaris port (Ben Taylor)
|
||||
- Preliminary SH4 target (Samuel Tardieu)
|
||||
- VNC server (Anthony Liguori)
|
||||
- slirp fixes (Ed Swierk et al.)
|
||||
- USB fixes
|
||||
- ARM Versatile Platform Baseboard emulation (Paul Brook)
|
||||
|
||||
version 0.8.0:
|
||||
|
||||
- ARM system emulation: Arm Integrator/CP board with an arm1026ej-s
|
||||
cpu (Paul Brook)
|
||||
- SMP support
|
||||
- Mac OS X cocoa improvements (Mike Kronenberg)
|
||||
- Mac OS X CoreAudio driver (Mike Kronenberg)
|
||||
- DirectSound driver (malc)
|
||||
- ALSA audio driver (malc)
|
||||
- new audio options: '-soundhw' and '-audio-help' (malc)
|
||||
- ES1370 PCI audio device (malc)
|
||||
- Initial USB support
|
||||
- Linux host serial port access
|
||||
- Linux host low level parallel port access
|
||||
- New network emulation code supporting VLANs.
|
||||
- MIPS and MIPSel User Linux emulation
|
||||
- MIPS fixes to boot Linux (Daniel Jacobowitz)
|
||||
- NX bit support
|
||||
- Initial SPARC SMP support (Blue Swirl)
|
||||
- Major overhaul of the virtual FAT driver for read/write support
|
||||
(Johannes Schindelin)
|
||||
|
||||
version 0.7.2:
|
||||
|
||||
- x86_64 fixes (Win2000 and Linux 2.6 boot in 32 bit)
|
||||
- merge self modifying code handling in dirty ram page mecanism.
|
||||
- MIPS fixes (Ralf Baechle)
|
||||
- better user net performances
|
||||
|
||||
version 0.7.1:
|
||||
|
||||
- read-only Virtual FAT support (Johannes Schindelin)
|
||||
- Windows 2000 install disk full hack (original idea from Vladimir
|
||||
N. Oleynik)
|
||||
- VMDK disk image creation (Filip Navara)
|
||||
- SPARC64 progress (Blue Swirl)
|
||||
- initial MIPS support (Jocelyn mayer)
|
||||
- MIPS improvements (Ralf Baechle)
|
||||
- 64 bit fixes in user networking (initial patch by Gwenole Beauchesne)
|
||||
- IOAPIC support (Filip Navara)
|
||||
|
||||
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
|
||||
|
||||
12
LICENSE
Normal file
12
LICENSE
Normal file
@@ -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.
|
||||
223
Makefile
223
Makefile
@@ -1,142 +1,145 @@
|
||||
include config.mak
|
||||
include config-host.mak
|
||||
|
||||
CFLAGS=-Wall -O2 -g
|
||||
CFLAGS=-Wall -O2 -g -fno-strict-aliasing -I.
|
||||
ifdef CONFIG_DARWIN
|
||||
CFLAGS+= -mdynamic-no-pic
|
||||
endif
|
||||
LDFLAGS=-g
|
||||
LIBS=
|
||||
DEFINES=-DHAVE_BYTESWAP_H
|
||||
|
||||
ifeq ($(ARCH),i386)
|
||||
CFLAGS+=-fomit-frame-pointer
|
||||
OP_CFLAGS=$(CFLAGS) -mpreferred-stack-boundary=2
|
||||
ifeq ($(GCC_MAJOR),3)
|
||||
OP_CFLAGS+= -falign-functions=0
|
||||
DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
TOOLS=qemu-img$(EXESUF)
|
||||
ifdef CONFIG_STATIC
|
||||
LDFLAGS+=-static
|
||||
endif
|
||||
ifdef BUILD_DOCS
|
||||
DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1
|
||||
else
|
||||
OP_CFLAGS+= -malign-functions=0
|
||||
endif
|
||||
# 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
|
||||
DOCS=
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ppc)
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
LDFLAGS+=-Wl,-T,ppc.ld
|
||||
endif
|
||||
all: dyngen$(EXESUF) $(TOOLS) $(DOCS)
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
|
||||
ifeq ($(ARCH),s390)
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
LDFLAGS+=-Wl,-T,s390.ld
|
||||
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 block-vvfat.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
|
||||
LIBS+=-lm
|
||||
|
||||
# profiling code
|
||||
ifdef TARGET_GPROF
|
||||
LDFLAGS+=-p
|
||||
main.o: CFLAGS+=-p
|
||||
endif
|
||||
|
||||
OBJS= elfload.o main.o syscall.o signal.o path.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) $(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
|
||||
|
||||
distclean: clean
|
||||
rm -f config.mak config.h
|
||||
rm -f config-host.mak config-host.h $(DOCS)
|
||||
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-doc: $(DOCS)
|
||||
mkdir -p "$(DESTDIR)$(docdir)"
|
||||
$(INSTALL) -m 644 qemu-doc.html qemu-tech.html "$(DESTDIR)$(docdir)"
|
||||
ifndef CONFIG_WIN32
|
||||
mkdir -p "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL) qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||
endif
|
||||
|
||||
install: all $(if $(BUILD_DOCS),install-doc)
|
||||
mkdir -p "$(DESTDIR)$(bindir)"
|
||||
$(INSTALL) -m 755 -s $(TOOLS) "$(DESTDIR)$(bindir)"
|
||||
mkdir -p "$(DESTDIR)$(datadir)"
|
||||
for x in bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \
|
||||
video.x proll.elf linux_boot.bin; do \
|
||||
$(INSTALL) -m 644 $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \
|
||||
done
|
||||
ifndef CONFIG_WIN32
|
||||
mkdir -p "$(DESTDIR)$(datadir)/keymaps"
|
||||
for x in $(KEYMAPS); do \
|
||||
$(INSTALL) -m 644 $(SRC_PATH)/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \
|
||||
done
|
||||
endif
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
|
||||
# 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]
|
||||
|
||||
cscope:
|
||||
rm -f ./cscope.*
|
||||
find . -name "*.[ch]" -print > ./cscope.files
|
||||
cscope -b
|
||||
|
||||
# documentation
|
||||
qemu-doc.html: qemu-doc.texi
|
||||
%.html: %.texi
|
||||
texi2html -monolithic -number $<
|
||||
|
||||
FILES= \
|
||||
README README.distrib COPYING COPYING.LIB TODO Changelog VERSION \
|
||||
dyngen.c ioctls.h ops_template.h op_string.h syscall_types.h\
|
||||
Makefile elf.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 syscall.c\
|
||||
dis-buf.c i386-dis.c opreg_template.h syscall_defs.h\
|
||||
ppc.ld s390.ld exec-i386.h exec-i386.c path.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 \
|
||||
tests/runcom.c tests/pi_10.com \
|
||||
tests/test_path.c \
|
||||
qemu-doc.texi qemu-doc.html
|
||||
%.info: %.texi
|
||||
makeinfo $< -o $@
|
||||
|
||||
FILE=qemu-$(VERSION)
|
||||
%.dvi: %.texi
|
||||
texi2dvi $<
|
||||
|
||||
qemu.1: qemu-doc.texi
|
||||
$(SRC_PATH)/texi2pod.pl $< qemu.pod
|
||||
pod2man --section=1 --center=" " --release=" " qemu.pod > $@
|
||||
|
||||
qemu-img.1: qemu-img.texi
|
||||
$(SRC_PATH)/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-$(VERSION)-i386-glibc21.tar.gz \
|
||||
$(BINPATH)/etc $(BINPATH)/lib $(BINPATH)/bin $(BINPATH)/usr
|
||||
tar zcvf /tmp/qemu-$(VERSION)-i386-wine.tar.gz \
|
||||
$(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-system-mips \
|
||||
$(bindir)/qemu-system-mipsel \
|
||||
$(bindir)/qemu-system-arm \
|
||||
$(bindir)/qemu-i386 \
|
||||
$(bindir)/qemu-arm \
|
||||
$(bindir)/qemu-armeb \
|
||||
$(bindir)/qemu-sparc \
|
||||
$(bindir)/qemu-ppc \
|
||||
$(bindir)/qemu-mips \
|
||||
$(bindir)/qemu-mipsel \
|
||||
$(bindir)/qemu-img \
|
||||
$(datadir)/bios.bin \
|
||||
$(datadir)/vgabios.bin \
|
||||
$(datadir)/vgabios-cirrus.bin \
|
||||
$(datadir)/ppc_rom.bin \
|
||||
$(datadir)/video.x \
|
||||
$(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
|
||||
|
||||
506
Makefile.target
Normal file
506
Makefile.target
Normal file
@@ -0,0 +1,506 @@
|
||||
include config.mak
|
||||
|
||||
TARGET_BASE_ARCH:=$(TARGET_ARCH)
|
||||
ifeq ($(TARGET_ARCH), x86_64)
|
||||
TARGET_BASE_ARCH:=i386
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), ppc64)
|
||||
TARGET_BASE_ARCH:=ppc
|
||||
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.. -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
|
||||
TARGET_ARCH2=$(TARGET_ARCH)
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
ifeq ($(TARGET_WORDS_BIGENDIAN),yes)
|
||||
TARGET_ARCH2=armeb
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH),mips)
|
||||
ifneq ($(TARGET_WORDS_BIGENDIAN),yes)
|
||||
TARGET_ARCH2=mipsel
|
||||
endif
|
||||
endif
|
||||
QEMU_USER=qemu-$(TARGET_ARCH2)
|
||||
# system emulator name
|
||||
ifdef CONFIG_SOFTMMU
|
||||
ifeq ($(TARGET_ARCH), i386)
|
||||
QEMU_SYSTEM=qemu$(EXESUF)
|
||||
else
|
||||
QEMU_SYSTEM=qemu-system-$(TARGET_ARCH2)$(EXESUF)
|
||||
endif
|
||||
else
|
||||
QEMU_SYSTEM=qemu-fast
|
||||
endif
|
||||
|
||||
ifdef CONFIG_USER_ONLY
|
||||
PROGS=$(QEMU_USER)
|
||||
else
|
||||
PROGS+=$(QEMU_SYSTEM)
|
||||
ifndef CONFIG_SOFTMMU
|
||||
CONFIG_STATIC=y
|
||||
endif
|
||||
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
|
||||
ifdef CONFIG_SOLARIS
|
||||
LIBS+=-lsocket -lnsl -lresolv
|
||||
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_BASE_ARCH), ppc)
|
||||
LIBOBJS+= op_helper.o helper.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), mips)
|
||||
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 helper.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), sh4)
|
||||
LIBOBJS+= op_helper.o 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_BASE_ARCH) $(ARCH)),ppc)
|
||||
LIBOBJS+=ppc-dis.o
|
||||
endif
|
||||
ifeq ($(findstring mips, $(TARGET_ARCH) $(ARCH)),mips)
|
||||
LIBOBJS+=mips-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 ($(findstring m68k, $(TARGET_ARCH) $(ARCH)),m68k)
|
||||
LIBOBJS+=m68k-dis.o
|
||||
endif
|
||||
ifeq ($(findstring sh4, $(TARGET_ARCH) $(ARCH)),sh4)
|
||||
LIBOBJS+=sh4-dis.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 loader.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 block-vvfat.o
|
||||
ifdef CONFIG_WIN32
|
||||
VL_OBJS+=tap-win32.o
|
||||
endif
|
||||
|
||||
SOUND_HW = sb16.o es1370.o
|
||||
AUDIODRV = audio.o noaudio.o wavaudio.o
|
||||
ifdef CONFIG_SDL
|
||||
AUDIODRV += sdlaudio.o
|
||||
endif
|
||||
ifdef CONFIG_OSS
|
||||
AUDIODRV += ossaudio.o
|
||||
endif
|
||||
ifdef CONFIG_COREAUDIO
|
||||
AUDIODRV += coreaudio.o
|
||||
endif
|
||||
ifdef CONFIG_ALSA
|
||||
AUDIODRV += alsaaudio.o
|
||||
LIBS += -lasound
|
||||
endif
|
||||
ifdef CONFIG_DSOUND
|
||||
AUDIODRV += dsoundaudio.o
|
||||
LIBS += -lole32 -ldxguid
|
||||
endif
|
||||
ifdef CONFIG_FMOD
|
||||
AUDIODRV += fmodaudio.o
|
||||
audio.o fmodaudio.o: DEFINES := -I$(CONFIG_FMOD_INC) $(DEFINES)
|
||||
LIBS += $(CONFIG_FMOD_LIB)
|
||||
endif
|
||||
ifdef CONFIG_ADLIB
|
||||
SOUND_HW += fmopl.o adlib.o
|
||||
endif
|
||||
|
||||
# USB layer
|
||||
VL_OBJS+= usb.o usb-hub.o usb-uhci.o usb-linux.o usb-hid.o
|
||||
|
||||
# PCI network cards
|
||||
VL_OBJS+= ne2000.o rtl8139.o
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), i386)
|
||||
# Hardware support
|
||||
VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
|
||||
VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
|
||||
VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o
|
||||
DEFINES += -DHAS_AUDIO
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), ppc)
|
||||
VL_OBJS+= ppc.o ide.o pckbd.o ps2.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 heathrow_pic.o mixeng.o
|
||||
DEFINES += -DHAS_AUDIO
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), mips)
|
||||
VL_OBJS+= mips_r4k.o dma.o vga.o serial.o i8254.o i8259.o
|
||||
#VL_OBJS+= #ide.o pckbd.o fdc.o m48t59.o
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||
ifeq ($(TARGET_ARCH), sparc64)
|
||||
VL_OBJS+= sun4u.o ide.o pckbd.o ps2.o vga.o
|
||||
VL_OBJS+= fdc.o mc146818rtc.o serial.o m48t59.o
|
||||
VL_OBJS+= cirrus_vga.o parallel.o
|
||||
else
|
||||
VL_OBJS+= sun4m.o tcx.o lance.o iommu.o m48t59.o slavio_intctl.o
|
||||
VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), arm)
|
||||
VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
|
||||
VL_OBJS+= arm_boot.o pl011.o pl050.o pl080.o pl110.o pl190.o
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), sh4)
|
||||
VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o
|
||||
endif
|
||||
ifdef CONFIG_GDBSTUB
|
||||
VL_OBJS+=gdbstub.o
|
||||
endif
|
||||
ifdef CONFIG_SDL
|
||||
VL_OBJS+=sdl.o
|
||||
endif
|
||||
VL_OBJS+=vnc.o
|
||||
ifdef CONFIG_COCOA
|
||||
VL_OBJS+=cocoa.o
|
||||
COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
|
||||
ifdef CONFIG_COREAUDIO
|
||||
COCOA_LIBS+=-framework CoreAudio
|
||||
endif
|
||||
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
|
||||
ifndef CONFIG_SOLARIS
|
||||
VL_LIBS=-lutil
|
||||
endif
|
||||
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
|
||||
|
||||
ifdef CONFIG_WIN32
|
||||
SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole
|
||||
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 $@ $<
|
||||
|
||||
vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h
|
||||
$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
sdlaudio.o: sdlaudio.c
|
||||
$(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
|
||||
|
||||
depend: $(SRCS)
|
||||
$(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend
|
||||
|
||||
vldepend: $(VL_OBJS:.o=.c)
|
||||
$(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
|
||||
pl110.o: pl110_template.h
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||
op.o: op.c op_template.h op_mem.h fop_template.h fbranch_template.h
|
||||
magic_load.o: elf_op.h
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), ppc)
|
||||
op.o: op.c op_template.h op_mem.h
|
||||
op_helper.o: op_helper_mem.h
|
||||
translate.o: translate.c translate_init.c
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), mips)
|
||||
op.o: op.c op_template.c op_mem.c
|
||||
op_helper.o: op_helper_mem.c
|
||||
endif
|
||||
|
||||
loader.o: loader.c elf_ops.h
|
||||
|
||||
ifeq ($(TARGET_ARCH), sh4)
|
||||
op.o: op.c op_mem.c cpu.h
|
||||
op_helper.o: op_helper.c exec.h cpu.h
|
||||
helper.o: helper.c exec.h cpu.h
|
||||
sh7750.o: sh7750.c sh7750_regs.h sh7750_regnames.h cpu.h
|
||||
shix.o: shix.c sh7750_regs.h sh7750_regnames.h
|
||||
sh7750_regnames.o: sh7750_regnames.c sh7750_regnames.h sh7750_regs.h
|
||||
tc58128.o: tc58128.c
|
||||
endif
|
||||
|
||||
%.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) "$(DESTDIR)$(bindir)"
|
||||
endif
|
||||
|
||||
ifneq ($(wildcard .depend),)
|
||||
include .depend
|
||||
endif
|
||||
|
||||
ifeq (1, 0)
|
||||
audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o \
|
||||
fmodaudio.o alsaaudio.o mixeng.o sb16.o es1370.o gus.o adlib.o: \
|
||||
CFLAGS := $(CFLAGS) -Wall -Werror -W -Wsign-compare
|
||||
endif
|
||||
42
README
42
README
@@ -1,45 +1,3 @@
|
||||
The QEMU x86 emulator
|
||||
---------------------
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
Type
|
||||
|
||||
./configure --interp-prefix=/usr/local/qemu-i386
|
||||
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-XXX-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-i386
|
||||
|
||||
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.
|
||||
16
README.distrib
Normal file
16
README.distrib
Normal file
@@ -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
|
||||
69
TODO
69
TODO
@@ -1,12 +1,61 @@
|
||||
- fix thread locks
|
||||
- optimize translated cache chaining (DLL PLT-like system)
|
||||
- fix thread stack liberation (use kernel 2.5.xxx CLONE_CHILD_CLEARTID)
|
||||
- fix x86 stack allocation
|
||||
- fix iret/lret restarting
|
||||
short term:
|
||||
----------
|
||||
- support variable tsc freq
|
||||
- cpu_interrupt() win32/SMP fix
|
||||
- USB host async
|
||||
- IDE async
|
||||
- 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)
|
||||
|
||||
ppc specific:
|
||||
------------
|
||||
- TLB invalidate not needed if msr_pr changes
|
||||
- SPR_ENCODE() not useful
|
||||
- enable shift optimizations ?
|
||||
|
||||
linux-user specific:
|
||||
-------------------
|
||||
- add IPC syscalls
|
||||
- handle rare page fault cases (in particular if page fault in helpers or
|
||||
in syscall emulation code).
|
||||
- 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)
|
||||
- make it self runnable (handle self modifying code, relocate stack
|
||||
and dyn loader)
|
||||
- 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.
|
||||
- fix thread stack freeing (use kernel 2.5.x CLONE_CHILD_CLEARTID)
|
||||
- use kernel traps for unaligned accesses on ARM ?
|
||||
|
||||
|
||||
lower priority:
|
||||
--------------
|
||||
- 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)
|
||||
- use -msoft-float on ARM
|
||||
|
||||
431
a.out.h
Normal file
431
a.out.h
Normal file
@@ -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_ */
|
||||
|
||||
26
aes.h
Normal file
26
aes.h
Normal file
@@ -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
|
||||
1960
alpha-dis.c
Normal file
1960
alpha-dis.c
Normal file
File diff suppressed because it is too large
Load Diff
128
alpha.ld
Normal file
128
alpha.ld
Normal file
@@ -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 . */
|
||||
}
|
||||
128
arm.ld
Normal file
128
arm.ld
Normal file
@@ -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 . */
|
||||
}
|
||||
981
audio/alsaaudio.c
Normal file
981
audio/alsaaudio.c
Normal file
@@ -0,0 +1,981 @@
|
||||
/*
|
||||
* QEMU ALSA audio driver
|
||||
*
|
||||
* Copyright (c) 2005 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 <alsa/asoundlib.h>
|
||||
#include "vl.h"
|
||||
|
||||
#define AUDIO_CAP "alsa"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct ALSAVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
void *pcm_buf;
|
||||
snd_pcm_t *handle;
|
||||
} ALSAVoiceOut;
|
||||
|
||||
typedef struct ALSAVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
snd_pcm_t *handle;
|
||||
void *pcm_buf;
|
||||
} ALSAVoiceIn;
|
||||
|
||||
static struct {
|
||||
int size_in_usec_in;
|
||||
int size_in_usec_out;
|
||||
const char *pcm_name_in;
|
||||
const char *pcm_name_out;
|
||||
unsigned int buffer_size_in;
|
||||
unsigned int period_size_in;
|
||||
unsigned int buffer_size_out;
|
||||
unsigned int period_size_out;
|
||||
unsigned int threshold;
|
||||
|
||||
int buffer_size_in_overriden;
|
||||
int period_size_in_overriden;
|
||||
|
||||
int buffer_size_out_overriden;
|
||||
int period_size_out_overriden;
|
||||
int verbose;
|
||||
} conf = {
|
||||
#ifdef HIGH_LATENCY
|
||||
.size_in_usec_in = 1,
|
||||
.size_in_usec_out = 1,
|
||||
#endif
|
||||
.pcm_name_out = "hw:0,0",
|
||||
.pcm_name_in = "hw:0,0",
|
||||
#ifdef HIGH_LATENCY
|
||||
.buffer_size_in = 400000,
|
||||
.period_size_in = 400000 / 4,
|
||||
.buffer_size_out = 400000,
|
||||
.period_size_out = 400000 / 4,
|
||||
#else
|
||||
#define DEFAULT_BUFFER_SIZE 1024
|
||||
#define DEFAULT_PERIOD_SIZE 256
|
||||
.buffer_size_in = DEFAULT_BUFFER_SIZE * 4,
|
||||
.period_size_in = DEFAULT_PERIOD_SIZE * 4,
|
||||
.buffer_size_out = DEFAULT_BUFFER_SIZE,
|
||||
.period_size_out = DEFAULT_PERIOD_SIZE,
|
||||
.buffer_size_in_overriden = 0,
|
||||
.buffer_size_out_overriden = 0,
|
||||
.period_size_in_overriden = 0,
|
||||
.period_size_out_overriden = 0,
|
||||
#endif
|
||||
.threshold = 0,
|
||||
.verbose = 0
|
||||
};
|
||||
|
||||
struct alsa_params_req {
|
||||
int freq;
|
||||
audfmt_e fmt;
|
||||
int nchannels;
|
||||
unsigned int buffer_size;
|
||||
unsigned int period_size;
|
||||
};
|
||||
|
||||
struct alsa_params_obt {
|
||||
int freq;
|
||||
audfmt_e fmt;
|
||||
int nchannels;
|
||||
snd_pcm_uframes_t samples;
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
|
||||
int err,
|
||||
const char *typ,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
|
||||
}
|
||||
|
||||
static void alsa_anal_close (snd_pcm_t **handlep)
|
||||
{
|
||||
int err = snd_pcm_close (*handlep);
|
||||
if (err) {
|
||||
alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep);
|
||||
}
|
||||
*handlep = NULL;
|
||||
}
|
||||
|
||||
static int alsa_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int aud_to_alsafmt (audfmt_e fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AUD_FMT_S8:
|
||||
return SND_PCM_FORMAT_S8;
|
||||
|
||||
case AUD_FMT_U8:
|
||||
return SND_PCM_FORMAT_U8;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
return SND_PCM_FORMAT_S16_LE;
|
||||
|
||||
case AUD_FMT_U16:
|
||||
return SND_PCM_FORMAT_U16_LE;
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||
#ifdef DEBUG_AUDIO
|
||||
abort ();
|
||||
#endif
|
||||
return SND_PCM_FORMAT_U8;
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness)
|
||||
{
|
||||
switch (alsafmt) {
|
||||
case SND_PCM_FORMAT_S8:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_S8;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_U8:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_U8;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_S16_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_U16_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_S16_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_U16_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Unrecognized audio format %d\n", alsafmt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined DEBUG_MISMATCHES || defined DEBUG
|
||||
static void alsa_dump_info (struct alsa_params_req *req,
|
||||
struct alsa_params_obt *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 ("============================================\n");
|
||||
dolog ("requested: buffer size %d period size %d\n",
|
||||
req->buffer_size, req->period_size);
|
||||
dolog ("obtained: samples %ld\n", obt->samples);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
|
||||
{
|
||||
int err;
|
||||
snd_pcm_sw_params_t *sw_params;
|
||||
|
||||
snd_pcm_sw_params_alloca (&sw_params);
|
||||
|
||||
err = snd_pcm_sw_params_current (handle, sw_params);
|
||||
if (err < 0) {
|
||||
dolog ("Could not fully initialize DAC\n");
|
||||
alsa_logerr (err, "Failed to get current software parameters\n");
|
||||
return;
|
||||
}
|
||||
|
||||
err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);
|
||||
if (err < 0) {
|
||||
dolog ("Could not fully initialize DAC\n");
|
||||
alsa_logerr (err, "Failed to set software threshold to %ld\n",
|
||||
threshold);
|
||||
return;
|
||||
}
|
||||
|
||||
err = snd_pcm_sw_params (handle, sw_params);
|
||||
if (err < 0) {
|
||||
dolog ("Could not fully initialize DAC\n");
|
||||
alsa_logerr (err, "Failed to set software parameters\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_open (int in, struct alsa_params_req *req,
|
||||
struct alsa_params_obt *obt, snd_pcm_t **handlep)
|
||||
{
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
int err, freq, nchannels;
|
||||
const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;
|
||||
unsigned int period_size, buffer_size;
|
||||
snd_pcm_uframes_t obt_buffer_size;
|
||||
const char *typ = in ? "ADC" : "DAC";
|
||||
|
||||
freq = req->freq;
|
||||
period_size = req->period_size;
|
||||
buffer_size = req->buffer_size;
|
||||
nchannels = req->nchannels;
|
||||
|
||||
snd_pcm_hw_params_alloca (&hw_params);
|
||||
|
||||
err = snd_pcm_open (
|
||||
&handle,
|
||||
pcm_name,
|
||||
in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
|
||||
SND_PCM_NONBLOCK
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_any (handle, hw_params);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_access (
|
||||
handle,
|
||||
hw_params,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set access type\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_channels_near (
|
||||
handle,
|
||||
hw_params,
|
||||
&nchannels
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",
|
||||
req->nchannels);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (nchannels != 1 && nchannels != 2) {
|
||||
alsa_logerr2 (err, typ,
|
||||
"Can not handle obtained number of channels %d\n",
|
||||
nchannels);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out))) {
|
||||
if (!buffer_size) {
|
||||
buffer_size = DEFAULT_BUFFER_SIZE;
|
||||
period_size= DEFAULT_PERIOD_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer_size) {
|
||||
if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) {
|
||||
if (period_size) {
|
||||
err = snd_pcm_hw_params_set_period_time_near (
|
||||
handle,
|
||||
hw_params,
|
||||
&period_size,
|
||||
0
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ,
|
||||
"Failed to set period time %d\n",
|
||||
req->period_size);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_buffer_time_near (
|
||||
handle,
|
||||
hw_params,
|
||||
&buffer_size,
|
||||
0
|
||||
);
|
||||
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ,
|
||||
"Failed to set buffer time %d\n",
|
||||
req->buffer_size);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int dir;
|
||||
snd_pcm_uframes_t minval;
|
||||
|
||||
if (period_size) {
|
||||
minval = period_size;
|
||||
dir = 0;
|
||||
|
||||
err = snd_pcm_hw_params_get_period_size_min (
|
||||
hw_params,
|
||||
&minval,
|
||||
&dir
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr (
|
||||
err,
|
||||
"Could not get minmal period size for %s\n",
|
||||
typ
|
||||
);
|
||||
}
|
||||
else {
|
||||
if (period_size < minval) {
|
||||
if ((in && conf.period_size_in_overriden)
|
||||
|| (!in && conf.period_size_out_overriden)) {
|
||||
dolog ("%s period size(%d) is less "
|
||||
"than minmal period size(%ld)\n",
|
||||
typ,
|
||||
period_size,
|
||||
minval);
|
||||
}
|
||||
period_size = minval;
|
||||
}
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_period_size (
|
||||
handle,
|
||||
hw_params,
|
||||
period_size,
|
||||
0
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set period size %d\n",
|
||||
req->period_size);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
minval = buffer_size;
|
||||
err = snd_pcm_hw_params_get_buffer_size_min (
|
||||
hw_params,
|
||||
&minval
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "Could not get minmal buffer size for %s\n",
|
||||
typ);
|
||||
}
|
||||
else {
|
||||
if (buffer_size < minval) {
|
||||
if ((in && conf.buffer_size_in_overriden)
|
||||
|| (!in && conf.buffer_size_out_overriden)) {
|
||||
dolog (
|
||||
"%s buffer size(%d) is less "
|
||||
"than minimal buffer size(%ld)\n",
|
||||
typ,
|
||||
buffer_size,
|
||||
minval
|
||||
);
|
||||
}
|
||||
buffer_size = minval;
|
||||
}
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_buffer_size (
|
||||
handle,
|
||||
hw_params,
|
||||
buffer_size
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set buffer size %d\n",
|
||||
req->buffer_size);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
dolog ("warning: Buffer size is not set\n");
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params (handle, hw_params);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to get buffer size\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = snd_pcm_prepare (handle);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!in && conf.threshold) {
|
||||
snd_pcm_uframes_t threshold;
|
||||
int bytes_per_sec;
|
||||
|
||||
bytes_per_sec = freq
|
||||
<< (nchannels == 2)
|
||||
<< (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16);
|
||||
|
||||
threshold = (conf.threshold * bytes_per_sec) / 1000;
|
||||
alsa_set_threshold (handle, threshold);
|
||||
}
|
||||
|
||||
obt->fmt = req->fmt;
|
||||
obt->nchannels = nchannels;
|
||||
obt->freq = freq;
|
||||
obt->samples = obt_buffer_size;
|
||||
*handlep = handle;
|
||||
|
||||
#if defined DEBUG_MISMATCHES || defined DEBUG
|
||||
if (obt->fmt != req->fmt ||
|
||||
obt->nchannels != req->nchannels ||
|
||||
obt->freq != req->freq) {
|
||||
dolog ("Audio paramters mismatch for %s\n", typ);
|
||||
alsa_dump_info (req, obt);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
alsa_dump_info (req, obt);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
err:
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int alsa_recover (snd_pcm_t *handle)
|
||||
{
|
||||
int err = snd_pcm_prepare (handle);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "Failed to prepare handle %p\n", handle);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
|
||||
{
|
||||
snd_pcm_sframes_t avail;
|
||||
|
||||
avail = snd_pcm_avail_update (handle);
|
||||
if (avail < 0) {
|
||||
if (avail == -EPIPE) {
|
||||
if (!alsa_recover (handle)) {
|
||||
avail = snd_pcm_avail_update (handle);
|
||||
}
|
||||
}
|
||||
|
||||
if (avail < 0) {
|
||||
alsa_logerr (avail,
|
||||
"Could not obtain number of available frames\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return avail;
|
||||
}
|
||||
|
||||
static int alsa_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
int rpos, live, decr;
|
||||
int samples;
|
||||
uint8_t *dst;
|
||||
st_sample_t *src;
|
||||
snd_pcm_sframes_t avail;
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
avail = alsa_get_avail (alsa->handle);
|
||||
if (avail < 0) {
|
||||
dolog ("Could not get number of available playback frames\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = audio_MIN (live, avail);
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int len = audio_MIN (samples, left_till_end_samples);
|
||||
snd_pcm_sframes_t written;
|
||||
|
||||
src = hw->mix_buf + rpos;
|
||||
dst = advance (alsa->pcm_buf, rpos << hw->info.shift);
|
||||
|
||||
hw->clip (dst, src, len);
|
||||
|
||||
while (len) {
|
||||
written = snd_pcm_writei (alsa->handle, dst, len);
|
||||
|
||||
if (written <= 0) {
|
||||
switch (written) {
|
||||
case 0:
|
||||
if (conf.verbose) {
|
||||
dolog ("Failed to write %d frames (wrote zero)\n", len);
|
||||
}
|
||||
goto exit;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover (alsa->handle)) {
|
||||
alsa_logerr (written, "Failed to write %d frames\n",
|
||||
len);
|
||||
goto exit;
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Recovering from playback xrun\n");
|
||||
}
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
goto exit;
|
||||
|
||||
default:
|
||||
alsa_logerr (written, "Failed to write %d frames to %p\n",
|
||||
len, dst);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
mixeng_clear (src, written);
|
||||
rpos = (rpos + written) % hw->samples;
|
||||
samples -= written;
|
||||
len -= written;
|
||||
dst = advance (dst, written << hw->info.shift);
|
||||
src += written;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void alsa_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
|
||||
ldebug ("alsa_fini\n");
|
||||
alsa_anal_close (&alsa->handle);
|
||||
|
||||
if (alsa->pcm_buf) {
|
||||
qemu_free (alsa->pcm_buf);
|
||||
alsa->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
struct alsa_params_req req;
|
||||
struct alsa_params_obt obt;
|
||||
audfmt_e effective_fmt;
|
||||
int endianness;
|
||||
int err;
|
||||
snd_pcm_t *handle;
|
||||
audsettings_t obt_as;
|
||||
|
||||
req.fmt = aud_to_alsafmt (as->fmt);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.period_size = conf.period_size_out;
|
||||
req.buffer_size = conf.buffer_size_out;
|
||||
|
||||
if (alsa_open (0, &req, &obt, &handle)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
|
||||
if (err) {
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obt_as.freq = obt.freq;
|
||||
obt_as.nchannels = obt.nchannels;
|
||||
obt_as.fmt = effective_fmt;
|
||||
|
||||
audio_pcm_init_info (
|
||||
&hw->info,
|
||||
&obt_as,
|
||||
audio_need_to_swap_endian (endianness)
|
||||
);
|
||||
hw->samples = obt.samples;
|
||||
|
||||
alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
|
||||
if (!alsa->pcm_buf) {
|
||||
dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
alsa->handle = handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (pause) {
|
||||
err = snd_pcm_drop (handle);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "Could not stop %s\n", typ);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
err = snd_pcm_prepare (handle);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "Could not prepare handle for %s\n", typ);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
ldebug ("enabling voice\n");
|
||||
return alsa_voice_ctl (alsa->handle, "playback", 0);
|
||||
|
||||
case VOICE_DISABLE:
|
||||
ldebug ("disabling voice\n");
|
||||
return alsa_voice_ctl (alsa->handle, "playback", 1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
struct alsa_params_req req;
|
||||
struct alsa_params_obt obt;
|
||||
int endianness;
|
||||
int err;
|
||||
audfmt_e effective_fmt;
|
||||
snd_pcm_t *handle;
|
||||
audsettings_t obt_as;
|
||||
|
||||
req.fmt = aud_to_alsafmt (as->fmt);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.period_size = conf.period_size_in;
|
||||
req.buffer_size = conf.buffer_size_in;
|
||||
|
||||
if (alsa_open (1, &req, &obt, &handle)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
|
||||
if (err) {
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obt_as.freq = obt.freq;
|
||||
obt_as.nchannels = obt.nchannels;
|
||||
obt_as.fmt = effective_fmt;
|
||||
|
||||
audio_pcm_init_info (
|
||||
&hw->info,
|
||||
&obt_as,
|
||||
audio_need_to_swap_endian (endianness)
|
||||
);
|
||||
hw->samples = obt.samples;
|
||||
|
||||
alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
||||
if (!alsa->pcm_buf) {
|
||||
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
alsa->handle = handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alsa_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
|
||||
alsa_anal_close (&alsa->handle);
|
||||
|
||||
if (alsa->pcm_buf) {
|
||||
qemu_free (alsa->pcm_buf);
|
||||
alsa->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
int hwshift = hw->info.shift;
|
||||
int i;
|
||||
int live = audio_pcm_hw_get_live_in (hw);
|
||||
int dead = hw->samples - live;
|
||||
int decr;
|
||||
struct {
|
||||
int add;
|
||||
int len;
|
||||
} bufs[2] = {
|
||||
{ hw->wpos, 0 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
snd_pcm_sframes_t avail;
|
||||
snd_pcm_uframes_t read_samples = 0;
|
||||
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
avail = alsa_get_avail (alsa->handle);
|
||||
if (avail < 0) {
|
||||
dolog ("Could not get number of captured frames\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) {
|
||||
avail = hw->samples;
|
||||
}
|
||||
|
||||
decr = audio_MIN (dead, avail);
|
||||
if (!decr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hw->wpos + decr > hw->samples) {
|
||||
bufs[0].len = (hw->samples - hw->wpos);
|
||||
bufs[1].len = (decr - (hw->samples - hw->wpos));
|
||||
}
|
||||
else {
|
||||
bufs[0].len = decr;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
void *src;
|
||||
st_sample_t *dst;
|
||||
snd_pcm_sframes_t nread;
|
||||
snd_pcm_uframes_t len;
|
||||
|
||||
len = bufs[i].len;
|
||||
|
||||
src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
|
||||
dst = hw->conv_buf + bufs[i].add;
|
||||
|
||||
while (len) {
|
||||
nread = snd_pcm_readi (alsa->handle, src, len);
|
||||
|
||||
if (nread <= 0) {
|
||||
switch (nread) {
|
||||
case 0:
|
||||
if (conf.verbose) {
|
||||
dolog ("Failed to read %ld frames (read zero)\n", len);
|
||||
}
|
||||
goto exit;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover (alsa->handle)) {
|
||||
alsa_logerr (nread, "Failed to read %ld frames\n", len);
|
||||
goto exit;
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Recovering from capture xrun\n");
|
||||
}
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
goto exit;
|
||||
|
||||
default:
|
||||
alsa_logerr (
|
||||
nread,
|
||||
"Failed to read %ld frames from %p\n",
|
||||
len,
|
||||
src
|
||||
);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
hw->conv (dst, src, nread, &nominal_volume);
|
||||
|
||||
src = advance (src, nread << hwshift);
|
||||
dst += nread;
|
||||
|
||||
read_samples += nread;
|
||||
len -= nread;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
hw->wpos = (hw->wpos + read_samples) % hw->samples;
|
||||
return read_samples;
|
||||
}
|
||||
|
||||
static int alsa_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, size);
|
||||
}
|
||||
|
||||
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
ldebug ("enabling voice\n");
|
||||
return alsa_voice_ctl (alsa->handle, "capture", 0);
|
||||
|
||||
case VOICE_DISABLE:
|
||||
ldebug ("disabling voice\n");
|
||||
return alsa_voice_ctl (alsa->handle, "capture", 1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *alsa_audio_init (void)
|
||||
{
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void alsa_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
static struct audio_option alsa_options[] = {
|
||||
{"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out,
|
||||
"DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
|
||||
{"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out,
|
||||
"DAC period size", &conf.period_size_out_overriden, 0},
|
||||
{"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out,
|
||||
"DAC buffer size", &conf.buffer_size_out_overriden, 0},
|
||||
|
||||
{"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in,
|
||||
"ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
|
||||
{"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in,
|
||||
"ADC period size", &conf.period_size_in_overriden, 0},
|
||||
{"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in,
|
||||
"ADC buffer size", &conf.buffer_size_in_overriden, 0},
|
||||
|
||||
{"THRESHOLD", AUD_OPT_INT, &conf.threshold,
|
||||
"(undocumented)", NULL, 0},
|
||||
|
||||
{"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out,
|
||||
"DAC device name (for instance dmix)", NULL, 0},
|
||||
|
||||
{"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in,
|
||||
"ADC device name", NULL, 0},
|
||||
|
||||
{"VERBOSE", AUD_OPT_BOOL, &conf.verbose,
|
||||
"Behave in a more verbose way", NULL, 0},
|
||||
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops alsa_pcm_ops = {
|
||||
alsa_init_out,
|
||||
alsa_fini_out,
|
||||
alsa_run_out,
|
||||
alsa_write,
|
||||
alsa_ctl_out,
|
||||
|
||||
alsa_init_in,
|
||||
alsa_fini_in,
|
||||
alsa_run_in,
|
||||
alsa_read,
|
||||
alsa_ctl_in
|
||||
};
|
||||
|
||||
struct audio_driver alsa_audio_driver = {
|
||||
INIT_FIELD (name = ) "alsa",
|
||||
INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org",
|
||||
INIT_FIELD (options = ) alsa_options,
|
||||
INIT_FIELD (init = ) alsa_audio_init,
|
||||
INIT_FIELD (fini = ) alsa_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &alsa_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 1,
|
||||
INIT_FIELD (max_voices_out = ) INT_MAX,
|
||||
INIT_FIELD (max_voices_in = ) INT_MAX,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn)
|
||||
};
|
||||
1481
audio/audio.c
Normal file
1481
audio/audio.c
Normal file
File diff suppressed because it is too large
Load Diff
133
audio/audio.h
Normal file
133
audio/audio.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* QEMU Audio subsystem header
|
||||
*
|
||||
* Copyright (c) 2003-2005 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 "sys-queue.h"
|
||||
|
||||
typedef void (*audio_callback_fn_t) (void *opaque, int avail);
|
||||
|
||||
typedef enum {
|
||||
AUD_FMT_U8,
|
||||
AUD_FMT_S8,
|
||||
AUD_FMT_U16,
|
||||
AUD_FMT_S16
|
||||
} audfmt_e;
|
||||
|
||||
typedef struct {
|
||||
int freq;
|
||||
int nchannels;
|
||||
audfmt_e fmt;
|
||||
} audsettings_t;
|
||||
|
||||
typedef struct AudioState AudioState;
|
||||
typedef struct SWVoiceOut SWVoiceOut;
|
||||
typedef struct SWVoiceIn SWVoiceIn;
|
||||
|
||||
typedef struct QEMUSoundCard {
|
||||
AudioState *audio;
|
||||
char *name;
|
||||
LIST_ENTRY (QEMUSoundCard) entries;
|
||||
} QEMUSoundCard;
|
||||
|
||||
typedef struct QEMUAudioTimeStamp {
|
||||
uint64_t old_ts;
|
||||
} QEMUAudioTimeStamp;
|
||||
|
||||
void AUD_vlog (const char *cap, const char *fmt, va_list ap);
|
||||
void AUD_log (const char *cap, const char *fmt, ...)
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((__format__ (__printf__, 2, 3)))
|
||||
#endif
|
||||
;
|
||||
|
||||
AudioState *AUD_init (void);
|
||||
void AUD_help (void);
|
||||
void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card);
|
||||
void AUD_remove_card (QEMUSoundCard *card);
|
||||
|
||||
SWVoiceOut *AUD_open_out (
|
||||
QEMUSoundCard *card,
|
||||
SWVoiceOut *sw,
|
||||
const char *name,
|
||||
void *callback_opaque,
|
||||
audio_callback_fn_t callback_fn,
|
||||
audsettings_t *settings,
|
||||
int sw_endian
|
||||
);
|
||||
|
||||
void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
|
||||
int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
|
||||
int AUD_get_buffer_size_out (SWVoiceOut *sw);
|
||||
void AUD_set_active_out (SWVoiceOut *sw, int on);
|
||||
int AUD_is_active_out (SWVoiceOut *sw);
|
||||
|
||||
void AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
|
||||
uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
|
||||
|
||||
SWVoiceIn *AUD_open_in (
|
||||
QEMUSoundCard *card,
|
||||
SWVoiceIn *sw,
|
||||
const char *name,
|
||||
void *callback_opaque,
|
||||
audio_callback_fn_t callback_fn,
|
||||
audsettings_t *settings,
|
||||
int sw_endian
|
||||
);
|
||||
|
||||
void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
|
||||
int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
|
||||
void AUD_set_active_in (SWVoiceIn *sw, int on);
|
||||
int AUD_is_active_in (SWVoiceIn *sw);
|
||||
|
||||
void AUD_init_time_stamp_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
|
||||
uint64_t AUD_get_elapsed_usec_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
|
||||
|
||||
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);
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define audio_MIN(a, b) ( __extension__ ({ \
|
||||
__typeof (a) ta = a; \
|
||||
__typeof (b) tb = b; \
|
||||
((ta)>(tb)?(tb):(ta)); \
|
||||
}))
|
||||
|
||||
#define audio_MAX(a, b) ( __extension__ ({ \
|
||||
__typeof (a) ta = a; \
|
||||
__typeof (b) tb = b; \
|
||||
((ta)<(tb)?(tb):(ta)); \
|
||||
}))
|
||||
#else
|
||||
#define audio_MIN(a, b) ((a)>(b)?(b):(a))
|
||||
#define audio_MAX(a, b) ((a)<(b)?(b):(a))
|
||||
#endif
|
||||
|
||||
#endif /* audio.h */
|
||||
268
audio/audio_int.h
Normal file
268
audio/audio_int.h
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* QEMU Audio subsystem header
|
||||
*
|
||||
* Copyright (c) 2003-2005 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
|
||||
|
||||
#ifdef CONFIG_COREAUDIO
|
||||
#define FLOAT_MIXENG
|
||||
/* #define RECIPROCAL */
|
||||
#endif
|
||||
#include "mixeng.h"
|
||||
|
||||
struct audio_pcm_ops;
|
||||
|
||||
typedef enum {
|
||||
AUD_OPT_INT,
|
||||
AUD_OPT_FMT,
|
||||
AUD_OPT_STR,
|
||||
AUD_OPT_BOOL
|
||||
} audio_option_tag_e;
|
||||
|
||||
struct audio_option {
|
||||
const char *name;
|
||||
audio_option_tag_e tag;
|
||||
void *valp;
|
||||
const char *descr;
|
||||
int *overridenp;
|
||||
int overriden;
|
||||
};
|
||||
|
||||
struct audio_callback {
|
||||
void *opaque;
|
||||
audio_callback_fn_t fn;
|
||||
};
|
||||
|
||||
struct audio_pcm_info {
|
||||
int bits;
|
||||
int sign;
|
||||
int freq;
|
||||
int nchannels;
|
||||
int align;
|
||||
int shift;
|
||||
int bytes_per_second;
|
||||
int swap_endian;
|
||||
};
|
||||
|
||||
typedef struct HWVoiceOut {
|
||||
int enabled;
|
||||
int pending_disable;
|
||||
int valid;
|
||||
struct audio_pcm_info info;
|
||||
|
||||
f_sample *clip;
|
||||
|
||||
int rpos;
|
||||
uint64_t ts_helper;
|
||||
|
||||
st_sample_t *mix_buf;
|
||||
|
||||
int samples;
|
||||
LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
LIST_ENTRY (HWVoiceOut) entries;
|
||||
} HWVoiceOut;
|
||||
|
||||
typedef struct HWVoiceIn {
|
||||
int enabled;
|
||||
struct audio_pcm_info info;
|
||||
|
||||
t_sample *conv;
|
||||
|
||||
int wpos;
|
||||
int total_samples_captured;
|
||||
uint64_t ts_helper;
|
||||
|
||||
st_sample_t *conv_buf;
|
||||
|
||||
int samples;
|
||||
LIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
LIST_ENTRY (HWVoiceIn) entries;
|
||||
} HWVoiceIn;
|
||||
|
||||
struct SWVoiceOut {
|
||||
struct audio_pcm_info info;
|
||||
t_sample *conv;
|
||||
int64_t ratio;
|
||||
st_sample_t *buf;
|
||||
void *rate;
|
||||
int total_hw_samples_mixed;
|
||||
int active;
|
||||
int empty;
|
||||
HWVoiceOut *hw;
|
||||
char *name;
|
||||
volume_t vol;
|
||||
struct audio_callback callback;
|
||||
LIST_ENTRY (SWVoiceOut) entries;
|
||||
};
|
||||
|
||||
struct SWVoiceIn {
|
||||
int active;
|
||||
struct audio_pcm_info info;
|
||||
int64_t ratio;
|
||||
void *rate;
|
||||
int total_hw_samples_acquired;
|
||||
st_sample_t *buf;
|
||||
f_sample *clip;
|
||||
HWVoiceIn *hw;
|
||||
char *name;
|
||||
volume_t vol;
|
||||
struct audio_callback callback;
|
||||
LIST_ENTRY (SWVoiceIn) entries;
|
||||
};
|
||||
|
||||
struct audio_driver {
|
||||
const char *name;
|
||||
const char *descr;
|
||||
struct audio_option *options;
|
||||
void *(*init) (void);
|
||||
void (*fini) (void *);
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
int can_be_default;
|
||||
int max_voices_out;
|
||||
int max_voices_in;
|
||||
int voice_size_out;
|
||||
int voice_size_in;
|
||||
};
|
||||
|
||||
struct audio_pcm_ops {
|
||||
int (*init_out)(HWVoiceOut *hw, audsettings_t *as);
|
||||
void (*fini_out)(HWVoiceOut *hw);
|
||||
int (*run_out) (HWVoiceOut *hw);
|
||||
int (*write) (SWVoiceOut *sw, void *buf, int size);
|
||||
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
|
||||
|
||||
int (*init_in) (HWVoiceIn *hw, audsettings_t *as);
|
||||
void (*fini_in) (HWVoiceIn *hw);
|
||||
int (*run_in) (HWVoiceIn *hw);
|
||||
int (*read) (SWVoiceIn *sw, void *buf, int size);
|
||||
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
|
||||
};
|
||||
|
||||
struct AudioState {
|
||||
struct audio_driver *drv;
|
||||
void *drv_opaque;
|
||||
|
||||
QEMUTimer *ts;
|
||||
LIST_HEAD (card_head, QEMUSoundCard) card_head;
|
||||
LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
|
||||
LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
|
||||
int nb_hw_voices_out;
|
||||
int nb_hw_voices_in;
|
||||
};
|
||||
|
||||
extern struct audio_driver no_audio_driver;
|
||||
extern struct audio_driver oss_audio_driver;
|
||||
extern struct audio_driver sdl_audio_driver;
|
||||
extern struct audio_driver wav_audio_driver;
|
||||
extern struct audio_driver fmod_audio_driver;
|
||||
extern struct audio_driver alsa_audio_driver;
|
||||
extern struct audio_driver coreaudio_audio_driver;
|
||||
extern struct audio_driver dsound_audio_driver;
|
||||
extern volume_t nominal_volume;
|
||||
|
||||
void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as,
|
||||
int swap_endian);
|
||||
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
|
||||
|
||||
int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
|
||||
int audio_pcm_hw_get_live_in (HWVoiceIn *hw);
|
||||
|
||||
int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
|
||||
int audio_pcm_hw_get_live_out (HWVoiceOut *hw);
|
||||
int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live);
|
||||
|
||||
int audio_bug (const char *funcname, int cond);
|
||||
void *audio_calloc (const char *funcname, int nmemb, size_t size);
|
||||
|
||||
#define VOICE_ENABLE 1
|
||||
#define VOICE_DISABLE 2
|
||||
|
||||
static inline int audio_ring_dist (int dst, int src, int len)
|
||||
{
|
||||
return (dst >= src) ? (dst - src) : (len - src + dst);
|
||||
}
|
||||
|
||||
static inline int audio_need_to_swap_endian (int endianness)
|
||||
{
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
return endianness != 1;
|
||||
#else
|
||||
return endianness != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined __GNUC__
|
||||
#define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2)))
|
||||
#define INIT_FIELD(f) . f
|
||||
#define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (__printf__, n, m)))
|
||||
#else
|
||||
#define GCC_ATTR /**/
|
||||
#define INIT_FIELD(f) /**/
|
||||
#define GCC_FMT_ATTR(n, m)
|
||||
#endif
|
||||
|
||||
static void GCC_ATTR dolog (const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void GCC_ATTR ldebug (const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
}
|
||||
#else
|
||||
#if defined NDEBUG && defined __GNUC__
|
||||
#define ldebug(...)
|
||||
#elif defined NDEBUG && defined _MSC_VER
|
||||
#define ldebug __noop
|
||||
#else
|
||||
static void GCC_ATTR ldebug (const char *fmt, ...)
|
||||
{
|
||||
(void) fmt;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#undef GCC_ATTR
|
||||
|
||||
#define AUDIO_STRINGIFY_(n) #n
|
||||
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
|
||||
|
||||
#if defined _MSC_VER || defined __GNUC__
|
||||
#define AUDIO_FUNC __FUNCTION__
|
||||
#else
|
||||
#define AUDIO_FUNC __FILE__ ":" AUDIO_STRINGIFY (__LINE__)
|
||||
#endif
|
||||
|
||||
#endif /* audio_int.h */
|
||||
565
audio/audio_template.h
Normal file
565
audio/audio_template.h
Normal file
@@ -0,0 +1,565 @@
|
||||
/*
|
||||
* QEMU Audio subsystem header
|
||||
*
|
||||
* Copyright (c) 2005 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.
|
||||
*/
|
||||
|
||||
#ifdef DAC
|
||||
#define NAME "playback"
|
||||
#define HWBUF hw->mix_buf
|
||||
#define TYPE out
|
||||
#define HW HWVoiceOut
|
||||
#define SW SWVoiceOut
|
||||
#else
|
||||
#define NAME "capture"
|
||||
#define TYPE in
|
||||
#define HW HWVoiceIn
|
||||
#define SW SWVoiceIn
|
||||
#define HWBUF hw->conv_buf
|
||||
#endif
|
||||
|
||||
static void glue (audio_init_nb_voices_, TYPE) (
|
||||
AudioState *s,
|
||||
struct audio_driver *drv
|
||||
)
|
||||
{
|
||||
int max_voices = glue (drv->max_voices_, TYPE);
|
||||
int voice_size = glue (drv->voice_size_, TYPE);
|
||||
|
||||
if (glue (s->nb_hw_voices_, TYPE) > max_voices) {
|
||||
if (!max_voices) {
|
||||
#ifdef DAC
|
||||
dolog ("Driver `%s' does not support " NAME "\n", drv->name);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n",
|
||||
drv->name,
|
||||
glue (s->nb_hw_voices_, TYPE),
|
||||
max_voices);
|
||||
}
|
||||
glue (s->nb_hw_voices_, TYPE) = max_voices;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, !voice_size && max_voices)) {
|
||||
dolog ("drv=`%s' voice_size=0 max_voices=%d\n",
|
||||
drv->name, max_voices);
|
||||
glue (s->nb_hw_voices_, TYPE) = 0;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, voice_size && !max_voices)) {
|
||||
dolog ("drv=`%s' voice_size=%d max_voices=0\n",
|
||||
drv->name, voice_size);
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
|
||||
{
|
||||
if (HWBUF) {
|
||||
qemu_free (HWBUF);
|
||||
}
|
||||
|
||||
HWBUF = NULL;
|
||||
}
|
||||
|
||||
static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
|
||||
{
|
||||
HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t));
|
||||
if (!HWBUF) {
|
||||
dolog ("Could not allocate " NAME " buffer (%d samples)\n",
|
||||
hw->samples);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
|
||||
{
|
||||
if (sw->buf) {
|
||||
qemu_free (sw->buf);
|
||||
}
|
||||
|
||||
if (sw->rate) {
|
||||
st_rate_stop (sw->rate);
|
||||
}
|
||||
|
||||
sw->buf = NULL;
|
||||
sw->rate = NULL;
|
||||
}
|
||||
|
||||
static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
|
||||
{
|
||||
int samples;
|
||||
|
||||
#ifdef DAC
|
||||
samples = sw->hw->samples;
|
||||
#else
|
||||
samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
|
||||
#endif
|
||||
|
||||
sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (st_sample_t));
|
||||
if (!sw->buf) {
|
||||
dolog ("Could not allocate buffer for `%s' (%d samples)\n",
|
||||
SW_NAME (sw), samples);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DAC
|
||||
sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq);
|
||||
#else
|
||||
sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq);
|
||||
#endif
|
||||
if (!sw->rate) {
|
||||
qemu_free (sw->buf);
|
||||
sw->buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int glue (audio_pcm_sw_init_, TYPE) (
|
||||
SW *sw,
|
||||
HW *hw,
|
||||
const char *name,
|
||||
audsettings_t *as,
|
||||
int endian
|
||||
)
|
||||
{
|
||||
int err;
|
||||
|
||||
audio_pcm_init_info (&sw->info, as, audio_need_to_swap_endian (endian));
|
||||
sw->hw = hw;
|
||||
sw->active = 0;
|
||||
#ifdef DAC
|
||||
sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq;
|
||||
sw->total_hw_samples_mixed = 0;
|
||||
sw->empty = 1;
|
||||
#else
|
||||
sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
|
||||
#endif
|
||||
|
||||
#ifdef DAC
|
||||
sw->conv = mixeng_conv
|
||||
#else
|
||||
sw->clip = mixeng_clip
|
||||
#endif
|
||||
[sw->info.nchannels == 2]
|
||||
[sw->info.sign]
|
||||
[sw->info.swap_endian]
|
||||
[sw->info.bits == 16];
|
||||
|
||||
sw->name = qemu_strdup (name);
|
||||
err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw);
|
||||
if (err) {
|
||||
qemu_free (sw->name);
|
||||
sw->name = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
|
||||
{
|
||||
glue (audio_pcm_sw_free_resources_, TYPE) (sw);
|
||||
if (sw->name) {
|
||||
qemu_free (sw->name);
|
||||
sw->name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw)
|
||||
{
|
||||
LIST_INSERT_HEAD (&hw->sw_head, sw, entries);
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
|
||||
{
|
||||
LIST_REMOVE (sw, entries);
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp)
|
||||
{
|
||||
HW *hw = *hwp;
|
||||
|
||||
if (!hw->sw_head.lh_first) {
|
||||
LIST_REMOVE (hw, entries);
|
||||
glue (s->nb_hw_voices_, TYPE) += 1;
|
||||
glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
|
||||
glue (hw->pcm_ops->fini_, TYPE) (hw);
|
||||
qemu_free (hw);
|
||||
*hwp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static HW *glue (audio_pcm_hw_find_any_, TYPE) (AudioState *s, HW *hw)
|
||||
{
|
||||
return hw ? hw->entries.le_next : s->glue (hw_head_, TYPE).lh_first;
|
||||
}
|
||||
|
||||
static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (AudioState *s, HW *hw)
|
||||
{
|
||||
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) {
|
||||
if (hw->enabled) {
|
||||
return hw;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
|
||||
AudioState *s,
|
||||
HW *hw,
|
||||
audsettings_t *as
|
||||
)
|
||||
{
|
||||
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) {
|
||||
if (audio_pcm_info_eq (&hw->info, as)) {
|
||||
return hw;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
|
||||
{
|
||||
HW *hw;
|
||||
struct audio_driver *drv = s->drv;
|
||||
|
||||
if (!glue (s->nb_hw_voices_, TYPE)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, !drv)) {
|
||||
dolog ("No host audio driver\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, !drv->pcm_ops)) {
|
||||
dolog ("Host audio driver without pcm_ops\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE));
|
||||
if (!hw) {
|
||||
dolog ("Can not allocate voice `%s' size %d\n",
|
||||
drv->name, glue (drv->voice_size_, TYPE));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hw->pcm_ops = drv->pcm_ops;
|
||||
LIST_INIT (&hw->sw_head);
|
||||
|
||||
if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
|
||||
goto err0;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
|
||||
dolog ("hw->samples=%d\n", hw->samples);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
#ifdef DAC
|
||||
hw->clip = mixeng_clip
|
||||
#else
|
||||
hw->conv = mixeng_conv
|
||||
#endif
|
||||
[hw->info.nchannels == 2]
|
||||
[hw->info.sign]
|
||||
[hw->info.swap_endian]
|
||||
[hw->info.bits == 16];
|
||||
|
||||
if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
|
||||
goto err1;
|
||||
}
|
||||
|
||||
LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
|
||||
glue (s->nb_hw_voices_, TYPE) -= 1;
|
||||
return hw;
|
||||
|
||||
err1:
|
||||
glue (hw->pcm_ops->fini_, TYPE) (hw);
|
||||
err0:
|
||||
qemu_free (hw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HW *glue (audio_pcm_hw_add_, TYPE) (AudioState *s, audsettings_t *as)
|
||||
{
|
||||
HW *hw;
|
||||
|
||||
if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
|
||||
hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
|
||||
if (hw) {
|
||||
return hw;
|
||||
}
|
||||
}
|
||||
|
||||
hw = glue (audio_pcm_hw_find_specific_, TYPE) (s, NULL, as);
|
||||
if (hw) {
|
||||
return hw;
|
||||
}
|
||||
|
||||
hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
|
||||
if (hw) {
|
||||
return hw;
|
||||
}
|
||||
|
||||
return glue (audio_pcm_hw_find_any_, TYPE) (s, NULL);
|
||||
}
|
||||
|
||||
static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
|
||||
AudioState *s,
|
||||
const char *sw_name,
|
||||
audsettings_t *as,
|
||||
int sw_endian
|
||||
)
|
||||
{
|
||||
SW *sw;
|
||||
HW *hw;
|
||||
audsettings_t hw_as;
|
||||
|
||||
if (glue (conf.fixed_, TYPE).enabled) {
|
||||
hw_as = glue (conf.fixed_, TYPE).settings;
|
||||
}
|
||||
else {
|
||||
hw_as = *as;
|
||||
}
|
||||
|
||||
sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw));
|
||||
if (!sw) {
|
||||
dolog ("Could not allocate soft voice `%s' (%zu bytes)\n",
|
||||
sw_name ? sw_name : "unknown", sizeof (*sw));
|
||||
goto err1;
|
||||
}
|
||||
|
||||
hw = glue (audio_pcm_hw_add_, TYPE) (s, &hw_as);
|
||||
if (!hw) {
|
||||
goto err2;
|
||||
}
|
||||
|
||||
glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
|
||||
|
||||
if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as, sw_endian)) {
|
||||
goto err3;
|
||||
}
|
||||
|
||||
return sw;
|
||||
|
||||
err3:
|
||||
glue (audio_pcm_hw_del_sw_, TYPE) (sw);
|
||||
glue (audio_pcm_hw_gc_, TYPE) (s, &hw);
|
||||
err2:
|
||||
qemu_free (sw);
|
||||
err1:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void glue (audio_close_, TYPE) (AudioState *s, SW *sw)
|
||||
{
|
||||
glue (audio_pcm_sw_fini_, TYPE) (sw);
|
||||
glue (audio_pcm_hw_del_sw_, TYPE) (sw);
|
||||
glue (audio_pcm_hw_gc_, TYPE) (s, &sw->hw);
|
||||
qemu_free (sw);
|
||||
}
|
||||
|
||||
void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
|
||||
{
|
||||
if (sw) {
|
||||
if (audio_bug (AUDIO_FUNC, !card || !card->audio)) {
|
||||
dolog ("card=%p card->audio=%p\n",
|
||||
card, card ? card->audio : NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
glue (audio_close_, TYPE) (card->audio, sw);
|
||||
}
|
||||
}
|
||||
|
||||
SW *glue (AUD_open_, TYPE) (
|
||||
QEMUSoundCard *card,
|
||||
SW *sw,
|
||||
const char *name,
|
||||
void *callback_opaque ,
|
||||
audio_callback_fn_t callback_fn,
|
||||
audsettings_t *as,
|
||||
int sw_endian
|
||||
)
|
||||
{
|
||||
AudioState *s;
|
||||
#ifdef DAC
|
||||
int live = 0;
|
||||
SW *old_sw = NULL;
|
||||
#endif
|
||||
|
||||
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
|
||||
name, as->freq, as->nchannels, as->fmt);
|
||||
|
||||
if (audio_bug (AUDIO_FUNC,
|
||||
!card || !card->audio || !name || !callback_fn || !as)) {
|
||||
dolog ("card=%p card->audio=%p name=%p callback_fn=%p as=%p\n",
|
||||
card, card ? card->audio : NULL, name, callback_fn, as);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s = card->audio;
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, audio_validate_settigs (as))) {
|
||||
audio_print_settings (as);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, !s->drv)) {
|
||||
dolog ("Can not open `%s' (no host audio driver)\n", name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (sw && audio_pcm_info_eq (&sw->info, as)) {
|
||||
return sw;
|
||||
}
|
||||
|
||||
#ifdef DAC
|
||||
if (conf.plive && sw && (!sw->active && !sw->empty)) {
|
||||
live = sw->total_hw_samples_mixed;
|
||||
|
||||
#ifdef DEBUG_PLIVE
|
||||
dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live);
|
||||
dolog ("Old %s freq %d, bits %d, channels %d\n",
|
||||
SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels);
|
||||
dolog ("New %s freq %d, bits %d, channels %d\n",
|
||||
name,
|
||||
freq,
|
||||
(fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8,
|
||||
nchannels);
|
||||
#endif
|
||||
|
||||
if (live) {
|
||||
old_sw = sw;
|
||||
old_sw->callback.fn = NULL;
|
||||
sw = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!glue (conf.fixed_, TYPE).enabled && sw) {
|
||||
glue (AUD_close_, TYPE) (card, sw);
|
||||
sw = NULL;
|
||||
}
|
||||
|
||||
if (sw) {
|
||||
HW *hw = sw->hw;
|
||||
|
||||
if (!hw) {
|
||||
dolog ("Internal logic error voice `%s' has no hardware store\n",
|
||||
SW_NAME (sw));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
glue (audio_pcm_sw_fini_, TYPE) (sw);
|
||||
if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as, sw_endian)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else {
|
||||
sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as, sw_endian);
|
||||
if (!sw) {
|
||||
dolog ("Failed to create voice `%s'\n", name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (sw) {
|
||||
sw->vol = nominal_volume;
|
||||
sw->callback.fn = callback_fn;
|
||||
sw->callback.opaque = callback_opaque;
|
||||
|
||||
#ifdef DAC
|
||||
if (live) {
|
||||
int mixed =
|
||||
(live << old_sw->info.shift)
|
||||
* old_sw->info.bytes_per_second
|
||||
/ sw->info.bytes_per_second;
|
||||
|
||||
#ifdef DEBUG_PLIVE
|
||||
dolog ("Silence will be mixed %d\n", mixed);
|
||||
#endif
|
||||
sw->total_hw_samples_mixed += mixed;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
dolog ("%s\n", name);
|
||||
audio_pcm_print_info ("hw", &sw->hw->info);
|
||||
audio_pcm_print_info ("sw", &sw->info);
|
||||
#endif
|
||||
}
|
||||
|
||||
return sw;
|
||||
|
||||
fail:
|
||||
glue (AUD_close_, TYPE) (card, sw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int glue (AUD_is_active_, TYPE) (SW *sw)
|
||||
{
|
||||
return sw ? sw->active : 0;
|
||||
}
|
||||
|
||||
void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
|
||||
{
|
||||
if (!sw) {
|
||||
return;
|
||||
}
|
||||
|
||||
ts->old_ts = sw->hw->ts_helper;
|
||||
}
|
||||
|
||||
uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
|
||||
{
|
||||
uint64_t delta, cur_ts, old_ts;
|
||||
|
||||
if (!sw) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cur_ts = sw->hw->ts_helper;
|
||||
old_ts = ts->old_ts;
|
||||
/* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */
|
||||
|
||||
if (cur_ts >= old_ts) {
|
||||
delta = cur_ts - old_ts;
|
||||
}
|
||||
else {
|
||||
delta = UINT64_MAX - old_ts + cur_ts;
|
||||
}
|
||||
|
||||
if (!delta) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (delta * sw->hw->info.freq) / 1000000;
|
||||
}
|
||||
|
||||
#undef TYPE
|
||||
#undef HW
|
||||
#undef SW
|
||||
#undef HWBUF
|
||||
#undef NAME
|
||||
564
audio/coreaudio.c
Normal file
564
audio/coreaudio.c
Normal file
@@ -0,0 +1,564 @@
|
||||
/*
|
||||
* QEMU OS X CoreAudio audio driver
|
||||
*
|
||||
* Copyright (c) 2005 Mike Kronenberg
|
||||
*
|
||||
* 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 <CoreAudio/CoreAudio.h>
|
||||
#include <string.h> /* strerror */
|
||||
#include <pthread.h> /* pthread_X */
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
#define AUDIO_CAP "coreaudio"
|
||||
#include "audio_int.h"
|
||||
|
||||
struct {
|
||||
int buffer_frames;
|
||||
int nbuffers;
|
||||
int isAtexit;
|
||||
} conf = {
|
||||
.buffer_frames = 512,
|
||||
.nbuffers = 4,
|
||||
.isAtexit = 0
|
||||
};
|
||||
|
||||
typedef struct coreaudioVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
pthread_mutex_t mutex;
|
||||
int isAtexit;
|
||||
AudioDeviceID outputDeviceID;
|
||||
UInt32 audioDevicePropertyBufferFrameSize;
|
||||
AudioStreamBasicDescription outputStreamBasicDescription;
|
||||
int live;
|
||||
int decr;
|
||||
int rpos;
|
||||
} coreaudioVoiceOut;
|
||||
|
||||
static void coreaudio_logstatus (OSStatus status)
|
||||
{
|
||||
char *str = "BUG";
|
||||
|
||||
switch(status) {
|
||||
case kAudioHardwareNoError:
|
||||
str = "kAudioHardwareNoError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareNotRunningError:
|
||||
str = "kAudioHardwareNotRunningError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareUnspecifiedError:
|
||||
str = "kAudioHardwareUnspecifiedError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareUnknownPropertyError:
|
||||
str = "kAudioHardwareUnknownPropertyError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareBadPropertySizeError:
|
||||
str = "kAudioHardwareBadPropertySizeError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareIllegalOperationError:
|
||||
str = "kAudioHardwareIllegalOperationError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareBadDeviceError:
|
||||
str = "kAudioHardwareBadDeviceError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareBadStreamError:
|
||||
str = "kAudioHardwareBadStreamError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareUnsupportedOperationError:
|
||||
str = "kAudioHardwareUnsupportedOperationError";
|
||||
break;
|
||||
|
||||
case kAudioDeviceUnsupportedFormatError:
|
||||
str = "kAudioDeviceUnsupportedFormatError";
|
||||
break;
|
||||
|
||||
case kAudioDevicePermissionsError:
|
||||
str = "kAudioDevicePermissionsError";
|
||||
break;
|
||||
|
||||
default:
|
||||
AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", str);
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
|
||||
OSStatus status,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_log (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
coreaudio_logstatus (status);
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
|
||||
OSStatus status,
|
||||
const char *typ,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
coreaudio_logstatus (status);
|
||||
}
|
||||
|
||||
static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
|
||||
{
|
||||
OSStatus status;
|
||||
UInt32 result = 0;
|
||||
UInt32 propertySize = sizeof(outputDeviceID);
|
||||
status = AudioDeviceGetProperty(
|
||||
outputDeviceID, 0, 0,
|
||||
kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr(status,
|
||||
"Could not determine whether Device is playing\n");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void coreaudio_atexit (void)
|
||||
{
|
||||
conf.isAtexit = 1;
|
||||
}
|
||||
|
||||
static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_lock (&core->mutex);
|
||||
if (err) {
|
||||
dolog ("Could not lock voice for %s\nReason: %s\n",
|
||||
fn_name, strerror (err));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_unlock (&core->mutex);
|
||||
if (err) {
|
||||
dolog ("Could not unlock voice for %s\nReason: %s\n",
|
||||
fn_name, strerror (err));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coreaudio_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
int live, decr;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
if (coreaudio_lock (core, "coreaudio_run_out")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
|
||||
if (core->decr > live) {
|
||||
ldebug ("core->decr %d live %d core->live %d\n",
|
||||
core->decr,
|
||||
live,
|
||||
core->live);
|
||||
}
|
||||
|
||||
decr = audio_MIN (core->decr, live);
|
||||
core->decr -= decr;
|
||||
|
||||
core->live = live - decr;
|
||||
hw->rpos = core->rpos;
|
||||
|
||||
coreaudio_unlock (core, "coreaudio_run_out");
|
||||
return decr;
|
||||
}
|
||||
|
||||
/* callback to feed audiooutput buffer */
|
||||
static OSStatus audioDeviceIOProc(
|
||||
AudioDeviceID inDevice,
|
||||
const AudioTimeStamp* inNow,
|
||||
const AudioBufferList* inInputData,
|
||||
const AudioTimeStamp* inInputTime,
|
||||
AudioBufferList* outOutputData,
|
||||
const AudioTimeStamp* inOutputTime,
|
||||
void* hwptr)
|
||||
{
|
||||
UInt32 frame, frameCount;
|
||||
float *out = outOutputData->mBuffers[0].mData;
|
||||
HWVoiceOut *hw = hwptr;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
|
||||
int rpos, live;
|
||||
st_sample_t *src;
|
||||
#ifndef FLOAT_MIXENG
|
||||
#ifdef RECIPROCAL
|
||||
const float scale = 1.f / UINT_MAX;
|
||||
#else
|
||||
const float scale = UINT_MAX;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (coreaudio_lock (core, "audioDeviceIOProc")) {
|
||||
inInputTime = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
frameCount = core->audioDevicePropertyBufferFrameSize;
|
||||
live = core->live;
|
||||
|
||||
/* if there are not enough samples, set signal and return */
|
||||
if (live < frameCount) {
|
||||
inInputTime = 0;
|
||||
coreaudio_unlock (core, "audioDeviceIOProc(empty)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rpos = core->rpos;
|
||||
src = hw->mix_buf + rpos;
|
||||
|
||||
/* fill buffer */
|
||||
for (frame = 0; frame < frameCount; frame++) {
|
||||
#ifdef FLOAT_MIXENG
|
||||
*out++ = src[frame].l; /* left channel */
|
||||
*out++ = src[frame].r; /* right channel */
|
||||
#else
|
||||
#ifdef RECIPROCAL
|
||||
*out++ = src[frame].l * scale; /* left channel */
|
||||
*out++ = src[frame].r * scale; /* right channel */
|
||||
#else
|
||||
*out++ = src[frame].l / scale; /* left channel */
|
||||
*out++ = src[frame].r / scale; /* right channel */
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
mixeng_clear (src, frameCount);
|
||||
rpos = (rpos + frameCount) % hw->samples;
|
||||
core->decr += frameCount;
|
||||
core->rpos = rpos;
|
||||
|
||||
coreaudio_unlock (core, "audioDeviceIOProc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
OSStatus status;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
UInt32 propertySize;
|
||||
int err;
|
||||
int bits = 8;
|
||||
int endianess = 0;
|
||||
const char *typ = "playback";
|
||||
AudioValueRange frameRange;
|
||||
|
||||
/* create mutex */
|
||||
err = pthread_mutex_init(&core->mutex, NULL);
|
||||
if (err) {
|
||||
dolog("Could not create mutex\nReason: %s\n", strerror (err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
|
||||
bits = 16;
|
||||
endianess = 1;
|
||||
}
|
||||
|
||||
audio_pcm_init_info (
|
||||
&hw->info,
|
||||
as,
|
||||
/* Following is irrelevant actually since we do not use
|
||||
mixengs clipping routines */
|
||||
audio_need_to_swap_endian (endianess)
|
||||
);
|
||||
|
||||
/* open default output device */
|
||||
propertySize = sizeof(core->outputDeviceID);
|
||||
status = AudioHardwareGetProperty(
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
&propertySize,
|
||||
&core->outputDeviceID);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get default output Device\n");
|
||||
return -1;
|
||||
}
|
||||
if (core->outputDeviceID == kAudioDeviceUnknown) {
|
||||
dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get minimum and maximum buffer frame sizes */
|
||||
propertySize = sizeof(frameRange);
|
||||
status = AudioDeviceGetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
&propertySize,
|
||||
&frameRange);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get device buffer frame range\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (frameRange.mMinimum > conf.buffer_frames) {
|
||||
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
|
||||
dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
|
||||
}
|
||||
else if (frameRange.mMaximum < conf.buffer_frames) {
|
||||
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
|
||||
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
|
||||
}
|
||||
else {
|
||||
core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
|
||||
}
|
||||
|
||||
/* set Buffer Frame Size */
|
||||
propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
|
||||
status = AudioDeviceSetProperty(
|
||||
core->outputDeviceID,
|
||||
NULL,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
propertySize,
|
||||
&core->audioDevicePropertyBufferFrameSize);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not set device buffer frame size %ld\n",
|
||||
core->audioDevicePropertyBufferFrameSize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get Buffer Frame Size */
|
||||
propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
|
||||
status = AudioDeviceGetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
&propertySize,
|
||||
&core->audioDevicePropertyBufferFrameSize);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get device buffer frame size\n");
|
||||
return -1;
|
||||
}
|
||||
hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
|
||||
|
||||
/* get StreamFormat */
|
||||
propertySize = sizeof(core->outputStreamBasicDescription);
|
||||
status = AudioDeviceGetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
&propertySize,
|
||||
&core->outputStreamBasicDescription);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get Device Stream properties\n");
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set Samplerate */
|
||||
core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
|
||||
propertySize = sizeof(core->outputStreamBasicDescription);
|
||||
status = AudioDeviceSetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
propertySize,
|
||||
&core->outputStreamBasicDescription);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
|
||||
as->freq);
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set Callback */
|
||||
status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* start Playback */
|
||||
if (!isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ, "Could not start playback\n");
|
||||
AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void coreaudio_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
OSStatus status;
|
||||
int err;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
if (!conf.isAtexit) {
|
||||
/* stop playback */
|
||||
if (isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not stop playback\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* remove callback */
|
||||
status = AudioDeviceRemoveIOProc(core->outputDeviceID,
|
||||
audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not remove IOProc\n");
|
||||
}
|
||||
}
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
|
||||
/* destroy mutex */
|
||||
err = pthread_mutex_destroy(&core->mutex);
|
||||
if (err) {
|
||||
dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
|
||||
}
|
||||
}
|
||||
|
||||
static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
OSStatus status;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
/* start playback */
|
||||
if (!isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not resume playback\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
/* stop playback */
|
||||
if (!conf.isAtexit) {
|
||||
if (isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not pause playback\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *coreaudio_audio_init (void)
|
||||
{
|
||||
atexit(coreaudio_atexit);
|
||||
return &coreaudio_audio_init;
|
||||
}
|
||||
|
||||
static void coreaudio_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
static struct audio_option coreaudio_options[] = {
|
||||
{"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames,
|
||||
"Size of the buffer in frames", NULL, 0},
|
||||
{"BUFFER_COUNT", AUD_OPT_INT, &conf.nbuffers,
|
||||
"Number of buffers", NULL, 0},
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops coreaudio_pcm_ops = {
|
||||
coreaudio_init_out,
|
||||
coreaudio_fini_out,
|
||||
coreaudio_run_out,
|
||||
coreaudio_write,
|
||||
coreaudio_ctl_out,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct audio_driver coreaudio_audio_driver = {
|
||||
INIT_FIELD (name = ) "coreaudio",
|
||||
INIT_FIELD (descr = )
|
||||
"CoreAudio http://developer.apple.com/audio/coreaudio.html",
|
||||
INIT_FIELD (options = ) coreaudio_options,
|
||||
INIT_FIELD (init = ) coreaudio_audio_init,
|
||||
INIT_FIELD (fini = ) coreaudio_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 1,
|
||||
INIT_FIELD (max_voices_out = ) 1,
|
||||
INIT_FIELD (max_voices_in = ) 0,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) 0
|
||||
};
|
||||
282
audio/dsound_template.h
Normal file
282
audio/dsound_template.h
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* QEMU DirectSound audio driver header
|
||||
*
|
||||
* Copyright (c) 2005 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.
|
||||
*/
|
||||
#ifdef DSBTYPE_IN
|
||||
#define NAME "capture buffer"
|
||||
#define TYPE in
|
||||
#define IFACE IDirectSoundCaptureBuffer
|
||||
#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
|
||||
#define FIELD dsound_capture_buffer
|
||||
#else
|
||||
#define NAME "playback buffer"
|
||||
#define TYPE out
|
||||
#define IFACE IDirectSoundBuffer
|
||||
#define BUFPTR LPDIRECTSOUNDBUFFER
|
||||
#define FIELD dsound_buffer
|
||||
#endif
|
||||
|
||||
static int glue (dsound_unlock_, TYPE) (
|
||||
BUFPTR buf,
|
||||
LPVOID p1,
|
||||
LPVOID p2,
|
||||
DWORD blen1,
|
||||
DWORD blen2
|
||||
)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not unlock " NAME "\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int glue (dsound_lock_, TYPE) (
|
||||
BUFPTR buf,
|
||||
struct audio_pcm_info *info,
|
||||
DWORD pos,
|
||||
DWORD len,
|
||||
LPVOID *p1p,
|
||||
LPVOID *p2p,
|
||||
DWORD *blen1p,
|
||||
DWORD *blen2p,
|
||||
int entire
|
||||
)
|
||||
{
|
||||
HRESULT hr;
|
||||
int i;
|
||||
LPVOID p1 = NULL, p2 = NULL;
|
||||
DWORD blen1 = 0, blen2 = 0;
|
||||
|
||||
for (i = 0; i < conf.lock_retries; ++i) {
|
||||
hr = glue (IFACE, _Lock) (
|
||||
buf,
|
||||
pos,
|
||||
len,
|
||||
&p1,
|
||||
&blen1,
|
||||
&p2,
|
||||
&blen2,
|
||||
(entire
|
||||
#ifdef DSBTYPE_IN
|
||||
? DSCBLOCK_ENTIREBUFFER
|
||||
#else
|
||||
? DSBLOCK_ENTIREBUFFER
|
||||
#endif
|
||||
: 0)
|
||||
);
|
||||
|
||||
if (FAILED (hr)) {
|
||||
#ifndef DSBTYPE_IN
|
||||
if (hr == DSERR_BUFFERLOST) {
|
||||
if (glue (dsound_restore_, TYPE) (buf)) {
|
||||
dsound_logerr (hr, "Could not lock " NAME "\n");
|
||||
goto fail;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
dsound_logerr (hr, "Could not lock " NAME "\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == conf.lock_retries) {
|
||||
dolog ("%d attempts to lock " NAME " failed\n", i);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
|
||||
dolog ("DirectSound returned misaligned buffer %ld %ld\n",
|
||||
blen1, blen2);
|
||||
glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!p1 && blen1) {
|
||||
dolog ("warning: !p1 && blen1=%ld\n", blen1);
|
||||
blen1 = 0;
|
||||
}
|
||||
|
||||
if (!p2 && blen2) {
|
||||
dolog ("warning: !p2 && blen2=%ld\n", blen2);
|
||||
blen2 = 0;
|
||||
}
|
||||
|
||||
*p1p = p1;
|
||||
*p2p = p2;
|
||||
*blen1p = blen1;
|
||||
*blen2p = blen2;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
*p1p = NULL - 1;
|
||||
*p2p = NULL - 1;
|
||||
*blen1p = -1;
|
||||
*blen2p = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DSBTYPE_IN
|
||||
static void dsound_fini_in (HWVoiceIn *hw)
|
||||
#else
|
||||
static void dsound_fini_out (HWVoiceOut *hw)
|
||||
#endif
|
||||
{
|
||||
HRESULT hr;
|
||||
#ifdef DSBTYPE_IN
|
||||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||
#else
|
||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||
#endif
|
||||
|
||||
if (ds->FIELD) {
|
||||
hr = glue (IFACE, _Stop) (ds->FIELD);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not stop " NAME "\n");
|
||||
}
|
||||
|
||||
hr = glue (IFACE, _Release) (ds->FIELD);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not release " NAME "\n");
|
||||
}
|
||||
ds->FIELD = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DSBTYPE_IN
|
||||
static int dsound_init_in (HWVoiceIn *hw, audsettings_t *as)
|
||||
#else
|
||||
static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
#endif
|
||||
{
|
||||
int err;
|
||||
HRESULT hr;
|
||||
dsound *s = &glob_dsound;
|
||||
WAVEFORMATEX wfx;
|
||||
audsettings_t obt_as;
|
||||
#ifdef DSBTYPE_IN
|
||||
const char *typ = "ADC";
|
||||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||
DSCBUFFERDESC bd;
|
||||
DSCBCAPS bc;
|
||||
#else
|
||||
const char *typ = "DAC";
|
||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||
DSBUFFERDESC bd;
|
||||
DSBCAPS bc;
|
||||
#endif
|
||||
|
||||
err = waveformat_from_audio_settings (&wfx, as);
|
||||
if (err) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset (&bd, 0, sizeof (bd));
|
||||
bd.dwSize = sizeof (bd);
|
||||
bd.lpwfxFormat = &wfx;
|
||||
#ifdef DSBTYPE_IN
|
||||
bd.dwBufferBytes = conf.bufsize_in;
|
||||
hr = IDirectSoundCapture_CreateCaptureBuffer (
|
||||
s->dsound_capture,
|
||||
&bd,
|
||||
&ds->dsound_capture_buffer,
|
||||
NULL
|
||||
);
|
||||
#else
|
||||
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
|
||||
bd.dwBufferBytes = conf.bufsize_out;
|
||||
hr = IDirectSound_CreateSoundBuffer (
|
||||
s->dsound,
|
||||
&bd,
|
||||
&ds->dsound_buffer,
|
||||
NULL
|
||||
);
|
||||
#endif
|
||||
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr2 (hr, typ, "Could not create " NAME "\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
hr = glue (IFACE, _GetFormat) (ds->FIELD, &wfx, sizeof (wfx), NULL);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
dolog (NAME "\n");
|
||||
print_wave_format (&wfx);
|
||||
#endif
|
||||
|
||||
memset (&bc, 0, sizeof (bc));
|
||||
bc.dwSize = sizeof (bc);
|
||||
|
||||
hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
err = waveformat_to_audio_settings (&wfx, &obt_as);
|
||||
if (err) {
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
ds->first_time = 1;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as, audio_need_to_swap_endian (0));
|
||||
|
||||
if (bc.dwBufferBytes & hw->info.align) {
|
||||
dolog (
|
||||
"GetCaps returned misaligned buffer size %ld, alignment %d\n",
|
||||
bc.dwBufferBytes, hw->info.align + 1
|
||||
);
|
||||
}
|
||||
hw->samples = bc.dwBufferBytes >> hw->info.shift;
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
dolog ("caps %ld, desc %ld\n",
|
||||
bc.dwBufferBytes, bd.dwBufferBytes);
|
||||
|
||||
dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
|
||||
hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
fail0:
|
||||
glue (dsound_fini_, TYPE) (hw);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef NAME
|
||||
#undef TYPE
|
||||
#undef IFACE
|
||||
#undef BUFPTR
|
||||
#undef FIELD
|
||||
1076
audio/dsoundaudio.c
Normal file
1076
audio/dsoundaudio.c
Normal file
File diff suppressed because it is too large
Load Diff
683
audio/fmodaudio.c
Normal file
683
audio/fmodaudio.c
Normal file
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
* QEMU FMOD audio driver
|
||||
*
|
||||
* Copyright (c) 2004-2005 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"
|
||||
|
||||
#define AUDIO_CAP "fmod"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct FMODVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
unsigned int old_pos;
|
||||
FSOUND_SAMPLE *fmod_sample;
|
||||
int channel;
|
||||
} FMODVoiceOut;
|
||||
|
||||
typedef struct FMODVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
FSOUND_SAMPLE *fmod_sample;
|
||||
} FMODVoiceIn;
|
||||
|
||||
static struct {
|
||||
const char *drvname;
|
||||
int nb_samples;
|
||||
int freq;
|
||||
int nb_channels;
|
||||
int bufsize;
|
||||
int threshold;
|
||||
int broken_adc;
|
||||
} conf = {
|
||||
NULL,
|
||||
2048 * 2,
|
||||
44100,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n",
|
||||
FMOD_ErrorString (FSOUND_GetError ()));
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
|
||||
const char *typ,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n",
|
||||
FMOD_ErrorString (FSOUND_GetError ()));
|
||||
}
|
||||
|
||||
static int fmod_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static void fmod_clear_sample (FMODVoiceOut *fmd)
|
||||
{
|
||||
HWVoiceOut *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->info.shift,
|
||||
&p1,
|
||||
&p2,
|
||||
&len1,
|
||||
&len2
|
||||
);
|
||||
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to lock sample\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
|
||||
dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
|
||||
len1, len2, hw->info.align + 1);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((len1 + len2) - (hw->samples << hw->info.shift)) {
|
||||
dolog ("Lock returned incomplete length %d, %d\n",
|
||||
len1 + len2, hw->samples << hw->info.shift);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
|
||||
|
||||
fail:
|
||||
status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to unlock sample\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
|
||||
{
|
||||
int src_len1 = dst_len;
|
||||
int src_len2 = 0;
|
||||
int pos = hw->rpos + dst_len;
|
||||
st_sample_t *src1 = hw->mix_buf + hw->rpos;
|
||||
st_sample_t *src2 = NULL;
|
||||
|
||||
if (pos > hw->samples) {
|
||||
src_len1 = hw->samples - hw->rpos;
|
||||
src2 = hw->mix_buf;
|
||||
src_len2 = dst_len - src_len1;
|
||||
pos = src_len2;
|
||||
}
|
||||
|
||||
if (src_len1) {
|
||||
hw->clip (dst, src1, src_len1);
|
||||
mixeng_clear (src1, src_len1);
|
||||
}
|
||||
|
||||
if (src_len2) {
|
||||
dst = advance (dst, src_len1 << hw->info.shift);
|
||||
hw->clip (dst, src2, src_len2);
|
||||
mixeng_clear (src2, src_len2);
|
||||
}
|
||||
|
||||
hw->rpos = pos % hw->samples;
|
||||
}
|
||||
|
||||
static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
|
||||
unsigned int blen1, unsigned int blen2)
|
||||
{
|
||||
int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to unlock sample\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_lock_sample (
|
||||
FSOUND_SAMPLE *sample,
|
||||
struct audio_pcm_info *info,
|
||||
int pos,
|
||||
int len,
|
||||
void **p1,
|
||||
void **p2,
|
||||
unsigned int *blen1,
|
||||
unsigned int *blen2
|
||||
)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = FSOUND_Sample_Lock (
|
||||
sample,
|
||||
pos << info->shift,
|
||||
len << info->shift,
|
||||
p1,
|
||||
p2,
|
||||
blen1,
|
||||
blen2
|
||||
);
|
||||
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to lock sample\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((*blen1 & info->align) || (*blen2 & info->align)) {
|
||||
dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
|
||||
*blen1, *blen2, info->align + 1);
|
||||
|
||||
fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2);
|
||||
|
||||
*p1 = NULL - 1;
|
||||
*p2 = NULL - 1;
|
||||
*blen1 = ~0U;
|
||||
*blen2 = ~0U;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!*p1 && *blen1) {
|
||||
dolog ("warning: !p1 && blen1=%d\n", *blen1);
|
||||
*blen1 = 0;
|
||||
}
|
||||
|
||||
if (!p2 && *blen2) {
|
||||
dolog ("warning: !p2 && blen2=%d\n", *blen2);
|
||||
*blen2 = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
int live, decr;
|
||||
void *p1 = 0, *p2 = 0;
|
||||
unsigned int blen1 = 0, blen2 = 0;
|
||||
unsigned int len1 = 0, len2 = 0;
|
||||
int nb_live;
|
||||
|
||||
live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!hw->pending_disable
|
||||
&& nb_live
|
||||
&& (conf.threshold && live <= conf.threshold)) {
|
||||
ldebug ("live=%d nb_live=%d\n", live, nb_live);
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = live;
|
||||
|
||||
if (fmd->channel >= 0) {
|
||||
int len = decr;
|
||||
int old_pos = fmd->old_pos;
|
||||
int ppos = FSOUND_GetCurrentPosition (fmd->channel);
|
||||
|
||||
if (ppos == old_pos || !ppos) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
|
||||
len = ppos - old_pos;
|
||||
}
|
||||
else {
|
||||
if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
|
||||
len = hw->samples - old_pos + ppos;
|
||||
}
|
||||
}
|
||||
decr = len;
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, decr < 0)) {
|
||||
dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
|
||||
decr, live, ppos, old_pos, len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!decr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
|
||||
fmd->old_pos, decr,
|
||||
&p1, &p2,
|
||||
&blen1, &blen2)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len1 = blen1 >> hw->info.shift;
|
||||
len2 = blen2 >> hw->info.shift;
|
||||
ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
|
||||
decr = len1 + len2;
|
||||
|
||||
if (p1 && len1) {
|
||||
fmod_write_sample (hw, p1, len1);
|
||||
}
|
||||
|
||||
if (p2 && len2) {
|
||||
fmod_write_sample (hw, p2, len2);
|
||||
}
|
||||
|
||||
fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
|
||||
|
||||
fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
|
||||
return decr;
|
||||
}
|
||||
|
||||
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\n", fmt);
|
||||
#ifdef DEBUG_FMOD
|
||||
abort ();
|
||||
#endif
|
||||
mode |= FSOUND_8BITS;
|
||||
}
|
||||
mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
|
||||
return mode;
|
||||
}
|
||||
|
||||
static void fmod_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) 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_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
int bits16, mode, channel;
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
|
||||
mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
|
||||
fmd->fmod_sample = FSOUND_Sample_Alloc (
|
||||
FSOUND_FREE, /* index */
|
||||
conf.nb_samples, /* length */
|
||||
mode, /* mode */
|
||||
as->freq, /* freq */
|
||||
255, /* volume */
|
||||
128, /* pan */
|
||||
255 /* priority */
|
||||
);
|
||||
|
||||
if (!fmd->fmod_sample) {
|
||||
fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
|
||||
if (channel < 0) {
|
||||
fmod_logerr2 ("DAC", "Failed to start playing sound\n");
|
||||
FSOUND_Sample_Free (fmd->fmod_sample);
|
||||
return -1;
|
||||
}
|
||||
fmd->channel = channel;
|
||||
|
||||
/* FMOD always operates on little endian frames? */
|
||||
audio_pcm_init_info (&hw->info, as, audio_need_to_swap_endian (0));
|
||||
bits16 = (mode & FSOUND_16BITS) != 0;
|
||||
hw->samples = conf.nb_samples;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
int status;
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
fmod_clear_sample (fmd);
|
||||
status = FSOUND_SetPaused (fmd->channel, 0);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
status = FSOUND_SetPaused (fmd->channel, 1);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_init_in (HWVoiceIn *hw, audsettings_t *as)
|
||||
{
|
||||
int bits16, mode;
|
||||
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
|
||||
|
||||
if (conf.broken_adc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
|
||||
fmd->fmod_sample = FSOUND_Sample_Alloc (
|
||||
FSOUND_FREE, /* index */
|
||||
conf.nb_samples, /* length */
|
||||
mode, /* mode */
|
||||
as->freq, /* freq */
|
||||
255, /* volume */
|
||||
128, /* pan */
|
||||
255 /* priority */
|
||||
);
|
||||
|
||||
if (!fmd->fmod_sample) {
|
||||
fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FMOD always operates on little endian frames? */
|
||||
audio_pcm_init_info (&hw->info, as, audio_need_to_swap_endian (0));
|
||||
bits16 = (mode & FSOUND_16BITS) != 0;
|
||||
hw->samples = conf.nb_samples;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fmod_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
|
||||
|
||||
if (fmd->fmod_sample) {
|
||||
FSOUND_Record_Stop ();
|
||||
FSOUND_Sample_Free (fmd->fmod_sample);
|
||||
fmd->fmod_sample = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int fmod_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
|
||||
int hwshift = hw->info.shift;
|
||||
int live, dead, new_pos, len;
|
||||
unsigned int blen1 = 0, blen2 = 0;
|
||||
unsigned int len1, len2;
|
||||
unsigned int decr;
|
||||
void *p1, *p2;
|
||||
|
||||
live = audio_pcm_hw_get_live_in (hw);
|
||||
dead = hw->samples - live;
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_pos = FSOUND_Record_GetPosition ();
|
||||
if (new_pos < 0) {
|
||||
fmod_logerr ("Could not get recording position\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = audio_ring_dist (new_pos, hw->wpos, hw->samples);
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
len = audio_MIN (len, dead);
|
||||
|
||||
if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
|
||||
hw->wpos, len,
|
||||
&p1, &p2,
|
||||
&blen1, &blen2)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len1 = blen1 >> hwshift;
|
||||
len2 = blen2 >> hwshift;
|
||||
decr = len1 + len2;
|
||||
|
||||
if (p1 && blen1) {
|
||||
hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
|
||||
}
|
||||
if (p2 && len2) {
|
||||
hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
|
||||
}
|
||||
|
||||
fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
|
||||
hw->wpos = (hw->wpos + decr) % hw->samples;
|
||||
return decr;
|
||||
}
|
||||
|
||||
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
|
||||
{"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
|
||||
};
|
||||
|
||||
static void *fmod_audio_init (void)
|
||||
{
|
||||
size_t i;
|
||||
double ver;
|
||||
int status;
|
||||
int output_type = -1;
|
||||
const char *drv = conf.drvname;
|
||||
|
||||
ver = FSOUND_GetVersion ();
|
||||
if (ver < FMOD_VERSION) {
|
||||
dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
if (ver < 3.75) {
|
||||
dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
|
||||
"ADC will be disabled.\n");
|
||||
conf.broken_adc = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
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 driver `%s'\n", drv);
|
||||
dolog ("Valid drivers:\n");
|
||||
for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
|
||||
dolog (" %s\n", drvtab[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (output_type != -1) {
|
||||
status = FSOUND_SetOutput (output_type);
|
||||
if (!status) {
|
||||
fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (conf.bufsize) {
|
||||
status = FSOUND_SetBufferSize (conf.bufsize);
|
||||
if (!status) {
|
||||
fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
|
||||
}
|
||||
}
|
||||
|
||||
status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
|
||||
if (!status) {
|
||||
fmod_logerr ("FSOUND_Init failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static int fmod_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, size);
|
||||
}
|
||||
|
||||
static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
int status;
|
||||
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to start recording\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
status = FSOUND_Record_Stop ();
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to stop recording\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fmod_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
FSOUND_Close ();
|
||||
}
|
||||
|
||||
static struct audio_option fmod_options[] = {
|
||||
{"DRV", AUD_OPT_STR, &conf.drvname,
|
||||
"FMOD driver", NULL, 0},
|
||||
{"FREQ", AUD_OPT_INT, &conf.freq,
|
||||
"Default frequency", NULL, 0},
|
||||
{"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
|
||||
"Buffer size in samples", NULL, 0},
|
||||
{"CHANNELS", AUD_OPT_INT, &conf.nb_channels,
|
||||
"Number of default channels (1 - mono, 2 - stereo)", NULL, 0},
|
||||
{"BUFSIZE", AUD_OPT_INT, &conf.bufsize,
|
||||
"(undocumented)", NULL, 0},
|
||||
#if 0
|
||||
{"THRESHOLD", AUD_OPT_INT, &conf.threshold,
|
||||
"(undocumented)"},
|
||||
#endif
|
||||
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops fmod_pcm_ops = {
|
||||
fmod_init_out,
|
||||
fmod_fini_out,
|
||||
fmod_run_out,
|
||||
fmod_write,
|
||||
fmod_ctl_out,
|
||||
|
||||
fmod_init_in,
|
||||
fmod_fini_in,
|
||||
fmod_run_in,
|
||||
fmod_read,
|
||||
fmod_ctl_in
|
||||
};
|
||||
|
||||
struct audio_driver fmod_audio_driver = {
|
||||
INIT_FIELD (name = ) "fmod",
|
||||
INIT_FIELD (descr = ) "FMOD 3.xx http://www.fmod.org",
|
||||
INIT_FIELD (options = ) fmod_options,
|
||||
INIT_FIELD (init = ) fmod_audio_init,
|
||||
INIT_FIELD (fini = ) fmod_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &fmod_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 1,
|
||||
INIT_FIELD (max_voices_out = ) INT_MAX,
|
||||
INIT_FIELD (max_voices_in = ) INT_MAX,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) sizeof (FMODVoiceIn)
|
||||
};
|
||||
277
audio/mixeng.c
Normal file
277
audio/mixeng.c
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* QEMU Mixing engine
|
||||
*
|
||||
* Copyright (c) 2004-2005 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 AUDIO_CAP "mixeng"
|
||||
#include "audio_int.h"
|
||||
|
||||
#define NOVOL
|
||||
|
||||
/* 8 bit */
|
||||
#define ENDIAN_CONVERSION natural
|
||||
#define ENDIAN_CONVERT(v) (v)
|
||||
|
||||
/* Signed 8 bit */
|
||||
#define IN_T int8_t
|
||||
#define IN_MIN SCHAR_MIN
|
||||
#define IN_MAX SCHAR_MAX
|
||||
#define SIGNED
|
||||
#define SHIFT 8
|
||||
#include "mixeng_template.h"
|
||||
#undef SIGNED
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
/* Unsigned 8 bit */
|
||||
#define IN_T uint8_t
|
||||
#define IN_MIN 0
|
||||
#define IN_MAX UCHAR_MAX
|
||||
#define SHIFT 8
|
||||
#include "mixeng_template.h"
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
#undef ENDIAN_CONVERT
|
||||
#undef ENDIAN_CONVERSION
|
||||
|
||||
/* Signed 16 bit */
|
||||
#define IN_T int16_t
|
||||
#define IN_MIN SHRT_MIN
|
||||
#define IN_MAX SHRT_MAX
|
||||
#define SIGNED
|
||||
#define SHIFT 16
|
||||
#define ENDIAN_CONVERSION natural
|
||||
#define ENDIAN_CONVERT(v) (v)
|
||||
#include "mixeng_template.h"
|
||||
#undef ENDIAN_CONVERT
|
||||
#undef ENDIAN_CONVERSION
|
||||
#define ENDIAN_CONVERSION swap
|
||||
#define ENDIAN_CONVERT(v) bswap16 (v)
|
||||
#include "mixeng_template.h"
|
||||
#undef ENDIAN_CONVERT
|
||||
#undef ENDIAN_CONVERSION
|
||||
#undef SIGNED
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
#define IN_T uint16_t
|
||||
#define IN_MIN 0
|
||||
#define IN_MAX USHRT_MAX
|
||||
#define SHIFT 16
|
||||
#define ENDIAN_CONVERSION natural
|
||||
#define ENDIAN_CONVERT(v) (v)
|
||||
#include "mixeng_template.h"
|
||||
#undef ENDIAN_CONVERT
|
||||
#undef ENDIAN_CONVERSION
|
||||
#define ENDIAN_CONVERSION swap
|
||||
#define ENDIAN_CONVERT(v) bswap16 (v)
|
||||
#include "mixeng_template.h"
|
||||
#undef ENDIAN_CONVERT
|
||||
#undef ENDIAN_CONVERSION
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
t_sample *mixeng_conv[2][2][2][2] = {
|
||||
{
|
||||
{
|
||||
{
|
||||
conv_natural_uint8_t_to_mono,
|
||||
conv_natural_uint16_t_to_mono
|
||||
},
|
||||
{
|
||||
conv_natural_uint8_t_to_mono,
|
||||
conv_swap_uint16_t_to_mono
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
conv_natural_int8_t_to_mono,
|
||||
conv_natural_int16_t_to_mono
|
||||
},
|
||||
{
|
||||
conv_natural_int8_t_to_mono,
|
||||
conv_swap_int16_t_to_mono
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
{
|
||||
conv_natural_uint8_t_to_stereo,
|
||||
conv_natural_uint16_t_to_stereo
|
||||
},
|
||||
{
|
||||
conv_natural_uint8_t_to_stereo,
|
||||
conv_swap_uint16_t_to_stereo
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
conv_natural_int8_t_to_stereo,
|
||||
conv_natural_int16_t_to_stereo
|
||||
},
|
||||
{
|
||||
conv_natural_int8_t_to_stereo,
|
||||
conv_swap_int16_t_to_stereo
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
f_sample *mixeng_clip[2][2][2][2] = {
|
||||
{
|
||||
{
|
||||
{
|
||||
clip_natural_uint8_t_from_mono,
|
||||
clip_natural_uint16_t_from_mono
|
||||
},
|
||||
{
|
||||
clip_natural_uint8_t_from_mono,
|
||||
clip_swap_uint16_t_from_mono
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
clip_natural_int8_t_from_mono,
|
||||
clip_natural_int16_t_from_mono
|
||||
},
|
||||
{
|
||||
clip_natural_int8_t_from_mono,
|
||||
clip_swap_int16_t_from_mono
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
{
|
||||
clip_natural_uint8_t_from_stereo,
|
||||
clip_natural_uint16_t_from_stereo
|
||||
},
|
||||
{
|
||||
clip_natural_uint8_t_from_stereo,
|
||||
clip_swap_uint16_t_from_stereo
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
clip_natural_int8_t_from_stereo,
|
||||
clip_natural_int16_t_from_stereo
|
||||
},
|
||||
{
|
||||
clip_natural_int8_t_from_stereo,
|
||||
clip_swap_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 */
|
||||
struct rate {
|
||||
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 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Prepare processing.
|
||||
*/
|
||||
void *st_rate_start (int inrate, int outrate)
|
||||
{
|
||||
struct rate *rate = audio_calloc (AUDIO_FUNC, 1, sizeof (*rate));
|
||||
|
||||
if (!rate) {
|
||||
dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rate->opos = 0;
|
||||
|
||||
/* increment */
|
||||
rate->opos_inc = ((uint64_t) inrate << 32) / outrate;
|
||||
|
||||
rate->ipos = 0;
|
||||
rate->ilast.l = 0;
|
||||
rate->ilast.r = 0;
|
||||
return rate;
|
||||
}
|
||||
|
||||
#define NAME st_rate_flow_mix
|
||||
#define OP(a, b) a += b
|
||||
#include "rate_template.h"
|
||||
|
||||
#define NAME st_rate_flow
|
||||
#define OP(a, b) a = b
|
||||
#include "rate_template.h"
|
||||
|
||||
void st_rate_stop (void *opaque)
|
||||
{
|
||||
qemu_free (opaque);
|
||||
}
|
||||
|
||||
void mixeng_clear (st_sample_t *buf, int len)
|
||||
{
|
||||
memset (buf, 0, len * sizeof (st_sample_t));
|
||||
}
|
||||
51
audio/mixeng.h
Normal file
51
audio/mixeng.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* QEMU Mixing engine header
|
||||
*
|
||||
* Copyright (c) 2004-2005 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
|
||||
|
||||
#ifdef FLOAT_MIXENG
|
||||
typedef float real_t;
|
||||
typedef struct { int mute; real_t r; real_t l; } volume_t;
|
||||
typedef struct { real_t l; real_t r; } st_sample_t;
|
||||
#else
|
||||
typedef struct { int mute; int64_t r; int64_t l; } volume_t;
|
||||
typedef struct { int64_t l; int64_t r; } st_sample_t;
|
||||
#endif
|
||||
|
||||
typedef void (t_sample) (st_sample_t *dst, const void *src,
|
||||
int samples, volume_t *vol);
|
||||
typedef void (f_sample) (void *dst, const st_sample_t *src, int samples);
|
||||
|
||||
extern t_sample *mixeng_conv[2][2][2][2];
|
||||
extern f_sample *mixeng_clip[2][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_flow_mix (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
|
||||
int *isamp, int *osamp);
|
||||
void st_rate_stop (void *opaque);
|
||||
void mixeng_clear (st_sample_t *buf, int len);
|
||||
|
||||
#endif /* mixeng.h */
|
||||
177
audio/mixeng_template.h
Normal file
177
audio/mixeng_template.h
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* QEMU Mixing engine
|
||||
*
|
||||
* Copyright (c) 2004-2005 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
|
||||
*/
|
||||
|
||||
#ifndef SIGNED
|
||||
#define HALF (IN_MAX >> 1)
|
||||
#endif
|
||||
|
||||
#ifdef NOVOL
|
||||
#define VOL(a, b) a
|
||||
#else
|
||||
#ifdef FLOAT_MIXENG
|
||||
#define VOL(a, b) ((a) * (b))
|
||||
#else
|
||||
#define VOL(a, b) ((a) * (b)) >> 32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define ET glue (ENDIAN_CONVERSION, glue (_, IN_T))
|
||||
|
||||
#ifdef FLOAT_MIXENG
|
||||
static real_t inline glue (conv_, ET) (IN_T v)
|
||||
{
|
||||
IN_T nv = ENDIAN_CONVERT (v);
|
||||
|
||||
#ifdef RECIPROCAL
|
||||
#ifdef SIGNED
|
||||
return nv * (1.f / (real_t) (IN_MAX - IN_MIN));
|
||||
#else
|
||||
return (nv - HALF) * (1.f / (real_t) IN_MAX);
|
||||
#endif
|
||||
#else /* !RECIPROCAL */
|
||||
#ifdef SIGNED
|
||||
return nv / (real_t) (IN_MAX - IN_MIN);
|
||||
#else
|
||||
return (nv - HALF) / (real_t) IN_MAX;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static IN_T inline glue (clip_, ET) (real_t v)
|
||||
{
|
||||
if (v >= 0.5) {
|
||||
return IN_MAX;
|
||||
}
|
||||
else if (v < -0.5) {
|
||||
return IN_MIN;
|
||||
}
|
||||
|
||||
#ifdef SIGNED
|
||||
return ENDIAN_CONVERT ((IN_T) (v * (IN_MAX - IN_MIN)));
|
||||
#else
|
||||
return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
|
||||
#endif
|
||||
}
|
||||
|
||||
#else /* !FLOAT_MIXENG */
|
||||
|
||||
static inline int64_t glue (conv_, ET) (IN_T v)
|
||||
{
|
||||
IN_T nv = ENDIAN_CONVERT (v);
|
||||
#ifdef SIGNED
|
||||
return ((int64_t) nv) << (32 - SHIFT);
|
||||
#else
|
||||
return ((int64_t) nv - HALF) << (32 - SHIFT);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline IN_T glue (clip_, ET) (int64_t v)
|
||||
{
|
||||
if (v >= 0x7f000000) {
|
||||
return IN_MAX;
|
||||
}
|
||||
else if (v < -2147483648LL) {
|
||||
return IN_MIN;
|
||||
}
|
||||
|
||||
#ifdef SIGNED
|
||||
return ENDIAN_CONVERT ((IN_T) (v >> (32 - SHIFT)));
|
||||
#else
|
||||
return ENDIAN_CONVERT ((IN_T) ((v >> (32 - SHIFT)) + HALF));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void glue (glue (conv_, ET), _to_stereo)
|
||||
(st_sample_t *dst, const void *src, int samples, volume_t *vol)
|
||||
{
|
||||
st_sample_t *out = dst;
|
||||
IN_T *in = (IN_T *) src;
|
||||
#ifndef NOVOL
|
||||
if (vol->mute) {
|
||||
mixeng_clear (dst, samples);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
(void) vol;
|
||||
#endif
|
||||
while (samples--) {
|
||||
out->l = VOL (glue (conv_, ET) (*in++), vol->l);
|
||||
out->r = VOL (glue (conv_, ET) (*in++), vol->r);
|
||||
out += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (glue (conv_, ET), _to_mono)
|
||||
(st_sample_t *dst, const void *src, int samples, volume_t *vol)
|
||||
{
|
||||
st_sample_t *out = dst;
|
||||
IN_T *in = (IN_T *) src;
|
||||
#ifndef NOVOL
|
||||
if (vol->mute) {
|
||||
mixeng_clear (dst, samples);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
(void) vol;
|
||||
#endif
|
||||
while (samples--) {
|
||||
out->l = VOL (glue (conv_, ET) (in[0]), vol->l);
|
||||
out->r = out->l;
|
||||
out += 1;
|
||||
in += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (glue (clip_, ET), _from_stereo)
|
||||
(void *dst, const st_sample_t *src, int samples)
|
||||
{
|
||||
const st_sample_t *in = src;
|
||||
IN_T *out = (IN_T *) dst;
|
||||
while (samples--) {
|
||||
*out++ = glue (clip_, ET) (in->l);
|
||||
*out++ = glue (clip_, ET) (in->r);
|
||||
in += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (glue (clip_, ET), _from_mono)
|
||||
(void *dst, const st_sample_t *src, int samples)
|
||||
{
|
||||
const st_sample_t *in = src;
|
||||
IN_T *out = (IN_T *) dst;
|
||||
while (samples--) {
|
||||
*out++ = glue (clip_, ET) (in->l + in->r);
|
||||
in += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#undef ET
|
||||
#undef HALF
|
||||
#undef VOL
|
||||
170
audio/noaudio.c
Normal file
170
audio/noaudio.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* QEMU Timer based audio emulation
|
||||
*
|
||||
* Copyright (c) 2004-2005 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 AUDIO_CAP "noaudio"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct NoVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int64_t old_ticks;
|
||||
} NoVoiceOut;
|
||||
|
||||
typedef struct NoVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
int64_t old_ticks;
|
||||
} NoVoiceIn;
|
||||
|
||||
static int no_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
NoVoiceOut *no = (NoVoiceOut *) hw;
|
||||
int live, decr, samples;
|
||||
int64_t now = qemu_get_clock (vm_clock);
|
||||
int64_t ticks = now - no->old_ticks;
|
||||
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
|
||||
|
||||
if (bytes > INT_MAX) {
|
||||
samples = INT_MAX >> hw->info.shift;
|
||||
}
|
||||
else {
|
||||
samples = bytes >> hw->info.shift;
|
||||
}
|
||||
|
||||
live = audio_pcm_hw_get_live_out (&no->hw);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
no->old_ticks = now;
|
||||
decr = audio_MIN (live, samples);
|
||||
hw->rpos = (hw->rpos + decr) % hw->samples;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int no_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int no_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
audio_pcm_init_info (&hw->info, as, 0);
|
||||
hw->samples = 1024;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void no_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
(void) hw;
|
||||
}
|
||||
|
||||
static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int no_init_in (HWVoiceIn *hw, audsettings_t *as)
|
||||
{
|
||||
audio_pcm_init_info (&hw->info, as, 0);
|
||||
hw->samples = 1024;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void no_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
(void) hw;
|
||||
}
|
||||
|
||||
static int no_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
NoVoiceIn *no = (NoVoiceIn *) hw;
|
||||
int64_t now = qemu_get_clock (vm_clock);
|
||||
int64_t ticks = now - no->old_ticks;
|
||||
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
|
||||
int live = audio_pcm_hw_get_live_in (hw);
|
||||
int dead = hw->samples - live;
|
||||
int samples;
|
||||
|
||||
bytes = audio_MIN (bytes, INT_MAX);
|
||||
samples = bytes >> hw->info.shift;
|
||||
samples = audio_MIN (samples, dead);
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
static int no_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
int samples = size >> sw->info.shift;
|
||||
int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
|
||||
int to_clear = audio_MIN (samples, total);
|
||||
audio_pcm_info_clear_buf (&sw->info, buf, to_clear);
|
||||
return to_clear;
|
||||
}
|
||||
|
||||
static int no_ctl_in (HWVoiceIn *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)
|
||||
{
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
static struct audio_pcm_ops no_pcm_ops = {
|
||||
no_init_out,
|
||||
no_fini_out,
|
||||
no_run_out,
|
||||
no_write,
|
||||
no_ctl_out,
|
||||
|
||||
no_init_in,
|
||||
no_fini_in,
|
||||
no_run_in,
|
||||
no_read,
|
||||
no_ctl_in
|
||||
};
|
||||
|
||||
struct audio_driver no_audio_driver = {
|
||||
INIT_FIELD (name = ) "none",
|
||||
INIT_FIELD (descr = ) "Timer based audio emulation",
|
||||
INIT_FIELD (options = ) NULL,
|
||||
INIT_FIELD (init = ) no_audio_init,
|
||||
INIT_FIELD (fini = ) no_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &no_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 1,
|
||||
INIT_FIELD (max_voices_out = ) INT_MAX,
|
||||
INIT_FIELD (max_voices_in = ) INT_MAX,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (NoVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) sizeof (NoVoiceIn)
|
||||
};
|
||||
762
audio/ossaudio.c
Normal file
762
audio/ossaudio.c
Normal file
@@ -0,0 +1,762 @@
|
||||
/*
|
||||
* QEMU OSS audio driver
|
||||
*
|
||||
* Copyright (c) 2003-2005 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 "vl.h"
|
||||
|
||||
#define AUDIO_CAP "oss"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct OSSVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
void *pcm_buf;
|
||||
int fd;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
int mmapped;
|
||||
int old_optr;
|
||||
} OSSVoiceOut;
|
||||
|
||||
typedef struct OSSVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
void *pcm_buf;
|
||||
int fd;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
int old_optr;
|
||||
} OSSVoiceIn;
|
||||
|
||||
static struct {
|
||||
int try_mmap;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
const char *devpath_out;
|
||||
const char *devpath_in;
|
||||
} conf = {
|
||||
.try_mmap = 0,
|
||||
.nfrags = 4,
|
||||
.fragsize = 4096,
|
||||
.devpath_out = "/dev/dsp",
|
||||
.devpath_in = "/dev/dsp"
|
||||
};
|
||||
|
||||
struct oss_params {
|
||||
int freq;
|
||||
audfmt_e fmt;
|
||||
int nchannels;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
|
||||
int err,
|
||||
const char *typ,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
|
||||
}
|
||||
|
||||
static void oss_anal_close (int *fdp)
|
||||
{
|
||||
int err = close (*fdp);
|
||||
if (err) {
|
||||
oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
|
||||
}
|
||||
*fdp = -1;
|
||||
}
|
||||
|
||||
static int oss_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_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\n", fmt);
|
||||
#ifdef DEBUG_AUDIO
|
||||
abort ();
|
||||
#endif
|
||||
return AFMT_U8;
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
|
||||
{
|
||||
switch (ossfmt) {
|
||||
case AFMT_S8:
|
||||
*endianness =0;
|
||||
*fmt = AUD_FMT_S8;
|
||||
break;
|
||||
|
||||
case AFMT_U8:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_U8;
|
||||
break;
|
||||
|
||||
case AFMT_S16_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AFMT_U16_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
case AFMT_S16_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AFMT_U16_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Unrecognized audio format %d\n", ossfmt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined DEBUG_MISMATCHES || defined DEBUG
|
||||
static void oss_dump_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 (int in, 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 = in ? conf.devpath_in : conf.devpath_out;
|
||||
const char *typ = in ? "ADC" : "DAC";
|
||||
|
||||
fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
|
||||
if (-1 == fd) {
|
||||
oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
freq = req->freq;
|
||||
nchannels = req->nchannels;
|
||||
fmt = req->fmt;
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
|
||||
oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
|
||||
oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
|
||||
req->nchannels);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
|
||||
oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) {
|
||||
oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
|
||||
if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
|
||||
oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
|
||||
req->nfrags, req->fragsize);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
|
||||
oss_logerr2 (errno, typ, "Failed to get buffer length\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
obt->fmt = fmt;
|
||||
obt->nchannels = nchannels;
|
||||
obt->freq = freq;
|
||||
obt->nfrags = abinfo.fragstotal;
|
||||
obt->fragsize = abinfo.fragsize;
|
||||
*pfd = fd;
|
||||
|
||||
#ifdef DEBUG_MISMATCHES
|
||||
if ((req->fmt != obt->fmt) ||
|
||||
(req->nchannels != obt->nchannels) ||
|
||||
(req->freq != obt->freq) ||
|
||||
(req->fragsize != obt->fragsize) ||
|
||||
(req->nfrags != obt->nfrags)) {
|
||||
dolog ("Audio parameters mismatch\n");
|
||||
oss_dump_info (req, obt);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
oss_dump_info (req, obt);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
err:
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int oss_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
int err, rpos, live, decr;
|
||||
int samples;
|
||||
uint8_t *dst;
|
||||
st_sample_t *src;
|
||||
struct audio_buf_info abinfo;
|
||||
struct count_info cntinfo;
|
||||
int bufsize;
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bufsize = hw->samples << hw->info.shift;
|
||||
|
||||
if (oss->mmapped) {
|
||||
int bytes;
|
||||
|
||||
err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
|
||||
if (err < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cntinfo.ptr == oss->old_optr) {
|
||||
if (abs (hw->samples - live) < 64) {
|
||||
dolog ("warning: Overrun\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cntinfo.ptr > oss->old_optr) {
|
||||
bytes = cntinfo.ptr - oss->old_optr;
|
||||
}
|
||||
else {
|
||||
bytes = bufsize + cntinfo.ptr - oss->old_optr;
|
||||
}
|
||||
|
||||
decr = audio_MIN (bytes >> hw->info.shift, live);
|
||||
}
|
||||
else {
|
||||
err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
|
||||
if (err < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (abinfo.bytes < 0 || abinfo.bytes > bufsize) {
|
||||
ldebug ("warning: Invalid available size, size=%d bufsize=%d\n",
|
||||
abinfo.bytes, bufsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
|
||||
if (!decr) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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 = hw->mix_buf + rpos;
|
||||
dst = advance (oss->pcm_buf, rpos << hw->info.shift);
|
||||
|
||||
hw->clip (dst, src, convert_samples);
|
||||
if (!oss->mmapped) {
|
||||
int written;
|
||||
|
||||
written = write (oss->fd, dst, convert_samples << hw->info.shift);
|
||||
/* XXX: follow errno recommendations ? */
|
||||
if (written == -1) {
|
||||
oss_logerr (
|
||||
errno,
|
||||
"Failed to write %d bytes of audio data from %p\n",
|
||||
convert_samples << hw->info.shift,
|
||||
dst
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (written != convert_samples << hw->info.shift) {
|
||||
int wsamples = written >> hw->info.shift;
|
||||
int wbytes = wsamples << hw->info.shift;
|
||||
if (wbytes != written) {
|
||||
dolog ("warning: Misaligned write %d (requested %d), "
|
||||
"alignment %d\n",
|
||||
wbytes, written, hw->info.align + 1);
|
||||
}
|
||||
mixeng_clear (src, wsamples);
|
||||
decr -= wsamples;
|
||||
rpos = (rpos + wsamples) % hw->samples;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mixeng_clear (src, convert_samples);
|
||||
|
||||
rpos = (rpos + convert_samples) % hw->samples;
|
||||
samples -= convert_samples;
|
||||
}
|
||||
if (oss->mmapped) {
|
||||
oss->old_optr = cntinfo.ptr;
|
||||
}
|
||||
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void oss_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
int err;
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
|
||||
ldebug ("oss_fini\n");
|
||||
oss_anal_close (&oss->fd);
|
||||
|
||||
if (oss->pcm_buf) {
|
||||
if (oss->mmapped) {
|
||||
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
if (err) {
|
||||
oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
|
||||
oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
}
|
||||
}
|
||||
else {
|
||||
qemu_free (oss->pcm_buf);
|
||||
}
|
||||
oss->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
struct oss_params req, obt;
|
||||
int endianness;
|
||||
int err;
|
||||
int fd;
|
||||
audfmt_e effective_fmt;
|
||||
audsettings_t obt_as;
|
||||
|
||||
oss->fd = -1;
|
||||
|
||||
req.fmt = aud_to_ossfmt (as->fmt);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.fragsize = conf.fragsize;
|
||||
req.nfrags = conf.nfrags;
|
||||
|
||||
if (oss_open (0, &req, &obt, &fd)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
|
||||
if (err) {
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obt_as.freq = obt.freq;
|
||||
obt_as.nchannels = obt.nchannels;
|
||||
obt_as.fmt = effective_fmt;
|
||||
|
||||
audio_pcm_init_info (
|
||||
&hw->info,
|
||||
&obt_as,
|
||||
audio_need_to_swap_endian (endianness)
|
||||
);
|
||||
oss->nfrags = obt.nfrags;
|
||||
oss->fragsize = obt.fragsize;
|
||||
|
||||
if (obt.nfrags * obt.fragsize & hw->info.align) {
|
||||
dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
|
||||
obt.nfrags * obt.fragsize, hw->info.align + 1);
|
||||
}
|
||||
|
||||
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
|
||||
|
||||
oss->mmapped = 0;
|
||||
if (conf.try_mmap) {
|
||||
oss->pcm_buf = mmap (
|
||||
0,
|
||||
hw->samples << hw->info.shift,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
fd,
|
||||
0
|
||||
);
|
||||
if (oss->pcm_buf == MAP_FAILED) {
|
||||
oss_logerr (errno, "Failed to map %d bytes of DAC\n",
|
||||
hw->samples << hw->info.shift);
|
||||
} else {
|
||||
int err;
|
||||
int trig = 0;
|
||||
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
|
||||
}
|
||||
else {
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (
|
||||
errno,
|
||||
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
);
|
||||
}
|
||||
else {
|
||||
oss->mmapped = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
if (err) {
|
||||
oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
|
||||
oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
oss->pcm_buf = audio_calloc (
|
||||
AUDIO_FUNC,
|
||||
hw->samples,
|
||||
1 << hw->info.shift
|
||||
);
|
||||
if (!oss->pcm_buf) {
|
||||
dolog (
|
||||
"Could not allocate DAC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples,
|
||||
1 << hw->info.shift
|
||||
);
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
oss->fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
int trig;
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
|
||||
if (!oss->mmapped) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
ldebug ("enabling voice\n");
|
||||
audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (
|
||||
errno,
|
||||
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
ldebug ("disabling voice\n");
|
||||
trig = 0;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oss_init_in (HWVoiceIn *hw, audsettings_t *as)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
struct oss_params req, obt;
|
||||
int endianness;
|
||||
int err;
|
||||
int fd;
|
||||
audfmt_e effective_fmt;
|
||||
audsettings_t obt_as;
|
||||
|
||||
oss->fd = -1;
|
||||
|
||||
req.fmt = aud_to_ossfmt (as->fmt);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.fragsize = conf.fragsize;
|
||||
req.nfrags = conf.nfrags;
|
||||
if (oss_open (1, &req, &obt, &fd)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
|
||||
if (err) {
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obt_as.freq = obt.freq;
|
||||
obt_as.nchannels = obt.nchannels;
|
||||
obt_as.fmt = effective_fmt;
|
||||
|
||||
audio_pcm_init_info (
|
||||
&hw->info,
|
||||
&obt_as,
|
||||
audio_need_to_swap_endian (endianness)
|
||||
);
|
||||
oss->nfrags = obt.nfrags;
|
||||
oss->fragsize = obt.fragsize;
|
||||
|
||||
if (obt.nfrags * obt.fragsize & hw->info.align) {
|
||||
dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
|
||||
obt.nfrags * obt.fragsize, hw->info.align + 1);
|
||||
}
|
||||
|
||||
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
|
||||
oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
||||
if (!oss->pcm_buf) {
|
||||
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
oss->fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void oss_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
|
||||
oss_anal_close (&oss->fd);
|
||||
|
||||
if (oss->pcm_buf) {
|
||||
qemu_free (oss->pcm_buf);
|
||||
oss->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
int hwshift = hw->info.shift;
|
||||
int i;
|
||||
int live = audio_pcm_hw_get_live_in (hw);
|
||||
int dead = hw->samples - live;
|
||||
size_t read_samples = 0;
|
||||
struct {
|
||||
int add;
|
||||
int len;
|
||||
} bufs[2] = {
|
||||
{ hw->wpos, 0 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hw->wpos + dead > hw->samples) {
|
||||
bufs[0].len = (hw->samples - hw->wpos) << hwshift;
|
||||
bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
|
||||
}
|
||||
else {
|
||||
bufs[0].len = dead << hwshift;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
ssize_t nread;
|
||||
|
||||
if (bufs[i].len) {
|
||||
void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
|
||||
nread = read (oss->fd, p, bufs[i].len);
|
||||
|
||||
if (nread > 0) {
|
||||
if (nread & hw->info.align) {
|
||||
dolog ("warning: Misaligned read %zd (requested %d), "
|
||||
"alignment %d\n", nread, bufs[i].add << hwshift,
|
||||
hw->info.align + 1);
|
||||
}
|
||||
read_samples += nread >> hwshift;
|
||||
hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
|
||||
&nominal_volume);
|
||||
}
|
||||
|
||||
if (bufs[i].len - nread) {
|
||||
if (nread == -1) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
break;
|
||||
default:
|
||||
oss_logerr (
|
||||
errno,
|
||||
"Failed to read %d bytes of audio (to %p)\n",
|
||||
bufs[i].len, p
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hw->wpos = (hw->wpos + read_samples) % hw->samples;
|
||||
return read_samples;
|
||||
}
|
||||
|
||||
static int oss_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, size);
|
||||
}
|
||||
|
||||
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *oss_audio_init (void)
|
||||
{
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void oss_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
static struct audio_option oss_options[] = {
|
||||
{"FRAGSIZE", AUD_OPT_INT, &conf.fragsize,
|
||||
"Fragment size in bytes", NULL, 0},
|
||||
{"NFRAGS", AUD_OPT_INT, &conf.nfrags,
|
||||
"Number of fragments", NULL, 0},
|
||||
{"MMAP", AUD_OPT_BOOL, &conf.try_mmap,
|
||||
"Try using memory mapped access", NULL, 0},
|
||||
{"DAC_DEV", AUD_OPT_STR, &conf.devpath_out,
|
||||
"Path to DAC device", NULL, 0},
|
||||
{"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
|
||||
"Path to ADC device", NULL, 0},
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops oss_pcm_ops = {
|
||||
oss_init_out,
|
||||
oss_fini_out,
|
||||
oss_run_out,
|
||||
oss_write,
|
||||
oss_ctl_out,
|
||||
|
||||
oss_init_in,
|
||||
oss_fini_in,
|
||||
oss_run_in,
|
||||
oss_read,
|
||||
oss_ctl_in
|
||||
};
|
||||
|
||||
struct audio_driver oss_audio_driver = {
|
||||
INIT_FIELD (name = ) "oss",
|
||||
INIT_FIELD (descr = ) "OSS http://www.opensound.com",
|
||||
INIT_FIELD (options = ) oss_options,
|
||||
INIT_FIELD (init = ) oss_audio_init,
|
||||
INIT_FIELD (fini = ) oss_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &oss_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 1,
|
||||
INIT_FIELD (max_voices_out = ) INT_MAX,
|
||||
INIT_FIELD (max_voices_in = ) INT_MAX,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) sizeof (OSSVoiceIn)
|
||||
};
|
||||
111
audio/rate_template.h
Normal file
111
audio/rate_template.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* QEMU Mixing engine
|
||||
*
|
||||
* Copyright (c) 2004-2005 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Processed signed long samples from ibuf to obuf.
|
||||
* Return number of samples processed.
|
||||
*/
|
||||
void NAME (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
|
||||
int *isamp, int *osamp)
|
||||
{
|
||||
struct rate *rate = opaque;
|
||||
st_sample_t *istart, *iend;
|
||||
st_sample_t *ostart, *oend;
|
||||
st_sample_t ilast, icur, out;
|
||||
#ifdef FLOAT_MIXENG
|
||||
real_t t;
|
||||
#else
|
||||
int64_t t;
|
||||
#endif
|
||||
|
||||
ilast = rate->ilast;
|
||||
|
||||
istart = ibuf;
|
||||
iend = ibuf + *isamp;
|
||||
|
||||
ostart = obuf;
|
||||
oend = obuf + *osamp;
|
||||
|
||||
if (rate->opos_inc == (1ULL + UINT_MAX)) {
|
||||
int i, n = *isamp > *osamp ? *osamp : *isamp;
|
||||
for (i = 0; i < n; i++) {
|
||||
OP (obuf[i].l, ibuf[i].r);
|
||||
OP (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 */
|
||||
#ifdef FLOAT_MIXENG
|
||||
#ifdef RECIPROCAL
|
||||
t = (rate->opos & UINT_MAX) * (1.f / UINT_MAX);
|
||||
#else
|
||||
t = (rate->opos & UINT_MAX) / (real_t) UINT_MAX;
|
||||
#endif
|
||||
out.l = (ilast.l * (1.0 - t)) + icur.l * t;
|
||||
out.r = (ilast.r * (1.0 - t)) + icur.r * t;
|
||||
#else
|
||||
t = rate->opos & 0xffffffff;
|
||||
out.l = (ilast.l * ((int64_t) UINT_MAX - t) + icur.l * t) >> 32;
|
||||
out.r = (ilast.r * ((int64_t) UINT_MAX - t) + icur.r * t) >> 32;
|
||||
#endif
|
||||
|
||||
/* output sample & increment position */
|
||||
OP (obuf->l, out.l);
|
||||
OP (obuf->r, out.r);
|
||||
obuf += 1;
|
||||
rate->opos += rate->opos_inc;
|
||||
}
|
||||
|
||||
the_end:
|
||||
*isamp = ibuf - istart;
|
||||
*osamp = obuf - ostart;
|
||||
rate->ilast = ilast;
|
||||
}
|
||||
|
||||
#undef NAME
|
||||
#undef OP
|
||||
437
audio/sdlaudio.c
Normal file
437
audio/sdlaudio.c
Normal file
@@ -0,0 +1,437 @@
|
||||
/*
|
||||
* QEMU SDL audio driver
|
||||
*
|
||||
* Copyright (c) 2004-2005 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"
|
||||
|
||||
#define AUDIO_CAP "sdl"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct SDLVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int live;
|
||||
int rpos;
|
||||
int decr;
|
||||
} SDLVoiceOut;
|
||||
|
||||
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 GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
|
||||
}
|
||||
|
||||
static int sdl_lock (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_LockMutex (s->mutex)) {
|
||||
sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_unlock (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_UnlockMutex (s->mutex)) {
|
||||
sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_post (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_SemPost (s->sem)) {
|
||||
sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_wait (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_SemWait (s->sem)) {
|
||||
sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (sdl_unlock (s, forfn)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sdl_post (s, forfn);
|
||||
}
|
||||
|
||||
static int aud_to_sdlfmt (audfmt_e fmt, int *shift)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AUD_FMT_S8:
|
||||
*shift = 0;
|
||||
return AUDIO_S8;
|
||||
|
||||
case AUD_FMT_U8:
|
||||
*shift = 0;
|
||||
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\n", fmt);
|
||||
#ifdef DEBUG_AUDIO
|
||||
abort ();
|
||||
#endif
|
||||
return AUDIO_U8;
|
||||
}
|
||||
}
|
||||
|
||||
static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
|
||||
{
|
||||
switch (sdlfmt) {
|
||||
case AUDIO_S8:
|
||||
*endianess = 0;
|
||||
*fmt = AUD_FMT_S8;
|
||||
break;
|
||||
|
||||
case AUDIO_U8:
|
||||
*endianess = 0;
|
||||
*fmt = AUD_FMT_U8;
|
||||
break;
|
||||
|
||||
case AUDIO_S16LSB:
|
||||
*endianess = 0;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AUDIO_U16LSB:
|
||||
*endianess = 0;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
case AUDIO_S16MSB:
|
||||
*endianess = 1;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AUDIO_U16MSB:
|
||||
*endianess = 1;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = SDL_OpenAudio (req, obt);
|
||||
if (status) {
|
||||
sdl_logerr ("SDL_OpenAudio failed\n");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void sdl_close (SDLAudioState *s)
|
||||
{
|
||||
if (s->initialized) {
|
||||
sdl_lock (s, "sdl_close");
|
||||
s->exit = 1;
|
||||
sdl_unlock_and_post (s, "sdl_close");
|
||||
SDL_PauseAudio (1);
|
||||
SDL_CloseAudio ();
|
||||
s->initialized = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
||||
{
|
||||
SDLVoiceOut *sdl = opaque;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
HWVoiceOut *hw = &sdl->hw;
|
||||
int samples = len >> hw->info.shift;
|
||||
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (samples) {
|
||||
int to_mix, decr;
|
||||
|
||||
/* dolog ("in callback samples=%d\n", samples); */
|
||||
sdl_wait (s, "sdl_callback");
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sdl_lock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
|
||||
dolog ("sdl->live=%d hw->samples=%d\n",
|
||||
sdl->live, hw->samples);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sdl->live) {
|
||||
goto again;
|
||||
}
|
||||
|
||||
/* dolog ("in callback live=%d\n", live); */
|
||||
to_mix = audio_MIN (samples, sdl->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);
|
||||
mixeng_clear (src, chunk);
|
||||
sdl->rpos = (sdl->rpos + chunk) % hw->samples;
|
||||
to_mix -= chunk;
|
||||
buf += chunk << hw->info.shift;
|
||||
}
|
||||
samples -= decr;
|
||||
sdl->live -= decr;
|
||||
sdl->decr += decr;
|
||||
|
||||
again:
|
||||
if (sdl_unlock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* dolog ("done len=%d\n", len); */
|
||||
}
|
||||
|
||||
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int sdl_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
int decr, live;
|
||||
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
|
||||
if (sdl_lock (s, "sdl_callback")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
|
||||
if (sdl->decr > live) {
|
||||
ldebug ("sdl->decr %d live %d sdl->live %d\n",
|
||||
sdl->decr,
|
||||
live,
|
||||
sdl->live);
|
||||
}
|
||||
|
||||
decr = audio_MIN (sdl->decr, live);
|
||||
sdl->decr -= decr;
|
||||
|
||||
sdl->live = live - decr;
|
||||
hw->rpos = sdl->rpos;
|
||||
|
||||
if (sdl->live > 0) {
|
||||
sdl_unlock_and_post (s, "sdl_callback");
|
||||
}
|
||||
else {
|
||||
sdl_unlock (s, "sdl_callback");
|
||||
}
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void sdl_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
(void) hw;
|
||||
|
||||
sdl_close (&glob_sdl);
|
||||
}
|
||||
|
||||
static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
SDL_AudioSpec req, obt;
|
||||
int shift;
|
||||
int endianess;
|
||||
int err;
|
||||
audfmt_e effective_fmt;
|
||||
audsettings_t obt_as;
|
||||
|
||||
shift <<= as->nchannels == 2;
|
||||
|
||||
req.freq = as->freq;
|
||||
req.format = aud_to_sdlfmt (as->fmt, &shift);
|
||||
req.channels = as->nchannels;
|
||||
req.samples = conf.nb_samples;
|
||||
req.callback = sdl_callback;
|
||||
req.userdata = sdl;
|
||||
|
||||
if (sdl_open (&req, &obt)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
|
||||
if (err) {
|
||||
sdl_close (s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obt_as.freq = obt.freq;
|
||||
obt_as.nchannels = obt.channels;
|
||||
obt_as.fmt = effective_fmt;
|
||||
|
||||
audio_pcm_init_info (
|
||||
&hw->info,
|
||||
&obt_as,
|
||||
audio_need_to_swap_endian (endianess)
|
||||
);
|
||||
hw->samples = obt.samples;
|
||||
|
||||
s->initialized = 1;
|
||||
s->exit = 0;
|
||||
SDL_PauseAudio (0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_ctl_out (HWVoiceOut *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;
|
||||
|
||||
if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
|
||||
sdl_logerr ("SDL failed to initialize audio subsystem\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->mutex = SDL_CreateMutex ();
|
||||
if (!s->mutex) {
|
||||
sdl_logerr ("Failed to create SDL mutex\n");
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->sem = SDL_CreateSemaphore (0);
|
||||
if (!s->sem) {
|
||||
sdl_logerr ("Failed to create SDL semaphore\n");
|
||||
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);
|
||||
}
|
||||
|
||||
static struct audio_option sdl_options[] = {
|
||||
{"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
|
||||
"Size of SDL buffer in samples", NULL, 0},
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops sdl_pcm_ops = {
|
||||
sdl_init_out,
|
||||
sdl_fini_out,
|
||||
sdl_run_out,
|
||||
sdl_write_out,
|
||||
sdl_ctl_out,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct audio_driver sdl_audio_driver = {
|
||||
INIT_FIELD (name = ) "sdl",
|
||||
INIT_FIELD (descr = ) "SDL http://www.libsdl.org",
|
||||
INIT_FIELD (options = ) sdl_options,
|
||||
INIT_FIELD (init = ) sdl_audio_init,
|
||||
INIT_FIELD (fini = ) sdl_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &sdl_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 1,
|
||||
INIT_FIELD (max_voices_out = ) 1,
|
||||
INIT_FIELD (max_voices_in = ) 0,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (SDLVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) 0
|
||||
};
|
||||
241
audio/sys-queue.h
Normal file
241
audio/sys-queue.h
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.3 (Berkeley) 12/13/93
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H
|
||||
#define _SYS_QUEUE_H 1
|
||||
|
||||
/*
|
||||
* This file defines three types of data structures: lists, tail queues,
|
||||
* and circular queues.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list after
|
||||
* an existing element or at the head of the list. A list may only be
|
||||
* traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list after
|
||||
* an existing element, at the head of the list, or at the end of the
|
||||
* list. A tail queue may only be traversed in the forward direction.
|
||||
*
|
||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the list.
|
||||
* A circle queue may be traversed in either direction, but has a more
|
||||
* complex end of list detection.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*/
|
||||
|
||||
/*
|
||||
* List definitions.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define LIST_INIT(head) { \
|
||||
(head)->lh_first = NULL; \
|
||||
}
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
}
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) { \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
}
|
||||
|
||||
#define LIST_REMOVE(elm, field) { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_INIT(head) { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
}
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
}
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) { \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
}
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) { \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
}
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) { \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue definitions.
|
||||
*/
|
||||
#define CIRCLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *cqh_first; /* first element */ \
|
||||
struct type *cqh_last; /* last element */ \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *cqe_next; /* next element */ \
|
||||
struct type *cqe_prev; /* previous element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue functions.
|
||||
*/
|
||||
#define CIRCLEQ_INIT(head) { \
|
||||
(head)->cqh_first = (void *)(head); \
|
||||
(head)->cqh_last = (void *)(head); \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) { \
|
||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev = (listelm); \
|
||||
if ((listelm)->field.cqe_next == (void *)(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||
(listelm)->field.cqe_next = (elm); \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) { \
|
||||
(elm)->field.cqe_next = (listelm); \
|
||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||
if ((listelm)->field.cqe_prev == (void *)(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||
(listelm)->field.cqe_prev = (elm); \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) { \
|
||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||
(elm)->field.cqe_prev = (void *)(head); \
|
||||
if ((head)->cqh_last == (void *)(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||
(head)->cqh_first = (elm); \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) { \
|
||||
(elm)->field.cqe_next = (void *)(head); \
|
||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||
if ((head)->cqh_first == (void *)(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(head)->cqh_last->field.cqe_next = (elm); \
|
||||
(head)->cqh_last = (elm); \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) { \
|
||||
if ((elm)->field.cqe_next == (void *)(head)) \
|
||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||
else \
|
||||
(elm)->field.cqe_next->field.cqe_prev = \
|
||||
(elm)->field.cqe_prev; \
|
||||
if ((elm)->field.cqe_prev == (void *)(head)) \
|
||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||
else \
|
||||
(elm)->field.cqe_prev->field.cqe_next = \
|
||||
(elm)->field.cqe_next; \
|
||||
}
|
||||
#endif /* sys/queue.h */
|
||||
255
audio/wavaudio.c
Normal file
255
audio/wavaudio.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* QEMU WAV audio driver
|
||||
*
|
||||
* Copyright (c) 2004-2005 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 AUDIO_CAP "wav"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct WAVVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
QEMUFile *f;
|
||||
int64_t old_ticks;
|
||||
void *pcm_buf;
|
||||
int total_samples;
|
||||
} WAVVoiceOut;
|
||||
|
||||
static struct {
|
||||
audsettings_t settings;
|
||||
const char *wav_path;
|
||||
} conf = {
|
||||
{
|
||||
44100,
|
||||
2,
|
||||
AUD_FMT_S16
|
||||
},
|
||||
"qemu.wav"
|
||||
};
|
||||
|
||||
static int wav_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) 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->info.bytes_per_second) / ticks_per_sec;
|
||||
|
||||
if (bytes > INT_MAX) {
|
||||
samples = INT_MAX >> hw->info.shift;
|
||||
}
|
||||
else {
|
||||
samples = bytes >> hw->info.shift;
|
||||
}
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 = hw->mix_buf + rpos;
|
||||
dst = advance (wav->pcm_buf, rpos << hw->info.shift);
|
||||
|
||||
hw->clip (dst, src, convert_samples);
|
||||
qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
|
||||
mixeng_clear (src, convert_samples);
|
||||
|
||||
rpos = (rpos + convert_samples) % hw->samples;
|
||||
samples -= convert_samples;
|
||||
wav->total_samples += convert_samples;
|
||||
}
|
||||
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_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_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
int bits16 = 0, stereo = 0;
|
||||
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
|
||||
};
|
||||
audsettings_t wav_as = conf.settings;
|
||||
|
||||
(void) as;
|
||||
|
||||
stereo = wav_as.nchannels == 2;
|
||||
switch (wav_as.fmt) {
|
||||
case AUD_FMT_S8:
|
||||
case AUD_FMT_U8:
|
||||
bits16 = 0;
|
||||
break;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
case AUD_FMT_U16:
|
||||
bits16 = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
hdr[34] = bits16 ? 0x10 : 0x08;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &wav_as, audio_need_to_swap_endian (0));
|
||||
|
||||
hw->samples = 1024;
|
||||
wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
||||
if (!wav->pcm_buf) {
|
||||
dolog ("Could not allocate buffer (%d bytes)\n",
|
||||
hw->samples << hw->info.shift);
|
||||
return -1;
|
||||
}
|
||||
|
||||
le_store (hdr + 22, hw->info.nchannels, 2);
|
||||
le_store (hdr + 24, hw->info.freq, 4);
|
||||
le_store (hdr + 28, hw->info.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_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
uint8_t rlen[4];
|
||||
uint8_t dlen[4];
|
||||
uint32_t datalen = wav->total_samples << hw->info.shift;
|
||||
uint32_t rifflen = datalen + 36;
|
||||
|
||||
if (!wav->f) {
|
||||
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_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *wav_audio_init (void)
|
||||
{
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void wav_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
ldebug ("wav_fini");
|
||||
}
|
||||
|
||||
struct audio_option wav_options[] = {
|
||||
{"FREQUENCY", AUD_OPT_INT, &conf.settings.freq,
|
||||
"Frequency", NULL, 0},
|
||||
|
||||
{"FORMAT", AUD_OPT_FMT, &conf.settings.fmt,
|
||||
"Format", NULL, 0},
|
||||
|
||||
{"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
|
||||
"Number of channels (1 - mono, 2 - stereo)", NULL, 0},
|
||||
|
||||
{"PATH", AUD_OPT_STR, &conf.wav_path,
|
||||
"Path to wave file", NULL, 0},
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
struct audio_pcm_ops wav_pcm_ops = {
|
||||
wav_init_out,
|
||||
wav_fini_out,
|
||||
wav_run_out,
|
||||
wav_write_out,
|
||||
wav_ctl_out,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct audio_driver wav_audio_driver = {
|
||||
INIT_FIELD (name = ) "wav",
|
||||
INIT_FIELD (descr = )
|
||||
"WAV renderer http://wikipedia.org/wiki/WAV",
|
||||
INIT_FIELD (options = ) wav_options,
|
||||
INIT_FIELD (init = ) wav_audio_init,
|
||||
INIT_FIELD (fini = ) wav_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &wav_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 0,
|
||||
INIT_FIELD (max_voices_out = ) 1,
|
||||
INIT_FIELD (max_voices_in = ) 0,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) 0
|
||||
};
|
||||
224
block-bochs.c
Normal file
224
block-bochs.c
Normal file
@@ -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
block-cloop.c
Normal file
169
block-cloop.c
Normal file
@@ -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;
|
||||
uint8_t *compressed_block;
|
||||
uint8_t *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 = malloc(max_compressed_block_size+1)))
|
||||
goto cloop_close;
|
||||
if(!(s->uncompressed_block = 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,
|
||||
};
|
||||
|
||||
|
||||
264
block-cow.c
Normal file
264
block-cow.c
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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 (buf_size >= sizeof(struct cow_header_v2) &&
|
||||
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
|
||||
297
block-dmg.c
Normal file
297
block-dmg.c
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* 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;
|
||||
uint8_t *compressed_chunk;
|
||||
uint8_t *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);
|
||||
/* open raw instead */
|
||||
bs->drv=&bdrv_raw;
|
||||
return bs->drv->bdrv_open(bs,filename);
|
||||
}
|
||||
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 = malloc(max_compressed_size+1)))
|
||||
goto dmg_close;
|
||||
if(!(s->uncompressed_chunk = 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,
|
||||
};
|
||||
|
||||
710
block-qcow.c
Normal file
710
block-qcow.c
Normal file
@@ -0,0 +1,710 @@
|
||||
/*
|
||||
* 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 (buf_size >= sizeof(QCowHeader) &&
|
||||
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) {
|
||||
if (strcmp(backing_file, "fat:")) {
|
||||
const char *p;
|
||||
/* XXX: this is a hack: we do not attempt to check for URL
|
||||
like syntax */
|
||||
p = strchr(backing_file, ':');
|
||||
if (p && (p - backing_file) >= 2) {
|
||||
/* URL like but exclude "c:" like filenames */
|
||||
pstrcpy(backing_filename, sizeof(backing_filename),
|
||||
backing_file);
|
||||
} else {
|
||||
realpath(backing_file, backing_filename);
|
||||
if (stat(backing_filename, &st) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
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;
|
||||
} else
|
||||
backing_file = NULL;
|
||||
header.mtime = cpu_to_be32(st.st_mtime);
|
||||
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_make_empty(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint32_t l1_length = s->l1_size * sizeof(uint64_t);
|
||||
|
||||
memset(s->l1_table, 0, l1_length);
|
||||
lseek(s->fd, s->l1_table_offset, SEEK_SET);
|
||||
if (write(s->fd, s->l1_table, l1_length) < 0)
|
||||
return -1;
|
||||
ftruncate(s->fd, s->l1_table_offset + l1_length);
|
||||
|
||||
memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
|
||||
memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t));
|
||||
memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t));
|
||||
|
||||
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,
|
||||
qcow_make_empty
|
||||
};
|
||||
|
||||
|
||||
439
block-vmdk.c
Normal file
439
block-vmdk.c
Normal file
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* 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 = le64_to_cpu(header.capacity);
|
||||
s->cluster_sectors = le64_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 int vmdk_create(const char *filename, int64_t total_size,
|
||||
const char *backing_file, int flags)
|
||||
{
|
||||
int fd, i;
|
||||
VMDK4Header header;
|
||||
uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
|
||||
char *desc_template =
|
||||
"# Disk DescriptorFile\n"
|
||||
"version=1\n"
|
||||
"CID=%x\n"
|
||||
"parentCID=ffffffff\n"
|
||||
"createType=\"monolithicSparse\"\n"
|
||||
"\n"
|
||||
"# Extent description\n"
|
||||
"RW %lu SPARSE \"%s\"\n"
|
||||
"\n"
|
||||
"# The Disk Data Base \n"
|
||||
"#DDB\n"
|
||||
"\n"
|
||||
"ddb.virtualHWVersion = \"3\"\n"
|
||||
"ddb.geometry.cylinders = \"%lu\"\n"
|
||||
"ddb.geometry.heads = \"16\"\n"
|
||||
"ddb.geometry.sectors = \"63\"\n"
|
||||
"ddb.adapterType = \"ide\"\n";
|
||||
char desc[1024];
|
||||
const char *real_filename, *temp_str;
|
||||
|
||||
/* XXX: add support for backing file */
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
magic = cpu_to_be32(VMDK4_MAGIC);
|
||||
memset(&header, 0, sizeof(header));
|
||||
header.version = cpu_to_le32(1);
|
||||
header.flags = cpu_to_le32(3); /* ?? */
|
||||
header.capacity = cpu_to_le64(total_size);
|
||||
header.granularity = cpu_to_le64(128);
|
||||
header.num_gtes_per_gte = cpu_to_le32(512);
|
||||
|
||||
grains = (total_size + header.granularity - 1) / header.granularity;
|
||||
gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9;
|
||||
gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
|
||||
gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
|
||||
|
||||
header.desc_offset = 1;
|
||||
header.desc_size = 20;
|
||||
header.rgd_offset = header.desc_offset + header.desc_size;
|
||||
header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count);
|
||||
header.grain_offset =
|
||||
((header.gd_offset + gd_size + (gt_size * gt_count) +
|
||||
header.granularity - 1) / header.granularity) *
|
||||
header.granularity;
|
||||
|
||||
header.desc_offset = cpu_to_le64(header.desc_offset);
|
||||
header.desc_size = cpu_to_le64(header.desc_size);
|
||||
header.rgd_offset = cpu_to_le64(header.rgd_offset);
|
||||
header.gd_offset = cpu_to_le64(header.gd_offset);
|
||||
header.grain_offset = cpu_to_le64(header.grain_offset);
|
||||
|
||||
header.check_bytes[0] = 0xa;
|
||||
header.check_bytes[1] = 0x20;
|
||||
header.check_bytes[2] = 0xd;
|
||||
header.check_bytes[3] = 0xa;
|
||||
|
||||
/* write all the data */
|
||||
write(fd, &magic, sizeof(magic));
|
||||
write(fd, &header, sizeof(header));
|
||||
|
||||
ftruncate(fd, header.grain_offset << 9);
|
||||
|
||||
/* write grain directory */
|
||||
lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET);
|
||||
for (i = 0, tmp = header.rgd_offset + gd_size;
|
||||
i < gt_count; i++, tmp += gt_size)
|
||||
write(fd, &tmp, sizeof(tmp));
|
||||
|
||||
/* write backup grain directory */
|
||||
lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET);
|
||||
for (i = 0, tmp = header.gd_offset + gd_size;
|
||||
i < gt_count; i++, tmp += gt_size)
|
||||
write(fd, &tmp, sizeof(tmp));
|
||||
|
||||
/* compose the descriptor */
|
||||
real_filename = filename;
|
||||
if ((temp_str = strrchr(real_filename, '\\')) != NULL)
|
||||
real_filename = temp_str + 1;
|
||||
if ((temp_str = strrchr(real_filename, '/')) != NULL)
|
||||
real_filename = temp_str + 1;
|
||||
if ((temp_str = strrchr(real_filename, ':')) != NULL)
|
||||
real_filename = temp_str + 1;
|
||||
sprintf(desc, desc_template, time(NULL), (unsigned long)total_size,
|
||||
real_filename, total_size / (63 * 16));
|
||||
|
||||
/* write the descriptor */
|
||||
lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET);
|
||||
write(fd, desc, strlen(desc));
|
||||
|
||||
close(fd);
|
||||
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,
|
||||
vmdk_create,
|
||||
vmdk_is_allocated,
|
||||
};
|
||||
242
block-vpc.c
Normal file
242
block-vpc.c
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* 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 (buf_size >= 8 && !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,
|
||||
};
|
||||
2807
block-vvfat.c
Normal file
2807
block-vvfat.c
Normal file
File diff suppressed because it is too large
Load Diff
797
block.c
Normal file
797
block.c
Normal file
@@ -0,0 +1,797 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#ifdef CONFIG_COCOA
|
||||
#include <paths.h>
|
||||
#include <sys/param.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/IOBSD.h>
|
||||
#include <IOKit/storage/IOMediaBSDClient.h>
|
||||
#include <IOKit/storage/IOMedia.h>
|
||||
#include <IOKit/storage/IOCDMedia.h>
|
||||
//#include <IOKit/storage/IOCDTypes.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
#ifdef __sun__
|
||||
#include <sys/dkio.h>
|
||||
#endif
|
||||
|
||||
static BlockDriverState *bdrv_first;
|
||||
static BlockDriver *first_drv;
|
||||
|
||||
#ifdef CONFIG_COCOA
|
||||
static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator );
|
||||
static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize );
|
||||
|
||||
kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
|
||||
{
|
||||
kern_return_t kernResult;
|
||||
mach_port_t masterPort;
|
||||
CFMutableDictionaryRef classesToMatch;
|
||||
|
||||
kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
|
||||
if ( KERN_SUCCESS != kernResult ) {
|
||||
printf( "IOMasterPort returned %d\n", kernResult );
|
||||
}
|
||||
|
||||
classesToMatch = IOServiceMatching( kIOCDMediaClass );
|
||||
if ( classesToMatch == NULL ) {
|
||||
printf( "IOServiceMatching returned a NULL dictionary.\n" );
|
||||
} else {
|
||||
CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
|
||||
}
|
||||
kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator );
|
||||
if ( KERN_SUCCESS != kernResult )
|
||||
{
|
||||
printf( "IOServiceGetMatchingServices returned %d\n", kernResult );
|
||||
}
|
||||
|
||||
return kernResult;
|
||||
}
|
||||
|
||||
kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize )
|
||||
{
|
||||
io_object_t nextMedia;
|
||||
kern_return_t kernResult = KERN_FAILURE;
|
||||
*bsdPath = '\0';
|
||||
nextMedia = IOIteratorNext( mediaIterator );
|
||||
if ( nextMedia )
|
||||
{
|
||||
CFTypeRef bsdPathAsCFString;
|
||||
bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 );
|
||||
if ( bsdPathAsCFString ) {
|
||||
size_t devPathLength;
|
||||
strcpy( bsdPath, _PATH_DEV );
|
||||
strcat( bsdPath, "r" );
|
||||
devPathLength = strlen( bsdPath );
|
||||
if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) {
|
||||
kernResult = KERN_SUCCESS;
|
||||
}
|
||||
CFRelease( bsdPathAsCFString );
|
||||
}
|
||||
IOObjectRelease( nextMedia );
|
||||
}
|
||||
|
||||
return kernResult;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
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
|
||||
void get_tmp_filename(char *filename, int size)
|
||||
{
|
||||
char* p = strrchr(filename, '/');
|
||||
|
||||
if (p == NULL)
|
||||
return;
|
||||
|
||||
/* XXX: find a better function */
|
||||
tmpnam(p);
|
||||
*p = '/';
|
||||
}
|
||||
#else
|
||||
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) {
|
||||
buf = NULL;
|
||||
ret = 0;
|
||||
} else {
|
||||
#ifdef DIOCGSECTORSIZE
|
||||
{
|
||||
unsigned int sectorsize = 512;
|
||||
if (!ioctl(fd, DIOCGSECTORSIZE, §orsize) &&
|
||||
sectorsize > bufsize)
|
||||
bufsize = sectorsize;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_COCOA
|
||||
u_int32_t blockSize = 512;
|
||||
if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) {
|
||||
bufsize = blockSize;
|
||||
}
|
||||
#endif
|
||||
buf = qemu_malloc(bufsize);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
ret = read(fd, buf, bufsize);
|
||||
if (ret < 0) {
|
||||
close(fd);
|
||||
qemu_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;
|
||||
}
|
||||
}
|
||||
qemu_free(buf);
|
||||
return drv;
|
||||
}
|
||||
|
||||
int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot)
|
||||
{
|
||||
#ifdef CONFIG_COCOA
|
||||
if ( strncmp( filename, "/dev/cdrom", 10 ) == 0 ) {
|
||||
kern_return_t kernResult;
|
||||
io_iterator_t mediaIterator;
|
||||
char bsdPath[ MAXPATHLEN ];
|
||||
int fd;
|
||||
|
||||
kernResult = FindEjectableCDMedia( &mediaIterator );
|
||||
kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) );
|
||||
|
||||
if ( bsdPath[ 0 ] != '\0' ) {
|
||||
strcat(bsdPath,"s0");
|
||||
/* some CDs don't have a partition 0 */
|
||||
fd = open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0) {
|
||||
bsdPath[strlen(bsdPath)-1] = '1';
|
||||
} else {
|
||||
close(fd);
|
||||
}
|
||||
filename = bsdPath;
|
||||
}
|
||||
|
||||
if ( mediaIterator )
|
||||
IOObjectRelease( mediaIterator );
|
||||
}
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (bs->drv->bdrv_make_empty)
|
||||
return bs->drv->bdrv_make_empty(bs);
|
||||
|
||||
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;
|
||||
if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
|
||||
memcpy(bs->boot_sector_data, buf, 512);
|
||||
}
|
||||
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
|
||||
#ifdef __sun__
|
||||
struct dk_minfo minfo;
|
||||
int rv;
|
||||
#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
|
||||
#ifdef CONFIG_COCOA
|
||||
size = LONG_LONG_MAX;
|
||||
#else
|
||||
size = lseek(fd, 0LL, SEEK_END);
|
||||
#endif
|
||||
} else
|
||||
#endif
|
||||
#ifdef __sun__
|
||||
/*
|
||||
* use the DKIOCGMEDIAINFO ioctl to read the size.
|
||||
*/
|
||||
rv = ioctl ( fd, DKIOCGMEDIAINFO, &minfo );
|
||||
if ( rv != -1 ) {
|
||||
size = minfo.dki_lbsize * minfo.dki_capacity;
|
||||
} else /* there are reports that lseek on some devices
|
||||
fails, but irc discussion said that contingency
|
||||
on contingency was overkill */
|
||||
#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);
|
||||
bdrv_register(&bdrv_vvfat);
|
||||
}
|
||||
80
block_int.h
Normal file
80
block_int.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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);
|
||||
int (*bdrv_make_empty)(BlockDriverState *bs);
|
||||
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;
|
||||
};
|
||||
|
||||
void get_tmp_filename(char *filename, int size);
|
||||
|
||||
#endif /* BLOCK_INT_H */
|
||||
202
bswap.h
Normal file
202
bswap.h
Normal file
@@ -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 */
|
||||
911
cocoa.m
Normal file
911
cocoa.m
Normal file
@@ -0,0 +1,911 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
int grab = 0;
|
||||
int modifiers_state[256];
|
||||
|
||||
/* 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
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
int keymap[] =
|
||||
{
|
||||
// SdlI macI macH SdlH 104xtH 104xtC sdl
|
||||
30, // 0 0x00 0x1e A QZ_a
|
||||
31, // 1 0x01 0x1f S QZ_s
|
||||
32, // 2 0x02 0x20 D QZ_d
|
||||
33, // 3 0x03 0x21 F QZ_f
|
||||
35, // 4 0x04 0x23 H QZ_h
|
||||
34, // 5 0x05 0x22 G QZ_g
|
||||
44, // 6 0x06 0x2c Z QZ_z
|
||||
45, // 7 0x07 0x2d X QZ_x
|
||||
46, // 8 0x08 0x2e C QZ_c
|
||||
47, // 9 0x09 0x2f V QZ_v
|
||||
0, // 10 0x0A Undefined
|
||||
48, // 11 0x0B 0x30 B QZ_b
|
||||
16, // 12 0x0C 0x10 Q QZ_q
|
||||
17, // 13 0x0D 0x11 W QZ_w
|
||||
18, // 14 0x0E 0x12 E QZ_e
|
||||
19, // 15 0x0F 0x13 R QZ_r
|
||||
21, // 16 0x10 0x15 Y QZ_y
|
||||
20, // 17 0x11 0x14 T QZ_t
|
||||
2, // 18 0x12 0x02 1 QZ_1
|
||||
3, // 19 0x13 0x03 2 QZ_2
|
||||
4, // 20 0x14 0x04 3 QZ_3
|
||||
5, // 21 0x15 0x05 4 QZ_4
|
||||
7, // 22 0x16 0x07 6 QZ_6
|
||||
6, // 23 0x17 0x06 5 QZ_5
|
||||
13, // 24 0x18 0x0d = QZ_EQUALS
|
||||
10, // 25 0x19 0x0a 9 QZ_9
|
||||
8, // 26 0x1A 0x08 7 QZ_7
|
||||
12, // 27 0x1B 0x0c - QZ_MINUS
|
||||
9, // 28 0x1C 0x09 8 QZ_8
|
||||
11, // 29 0x1D 0x0b 0 QZ_0
|
||||
27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET
|
||||
24, // 31 0x1F 0x18 O QZ_o
|
||||
22, // 32 0x20 0x16 U QZ_u
|
||||
26, // 33 0x21 0x1a [ QZ_LEFTBRACKET
|
||||
23, // 34 0x22 0x17 I QZ_i
|
||||
25, // 35 0x23 0x19 P QZ_p
|
||||
28, // 36 0x24 0x1c ENTER QZ_RETURN
|
||||
38, // 37 0x25 0x26 L QZ_l
|
||||
36, // 38 0x26 0x24 J QZ_j
|
||||
40, // 39 0x27 0x28 ' QZ_QUOTE
|
||||
37, // 40 0x28 0x25 K QZ_k
|
||||
39, // 41 0x29 0x27 ; QZ_SEMICOLON
|
||||
43, // 42 0x2A 0x2b \ QZ_BACKSLASH
|
||||
51, // 43 0x2B 0x33 , QZ_COMMA
|
||||
53, // 44 0x2C 0x35 / QZ_SLASH
|
||||
49, // 45 0x2D 0x31 N QZ_n
|
||||
50, // 46 0x2E 0x32 M QZ_m
|
||||
52, // 47 0x2F 0x34 . QZ_PERIOD
|
||||
15, // 48 0x30 0x0f TAB QZ_TAB
|
||||
57, // 49 0x31 0x39 SPACE QZ_SPACE
|
||||
41, // 50 0x32 0x29 ` QZ_BACKQUOTE
|
||||
14, // 51 0x33 0x0e BKSP QZ_BACKSPACE
|
||||
0, // 52 0x34 Undefined
|
||||
1, // 53 0x35 0x01 ESC QZ_ESCAPE
|
||||
0, // 54 0x36 QZ_RMETA
|
||||
0, // 55 0x37 QZ_LMETA
|
||||
42, // 56 0x38 0x2a L SHFT QZ_LSHIFT
|
||||
58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK
|
||||
56, // 58 0x3A 0x38 L ALT QZ_LALT
|
||||
29, // 59 0x3B 0x1d L CTRL QZ_LCTRL
|
||||
54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT
|
||||
184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT
|
||||
157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL
|
||||
0, // 63 0x3F Undefined
|
||||
0, // 64 0x40 Undefined
|
||||
0, // 65 0x41 Undefined
|
||||
0, // 66 0x42 Undefined
|
||||
55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY
|
||||
0, // 68 0x44 Undefined
|
||||
78, // 69 0x45 0x4e KP + QZ_KP_PLUS
|
||||
0, // 70 0x46 Undefined
|
||||
69, // 71 0x47 0x45 NUM QZ_NUMLOCK
|
||||
0, // 72 0x48 Undefined
|
||||
0, // 73 0x49 Undefined
|
||||
0, // 74 0x4A Undefined
|
||||
181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE
|
||||
152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER
|
||||
0, // 77 0x4D undefined
|
||||
74, // 78 0x4E 0x4a KP - QZ_KP_MINUS
|
||||
0, // 79 0x4F Undefined
|
||||
0, // 80 0x50 Undefined
|
||||
0, // 81 0x51 QZ_KP_EQUALS
|
||||
82, // 82 0x52 0x52 KP 0 QZ_KP0
|
||||
79, // 83 0x53 0x4f KP 1 QZ_KP1
|
||||
80, // 84 0x54 0x50 KP 2 QZ_KP2
|
||||
81, // 85 0x55 0x51 KP 3 QZ_KP3
|
||||
75, // 86 0x56 0x4b KP 4 QZ_KP4
|
||||
76, // 87 0x57 0x4c KP 5 QZ_KP5
|
||||
77, // 88 0x58 0x4d KP 6 QZ_KP6
|
||||
71, // 89 0x59 0x47 KP 7 QZ_KP7
|
||||
0, // 90 0x5A Undefined
|
||||
72, // 91 0x5B 0x48 KP 8 QZ_KP8
|
||||
73, // 92 0x5C 0x49 KP 9 QZ_KP9
|
||||
0, // 93 0x5D Undefined
|
||||
0, // 94 0x5E Undefined
|
||||
0, // 95 0x5F Undefined
|
||||
63, // 96 0x60 0x3f F5 QZ_F5
|
||||
64, // 97 0x61 0x40 F6 QZ_F6
|
||||
65, // 98 0x62 0x41 F7 QZ_F7
|
||||
61, // 99 0x63 0x3d F3 QZ_F3
|
||||
66, // 100 0x64 0x42 F8 QZ_F8
|
||||
67, // 101 0x65 0x43 F9 QZ_F9
|
||||
0, // 102 0x66 Undefined
|
||||
87, // 103 0x67 0x57 F11 QZ_F11
|
||||
0, // 104 0x68 Undefined
|
||||
183,// 105 0x69 0xb7 QZ_PRINT
|
||||
0, // 106 0x6A Undefined
|
||||
70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK
|
||||
0, // 108 0x6C Undefined
|
||||
68, // 109 0x6D 0x44 F10 QZ_F10
|
||||
0, // 110 0x6E Undefined
|
||||
88, // 111 0x6F 0x58 F12 QZ_F12
|
||||
0, // 112 0x70 Undefined
|
||||
110,// 113 0x71 0x0 QZ_PAUSE
|
||||
210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT
|
||||
199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME
|
||||
201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP
|
||||
211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE
|
||||
62, // 118 0x76 0x3e F4 QZ_F4
|
||||
207,// 119 0x77 0xcf E0,4f END QZ_END
|
||||
60, // 120 0x78 0x3c F2 QZ_F2
|
||||
209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN
|
||||
59, // 122 0x7A 0x3b F1 QZ_F1
|
||||
203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT
|
||||
205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT
|
||||
208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN
|
||||
200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP
|
||||
/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */
|
||||
|
||||
/* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */
|
||||
/*
|
||||
219 // 0xdb e0,5b L GUI
|
||||
220 // 0xdc e0,5c R GUI
|
||||
221 // 0xdd e0,5d APPS
|
||||
// E0,2A,E0,37 PRNT SCRN
|
||||
// E1,1D,45,E1,9D,C5 PAUSE
|
||||
83 // 0x53 0x53 KP .
|
||||
// ACPI Scan Codes
|
||||
222 // 0xde E0, 5E Power
|
||||
223 // 0xdf E0, 5F Sleep
|
||||
227 // 0xe3 E0, 63 Wake
|
||||
// Windows Multimedia Scan Codes
|
||||
153 // 0x99 E0, 19 Next Track
|
||||
144 // 0x90 E0, 10 Previous Track
|
||||
164 // 0xa4 E0, 24 Stop
|
||||
162 // 0xa2 E0, 22 Play/Pause
|
||||
160 // 0xa0 E0, 20 Mute
|
||||
176 // 0xb0 E0, 30 Volume Up
|
||||
174 // 0xae E0, 2E Volume Down
|
||||
237 // 0xed E0, 6D Media Select
|
||||
236 // 0xec E0, 6C E-Mail
|
||||
161 // 0xa1 E0, 21 Calculator
|
||||
235 // 0xeb E0, 6B My Computer
|
||||
229 // 0xe5 E0, 65 WWW Search
|
||||
178 // 0xb2 E0, 32 WWW Home
|
||||
234 // 0xea E0, 6A WWW Back
|
||||
233 // 0xe9 E0, 69 WWW Forward
|
||||
232 // 0xe8 E0, 68 WWW Stop
|
||||
231 // 0xe7 E0, 67 WWW Refresh
|
||||
230 // 0xe6 E0, 66 WWW Favorites
|
||||
*/
|
||||
};
|
||||
|
||||
int cocoa_keycode_to_qemu(int keycode)
|
||||
{
|
||||
if((sizeof(keymap)/sizeof(int)) <= 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;
|
||||
|
||||
pool = [ [ NSAutoreleasePool alloc ] init ];
|
||||
distantPast = [ NSDate distantPast ];
|
||||
|
||||
vga_hw_update();
|
||||
|
||||
do {
|
||||
event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
|
||||
inMode: NSDefaultRunLoopMode dequeue:YES ];
|
||||
if (event != nil) {
|
||||
switch ([event type]) {
|
||||
case NSFlagsChanged:
|
||||
{
|
||||
int keycode = cocoa_keycode_to_qemu([event keyCode]);
|
||||
|
||||
if (keycode)
|
||||
{
|
||||
if (keycode == 58 || keycode == 69) {
|
||||
/* emulate caps lock and num lock keydown and keyup */
|
||||
kbd_put_keycode(keycode);
|
||||
kbd_put_keycode(keycode | 0x80);
|
||||
} else if (is_graphic_console()) {
|
||||
if (keycode & 0x80)
|
||||
kbd_put_keycode(0xe0);
|
||||
if (modifiers_state[keycode] == 0) {
|
||||
/* keydown */
|
||||
kbd_put_keycode(keycode & 0x7f);
|
||||
modifiers_state[keycode] = 1;
|
||||
} else {
|
||||
/* keyup */
|
||||
kbd_put_keycode(keycode | 0x80);
|
||||
modifiers_state[keycode] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* release Mouse grab when pressing ctrl+alt */
|
||||
if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask))
|
||||
{
|
||||
[window setTitle: @"QEMU"];
|
||||
[NSCursor unhide];
|
||||
CGAssociateMouseAndMouseCursorPosition ( TRUE );
|
||||
grab = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NSKeyDown:
|
||||
{
|
||||
int keycode = cocoa_keycode_to_qemu([event keyCode]);
|
||||
|
||||
/* handle command Key Combos */
|
||||
if ([event modifierFlags] & NSCommandKeyMask) {
|
||||
switch ([event keyCode]) {
|
||||
/* quit */
|
||||
case 12: /* q key */
|
||||
/* switch to windowed View */
|
||||
exit(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle control + alt Key Combos */
|
||||
if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
|
||||
switch (keycode) {
|
||||
/* toggle Monitor */
|
||||
case 0x02 ... 0x0a: /* '1' to '9' keys */
|
||||
console_select(keycode - 0x02);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* handle standard key events */
|
||||
if (is_graphic_console()) {
|
||||
if (keycode & 0x80) //check bit for e0 in front
|
||||
kbd_put_keycode(0xe0);
|
||||
kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front
|
||||
/* handle monitor key events */
|
||||
} else {
|
||||
switch([event keyCode]) {
|
||||
case 123:
|
||||
kbd_put_keysym(QEMU_KEY_LEFT);
|
||||
break;
|
||||
case 124:
|
||||
kbd_put_keysym(QEMU_KEY_RIGHT);
|
||||
break;
|
||||
case 125:
|
||||
kbd_put_keysym(QEMU_KEY_DOWN);
|
||||
break;
|
||||
case 126:
|
||||
kbd_put_keysym(QEMU_KEY_UP);
|
||||
break;
|
||||
default:
|
||||
kbd_put_keysym([[event characters] characterAtIndex:0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NSKeyUp:
|
||||
{
|
||||
int keycode = cocoa_keycode_to_qemu([event keyCode]);
|
||||
if (is_graphic_console()) {
|
||||
if (keycode & 0x80)
|
||||
kbd_put_keycode(0xe0);
|
||||
kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NSMouseMoved:
|
||||
if (grab) {
|
||||
int dx = [event deltaX];
|
||||
int dy = [event deltaY];
|
||||
int dz = [event deltaZ];
|
||||
int buttons = 0;
|
||||
kbd_mouse_event(dx, dy, dz, buttons);
|
||||
}
|
||||
break;
|
||||
|
||||
case NSLeftMouseDown:
|
||||
if (grab) {
|
||||
int buttons = 0;
|
||||
|
||||
/* leftclick+command simulates rightclick */
|
||||
if ([event modifierFlags] & NSCommandKeyMask) {
|
||||
buttons |= MOUSE_EVENT_RBUTTON;
|
||||
} else {
|
||||
buttons |= MOUSE_EVENT_LBUTTON;
|
||||
}
|
||||
kbd_mouse_event(0, 0, 0, buttons);
|
||||
} else {
|
||||
[NSApp sendEvent: event];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSLeftMouseDragged:
|
||||
if (grab) {
|
||||
int dx = [event deltaX];
|
||||
int dy = [event deltaY];
|
||||
int dz = [event deltaZ];
|
||||
int buttons = 0;
|
||||
if ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) { //leftclick+command simulates rightclick
|
||||
buttons |= MOUSE_EVENT_RBUTTON;
|
||||
} else {
|
||||
buttons |= MOUSE_EVENT_LBUTTON;
|
||||
}
|
||||
kbd_mouse_event(dx, dy, dz, buttons);
|
||||
}
|
||||
break;
|
||||
|
||||
case NSLeftMouseUp:
|
||||
if (grab) {
|
||||
kbd_mouse_event(0, 0, 0, 0);
|
||||
} else {
|
||||
[window setTitle: @"QEMU (Press ctrl + alt to release Mouse)"];
|
||||
[NSCursor hide];
|
||||
CGAssociateMouseAndMouseCursorPosition ( FALSE );
|
||||
grab = 1;
|
||||
//[NSApp sendEvent: event];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSRightMouseDown:
|
||||
if (grab) {
|
||||
int buttons = 0;
|
||||
|
||||
buttons |= MOUSE_EVENT_RBUTTON;
|
||||
kbd_mouse_event(0, 0, 0, buttons);
|
||||
} else {
|
||||
[NSApp sendEvent: event];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSRightMouseDragged:
|
||||
if (grab) {
|
||||
int dx = [event deltaX];
|
||||
int dy = [event deltaY];
|
||||
int dz = [event deltaZ];
|
||||
int buttons = 0;
|
||||
buttons |= MOUSE_EVENT_RBUTTON;
|
||||
kbd_mouse_event(dx, dy, dz, buttons);
|
||||
}
|
||||
break;
|
||||
|
||||
case NSRightMouseUp:
|
||||
if (grab) {
|
||||
kbd_mouse_event(0, 0, 0, 0);
|
||||
} else {
|
||||
[NSApp sendEvent: event];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSOtherMouseDragged:
|
||||
if (grab) {
|
||||
int dx = [event deltaX];
|
||||
int dy = [event deltaY];
|
||||
int dz = [event deltaZ];
|
||||
int buttons = 0;
|
||||
buttons |= MOUSE_EVENT_MBUTTON;
|
||||
kbd_mouse_event(dx, dy, dz, buttons);
|
||||
}
|
||||
break;
|
||||
|
||||
case NSOtherMouseDown:
|
||||
if (grab) {
|
||||
int buttons = 0;
|
||||
buttons |= MOUSE_EVENT_MBUTTON;
|
||||
kbd_mouse_event(0, 0, 0, buttons);
|
||||
} else {
|
||||
[NSApp sendEvent:event];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSOtherMouseUp:
|
||||
if (grab) {
|
||||
kbd_mouse_event(0, 0, 0, 0);
|
||||
} else {
|
||||
[NSApp sendEvent: event];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSScrollWheel:
|
||||
if (grab) {
|
||||
int dz = [event deltaY];
|
||||
kbd_mouse_event(0, 0, -dz, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
849
configure
vendored
849
configure
vendored
@@ -15,23 +15,28 @@ 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"
|
||||
interp_prefix="/usr/gnemul/qemu-i386"
|
||||
prefix=""
|
||||
interp_prefix="/usr/gnemul/qemu-%M"
|
||||
static="no"
|
||||
cross_prefix=""
|
||||
cc="gcc"
|
||||
host_cc="gcc"
|
||||
ar="ar"
|
||||
make="make"
|
||||
install="install"
|
||||
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)
|
||||
@@ -46,70 +51,192 @@ case "$cpu" in
|
||||
s390)
|
||||
cpu="s390"
|
||||
;;
|
||||
sparc|sun4[muv])
|
||||
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"
|
||||
dsound="no"
|
||||
coreaudio="no"
|
||||
alsa="no"
|
||||
fmod="no"
|
||||
fmod_lib=""
|
||||
fmod_inc=""
|
||||
bsd="no"
|
||||
linux="no"
|
||||
kqemu="no"
|
||||
profiler="no"
|
||||
kernel_path=""
|
||||
cocoa="no"
|
||||
check_gfx="yes"
|
||||
check_gcc="yes"
|
||||
softmmu="yes"
|
||||
user="no"
|
||||
build_docs="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" -o "$cpu" = "x86_64" ] ; then
|
||||
kqemu="yes"
|
||||
fi
|
||||
;;
|
||||
NetBSD)
|
||||
bsd="yes"
|
||||
oss="yes"
|
||||
;;
|
||||
OpenBSD)
|
||||
bsd="yes"
|
||||
oss="yes"
|
||||
;;
|
||||
Darwin)
|
||||
bsd="yes"
|
||||
darwin="yes"
|
||||
;;
|
||||
SunOS)
|
||||
solaris="yes"
|
||||
;;
|
||||
*)
|
||||
oss="yes"
|
||||
linux="yes"
|
||||
user="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
|
||||
|
||||
if [ "$solaris" = "yes" ] ; then
|
||||
make="gmake"
|
||||
install="ginstall"
|
||||
solarisrev=`uname -r | cut -f2 -d.`
|
||||
fi
|
||||
|
||||
# find source path
|
||||
# XXX: we assume an absolute path is given when launching configure,
|
||||
# except in './configure' case.
|
||||
source_path=${0%configure}
|
||||
source_path=${source_path%/}
|
||||
source_path_used="yes"
|
||||
if test -z "$source_path" -o "$source_path" = "." ; then
|
||||
source_path=`dirname "$0"`
|
||||
if [ -z "$source_path" ]; then
|
||||
source_path=`pwd`
|
||||
else
|
||||
source_path=`cd "$source_path"; pwd`
|
||||
fi
|
||||
if test "$source_path" = `pwd` ; then
|
||||
source_path_used="no"
|
||||
else
|
||||
source_path_used="yes"
|
||||
fi
|
||||
|
||||
for opt do
|
||||
optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
|
||||
case "$opt" in
|
||||
--prefix=*) prefix=`echo $opt | cut -d '=' -f 2`
|
||||
--help|-h) show_help=yes
|
||||
;;
|
||||
--interp-prefix=*) interp_prefix=`echo $opt | cut -d '=' -f 2`
|
||||
--prefix=*) prefix="$optarg"
|
||||
;;
|
||||
--source-path=*) source_path=`echo $opt | cut -d '=' -f 2`
|
||||
--interp-prefix=*) interp_prefix="$optarg"
|
||||
;;
|
||||
--cross-prefix=*) cross_prefix=`echo $opt | cut -d '=' -f 2`
|
||||
--source-path=*) source_path="$optarg"
|
||||
source_path_used="yes"
|
||||
;;
|
||||
--cc=*) cc=`echo $opt | cut -d '=' -f 2`
|
||||
--cross-prefix=*) cross_prefix="$optarg"
|
||||
;;
|
||||
--make=*) make=`echo $opt | cut -d '=' -f 2`
|
||||
--cc=*) cc="$optarg"
|
||||
;;
|
||||
--extra-cflags=*) CFLAGS="${opt#--extra-cflags=}"
|
||||
--host-cc=*) host_cc="$optarg"
|
||||
;;
|
||||
--extra-ldflags=*) LDFLAGS="${opt#--extra-ldflags=}"
|
||||
--make=*) make="$optarg"
|
||||
;;
|
||||
--extra-libs=*) extralibs=${opt#--extra-libs=}
|
||||
--install=*) install="$optarg"
|
||||
;;
|
||||
--cpu=*) cpu=`echo $opt | cut -d '=' -f 2`
|
||||
--extra-cflags=*) CFLAGS="$optarg"
|
||||
;;
|
||||
--extra-ldflags=*) LDFLAGS="$optarg"
|
||||
;;
|
||||
--cpu=*) cpu="$optarg"
|
||||
;;
|
||||
--target-list=*) target_list="$optarg"
|
||||
;;
|
||||
--enable-gprof) gprof="yes"
|
||||
;;
|
||||
--static) static="yes"
|
||||
;;
|
||||
--disable-sdl) sdl="no"
|
||||
;;
|
||||
--enable-coreaudio) coreaudio="yes"
|
||||
;;
|
||||
--enable-alsa) alsa="yes"
|
||||
;;
|
||||
--enable-dsound) dsound="yes"
|
||||
;;
|
||||
--enable-fmod) fmod="yes"
|
||||
;;
|
||||
--fmod-lib=*) fmod_lib="$optarg"
|
||||
;;
|
||||
--fmod-inc=*) fmod_inc="$optarg"
|
||||
;;
|
||||
--enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-"
|
||||
;;
|
||||
--disable-slirp) slirp="no"
|
||||
;;
|
||||
--enable-adlib) adlib="yes"
|
||||
;;
|
||||
--disable-kqemu) kqemu="no"
|
||||
;;
|
||||
--enable-profiler) profiler="yes"
|
||||
;;
|
||||
--kernel-path=*) kernel_path="$optarg"
|
||||
;;
|
||||
--enable-cocoa) cocoa="yes" ; coreaudio="yes" ; sdl="no"
|
||||
;;
|
||||
--disable-gfx-check) check_gfx="no"
|
||||
;;
|
||||
--disable-gcc-check) check_gcc="no"
|
||||
;;
|
||||
--disable-system) softmmu="no"
|
||||
;;
|
||||
--enable-system) softmmu="yes"
|
||||
;;
|
||||
--disable-user) user="no"
|
||||
;;
|
||||
--enable-user) user="yes"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -118,10 +245,127 @@ if test -z "$CFLAGS"; then
|
||||
CFLAGS="-O2"
|
||||
fi
|
||||
|
||||
if test x"$show_help" = x"yes" ; then
|
||||
cat << EOF
|
||||
|
||||
Usage: configure [options]
|
||||
Options: [defaults in brackets after descriptions]
|
||||
|
||||
EOF
|
||||
echo "Standard options:"
|
||||
echo " --help print this message"
|
||||
echo " --prefix=PREFIX install in PREFIX [$prefix]"
|
||||
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 support"
|
||||
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 " --host-cc=CC use C compiler CC [$host_cc] for dyngen etc."
|
||||
echo " --make=MAKE use specified make [$make]"
|
||||
echo " --install=INSTALL use specified install [$install]"
|
||||
echo " --static enable static build [$static]"
|
||||
echo " --enable-cocoa enable COCOA (Mac OS X only)"
|
||||
echo " --enable-mingw32 enable Win32 cross compilation with mingw32"
|
||||
echo " --enable-adlib enable Adlib emulation"
|
||||
echo " --enable-coreaudio enable Coreaudio audio driver"
|
||||
echo " --enable-alsa enable ALSA audio driver"
|
||||
echo " --enable-fmod enable FMOD audio driver"
|
||||
echo " --enabled-dsound enable DirectSound audio driver"
|
||||
echo " --enable-system enable all system emulation targets"
|
||||
echo " --disable-system disable all system emulation targets"
|
||||
echo " --enable-user enable all linux usermode emulation targets"
|
||||
echo " --disable-user disable all linux usermode emulation targets"
|
||||
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
|
||||
|
||||
cc="${cross_prefix}${cc}"
|
||||
ar="${cross_prefix}${ar}"
|
||||
strip="${cross_prefix}${strip}"
|
||||
|
||||
if [ ! -x "`which $cc`" ] ; then
|
||||
echo "Compiler $cc could not be found"
|
||||
exit
|
||||
fi
|
||||
|
||||
if test "$mingw32" = "yes" ; then
|
||||
linux="no"
|
||||
EXESUF=".exe"
|
||||
gdbstub="no"
|
||||
oss="no"
|
||||
if [ "$cpu" = "i386" ] ; then
|
||||
kqemu="yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# Solaris specific configure tool chain decisions
|
||||
#
|
||||
if test "$solaris" = "yes" ; then
|
||||
#
|
||||
# gcc for solaris 10/fcs in /usr/sfw/bin doesn't compile qemu correctly
|
||||
# override the check with --disable-gcc-check
|
||||
#
|
||||
if test "$solarisrev" -eq 10 -a "$check_gcc" = "yes" ; then
|
||||
solgcc=`which $cc`
|
||||
if test "$solgcc" = "/usr/sfw/bin/gcc" ; then
|
||||
echo "Solaris 10/FCS gcc in /usr/sfw/bin will not compiled qemu correctly."
|
||||
echo "please get gcc-3.4.3 or later, from www.blastwave.org using pkg-get -i gcc3"
|
||||
echo "or get the latest patch from SunSolve for gcc"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
solinst=`which $install 2> /dev/null | /usr/bin/grep -v "no $install in"`
|
||||
if test -z "$solinst" ; then
|
||||
echo "Solaris install program not found. Use --install=/usr/ucb/install or"
|
||||
echo "install fileutils from www.blastwave.org using pkg-get -i fileutils"
|
||||
echo "to get ginstall which is used by default (which lives in /opt/csw/bin)"
|
||||
exit 1
|
||||
fi
|
||||
if test "$solinst" = "/usr/sbin/install" ; then
|
||||
echo "Error: Solaris /usr/sbin/install is not an appropriate install program."
|
||||
echo "try ginstall from the GNU fileutils available from www.blastwave.org"
|
||||
echo "using pkg-get -i fileutils, or use --install=/usr/ucb/install"
|
||||
exit 1
|
||||
fi
|
||||
sol_ar=`which ar 2> /dev/null | /usr/bin/grep -v "no ar in"`
|
||||
if test -z "$sol_ar" ; then
|
||||
echo "Error: No path includes ar"
|
||||
if test -f /usr/ccs/bin/ar ; then
|
||||
echo "Add /usr/ccs/bin to your path and rerun configure"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if test -z "$target_list" ; then
|
||||
# these targets are portable
|
||||
if [ "$softmmu" = "yes" ] ; then
|
||||
target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu mipsel-softmmu arm-softmmu"
|
||||
fi
|
||||
# the following are Linux specific
|
||||
if [ "$user" = "yes" ] ; then
|
||||
target_list="i386-user arm-user armeb-user sparc-user ppc-user mips-user mipsel-user $target_list"
|
||||
fi
|
||||
else
|
||||
target_list=`echo "$target_list" | sed -e 's/,/ /g'`
|
||||
fi
|
||||
if test -z "$target_list" ; then
|
||||
echo "No targets enabled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$cross_prefix" ; then
|
||||
|
||||
# ---
|
||||
@@ -129,8 +373,8 @@ if test -z "$cross_prefix" ; then
|
||||
cat > $TMPC << EOF
|
||||
#include <inttypes.h>
|
||||
int main(int argc, char ** argv){
|
||||
volatile uint32_t i=0x01234567;
|
||||
return (*((uint8_t*)(&i))) == 0x67;
|
||||
volatile uint32_t i=0x01234567;
|
||||
return (*((uint8_t*)(&i))) == 0x67;
|
||||
}
|
||||
EOF
|
||||
|
||||
@@ -143,113 +387,476 @@ fi
|
||||
else
|
||||
|
||||
# if cross compiling, cannot launch a program, so make a static guess
|
||||
if test "$cpu" = "powerpc" -o "$cpu" = "mips" -o "$cpu" = "s390" ; 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
|
||||
# host long bits test
|
||||
hostlongbits="32"
|
||||
if test "$cpu" = "sparc64" -o "$cpu" = "ia64" -o "$cpu" = "x86_64" -o "$cpu" = "alpha"; then
|
||||
hostlongbits="64"
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
||||
if test x"$1" = x"-h" -o x"$1" = x"--help" ; then
|
||||
cat << EOF
|
||||
|
||||
Usage: configure [options]
|
||||
Options: [defaults in brackets after descriptions]
|
||||
|
||||
# Check for gcc4, error if pre-gcc4
|
||||
if test "$check_gcc" = "yes" ; then
|
||||
cat > $TMPC <<EOF
|
||||
#if __GNUC__ < 4
|
||||
#error gcc3
|
||||
#endif
|
||||
int main(){return 0;}
|
||||
EOF
|
||||
echo "Standard options:"
|
||||
echo " --help print this message"
|
||||
echo " --prefix=PREFIX install in PREFIX [$prefix]"
|
||||
echo " --interp-prefix=PREFIX where to find shared libraries, etc. [$interp_prefix]"
|
||||
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 ""
|
||||
echo "NOTE: The object files are build at the place where configure is launched"
|
||||
exit 1
|
||||
if $cc -o $TMPO $TMPC 2>/dev/null ; then
|
||||
echo "ERROR: \"$cc\" looks like gcc 4.x"
|
||||
echo "QEMU is known to have problems when compiled with gcc 4.x"
|
||||
echo "It is recommended that you use gcc 3.x to build QEMU"
|
||||
echo "To use this compiler anyway, configure with --disable-gcc-check"
|
||||
exit 1;
|
||||
fi
|
||||
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"
|
||||
##########################################
|
||||
# SDL probe
|
||||
|
||||
echo "Creating config.mak and config.h"
|
||||
sdl_too_old=no
|
||||
|
||||
echo "# Automatically generated by configure - do not modify" > config.mak
|
||||
echo "/* Automatically generated by configure - do not modify */" > $TMPH
|
||||
if test -z "$sdl" ; then
|
||||
|
||||
echo "prefix=$prefix" >> config.mak
|
||||
echo "#define CONFIG_QEMU_PREFIX \"$interp_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
|
||||
echo "#define HOST_I386 1" >> $TMPH
|
||||
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
|
||||
|
||||
# Check if tools are available to build documentation.
|
||||
if [ -x "`which texi2html`" ] && [ -x "`which pod2man`" ]; then
|
||||
build_docs="yes"
|
||||
fi
|
||||
|
||||
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 "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 "Host C compiler $host_cc"
|
||||
echo "make $make"
|
||||
echo "install $install"
|
||||
echo "host CPU $cpu"
|
||||
echo "host big endian $bigendian"
|
||||
echo "target list $target_list"
|
||||
echo "gprof enabled $gprof"
|
||||
echo "profiler $profiler"
|
||||
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 "CoreAudio support $coreaudio"
|
||||
echo "ALSA support $alsa"
|
||||
echo "DSound support $dsound"
|
||||
if test "$fmod" = "yes"; then
|
||||
if test -z $fmod_lib || test -z $fmod_inc; then
|
||||
echo
|
||||
echo "Error: You must specify path to FMOD library and headers"
|
||||
echo "Example: --fmod-inc=/path/include/fmod --fmod-lib=/path/lib/libfmod-3.74.so"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
fmod_support=" (lib='$fmod_lib' include='$fmod_inc')"
|
||||
else
|
||||
fmod_support=""
|
||||
fi
|
||||
echo "FMOD support $fmod $fmod_support"
|
||||
echo "kqemu support $kqemu"
|
||||
echo "Documentation $build_docs"
|
||||
|
||||
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 "# Configured with: $0 $@" >> $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 "INSTALL=$install" >> $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 "#define HOST_ARM 1" >> $TMPH
|
||||
echo "ARCH=arm" >> $config_mak
|
||||
echo "#define HOST_ARM 1" >> $config_h
|
||||
elif test "$cpu" = "powerpc" ; then
|
||||
echo "ARCH=ppc" >> config.mak
|
||||
echo "#define HOST_PPC 1" >> $TMPH
|
||||
echo "ARCH=ppc" >> $config_mak
|
||||
echo "#define HOST_PPC 1" >> $config_h
|
||||
elif test "$cpu" = "mips" ; then
|
||||
echo "ARCH=mips" >> config.mak
|
||||
echo "#define HOST_MIPS 1" >> $TMPH
|
||||
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" >> $TMPH
|
||||
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" >> $TMPH
|
||||
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
|
||||
echo "#define HOST_LONG_BITS $hostlongbits" >> $config_h
|
||||
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 "$solaris" = "yes" ; then
|
||||
echo "CONFIG_SOLARIS=yes" >> $config_mak
|
||||
echo "#define HOST_SOLARIS $solarisrev" >> $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 $profiler = "yes" ; then
|
||||
echo "#define CONFIG_PROFILER 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 "$coreaudio" = "yes" ; then
|
||||
echo "CONFIG_COREAUDIO=yes" >> $config_mak
|
||||
echo "#define CONFIG_COREAUDIO 1" >> $config_h
|
||||
fi
|
||||
if test "$alsa" = "yes" ; then
|
||||
echo "CONFIG_ALSA=yes" >> $config_mak
|
||||
echo "#define CONFIG_ALSA 1" >> $config_h
|
||||
fi
|
||||
if test "$dsound" = "yes" ; then
|
||||
echo "CONFIG_DSOUND=yes" >> $config_mak
|
||||
echo "#define CONFIG_DSOUND 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
|
||||
qemu_version=`head $source_path/VERSION`
|
||||
echo "VERSION=$qemu_version" >>$config_mak
|
||||
echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
|
||||
|
||||
echo "SRC_PATH=$source_path" >> $config_mak
|
||||
if [ "$source_path_used" = "yes" ]; then
|
||||
echo "VPATH=$source_path" >> $config_mak
|
||||
fi
|
||||
echo "TARGET_DIRS=$target_list" >> $config_mak
|
||||
if [ "$build_docs" = "yes" ] ; then
|
||||
echo "BUILD_DOCS=yes" >> $config_mak
|
||||
fi
|
||||
|
||||
# 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_cpu" = "ppc64" ] && target_bigendian=yes
|
||||
[ "$target_cpu" = "mips" ] && 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
|
||||
|
||||
if test "$target_user_only" = "no" -a "$check_gfx" = "yes" \
|
||||
-a "$sdl" = "no" -a "$cocoa" = "no" ; then
|
||||
echo "ERROR: QEMU requires SDL or Cocoa for graphical output"
|
||||
echo "To build QEMU without graphical output configure with --disable-gfx-check"
|
||||
echo "Note that this will disable all output from the virtual graphics card."
|
||||
exit 1;
|
||||
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
|
||||
|
||||
#
|
||||
# don't use ln -sf as not all "ln -sf" over write the file/link
|
||||
#
|
||||
rm -f $target_dir/Makefile
|
||||
ln -s $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" = "ppc64" ; then
|
||||
echo "TARGET_ARCH=ppc64" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"ppc64\"" >> $config_h
|
||||
echo "#define TARGET_PPC 1" >> $config_h
|
||||
echo "#define TARGET_PPC64 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
|
||||
elif test "$target_cpu" = "mips" -o "$target_cpu" = "mipsel" ; then
|
||||
echo "TARGET_ARCH=mips" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"mips\"" >> $config_h
|
||||
echo "#define TARGET_MIPS 1" >> $config_h
|
||||
elif test "$target_cpu" = "sh4" ; then
|
||||
echo "TARGET_ARCH=sh4" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"sh4\"" >> $config_h
|
||||
echo "#define TARGET_SH4 1" >> $config_h
|
||||
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
|
||||
if [ "${aa}" = "yes" ] ; then
|
||||
echo "SDL_CFLAGS=`$sdl_config --cflags` `aalib-config --cflags`" >> $config_mak
|
||||
else
|
||||
echo "SDL_CFLAGS=`$sdl_config --cflags`" >> $config_mak
|
||||
fi
|
||||
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
|
||||
@@ -258,17 +865,11 @@ if test "$source_path_used" = "yes" ; then
|
||||
for dir in $DIRS ; do
|
||||
mkdir -p $dir
|
||||
done
|
||||
# remove the link and recreate it, as not all "ln -sf" overwrite the link
|
||||
for f in $FILES ; do
|
||||
ln -sf $source_path/$f $f
|
||||
rm -f $f
|
||||
ln -s $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 $TMPO $TMPC $TMPE $TMPS $TMPH
|
||||
rm -f $TMPO $TMPC $TMPE $TMPS
|
||||
|
||||
919
cpu-all.h
Normal file
919
cpu-all.h
Normal file
@@ -0,0 +1,919 @@
|
||||
/*
|
||||
* 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(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
|
||||
|
||||
/* conservative code for little endian unaligned accesses */
|
||||
static inline int lduw_le_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_le_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_le_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_le_p(void *ptr)
|
||||
{
|
||||
uint8_t *p = ptr;
|
||||
uint32_t v1, v2;
|
||||
v1 = ldl_le_p(p);
|
||||
v2 = ldl_le_p(p + 4);
|
||||
return v1 | ((uint64_t)v2 << 32);
|
||||
}
|
||||
|
||||
static inline void stw_le_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_le_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_le_p(void *ptr, uint64_t v)
|
||||
{
|
||||
uint8_t *p = ptr;
|
||||
stl_le_p(p, (uint32_t)v);
|
||||
stl_le_p(p + 4, v >> 32);
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float32 ldfl_le_p(void *ptr)
|
||||
{
|
||||
union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.i = ldl_le_p(ptr);
|
||||
return u.f;
|
||||
}
|
||||
|
||||
static inline void stfl_le_p(void *ptr, float32 v)
|
||||
{
|
||||
union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.f = v;
|
||||
stl_le_p(ptr, u.i);
|
||||
}
|
||||
|
||||
static inline float64 ldfq_le_p(void *ptr)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.l.lower = ldl_le_p(ptr);
|
||||
u.l.upper = ldl_le_p(ptr + 4);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static inline void stfq_le_p(void *ptr, float64 v)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.d = v;
|
||||
stl_le_p(ptr, u.l.lower);
|
||||
stl_le_p(ptr + 4, u.l.upper);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int lduw_le_p(void *ptr)
|
||||
{
|
||||
return *(uint16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldsw_le_p(void *ptr)
|
||||
{
|
||||
return *(int16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldl_le_p(void *ptr)
|
||||
{
|
||||
return *(uint32_t *)ptr;
|
||||
}
|
||||
|
||||
static inline uint64_t ldq_le_p(void *ptr)
|
||||
{
|
||||
return *(uint64_t *)ptr;
|
||||
}
|
||||
|
||||
static inline void stw_le_p(void *ptr, int v)
|
||||
{
|
||||
*(uint16_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stl_le_p(void *ptr, int v)
|
||||
{
|
||||
*(uint32_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stq_le_p(void *ptr, uint64_t v)
|
||||
{
|
||||
*(uint64_t *)ptr = v;
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float32 ldfl_le_p(void *ptr)
|
||||
{
|
||||
return *(float32 *)ptr;
|
||||
}
|
||||
|
||||
static inline float64 ldfq_le_p(void *ptr)
|
||||
{
|
||||
return *(float64 *)ptr;
|
||||
}
|
||||
|
||||
static inline void stfl_le_p(void *ptr, float32 v)
|
||||
{
|
||||
*(float32 *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stfq_le_p(void *ptr, float64 v)
|
||||
{
|
||||
*(float64 *)ptr = v;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
|
||||
|
||||
static inline int lduw_be_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_be_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_be_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_be_p(void *ptr)
|
||||
{
|
||||
uint32_t a,b;
|
||||
a = ldl_be_p(ptr);
|
||||
b = ldl_be_p(ptr+4);
|
||||
return (((uint64_t)a<<32)|b);
|
||||
}
|
||||
|
||||
static inline void stw_be_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_be_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_be_p(void *ptr, uint64_t v)
|
||||
{
|
||||
stl_be_p(ptr, v >> 32);
|
||||
stl_be_p(ptr + 4, v);
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float32 ldfl_be_p(void *ptr)
|
||||
{
|
||||
union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.i = ldl_be_p(ptr);
|
||||
return u.f;
|
||||
}
|
||||
|
||||
static inline void stfl_be_p(void *ptr, float32 v)
|
||||
{
|
||||
union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.f = v;
|
||||
stl_be_p(ptr, u.i);
|
||||
}
|
||||
|
||||
static inline float64 ldfq_be_p(void *ptr)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.l.upper = ldl_be_p(ptr);
|
||||
u.l.lower = ldl_be_p(ptr + 4);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static inline void stfq_be_p(void *ptr, float64 v)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.d = v;
|
||||
stl_be_p(ptr, u.l.upper);
|
||||
stl_be_p(ptr + 4, u.l.lower);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int lduw_be_p(void *ptr)
|
||||
{
|
||||
return *(uint16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldsw_be_p(void *ptr)
|
||||
{
|
||||
return *(int16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldl_be_p(void *ptr)
|
||||
{
|
||||
return *(uint32_t *)ptr;
|
||||
}
|
||||
|
||||
static inline uint64_t ldq_be_p(void *ptr)
|
||||
{
|
||||
return *(uint64_t *)ptr;
|
||||
}
|
||||
|
||||
static inline void stw_be_p(void *ptr, int v)
|
||||
{
|
||||
*(uint16_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stl_be_p(void *ptr, int v)
|
||||
{
|
||||
*(uint32_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stq_be_p(void *ptr, uint64_t v)
|
||||
{
|
||||
*(uint64_t *)ptr = v;
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float32 ldfl_be_p(void *ptr)
|
||||
{
|
||||
return *(float32 *)ptr;
|
||||
}
|
||||
|
||||
static inline float64 ldfq_be_p(void *ptr)
|
||||
{
|
||||
return *(float64 *)ptr;
|
||||
}
|
||||
|
||||
static inline void stfl_be_p(void *ptr, float32 v)
|
||||
{
|
||||
*(float32 *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stfq_be_p(void *ptr, float64 v)
|
||||
{
|
||||
*(float64 *)ptr = v;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* target CPU memory access functions */
|
||||
#if defined(TARGET_WORDS_BIGENDIAN)
|
||||
#define lduw_p(p) lduw_be_p(p)
|
||||
#define ldsw_p(p) ldsw_be_p(p)
|
||||
#define ldl_p(p) ldl_be_p(p)
|
||||
#define ldq_p(p) ldq_be_p(p)
|
||||
#define ldfl_p(p) ldfl_be_p(p)
|
||||
#define ldfq_p(p) ldfq_be_p(p)
|
||||
#define stw_p(p, v) stw_be_p(p, v)
|
||||
#define stl_p(p, v) stl_be_p(p, v)
|
||||
#define stq_p(p, v) stq_be_p(p, v)
|
||||
#define stfl_p(p, v) stfl_be_p(p, v)
|
||||
#define stfq_p(p, v) stfq_be_p(p, v)
|
||||
#else
|
||||
#define lduw_p(p) lduw_le_p(p)
|
||||
#define ldsw_p(p) ldsw_le_p(p)
|
||||
#define ldl_p(p) ldl_le_p(p)
|
||||
#define ldq_p(p) ldq_le_p(p)
|
||||
#define ldfl_p(p) ldfl_le_p(p)
|
||||
#define ldfq_p(p) ldfq_le_p(p)
|
||||
#define stw_p(p, v) stw_le_p(p, v)
|
||||
#define stl_p(p, v) stl_le_p(p, v)
|
||||
#define stq_p(p, v) stq_le_p(p, v)
|
||||
#define stfl_p(p, v) stfl_le_p(p, v)
|
||||
#define stfq_p(p, v) stfq_le_p(p, v)
|
||||
#endif
|
||||
|
||||
/* MMU memory access macros */
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
/* On some host systems the guest address space is reserved on the host.
|
||||
* This allows the guest address space to be offset to a convenient location.
|
||||
*/
|
||||
//#define GUEST_BASE 0x20000000
|
||||
#define GUEST_BASE 0
|
||||
|
||||
/* All direct uses of g2h and h2g need to go away for usermode softmmu. */
|
||||
#define g2h(x) ((void *)((unsigned long)(x) + GUEST_BASE))
|
||||
#define h2g(x) ((target_ulong)(x - GUEST_BASE))
|
||||
|
||||
#define saddr(x) g2h(x)
|
||||
#define laddr(x) g2h(x)
|
||||
|
||||
#else /* !CONFIG_USER_ONLY */
|
||||
/* NOTE: we use double casts if pointers and target_ulong have
|
||||
different sizes */
|
||||
#define saddr(x) (uint8_t *)(long)(x)
|
||||
#define laddr(x) (uint8_t *)(long)(x)
|
||||
#endif
|
||||
|
||||
#define ldub_raw(p) ldub_p(laddr((p)))
|
||||
#define ldsb_raw(p) ldsb_p(laddr((p)))
|
||||
#define lduw_raw(p) lduw_p(laddr((p)))
|
||||
#define ldsw_raw(p) ldsw_p(laddr((p)))
|
||||
#define ldl_raw(p) ldl_p(laddr((p)))
|
||||
#define ldq_raw(p) ldq_p(laddr((p)))
|
||||
#define ldfl_raw(p) ldfl_p(laddr((p)))
|
||||
#define ldfq_raw(p) ldfq_p(laddr((p)))
|
||||
#define stb_raw(p, v) stb_p(saddr((p)), v)
|
||||
#define stw_raw(p, v) stw_p(saddr((p)), v)
|
||||
#define stl_raw(p, v) stl_p(saddr((p)), v)
|
||||
#define stq_raw(p, v) stq_p(saddr((p)), v)
|
||||
#define stfl_raw(p, v) stfl_p(saddr((p)), v)
|
||||
#define stfq_raw(p, v) stfq_p(saddr((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)
|
||||
|
||||
/* ??? These should be the larger of unsigned long and target_ulong. */
|
||||
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(target_ulong address);
|
||||
void page_set_flags(target_ulong start, target_ulong end, int flags);
|
||||
void page_unprotect_range(target_ulong data, target_ulong 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
|
||||
|
||||
#elif defined(TARGET_MIPS)
|
||||
#define CPUState CPUMIPSState
|
||||
#define cpu_init cpu_mips_init
|
||||
#define cpu_exec cpu_mips_exec
|
||||
#define cpu_gen_code cpu_mips_gen_code
|
||||
#define cpu_signal_handler cpu_mips_signal_handler
|
||||
|
||||
#elif defined(TARGET_SH4)
|
||||
#define CPUState CPUSH4State
|
||||
#define cpu_init cpu_sh4_init
|
||||
#define cpu_exec cpu_sh4_exec
|
||||
#define cpu_gen_code cpu_sh4_gen_code
|
||||
#define cpu_signal_handler cpu_sh4_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 *first_cpu;
|
||||
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 */
|
||||
#define CPU_INTERRUPT_FIQ 0x10 /* Fast interrupt pending. */
|
||||
#define CPU_INTERRUPT_HALT 0x20 /* CPU halt wanted */
|
||||
|
||||
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 TLB_INVALID_MASK (1 << 3)
|
||||
#define IO_MEM_SHIFT 4
|
||||
#define IO_MEM_NB_ENTRIES (1 << (TARGET_PAGE_BITS - IO_MEM_SHIFT))
|
||||
|
||||
#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_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 ldub_phys(target_phys_addr_t addr);
|
||||
uint32_t lduw_phys(target_phys_addr_t addr);
|
||||
uint32_t ldl_phys(target_phys_addr_t addr);
|
||||
uint64_t ldq_phys(target_phys_addr_t addr);
|
||||
void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val);
|
||||
void stb_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stw_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stl_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stq_phys(target_phys_addr_t addr, uint64_t val);
|
||||
|
||||
void cpu_physical_memory_write_rom(target_phys_addr_t addr,
|
||||
const uint8_t *buf, int len);
|
||||
int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
|
||||
uint8_t *buf, int len, int is_write);
|
||||
|
||||
#define VGA_DIRTY_FLAG 0x01
|
||||
#define CODE_DIRTY_FLAG 0x02
|
||||
|
||||
/* read dirty bit (return 0 or 1) */
|
||||
static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
|
||||
{
|
||||
return phys_ram_dirty[addr >> TARGET_PAGE_BITS] == 0xff;
|
||||
}
|
||||
|
||||
static inline int cpu_physical_memory_get_dirty(ram_addr_t addr,
|
||||
int dirty_flags)
|
||||
{
|
||||
return phys_ram_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags;
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_set_dirty(ram_addr_t addr)
|
||||
{
|
||||
phys_ram_dirty[addr >> TARGET_PAGE_BITS] = 0xff;
|
||||
}
|
||||
|
||||
void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
|
||||
int dirty_flags);
|
||||
void cpu_tlb_update_dirty(CPUState *env);
|
||||
|
||||
void dump_exec_info(FILE *f,
|
||||
int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
|
||||
|
||||
/* profiling */
|
||||
#ifdef CONFIG_PROFILER
|
||||
static inline int64_t profile_getclock(void)
|
||||
{
|
||||
int64_t val;
|
||||
asm volatile ("rdtsc" : "=A" (val));
|
||||
return val;
|
||||
}
|
||||
|
||||
extern int64_t kqemu_time, kqemu_time_start;
|
||||
extern int64_t qemu_time, qemu_time_start;
|
||||
extern int64_t tlb_flush_time;
|
||||
extern int64_t kqemu_exec_count;
|
||||
extern int64_t dev_time;
|
||||
extern int64_t kqemu_ret_int_count;
|
||||
extern int64_t kqemu_ret_excp_count;
|
||||
extern int64_t kqemu_ret_intr_count;
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* CPU_ALL_H */
|
||||
125
cpu-defs.h
Normal file
125
cpu-defs.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#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
|
||||
|
||||
/* address in the RAM (different from a physical address) */
|
||||
typedef unsigned long ram_addr_t;
|
||||
|
||||
#define HOST_LONG_SIZE (HOST_LONG_BITS / 8)
|
||||
|
||||
#define EXCP_INTERRUPT 0x10000 /* async interruption */
|
||||
#define EXCP_HLT 0x10001 /* hlt instruction reached */
|
||||
#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */
|
||||
#define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */
|
||||
#define MAX_BREAKPOINTS 32
|
||||
|
||||
#define TB_JMP_CACHE_BITS 12
|
||||
#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
|
||||
|
||||
#define CPU_TLB_BITS 8
|
||||
#define CPU_TLB_SIZE (1 << CPU_TLB_BITS)
|
||||
|
||||
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 addr_read;
|
||||
target_ulong addr_write;
|
||||
target_ulong addr_code;
|
||||
/* addend to virtual address to get physical address */
|
||||
target_phys_addr_t addend;
|
||||
} CPUTLBEntry;
|
||||
|
||||
#define CPU_COMMON \
|
||||
struct TranslationBlock *current_tb; /* currently executing TB */ \
|
||||
/* soft mmu support */ \
|
||||
/* in order to avoid passing too many arguments to the memory \
|
||||
write helpers, we store some rarely used information in the CPU \
|
||||
context) */ \
|
||||
unsigned long mem_write_pc; /* host pc at which the memory was \
|
||||
written */ \
|
||||
target_ulong mem_write_vaddr; /* target virtual addr at which the \
|
||||
memory was written */ \
|
||||
/* 0 = kernel, 1 = user */ \
|
||||
CPUTLBEntry tlb_table[2][CPU_TLB_SIZE]; \
|
||||
struct TranslationBlock *tb_jmp_cache[TB_JMP_CACHE_SIZE]; \
|
||||
\
|
||||
/* from this point: preserved by CPU reset */ \
|
||||
/* ice debug support */ \
|
||||
target_ulong breakpoints[MAX_BREAKPOINTS]; \
|
||||
int nb_breakpoints; \
|
||||
int singlestep_enabled; \
|
||||
\
|
||||
void *next_cpu; /* next CPU sharing TB cache */ \
|
||||
int cpu_index; /* CPU index (informative) */ \
|
||||
/* user data */ \
|
||||
void *opaque;
|
||||
|
||||
#endif
|
||||
1480
cpu-exec.c
Normal file
1480
cpu-exec.c
Normal file
File diff suppressed because it is too large
Load Diff
434
cpu-i386.h
434
cpu-i386.h
@@ -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 0
|
||||
#define EXCP01_SSTP 1
|
||||
#define EXCP02_NMI 2
|
||||
#define EXCP03_INT3 3
|
||||
#define EXCP04_INTO 4
|
||||
#define EXCP05_BOUND 5
|
||||
#define EXCP06_ILLOP 6
|
||||
#define EXCP07_PREX 7
|
||||
#define EXCP08_DBLE 8
|
||||
#define EXCP09_XERR 9
|
||||
#define EXCP0A_TSS 10
|
||||
#define EXCP0B_NOSEG 11
|
||||
#define EXCP0C_STACK 12
|
||||
#define EXCP0D_GPF 13
|
||||
#define EXCP0E_PAGE 14
|
||||
#define EXCP10_COPR 16
|
||||
#define EXCP11_ALGN 17
|
||||
#define EXCP12_MCHK 18
|
||||
|
||||
#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 */
|
||||
88
dis-asm.h
88
dis-asm.h
@@ -9,6 +9,7 @@
|
||||
#ifndef DIS_ASM_H
|
||||
#define DIS_ASM_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
@@ -16,7 +17,11 @@
|
||||
#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,
|
||||
@@ -51,6 +56,17 @@ enum bfd_architecture
|
||||
#define bfd_mach_m68030 5
|
||||
#define bfd_mach_m68040 6
|
||||
#define bfd_mach_m68060 7
|
||||
#define bfd_mach_cpu32 8
|
||||
#define bfd_mach_mcf5200 9
|
||||
#define bfd_mach_mcf5206e 10
|
||||
#define bfd_mach_mcf5307 11
|
||||
#define bfd_mach_mcf5407 12
|
||||
#define bfd_mach_mcf528x 13
|
||||
#define bfd_mach_mcfv4e 14
|
||||
#define bfd_mach_mcf521x 15
|
||||
#define bfd_mach_mcf5249 16
|
||||
#define bfd_mach_mcf547x 17
|
||||
#define bfd_mach_mcf548x 18
|
||||
bfd_arch_vax, /* DEC Vax */
|
||||
bfd_arch_i960, /* Intel 960 */
|
||||
/* The order of the following is important.
|
||||
@@ -73,16 +89,20 @@ enum bfd_architecture
|
||||
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. */
|
||||
/* 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_v9 6
|
||||
#define bfd_mach_sparc_v9a 7 /* with ultrasparc add'ns */
|
||||
/* Nonzero if MACH has the v9 instruction set. */
|
||||
#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_v9a)
|
||||
((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
|
||||
@@ -101,6 +121,9 @@ enum bfd_architecture
|
||||
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 */
|
||||
@@ -114,6 +137,24 @@ enum bfd_architecture
|
||||
#define bfd_mach_h8300h 2
|
||||
#define bfd_mach_h8300s 3
|
||||
bfd_arch_powerpc, /* PowerPC */
|
||||
#define bfd_mach_ppc 0
|
||||
#define bfd_mach_ppc64 1
|
||||
#define bfd_mach_ppc_403 403
|
||||
#define bfd_mach_ppc_403gc 4030
|
||||
#define bfd_mach_ppc_505 505
|
||||
#define bfd_mach_ppc_601 601
|
||||
#define bfd_mach_ppc_602 602
|
||||
#define bfd_mach_ppc_603 603
|
||||
#define bfd_mach_ppc_ec603e 6031
|
||||
#define bfd_mach_ppc_604 604
|
||||
#define bfd_mach_ppc_620 620
|
||||
#define bfd_mach_ppc_630 630
|
||||
#define bfd_mach_ppc_750 750
|
||||
#define bfd_mach_ppc_860 860
|
||||
#define bfd_mach_ppc_a35 35
|
||||
#define bfd_mach_ppc_rs64ii 642
|
||||
#define bfd_mach_ppc_rs64iii 643
|
||||
#define bfd_mach_ppc_7400 7400
|
||||
bfd_arch_rs6000, /* IBM RS/6000 */
|
||||
bfd_arch_hppa, /* HP PA RISC */
|
||||
bfd_arch_d10v, /* Mitsubishi D10V */
|
||||
@@ -122,10 +163,23 @@ enum bfd_architecture
|
||||
#define bfd_mach_z8002 2
|
||||
bfd_arch_h8500, /* Hitachi H8/500 */
|
||||
bfd_arch_sh, /* Hitachi SH */
|
||||
#define bfd_mach_sh 0
|
||||
#define bfd_mach_sh 1
|
||||
#define bfd_mach_sh2 0x20
|
||||
#define bfd_mach_sh_dsp 0x2d
|
||||
#define bfd_mach_sh2a 0x2a
|
||||
#define bfd_mach_sh2a_nofpu 0x2b
|
||||
#define bfd_mach_sh2e 0x2e
|
||||
#define bfd_mach_sh3 0x30
|
||||
#define bfd_mach_sh3_nommu 0x31
|
||||
#define bfd_mach_sh3_dsp 0x3d
|
||||
#define bfd_mach_sh3e 0x3e
|
||||
#define bfd_mach_sh4 0x40
|
||||
#define bfd_mach_sh4_nofpu 0x41
|
||||
#define bfd_mach_sh4_nommu_nofpu 0x42
|
||||
#define bfd_mach_sh4a 0x4a
|
||||
#define bfd_mach_sh4a_nofpu 0x4b
|
||||
#define bfd_mach_sh4al_dsp 0x4d
|
||||
#define bfd_mach_sh5 0x50
|
||||
bfd_arch_alpha, /* Dec Alpha */
|
||||
bfd_arch_arm, /* Advanced Risc Machines ARM */
|
||||
#define bfd_mach_arm_2 1
|
||||
@@ -279,6 +333,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;
|
||||
|
||||
|
||||
@@ -299,8 +356,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*));
|
||||
@@ -320,6 +376,7 @@ 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. */
|
||||
@@ -380,6 +437,19 @@ 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
|
||||
#define ATTRIBUTE_UNUSED __attribute__((unused))
|
||||
|
||||
/* from libbfd */
|
||||
|
||||
bfd_vma bfd_getl32 (const bfd_byte *addr);
|
||||
bfd_vma bfd_getb32 (const bfd_byte *addr);
|
||||
bfd_vma bfd_getl16 (const bfd_byte *addr);
|
||||
bfd_vma bfd_getb16 (const bfd_byte *addr);
|
||||
typedef enum bfd_boolean {false, true} boolean;
|
||||
typedef boolean bfd_boolean;
|
||||
|
||||
#endif /* ! defined (DIS_ASM_H) */
|
||||
|
||||
79
dis-buf.c
79
dis-buf.c
@@ -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;
|
||||
}
|
||||
413
disas.c
Normal file
413
disas.c
Normal file
@@ -0,0 +1,413 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
bfd_vma bfd_getl16 (const bfd_byte *addr)
|
||||
{
|
||||
unsigned long v;
|
||||
|
||||
v = (unsigned long) addr[0];
|
||||
v |= (unsigned long) addr[1] << 8;
|
||||
return (bfd_vma) v;
|
||||
}
|
||||
|
||||
bfd_vma bfd_getb16 (const bfd_byte *addr)
|
||||
{
|
||||
unsigned long v;
|
||||
|
||||
v = (unsigned long) addr[0] << 24;
|
||||
v |= (unsigned long) addr[1] << 16;
|
||||
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
|
||||
ppc - nonzero means little endian
|
||||
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;
|
||||
#ifdef TARGET_SPARC64
|
||||
disasm_info.mach = bfd_mach_sparc_v9b;
|
||||
#endif
|
||||
#elif defined(TARGET_PPC)
|
||||
if (flags)
|
||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||
#ifdef TARGET_PPC64
|
||||
disasm_info.mach = bfd_mach_ppc64;
|
||||
#else
|
||||
disasm_info.mach = bfd_mach_ppc;
|
||||
#endif
|
||||
print_insn = print_insn_ppc;
|
||||
#elif defined(TARGET_MIPS)
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
print_insn = print_insn_big_mips;
|
||||
#else
|
||||
print_insn = print_insn_little_mips;
|
||||
#endif
|
||||
#elif defined(TARGET_M68K)
|
||||
print_insn = print_insn_m68k;
|
||||
#elif defined(TARGET_SH4)
|
||||
disasm_info.mach = bfd_mach_sh4;
|
||||
print_insn = print_insn_sh;
|
||||
#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;
|
||||
#elif defined(__MIPSEB__)
|
||||
print_insn = print_insn_big_mips;
|
||||
#elif defined(__MIPSEL__)
|
||||
print_insn = print_insn_little_mips;
|
||||
#elif defined(__m68k__)
|
||||
print_insn = print_insn_m68k;
|
||||
#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;
|
||||
target_ulong addr;
|
||||
|
||||
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;
|
||||
|
||||
addr = sym[i].st_value;
|
||||
#ifdef TARGET_ARM
|
||||
/* The bottom address bit marks a Thumb symbol. */
|
||||
addr &= ~(target_ulong)1;
|
||||
#endif
|
||||
if (orig_addr >= addr
|
||||
&& orig_addr < addr + 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 CPUState *monitor_disas_env;
|
||||
|
||||
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(monitor_disas_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(CPUState *env,
|
||||
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_env = env;
|
||||
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)
|
||||
#ifdef TARGET_PPC64
|
||||
disasm_info.mach = bfd_mach_ppc64;
|
||||
#else
|
||||
disasm_info.mach = bfd_mach_ppc;
|
||||
#endif
|
||||
print_insn = print_insn_ppc;
|
||||
#elif defined(TARGET_MIPS)
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
print_insn = print_insn_big_mips;
|
||||
#else
|
||||
print_insn = print_insn_little_mips;
|
||||
#endif
|
||||
#elif defined(TARGET_M68K)
|
||||
print_insn = print_insn_m68k;
|
||||
#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
|
||||
21
disas.h
Normal file
21
disas.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#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(CPUState *env,
|
||||
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 */
|
||||
257
dyngen-exec.h
Normal file
257
dyngen-exec.h
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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__
|
||||
|
||||
/* prevent Solaris from trying to typedef FILE in gcc's
|
||||
include/floatingpoint.h which will conflict with the
|
||||
definition down below */
|
||||
#ifdef __sun__
|
||||
#define _FILEDEFED
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
|
||||
/* if Solaris/__sun__, don't typedef int8_t, as it will be typedef'd
|
||||
prior to this and will cause an error in compliation, conflicting
|
||||
with /usr/include/sys/int_types.h, line 75 */
|
||||
#ifndef __sun__
|
||||
typedef signed char int8_t;
|
||||
#endif
|
||||
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")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n)
|
||||
#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")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ( \
|
||||
"set " ASM_NAME(__op_gen_label) #n ", %g1; jmp %g1; 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__) */
|
||||
9
dyngen-op.h
Normal file
9
dyngen-op.h
Normal file
@@ -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;
|
||||
}
|
||||
429
dyngen.h
Normal file
429
dyngen.h
Normal file
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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);
|
||||
}
|
||||
/* Keep code ptr aligned. */
|
||||
if ((long) gen_code_ptr & 15)
|
||||
gen_code_ptr += 8;
|
||||
*gen_code_pp = gen_code_ptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
134
elf.h
134
elf.h
@@ -1,5 +1,5 @@
|
||||
#ifndef _ELF_H
|
||||
#define _ELF_H
|
||||
#ifndef _QEMU_ELF_H
|
||||
#define _QEMU_ELF_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
@@ -31,11 +31,29 @@ typedef int64_t Elf64_Sxword;
|
||||
#define PT_LOPROC 0x70000000
|
||||
#define PT_HIPROC 0x7fffffff
|
||||
#define PT_MIPS_REGINFO 0x70000000
|
||||
#define PT_MIPS_OPTIONS 0x70000001
|
||||
|
||||
/* Flags in the e_flags field of the header */
|
||||
/* MIPS architecture level. */
|
||||
#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
|
||||
#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
|
||||
#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
|
||||
#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
|
||||
#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
|
||||
#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */
|
||||
#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */
|
||||
|
||||
/* The ABI of a file. */
|
||||
#define EF_MIPS_ABI_O32 0x00001000 /* O32 ABI. */
|
||||
#define EF_MIPS_ABI_O64 0x00002000 /* O32 extended for 64 bit. */
|
||||
|
||||
#define EF_MIPS_NOREORDER 0x00000001
|
||||
#define EF_MIPS_PIC 0x00000002
|
||||
#define EF_MIPS_CPIC 0x00000004
|
||||
#define EF_MIPS_ABI2 0x00000020
|
||||
#define EF_MIPS_OPTIONS_FIRST 0x00000080
|
||||
#define EF_MIPS_32BITMODE 0x00000100
|
||||
#define EF_MIPS_ABI 0x0000f000
|
||||
#define EF_MIPS_ARCH 0xf0000000
|
||||
|
||||
/* These constants define the different elf file types */
|
||||
@@ -767,6 +785,114 @@ typedef struct {
|
||||
#define PF_HP_LAZYSWAP 0x04000000
|
||||
#define PF_HP_SBP 0x08000000
|
||||
|
||||
/* IA-64 specific declarations. */
|
||||
|
||||
/* Processor specific flags for the Ehdr e_flags field. */
|
||||
#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */
|
||||
#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */
|
||||
#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */
|
||||
|
||||
/* Processor specific values for the Phdr p_type field. */
|
||||
#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */
|
||||
#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */
|
||||
|
||||
/* Processor specific flags for the Phdr p_flags field. */
|
||||
#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */
|
||||
|
||||
/* Processor specific values for the Shdr sh_type field. */
|
||||
#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */
|
||||
#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */
|
||||
|
||||
/* Processor specific flags for the Shdr sh_flags field. */
|
||||
#define SHF_IA_64_SHORT 0x10000000 /* section near gp */
|
||||
#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */
|
||||
|
||||
/* Processor specific values for the Dyn d_tag field. */
|
||||
#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0)
|
||||
#define DT_IA_64_NUM 1
|
||||
|
||||
/* IA-64 relocations. */
|
||||
#define R_IA64_NONE 0x00 /* none */
|
||||
#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */
|
||||
#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */
|
||||
#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */
|
||||
#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */
|
||||
#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */
|
||||
#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */
|
||||
#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */
|
||||
#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */
|
||||
#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */
|
||||
#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */
|
||||
#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */
|
||||
#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */
|
||||
#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */
|
||||
#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */
|
||||
#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */
|
||||
#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */
|
||||
#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */
|
||||
#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */
|
||||
#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */
|
||||
#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */
|
||||
#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */
|
||||
#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */
|
||||
#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */
|
||||
#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */
|
||||
#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */
|
||||
#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */
|
||||
#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */
|
||||
#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */
|
||||
#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */
|
||||
#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */
|
||||
#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */
|
||||
#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */
|
||||
#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */
|
||||
#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */
|
||||
#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */
|
||||
#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */
|
||||
#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */
|
||||
#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */
|
||||
#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */
|
||||
#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */
|
||||
#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */
|
||||
#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */
|
||||
#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */
|
||||
#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */
|
||||
#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */
|
||||
#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */
|
||||
#define R_IA64_REL32MSB 0x6c /* data 4 + REL */
|
||||
#define R_IA64_REL32LSB 0x6d /* data 4 + REL */
|
||||
#define R_IA64_REL64MSB 0x6e /* data 8 + REL */
|
||||
#define R_IA64_REL64LSB 0x6f /* data 8 + REL */
|
||||
#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */
|
||||
#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */
|
||||
#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */
|
||||
#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */
|
||||
#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */
|
||||
#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */
|
||||
#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */
|
||||
#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */
|
||||
#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */
|
||||
#define R_IA64_COPY 0x84 /* copy relocation */
|
||||
#define R_IA64_SUB 0x85 /* Addend and symbol difference */
|
||||
#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */
|
||||
#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */
|
||||
#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */
|
||||
#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */
|
||||
#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */
|
||||
#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */
|
||||
#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */
|
||||
#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */
|
||||
#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */
|
||||
#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */
|
||||
#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */
|
||||
#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */
|
||||
#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */
|
||||
#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */
|
||||
#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */
|
||||
#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */
|
||||
#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */
|
||||
#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */
|
||||
#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */
|
||||
|
||||
typedef struct elf32_rel {
|
||||
Elf32_Addr r_offset;
|
||||
@@ -996,6 +1122,7 @@ typedef struct elf64_note {
|
||||
#define elf_phdr elf32_phdr
|
||||
#define elf_note elf32_note
|
||||
#define elf_shdr elf32_shdr
|
||||
#define elf_sym elf32_sym
|
||||
|
||||
#ifdef ELF_USES_RELOCA
|
||||
# define ELF_RELOC Elf32_Rela
|
||||
@@ -1009,6 +1136,7 @@ typedef struct elf64_note {
|
||||
#define elf_phdr elf64_phdr
|
||||
#define elf_note elf64_note
|
||||
#define elf_shdr elf64_shdr
|
||||
#define elf_sym elf64_sym
|
||||
|
||||
#ifdef ELF_USES_RELOCA
|
||||
# define ELF_RELOC Elf64_Rela
|
||||
@@ -1029,4 +1157,4 @@ typedef struct elf64_note {
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _ELF_H */
|
||||
#endif /* _QEMU_ELF_H */
|
||||
|
||||
205
elf_ops.h
Normal file
205
elf_ops.h
Normal file
@@ -0,0 +1,205 @@
|
||||
static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr)
|
||||
{
|
||||
bswap16s(&ehdr->e_type); /* Object file type */
|
||||
bswap16s(&ehdr->e_machine); /* Architecture */
|
||||
bswap32s(&ehdr->e_version); /* Object file version */
|
||||
bswapSZs(&ehdr->e_entry); /* Entry point virtual address */
|
||||
bswapSZs(&ehdr->e_phoff); /* Program header table file offset */
|
||||
bswapSZs(&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 glue(bswap_phdr, SZ)(struct elf_phdr *phdr)
|
||||
{
|
||||
bswap32s(&phdr->p_type); /* Segment type */
|
||||
bswapSZs(&phdr->p_offset); /* Segment file offset */
|
||||
bswapSZs(&phdr->p_vaddr); /* Segment virtual address */
|
||||
bswapSZs(&phdr->p_paddr); /* Segment physical address */
|
||||
bswapSZs(&phdr->p_filesz); /* Segment size in file */
|
||||
bswapSZs(&phdr->p_memsz); /* Segment size in memory */
|
||||
bswap32s(&phdr->p_flags); /* Segment flags */
|
||||
bswapSZs(&phdr->p_align); /* Segment alignment */
|
||||
}
|
||||
|
||||
static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr)
|
||||
{
|
||||
bswap32s(&shdr->sh_name);
|
||||
bswap32s(&shdr->sh_type);
|
||||
bswapSZs(&shdr->sh_flags);
|
||||
bswapSZs(&shdr->sh_addr);
|
||||
bswapSZs(&shdr->sh_offset);
|
||||
bswapSZs(&shdr->sh_size);
|
||||
bswap32s(&shdr->sh_link);
|
||||
bswap32s(&shdr->sh_info);
|
||||
bswapSZs(&shdr->sh_addralign);
|
||||
bswapSZs(&shdr->sh_entsize);
|
||||
}
|
||||
|
||||
static void glue(bswap_sym, SZ)(struct elf_sym *sym)
|
||||
{
|
||||
bswap32s(&sym->st_name);
|
||||
bswapSZs(&sym->st_value);
|
||||
bswapSZs(&sym->st_size);
|
||||
bswap16s(&sym->st_shndx);
|
||||
}
|
||||
|
||||
static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table,
|
||||
int n, int type)
|
||||
{
|
||||
int i;
|
||||
for(i=0;i<n;i++) {
|
||||
if (shdr_table[i].sh_type == type)
|
||||
return shdr_table + i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
|
||||
{
|
||||
struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
|
||||
struct elf_sym *syms = NULL;
|
||||
#if (SZ == 64)
|
||||
struct elf32_sym *syms32 = NULL;
|
||||
#endif
|
||||
struct syminfo *s;
|
||||
int nsyms, i;
|
||||
char *str = NULL;
|
||||
|
||||
shdr_table = load_at(fd, ehdr->e_shoff,
|
||||
sizeof(struct elf_shdr) * ehdr->e_shnum);
|
||||
if (!shdr_table)
|
||||
return -1;
|
||||
|
||||
if (must_swab) {
|
||||
for (i = 0; i < ehdr->e_shnum; i++) {
|
||||
glue(bswap_shdr, SZ)(shdr_table + i);
|
||||
}
|
||||
}
|
||||
|
||||
symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB);
|
||||
if (!symtab)
|
||||
goto fail;
|
||||
syms = load_at(fd, symtab->sh_offset, symtab->sh_size);
|
||||
if (!syms)
|
||||
goto fail;
|
||||
|
||||
nsyms = symtab->sh_size / sizeof(struct elf_sym);
|
||||
#if (SZ == 64)
|
||||
syms32 = qemu_mallocz(nsyms * sizeof(struct elf32_sym));
|
||||
#endif
|
||||
for (i = 0; i < nsyms; i++) {
|
||||
if (must_swab)
|
||||
glue(bswap_sym, SZ)(&syms[i]);
|
||||
#if (SZ == 64)
|
||||
syms32[i].st_name = syms[i].st_name;
|
||||
syms32[i].st_info = syms[i].st_info;
|
||||
syms32[i].st_other = syms[i].st_other;
|
||||
syms32[i].st_shndx = syms[i].st_shndx;
|
||||
syms32[i].st_value = syms[i].st_value & 0xffffffff;
|
||||
syms32[i].st_size = syms[i].st_size & 0xffffffff;
|
||||
#endif
|
||||
}
|
||||
/* String table */
|
||||
if (symtab->sh_link >= ehdr->e_shnum)
|
||||
goto fail;
|
||||
strtab = &shdr_table[symtab->sh_link];
|
||||
|
||||
str = load_at(fd, strtab->sh_offset, strtab->sh_size);
|
||||
if (!str)
|
||||
goto fail;
|
||||
|
||||
/* Commit */
|
||||
s = qemu_mallocz(sizeof(*s));
|
||||
#if (SZ == 64)
|
||||
s->disas_symtab = syms32;
|
||||
qemu_free(syms);
|
||||
#else
|
||||
s->disas_symtab = syms;
|
||||
#endif
|
||||
s->disas_num_syms = nsyms;
|
||||
s->disas_strtab = str;
|
||||
s->next = syminfos;
|
||||
syminfos = s;
|
||||
qemu_free(shdr_table);
|
||||
return 0;
|
||||
fail:
|
||||
#if (SZ == 64)
|
||||
qemu_free(syms32);
|
||||
#endif
|
||||
qemu_free(syms);
|
||||
qemu_free(str);
|
||||
qemu_free(shdr_table);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int glue(load_elf, SZ)(int fd, int64_t virt_to_phys_addend,
|
||||
int must_swab, uint64_t *pentry)
|
||||
{
|
||||
struct elfhdr ehdr;
|
||||
struct elf_phdr *phdr = NULL, *ph;
|
||||
int size, i, total_size;
|
||||
elf_word mem_size, addr;
|
||||
uint8_t *data = NULL;
|
||||
|
||||
if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
|
||||
goto fail;
|
||||
if (must_swab) {
|
||||
glue(bswap_ehdr, SZ)(&ehdr);
|
||||
}
|
||||
|
||||
if (pentry)
|
||||
*pentry = (uint64_t)ehdr.e_entry;
|
||||
|
||||
glue(load_symbols, SZ)(&ehdr, fd, must_swab);
|
||||
|
||||
size = ehdr.e_phnum * sizeof(phdr[0]);
|
||||
lseek(fd, ehdr.e_phoff, SEEK_SET);
|
||||
phdr = qemu_mallocz(size);
|
||||
if (!phdr)
|
||||
goto fail;
|
||||
if (read(fd, phdr, size) != size)
|
||||
goto fail;
|
||||
if (must_swab) {
|
||||
for(i = 0; i < ehdr.e_phnum; i++) {
|
||||
ph = &phdr[i];
|
||||
glue(bswap_phdr, SZ)(ph);
|
||||
}
|
||||
}
|
||||
|
||||
total_size = 0;
|
||||
for(i = 0; i < ehdr.e_phnum; i++) {
|
||||
ph = &phdr[i];
|
||||
if (ph->p_type == PT_LOAD) {
|
||||
mem_size = ph->p_memsz;
|
||||
/* XXX: avoid allocating */
|
||||
data = qemu_mallocz(mem_size);
|
||||
if (ph->p_filesz > 0) {
|
||||
if (lseek(fd, ph->p_offset, SEEK_SET) < 0)
|
||||
goto fail;
|
||||
if (read(fd, data, ph->p_filesz) != ph->p_filesz)
|
||||
goto fail;
|
||||
}
|
||||
addr = ph->p_vaddr + virt_to_phys_addend;
|
||||
|
||||
cpu_physical_memory_write_rom(addr, data, mem_size);
|
||||
|
||||
total_size += mem_size;
|
||||
|
||||
qemu_free(data);
|
||||
data = NULL;
|
||||
}
|
||||
}
|
||||
qemu_free(phdr);
|
||||
return total_size;
|
||||
fail:
|
||||
qemu_free(data);
|
||||
qemu_free(phdr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
605
exec-all.h
Normal file
605
exec-all.h
Normal file
@@ -0,0 +1,605 @@
|
||||
/*
|
||||
* 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 __GNUC__ < 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];
|
||||
extern uint32_t gen_opc_hflags[OPC_BUF_SIZE];
|
||||
|
||||
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(CPUState *env);
|
||||
int page_unprotect(target_ulong 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_exec(CPUState *env, target_ulong vaddr,
|
||||
target_phys_addr_t paddr, int prot,
|
||||
int is_user, int is_softmmu);
|
||||
static inline int tlb_set_page(CPUState *env, target_ulong vaddr,
|
||||
target_phys_addr_t paddr, int prot,
|
||||
int is_user, int is_softmmu)
|
||||
{
|
||||
if (prot & PAGE_READ)
|
||||
prot |= PAGE_EXEC;
|
||||
return tlb_set_page_exec(env, vaddr, paddr, prot, is_user, 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_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 (16 * 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 */
|
||||
/* 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_jmp_cache_hash_func(target_ulong pc)
|
||||
{
|
||||
return (pc ^ (pc >> TB_JMP_CACHE_BITS)) & (TB_JMP_CACHE_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_phys(TranslationBlock *tb,
|
||||
target_ulong phys_pc, target_ulong phys_page2);
|
||||
|
||||
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;
|
||||
|
||||
#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
|
||||
|
||||
#define ASM_OP_LABEL_NAME(n, opname) \
|
||||
ASM_NAME(__op_label) #n "." ASM_NAME(opname)
|
||||
|
||||
#if defined(__powerpc__)
|
||||
|
||||
/* we patch the jump instruction directly */
|
||||
#define GOTO_TB(opname, tbparam, n)\
|
||||
do {\
|
||||
asm volatile (ASM_DATA_SECTION\
|
||||
ASM_OP_LABEL_NAME(n, 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_OP_LABEL_NAME(n, 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 \
|
||||
__asm__(ASM_OP_LABEL_NAME(n, opname)) = &&label ## n;\
|
||||
goto *(void *)(((TranslationBlock *)tbparam)->tb_next[n]);\
|
||||
label ## n: ;\
|
||||
dummy_label ## n: ;\
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
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 */
|
||||
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_MIPS)
|
||||
is_user = ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM);
|
||||
#elif defined (TARGET_SPARC)
|
||||
is_user = (env->psrs == 0);
|
||||
#elif defined (TARGET_ARM)
|
||||
is_user = ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR);
|
||||
#elif defined (TARGET_SH4)
|
||||
is_user = ((env->sr & SR_MD) == 0);
|
||||
#else
|
||||
#error unimplemented CPU
|
||||
#endif
|
||||
if (__builtin_expect(env->tlb_table[is_user][index].addr_code !=
|
||||
(addr & TARGET_PAGE_MASK), 0)) {
|
||||
ldub_code(addr);
|
||||
}
|
||||
pd = env->tlb_table[is_user][index].addr_code & ~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_table[is_user][index].addend - (unsigned long)phys_ram_base;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef USE_KQEMU
|
||||
#define KQEMU_MODIFY_PAGE_MASK (0xff & ~(VGA_DIRTY_FLAG | CODE_DIRTY_FLAG))
|
||||
|
||||
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);
|
||||
void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr);
|
||||
void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr);
|
||||
void kqemu_cpu_interrupt(CPUState *env);
|
||||
void kqemu_record_dump(void);
|
||||
|
||||
static inline int kqemu_is_ok(CPUState *env)
|
||||
{
|
||||
return(env->kqemu_enabled &&
|
||||
(env->cr[0] & CR0_PE_MASK) &&
|
||||
!(env->hflags & HF_INHIBIT_IRQ_MASK) &&
|
||||
(env->eflags & IF_MASK) &&
|
||||
!(env->eflags & VM_MASK) &&
|
||||
(env->kqemu_enabled == 2 ||
|
||||
((env->hflags & HF_CPL_MASK) == 3 &&
|
||||
(env->eflags & IOPL_MASK) != IOPL_MASK)));
|
||||
}
|
||||
|
||||
#endif
|
||||
514
exec-i386.c
514
exec-i386.c
@@ -1,514 +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
|
||||
|
||||
#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
|
||||
|
||||
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
|
||||
}
|
||||
177
exec-i386.h
177
exec-i386.h
@@ -1,177 +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 BUGGY_GCC_DIV64
|
||||
#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
|
||||
#ifdef __s390__
|
||||
register unsigned int T0 asm("r7");
|
||||
register unsigned int T1 asm("r8");
|
||||
register unsigned int A0 asm("r9");
|
||||
register struct CPUX86State *env asm("r10");
|
||||
#endif
|
||||
#ifdef __alpha__
|
||||
register unsigned int T0 asm("$9");
|
||||
register unsigned int T1 asm("$10");
|
||||
register unsigned int A0 asm("$11");
|
||||
register struct CPUX86State *env asm("$12");
|
||||
#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);
|
||||
720
fpu/softfloat-macros.h
Normal file
720
fpu/softfloat-macros.h
Normal file
@@ -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 );
|
||||
|
||||
}
|
||||
|
||||
363
fpu/softfloat-native.c
Normal file
363
fpu/softfloat-native.c
Normal file
@@ -0,0 +1,363 @@
|
||||
/* 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) ((long)rint(d))
|
||||
#define llrint(d) ((long long)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
|
||||
|
||||
/* XXX: this code implements the x86 behaviour, not the IEEE one. */
|
||||
#if HOST_LONG_BITS == 32
|
||||
static inline int long_to_int32(long a)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#else
|
||||
static inline int long_to_int32(long a)
|
||||
{
|
||||
if (a != (int32_t)a)
|
||||
a = 0x80000000;
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float32_to_int32( float32 a STATUS_PARAM)
|
||||
{
|
||||
return long_to_int32(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 long_to_int32(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 long_to_int32(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
|
||||
359
fpu/softfloat-native.h
Normal file
359
fpu/softfloat-native.h
Normal file
@@ -0,0 +1,359 @@
|
||||
/* Native implementation of soft float functions */
|
||||
#include <math.h>
|
||||
|
||||
#if (defined(_BSD) && !defined(__APPLE__)) || defined(HOST_SOLARIS)
|
||||
#include <ieeefp.h>
|
||||
#define fabsf(f) ((float)fabs(f))
|
||||
#else
|
||||
#include <fenv.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define some C99-7.12.3 classification macros and
|
||||
* some C99-.12.4 for Solaris systems OS less than 10,
|
||||
* or Solaris 10 systems running GCC 3.x or less.
|
||||
* Solaris 10 with GCC4 does not need these macros as they
|
||||
* are defined in <iso/math_c99.h> with a compiler directive
|
||||
*/
|
||||
#if defined(HOST_SOLARIS) && (( HOST_SOLARIS <= 9 ) || ( ( HOST_SOLARIS >= 10 ) && ( __GNUC__ <= 4) ))
|
||||
/*
|
||||
* C99 7.12.3 classification macros
|
||||
* and
|
||||
* C99 7.12.14 comparison macros
|
||||
*
|
||||
* ... do not work on Solaris 10 using GNU CC 3.4.x.
|
||||
* Try to workaround the missing / broken C99 math macros.
|
||||
*/
|
||||
|
||||
#define isnormal(x) (fpclass(x) >= FP_NZERO)
|
||||
#define isgreater(x, y) ((!unordered(x, y)) && ((x) > (y)))
|
||||
#define isgreaterequal(x, y) ((!unordered(x, y)) && ((x) >= (y)))
|
||||
#define isless(x, y) ((!unordered(x, y)) && ((x) < (y)))
|
||||
#define islessequal(x, y) ((!unordered(x, y)) && ((x) <= (y)))
|
||||
#define isunordered(x,y) unordered(x, y)
|
||||
#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__)) || defined(HOST_SOLARIS)
|
||||
enum {
|
||||
float_round_nearest_even = FP_RN,
|
||||
float_round_down = FP_RM,
|
||||
float_round_up = FP_RP,
|
||||
float_round_to_zero = FP_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
|
||||
464
fpu/softfloat-specialize.h
Normal file
464
fpu/softfloat-specialize.h
Normal file
@@ -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
fpu/softfloat.c
Normal file
5320
fpu/softfloat.c
Normal file
File diff suppressed because it is too large
Load Diff
398
fpu/softfloat.h
Normal file
398
fpu/softfloat.h
Normal file
@@ -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( int8 flags 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 */
|
||||
943
gdbstub.c
Normal file
943
gdbstub.c
Normal file
@@ -0,0 +1,943 @@
|
||||
/*
|
||||
* gdb server stub
|
||||
*
|
||||
* Copyright (c) 2003-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
|
||||
*/
|
||||
#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 {
|
||||
CPUState *env; /* current CPU */
|
||||
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(do_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(do_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]);
|
||||
do_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]);
|
||||
do_store_xer(env, tswapl(registers[101]));
|
||||
}
|
||||
#elif defined (TARGET_SPARC)
|
||||
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
{
|
||||
target_ulong *registers = (target_ulong *)mem_buf;
|
||||
int i;
|
||||
|
||||
/* fill in g0..g7 */
|
||||
for(i = 0; i < 8; 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]));
|
||||
}
|
||||
#ifndef TARGET_SPARC64
|
||||
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
|
||||
registers[64] = tswapl(env->y);
|
||||
{
|
||||
target_ulong tmp;
|
||||
|
||||
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 * sizeof(target_ulong);
|
||||
#else
|
||||
for (i = 0; i < 32; i += 2) {
|
||||
registers[i/2 + 64] = tswapl(*((uint64_t *)&env->fpr[i]));
|
||||
}
|
||||
registers[81] = tswapl(env->pc);
|
||||
registers[82] = tswapl(env->npc);
|
||||
registers[83] = tswapl(env->tstate[env->tl]);
|
||||
registers[84] = tswapl(env->fsr);
|
||||
registers[85] = tswapl(env->fprs);
|
||||
registers[86] = tswapl(env->y);
|
||||
return 87 * sizeof(target_ulong);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
{
|
||||
target_ulong *registers = (target_ulong *)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 + 8]);
|
||||
}
|
||||
/* fill in fprs */
|
||||
for (i = 0; i < 32; i++) {
|
||||
*((uint32_t *)&env->fpr[i]) = tswapl(registers[i + 32]);
|
||||
}
|
||||
#ifndef TARGET_SPARC64
|
||||
/* 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]);
|
||||
#else
|
||||
for (i = 0; i < 32; i += 2) {
|
||||
uint64_t tmp;
|
||||
tmp = tswapl(registers[i/2 + 64]) << 32;
|
||||
tmp |= tswapl(registers[i/2 + 64 + 1]);
|
||||
*((uint64_t *)&env->fpr[i]) = tmp;
|
||||
}
|
||||
env->pc = tswapl(registers[81]);
|
||||
env->npc = tswapl(registers[82]);
|
||||
env->tstate[env->tl] = tswapl(registers[83]);
|
||||
env->fsr = tswapl(registers[84]);
|
||||
env->fprs = tswapl(registers[85]);
|
||||
env->y = tswapl(registers[86]);
|
||||
#endif
|
||||
}
|
||||
#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 (cpsr_read(env));
|
||||
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;
|
||||
cpsr_write (env, tswapl(*(uint32_t *)ptr), 0xffffffff);
|
||||
}
|
||||
#elif defined (TARGET_MIPS)
|
||||
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
{
|
||||
int i;
|
||||
uint8_t *ptr;
|
||||
|
||||
ptr = mem_buf;
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
*(uint32_t *)ptr = tswapl(env->gpr[i]);
|
||||
ptr += 4;
|
||||
}
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->CP0_Status);
|
||||
ptr += 4;
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->LO);
|
||||
ptr += 4;
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->HI);
|
||||
ptr += 4;
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->CP0_BadVAddr);
|
||||
ptr += 4;
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->CP0_Cause);
|
||||
ptr += 4;
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->PC);
|
||||
ptr += 4;
|
||||
|
||||
/* 32 FP registers, fsr, fir, fp. Not yet implemented. */
|
||||
|
||||
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;
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
env->gpr[i] = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
}
|
||||
|
||||
env->CP0_Status = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
|
||||
env->LO = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
|
||||
env->HI = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
|
||||
env->CP0_BadVAddr = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
|
||||
env->CP0_Cause = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
|
||||
env->PC = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
}
|
||||
#elif defined (TARGET_SH4)
|
||||
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
{
|
||||
uint32_t *ptr = (uint32_t *)mem_buf;
|
||||
int i;
|
||||
|
||||
#define SAVE(x) *ptr++=tswapl(x)
|
||||
for (i = 0; i < 16; i++) SAVE(env->gregs[i]);
|
||||
SAVE (env->pc);
|
||||
SAVE (env->pr);
|
||||
SAVE (env->gbr);
|
||||
SAVE (env->vbr);
|
||||
SAVE (env->mach);
|
||||
SAVE (env->macl);
|
||||
SAVE (env->sr);
|
||||
SAVE (0); /* TICKS */
|
||||
SAVE (0); /* STALLS */
|
||||
SAVE (0); /* CYCLES */
|
||||
SAVE (0); /* INSTS */
|
||||
SAVE (0); /* PLR */
|
||||
|
||||
return ((uint8_t *)ptr - mem_buf);
|
||||
}
|
||||
|
||||
static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
{
|
||||
uint32_t *ptr = (uint32_t *)mem_buf;
|
||||
int i;
|
||||
|
||||
#define LOAD(x) (x)=*ptr++;
|
||||
for (i = 0; i < 16; i++) LOAD(env->gregs[i]);
|
||||
LOAD (env->pc);
|
||||
LOAD (env->pr);
|
||||
LOAD (env->gbr);
|
||||
LOAD (env->vbr);
|
||||
LOAD (env->mach);
|
||||
LOAD (env->macl);
|
||||
LOAD (env->sr);
|
||||
}
|
||||
#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;
|
||||
#elif defined (TARGET_ARM)
|
||||
env->regs[15] = addr;
|
||||
#elif defined (TARGET_SH4)
|
||||
env->pc = addr;
|
||||
#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;
|
||||
#elif defined (TARGET_ARM)
|
||||
env->regs[15] = addr;
|
||||
#elif defined (TARGET_SH4)
|
||||
env->pc = addr;
|
||||
#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) {
|
||||
put_packet (s, "E14");
|
||||
} else {
|
||||
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(s->env, 0);
|
||||
|
||||
if (reason == EXCP_DEBUG) {
|
||||
tb_flush(s->env);
|
||||
ret = SIGTRAP;
|
||||
} else if (reason == EXCP_INTERRUPT) {
|
||||
ret = SIGINT;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "S%02x", ret);
|
||||
put_packet(s, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void gdb_read_byte(GDBState *s, int ch)
|
||||
{
|
||||
CPUState *env = s->env;
|
||||
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, 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 void gdb_read(void *opaque)
|
||||
{
|
||||
GDBState *s = opaque;
|
||||
int i, size;
|
||||
uint8_t buf[4096];
|
||||
|
||||
size = read(s->fd, buf, sizeof(buf));
|
||||
if (size < 0)
|
||||
return;
|
||||
if (size == 0) {
|
||||
/* end of connection */
|
||||
qemu_del_vm_stop_handler(gdb_vm_stopped, s);
|
||||
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
||||
qemu_free(s);
|
||||
vm_start();
|
||||
} else {
|
||||
for(i = 0; i < size; i++)
|
||||
gdb_read_byte(s, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void gdb_accept(void *opaque)
|
||||
{
|
||||
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->env = first_cpu; /* XXX: allow to change CPU */
|
||||
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_set_fd_handler(s->fd, gdb_read, NULL, 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);
|
||||
#else
|
||||
qemu_set_fd_handler(gdbserver_fd, gdb_accept, NULL, NULL);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
12
gdbstub.h
Normal file
12
gdbstub.h
Normal file
@@ -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
|
||||
410
hw/adb.c
Normal file
410
hw/adb.c
Normal file
@@ -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, 0);
|
||||
}
|
||||
341
hw/adlib.c
Normal file
341
hw/adlib.c
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* QEMU Proxy for OPL2/3 emulation by MAME team
|
||||
*
|
||||
* Copyright (c) 2004-2005 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 ADLIB_KILL_TIMERS 1
|
||||
|
||||
#define dolog(...) AUD_log ("adlib", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
#ifdef HAS_YMF262
|
||||
#include "ymf262.h"
|
||||
void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
|
||||
#define SHIFT 2
|
||||
#else
|
||||
#include "fmopl.h"
|
||||
#define SHIFT 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 {
|
||||
QEMUSoundCard card;
|
||||
int ticking[2];
|
||||
int enabled;
|
||||
int active;
|
||||
int bufpos;
|
||||
#ifdef DEBUG
|
||||
int64_t exp[2];
|
||||
#endif
|
||||
int16_t *mixbuf;
|
||||
uint64_t dexp[2];
|
||||
SWVoiceOut *voice;
|
||||
int left, pos, samples;
|
||||
QEMUAudioTimeStamp ats;
|
||||
#ifndef HAS_YMF262
|
||||
FM_OPL *opl;
|
||||
#endif
|
||||
} AdlibState;
|
||||
|
||||
static AdlibState glob_adlib;
|
||||
|
||||
static void adlib_stop_opl_timer (AdlibState *s, size_t n)
|
||||
{
|
||||
#ifdef HAS_YMF262
|
||||
YMF262TimerOver (0, n);
|
||||
#else
|
||||
OPLTimerOver (s->opl, n);
|
||||
#endif
|
||||
s->ticking[n] = 0;
|
||||
}
|
||||
|
||||
static void adlib_kill_timers (AdlibState *s)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
if (s->ticking[i]) {
|
||||
uint64_t delta;
|
||||
|
||||
delta = AUD_get_elapsed_usec_out (s->voice, &s->ats);
|
||||
ldebug (
|
||||
"delta = %f dexp = %f expired => %d\n",
|
||||
delta / 1000000.0,
|
||||
s->dexp[i] / 1000000.0,
|
||||
delta >= s->dexp[i]
|
||||
);
|
||||
if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
|
||||
adlib_stop_opl_timer (s, i);
|
||||
AUD_init_time_stamp_out (s->voice, &s->ats);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static IO_WRITE_PROTO(adlib_write)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
int a = nport & 3;
|
||||
int status;
|
||||
|
||||
s->active = 1;
|
||||
AUD_set_active_out (s->voice, 1);
|
||||
|
||||
adlib_kill_timers (s);
|
||||
|
||||
#ifdef HAS_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;
|
||||
|
||||
adlib_kill_timers (s);
|
||||
|
||||
#ifdef HAS_YMF262
|
||||
data = YMF262Read (0, a);
|
||||
#else
|
||||
data = OPLRead (s->opl, a);
|
||||
#endif
|
||||
return data;
|
||||
}
|
||||
|
||||
static void timer_handler (int c, double interval_Sec)
|
||||
{
|
||||
AdlibState *s = &glob_adlib;
|
||||
unsigned n = c & 1;
|
||||
#ifdef DEBUG
|
||||
double interval;
|
||||
int64_t exp;
|
||||
#endif
|
||||
|
||||
if (interval_Sec == 0.0) {
|
||||
s->ticking[n] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
s->ticking[n] = 1;
|
||||
#ifdef DEBUG
|
||||
interval = ticks_per_sec * interval_Sec;
|
||||
exp = qemu_get_clock (vm_clock) + interval;
|
||||
s->exp[n] = exp;
|
||||
#endif
|
||||
|
||||
s->dexp[n] = interval_Sec * 1000000.0;
|
||||
AUD_init_time_stamp_out (s->voice, &s->ats);
|
||||
}
|
||||
|
||||
static int write_audio (AdlibState *s, int samples)
|
||||
{
|
||||
int net = 0;
|
||||
int pos = s->pos;
|
||||
|
||||
while (samples) {
|
||||
int nbytes, wbytes, wsampl;
|
||||
|
||||
nbytes = samples << SHIFT;
|
||||
wbytes = AUD_write (
|
||||
s->voice,
|
||||
s->mixbuf + (pos << (SHIFT - 1)),
|
||||
nbytes
|
||||
);
|
||||
|
||||
if (wbytes) {
|
||||
wsampl = wbytes >> SHIFT;
|
||||
|
||||
samples -= wsampl;
|
||||
pos = (pos + wsampl) % s->samples;
|
||||
|
||||
net += wsampl;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return net;
|
||||
}
|
||||
|
||||
static void adlib_callback (void *opaque, int free)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
int samples, net = 0, to_play, written;
|
||||
|
||||
samples = free >> SHIFT;
|
||||
if (!(s->active && s->enabled) || !samples) {
|
||||
return;
|
||||
}
|
||||
|
||||
to_play = audio_MIN (s->left, samples);
|
||||
while (to_play) {
|
||||
written = write_audio (s, to_play);
|
||||
|
||||
if (written) {
|
||||
s->left -= written;
|
||||
samples -= written;
|
||||
to_play -= written;
|
||||
s->pos = (s->pos + written) % s->samples;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
samples = audio_MIN (samples, s->samples - s->pos);
|
||||
if (!samples) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAS_YMF262
|
||||
YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
|
||||
#else
|
||||
YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
|
||||
#endif
|
||||
|
||||
while (samples) {
|
||||
written = write_audio (s, samples);
|
||||
|
||||
if (written) {
|
||||
net += written;
|
||||
samples -= written;
|
||||
s->pos = (s->pos + written) % s->samples;
|
||||
}
|
||||
else {
|
||||
s->left = samples;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Adlib_fini (AdlibState *s)
|
||||
{
|
||||
#ifdef HAS_YMF262
|
||||
YMF262Shutdown ();
|
||||
#else
|
||||
if (s->opl) {
|
||||
OPLDestroy (s->opl);
|
||||
s->opl = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s->mixbuf) {
|
||||
qemu_free (s->mixbuf);
|
||||
}
|
||||
|
||||
s->active = 0;
|
||||
s->enabled = 0;
|
||||
AUD_remove_card (&s->card);
|
||||
}
|
||||
|
||||
int Adlib_init (AudioState *audio)
|
||||
{
|
||||
AdlibState *s = &glob_adlib;
|
||||
audsettings_t as;
|
||||
|
||||
if (!audio) {
|
||||
dolog ("No audio state\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAS_YMF262
|
||||
if (YMF262Init (1, 14318180, conf.freq)) {
|
||||
dolog ("YMF262Init %d failed\n", conf.freq);
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
YMF262SetTimerHandler (0, timer_handler, 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 -1;
|
||||
}
|
||||
else {
|
||||
OPLSetTimerHandler (s->opl, timer_handler, 0);
|
||||
s->enabled = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
as.freq = conf.freq;
|
||||
as.nchannels = SHIFT;
|
||||
as.fmt = AUD_FMT_S16;
|
||||
|
||||
AUD_register_card (audio, "adlib", &s->card);
|
||||
|
||||
s->voice = AUD_open_out (
|
||||
&s->card,
|
||||
s->voice,
|
||||
"adlib",
|
||||
s,
|
||||
adlib_callback,
|
||||
&as,
|
||||
0 /* XXX: little endian? */
|
||||
);
|
||||
if (!s->voice) {
|
||||
Adlib_fini (s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
|
||||
s->mixbuf = qemu_mallocz (s->samples << SHIFT);
|
||||
|
||||
if (!s->mixbuf) {
|
||||
dolog ("Could not allocate mixing buffer, %d samples (each %d bytes)\n",
|
||||
s->samples, 1 << SHIFT);
|
||||
Adlib_fini (s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
105
hw/arm_boot.c
Normal file
105
hw/arm_boot.c
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* ARM kernel loader.
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
#define KERNEL_ARGS_ADDR 0x100
|
||||
#define KERNEL_LOAD_ADDR 0x00010000
|
||||
#define INITRD_LOAD_ADDR 0x00800000
|
||||
|
||||
/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
|
||||
static uint32_t bootloader[] = {
|
||||
0xe3a00000, /* mov r0, #0 */
|
||||
0xe3a01000, /* mov r1, #0x?? */
|
||||
0xe3811c00, /* orr r1, r1, #0x??00 */
|
||||
0xe59f2000, /* ldr r2, [pc, #0] */
|
||||
0xe59ff000, /* ldr pc, [pc, #0] */
|
||||
0, /* Address of kernel args. Set by integratorcp_init. */
|
||||
0 /* Kernel entry point. Set by integratorcp_init. */
|
||||
};
|
||||
|
||||
static void set_kernel_args(uint32_t ram_size, int initrd_size,
|
||||
const char *kernel_cmdline)
|
||||
{
|
||||
uint32_t *p;
|
||||
|
||||
p = (uint32_t *)(phys_ram_base + KERNEL_ARGS_ADDR);
|
||||
/* ATAG_CORE */
|
||||
stl_raw(p++, 5);
|
||||
stl_raw(p++, 0x54410001);
|
||||
stl_raw(p++, 1);
|
||||
stl_raw(p++, 0x1000);
|
||||
stl_raw(p++, 0);
|
||||
/* ATAG_MEM */
|
||||
stl_raw(p++, 4);
|
||||
stl_raw(p++, 0x54410002);
|
||||
stl_raw(p++, ram_size);
|
||||
stl_raw(p++, 0);
|
||||
if (initrd_size) {
|
||||
/* ATAG_INITRD2 */
|
||||
stl_raw(p++, 4);
|
||||
stl_raw(p++, 0x54420005);
|
||||
stl_raw(p++, INITRD_LOAD_ADDR);
|
||||
stl_raw(p++, initrd_size);
|
||||
}
|
||||
if (kernel_cmdline && *kernel_cmdline) {
|
||||
/* ATAG_CMDLINE */
|
||||
int cmdline_size;
|
||||
|
||||
cmdline_size = strlen(kernel_cmdline);
|
||||
memcpy (p + 2, kernel_cmdline, cmdline_size + 1);
|
||||
cmdline_size = (cmdline_size >> 2) + 1;
|
||||
stl_raw(p++, cmdline_size + 2);
|
||||
stl_raw(p++, 0x54410009);
|
||||
p += cmdline_size;
|
||||
}
|
||||
/* ATAG_END */
|
||||
stl_raw(p++, 0);
|
||||
stl_raw(p++, 0);
|
||||
}
|
||||
|
||||
void arm_load_kernel(int ram_size, const char *kernel_filename,
|
||||
const char *kernel_cmdline, const char *initrd_filename,
|
||||
int board_id)
|
||||
{
|
||||
int kernel_size;
|
||||
int initrd_size;
|
||||
int n;
|
||||
|
||||
/* Load the kernel. */
|
||||
if (!kernel_filename) {
|
||||
fprintf(stderr, "Kernel image must be specified\n");
|
||||
exit(1);
|
||||
}
|
||||
kernel_size = load_image(kernel_filename,
|
||||
phys_ram_base + KERNEL_LOAD_ADDR);
|
||||
if (kernel_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
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 initrd '%s'\n",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
initrd_size = 0;
|
||||
}
|
||||
bootloader[1] |= board_id & 0xff;
|
||||
bootloader[2] |= (board_id >> 8) & 0xff;
|
||||
bootloader[5] = KERNEL_ARGS_ADDR;
|
||||
bootloader[6] = KERNEL_LOAD_ADDR;
|
||||
for (n = 0; n < sizeof(bootloader) / 4; n++)
|
||||
stl_raw(phys_ram_base + (n * 4), bootloader[n]);
|
||||
set_kernel_args(ram_size, initrd_size, kernel_cmdline);
|
||||
}
|
||||
|
||||
73
hw/arm_pic.c
Normal file
73
hw/arm_pic.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Generic ARM Programmable Interrupt Controller support.
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the LGPL
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
#include "arm_pic.h"
|
||||
|
||||
/* Stub functions for hardware that doesn't exist. */
|
||||
void pic_set_irq(int irq, int level)
|
||||
{
|
||||
cpu_abort(cpu_single_env, "pic_set_irq");
|
||||
}
|
||||
|
||||
void pic_info(void)
|
||||
{
|
||||
}
|
||||
|
||||
void irq_info(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void pic_set_irq_new(void *opaque, int irq, int level)
|
||||
{
|
||||
arm_pic_handler *p = (arm_pic_handler *)opaque;
|
||||
/* Call the real handler. */
|
||||
(*p)(opaque, irq, level);
|
||||
}
|
||||
|
||||
/* Model the IRQ/FIQ CPU interrupt lines as a two input interrupt controller.
|
||||
Input 0 is IRQ and input 1 is FIQ. */
|
||||
typedef struct
|
||||
{
|
||||
arm_pic_handler handler;
|
||||
CPUState *cpu_env;
|
||||
} arm_pic_cpu_state;
|
||||
|
||||
static void arm_pic_cpu_handler(void *opaque, int irq, int level)
|
||||
{
|
||||
arm_pic_cpu_state *s = (arm_pic_cpu_state *)opaque;
|
||||
switch (irq) {
|
||||
case ARM_PIC_CPU_IRQ:
|
||||
if (level)
|
||||
cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
|
||||
else
|
||||
cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
|
||||
break;
|
||||
case ARM_PIC_CPU_FIQ:
|
||||
if (level)
|
||||
cpu_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
|
||||
else
|
||||
cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
|
||||
break;
|
||||
default:
|
||||
cpu_abort(s->cpu_env, "arm_pic_cpu_handler: Bad interrput line %d\n",
|
||||
irq);
|
||||
}
|
||||
}
|
||||
|
||||
void *arm_pic_init_cpu(CPUState *env)
|
||||
{
|
||||
arm_pic_cpu_state *s;
|
||||
|
||||
s = (arm_pic_cpu_state *)malloc(sizeof(arm_pic_cpu_state));
|
||||
s->handler = arm_pic_cpu_handler;
|
||||
s->cpu_env = env;
|
||||
return s;
|
||||
}
|
||||
27
hw/arm_pic.h
Normal file
27
hw/arm_pic.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Generic ARM Programmable Interrupt Controller support.
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the LGPL.
|
||||
*
|
||||
* Arm hardware uses a wide variety of interrupt handling hardware.
|
||||
* This provides a generic framework for connecting interrupt sources and
|
||||
* inputs.
|
||||
*/
|
||||
|
||||
#ifndef ARM_INTERRUPT_H
|
||||
#define ARM_INTERRUPT_H 1
|
||||
|
||||
/* The first element of an individual PIC state structures should
|
||||
be a pointer to the handler routine. */
|
||||
typedef void (*arm_pic_handler)(void *opaque, int irq, int level);
|
||||
|
||||
/* The CPU is also modeled as an interrupt controller. */
|
||||
#define ARM_PIC_CPU_IRQ 0
|
||||
#define ARM_PIC_CPU_FIQ 1
|
||||
void *arm_pic_init_cpu(CPUState *env);
|
||||
|
||||
#endif /* !ARM_INTERRUPT_H */
|
||||
|
||||
383
hw/arm_timer.c
Normal file
383
hw/arm_timer.c
Normal file
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* ARM PrimeCell Timer modules.
|
||||
*
|
||||
* Copyright (c) 2005-2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
#include "arm_pic.h"
|
||||
|
||||
/* Common timer implementation. */
|
||||
|
||||
#define TIMER_CTRL_ONESHOT (1 << 0)
|
||||
#define TIMER_CTRL_32BIT (1 << 1)
|
||||
#define TIMER_CTRL_DIV1 (0 << 2)
|
||||
#define TIMER_CTRL_DIV16 (1 << 2)
|
||||
#define TIMER_CTRL_DIV256 (2 << 2)
|
||||
#define TIMER_CTRL_IE (1 << 5)
|
||||
#define TIMER_CTRL_PERIODIC (1 << 6)
|
||||
#define TIMER_CTRL_ENABLE (1 << 7)
|
||||
|
||||
typedef struct {
|
||||
int64_t next_time;
|
||||
int64_t expires;
|
||||
int64_t loaded;
|
||||
QEMUTimer *timer;
|
||||
uint32_t control;
|
||||
uint32_t count;
|
||||
uint32_t limit;
|
||||
int raw_freq;
|
||||
int freq;
|
||||
int int_level;
|
||||
void *pic;
|
||||
int irq;
|
||||
} arm_timer_state;
|
||||
|
||||
/* Calculate the new expiry time of the given timer. */
|
||||
|
||||
static void arm_timer_reload(arm_timer_state *s)
|
||||
{
|
||||
int64_t delay;
|
||||
|
||||
s->loaded = s->expires;
|
||||
delay = muldiv64(s->count, ticks_per_sec, s->freq);
|
||||
if (delay == 0)
|
||||
delay = 1;
|
||||
s->expires += delay;
|
||||
}
|
||||
|
||||
/* Check all active timers, and schedule the next timer interrupt. */
|
||||
|
||||
static void arm_timer_update(arm_timer_state *s, int64_t now)
|
||||
{
|
||||
int64_t next;
|
||||
|
||||
/* Ignore disabled timers. */
|
||||
if ((s->control & TIMER_CTRL_ENABLE) == 0)
|
||||
return;
|
||||
/* Ignore expired one-shot timers. */
|
||||
if (s->count == 0 && (s->control & TIMER_CTRL_ONESHOT))
|
||||
return;
|
||||
if (s->expires - now <= 0) {
|
||||
/* Timer has expired. */
|
||||
s->int_level = 1;
|
||||
if (s->control & TIMER_CTRL_ONESHOT) {
|
||||
/* One-shot. */
|
||||
s->count = 0;
|
||||
} else {
|
||||
if ((s->control & TIMER_CTRL_PERIODIC) == 0) {
|
||||
/* Free running. */
|
||||
if (s->control & TIMER_CTRL_32BIT)
|
||||
s->count = 0xffffffff;
|
||||
else
|
||||
s->count = 0xffff;
|
||||
} else {
|
||||
/* Periodic. */
|
||||
s->count = s->limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (s->expires - now <= 0) {
|
||||
arm_timer_reload(s);
|
||||
}
|
||||
/* Update interrupts. */
|
||||
if (s->int_level && (s->control & TIMER_CTRL_IE)) {
|
||||
pic_set_irq_new(s->pic, s->irq, 1);
|
||||
} else {
|
||||
pic_set_irq_new(s->pic, s->irq, 0);
|
||||
}
|
||||
|
||||
next = now;
|
||||
if (next - s->expires < 0)
|
||||
next = s->expires;
|
||||
|
||||
/* Schedule the next timer interrupt. */
|
||||
if (next == now) {
|
||||
qemu_del_timer(s->timer);
|
||||
s->next_time = 0;
|
||||
} else if (next != s->next_time) {
|
||||
qemu_mod_timer(s->timer, next);
|
||||
s->next_time = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the current value of the timer. */
|
||||
static uint32_t arm_timer_getcount(arm_timer_state *s, int64_t now)
|
||||
{
|
||||
int64_t elapsed;
|
||||
int64_t period;
|
||||
|
||||
if (s->count == 0)
|
||||
return 0;
|
||||
if ((s->control & TIMER_CTRL_ENABLE) == 0)
|
||||
return s->count;
|
||||
elapsed = now - s->loaded;
|
||||
period = s->expires - s->loaded;
|
||||
/* If the timer should have expired then return 0. This can happen
|
||||
when the host timer signal doesnt occur immediately. It's better to
|
||||
have a timer appear to sit at zero for a while than have it wrap
|
||||
around before the guest interrupt is raised. */
|
||||
/* ??? Could we trigger the interrupt here? */
|
||||
if (elapsed > period)
|
||||
return 0;
|
||||
/* We need to calculate count * elapsed / period without overfowing.
|
||||
Scale both elapsed and period so they fit in a 32-bit int. */
|
||||
while (period != (int32_t)period) {
|
||||
period >>= 1;
|
||||
elapsed >>= 1;
|
||||
}
|
||||
return ((uint64_t)s->count * (uint64_t)(int32_t)elapsed)
|
||||
/ (int32_t)period;
|
||||
}
|
||||
|
||||
uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
arm_timer_state *s = (arm_timer_state *)opaque;
|
||||
|
||||
switch (offset >> 2) {
|
||||
case 0: /* TimerLoad */
|
||||
case 6: /* TimerBGLoad */
|
||||
return s->limit;
|
||||
case 1: /* TimerValue */
|
||||
return arm_timer_getcount(s, qemu_get_clock(vm_clock));
|
||||
case 2: /* TimerControl */
|
||||
return s->control;
|
||||
case 4: /* TimerRIS */
|
||||
return s->int_level;
|
||||
case 5: /* TimerMIS */
|
||||
if ((s->control & TIMER_CTRL_IE) == 0)
|
||||
return 0;
|
||||
return s->int_level;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void arm_timer_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
arm_timer_state *s = (arm_timer_state *)opaque;
|
||||
int64_t now;
|
||||
|
||||
now = qemu_get_clock(vm_clock);
|
||||
switch (offset >> 2) {
|
||||
case 0: /* TimerLoad */
|
||||
s->limit = value;
|
||||
s->count = value;
|
||||
s->expires = now;
|
||||
arm_timer_reload(s);
|
||||
break;
|
||||
case 1: /* TimerValue */
|
||||
/* ??? Linux seems to want to write to this readonly register.
|
||||
Ignore it. */
|
||||
break;
|
||||
case 2: /* TimerControl */
|
||||
if (s->control & TIMER_CTRL_ENABLE) {
|
||||
/* Pause the timer if it is running. This may cause some
|
||||
inaccuracy dure to rounding, but avoids a whole lot of other
|
||||
messyness. */
|
||||
s->count = arm_timer_getcount(s, now);
|
||||
}
|
||||
s->control = value;
|
||||
s->freq = s->raw_freq;
|
||||
/* ??? Need to recalculate expiry time after changing divisor. */
|
||||
switch ((value >> 2) & 3) {
|
||||
case 1: s->freq >>= 4; break;
|
||||
case 2: s->freq >>= 8; break;
|
||||
}
|
||||
if (s->control & TIMER_CTRL_ENABLE) {
|
||||
/* Restart the timer if still enabled. */
|
||||
s->expires = now;
|
||||
arm_timer_reload(s);
|
||||
}
|
||||
break;
|
||||
case 3: /* TimerIntClr */
|
||||
s->int_level = 0;
|
||||
break;
|
||||
case 6: /* TimerBGLoad */
|
||||
s->limit = value;
|
||||
break;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n", offset);
|
||||
}
|
||||
arm_timer_update(s, now);
|
||||
}
|
||||
|
||||
static void arm_timer_tick(void *opaque)
|
||||
{
|
||||
int64_t now;
|
||||
|
||||
now = qemu_get_clock(vm_clock);
|
||||
arm_timer_update((arm_timer_state *)opaque, now);
|
||||
}
|
||||
|
||||
static void *arm_timer_init(uint32_t freq, void *pic, int irq)
|
||||
{
|
||||
arm_timer_state *s;
|
||||
|
||||
s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state));
|
||||
s->pic = pic;
|
||||
s->irq = irq;
|
||||
s->raw_freq = s->freq = 1000000;
|
||||
s->control = TIMER_CTRL_IE;
|
||||
s->count = 0xffffffff;
|
||||
|
||||
s->timer = qemu_new_timer(vm_clock, arm_timer_tick, s);
|
||||
/* ??? Save/restore. */
|
||||
return s;
|
||||
}
|
||||
|
||||
/* ARM PrimeCell SP804 dual timer module.
|
||||
Docs for this device don't seem to be publicly available. This
|
||||
implementation is based on gueswork, the linux kernel sources and the
|
||||
Integrator/CP timer modules. */
|
||||
|
||||
typedef struct {
|
||||
/* Include a pseudo-PIC device to merge the two interrupt sources. */
|
||||
arm_pic_handler handler;
|
||||
void *timer[2];
|
||||
int level[2];
|
||||
uint32_t base;
|
||||
/* The output PIC device. */
|
||||
void *pic;
|
||||
int irq;
|
||||
} sp804_state;
|
||||
|
||||
static void sp804_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
sp804_state *s = (sp804_state *)opaque;
|
||||
|
||||
s->level[irq] = level;
|
||||
pic_set_irq_new(s->pic, s->irq, s->level[0] || s->level[1]);
|
||||
}
|
||||
|
||||
static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
sp804_state *s = (sp804_state *)opaque;
|
||||
|
||||
/* ??? Don't know the PrimeCell ID for this device. */
|
||||
offset -= s->base;
|
||||
if (offset < 0x20) {
|
||||
return arm_timer_read(s->timer[0], offset);
|
||||
} else {
|
||||
return arm_timer_read(s->timer[1], offset - 0x20);
|
||||
}
|
||||
}
|
||||
|
||||
static void sp804_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
sp804_state *s = (sp804_state *)opaque;
|
||||
|
||||
offset -= s->base;
|
||||
if (offset < 0x20) {
|
||||
arm_timer_write(s->timer[0], offset, value);
|
||||
} else {
|
||||
arm_timer_write(s->timer[1], offset - 0x20, value);
|
||||
}
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *sp804_readfn[] = {
|
||||
sp804_read,
|
||||
sp804_read,
|
||||
sp804_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *sp804_writefn[] = {
|
||||
sp804_write,
|
||||
sp804_write,
|
||||
sp804_write
|
||||
};
|
||||
|
||||
void sp804_init(uint32_t base, void *pic, int irq)
|
||||
{
|
||||
int iomemtype;
|
||||
sp804_state *s;
|
||||
|
||||
s = (sp804_state *)qemu_mallocz(sizeof(sp804_state));
|
||||
s->handler = sp804_set_irq;
|
||||
s->base = base;
|
||||
s->pic = pic;
|
||||
s->irq = irq;
|
||||
/* ??? The timers are actually configurable between 32kHz and 1MHz, but
|
||||
we don't implement that. */
|
||||
s->timer[0] = arm_timer_init(1000000, s, 0);
|
||||
s->timer[1] = arm_timer_init(1000000, s, 1);
|
||||
iomemtype = cpu_register_io_memory(0, sp804_readfn,
|
||||
sp804_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
|
||||
/* ??? Save/restore. */
|
||||
}
|
||||
|
||||
|
||||
/* Integrator/CP timer module. */
|
||||
|
||||
typedef struct {
|
||||
void *timer[3];
|
||||
uint32_t base;
|
||||
} icp_pit_state;
|
||||
|
||||
static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
icp_pit_state *s = (icp_pit_state *)opaque;
|
||||
int n;
|
||||
|
||||
/* ??? Don't know the PrimeCell ID for this device. */
|
||||
offset -= s->base;
|
||||
n = offset >> 8;
|
||||
if (n > 3)
|
||||
cpu_abort(cpu_single_env, "sp804_read: Bad timer %d\n", n);
|
||||
|
||||
return arm_timer_read(s->timer[n], offset & 0xff);
|
||||
}
|
||||
|
||||
static void icp_pit_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
icp_pit_state *s = (icp_pit_state *)opaque;
|
||||
int n;
|
||||
|
||||
offset -= s->base;
|
||||
n = offset >> 8;
|
||||
if (n > 3)
|
||||
cpu_abort(cpu_single_env, "sp804_write: Bad timer %d\n", n);
|
||||
|
||||
arm_timer_write(s->timer[n], offset & 0xff, value);
|
||||
}
|
||||
|
||||
|
||||
static CPUReadMemoryFunc *icp_pit_readfn[] = {
|
||||
icp_pit_read,
|
||||
icp_pit_read,
|
||||
icp_pit_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *icp_pit_writefn[] = {
|
||||
icp_pit_write,
|
||||
icp_pit_write,
|
||||
icp_pit_write
|
||||
};
|
||||
|
||||
void icp_pit_init(uint32_t base, void *pic, int irq)
|
||||
{
|
||||
int iomemtype;
|
||||
icp_pit_state *s;
|
||||
|
||||
s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
|
||||
s->base = base;
|
||||
/* Timer 0 runs at the system clock speed (40MHz). */
|
||||
s->timer[0] = arm_timer_init(40000000, pic, irq);
|
||||
/* The other two timers run at 1MHz. */
|
||||
s->timer[1] = arm_timer_init(1000000, pic, irq + 1);
|
||||
s->timer[2] = arm_timer_init(1000000, pic, irq + 2);
|
||||
|
||||
iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
|
||||
icp_pit_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
|
||||
/* ??? Save/restore. */
|
||||
}
|
||||
|
||||
3193
hw/cirrus_vga.c
Normal file
3193
hw/cirrus_vga.c
Normal file
File diff suppressed because it is too large
Load Diff
78
hw/cirrus_vga_rop.h
Normal file
78
hw/cirrus_vga_rop.h
Normal file
@@ -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
|
||||
281
hw/cirrus_vga_rop2.h
Normal file
281
hw/cirrus_vga_rop2.h
Normal file
@@ -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;
|
||||
for(y = 0; y < bltheight; y++) {
|
||||
pattern_x = skipleft;
|
||||
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
|
||||
656
hw/cuda.c
Normal file
656
hw/cuda.c
Normal file
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
/* XXX: implement all timer modes */
|
||||
|
||||
//#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 */
|
||||
#define T2_INT 0x20 /* Timer 2 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 {
|
||||
int index;
|
||||
uint16_t 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;
|
||||
|
||||
SetIRQFunc *set_irq;
|
||||
int irq;
|
||||
void *irq_opaque;
|
||||
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)) {
|
||||
s->set_irq(s->irq_opaque, s->irq, 1);
|
||||
} else {
|
||||
s->set_irq(s->irq_opaque, 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 (s->index == 0) {
|
||||
/* the timer goes down from latch to -1 (period of latch + 2) */
|
||||
if (d <= (s->counter_value + 1)) {
|
||||
counter = (s->counter_value - d) & 0xffff;
|
||||
} else {
|
||||
counter = (d - (s->counter_value + 1)) % (s->latch + 2);
|
||||
counter = (s->latch - counter) & 0xffff;
|
||||
}
|
||||
} else {
|
||||
counter = (s->counter_value - d) & 0xffff;
|
||||
}
|
||||
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;
|
||||
unsigned int counter;
|
||||
|
||||
/* current counter value */
|
||||
d = muldiv64(current_time - s->load_time,
|
||||
CUDA_TIMER_FREQ, ticks_per_sec);
|
||||
/* the timer goes down from latch to -1 (period of latch + 2) */
|
||||
if (d <= (s->counter_value + 1)) {
|
||||
counter = (s->counter_value - d) & 0xffff;
|
||||
} else {
|
||||
counter = (d - (s->counter_value + 1)) % (s->latch + 2);
|
||||
counter = (s->latch - counter) & 0xffff;
|
||||
}
|
||||
|
||||
/* Note: we consider the irq is raised on 0 */
|
||||
if (counter == 0xffff) {
|
||||
next_time = d + s->latch + 1;
|
||||
} else if (counter == 0) {
|
||||
next_time = d + s->latch + 2;
|
||||
} else {
|
||||
next_time = d + counter;
|
||||
}
|
||||
#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;
|
||||
cuda_update_irq(s);
|
||||
break;
|
||||
case 6:
|
||||
val = s->timers[0].latch & 0xff;
|
||||
break;
|
||||
case 7:
|
||||
/* XXX: check this */
|
||||
val = (s->timers[0].latch >> 8) & 0xff;
|
||||
break;
|
||||
case 8:
|
||||
val = get_counter(&s->timers[1]) & 0xff;
|
||||
s->ifr &= ~T2_INT;
|
||||
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;
|
||||
if (s->ifr & s->ier)
|
||||
val |= 0x80;
|
||||
break;
|
||||
case 14:
|
||||
val = s->ier | 0x80;
|
||||
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:
|
||||
s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
|
||||
cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
|
||||
break;
|
||||
case 5:
|
||||
s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
|
||||
s->ifr &= ~T1_INT;
|
||||
set_counter(s, &s->timers[0], s->timers[0].latch);
|
||||
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);
|
||||
s->ifr &= ~T1_INT;
|
||||
cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
|
||||
break;
|
||||
case 8:
|
||||
s->timers[1].latch = val;
|
||||
set_counter(s, &s->timers[1], val);
|
||||
break;
|
||||
case 9:
|
||||
set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch);
|
||||
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_from_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(SetIRQFunc *set_irq, void *irq_opaque, int irq)
|
||||
{
|
||||
CUDAState *s = &cuda_state;
|
||||
int cuda_mem_index;
|
||||
|
||||
s->set_irq = set_irq;
|
||||
s->irq_opaque = irq_opaque;
|
||||
s->irq = irq;
|
||||
|
||||
s->timers[0].index = 0;
|
||||
s->timers[0].timer = qemu_new_timer(vm_clock, cuda_timer1, s);
|
||||
s->timers[0].latch = 0xffff;
|
||||
set_counter(s, &s->timers[0], 0xffff);
|
||||
|
||||
s->timers[1].index = 1;
|
||||
s->timers[1].latch = 0;
|
||||
// s->ier = T1_INT | SR_INT;
|
||||
s->ier = 0;
|
||||
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;
|
||||
}
|
||||
537
hw/dma.c
Normal file
537
hw/dma.c
Normal file
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
CPUState *env = cpu_single_env;
|
||||
if (env)
|
||||
cpu_interrupt(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]);
|
||||
}
|
||||
1062
hw/es1370.c
Normal file
1062
hw/es1370.c
Normal file
File diff suppressed because it is too large
Load Diff
747
hw/esp.c
Normal file
747
hw/esp.c
Normal file
@@ -0,0 +1,747 @@
|
||||
/*
|
||||
* QEMU ESP emulation
|
||||
*
|
||||
* Copyright (c) 2005-2006 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)
|
||||
#define pic_set_irq(irq, level) \
|
||||
do { printf("ESP: set_irq(%d): %d\n", (irq), (level)); pic_set_irq((irq),(level));} while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, args...)
|
||||
#endif
|
||||
|
||||
#define ESPDMA_REGS 4
|
||||
#define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1)
|
||||
#define ESP_MAXREG 0x3f
|
||||
#define TI_BUFSZ 1024*1024 // XXX
|
||||
#define DMA_VER 0xa0000000
|
||||
#define DMA_INTR 1
|
||||
#define DMA_INTREN 0x10
|
||||
#define DMA_LOADED 0x04000000
|
||||
typedef struct ESPState ESPState;
|
||||
|
||||
typedef int ESPDMAFunc(ESPState *s,
|
||||
target_phys_addr_t phys_addr,
|
||||
int transfer_size1);
|
||||
|
||||
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;
|
||||
uint32_t ti_rptr, ti_wptr;
|
||||
int ti_dir;
|
||||
uint8_t ti_buf[TI_BUFSZ];
|
||||
int dma;
|
||||
ESPDMAFunc *dma_cb;
|
||||
int64_t offset, len;
|
||||
int target;
|
||||
};
|
||||
|
||||
#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 INTR_RST 0x80
|
||||
|
||||
#define SEQ_0 0x0
|
||||
#define SEQ_CD 0x4
|
||||
|
||||
/* XXX: stolen from ide.c, move to common ATAPI/SCSI library */
|
||||
static void lba_to_msf(uint8_t *buf, int lba)
|
||||
{
|
||||
lba += 150;
|
||||
buf[0] = (lba / 75) / 60;
|
||||
buf[1] = (lba / 75) % 60;
|
||||
buf[2] = lba % 75;
|
||||
}
|
||||
|
||||
static inline void cpu_to_ube16(uint8_t *buf, int val)
|
||||
{
|
||||
buf[0] = val >> 8;
|
||||
buf[1] = val;
|
||||
}
|
||||
|
||||
static inline void cpu_to_ube32(uint8_t *buf, unsigned int val)
|
||||
{
|
||||
buf[0] = val >> 24;
|
||||
buf[1] = val >> 16;
|
||||
buf[2] = val >> 8;
|
||||
buf[3] = val;
|
||||
}
|
||||
|
||||
/* same toc as bochs. Return -1 if error or the toc length */
|
||||
/* XXX: check this */
|
||||
static int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
|
||||
{
|
||||
uint8_t *q;
|
||||
int len;
|
||||
|
||||
if (start_track > 1 && start_track != 0xaa)
|
||||
return -1;
|
||||
q = buf + 2;
|
||||
*q++ = 1; /* first session */
|
||||
*q++ = 1; /* last session */
|
||||
if (start_track <= 1) {
|
||||
*q++ = 0; /* reserved */
|
||||
*q++ = 0x14; /* ADR, control */
|
||||
*q++ = 1; /* track number */
|
||||
*q++ = 0; /* reserved */
|
||||
if (msf) {
|
||||
*q++ = 0; /* reserved */
|
||||
lba_to_msf(q, 0);
|
||||
q += 3;
|
||||
} else {
|
||||
/* sector 0 */
|
||||
cpu_to_ube32(q, 0);
|
||||
q += 4;
|
||||
}
|
||||
}
|
||||
/* lead out track */
|
||||
*q++ = 0; /* reserved */
|
||||
*q++ = 0x16; /* ADR, control */
|
||||
*q++ = 0xaa; /* track number */
|
||||
*q++ = 0; /* reserved */
|
||||
if (msf) {
|
||||
*q++ = 0; /* reserved */
|
||||
lba_to_msf(q, nb_sectors);
|
||||
q += 3;
|
||||
} else {
|
||||
cpu_to_ube32(q, nb_sectors);
|
||||
q += 4;
|
||||
}
|
||||
len = q - buf;
|
||||
cpu_to_ube16(buf, len - 2);
|
||||
return len;
|
||||
}
|
||||
|
||||
/* mostly same info as PearPc */
|
||||
static int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf,
|
||||
int session_num)
|
||||
{
|
||||
uint8_t *q;
|
||||
int len;
|
||||
|
||||
q = buf + 2;
|
||||
*q++ = 1; /* first session */
|
||||
*q++ = 1; /* last session */
|
||||
|
||||
*q++ = 1; /* session number */
|
||||
*q++ = 0x14; /* data track */
|
||||
*q++ = 0; /* track number */
|
||||
*q++ = 0xa0; /* lead-in */
|
||||
*q++ = 0; /* min */
|
||||
*q++ = 0; /* sec */
|
||||
*q++ = 0; /* frame */
|
||||
*q++ = 0;
|
||||
*q++ = 1; /* first track */
|
||||
*q++ = 0x00; /* disk type */
|
||||
*q++ = 0x00;
|
||||
|
||||
*q++ = 1; /* session number */
|
||||
*q++ = 0x14; /* data track */
|
||||
*q++ = 0; /* track number */
|
||||
*q++ = 0xa1;
|
||||
*q++ = 0; /* min */
|
||||
*q++ = 0; /* sec */
|
||||
*q++ = 0; /* frame */
|
||||
*q++ = 0;
|
||||
*q++ = 1; /* last track */
|
||||
*q++ = 0x00;
|
||||
*q++ = 0x00;
|
||||
|
||||
*q++ = 1; /* session number */
|
||||
*q++ = 0x14; /* data track */
|
||||
*q++ = 0; /* track number */
|
||||
*q++ = 0xa2; /* lead-out */
|
||||
*q++ = 0; /* min */
|
||||
*q++ = 0; /* sec */
|
||||
*q++ = 0; /* frame */
|
||||
if (msf) {
|
||||
*q++ = 0; /* reserved */
|
||||
lba_to_msf(q, nb_sectors);
|
||||
q += 3;
|
||||
} else {
|
||||
cpu_to_ube32(q, nb_sectors);
|
||||
q += 4;
|
||||
}
|
||||
|
||||
*q++ = 1; /* session number */
|
||||
*q++ = 0x14; /* ADR, control */
|
||||
*q++ = 0; /* track number */
|
||||
*q++ = 1; /* point */
|
||||
*q++ = 0; /* min */
|
||||
*q++ = 0; /* sec */
|
||||
*q++ = 0; /* frame */
|
||||
if (msf) {
|
||||
*q++ = 0;
|
||||
lba_to_msf(q, 0);
|
||||
q += 3;
|
||||
} else {
|
||||
*q++ = 0;
|
||||
*q++ = 0;
|
||||
*q++ = 0;
|
||||
*q++ = 0;
|
||||
}
|
||||
|
||||
len = q - buf;
|
||||
cpu_to_ube16(buf, len - 2);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int esp_write_dma_cb(ESPState *s,
|
||||
target_phys_addr_t phys_addr,
|
||||
int transfer_size1)
|
||||
{
|
||||
DPRINTF("Write callback (offset %lld len %lld size %d trans_size %d)\n",
|
||||
s->offset, s->len, s->ti_size, transfer_size1);
|
||||
bdrv_write(s->bd[s->target], s->offset, s->ti_buf, s->len);
|
||||
s->offset = 0;
|
||||
s->len = 0;
|
||||
s->target = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handle_satn(ESPState *s)
|
||||
{
|
||||
uint8_t buf[32];
|
||||
uint32_t dmaptr, dmalen;
|
||||
unsigned int i;
|
||||
int64_t nb_sectors;
|
||||
int target;
|
||||
|
||||
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
||||
target = s->wregs[4] & 7;
|
||||
DPRINTF("Select with ATN len %d target %d\n", dmalen, target);
|
||||
if (s->dma) {
|
||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||
DPRINTF("DMA Direction: %c, addr 0x%8.8x\n", s->espdmaregs[0] & 0x100? 'w': 'r', dmaptr);
|
||||
cpu_physical_memory_read(dmaptr, buf, dmalen);
|
||||
} else {
|
||||
buf[0] = 0;
|
||||
memcpy(&buf[1], s->ti_buf, dmalen);
|
||||
dmalen++;
|
||||
}
|
||||
for (i = 0; i < dmalen; i++) {
|
||||
DPRINTF("Command %2.2x\n", buf[i]);
|
||||
}
|
||||
s->ti_dir = 0;
|
||||
s->ti_size = 0;
|
||||
s->ti_rptr = 0;
|
||||
s->ti_wptr = 0;
|
||||
|
||||
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] |= DMA_INTR;
|
||||
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_buf[4] = 32;
|
||||
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;
|
||||
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM)
|
||||
s->ti_buf[6] = 8; // sector size 2048
|
||||
else
|
||||
s->ti_buf[6] = 2; // sector size 512
|
||||
s->ti_buf[7] = 0;
|
||||
s->ti_dir = 1;
|
||||
s->ti_size = 8;
|
||||
break;
|
||||
case 0x28:
|
||||
{
|
||||
int64_t offset, len;
|
||||
|
||||
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) {
|
||||
offset = ((buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]) * 4;
|
||||
len = ((buf[8] << 8) | buf[9]) * 4;
|
||||
s->ti_size = len * 2048;
|
||||
} else {
|
||||
offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
|
||||
len = (buf[8] << 8) | buf[9];
|
||||
s->ti_size = len * 512;
|
||||
}
|
||||
DPRINTF("Read (10) (offset %lld len %lld)\n", offset, len);
|
||||
if (s->ti_size > TI_BUFSZ) {
|
||||
DPRINTF("size too large %d\n", s->ti_size);
|
||||
}
|
||||
bdrv_read(s->bd[target], offset, s->ti_buf, len);
|
||||
// XXX error handling
|
||||
s->ti_dir = 1;
|
||||
break;
|
||||
}
|
||||
case 0x2a:
|
||||
{
|
||||
int64_t offset, len;
|
||||
|
||||
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) {
|
||||
offset = ((buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]) * 4;
|
||||
len = ((buf[8] << 8) | buf[9]) * 4;
|
||||
s->ti_size = len * 2048;
|
||||
} else {
|
||||
offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
|
||||
len = (buf[8] << 8) | buf[9];
|
||||
s->ti_size = len * 512;
|
||||
}
|
||||
DPRINTF("Write (10) (offset %lld len %lld)\n", offset, len);
|
||||
if (s->ti_size > TI_BUFSZ) {
|
||||
DPRINTF("size too large %d\n", s->ti_size);
|
||||
}
|
||||
s->dma_cb = esp_write_dma_cb;
|
||||
s->offset = offset;
|
||||
s->len = len;
|
||||
s->target = target;
|
||||
// XXX error handling
|
||||
s->ti_dir = 0;
|
||||
break;
|
||||
}
|
||||
case 0x43:
|
||||
{
|
||||
int start_track, format, msf, len;
|
||||
|
||||
msf = buf[2] & 2;
|
||||
format = buf[3] & 0xf;
|
||||
start_track = buf[7];
|
||||
bdrv_get_geometry(s->bd[target], &nb_sectors);
|
||||
DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
|
||||
switch(format) {
|
||||
case 0:
|
||||
len = cdrom_read_toc(nb_sectors, buf, msf, start_track);
|
||||
if (len < 0)
|
||||
goto error_cmd;
|
||||
s->ti_size = len;
|
||||
break;
|
||||
case 1:
|
||||
/* multi session : only a single session defined */
|
||||
memset(buf, 0, 12);
|
||||
buf[1] = 0x0a;
|
||||
buf[2] = 0x01;
|
||||
buf[3] = 0x01;
|
||||
s->ti_size = 12;
|
||||
break;
|
||||
case 2:
|
||||
len = cdrom_read_toc_raw(nb_sectors, buf, msf, start_track);
|
||||
if (len < 0)
|
||||
goto error_cmd;
|
||||
s->ti_size = len;
|
||||
break;
|
||||
default:
|
||||
error_cmd:
|
||||
DPRINTF("Read TOC error\n");
|
||||
// XXX error handling
|
||||
break;
|
||||
}
|
||||
s->ti_dir = 1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DPRINTF("Unknown SCSI 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] |= DMA_INTR;
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
|
||||
static void dma_write(ESPState *s, const uint8_t *buf, uint32_t len)
|
||||
{
|
||||
uint32_t dmaptr, dmalen;
|
||||
|
||||
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
||||
DPRINTF("Transfer status len %d\n", dmalen);
|
||||
if (s->dma) {
|
||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||
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;
|
||||
} else {
|
||||
memcpy(s->ti_buf, buf, len);
|
||||
s->ti_size = dmalen;
|
||||
s->ti_rptr = 0;
|
||||
s->ti_wptr = 0;
|
||||
s->rregs[7] = dmalen;
|
||||
}
|
||||
s->espdmaregs[0] |= DMA_INTR;
|
||||
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;
|
||||
|
||||
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
||||
DPRINTF("Transfer Information len %d\n", dmalen);
|
||||
if (s->dma) {
|
||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||
DPRINTF("DMA Direction: %c, addr 0x%8.8x\n", s->espdmaregs[0] & 0x100? 'w': 'r', dmaptr);
|
||||
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);
|
||||
}
|
||||
if (s->dma_cb) {
|
||||
s->dma_cb(s, s->espdmaregs[1], dmalen);
|
||||
s->dma_cb = NULL;
|
||||
}
|
||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
|
||||
s->rregs[5] = INTR_BS;
|
||||
s->rregs[6] = 0;
|
||||
s->espdmaregs[0] |= DMA_INTR;
|
||||
} else {
|
||||
s->ti_size = dmalen;
|
||||
s->ti_rptr = 0;
|
||||
s->ti_wptr = 0;
|
||||
s->rregs[7] = dmalen;
|
||||
}
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
|
||||
static void esp_reset(void *opaque)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
memset(s->rregs, 0, ESP_MAXREG);
|
||||
memset(s->wregs, 0, ESP_MAXREG);
|
||||
s->rregs[0x0e] = 0x4; // Indicate fas100a
|
||||
memset(s->espdmaregs, 0, ESPDMA_REGS * 4);
|
||||
s->ti_size = 0;
|
||||
s->ti_rptr = 0;
|
||||
s->ti_wptr = 0;
|
||||
s->ti_dir = 0;
|
||||
s->dma = 0;
|
||||
s->dma_cb = NULL;
|
||||
}
|
||||
|
||||
static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr & ESP_MAXREG) >> 2;
|
||||
DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]);
|
||||
switch (saddr) {
|
||||
case 2:
|
||||
// FIFO
|
||||
if (s->ti_size > 0) {
|
||||
s->ti_size--;
|
||||
s->rregs[saddr] = s->ti_buf[s->ti_rptr++];
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
if (s->ti_size == 0) {
|
||||
s->ti_rptr = 0;
|
||||
s->ti_wptr = 0;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
// interrupt
|
||||
// Clear status bits except TC
|
||||
s->rregs[4] &= STAT_TC;
|
||||
pic_set_irq(s->irq, 0);
|
||||
s->espdmaregs[0] &= ~DMA_INTR;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
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 0:
|
||||
case 1:
|
||||
s->rregs[saddr] = val;
|
||||
break;
|
||||
case 2:
|
||||
// FIFO
|
||||
s->ti_size++;
|
||||
s->ti_buf[s->ti_wptr++] = val & 0xff;
|
||||
break;
|
||||
case 3:
|
||||
s->rregs[saddr] = val;
|
||||
// Command
|
||||
if (val & 0x80) {
|
||||
s->dma = 1;
|
||||
} else {
|
||||
s->dma = 0;
|
||||
}
|
||||
switch(val & 0x7f) {
|
||||
case 0:
|
||||
DPRINTF("NOP (%2.2x)\n", val);
|
||||
break;
|
||||
case 1:
|
||||
DPRINTF("Flush FIFO (%2.2x)\n", val);
|
||||
//s->ti_size = 0;
|
||||
s->rregs[5] = INTR_FC;
|
||||
s->rregs[6] = 0;
|
||||
break;
|
||||
case 2:
|
||||
DPRINTF("Chip reset (%2.2x)\n", val);
|
||||
esp_reset(s);
|
||||
break;
|
||||
case 3:
|
||||
DPRINTF("Bus reset (%2.2x)\n", val);
|
||||
s->rregs[5] = INTR_RST;
|
||||
if (!(s->wregs[8] & 0x40)) {
|
||||
s->espdmaregs[0] |= DMA_INTR;
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
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 ESP command (%2.2x)\n", val);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4 ... 7:
|
||||
break;
|
||||
case 8:
|
||||
s->rregs[saddr] = val;
|
||||
break;
|
||||
case 9 ... 10:
|
||||
break;
|
||||
case 11:
|
||||
s->rregs[saddr] = val & 0x15;
|
||||
break;
|
||||
case 12 ... 15:
|
||||
s->rregs[saddr] = val;
|
||||
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%8.8x\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%8.8x -> 0x%8.8x\n", saddr, s->espdmaregs[saddr], val);
|
||||
switch (saddr) {
|
||||
case 0:
|
||||
if (!(val & DMA_INTREN))
|
||||
pic_set_irq(s->irq, 0);
|
||||
if (val & 0x80) {
|
||||
esp_reset(s);
|
||||
} else if (val & 0x40) {
|
||||
val &= ~0x40;
|
||||
} else if (val == 0)
|
||||
val = 0x40;
|
||||
val &= 0x0fffffff;
|
||||
val |= DMA_VER;
|
||||
break;
|
||||
case 1:
|
||||
s->espdmaregs[0] = DMA_LOADED;
|
||||
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]);
|
||||
qemu_put_be32s(f, &s->ti_size);
|
||||
qemu_put_be32s(f, &s->ti_rptr);
|
||||
qemu_put_be32s(f, &s->ti_wptr);
|
||||
qemu_put_be32s(f, &s->ti_dir);
|
||||
qemu_put_buffer(f, s->ti_buf, TI_BUFSZ);
|
||||
qemu_put_be32s(f, &s->dma);
|
||||
}
|
||||
|
||||
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]);
|
||||
qemu_get_be32s(f, &s->ti_size);
|
||||
qemu_get_be32s(f, &s->ti_rptr);
|
||||
qemu_get_be32s(f, &s->ti_wptr);
|
||||
qemu_get_be32s(f, &s->ti_dir);
|
||||
qemu_get_buffer(f, s->ti_buf, TI_BUFSZ);
|
||||
qemu_get_be32s(f, &s->dma);
|
||||
|
||||
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
hw/fmopl.c
Normal file
1390
hw/fmopl.c
Normal file
File diff suppressed because it is too large
Load Diff
174
hw/fmopl.h
Normal file
174
hw/fmopl.h
Normal file
@@ -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
|
||||
168
hw/heathrow_pic.c
Normal file
168
hw/heathrow_pic.c
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Heathrow PIC support (standard PowerMac PIC)
|
||||
*
|
||||
* 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"
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
typedef struct HeathrowPIC {
|
||||
uint32_t events;
|
||||
uint32_t mask;
|
||||
uint32_t levels;
|
||||
uint32_t level_triggered;
|
||||
} HeathrowPIC;
|
||||
|
||||
struct HeathrowPICS {
|
||||
HeathrowPIC pics[2];
|
||||
};
|
||||
|
||||
static inline int check_irq(HeathrowPIC *pic)
|
||||
{
|
||||
return (pic->events | (pic->levels & pic->level_triggered)) & pic->mask;
|
||||
}
|
||||
|
||||
/* update the CPU irq state */
|
||||
static void heathrow_pic_update(HeathrowPICS *s)
|
||||
{
|
||||
if (check_irq(&s->pics[0]) || check_irq(&s->pics[1])) {
|
||||
cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
static void pic_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
{
|
||||
HeathrowPICS *s = opaque;
|
||||
HeathrowPIC *pic;
|
||||
unsigned int n;
|
||||
|
||||
value = bswap32(value);
|
||||
#ifdef DEBUG
|
||||
printf("pic_writel: %08x: %08x\n",
|
||||
addr, value);
|
||||
#endif
|
||||
n = ((addr & 0xfff) - 0x10) >> 4;
|
||||
if (n >= 2)
|
||||
return;
|
||||
pic = &s->pics[n];
|
||||
switch(addr & 0xf) {
|
||||
case 0x04:
|
||||
pic->mask = value;
|
||||
heathrow_pic_update(s);
|
||||
break;
|
||||
case 0x08:
|
||||
/* do not reset level triggered IRQs */
|
||||
value &= ~pic->level_triggered;
|
||||
pic->events &= ~value;
|
||||
heathrow_pic_update(s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t pic_readl (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
HeathrowPICS *s = opaque;
|
||||
HeathrowPIC *pic;
|
||||
unsigned int n;
|
||||
uint32_t value;
|
||||
|
||||
n = ((addr & 0xfff) - 0x10) >> 4;
|
||||
if (n >= 2) {
|
||||
value = 0;
|
||||
} else {
|
||||
pic = &s->pics[n];
|
||||
switch(addr & 0xf) {
|
||||
case 0x0:
|
||||
value = pic->events;
|
||||
break;
|
||||
case 0x4:
|
||||
value = pic->mask;
|
||||
break;
|
||||
case 0xc:
|
||||
value = pic->levels;
|
||||
break;
|
||||
default:
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("pic_readl: %08x: %08x\n",
|
||||
addr, value);
|
||||
#endif
|
||||
value = bswap32(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc *pic_write[] = {
|
||||
&pic_writel,
|
||||
&pic_writel,
|
||||
&pic_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *pic_read[] = {
|
||||
&pic_readl,
|
||||
&pic_readl,
|
||||
&pic_readl,
|
||||
};
|
||||
|
||||
|
||||
void heathrow_pic_set_irq(void *opaque, int num, int level)
|
||||
{
|
||||
HeathrowPICS *s = opaque;
|
||||
HeathrowPIC *pic;
|
||||
unsigned int irq_bit;
|
||||
|
||||
#if defined(DEBUG)
|
||||
{
|
||||
static int last_level[64];
|
||||
if (last_level[num] != level) {
|
||||
printf("set_irq: num=0x%02x level=%d\n", num, level);
|
||||
last_level[num] = level;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
pic = &s->pics[1 - (num >> 5)];
|
||||
irq_bit = 1 << (num & 0x1f);
|
||||
if (level) {
|
||||
pic->events |= irq_bit & ~pic->level_triggered;
|
||||
pic->levels |= irq_bit;
|
||||
} else {
|
||||
pic->levels &= ~irq_bit;
|
||||
}
|
||||
heathrow_pic_update(s);
|
||||
}
|
||||
|
||||
HeathrowPICS *heathrow_pic_init(int *pmem_index)
|
||||
{
|
||||
HeathrowPICS *s;
|
||||
|
||||
s = qemu_mallocz(sizeof(HeathrowPICS));
|
||||
s->pics[0].level_triggered = 0;
|
||||
s->pics[1].level_triggered = 0x1ff00000;
|
||||
*pmem_index = cpu_register_io_memory(0, pic_read, pic_write, s);
|
||||
return s;
|
||||
}
|
||||
482
hw/i8254.c
Normal file
482
hw/i8254.c
Normal file
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
int pit_get_initial_count(PITState *pit, int channel)
|
||||
{
|
||||
PITChannelState *s = &pit->channels[channel];
|
||||
return s->count;
|
||||
}
|
||||
|
||||
int pit_get_mode(PITState *pit, int channel)
|
||||
{
|
||||
PITChannelState *s = &pit->channels[channel];
|
||||
return s->mode;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
561
hw/i8259.c
Normal file
561
hw/i8259.c
Normal file
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* 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;
|
||||
PicState2 *pics_state;
|
||||
} PicState;
|
||||
|
||||
struct PicState2 {
|
||||
/* 0 is master pic, 1 is slave pic */
|
||||
/* XXX: better separation between the two pics */
|
||||
PicState pics[2];
|
||||
IRQRequestFunc *irq_request;
|
||||
void *irq_request_opaque;
|
||||
/* IOAPIC callback support */
|
||||
SetIRQFunc *alt_irq_func;
|
||||
void *alt_irq_opaque;
|
||||
};
|
||||
|
||||
#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 == &s->pics_state->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 */
|
||||
/* XXX: should not export it, but it is needed for an APIC kludge */
|
||||
void pic_update_irq(PicState2 *s)
|
||||
{
|
||||
int irq2, irq;
|
||||
|
||||
/* first look at slave pic */
|
||||
irq2 = pic_get_irq(&s->pics[1]);
|
||||
if (irq2 >= 0) {
|
||||
/* if irq request by slave pic, signal master PIC */
|
||||
pic_set_irq1(&s->pics[0], 2, 1);
|
||||
pic_set_irq1(&s->pics[0], 2, 0);
|
||||
}
|
||||
/* look at requested irq */
|
||||
irq = pic_get_irq(&s->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, s->pics[i].imr, s->pics[i].irr,
|
||||
s->pics[i].priority_add);
|
||||
|
||||
}
|
||||
}
|
||||
printf("pic: cpu_interrupt\n");
|
||||
#endif
|
||||
s->irq_request(s->irq_request_opaque, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_IRQ_LATENCY
|
||||
int64_t irq_time[16];
|
||||
#endif
|
||||
|
||||
void pic_set_irq_new(void *opaque, int irq, int level)
|
||||
{
|
||||
PicState2 *s = opaque;
|
||||
|
||||
#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(&s->pics[irq >> 3], irq & 7, level);
|
||||
/* used for IOAPIC irqs */
|
||||
if (s->alt_irq_func)
|
||||
s->alt_irq_func(s->alt_irq_opaque, irq, level);
|
||||
pic_update_irq(s);
|
||||
}
|
||||
|
||||
/* obsolete function */
|
||||
void pic_set_irq(int irq, int level)
|
||||
{
|
||||
pic_set_irq_new(isa_pic, irq, level);
|
||||
}
|
||||
|
||||
/* 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 pic_read_irq(PicState2 *s)
|
||||
{
|
||||
int irq, irq2, intno;
|
||||
|
||||
irq = pic_get_irq(&s->pics[0]);
|
||||
if (irq >= 0) {
|
||||
pic_intack(&s->pics[0], irq);
|
||||
if (irq == 2) {
|
||||
irq2 = pic_get_irq(&s->pics[1]);
|
||||
if (irq2 >= 0) {
|
||||
pic_intack(&s->pics[1], irq2);
|
||||
} else {
|
||||
/* spurious IRQ on slave controller */
|
||||
irq2 = 7;
|
||||
}
|
||||
intno = s->pics[1].irq_base + irq2;
|
||||
irq = irq2 + 8;
|
||||
} else {
|
||||
intno = s->pics[0].irq_base + irq;
|
||||
}
|
||||
} else {
|
||||
/* spurious IRQ on host controller */
|
||||
irq = 7;
|
||||
intno = s->pics[0].irq_base + irq;
|
||||
}
|
||||
pic_update_irq(s);
|
||||
|
||||
#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;
|
||||
|
||||
s->last_irr = 0;
|
||||
s->irr = 0;
|
||||
s->imr = 0;
|
||||
s->isr = 0;
|
||||
s->priority_add = 0;
|
||||
s->irq_base = 0;
|
||||
s->read_reg_select = 0;
|
||||
s->poll = 0;
|
||||
s->special_mask = 0;
|
||||
s->init_state = 0;
|
||||
s->auto_eoi = 0;
|
||||
s->rotate_on_auto_eoi = 0;
|
||||
s->special_fully_nested_mode = 0;
|
||||
s->init4 = 0;
|
||||
/* Note: ELCR is not reset */
|
||||
}
|
||||
|
||||
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 */
|
||||
s->pics_state->irq_request(s->pics_state->irq_request_opaque, 0);
|
||||
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(s->pics_state);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
irq = val & 7;
|
||||
s->isr &= ~(1 << irq);
|
||||
pic_update_irq(s->pics_state);
|
||||
break;
|
||||
case 6:
|
||||
s->priority_add = (val + 1) & 7;
|
||||
pic_update_irq(s->pics_state);
|
||||
break;
|
||||
case 7:
|
||||
irq = val & 7;
|
||||
s->isr &= ~(1 << irq);
|
||||
s->priority_add = (irq + 1) & 7;
|
||||
pic_update_irq(s->pics_state);
|
||||
break;
|
||||
default:
|
||||
/* no operation */
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch(s->init_state) {
|
||||
case 0:
|
||||
/* normal mode */
|
||||
s->imr = val;
|
||||
pic_update_irq(s->pics_state);
|
||||
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) {
|
||||
s->pics_state->pics[0].isr &= ~(1 << 2);
|
||||
s->pics_state->pics[0].irr &= ~(1 << 2);
|
||||
}
|
||||
s->irr &= ~(1 << ret);
|
||||
s->isr &= ~(1 << ret);
|
||||
if (addr1 >> 7 || ret != 2)
|
||||
pic_update_irq(s->pics_state);
|
||||
} else {
|
||||
ret = 0x07;
|
||||
pic_update_irq(s->pics_state);
|
||||
}
|
||||
|
||||
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 */
|
||||
/* XXX: may be the same than pic_read_irq() */
|
||||
uint32_t pic_intack_read(PicState2 *s)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pic_poll_read(&s->pics[0], 0x00);
|
||||
if (ret == 2)
|
||||
ret = pic_poll_read(&s->pics[1], 0x80) + 8;
|
||||
/* Prepare for ISR read */
|
||||
s->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;
|
||||
|
||||
if (!isa_pic)
|
||||
return;
|
||||
|
||||
for(i=0;i<2;i++) {
|
||||
s = &isa_pic->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
|
||||
}
|
||||
|
||||
PicState2 *pic_init(IRQRequestFunc *irq_request, void *irq_request_opaque)
|
||||
{
|
||||
PicState2 *s;
|
||||
s = qemu_mallocz(sizeof(PicState2));
|
||||
if (!s)
|
||||
return NULL;
|
||||
pic_init1(0x20, 0x4d0, &s->pics[0]);
|
||||
pic_init1(0xa0, 0x4d1, &s->pics[1]);
|
||||
s->pics[0].elcr_mask = 0xf8;
|
||||
s->pics[1].elcr_mask = 0xde;
|
||||
s->irq_request = irq_request;
|
||||
s->irq_request_opaque = irq_request_opaque;
|
||||
s->pics[0].pics_state = s;
|
||||
s->pics[1].pics_state = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func,
|
||||
void *alt_irq_opaque)
|
||||
{
|
||||
s->alt_irq_func = alt_irq_func;
|
||||
s->alt_irq_opaque = alt_irq_opaque;
|
||||
}
|
||||
546
hw/integratorcp.c
Normal file
546
hw/integratorcp.c
Normal file
@@ -0,0 +1,546 @@
|
||||
/*
|
||||
* ARM Integrator CP System emulation.
|
||||
*
|
||||
* Copyright (c) 2005-2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
#include "arm_pic.h"
|
||||
|
||||
void DMA_run (void)
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t flash_offset;
|
||||
uint32_t cm_osc;
|
||||
uint32_t cm_ctrl;
|
||||
uint32_t cm_lock;
|
||||
uint32_t cm_auxosc;
|
||||
uint32_t cm_sdram;
|
||||
uint32_t cm_init;
|
||||
uint32_t cm_flags;
|
||||
uint32_t cm_nvflags;
|
||||
uint32_t int_level;
|
||||
uint32_t irq_enabled;
|
||||
uint32_t fiq_enabled;
|
||||
} integratorcm_state;
|
||||
|
||||
static uint8_t integrator_spd[128] = {
|
||||
128, 8, 4, 11, 9, 1, 64, 0, 2, 0xa0, 0xa0, 0, 0, 8, 0, 1,
|
||||
0xe, 4, 0x1c, 1, 2, 0x20, 0xc0, 0, 0, 0, 0, 0x30, 0x28, 0x30, 0x28, 0x40
|
||||
};
|
||||
|
||||
static uint32_t integratorcm_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
integratorcm_state *s = (integratorcm_state *)opaque;
|
||||
offset -= 0x10000000;
|
||||
if (offset >= 0x100 && offset < 0x200) {
|
||||
/* CM_SPD */
|
||||
if (offset >= 0x180)
|
||||
return 0;
|
||||
return integrator_spd[offset >> 2];
|
||||
}
|
||||
switch (offset >> 2) {
|
||||
case 0: /* CM_ID */
|
||||
return 0x411a3001;
|
||||
case 1: /* CM_PROC */
|
||||
return 0;
|
||||
case 2: /* CM_OSC */
|
||||
return s->cm_osc;
|
||||
case 3: /* CM_CTRL */
|
||||
return s->cm_ctrl;
|
||||
case 4: /* CM_STAT */
|
||||
return 0x00100000;
|
||||
case 5: /* CM_LOCK */
|
||||
if (s->cm_lock == 0xa05f) {
|
||||
return 0x1a05f;
|
||||
} else {
|
||||
return s->cm_lock;
|
||||
}
|
||||
case 6: /* CM_LMBUSCNT */
|
||||
/* ??? High frequency timer. */
|
||||
cpu_abort(cpu_single_env, "integratorcm_read: CM_LMBUSCNT");
|
||||
case 7: /* CM_AUXOSC */
|
||||
return s->cm_auxosc;
|
||||
case 8: /* CM_SDRAM */
|
||||
return s->cm_sdram;
|
||||
case 9: /* CM_INIT */
|
||||
return s->cm_init;
|
||||
case 10: /* CM_REFCT */
|
||||
/* ??? High frequency timer. */
|
||||
cpu_abort(cpu_single_env, "integratorcm_read: CM_REFCT");
|
||||
case 12: /* CM_FLAGS */
|
||||
return s->cm_flags;
|
||||
case 14: /* CM_NVFLAGS */
|
||||
return s->cm_nvflags;
|
||||
case 16: /* CM_IRQ_STAT */
|
||||
return s->int_level & s->irq_enabled;
|
||||
case 17: /* CM_IRQ_RSTAT */
|
||||
return s->int_level;
|
||||
case 18: /* CM_IRQ_ENSET */
|
||||
return s->irq_enabled;
|
||||
case 20: /* CM_SOFT_INTSET */
|
||||
return s->int_level & 1;
|
||||
case 24: /* CM_FIQ_STAT */
|
||||
return s->int_level & s->fiq_enabled;
|
||||
case 25: /* CM_FIQ_RSTAT */
|
||||
return s->int_level;
|
||||
case 26: /* CM_FIQ_ENSET */
|
||||
return s->fiq_enabled;
|
||||
case 32: /* CM_VOLTAGE_CTL0 */
|
||||
case 33: /* CM_VOLTAGE_CTL1 */
|
||||
case 34: /* CM_VOLTAGE_CTL2 */
|
||||
case 35: /* CM_VOLTAGE_CTL3 */
|
||||
/* ??? Voltage control unimplemented. */
|
||||
return 0;
|
||||
default:
|
||||
cpu_abort (cpu_single_env,
|
||||
"integratorcm_read: Unimplemented offset 0x%x\n", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void integratorcm_do_remap(integratorcm_state *s, int flash)
|
||||
{
|
||||
if (flash) {
|
||||
cpu_register_physical_memory(0, 0x100000, IO_MEM_RAM);
|
||||
} else {
|
||||
cpu_register_physical_memory(0, 0x100000, s->flash_offset | IO_MEM_RAM);
|
||||
}
|
||||
//??? tlb_flush (cpu_single_env, 1);
|
||||
}
|
||||
|
||||
static void integratorcm_set_ctrl(integratorcm_state *s, uint32_t value)
|
||||
{
|
||||
if (value & 8) {
|
||||
cpu_abort(cpu_single_env, "Board reset\n");
|
||||
}
|
||||
if ((s->cm_init ^ value) & 4) {
|
||||
integratorcm_do_remap(s, (value & 4) == 0);
|
||||
}
|
||||
if ((s->cm_init ^ value) & 1) {
|
||||
printf("Green LED %s\n", (value & 1) ? "on" : "off");
|
||||
}
|
||||
s->cm_init = (s->cm_init & ~ 5) | (value ^ 5);
|
||||
}
|
||||
|
||||
static void integratorcm_update(integratorcm_state *s)
|
||||
{
|
||||
/* ??? The CPU irq/fiq is raised when either the core module or base PIC
|
||||
are active. */
|
||||
if (s->int_level & (s->irq_enabled | s->fiq_enabled))
|
||||
cpu_abort(cpu_single_env, "Core module interrupt\n");
|
||||
}
|
||||
|
||||
static void integratorcm_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
integratorcm_state *s = (integratorcm_state *)opaque;
|
||||
offset -= 0x10000000;
|
||||
switch (offset >> 2) {
|
||||
case 2: /* CM_OSC */
|
||||
if (s->cm_lock == 0xa05f)
|
||||
s->cm_osc = value;
|
||||
break;
|
||||
case 3: /* CM_CTRL */
|
||||
integratorcm_set_ctrl(s, value);
|
||||
break;
|
||||
case 5: /* CM_LOCK */
|
||||
s->cm_lock = value & 0xffff;
|
||||
break;
|
||||
case 7: /* CM_AUXOSC */
|
||||
if (s->cm_lock == 0xa05f)
|
||||
s->cm_auxosc = value;
|
||||
break;
|
||||
case 8: /* CM_SDRAM */
|
||||
s->cm_sdram = value;
|
||||
break;
|
||||
case 9: /* CM_INIT */
|
||||
/* ??? This can change the memory bus frequency. */
|
||||
s->cm_init = value;
|
||||
break;
|
||||
case 12: /* CM_FLAGSS */
|
||||
s->cm_flags |= value;
|
||||
break;
|
||||
case 13: /* CM_FLAGSC */
|
||||
s->cm_flags &= ~value;
|
||||
break;
|
||||
case 14: /* CM_NVFLAGSS */
|
||||
s->cm_nvflags |= value;
|
||||
break;
|
||||
case 15: /* CM_NVFLAGSS */
|
||||
s->cm_nvflags &= ~value;
|
||||
break;
|
||||
case 18: /* CM_IRQ_ENSET */
|
||||
s->irq_enabled |= value;
|
||||
integratorcm_update(s);
|
||||
break;
|
||||
case 19: /* CM_IRQ_ENCLR */
|
||||
s->irq_enabled &= ~value;
|
||||
integratorcm_update(s);
|
||||
break;
|
||||
case 20: /* CM_SOFT_INTSET */
|
||||
s->int_level |= (value & 1);
|
||||
integratorcm_update(s);
|
||||
break;
|
||||
case 21: /* CM_SOFT_INTCLR */
|
||||
s->int_level &= ~(value & 1);
|
||||
integratorcm_update(s);
|
||||
break;
|
||||
case 26: /* CM_FIQ_ENSET */
|
||||
s->fiq_enabled |= value;
|
||||
integratorcm_update(s);
|
||||
break;
|
||||
case 27: /* CM_FIQ_ENCLR */
|
||||
s->fiq_enabled &= ~value;
|
||||
integratorcm_update(s);
|
||||
break;
|
||||
case 32: /* CM_VOLTAGE_CTL0 */
|
||||
case 33: /* CM_VOLTAGE_CTL1 */
|
||||
case 34: /* CM_VOLTAGE_CTL2 */
|
||||
case 35: /* CM_VOLTAGE_CTL3 */
|
||||
/* ??? Voltage control unimplemented. */
|
||||
break;
|
||||
default:
|
||||
cpu_abort (cpu_single_env,
|
||||
"integratorcm_write: Unimplemented offset 0x%x\n", offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Integrator/CM control registers. */
|
||||
|
||||
static CPUReadMemoryFunc *integratorcm_readfn[] = {
|
||||
integratorcm_read,
|
||||
integratorcm_read,
|
||||
integratorcm_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *integratorcm_writefn[] = {
|
||||
integratorcm_write,
|
||||
integratorcm_write,
|
||||
integratorcm_write
|
||||
};
|
||||
|
||||
static void integratorcm_init(int memsz, uint32_t flash_offset)
|
||||
{
|
||||
int iomemtype;
|
||||
integratorcm_state *s;
|
||||
|
||||
s = (integratorcm_state *)qemu_mallocz(sizeof(integratorcm_state));
|
||||
s->cm_osc = 0x01000048;
|
||||
/* ??? What should the high bits of this value be? */
|
||||
s->cm_auxosc = 0x0007feff;
|
||||
s->cm_sdram = 0x00011122;
|
||||
if (memsz >= 256) {
|
||||
integrator_spd[31] = 64;
|
||||
s->cm_sdram |= 0x10;
|
||||
} else if (memsz >= 128) {
|
||||
integrator_spd[31] = 32;
|
||||
s->cm_sdram |= 0x0c;
|
||||
} else if (memsz >= 64) {
|
||||
integrator_spd[31] = 16;
|
||||
s->cm_sdram |= 0x08;
|
||||
} else if (memsz >= 32) {
|
||||
integrator_spd[31] = 4;
|
||||
s->cm_sdram |= 0x04;
|
||||
} else {
|
||||
integrator_spd[31] = 2;
|
||||
}
|
||||
memcpy(integrator_spd + 73, "QEMU-MEMORY", 11);
|
||||
s->cm_init = 0x00000112;
|
||||
s->flash_offset = flash_offset;
|
||||
|
||||
iomemtype = cpu_register_io_memory(0, integratorcm_readfn,
|
||||
integratorcm_writefn, s);
|
||||
cpu_register_physical_memory(0x10000000, 0x007fffff, iomemtype);
|
||||
integratorcm_do_remap(s, 1);
|
||||
/* ??? Save/restore. */
|
||||
}
|
||||
|
||||
/* Integrator/CP hardware emulation. */
|
||||
/* Primary interrupt controller. */
|
||||
|
||||
typedef struct icp_pic_state
|
||||
{
|
||||
arm_pic_handler handler;
|
||||
uint32_t base;
|
||||
uint32_t level;
|
||||
uint32_t irq_enabled;
|
||||
uint32_t fiq_enabled;
|
||||
void *parent;
|
||||
int parent_irq;
|
||||
int parent_fiq;
|
||||
} icp_pic_state;
|
||||
|
||||
static void icp_pic_update(icp_pic_state *s)
|
||||
{
|
||||
uint32_t flags;
|
||||
|
||||
if (s->parent_irq != -1) {
|
||||
flags = (s->level & s->irq_enabled);
|
||||
pic_set_irq_new(s->parent, s->parent_irq, flags != 0);
|
||||
}
|
||||
if (s->parent_fiq != -1) {
|
||||
flags = (s->level & s->fiq_enabled);
|
||||
pic_set_irq_new(s->parent, s->parent_fiq, flags != 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void icp_pic_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
icp_pic_state *s = (icp_pic_state *)opaque;
|
||||
if (level)
|
||||
s->level |= 1 << irq;
|
||||
else
|
||||
s->level &= ~(1 << irq);
|
||||
icp_pic_update(s);
|
||||
}
|
||||
|
||||
static uint32_t icp_pic_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
icp_pic_state *s = (icp_pic_state *)opaque;
|
||||
|
||||
offset -= s->base;
|
||||
switch (offset >> 2) {
|
||||
case 0: /* IRQ_STATUS */
|
||||
return s->level & s->irq_enabled;
|
||||
case 1: /* IRQ_RAWSTAT */
|
||||
return s->level;
|
||||
case 2: /* IRQ_ENABLESET */
|
||||
return s->irq_enabled;
|
||||
case 4: /* INT_SOFTSET */
|
||||
return s->level & 1;
|
||||
case 8: /* FRQ_STATUS */
|
||||
return s->level & s->fiq_enabled;
|
||||
case 9: /* FRQ_RAWSTAT */
|
||||
return s->level;
|
||||
case 10: /* FRQ_ENABLESET */
|
||||
return s->fiq_enabled;
|
||||
case 3: /* IRQ_ENABLECLR */
|
||||
case 5: /* INT_SOFTCLR */
|
||||
case 11: /* FRQ_ENABLECLR */
|
||||
default:
|
||||
printf ("icp_pic_read: Bad register offset 0x%x\n", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void icp_pic_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
icp_pic_state *s = (icp_pic_state *)opaque;
|
||||
offset -= s->base;
|
||||
|
||||
switch (offset >> 2) {
|
||||
case 2: /* IRQ_ENABLESET */
|
||||
s->irq_enabled |= value;
|
||||
break;
|
||||
case 3: /* IRQ_ENABLECLR */
|
||||
s->irq_enabled &= ~value;
|
||||
break;
|
||||
case 4: /* INT_SOFTSET */
|
||||
if (value & 1)
|
||||
pic_set_irq_new(s, 0, 1);
|
||||
break;
|
||||
case 5: /* INT_SOFTCLR */
|
||||
if (value & 1)
|
||||
pic_set_irq_new(s, 0, 0);
|
||||
break;
|
||||
case 10: /* FRQ_ENABLESET */
|
||||
s->fiq_enabled |= value;
|
||||
break;
|
||||
case 11: /* FRQ_ENABLECLR */
|
||||
s->fiq_enabled &= ~value;
|
||||
break;
|
||||
case 0: /* IRQ_STATUS */
|
||||
case 1: /* IRQ_RAWSTAT */
|
||||
case 8: /* FRQ_STATUS */
|
||||
case 9: /* FRQ_RAWSTAT */
|
||||
default:
|
||||
printf ("icp_pic_write: Bad register offset 0x%x\n", offset);
|
||||
return;
|
||||
}
|
||||
icp_pic_update(s);
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *icp_pic_readfn[] = {
|
||||
icp_pic_read,
|
||||
icp_pic_read,
|
||||
icp_pic_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *icp_pic_writefn[] = {
|
||||
icp_pic_write,
|
||||
icp_pic_write,
|
||||
icp_pic_write
|
||||
};
|
||||
|
||||
static icp_pic_state *icp_pic_init(uint32_t base, void *parent,
|
||||
int parent_irq, int parent_fiq)
|
||||
{
|
||||
icp_pic_state *s;
|
||||
int iomemtype;
|
||||
|
||||
s = (icp_pic_state *)qemu_mallocz(sizeof(icp_pic_state));
|
||||
if (!s)
|
||||
return NULL;
|
||||
s->handler = icp_pic_set_irq;
|
||||
s->base = base;
|
||||
s->parent = parent;
|
||||
s->parent_irq = parent_irq;
|
||||
s->parent_fiq = parent_fiq;
|
||||
iomemtype = cpu_register_io_memory(0, icp_pic_readfn,
|
||||
icp_pic_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x007fffff, iomemtype);
|
||||
/* ??? Save/restore. */
|
||||
return s;
|
||||
}
|
||||
|
||||
/* CP control registers. */
|
||||
typedef struct {
|
||||
uint32_t base;
|
||||
} icp_control_state;
|
||||
|
||||
static uint32_t icp_control_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
icp_control_state *s = (icp_control_state *)opaque;
|
||||
offset -= s->base;
|
||||
switch (offset >> 2) {
|
||||
case 0: /* CP_IDFIELD */
|
||||
return 0x41034003;
|
||||
case 1: /* CP_FLASHPROG */
|
||||
return 0;
|
||||
case 2: /* CP_INTREG */
|
||||
return 0;
|
||||
case 3: /* CP_DECODE */
|
||||
return 0x11;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "icp_control_read: Bad offset %x\n", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void icp_control_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
icp_control_state *s = (icp_control_state *)opaque;
|
||||
offset -= s->base;
|
||||
switch (offset >> 2) {
|
||||
case 1: /* CP_FLASHPROG */
|
||||
case 2: /* CP_INTREG */
|
||||
case 3: /* CP_DECODE */
|
||||
/* Nothing interesting implemented yet. */
|
||||
break;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "icp_control_write: Bad offset %x\n", offset);
|
||||
}
|
||||
}
|
||||
static CPUReadMemoryFunc *icp_control_readfn[] = {
|
||||
icp_control_read,
|
||||
icp_control_read,
|
||||
icp_control_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *icp_control_writefn[] = {
|
||||
icp_control_write,
|
||||
icp_control_write,
|
||||
icp_control_write
|
||||
};
|
||||
|
||||
static void icp_control_init(uint32_t base)
|
||||
{
|
||||
int iomemtype;
|
||||
icp_control_state *s;
|
||||
|
||||
s = (icp_control_state *)qemu_mallocz(sizeof(icp_control_state));
|
||||
iomemtype = cpu_register_io_memory(0, icp_control_readfn,
|
||||
icp_control_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x007fffff, iomemtype);
|
||||
s->base = base;
|
||||
/* ??? Save/restore. */
|
||||
}
|
||||
|
||||
|
||||
/* Board init. */
|
||||
|
||||
static void integratorcp_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, uint32_t cpuid)
|
||||
{
|
||||
CPUState *env;
|
||||
uint32_t bios_offset;
|
||||
icp_pic_state *pic;
|
||||
void *cpu_pic;
|
||||
|
||||
env = cpu_init();
|
||||
cpu_arm_set_model(env, cpuid);
|
||||
bios_offset = ram_size + vga_ram_size;
|
||||
/* ??? On a real system the first 1Mb is mapped as SSRAM or boot flash. */
|
||||
/* ??? RAM shoud repeat to fill physical memory space. */
|
||||
/* SDRAM at address zero*/
|
||||
cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
|
||||
/* And again at address 0x80000000 */
|
||||
cpu_register_physical_memory(0x80000000, ram_size, IO_MEM_RAM);
|
||||
|
||||
integratorcm_init(ram_size >> 20, bios_offset);
|
||||
cpu_pic = arm_pic_init_cpu(env);
|
||||
pic = icp_pic_init(0x14000000, cpu_pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ);
|
||||
icp_pic_init(0xca000000, pic, 26, -1);
|
||||
icp_pit_init(0x13000000, pic, 5);
|
||||
pl011_init(0x16000000, pic, 1, serial_hds[0]);
|
||||
pl011_init(0x17000000, pic, 2, serial_hds[1]);
|
||||
icp_control_init(0xcb000000);
|
||||
pl050_init(0x18000000, pic, 3, 0);
|
||||
pl050_init(0x19000000, pic, 4, 1);
|
||||
if (nd_table[0].vlan) {
|
||||
if (nd_table[0].model == NULL
|
||||
|| strcmp(nd_table[0].model, "smc91c111") == 0) {
|
||||
smc91c111_init(&nd_table[0], 0xc8000000, pic, 27);
|
||||
} else {
|
||||
fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
pl110_init(ds, 0xc0000000, pic, 22, 0);
|
||||
|
||||
arm_load_kernel(ram_size, kernel_filename, kernel_cmdline,
|
||||
initrd_filename, 0x113);
|
||||
}
|
||||
|
||||
static void integratorcp926_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)
|
||||
{
|
||||
integratorcp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename,
|
||||
snapshot, kernel_filename, kernel_cmdline,
|
||||
initrd_filename, ARM_CPUID_ARM926);
|
||||
}
|
||||
|
||||
static void integratorcp1026_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)
|
||||
{
|
||||
integratorcp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename,
|
||||
snapshot, kernel_filename, kernel_cmdline,
|
||||
initrd_filename, ARM_CPUID_ARM1026);
|
||||
}
|
||||
|
||||
QEMUMachine integratorcp926_machine = {
|
||||
"integratorcp926",
|
||||
"ARM Integrator/CP (ARM926EJ-S)",
|
||||
integratorcp926_init,
|
||||
};
|
||||
|
||||
QEMUMachine integratorcp1026_machine = {
|
||||
"integratorcp1026",
|
||||
"ARM Integrator/CP (ARM1026EJ-S)",
|
||||
integratorcp1026_init,
|
||||
};
|
||||
258
hw/iommu.c
Normal file
258
hw/iommu.c
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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/4)
|
||||
#define IOMMU_CTRL (0x0000 >> 2)
|
||||
#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */
|
||||
#define IOMMU_CTRL_VERS 0x0f000000 /* Version */
|
||||
#define IOMMU_VERSION 0x04000000
|
||||
#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 */
|
||||
#define IOMMU_CTRL_MASK 0x0000001d
|
||||
|
||||
#define IOMMU_BASE (0x0004 >> 2)
|
||||
#define IOMMU_BASE_MASK 0x07fffc00
|
||||
|
||||
#define IOMMU_TLBFLUSH (0x0014 >> 2)
|
||||
#define IOMMU_TLBFLUSH_MASK 0xffffffff
|
||||
|
||||
#define IOMMU_PGFLUSH (0x0018 >> 2)
|
||||
#define IOMMU_PGFLUSH_MASK 0xffffffff
|
||||
|
||||
#define IOMMU_SBCFG0 (0x1010 >> 2) /* SBUS configration per-slot */
|
||||
#define IOMMU_SBCFG1 (0x1014 >> 2) /* SBUS configration per-slot */
|
||||
#define IOMMU_SBCFG2 (0x1018 >> 2) /* SBUS configration per-slot */
|
||||
#define IOMMU_SBCFG3 (0x101c >> 2) /* SBUS configration per-slot */
|
||||
#define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when bypass enabled */
|
||||
#define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */
|
||||
#define IOMMU_SBCFG_BA8 0x00000002 /* Slave supports 8 byte bursts */
|
||||
#define IOMMU_SBCFG_BYPASS 0x00000001 /* Bypass IOMMU, treat all addresses
|
||||
produced by this device as pure
|
||||
physical. */
|
||||
#define IOMMU_SBCFG_MASK 0x00010003
|
||||
|
||||
#define IOMMU_ARBEN (0x2000 >> 2) /* SBUS arbitration enable */
|
||||
#define IOMMU_ARBEN_MASK 0x001f0000
|
||||
#define IOMMU_MID 0x00000008
|
||||
|
||||
/* 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 IOMMU_CTRL:
|
||||
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);
|
||||
s->regs[saddr] = ((val & IOMMU_CTRL_MASK) | IOMMU_VERSION);
|
||||
break;
|
||||
case IOMMU_BASE:
|
||||
s->regs[saddr] = val & IOMMU_BASE_MASK;
|
||||
break;
|
||||
case IOMMU_TLBFLUSH:
|
||||
DPRINTF("tlb flush %x\n", val);
|
||||
s->regs[saddr] = val & IOMMU_TLBFLUSH_MASK;
|
||||
break;
|
||||
case IOMMU_PGFLUSH:
|
||||
DPRINTF("page flush %x\n", val);
|
||||
s->regs[saddr] = val & IOMMU_PGFLUSH_MASK;
|
||||
break;
|
||||
case IOMMU_SBCFG0:
|
||||
case IOMMU_SBCFG1:
|
||||
case IOMMU_SBCFG2:
|
||||
case IOMMU_SBCFG3:
|
||||
s->regs[saddr] = val & IOMMU_SBCFG_MASK;
|
||||
break;
|
||||
case IOMMU_ARBEN:
|
||||
// XXX implement SBus probing: fault when reading unmapped
|
||||
// addresses, fault cause and address stored to MMU/IOMMU
|
||||
s->regs[saddr] = (val & IOMMU_ARBEN_MASK) | IOMMU_MID;
|
||||
break;
|
||||
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;
|
||||
pa = ldl_phys(iopte);
|
||||
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;
|
||||
s->regs[0] = IOMMU_VERSION;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
462
hw/lance.c
Normal file
462
hw/lance.c
Normal file
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* 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 {
|
||||
VLANClientState *vc;
|
||||
uint8_t macaddr[6]; /* init mac address */
|
||||
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->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,
|
||||
};
|
||||
|
||||
|
||||
#define MIN_BUF_SIZE 60
|
||||
|
||||
static int lance_can_receive(void *opaque)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
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->vc, 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(NICInfo *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->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);
|
||||
|
||||
memcpy(s->macaddr, nd->macaddr, 6);
|
||||
|
||||
lance_reset(s);
|
||||
|
||||
s->vc = qemu_new_vlan_client(nd->vlan, lance_receive, lance_can_receive, s);
|
||||
|
||||
snprintf(s->vc->info_str, sizeof(s->vc->info_str),
|
||||
"lance macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
s->macaddr[0],
|
||||
s->macaddr[1],
|
||||
s->macaddr[2],
|
||||
s->macaddr[3],
|
||||
s->macaddr[4],
|
||||
s->macaddr[5]);
|
||||
|
||||
register_savevm("lance", leaddr, 1, lance_save, lance_load, s);
|
||||
qemu_register_reset(lance_reset, s);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user