1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
let HEADERSTART = 0x0104 // the location of the header
let GLOBALCHECK = 0x014E
// copyTo copies one byte from the source to the destination array
// we are also substracting the HEADERSTART for easier mapping of the addresses
let private copyTo (destA :byte[]) (srcA: byte[]) offset srcIdx =
if srcA <> null && srcIdx >= 0 && srcIdx < srcA.Length then
destA.[(offset - HEADERSTART)+srcIdx] <- srcA.[srcIdx]
else
()
// prepares a string to be serialized in the header
let private str len offset s : (int*int*byte[]) =
if System.String.IsNullOrEmpty s then
(len, offset, null)
else
let s = s.ToUpperInvariant ()
let len = min len s.Length
let buf = Encoding.ASCII.GetBytes(s, 0, len)
(len, offset, buf)
// prepares a byte value for the header
let private bv offset (b:byte) =
let v = byte b
(1, offset, [|v|])
// prepares a 16 bit value for the header
let private word offset v =
let v1 = byte v
let v2 = byte (v >>> 8)
(2, offset, [|v1; v2|])
// get the rom size from the cartridge type
let private getRomSizeType ct =
match ct with
| Simple _ -> RomSize._32k
| MBC1 det -> det.RomSize
| MBC2 (rs, _) -> rs
| MMM01 det -> det.RomSize
| MBC3 (det, _) -> det.RomSize
| MBC5 (det, _) -> det.RomSize
| MBC6 det -> det.RomSize
| MBC7 det -> det.RomSize
// get the ram size from the cartridge type
let private getRamSize ct =
match ct with
| Simple (false,_) -> RamSize.None
| Simple (true, _) -> RamSize._8k
| MBC1 det -> det.RamSize
| MBC2 _ -> RamSize.None
| MMM01 det -> det.RamSize
| MBC3 (det, _) -> det.RamSize
| MBC5 (det, _) -> det.RamSize
| MBC6 det -> det.RamSize
| MBC7 det -> det.RamSize
// get the byte value for the header which indicates the MBC type.
let private getMBCMarker ct =
match ct with
| Simple (false, _) -> 0x00uy
| MBC1 {HasBattery = false; RamSize = RamSize.None } -> 0x01uy
| MBC1 {HasBattery = false } -> 0x02uy
| MBC1 _ -> 0x03uy
| MBC2 (_, false) -> 0x05uy
| MBC2 _ -> 0x06uy
| Simple (true, false) -> 0x08uy
| Simple (true, true) -> 0x09uy
| MMM01 {RamSize = RamSize.None} -> 0x0Buy
| MMM01 {HasBattery = false} -> 0x0Cuy
| MMM01 _ -> 0x0Duy
| MBC3 ({HasBattery=true; RamSize = RamSize.None}, true) -> 0x0Fuy
| MBC3 ({HasBattery=true}, true) -> 0x10uy
| MBC3 ({RamSize = RamSize.None},_) -> 0x11uy
| MBC3 ({HasBattery=false},_) -> 0x12uy
| MBC3 _ -> 0x13uy
| MBC5 ({RamSize = RamSize.None}, false) -> 0x19uy
| MBC5 ({HasBattery = false}, false) -> 0x1Auy
| MBC5 ({HasBattery = true}, false) -> 0x1Buy
| MBC5 ({RamSize = RamSize.None}, true) -> 0x1Cuy
| MBC5 ({HasBattery = false}, true) -> 0x1Duy
| MBC5 ({HasBattery = true}, true) -> 0x1Euy
| MBC6 _ -> 0x20uy
| MBC7 _ -> 0x22uy
// create a byte array with the information from the cartridge type (rom size, ram size, MBC type...)
let private cartridgeTypeToBytes (ct: CartridgeType) : byte[] =
let res = [|
getMBCMarker ct;
byte (getRomSizeType ct);
byte (getRamSize ct)
|]
res
// calculate the header checksum.
let private calcCheckSum (buf: byte[]) : byte =
seq {0x0134 .. 0x014C}
|> Seq.map (fun x -> x-HEADERSTART)
|> Seq.map (fun idx -> int (buf.[idx]))
|> Seq.fold (fun state v -> state - v - 1) 0
|> byte
// convert the header to a byte array.
let headerToBytes (h:CartridgeHeader) : byte[] =
// allocate the resulting array
let result = Array.zeroCreate<byte>(0x4A)
let copyTo (cnt, offset, data) =
Seq.init cnt (fun x -> x)
|> Seq.iter (copyTo result data offset)
copyTo (48, 0x0104, LOGO) // Nintendo logo
copyTo (str 16 0x0134 h.Title) // Game title
copyTo (str 4 0x013F h.ManufacturerCode) // New Manufacturer Code
copyTo (bv 0x0143 (byte h.GBC)) // GBC Flag
copyTo (word 0x0144 h.LicenseeCode) // Licensee Code
if h.SGB then
copyTo (bv 0x0146 0x03uy) // Super-Gameboy Flag
copyTo (3, 0x0147, cartridgeTypeToBytes h.CartridgeType) // 3 bytes cartridge type
copyTo (bv 0x014A (byte h.Destination)) // Destination
copyTo (bv 0x014B 0x33uy) // Old Licensee Code
copyTo (bv 0x014C h.Version) // Game version
copyTo (bv 0x014D (calcCheckSum result)) // header checksum
result
|