dma.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. #ifdef SCPU_CPP
  2. void sCPU::dma_add_clocks(unsigned clocks) {
  3. status.dma_clocks += clocks;
  4. add_clocks(clocks);
  5. }
  6. bool sCPU::dma_addr_valid(uint32 abus) {
  7. //reads from B-bus or S-CPU registers are invalid
  8. if((abus & 0x40ff00) == 0x2100) return false; //$[00-3f|80-bf]:[2100-21ff]
  9. if((abus & 0x40fe00) == 0x4000) return false; //$[00-3f|80-bf]:[4000-41ff]
  10. if((abus & 0x40ffe0) == 0x4200) return false; //$[00-3f|80-bf]:[4200-421f]
  11. if((abus & 0x40ff80) == 0x4300) return false; //$[00-3f|80-bf]:[4300-437f]
  12. return true;
  13. }
  14. uint8 sCPU::dma_read(uint32 abus) {
  15. if(dma_addr_valid(abus) == false) return 0x00; //does not return S-CPU MDR
  16. return bus.read(abus);
  17. }
  18. void sCPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) {
  19. if(direction == 0) {
  20. //a->b transfer (to $21xx)
  21. if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) {
  22. //illegal WRAM->WRAM transfer (bus conflict)
  23. //read most likely occurs; no write occurs
  24. //read is irrelevent, as it cannot be observed by software
  25. dma_add_clocks(8);
  26. } else {
  27. dma_add_clocks(4);
  28. uint8 data = dma_read(abus);
  29. dma_add_clocks(4);
  30. bus.write(0x2100 | bbus, data);
  31. }
  32. } else {
  33. //b->a transfer (from $21xx)
  34. if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) {
  35. //illegal WRAM->WRAM transfer (bus conflict)
  36. //no read occurs; write does occur
  37. dma_add_clocks(8);
  38. bus.write(abus, 0x00); //does not write S-CPU MDR
  39. } else {
  40. dma_add_clocks(4);
  41. uint8 data = bus.read(0x2100 | bbus);
  42. dma_add_clocks(4);
  43. if(dma_addr_valid(abus) == true) {
  44. bus.write(abus, data);
  45. }
  46. }
  47. }
  48. cycle_edge();
  49. }
  50. /*****
  51. * address calculation functions
  52. *****/
  53. uint8 sCPU::dma_bbus(uint8 i, uint8 index) {
  54. switch(channel[i].xfermode) { default:
  55. case 0: return (channel[i].destaddr); //0
  56. case 1: return (channel[i].destaddr + (index & 1)); //0,1
  57. case 2: return (channel[i].destaddr); //0,0
  58. case 3: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1
  59. case 4: return (channel[i].destaddr + (index & 3)); //0,1,2,3
  60. case 5: return (channel[i].destaddr + (index & 1)); //0,1,0,1
  61. case 6: return (channel[i].destaddr); //0,0 [2]
  62. case 7: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 [3]
  63. }
  64. }
  65. inline uint32 sCPU::dma_addr(uint8 i) {
  66. uint32 r = (channel[i].srcbank << 16) | (channel[i].srcaddr);
  67. if(channel[i].fixedxfer == false) {
  68. if(channel[i].reversexfer == false) {
  69. channel[i].srcaddr++;
  70. } else {
  71. channel[i].srcaddr--;
  72. }
  73. }
  74. return r;
  75. }
  76. inline uint32 sCPU::hdma_addr(uint8 i) {
  77. return (channel[i].srcbank << 16) | (channel[i].hdma_addr++);
  78. }
  79. inline uint32 sCPU::hdma_iaddr(uint8 i) {
  80. return (channel[i].hdma_ibank << 16) | (channel[i].hdma_iaddr++);
  81. }
  82. /*****
  83. * DMA functions
  84. *****/
  85. uint8 sCPU::dma_enabled_channels() {
  86. uint8 r = 0;
  87. for(unsigned i = 0; i < 8; i++) {
  88. if(channel[i].dma_enabled) r++;
  89. }
  90. return r;
  91. }
  92. void sCPU::dma_run() {
  93. dma_add_clocks(8);
  94. cycle_edge();
  95. for(unsigned i = 0; i < 8; i++) {
  96. if(channel[i].dma_enabled == false) continue;
  97. dma_add_clocks(8);
  98. cycle_edge();
  99. unsigned index = 0;
  100. do {
  101. dma_transfer(channel[i].direction, dma_bbus(i, index++), dma_addr(i));
  102. } while(channel[i].dma_enabled && --channel[i].xfersize);
  103. channel[i].dma_enabled = false;
  104. }
  105. status.irq_lock = true;
  106. event.enqueue(2, EventIrqLockRelease);
  107. }
  108. /*****
  109. * HDMA functions
  110. *****/
  111. inline bool sCPU::hdma_active(uint8 i) {
  112. return (channel[i].hdma_enabled && !channel[i].hdma_completed);
  113. }
  114. inline bool sCPU::hdma_active_after(uint8 i) {
  115. for(unsigned n = i + 1; n < 8; n++) {
  116. if(hdma_active(n) == true) return true;
  117. }
  118. return false;
  119. }
  120. inline uint8 sCPU::hdma_enabled_channels() {
  121. uint8 r = 0;
  122. for(unsigned i = 0; i < 8; i++) {
  123. if(channel[i].hdma_enabled) r++;
  124. }
  125. return r;
  126. }
  127. inline uint8 sCPU::hdma_active_channels() {
  128. uint8 r = 0;
  129. for(unsigned i = 0; i < 8; i++) {
  130. if(hdma_active(i) == true) r++;
  131. }
  132. return r;
  133. }
  134. void sCPU::hdma_update(uint8 i) {
  135. channel[i].hdma_line_counter = dma_read(hdma_addr(i));
  136. dma_add_clocks(8);
  137. channel[i].hdma_completed = (channel[i].hdma_line_counter == 0);
  138. channel[i].hdma_do_transfer = !channel[i].hdma_completed;
  139. if(channel[i].hdma_indirect) {
  140. channel[i].hdma_iaddr = dma_read(hdma_addr(i)) << 8;
  141. dma_add_clocks(8);
  142. if(!channel[i].hdma_completed || hdma_active_after(i)) {
  143. channel[i].hdma_iaddr >>= 8;
  144. channel[i].hdma_iaddr |= dma_read(hdma_addr(i)) << 8;
  145. dma_add_clocks(8);
  146. }
  147. }
  148. }
  149. void sCPU::hdma_run() {
  150. dma_add_clocks(8);
  151. for(unsigned i = 0; i < 8; i++) {
  152. if(hdma_active(i) == false) continue;
  153. channel[i].dma_enabled = false; //HDMA run during DMA will stop DMA mid-transfer
  154. if(channel[i].hdma_do_transfer) {
  155. static const unsigned transfer_length[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
  156. unsigned length = transfer_length[channel[i].xfermode];
  157. for(unsigned index = 0; index < length; index++) {
  158. unsigned addr = !channel[i].hdma_indirect ? hdma_addr(i) : hdma_iaddr(i);
  159. dma_transfer(channel[i].direction, dma_bbus(i, index), addr);
  160. }
  161. }
  162. }
  163. for(unsigned i = 0; i < 8; i++) {
  164. if(hdma_active(i) == false) continue;
  165. channel[i].hdma_line_counter--;
  166. channel[i].hdma_do_transfer = bool(channel[i].hdma_line_counter & 0x80);
  167. if((channel[i].hdma_line_counter & 0x7f) == 0) {
  168. hdma_update(i);
  169. } else {
  170. dma_add_clocks(8);
  171. }
  172. }
  173. status.irq_lock = true;
  174. event.enqueue(2, EventIrqLockRelease);
  175. }
  176. void sCPU::hdma_init_reset() {
  177. for(unsigned i = 0; i < 8; i++) {
  178. channel[i].hdma_completed = false;
  179. channel[i].hdma_do_transfer = false;
  180. }
  181. }
  182. void sCPU::hdma_init() {
  183. dma_add_clocks(8);
  184. for(unsigned i = 0; i < 8; i++) {
  185. if(!channel[i].hdma_enabled) continue;
  186. channel[i].dma_enabled = false; //HDMA init during DMA will stop DMA mid-transfer
  187. channel[i].hdma_addr = channel[i].srcaddr;
  188. hdma_update(i);
  189. }
  190. status.irq_lock = true;
  191. event.enqueue(2, EventIrqLockRelease);
  192. }
  193. /*****
  194. * power / reset functions
  195. *****/
  196. void sCPU::dma_power() {
  197. for(unsigned i = 0; i < 8; i++) {
  198. channel[i].dmap = 0xff;
  199. channel[i].direction = 1;
  200. channel[i].hdma_indirect = true;
  201. channel[i].reversexfer = true;
  202. channel[i].fixedxfer = true;
  203. channel[i].xfermode = 7;
  204. channel[i].destaddr = 0xff;
  205. channel[i].srcaddr = 0xffff;
  206. channel[i].srcbank = 0xff;
  207. channel[i].xfersize = 0xffff;
  208. //channel[i].hdma_iaddr = 0xffff; //union with xfersize
  209. channel[i].hdma_ibank = 0xff;
  210. channel[i].hdma_addr = 0xffff;
  211. channel[i].hdma_line_counter = 0xff;
  212. channel[i].unknown = 0xff;
  213. }
  214. }
  215. void sCPU::dma_reset() {
  216. for(unsigned i = 0; i < 8; i++) {
  217. channel[i].dma_enabled = false;
  218. channel[i].hdma_enabled = false;
  219. channel[i].hdma_completed = false;
  220. channel[i].hdma_do_transfer = false;
  221. }
  222. }
  223. #endif //ifdef SCPU_CPP