Fantasy 8Bit system (F8), is a fantasy 8bit console and a set of libraries for creating fantasy 8bit consoles.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
4.0KB

  1. var IMem = require('./imem.js');
  2. class Switch{
  3. constructor(mem){
  4. this.__mem = [mem];
  5. this.__idx = 0;
  6. }
  7. get mem(){return this.__mem[this.__idx];}
  8. get idx(){return this.__idx;}
  9. set idx(i){
  10. if (i >= 0 && i < this.__mem.length)
  11. this.__idx = i;
  12. }
  13. addMemModule(mem){
  14. if (!(mem instanceof IMem))
  15. throw new ValueError("Only IMem instances can be added to MMC Banks.");
  16. if (this.__mem.length >= 4)
  17. throw new RangeError("Bank handling maximum memory modules.");
  18. if (this.__mem.length > 0 && mem.size !== this.__mem[0].size)
  19. throw new RangeError("Memory module does not match size of already connected memory modules on this bank.");
  20. this.__mem.push(mem);
  21. }
  22. }
  23. class MMC extends IMem{
  24. constructor(){
  25. super();
  26. this.__switches = [];
  27. this.__addr = 0;
  28. this.__sidx = 0;
  29. }
  30. get pages(){
  31. return this.__switches.reduce((acc, s)=>{
  32. return acc + s.mem.pages;
  33. }, 0);
  34. }
  35. get size(){
  36. return this.__switches.reduce((acc, s)=>{
  37. return acc + s.mem.size;
  38. }, 0);
  39. }
  40. get writable(){
  41. let iw = false;
  42. for (let i=0; i < this.__switches.length; i++){
  43. if (this.__switches[i].mem.writable){
  44. iw = true; break;
  45. }
  46. }
  47. return iw;
  48. }
  49. get switches(){return this.__switches.length;}
  50. get address(){return this.__addr;}
  51. set address(a){
  52. this.__addr = Math.min(this.size - 1, Math.max(0, a));
  53. let offset = 0;
  54. for (let s=0; s < this.__switches.length; s++){
  55. if (this.__addr >= offset && this.__addr < offset + this.__switches[s].mem.size){
  56. this.__sidx = s;
  57. this.__switches[s].mem.address = this.__addr - offset;
  58. break;
  59. } else {
  60. offset += this.__switches[s].mem.size;
  61. }
  62. }
  63. }
  64. get byte(){
  65. return (this.__switches.length > 0) ? this.__switches[this.__sidx].mem.byte : -1;
  66. }
  67. set byte(b){
  68. if (this.__switches.length > 0){
  69. this.__switches[this.__sidx].mem.byte = b;
  70. }
  71. }
  72. load(addr, data){
  73. if (addr < 0 || addr > this.size)
  74. throw new RangeError("Memory address out of range.");
  75. let offset = 0;
  76. let doff = 0;
  77. let addre = addr + data.length;
  78. for (let s = 0; s < this.__switches.length; s++){
  79. let mem = this.__switches[s].mem;
  80. if (addr < offset + mem.size && addre >= offset){
  81. let a = (addr > offset) ? addr - offset : 0;
  82. let dlen = (mem.size - a < data.length - doff) ?
  83. mem.size - a : data.length - doff;
  84. mem.load(a, data.slice(doff, doff + dlen));
  85. doff += dlen;
  86. }
  87. offset += mem.size;
  88. }
  89. return this;
  90. }
  91. clearPage(paddr){
  92. for (let i=0; i < this.__switches.length; i++){
  93. let pgs = this.__switches[i].mem.pages;
  94. if (paddr < pgs){
  95. this.__switches[i].mem.clearPage(addr);
  96. break;
  97. }
  98. paddr -= pgs;
  99. }
  100. return this;
  101. }
  102. clear(){
  103. this.__switches.forEach((s)=>{
  104. s.clear();
  105. });
  106. return this;
  107. }
  108. connectMemory(mem, addroff){
  109. addroff = (typeof(addroff) === 'number' && addroff >= 0) ? addroff : -1;
  110. if (addroff < 0 || addroff === this.size){
  111. this.__switches.push(new Switch(mem));
  112. } else {
  113. let offset = 0;
  114. for (let s=0; s < this.__switches.length; s++){
  115. if (addroff === offset){
  116. if (this.__switches[s].mem.size !== mem.size)
  117. throw new RangeError("Memory modules assigned to the same bank must be the same byte size.");
  118. this.__switches[s].addMemModule(mem);
  119. offset = -1;
  120. break;
  121. } else {
  122. offset += this.__switches[s].mem.size;
  123. }
  124. }
  125. if (offset >= 0)
  126. throw new RangeError("Cannot align memory module to bank at address " + addroff);
  127. }
  128. return this;
  129. }
  130. switchBank(bank){
  131. // The variable <bank> contains two 4 bit offsets.
  132. // The upper 4 bits is the bank switch offset.
  133. // The lower 4 bits is the bank offset within the switch.
  134. let mmcidx = (bank & 0xF0) >> 4;
  135. if (mmcidx < this.__switches.length){
  136. this.__switches[mmcidx].idx = (bank & 0x0F);
  137. }
  138. }
  139. }
  140. module.exports = MMC;