User:Myask/Universal Mapper Description Language: Difference between revisions
No edit summary |
(→Declarations:: solder? DIP? jumper!) |
||
(11 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
[http://forums.nesdev.org/viewtopic.php?f=2&t=14542 Discussion thread.] | |||
=Needed features= | |||
Been thinking about making some better way to talk about mappers, as described [http://forums.nesdev.org/viewtopic.php?p=74174#p74174 in this post] among several other places on our boards. | |||
Parts/functions | Parts/functions | ||
* Define state bits | * Define state bits | ||
Line 19: | Line 22: | ||
Hard Part | Hard Part | ||
* Expansion audio (analog, can involve extra oscillators as [[VRC7 audio]] does) | * Expansion audio (analog, can involve extra oscillators as [[VRC7 audio]] does) | ||
=Specification= | |||
===Comments=== | |||
//c-style | |||
/*and c-style*/ | |||
//Let's also treat all whitespace the same (except newlines terminating //, blocks) | |||
//and "to" should be ignored in whitespace | |||
===Declarations: === | |||
mapper name begin | |||
//contents | |||
end name; | |||
The outer part. Technically a block started with begin/end. Name optional. | |||
mem name (address lines, data lines, writable, class); | |||
*writable can be RAM or ROM. | |||
*Class can be PRG, CHR, VRAM, [audio?] and MISC. Class controls default connections, and helps make obvious to readers what a thing is. | |||
(see NROM example for default connections. Any prg gets CPU_A, CPU_D, /ROMSEL ("ROMSEL_n"), and optionally the R/W signal if it's a RAM. chr/vram gets PPU_A, PPU_D, read/writes, and enable depending on PPU_A[13] (or /A13 if VRAM) | |||
solder name to /*contents*/; //can block if desired | |||
jumper name to /*contents*/; //can block if desired | |||
dip name to /*contents*/; //can block if desired: same as solder for emulator | |||
*dipswitch | |||
Just different name, really both just a state-based if statement, and these state bits are not settable except hardware(emulator)-side, unlike… | |||
register name; | |||
reg name;//short form | |||
init name to value; //can refer to iNES header fields like mirroring | |||
Not that iNES is something we want much of, but it'll cut down on file redundancy. | |||
===Statements:=== | |||
connect x to y to z; //any size netlist, whitespace-separated | |||
= x y z; //shortform | |||
set name to value; //set a state bit | |||
<= x y; //shortform | |||
===Operators=== | |||
bitwise & AND | OR ^ XOR ~ invert | |||
logical &&AND ||OR ^^XOR !not | |||
mathematical + - * binary operations, -negation | |||
<nowiki>{concatenate, concatenatee} [bus-index:range] {3 duplicate} //as in verilog</nowiki> | |||
===Execution blocks=== | |||
on CPU_WRITE /*do stuff*/; | |||
on PPU_WRITE | |||
on CPU_READ | |||
on PPU_READ | |||
on CLOCK | |||
===Handy Shorthand Defines=== | |||
bankreg name (which bus, width, address lines replaced, address mask selecting=equals what, which bus to write [inc A or D to write], mask to write=equals what,, written bits); | |||
fixedbank (which bus, width, address lines replaced=with what, address mask selecting=equals what); | |||
e.g. | |||
bankreg bnrom (CPU, 2, 16:15, 16'h8000=16'h8000, CPU_D, 16'h8000=16'h8000, CPU_D[1:0]); | |||
bankreg bxrom (CPU, 8, 22:15, 16'h8000=16'h8000, CPU_D, 16'h8000=16'h8000, CPU_D[7:0]); | |||
bankreg gnrom_cpu (CPU, 2, 16:15, 16'h8000=16'h8000, CPU_D, 16'h8000=16'h8000, CPU_D[5:4]); | |||
bankreg gnrom_ppu (PPU, 2, 14:13, 16'h2000=16'h0000, CPU_D, 16'h8000=16'h8000, CPU_D[1:0]); | |||
bankreg magic_series_cpu (CPU, 7, 21:15, 16'h8000=16'h8000, CPU_D, 16'h8000=16'h8000, CPU_D[7:1]); | |||
bankreg magic_series_ppu (PPU, 8, 20:13, 16'h2000=16'h0000, CPU_D, 16'h8000=16'h8000, CPU_D[7:0]); | |||
bankreg unrom (CPU, 3, 16:14, 16'hC000=16'h8000, CPU_D, 16'h8000=16'h8000, CPU_D[2:0]); | |||
fixedbank unrom_hi (CPU, 3, 16:14=3'b111, 16hC000=16'hC000); | |||
The idea here is that it autogenerates a register of appropriate width, and statements in ON_x_WRITE to write that register when its write mask equality is met, and sets the appropriate address lines when its select mask is met. fixedbank does not need a register, obviously. So, for bnrom, it would expand to | |||
reg bnrom_bank[1:0]; | |||
on CPU_WRITE begin | |||
if (CPU_A & 16'h8000 == 16'h8000) bnrom_bank <= CPU_D[1:0]; | |||
if (CPU_A & 16'h8000 == 16'h8000) prg.a[16:15] = bnrom_bank; | |||
end CPU_WRITE; | |||
on CPU_READ if (CPU_A & 16'h8000=16'h8000) prg.a[16:15] = bnrom_bank; | |||
=[[NROM]]-256 example= | =[[NROM]]-256 example= | ||
mapper NROM_256V begin | |||
//without autofills | |||
//aside from the mirroring, strikes me as the same as "default connections"? | |||
//component section | |||
prgrom prg(32KiB); | |||
//could also write 256Kib..but seems like a source of many typo problems | |||
//perhaps go by address line, data line counts? | |||
chrrom chr(8KiB); | |||
//only difference between PRG and CHR def'ns are its default connections | |||
//and outputs | |||
CIC cic(NES);//allow other chips I guess? | |||
//dynamic components section | |||
solder h to connect CIRAM_A10 to PPU_A[10]; | |||
solder v to connect CIRAM_A10 to PPU_A[11]; | |||
init h iNES.6[0]; | |||
init v ~iNES.6[0]; | |||
//technically redundant per wiki as only V-using boards had solder pads? | |||
//make "to" as whitespace, allowing nice codelook but not requiring | |||
connect CIRAM_CE_n to PPU_A13_n; | |||
//connections: power | |||
connect VCC prg.vcc cic.vcc chr.vcc; | |||
connect GND prg.gnd cic.gnd chr.gnd; | |||
//allow multiple connections per statement | |||
//considering a shortform lke "=" for connect | |||
//connections: CIC | |||
//[omitted] | |||
//connections: PRG | |||
connect prg.a[14:0] CPU_A[14:0]; | |||
//NROM_128: connect prg.a[13:0] CPU_A[13:0]; | |||
//and connect prg.a[14] VCC; //several ways to do it, really. | |||
connect prg.d[7:0] CPU_D[7:0]; | |||
connect prg.oe_n prg.ce_n ROMSEL_n; //I suspect I've got these mildly wrong | |||
//connections: CHR | |||
connect chr.a[12:0] PPU_A[12:0]; | |||
connect chr.d[7:0] PPU_D[7:0]; | |||
connect chr.oe_n chr.ce_n PPU_A[13]; | |||
end NROM_256V; |
Latest revision as of 01:43, 16 July 2016
Needed features
Been thinking about making some better way to talk about mappers, as described in this post among several other places on our boards. Parts/functions
- Define state bits
- include easy ROM/RAM chip(/internal) declaration; don't want to exclude MagicFloor nor MMC5/6 from "correct" description
- Logic
- Arbitrary address bus size for chips?
Convenience addtions
- Conditional operations (optional but helps user-side…but makes it harder program-side)
- some header fields as parameters might be desirable (mirroring, chip sizes)
- on the other hand, they are different boards in some manner. Perhaps only as solder-pad options?
- Cartridge connector pins as predefined signal names, or a module (to allow picking 60 or 72-pin)
- Option to autoconnect power, ground, address lines that are not in file
- like connecting PPU_A[0:7] to CHR_ROM_A[0:7] if CHR_ROM_A[0:7] do not appear in the description)
- Also autoconnect CIC
Extra function thoughts
- Outputs (e.g. LED)
- Inputs (e.g. DIPswitch, solder pads)
- Describing expansion port devices in similar manner
- Describing controller port devices in similar manner
Hard Part
- Expansion audio (analog, can involve extra oscillators as VRC7 audio does)
Specification
Comments
//c-style /*and c-style*/ //Let's also treat all whitespace the same (except newlines terminating //, blocks) //and "to" should be ignored in whitespace
Declarations:
mapper name begin //contents end name;
The outer part. Technically a block started with begin/end. Name optional.
mem name (address lines, data lines, writable, class);
- writable can be RAM or ROM.
- Class can be PRG, CHR, VRAM, [audio?] and MISC. Class controls default connections, and helps make obvious to readers what a thing is.
(see NROM example for default connections. Any prg gets CPU_A, CPU_D, /ROMSEL ("ROMSEL_n"), and optionally the R/W signal if it's a RAM. chr/vram gets PPU_A, PPU_D, read/writes, and enable depending on PPU_A[13] (or /A13 if VRAM)
solder name to /*contents*/; //can block if desired jumper name to /*contents*/; //can block if desired dip name to /*contents*/; //can block if desired: same as solder for emulator
- dipswitch
Just different name, really both just a state-based if statement, and these state bits are not settable except hardware(emulator)-side, unlike…
register name; reg name;//short form
init name to value; //can refer to iNES header fields like mirroring
Not that iNES is something we want much of, but it'll cut down on file redundancy.
Statements:
connect x to y to z; //any size netlist, whitespace-separated = x y z; //shortform set name to value; //set a state bit <= x y; //shortform
Operators
bitwise & AND | OR ^ XOR ~ invert logical &&AND ||OR ^^XOR !not mathematical + - * binary operations, -negation {concatenate, concatenatee} [bus-index:range] {3 duplicate} //as in verilog
Execution blocks
on CPU_WRITE /*do stuff*/; on PPU_WRITE on CPU_READ on PPU_READ on CLOCK
Handy Shorthand Defines
bankreg name (which bus, width, address lines replaced, address mask selecting=equals what, which bus to write [inc A or D to write], mask to write=equals what,, written bits); fixedbank (which bus, width, address lines replaced=with what, address mask selecting=equals what);
e.g.
bankreg bnrom (CPU, 2, 16:15, 16'h8000=16'h8000, CPU_D, 16'h8000=16'h8000, CPU_D[1:0]);
bankreg bxrom (CPU, 8, 22:15, 16'h8000=16'h8000, CPU_D, 16'h8000=16'h8000, CPU_D[7:0]);
bankreg gnrom_cpu (CPU, 2, 16:15, 16'h8000=16'h8000, CPU_D, 16'h8000=16'h8000, CPU_D[5:4]); bankreg gnrom_ppu (PPU, 2, 14:13, 16'h2000=16'h0000, CPU_D, 16'h8000=16'h8000, CPU_D[1:0]);
bankreg magic_series_cpu (CPU, 7, 21:15, 16'h8000=16'h8000, CPU_D, 16'h8000=16'h8000, CPU_D[7:1]); bankreg magic_series_ppu (PPU, 8, 20:13, 16'h2000=16'h0000, CPU_D, 16'h8000=16'h8000, CPU_D[7:0]); bankreg unrom (CPU, 3, 16:14, 16'hC000=16'h8000, CPU_D, 16'h8000=16'h8000, CPU_D[2:0]); fixedbank unrom_hi (CPU, 3, 16:14=3'b111, 16hC000=16'hC000);
The idea here is that it autogenerates a register of appropriate width, and statements in ON_x_WRITE to write that register when its write mask equality is met, and sets the appropriate address lines when its select mask is met. fixedbank does not need a register, obviously. So, for bnrom, it would expand to
reg bnrom_bank[1:0]; on CPU_WRITE begin if (CPU_A & 16'h8000 == 16'h8000) bnrom_bank <= CPU_D[1:0]; if (CPU_A & 16'h8000 == 16'h8000) prg.a[16:15] = bnrom_bank; end CPU_WRITE; on CPU_READ if (CPU_A & 16'h8000=16'h8000) prg.a[16:15] = bnrom_bank;
NROM-256 example
mapper NROM_256V begin //without autofills //aside from the mirroring, strikes me as the same as "default connections"? //component section prgrom prg(32KiB); //could also write 256Kib..but seems like a source of many typo problems //perhaps go by address line, data line counts? chrrom chr(8KiB); //only difference between PRG and CHR def'ns are its default connections //and outputs CIC cic(NES);//allow other chips I guess? //dynamic components section solder h to connect CIRAM_A10 to PPU_A[10]; solder v to connect CIRAM_A10 to PPU_A[11]; init h iNES.6[0]; init v ~iNES.6[0]; //technically redundant per wiki as only V-using boards had solder pads? //make "to" as whitespace, allowing nice codelook but not requiring connect CIRAM_CE_n to PPU_A13_n; //connections: power connect VCC prg.vcc cic.vcc chr.vcc; connect GND prg.gnd cic.gnd chr.gnd; //allow multiple connections per statement //considering a shortform lke "=" for connect //connections: CIC //[omitted] //connections: PRG connect prg.a[14:0] CPU_A[14:0]; //NROM_128: connect prg.a[13:0] CPU_A[13:0]; //and connect prg.a[14] VCC; //several ways to do it, really. connect prg.d[7:0] CPU_D[7:0]; connect prg.oe_n prg.ce_n ROMSEL_n; //I suspect I've got these mildly wrong //connections: CHR connect chr.a[12:0] PPU_A[12:0]; connect chr.d[7:0] PPU_D[7:0]; connect chr.oe_n chr.ce_n PPU_A[13]; end NROM_256V;