nonogamer9

teest

Sep 19th, 2026 (edited)
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 63.76 KB | None | 0 0
  1. -- ================================================================
  2. -- OC-NES
  3. -- Mapper 0 / NROM
  4. -- Improved PPU + CPU/PPU timing
  5. --
  6. -- ROM: /home/game.nes
  7. -- Canvas: 160x50 terminal cells
  8. -- 320x200 Braille pixels
  9. --
  10. -- Controls:
  11. -- W/A/S/D = D-pad
  12. -- J = A
  13. -- K = B
  14. -- Space = Select
  15. -- Enter = Start
  16. -- Q = Quit
  17. -- ================================================================
  18.  
  19. local component = require("component")
  20. local computer = require("computer")
  21. local event = require("event")
  22. local keyboard = require("keyboard")
  23. local unicode = require("unicode")
  24. local uchar = unicode.char
  25.  
  26. local gpu = component.gpu
  27.  
  28. if not gpu then
  29. error("A GPU is required.")
  30. end
  31.  
  32. if not component.isAvailable("screen") then
  33. error("A screen is required.")
  34. end
  35.  
  36. -- ================================================================
  37. -- CONFIG
  38. -- ================================================================
  39.  
  40. local ROM_FILE = "/home/game.nes"
  41.  
  42. local CW, CH = 160, 50
  43.  
  44. local VRAM_W = CW * 2
  45. local VRAM_H = CH * 4
  46.  
  47. local NES_W = 256
  48. local NES_H = 240
  49.  
  50. -- NTSC NES CPU is ~1.789773 MHz.
  51. -- One frame is approximately 29780.5 CPU cycles.
  52. local CPU_CYCLES_FRAME = 29780
  53. local CPU_FRAME_ALTERNATE = 1
  54.  
  55. local FRAME_TIME = 1 / 60.0988
  56.  
  57. -- PPU:
  58. -- 341 PPU cycles per scanline
  59. -- 262 scanlines per frame
  60. local PPU_CYCLES_PER_SCANLINE = 341
  61. local PPU_SCANLINES = 262
  62.  
  63. -- ================================================================
  64. -- SCREEN
  65. -- ================================================================
  66.  
  67. local maxW, maxH = gpu.maxResolution()
  68.  
  69. local drawW = math.min(CW, maxW)
  70. local drawH = math.min(CH, maxH)
  71.  
  72. gpu.setResolution(drawW, drawH)
  73. gpu.setBackground(0x000000)
  74. gpu.setForeground(0xFFFFFF)
  75. gpu.fill(1, 1, drawW, drawH, " ")
  76.  
  77. -- ================================================================
  78. -- BRAILLE
  79. -- ================================================================
  80.  
  81. local W1, W2, W3, W4 = 1, 8, 2, 16
  82. local W5, W6, W7, W8 = 4, 32, 64, 128
  83.  
  84. local brailleChars = {}
  85.  
  86. for i = 0, 255 do
  87. brailleChars[i] = uchar(0x2800 + i)
  88. end
  89.  
  90. local cellPix = {0,0,0,0,0,0,0,0}
  91. local colorCount = {}
  92.  
  93. local function chooseColors()
  94. for k in pairs(colorCount) do
  95. colorCount[k] = nil
  96. end
  97.  
  98. local p = cellPix
  99.  
  100. for i = 1, 8 do
  101. local c = p[i]
  102. colorCount[c] = (colorCount[c] or 0) + 1
  103. end
  104.  
  105. local bg = p[1]
  106. local fg = p[1]
  107.  
  108. local best = -1
  109. local second = -1
  110.  
  111. for c, n in pairs(colorCount) do
  112. if n > best then
  113. second = best
  114. fg = bg
  115. best = n
  116. bg = c
  117. elseif n > second then
  118. second = n
  119. fg = c
  120. end
  121. end
  122.  
  123. return bg, fg
  124. end
  125.  
  126. -- ================================================================
  127. -- FILE LOADING
  128. -- ================================================================
  129.  
  130. local function readFile(path)
  131. local f, err = io.open(path, "rb")
  132.  
  133. if not f then
  134. error("Cannot open " .. path .. ": " .. tostring(err))
  135. end
  136.  
  137. local data = f:read("*a")
  138. f:close()
  139.  
  140. return data
  141. end
  142.  
  143. local rom = readFile(ROM_FILE)
  144.  
  145. local function u8(s, p)
  146. return string.byte(s, p) or 0
  147. end
  148.  
  149. -- ================================================================
  150. -- iNES HEADER
  151. -- ================================================================
  152.  
  153. if #rom < 16 then
  154. error("ROM too small for iNES header.")
  155. end
  156.  
  157. if rom:sub(1, 4) ~= "NES\26" then
  158. error("Not an iNES ROM.")
  159. end
  160.  
  161. local prgBanks = u8(rom, 5)
  162. local chrBanks = u8(rom, 6)
  163.  
  164. local flags6 = u8(rom, 7)
  165. local flags7 = u8(rom, 8)
  166. local prgRamBanks = u8(rom, 9)
  167. if prgRamBanks == 0 then prgRamBanks = 1 end
  168.  
  169. local mapper =
  170. math.floor(flags6 / 16) +
  171. math.floor(flags7 / 16) * 16
  172.  
  173. if mapper ~= 0 then
  174. error(
  175. "Unsupported mapper " ..
  176. tostring(mapper) ..
  177. ". Only Mapper 0 is supported."
  178. )
  179. end
  180.  
  181. if prgBanks ~= 1 and prgBanks ~= 2 then
  182. error("Mapper 0 requires 1 or 2 PRG-ROM banks.")
  183. end
  184.  
  185. if chrBanks ~= 0 and chrBanks ~= 1 then
  186. error("Mapper 0 supports 0 or 1 CHR-ROM banks (CHR-RAM or 8 KiB CHR-ROM).")
  187. end
  188.  
  189. local trainer = (flags6 & 0x04) ~= 0
  190.  
  191. local romOffset =
  192. 16 +
  193. (trainer and 512 or 0)
  194.  
  195. local PRG_SIZE = prgBanks * 16384
  196. local CHR_SIZE = 8192
  197. local CHR_RAM = chrBanks == 0
  198.  
  199. if #rom < romOffset + PRG_SIZE + chrBanks * CHR_SIZE then
  200. error("ROM file is truncated.")
  201. end
  202.  
  203. local prg = {}
  204. local chr = {}
  205.  
  206. for i = 0, PRG_SIZE - 1 do
  207. prg[i] = u8(rom, romOffset + i + 1)
  208. end
  209.  
  210. local chrOffset = romOffset + PRG_SIZE
  211.  
  212. for i = 0, CHR_SIZE - 1 do
  213. if CHR_RAM then
  214. chr[i] = 0
  215. else
  216. chr[i] = u8(rom, chrOffset + i + 1)
  217. end
  218. end
  219.  
  220. -- ================================================================
  221. -- NES MEMORY
  222. -- ================================================================
  223.  
  224. local ram = {}
  225.  
  226. for i = 0, 0x7FF do
  227. ram[i] = 0
  228. end
  229.  
  230. local PRG_RAM_SIZE = prgRamBanks * 8192
  231. local prgRam = {}
  232.  
  233. for i = 0, PRG_RAM_SIZE - 1 do
  234. prgRam[i] = 0
  235. end
  236.  
  237. local ppuRegs = {}
  238.  
  239. for i = 0, 7 do
  240. ppuRegs[i] = 0
  241. end
  242.  
  243. local apuRegs = {}
  244.  
  245. for i = 0, 0x1F do
  246. apuRegs[i] = 0
  247. end
  248.  
  249. -- Mapper 0 normally has 2 KiB CIRAM, but four-screen
  250. -- cartridges provide 4 KiB of dedicated nametable VRAM.
  251. local FOUR_SCREEN = (flags6 & 0x08) ~= 0
  252. local NAMETABLE_SIZE = FOUR_SCREEN and 4096 or 2048
  253. local nametable = {}
  254.  
  255. for i = 0, NAMETABLE_SIZE - 1 do
  256. nametable[i] = 0
  257. end
  258.  
  259. local palette = {}
  260.  
  261. for i = 0, 31 do
  262. palette[i] = 0
  263. end
  264.  
  265. local oam = {}
  266.  
  267. for i = 0, 255 do
  268. oam[i] = 0
  269. end
  270.  
  271. -- ================================================================
  272. -- CONTROLLER
  273. -- ================================================================
  274.  
  275. local buttons = {
  276. A = false,
  277. B = false,
  278. SELECT = false,
  279. START = false,
  280. UP = false,
  281. DOWN = false,
  282. LEFT = false,
  283. RIGHT = false
  284. }
  285.  
  286. local controllerLatch = 0
  287. local controllerIndex = 0
  288.  
  289. local function controllerByte()
  290. local v = 0
  291.  
  292. if buttons.A then v = v | 0x01 end
  293. if buttons.B then v = v | 0x02 end
  294. if buttons.SELECT then v = v | 0x04 end
  295. if buttons.START then v = v | 0x08 end
  296. if buttons.UP then v = v | 0x10 end
  297. if buttons.DOWN then v = v | 0x20 end
  298. if buttons.LEFT then v = v | 0x40 end
  299. if buttons.RIGHT then v = v | 0x80 end
  300.  
  301. return v
  302. end
  303.  
  304. local function controllerRead()
  305. if controllerLatch ~= 0 then
  306. return controllerByte() & 1
  307. end
  308.  
  309. if controllerIndex < 8 then
  310. local v =
  311. (controllerByte() >> controllerIndex) & 1
  312.  
  313. controllerIndex = controllerIndex + 1
  314.  
  315. return v
  316. end
  317.  
  318. return 1
  319. end
  320.  
  321. -- ================================================================
  322. -- PPU ADDRESS / SCROLL STATE
  323. -- ================================================================
  324.  
  325. local ppuAddr = 0
  326. local ppuAddrLatch = false
  327.  
  328. local ppuDataBuffer = 0
  329.  
  330. local ppuScrollLatch = false
  331. local ppuScrollX = 0
  332. local ppuScrollY = 0
  333.  
  334. local oamAddr = 0
  335.  
  336. -- PPU timing state.
  337. local ppuCycle = 0
  338. local ppuScanline = 0
  339. local ppuFrame = 0
  340.  
  341. -- Prevent repeated NMI triggering during the same VBlank.
  342. local nmiOccurredThisVBlank = false
  343. local nmiPending = false
  344. local oamDmaPending = false
  345.  
  346. -- ================================================================
  347. -- PPU MEMORY
  348. -- ================================================================
  349.  
  350. local function mirrorNametable(addr)
  351. local a = (addr - 0x2000) & 0x0FFF
  352.  
  353. local tableIndex = (a >> 10) & 3
  354. local offset = a & 0x03FF
  355.  
  356. -- Four-screen NROM has a full 4 KiB of nametable VRAM.
  357. if FOUR_SCREEN then
  358. return (tableIndex * 0x400) + offset
  359. end
  360.  
  361. -- iNES flags6 bit 0:
  362. --
  363. -- 0 = horizontal mirroring
  364. -- $2000 = $2400
  365. -- $2800 = $2C00
  366. --
  367. -- 1 = vertical mirroring
  368. -- $2000 = $2800
  369. -- $2400 = $2C00
  370.  
  371. if (flags6 & 1) ~= 0 then
  372. -- Vertical
  373. if tableIndex == 0 or tableIndex == 2 then
  374. return offset
  375. else
  376. return 0x400 + offset
  377. end
  378. else
  379. -- Horizontal
  380. if tableIndex == 0 or tableIndex == 1 then
  381. return offset
  382. else
  383. return 0x400 + offset
  384. end
  385. end
  386. end
  387.  
  388. local function paletteAddress(addr)
  389. local p = (addr - 0x3F00) & 0x1F
  390.  
  391. -- Universal background mirrors.
  392. if p == 0x10 then p = 0x00 end
  393. if p == 0x14 then p = 0x04 end
  394. if p == 0x18 then p = 0x08 end
  395. if p == 0x1C then p = 0x0C end
  396.  
  397. return p
  398. end
  399.  
  400. local function ppuRead(addr)
  401. addr = addr & 0x3FFF
  402.  
  403. if addr < 0x2000 then
  404.  
  405. -- CHR-ROM.
  406. return chr[addr] or 0
  407.  
  408. elseif addr < 0x3F00 then
  409.  
  410. return nametable[
  411. mirrorNametable(addr)
  412. ] or 0
  413.  
  414. else
  415.  
  416. return palette[
  417. paletteAddress(addr)
  418. ] or 0
  419.  
  420. end
  421. end
  422.  
  423. local function ppuWrite(addr, value)
  424. addr = addr & 0x3FFF
  425. value = value & 0xFF
  426.  
  427. if addr < 0x2000 then
  428.  
  429. if CHR_RAM then
  430. chr[addr] = value
  431. end
  432.  
  433. return
  434.  
  435. elseif addr < 0x3F00 then
  436.  
  437. nametable[
  438. mirrorNametable(addr)
  439. ] = value
  440.  
  441. else
  442.  
  443. palette[
  444. paletteAddress(addr)
  445. ] = value
  446.  
  447. end
  448. end
  449.  
  450. local function ppuIncrement()
  451. -- PPUCTRL bit 2.
  452. --
  453. -- 0 = +1
  454. -- 1 = +32
  455.  
  456. if (ppuRegs[0] & 0x04) ~= 0 then
  457. return 32
  458. end
  459.  
  460. return 1
  461. end
  462.  
  463. -- ================================================================
  464. -- CPU MEMORY
  465. -- ================================================================
  466.  
  467. local function cpuRead(addr)
  468. addr = addr & 0xFFFF
  469.  
  470. -- --------------------------------------------------------------
  471. -- CPU RAM
  472. -- --------------------------------------------------------------
  473.  
  474. if addr < 0x2000 then
  475.  
  476. return ram[addr & 0x07FF] or 0
  477.  
  478. -- --------------------------------------------------------------
  479. -- PPU registers
  480. -- --------------------------------------------------------------
  481.  
  482. elseif addr < 0x4000 then
  483.  
  484. local r =
  485. 0x2000 + (addr & 7)
  486.  
  487. -- PPUSTATUS
  488. if r == 0x2002 then
  489.  
  490. local v = ppuRegs[2] or 0
  491.  
  492. -- Reading PPUSTATUS clears VBlank.
  493. ppuRegs[2] = v & 0x7F
  494.  
  495. -- Reading status resets both $2005/$2006 write latches.
  496. ppuAddrLatch = false
  497. ppuScrollLatch = false
  498.  
  499. return v
  500.  
  501. -- OAMDATA
  502. elseif r == 0x2004 then
  503.  
  504. local v = oam[oamAddr] or 0
  505.  
  506. -- OAM attribute bits 2-4 are not stored on the PPU bus.
  507. if (oamAddr & 3) == 2 then
  508. v = v & 0xE3
  509. end
  510.  
  511. return v
  512.  
  513. -- PPUDATA
  514. elseif r == 0x2007 then
  515.  
  516. local value = ppuRead(ppuAddr)
  517.  
  518. if ppuAddr < 0x3F00 then
  519.  
  520. local old = ppuDataBuffer
  521.  
  522. ppuDataBuffer = value
  523.  
  524. ppuAddr =
  525. (ppuAddr + ppuIncrement()) &
  526. 0x3FFF
  527.  
  528. return old
  529.  
  530. else
  531.  
  532. -- Palette reads are not delayed.
  533. -- Fill the internal buffer with the mirrored
  534. -- nametable-side value.
  535.  
  536. ppuDataBuffer =
  537. ppuRead(
  538. (ppuAddr - 0x1000) &
  539. 0x3FFF
  540. )
  541.  
  542. ppuAddr =
  543. (ppuAddr + ppuIncrement()) &
  544. 0x3FFF
  545.  
  546. return value
  547.  
  548. end
  549. end
  550.  
  551. return ppuRegs[r - 0x2000] or 0
  552.  
  553. -- --------------------------------------------------------------
  554. -- APU / controllers
  555. -- --------------------------------------------------------------
  556.  
  557. elseif addr < 0x4020 then
  558.  
  559. if addr == 0x4016 then
  560. return controllerRead()
  561.  
  562. elseif addr == 0x4017 then
  563. return 0x40
  564.  
  565. else
  566. return apuRegs[
  567. addr - 0x4000
  568. ] or 0
  569. end
  570.  
  571. -- --------------------------------------------------------------
  572. -- Cartridge / expansion space
  573. -- --------------------------------------------------------------
  574.  
  575. elseif addr < 0x8000 then
  576.  
  577. return prgRam[(addr - 0x6000) % PRG_RAM_SIZE] or 0
  578.  
  579. -- --------------------------------------------------------------
  580. -- PRG ROM
  581. -- --------------------------------------------------------------
  582.  
  583. else
  584.  
  585. local p = addr - 0x8000
  586.  
  587. if PRG_SIZE == 16384 then
  588. p = p & 0x3FFF
  589. else
  590. p = p & 0x7FFF
  591. end
  592.  
  593. return prg[p] or 0
  594. end
  595. end
  596.  
  597. local function cpuWrite(addr, value)
  598. addr = addr & 0xFFFF
  599. value = value & 0xFF
  600.  
  601. -- --------------------------------------------------------------
  602. -- CPU RAM
  603. -- --------------------------------------------------------------
  604.  
  605. if addr < 0x2000 then
  606.  
  607. ram[addr & 0x07FF] = value
  608.  
  609. -- --------------------------------------------------------------
  610. -- PPU registers
  611. -- --------------------------------------------------------------
  612.  
  613. elseif addr < 0x4000 then
  614.  
  615. local r =
  616. 0x2000 + (addr & 7)
  617.  
  618. -- PPUCTRL
  619. if r == 0x2000 then
  620.  
  621. local old = ppuRegs[0]
  622.  
  623. ppuRegs[0] = value
  624.  
  625. -- If NMI gets enabled while VBlank is already active,
  626. -- generate the NMI.
  627.  
  628. if
  629. (old & 0x80) == 0 and
  630. (value & 0x80) ~= 0 and
  631. (ppuRegs[2] & 0x80) ~= 0
  632. then
  633. -- NMI is recognized at a CPU instruction boundary, not
  634. -- in the middle of the instruction that wrote $2000.
  635. nmiPending = true
  636. end
  637.  
  638. -- PPUMASK
  639. elseif r == 0x2001 then
  640.  
  641. ppuRegs[1] = value
  642.  
  643. -- OAMADDR
  644. elseif r == 0x2003 then
  645.  
  646. oamAddr = value
  647. ppuRegs[3] = value
  648.  
  649. -- OAMDATA
  650. elseif r == 0x2004 then
  651.  
  652. if (oamAddr & 3) == 2 then
  653. value = value & 0xE3
  654. end
  655.  
  656. oam[oamAddr] = value
  657.  
  658. oamAddr =
  659. (oamAddr + 1) & 0xFF
  660.  
  661. ppuRegs[3] = oamAddr
  662.  
  663. -- PPUSCROLL
  664. elseif r == 0x2005 then
  665.  
  666. if not ppuScrollLatch then
  667.  
  668. -- First write supplies horizontal scroll.
  669. ppuScrollX = value & 0xFF
  670. ppuScrollLatch = true
  671.  
  672. else
  673.  
  674. -- Second write supplies vertical scroll.
  675. ppuScrollY = value & 0xFF
  676. ppuScrollLatch = false
  677.  
  678. end
  679.  
  680. -- PPUADDR
  681. elseif r == 0x2006 then
  682.  
  683. if not ppuAddrLatch then
  684.  
  685. -- First write:
  686. -- bits 8-13.
  687.  
  688. ppuAddr =
  689. ((value & 0x3F) << 8) |
  690. (ppuAddr & 0x00FF)
  691.  
  692. ppuAddrLatch = true
  693.  
  694. else
  695.  
  696. -- Second write:
  697. -- bits 0-7.
  698.  
  699. ppuAddr =
  700. (ppuAddr & 0x3F00) |
  701. value
  702.  
  703. ppuAddr =
  704. ppuAddr & 0x3FFF
  705.  
  706. ppuAddrLatch = false
  707.  
  708. end
  709.  
  710. -- PPUDATA
  711. elseif r == 0x2007 then
  712.  
  713. ppuWrite(
  714. ppuAddr,
  715. value
  716. )
  717.  
  718. ppuAddr =
  719. (ppuAddr + ppuIncrement()) &
  720. 0x3FFF
  721.  
  722. end
  723.  
  724. -- --------------------------------------------------------------
  725. -- APU / DMA / controller
  726. -- --------------------------------------------------------------
  727.  
  728. elseif addr < 0x4020 then
  729.  
  730. -- OAMDMA
  731. if addr == 0x4014 then
  732.  
  733. local base = value << 8
  734.  
  735. for i = 0, 255 do
  736.  
  737. oam[(oamAddr + i) & 0xFF] =
  738. cpuRead(
  739. (base + i) & 0xFFFF
  740. )
  741.  
  742. end
  743.  
  744. oamAddr =
  745. (oamAddr + 256) & 0xFF
  746.  
  747. -- OAM DMA suspends the CPU for 513 or 514 cycles after
  748. -- the $4014 write. The main loop accounts for the stall.
  749. oamDmaPending = true
  750.  
  751. -- Controller strobe
  752. elseif addr == 0x4016 then
  753.  
  754. local newLatch = value & 1
  755.  
  756. if newLatch ~= 0 then
  757.  
  758. controllerLatch = 1
  759.  
  760. else
  761.  
  762. if controllerLatch ~= 0 then
  763. controllerIndex = 0
  764. end
  765.  
  766. controllerLatch = 0
  767.  
  768. end
  769.  
  770. else
  771.  
  772. apuRegs[
  773. addr - 0x4000
  774. ] = value
  775.  
  776. end
  777.  
  778. -- --------------------------------------------------------------
  779. -- No NROM PRG-RAM.
  780. -- --------------------------------------------------------------
  781.  
  782. elseif addr < 0x8000 then
  783.  
  784. prgRam[(addr - 0x6000) % PRG_RAM_SIZE] = value
  785.  
  786. -- --------------------------------------------------------------
  787. -- PRG-ROM writes ignored.
  788. -- --------------------------------------------------------------
  789.  
  790. else
  791.  
  792. return
  793. end
  794. end
  795.  
  796. -- ================================================================
  797. -- 6502 CPU
  798. -- ================================================================
  799.  
  800. local A, X, Y = 0, 0, 0
  801. local SP = 0xFD
  802. local PC = 0
  803. local P = 0x24
  804. local cpuTotalCycles = 0
  805.  
  806. local C = 1
  807. local Z = 2
  808. local I = 4
  809. local D = 8
  810. local B = 16
  811. local U = 32
  812. local V = 64
  813. local N = 128
  814.  
  815. local function setFlag(f, enabled)
  816. if enabled then
  817. P = P | f
  818. else
  819. P = P & (~f)
  820. end
  821. end
  822.  
  823. local function getFlag(f)
  824. return (P & f) ~= 0
  825. end
  826.  
  827. local function setZN(v)
  828. v = v & 0xFF
  829.  
  830. setFlag(Z, v == 0)
  831. setFlag(N, (v & 0x80) ~= 0)
  832. end
  833.  
  834. local function push(v)
  835. cpuWrite(
  836. 0x100 + SP,
  837. v
  838. )
  839.  
  840. SP =
  841. (SP - 1) & 0xFF
  842. end
  843.  
  844. local function pop()
  845. SP =
  846. (SP + 1) & 0xFF
  847.  
  848. return cpuRead(
  849. 0x100 + SP
  850. )
  851. end
  852.  
  853. local function fetch()
  854. local v = cpuRead(PC)
  855.  
  856. PC =
  857. (PC + 1) & 0xFFFF
  858.  
  859. return v
  860. end
  861.  
  862. local function fetch16()
  863. local lo = fetch()
  864. local hi = fetch()
  865.  
  866. return lo | (hi << 8)
  867. end
  868.  
  869. local function zp()
  870. return fetch()
  871. end
  872.  
  873. local function zpx()
  874. return (fetch() + X) & 0xFF
  875. end
  876.  
  877. local function zpy()
  878. return (fetch() + Y) & 0xFF
  879. end
  880.  
  881. local function abs()
  882. return fetch16()
  883. end
  884.  
  885. local function absx()
  886. local base = fetch16()
  887. local addr = (base + X) & 0xFFFF
  888. return addr, ((base & 0xFF00) ~= (addr & 0xFF00))
  889. end
  890.  
  891. local function absy()
  892. local base = fetch16()
  893. local addr = (base + Y) & 0xFFFF
  894. return addr, ((base & 0xFF00) ~= (addr & 0xFF00))
  895. end
  896.  
  897. local function indx()
  898. local p =
  899. (fetch() + X) & 0xFF
  900.  
  901. return
  902. cpuRead(p) |
  903. (cpuRead((p + 1) & 0xFF) << 8)
  904. end
  905.  
  906. local function indy()
  907. local p = fetch()
  908. local base =
  909. cpuRead(p) |
  910. (cpuRead((p + 1) & 0xFF) << 8)
  911. local addr = (base + Y) & 0xFFFF
  912.  
  913. return addr, ((base & 0xFF00) ~= (addr & 0xFF00))
  914. end
  915.  
  916. local function branch(cond)
  917. local off = fetch()
  918.  
  919. if off >= 128 then
  920. off = off - 256
  921. end
  922.  
  923. if not cond then
  924. return 2
  925. end
  926.  
  927. local oldPC = PC
  928. PC = (PC + off) & 0xFFFF
  929.  
  930. return
  931. 3 + (((oldPC & 0xFF00) ~= (PC & 0xFF00)) and 1 or 0)
  932. end
  933.  
  934. local function adc(v)
  935. local carry =
  936. getFlag(C) and 1 or 0
  937.  
  938. local a = A
  939.  
  940. local result =
  941. a + v + carry
  942.  
  943. setFlag(
  944. C,
  945. result > 255
  946. )
  947.  
  948. setFlag(
  949. V,
  950. ((~(a ~ v)) &
  951. (a ~ result) &
  952. 0x80) ~= 0
  953. )
  954.  
  955. A =
  956. result & 0xFF
  957.  
  958. setZN(A)
  959. end
  960.  
  961. local function sbc(v)
  962. adc(v ~ 0xFF)
  963. end
  964.  
  965. local function compare(reg, v)
  966. local r =
  967. (reg - v) & 0xFF
  968.  
  969. setFlag(
  970. C,
  971. reg >= v
  972. )
  973.  
  974. setZN(r)
  975. end
  976.  
  977. local function asl(v)
  978. setFlag(
  979. C,
  980. (v & 0x80) ~= 0
  981. )
  982.  
  983. v =
  984. (v << 1) & 0xFF
  985.  
  986. setZN(v)
  987.  
  988. return v
  989. end
  990.  
  991. local function lsr(v)
  992. setFlag(
  993. C,
  994. (v & 1) ~= 0
  995. )
  996.  
  997. v =
  998. (v >> 1) & 0xFF
  999.  
  1000. setZN(v)
  1001.  
  1002. return v
  1003. end
  1004.  
  1005. local function rol(v)
  1006. local c =
  1007. getFlag(C) and 1 or 0
  1008.  
  1009. setFlag(
  1010. C,
  1011. (v & 0x80) ~= 0
  1012. )
  1013.  
  1014. v =
  1015. ((v << 1) | c) & 0xFF
  1016.  
  1017. setZN(v)
  1018.  
  1019. return v
  1020. end
  1021.  
  1022. local function ror(v)
  1023. local c =
  1024. getFlag(C) and 1 or 0
  1025.  
  1026. setFlag(
  1027. C,
  1028. (v & 1) ~= 0
  1029. )
  1030.  
  1031. v =
  1032. ((v >> 1) | (c << 7)) & 0xFF
  1033.  
  1034. setZN(v)
  1035.  
  1036. return v
  1037. end
  1038.  
  1039. local function lda(v)
  1040. A = v & 0xFF
  1041. setZN(A)
  1042. end
  1043.  
  1044. local function ldx(v)
  1045. X = v & 0xFF
  1046. setZN(X)
  1047. end
  1048.  
  1049. local function ldy(v)
  1050. Y = v & 0xFF
  1051. setZN(Y)
  1052. end
  1053.  
  1054. -- ---------------------------------------------------------------
  1055. -- CPU reset
  1056. -- ---------------------------------------------------------------
  1057.  
  1058. local halted = false
  1059.  
  1060. local function cpuReset()
  1061. A = 0
  1062. X = 0
  1063. Y = 0
  1064.  
  1065. SP = 0xFD
  1066. P = 0x24
  1067. cpuTotalCycles = 0
  1068.  
  1069. PC =
  1070. cpuRead(0xFFFC) |
  1071. (cpuRead(0xFFFD) << 8)
  1072.  
  1073. halted = false
  1074. end
  1075.  
  1076. -- ================================================================
  1077. -- CPU STEP
  1078. -- ================================================================
  1079.  
  1080. local function cpuStep()
  1081.  
  1082. if halted then
  1083. return 2
  1084. end
  1085.  
  1086. local op = fetch()
  1087.  
  1088. -- --------------------------------------------------------------
  1089. -- LDA
  1090. -- --------------------------------------------------------------
  1091.  
  1092. if op == 0xA9 then
  1093. lda(fetch())
  1094. return 2
  1095.  
  1096. elseif op == 0xA5 then
  1097. lda(cpuRead(zp()))
  1098. return 3
  1099.  
  1100. elseif op == 0xB5 then
  1101. lda(cpuRead(zpx()))
  1102. return 4
  1103.  
  1104. elseif op == 0xAD then
  1105. lda(cpuRead(abs()))
  1106. return 4
  1107.  
  1108. elseif op == 0xBD then
  1109. local a, cross = absx()
  1110. lda(cpuRead(a))
  1111. return 4 + (cross and 1 or 0)
  1112.  
  1113. elseif op == 0xB9 then
  1114. local a, cross = absy()
  1115. lda(cpuRead(a))
  1116. return 4 + (cross and 1 or 0)
  1117.  
  1118. elseif op == 0xA1 then
  1119. lda(cpuRead(indx()))
  1120. return 6
  1121.  
  1122. elseif op == 0xB1 then
  1123. local a, cross = indy()
  1124. lda(cpuRead(a))
  1125. return 5 + (cross and 1 or 0)
  1126.  
  1127. -- --------------------------------------------------------------
  1128. -- LDX
  1129. -- --------------------------------------------------------------
  1130.  
  1131. elseif op == 0xA2 then
  1132. ldx(fetch())
  1133. return 2
  1134.  
  1135. elseif op == 0xA6 then
  1136. ldx(cpuRead(zp()))
  1137. return 3
  1138.  
  1139. elseif op == 0xB6 then
  1140. ldx(cpuRead(zpy()))
  1141. return 4
  1142.  
  1143. elseif op == 0xAE then
  1144. ldx(cpuRead(abs()))
  1145. return 4
  1146.  
  1147. elseif op == 0xBE then
  1148. local a, cross = absy()
  1149. ldx(cpuRead(a))
  1150. return 4 + (cross and 1 or 0)
  1151.  
  1152. -- --------------------------------------------------------------
  1153. -- LDY
  1154. -- --------------------------------------------------------------
  1155.  
  1156. elseif op == 0xA0 then
  1157. ldy(fetch())
  1158. return 2
  1159.  
  1160. elseif op == 0xA4 then
  1161. ldy(cpuRead(zp()))
  1162. return 3
  1163.  
  1164. elseif op == 0xB4 then
  1165. ldy(cpuRead(zpx()))
  1166. return 4
  1167.  
  1168. elseif op == 0xAC then
  1169. ldy(cpuRead(abs()))
  1170. return 4
  1171.  
  1172. elseif op == 0xBC then
  1173. local a, cross = absx()
  1174. ldy(cpuRead(a))
  1175. return 4 + (cross and 1 or 0)
  1176.  
  1177. -- --------------------------------------------------------------
  1178. -- STA
  1179. -- --------------------------------------------------------------
  1180.  
  1181. elseif op == 0x85 then
  1182. cpuWrite(zp(), A)
  1183. return 3
  1184.  
  1185. elseif op == 0x95 then
  1186. cpuWrite(zpx(), A)
  1187. return 4
  1188.  
  1189. elseif op == 0x8D then
  1190. cpuWrite(abs(), A)
  1191. return 4
  1192.  
  1193. elseif op == 0x9D then
  1194. cpuWrite(absx(), A)
  1195. return 5
  1196.  
  1197. elseif op == 0x99 then
  1198. cpuWrite(absy(), A)
  1199. return 5
  1200.  
  1201. elseif op == 0x81 then
  1202. cpuWrite(indx(), A)
  1203. return 6
  1204.  
  1205. elseif op == 0x91 then
  1206. cpuWrite(indy(), A)
  1207. return 6
  1208.  
  1209. -- --------------------------------------------------------------
  1210. -- STX
  1211. -- --------------------------------------------------------------
  1212.  
  1213. elseif op == 0x86 then
  1214. cpuWrite(zp(), X)
  1215. return 3
  1216.  
  1217. elseif op == 0x96 then
  1218. cpuWrite(zpy(), X)
  1219. return 4
  1220.  
  1221. elseif op == 0x8E then
  1222. cpuWrite(abs(), X)
  1223. return 4
  1224.  
  1225. -- --------------------------------------------------------------
  1226. -- STY
  1227. -- --------------------------------------------------------------
  1228.  
  1229. elseif op == 0x84 then
  1230. cpuWrite(zp(), Y)
  1231. return 3
  1232.  
  1233. elseif op == 0x94 then
  1234. cpuWrite(zpx(), Y)
  1235. return 4
  1236.  
  1237. elseif op == 0x8C then
  1238. cpuWrite(abs(), Y)
  1239. return 4
  1240.  
  1241. -- --------------------------------------------------------------
  1242. -- Transfers
  1243. -- --------------------------------------------------------------
  1244.  
  1245. elseif op == 0xAA then
  1246. X = A
  1247. setZN(X)
  1248. return 2
  1249.  
  1250. elseif op == 0xA8 then
  1251. Y = A
  1252. setZN(Y)
  1253. return 2
  1254.  
  1255. elseif op == 0x8A then
  1256. A = X
  1257. setZN(A)
  1258. return 2
  1259.  
  1260. elseif op == 0x98 then
  1261. A = Y
  1262. setZN(A)
  1263. return 2
  1264.  
  1265. elseif op == 0xBA then
  1266. X = SP
  1267. setZN(X)
  1268. return 2
  1269.  
  1270. elseif op == 0x9A then
  1271. SP = X
  1272. return 2
  1273.  
  1274. -- --------------------------------------------------------------
  1275. -- INC / DEC registers
  1276. -- --------------------------------------------------------------
  1277.  
  1278. elseif op == 0xE8 then
  1279. X = (X + 1) & 0xFF
  1280. setZN(X)
  1281. return 2
  1282.  
  1283. elseif op == 0xCA then
  1284. X = (X - 1) & 0xFF
  1285. setZN(X)
  1286. return 2
  1287.  
  1288. elseif op == 0xC8 then
  1289. Y = (Y + 1) & 0xFF
  1290. setZN(Y)
  1291. return 2
  1292.  
  1293. elseif op == 0x88 then
  1294. Y = (Y - 1) & 0xFF
  1295. setZN(Y)
  1296. return 2
  1297.  
  1298. -- --------------------------------------------------------------
  1299. -- INC memory
  1300. -- --------------------------------------------------------------
  1301.  
  1302. elseif op == 0xE6 then
  1303. local a = zp()
  1304. local v = (cpuRead(a) + 1) & 0xFF
  1305. cpuWrite(a, v)
  1306. setZN(v)
  1307. return 5
  1308.  
  1309. elseif op == 0xF6 then
  1310. local a = zpx()
  1311. local v = (cpuRead(a) + 1) & 0xFF
  1312. cpuWrite(a, v)
  1313. setZN(v)
  1314. return 6
  1315.  
  1316. elseif op == 0xEE then
  1317. local a = abs()
  1318. local v = (cpuRead(a) + 1) & 0xFF
  1319. cpuWrite(a, v)
  1320. setZN(v)
  1321. return 6
  1322.  
  1323. elseif op == 0xFE then
  1324. local a = absx()
  1325. local v = (cpuRead(a) + 1) & 0xFF
  1326. cpuWrite(a, v)
  1327. setZN(v)
  1328. return 7
  1329.  
  1330. -- --------------------------------------------------------------
  1331. -- DEC memory
  1332. -- --------------------------------------------------------------
  1333.  
  1334. elseif op == 0xC6 then
  1335. local a = zp()
  1336. local v = (cpuRead(a) - 1) & 0xFF
  1337. cpuWrite(a, v)
  1338. setZN(v)
  1339. return 5
  1340.  
  1341. elseif op == 0xD6 then
  1342. local a = zpx()
  1343. local v = (cpuRead(a) - 1) & 0xFF
  1344. cpuWrite(a, v)
  1345. setZN(v)
  1346. return 6
  1347.  
  1348. elseif op == 0xCE then
  1349. local a = abs()
  1350. local v = (cpuRead(a) - 1) & 0xFF
  1351. cpuWrite(a, v)
  1352. setZN(v)
  1353. return 6
  1354.  
  1355. elseif op == 0xDE then
  1356. local a = absx()
  1357. local v = (cpuRead(a) - 1) & 0xFF
  1358. cpuWrite(a, v)
  1359. setZN(v)
  1360. return 7
  1361.  
  1362. -- --------------------------------------------------------------
  1363. -- ADC
  1364. -- --------------------------------------------------------------
  1365.  
  1366. elseif op == 0x69 then
  1367. adc(fetch())
  1368. return 2
  1369.  
  1370. elseif op == 0x65 then
  1371. adc(cpuRead(zp()))
  1372. return 3
  1373.  
  1374. elseif op == 0x75 then
  1375. adc(cpuRead(zpx()))
  1376. return 4
  1377.  
  1378. elseif op == 0x6D then
  1379. adc(cpuRead(abs()))
  1380. return 4
  1381.  
  1382. elseif op == 0x7D then
  1383. local a, cross = absx()
  1384. adc(cpuRead(a))
  1385. return 4 + (cross and 1 or 0)
  1386.  
  1387. elseif op == 0x79 then
  1388. local a, cross = absy()
  1389. adc(cpuRead(a))
  1390. return 4 + (cross and 1 or 0)
  1391.  
  1392. elseif op == 0x61 then
  1393. adc(cpuRead(indx()))
  1394. return 6
  1395.  
  1396. elseif op == 0x71 then
  1397. local a, cross = indy()
  1398. adc(cpuRead(a))
  1399. return 5 + (cross and 1 or 0)
  1400.  
  1401. -- --------------------------------------------------------------
  1402. -- SBC
  1403. -- --------------------------------------------------------------
  1404.  
  1405. elseif op == 0xE9 then
  1406. sbc(fetch())
  1407. return 2
  1408.  
  1409. elseif op == 0xE5 then
  1410. sbc(cpuRead(zp()))
  1411. return 3
  1412.  
  1413. elseif op == 0xF5 then
  1414. sbc(cpuRead(zpx()))
  1415. return 4
  1416.  
  1417. elseif op == 0xED then
  1418. sbc(cpuRead(abs()))
  1419. return 4
  1420.  
  1421. elseif op == 0xFD then
  1422. local a, cross = absx()
  1423. sbc(cpuRead(a))
  1424. return 4 + (cross and 1 or 0)
  1425.  
  1426. elseif op == 0xF9 then
  1427. local a, cross = absy()
  1428. sbc(cpuRead(a))
  1429. return 4 + (cross and 1 or 0)
  1430.  
  1431. elseif op == 0xE1 then
  1432. sbc(cpuRead(indx()))
  1433. return 6
  1434.  
  1435. elseif op == 0xF1 then
  1436. local a, cross = indy()
  1437. sbc(cpuRead(a))
  1438. return 5 + (cross and 1 or 0)
  1439.  
  1440. -- --------------------------------------------------------------
  1441. -- AND
  1442. -- --------------------------------------------------------------
  1443.  
  1444. elseif op == 0x29 then
  1445. A = A & fetch()
  1446. setZN(A)
  1447. return 2
  1448.  
  1449. elseif op == 0x25 then
  1450. A = A & cpuRead(zp())
  1451. setZN(A)
  1452. return 3
  1453.  
  1454. elseif op == 0x35 then
  1455. A = A & cpuRead(zpx())
  1456. setZN(A)
  1457. return 4
  1458.  
  1459. elseif op == 0x2D then
  1460. A = A & cpuRead(abs())
  1461. setZN(A)
  1462. return 4
  1463.  
  1464. elseif op == 0x3D then
  1465. local a, cross = absx()
  1466. A = A & cpuRead(a)
  1467. setZN(A)
  1468. return 4 + (cross and 1 or 0)
  1469.  
  1470. elseif op == 0x39 then
  1471. local a, cross = absy()
  1472. A = A & cpuRead(a)
  1473. setZN(A)
  1474. return 4 + (cross and 1 or 0)
  1475.  
  1476. elseif op == 0x21 then
  1477. A = A & cpuRead(indx())
  1478. setZN(A)
  1479. return 6
  1480.  
  1481. elseif op == 0x31 then
  1482. local a, cross = indy()
  1483. A = A & cpuRead(a)
  1484. setZN(A)
  1485. return 5 + (cross and 1 or 0)
  1486.  
  1487. -- --------------------------------------------------------------
  1488. -- ORA
  1489. -- --------------------------------------------------------------
  1490.  
  1491. elseif op == 0x09 then
  1492. A = A | fetch()
  1493. setZN(A)
  1494. return 2
  1495.  
  1496. elseif op == 0x05 then
  1497. A = A | cpuRead(zp())
  1498. setZN(A)
  1499. return 3
  1500.  
  1501. elseif op == 0x15 then
  1502. A = A | cpuRead(zpx())
  1503. setZN(A)
  1504. return 4
  1505.  
  1506. elseif op == 0x0D then
  1507. A = A | cpuRead(abs())
  1508. setZN(A)
  1509. return 4
  1510.  
  1511. elseif op == 0x1D then
  1512. local a, cross = absx()
  1513. A = A | cpuRead(a)
  1514. setZN(A)
  1515. return 4 + (cross and 1 or 0)
  1516.  
  1517. elseif op == 0x19 then
  1518. local a, cross = absy()
  1519. A = A | cpuRead(a)
  1520. setZN(A)
  1521. return 4 + (cross and 1 or 0)
  1522.  
  1523. elseif op == 0x01 then
  1524. A = A | cpuRead(indx())
  1525. setZN(A)
  1526. return 6
  1527.  
  1528. elseif op == 0x11 then
  1529. local a, cross = indy()
  1530. A = A | cpuRead(a)
  1531. setZN(A)
  1532. return 5 + (cross and 1 or 0)
  1533.  
  1534. -- --------------------------------------------------------------
  1535. -- EOR
  1536. -- --------------------------------------------------------------
  1537.  
  1538. elseif op == 0x49 then
  1539. A = A ~ fetch()
  1540. setZN(A)
  1541. return 2
  1542.  
  1543. elseif op == 0x45 then
  1544. A = A ~ cpuRead(zp())
  1545. setZN(A)
  1546. return 3
  1547.  
  1548. elseif op == 0x55 then
  1549. A = A ~ cpuRead(zpx())
  1550. setZN(A)
  1551. return 4
  1552.  
  1553. elseif op == 0x4D then
  1554. A = A ~ cpuRead(abs())
  1555. setZN(A)
  1556. return 4
  1557.  
  1558. elseif op == 0x5D then
  1559. local a, cross = absx()
  1560. A = A ~ cpuRead(a)
  1561. setZN(A)
  1562. return 4 + (cross and 1 or 0)
  1563.  
  1564. elseif op == 0x59 then
  1565. local a, cross = absy()
  1566. A = A ~ cpuRead(a)
  1567. setZN(A)
  1568. return 4 + (cross and 1 or 0)
  1569.  
  1570. elseif op == 0x41 then
  1571. A = A ~ cpuRead(indx())
  1572. setZN(A)
  1573. return 6
  1574.  
  1575. elseif op == 0x51 then
  1576. local a, cross = indy()
  1577. A = A ~ cpuRead(a)
  1578. setZN(A)
  1579. return 5 + (cross and 1 or 0)
  1580.  
  1581. -- --------------------------------------------------------------
  1582. -- CMP
  1583. -- --------------------------------------------------------------
  1584.  
  1585. elseif op == 0xC9 then
  1586. compare(A, fetch())
  1587. return 2
  1588.  
  1589. elseif op == 0xC5 then
  1590. compare(A, cpuRead(zp()))
  1591. return 3
  1592.  
  1593. elseif op == 0xD5 then
  1594. compare(A, cpuRead(zpx()))
  1595. return 4
  1596.  
  1597. elseif op == 0xCD then
  1598. compare(A, cpuRead(abs()))
  1599. return 4
  1600.  
  1601. elseif op == 0xDD then
  1602. local a, cross = absx()
  1603. compare(A, cpuRead(a))
  1604. return 4 + (cross and 1 or 0)
  1605.  
  1606. elseif op == 0xD9 then
  1607. local a, cross = absy()
  1608. compare(A, cpuRead(a))
  1609. return 4 + (cross and 1 or 0)
  1610.  
  1611. elseif op == 0xC1 then
  1612. compare(A, cpuRead(indx()))
  1613. return 6
  1614.  
  1615. elseif op == 0xD1 then
  1616. local a, cross = indy()
  1617. compare(A, cpuRead(a))
  1618. return 5 + (cross and 1 or 0)
  1619.  
  1620. -- --------------------------------------------------------------
  1621. -- CPX
  1622. -- --------------------------------------------------------------
  1623.  
  1624. elseif op == 0xE0 then
  1625. compare(X, fetch())
  1626. return 2
  1627.  
  1628. elseif op == 0xE4 then
  1629. compare(X, cpuRead(zp()))
  1630. return 3
  1631.  
  1632. elseif op == 0xEC then
  1633. compare(X, cpuRead(abs()))
  1634. return 4
  1635.  
  1636. -- --------------------------------------------------------------
  1637. -- CPY
  1638. -- --------------------------------------------------------------
  1639.  
  1640. elseif op == 0xC0 then
  1641. compare(Y, fetch())
  1642. return 2
  1643.  
  1644. elseif op == 0xC4 then
  1645. compare(Y, cpuRead(zp()))
  1646. return 3
  1647.  
  1648. elseif op == 0xCC then
  1649. compare(Y, cpuRead(abs()))
  1650. return 4
  1651.  
  1652. -- --------------------------------------------------------------
  1653. -- ASL
  1654. -- --------------------------------------------------------------
  1655.  
  1656. elseif op == 0x0A then
  1657. A = asl(A)
  1658. return 2
  1659.  
  1660. elseif op == 0x06 then
  1661. local a = zp()
  1662. cpuWrite(a, asl(cpuRead(a)))
  1663. return 5
  1664.  
  1665. elseif op == 0x16 then
  1666. local a = zpx()
  1667. cpuWrite(a, asl(cpuRead(a)))
  1668. return 6
  1669.  
  1670. elseif op == 0x0E then
  1671. local a = abs()
  1672. cpuWrite(a, asl(cpuRead(a)))
  1673. return 6
  1674.  
  1675. elseif op == 0x1E then
  1676. local a = absx()
  1677. cpuWrite(a, asl(cpuRead(a)))
  1678. return 7
  1679.  
  1680. -- --------------------------------------------------------------
  1681. -- LSR
  1682. -- --------------------------------------------------------------
  1683.  
  1684. elseif op == 0x4A then
  1685. A = lsr(A)
  1686. return 2
  1687.  
  1688. elseif op == 0x46 then
  1689. local a = zp()
  1690. cpuWrite(a, lsr(cpuRead(a)))
  1691. return 5
  1692.  
  1693. elseif op == 0x56 then
  1694. local a = zpx()
  1695. cpuWrite(a, lsr(cpuRead(a)))
  1696. return 6
  1697.  
  1698. elseif op == 0x4E then
  1699. local a = abs()
  1700. cpuWrite(a, lsr(cpuRead(a)))
  1701. return 6
  1702.  
  1703. elseif op == 0x5E then
  1704. local a = absx()
  1705. cpuWrite(a, lsr(cpuRead(a)))
  1706. return 7
  1707.  
  1708. -- --------------------------------------------------------------
  1709. -- ROL
  1710. -- --------------------------------------------------------------
  1711.  
  1712. elseif op == 0x2A then
  1713. A = rol(A)
  1714. return 2
  1715.  
  1716. elseif op == 0x26 then
  1717. local a = zp()
  1718. cpuWrite(a, rol(cpuRead(a)))
  1719. return 5
  1720.  
  1721. elseif op == 0x36 then
  1722. local a = zpx()
  1723. cpuWrite(a, rol(cpuRead(a)))
  1724. return 6
  1725.  
  1726. elseif op == 0x2E then
  1727. local a = abs()
  1728. cpuWrite(a, rol(cpuRead(a)))
  1729. return 6
  1730.  
  1731. elseif op == 0x3E then
  1732. local a = absx()
  1733. cpuWrite(a, rol(cpuRead(a)))
  1734. return 7
  1735.  
  1736. -- --------------------------------------------------------------
  1737. -- ROR
  1738. -- --------------------------------------------------------------
  1739.  
  1740. elseif op == 0x6A then
  1741. A = ror(A)
  1742. return 2
  1743.  
  1744. elseif op == 0x66 then
  1745. local a = zp()
  1746. cpuWrite(a, ror(cpuRead(a)))
  1747. return 5
  1748.  
  1749. elseif op == 0x76 then
  1750. local a = zpx()
  1751. cpuWrite(a, ror(cpuRead(a)))
  1752. return 6
  1753.  
  1754. elseif op == 0x6E then
  1755. local a = abs()
  1756. cpuWrite(a, ror(cpuRead(a)))
  1757. return 6
  1758.  
  1759. elseif op == 0x7E then
  1760. local a = absx()
  1761. cpuWrite(a, ror(cpuRead(a)))
  1762. return 7
  1763.  
  1764. -- --------------------------------------------------------------
  1765. -- BIT
  1766. -- --------------------------------------------------------------
  1767.  
  1768. elseif op == 0x24 then
  1769.  
  1770. local v = cpuRead(zp())
  1771.  
  1772. setFlag(
  1773. Z,
  1774. (A & v) == 0
  1775. )
  1776.  
  1777. setFlag(
  1778. V,
  1779. (v & 0x40) ~= 0
  1780. )
  1781.  
  1782. setFlag(
  1783. N,
  1784. (v & 0x80) ~= 0
  1785. )
  1786.  
  1787. return 3
  1788.  
  1789. elseif op == 0x2C then
  1790.  
  1791. local v = cpuRead(abs())
  1792.  
  1793. setFlag(
  1794. Z,
  1795. (A & v) == 0
  1796. )
  1797.  
  1798. setFlag(
  1799. V,
  1800. (v & 0x40) ~= 0
  1801. )
  1802.  
  1803. setFlag(
  1804. N,
  1805. (v & 0x80) ~= 0
  1806. )
  1807.  
  1808. return 4
  1809.  
  1810. -- --------------------------------------------------------------
  1811. -- Branches
  1812. -- --------------------------------------------------------------
  1813.  
  1814. elseif op == 0x10 then
  1815. return branch(not getFlag(N))
  1816.  
  1817. elseif op == 0x30 then
  1818. return branch(getFlag(N))
  1819.  
  1820. elseif op == 0x50 then
  1821. return branch(not getFlag(V))
  1822.  
  1823. elseif op == 0x70 then
  1824. return branch(getFlag(V))
  1825.  
  1826. elseif op == 0x90 then
  1827. return branch(not getFlag(C))
  1828.  
  1829. elseif op == 0xB0 then
  1830. return branch(getFlag(C))
  1831.  
  1832. elseif op == 0xD0 then
  1833. return branch(not getFlag(Z))
  1834.  
  1835. elseif op == 0xF0 then
  1836. return branch(getFlag(Z))
  1837.  
  1838. -- --------------------------------------------------------------
  1839. -- JMP
  1840. -- --------------------------------------------------------------
  1841.  
  1842. elseif op == 0x4C then
  1843.  
  1844. PC = abs()
  1845.  
  1846. return 3
  1847.  
  1848. elseif op == 0x6C then
  1849.  
  1850. local ptr = abs()
  1851.  
  1852. local lo = cpuRead(ptr)
  1853.  
  1854. -- 6502 indirect JMP page-wrap bug.
  1855. local hiAddr =
  1856. (ptr & 0xFF00) |
  1857. ((ptr + 1) & 0xFF)
  1858.  
  1859. local hi =
  1860. cpuRead(hiAddr)
  1861.  
  1862. PC =
  1863. lo |
  1864. (hi << 8)
  1865.  
  1866. return 5
  1867.  
  1868. -- --------------------------------------------------------------
  1869. -- JSR
  1870. -- --------------------------------------------------------------
  1871.  
  1872. elseif op == 0x20 then
  1873.  
  1874. local target = abs()
  1875.  
  1876. local ret =
  1877. (PC - 1) & 0xFFFF
  1878.  
  1879. push((ret >> 8) & 0xFF)
  1880. push(ret & 0xFF)
  1881.  
  1882. PC = target
  1883.  
  1884. return 6
  1885.  
  1886. -- --------------------------------------------------------------
  1887. -- RTS
  1888. -- --------------------------------------------------------------
  1889.  
  1890. elseif op == 0x60 then
  1891.  
  1892. local lo = pop()
  1893. local hi = pop()
  1894.  
  1895. PC =
  1896. ((hi << 8) | lo) + 1
  1897.  
  1898. PC =
  1899. PC & 0xFFFF
  1900.  
  1901. return 6
  1902.  
  1903. -- --------------------------------------------------------------
  1904. -- Stack
  1905. -- --------------------------------------------------------------
  1906.  
  1907. elseif op == 0x48 then
  1908. push(A)
  1909. return 3
  1910.  
  1911. elseif op == 0x68 then
  1912. A = pop()
  1913. setZN(A)
  1914. return 4
  1915.  
  1916. elseif op == 0x08 then
  1917. push(P | B | U)
  1918. return 3
  1919.  
  1920. elseif op == 0x28 then
  1921. P = pop() | U
  1922. return 4
  1923.  
  1924. -- --------------------------------------------------------------
  1925. -- Flags
  1926. -- --------------------------------------------------------------
  1927.  
  1928. elseif op == 0x18 then
  1929. setFlag(C, false)
  1930. return 2
  1931.  
  1932. elseif op == 0x38 then
  1933. setFlag(C, true)
  1934. return 2
  1935.  
  1936. elseif op == 0x58 then
  1937. setFlag(I, false)
  1938. return 2
  1939.  
  1940. elseif op == 0x78 then
  1941. setFlag(I, true)
  1942. return 2
  1943.  
  1944. elseif op == 0xB8 then
  1945. setFlag(V, false)
  1946. return 2
  1947.  
  1948. elseif op == 0xD8 then
  1949. setFlag(D, false)
  1950. return 2
  1951.  
  1952. elseif op == 0xF8 then
  1953. setFlag(D, true)
  1954. return 2
  1955.  
  1956. -- --------------------------------------------------------------
  1957. -- NOP
  1958. -- --------------------------------------------------------------
  1959.  
  1960. elseif op == 0xEA then
  1961. return 2
  1962.  
  1963. -- --------------------------------------------------------------
  1964. -- BRK
  1965. -- --------------------------------------------------------------
  1966.  
  1967. elseif op == 0x00 then
  1968.  
  1969. PC =
  1970. (PC + 1) & 0xFFFF
  1971.  
  1972. push((PC >> 8) & 0xFF)
  1973. push(PC & 0xFF)
  1974.  
  1975. push(P | B | U)
  1976.  
  1977. setFlag(I, true)
  1978.  
  1979. PC =
  1980. cpuRead(0xFFFE) |
  1981. (cpuRead(0xFFFF) << 8)
  1982.  
  1983. return 7
  1984.  
  1985. -- --------------------------------------------------------------
  1986. -- RTI
  1987. -- --------------------------------------------------------------
  1988.  
  1989. elseif op == 0x40 then
  1990.  
  1991. P = pop() | U
  1992.  
  1993. local lo = pop()
  1994. local hi = pop()
  1995.  
  1996. PC =
  1997. lo |
  1998. (hi << 8)
  1999.  
  2000. return 6
  2001.  
  2002. -- --------------------------------------------------------------
  2003. -- Common unofficial NOPs
  2004. -- --------------------------------------------------------------
  2005.  
  2006. elseif
  2007. op == 0x1A or
  2008. op == 0x3A or
  2009. op == 0x5A or
  2010. op == 0x7A or
  2011. op == 0xDA or
  2012. op == 0xFA
  2013. then
  2014.  
  2015. return 2
  2016.  
  2017. elseif
  2018. op == 0x80 or
  2019. op == 0x82 or
  2020. op == 0x89 or
  2021. op == 0xC2 or
  2022. op == 0xE2
  2023. then
  2024. fetch()
  2025. return 2
  2026.  
  2027. elseif
  2028. op == 0x04 or
  2029. op == 0x44 or
  2030. op == 0x64
  2031. then
  2032. zp()
  2033. return 3
  2034.  
  2035. elseif
  2036. op == 0x14 or
  2037. op == 0x34 or
  2038. op == 0x54 or
  2039. op == 0x74 or
  2040. op == 0xD4 or
  2041. op == 0xF4
  2042. then
  2043. zpx()
  2044. return 4
  2045.  
  2046. elseif op == 0x0C then
  2047. abs()
  2048. return 4
  2049.  
  2050. elseif
  2051. op == 0x1C or
  2052. op == 0x3C or
  2053. op == 0x5C or
  2054. op == 0x7C or
  2055. op == 0xDC or
  2056. op == 0xFC
  2057. then
  2058. local _, cross = absx()
  2059. return 4 + (cross and 1 or 0)
  2060.  
  2061. else
  2062.  
  2063. halted = true
  2064.  
  2065. return 2
  2066. end
  2067. end
  2068.  
  2069. -- ================================================================
  2070. -- INTERRUPT SERVICE
  2071. -- ================================================================
  2072.  
  2073. local function serviceNMI()
  2074. if not nmiPending then
  2075. return 0
  2076. end
  2077.  
  2078. nmiPending = false
  2079.  
  2080. push((PC >> 8) & 0xFF)
  2081. push(PC & 0xFF)
  2082. push((P & ~B) | U)
  2083.  
  2084. setFlag(I, true)
  2085.  
  2086. PC =
  2087. cpuRead(0xFFFA) |
  2088. (cpuRead(0xFFFB) << 8)
  2089.  
  2090. return 7
  2091. end
  2092.  
  2093. -- ================================================================
  2094. -- NES PALETTE
  2095. -- ================================================================
  2096.  
  2097. local NES_COLORS = {
  2098. 0x545454,0x001E74,0x081090,0x300088,
  2099. 0x440064,0x5C0030,0x540400,0x3C1800,
  2100. 0x202A00,0x083A00,0x004000,0x003C00,
  2101. 0x00323C,0x000000,0x000000,0x000000,
  2102.  
  2103. 0x989698,0x084CC4,0x3032EC,0x5C1EE4,
  2104. 0x8814B0,0xA01464,0x982220,0x783C00,
  2105. 0x545A00,0x287200,0x087C00,0x007628,
  2106. 0x006678,0x000000,0x000000,0x000000,
  2107.  
  2108. 0xECEEEC,0x4C9AEC,0x787CEC,0xB062EC,
  2109. 0xE454EC,0xEC58B4,0xEC6A64,0xD48820,
  2110. 0xA0AA00,0x74C400,0x4CD020,0x38C898,
  2111. 0x38B8CC,0x3C3C3C,0x000000,0x000000,
  2112.  
  2113. 0xECEEEC,0xA8CCEC,0xBCBCEC,0xD4B2EC,
  2114. 0xECB4EC,0xECB4D4,0xECB4B0,0xE4C490,
  2115. 0xCCD48C,0xB4E494,0xA8E4A8,0xA8DCC0,
  2116. 0xA8D4D8,0xA8A8A8,0x000000,0x000000
  2117. }
  2118.  
  2119. local function colorForNES(index)
  2120. index = index & 0x3F
  2121.  
  2122. -- PPUMASK bit 0 forces the output through the grayscale entries.
  2123. if (ppuRegs[1] & 0x01) ~= 0 then
  2124. index = index & 0x30
  2125. end
  2126.  
  2127. return NES_COLORS[index + 1] or 0
  2128. end
  2129.  
  2130. -- ================================================================
  2131. -- FRAMEBUFFERS
  2132. -- ================================================================
  2133.  
  2134. -- Indexed NES palette entries.
  2135. local nesFrame = {}
  2136.  
  2137. -- True if background pixel has nonzero pattern bits.
  2138. local nesBgOpaque = {}
  2139.  
  2140. for y = 0, NES_H - 1 do
  2141.  
  2142. local row = {}
  2143. local opaque = {}
  2144.  
  2145. for x = 0, NES_W - 1 do
  2146. row[x] = 0
  2147. opaque[x] = false
  2148. end
  2149.  
  2150. nesFrame[y] = row
  2151. nesBgOpaque[y] = opaque
  2152. end
  2153.  
  2154. -- ================================================================
  2155. -- BACKGROUND RENDERING
  2156. -- ================================================================
  2157.  
  2158. local function getNametableBase(nt)
  2159. nt = nt & 3
  2160.  
  2161. if nt == 0 then
  2162. return 0x2000
  2163. elseif nt == 1 then
  2164. return 0x2400
  2165. elseif nt == 2 then
  2166. return 0x2800
  2167. else
  2168. return 0x2C00
  2169. end
  2170. end
  2171.  
  2172. local function renderBackgroundScanline(scanY)
  2173.  
  2174. local control = ppuRegs[0] or 0
  2175. local mask = ppuRegs[1] or 0
  2176.  
  2177. local row =
  2178. nesFrame[scanY]
  2179.  
  2180. local opaque =
  2181. nesBgOpaque[scanY]
  2182.  
  2183. -- --------------------------------------------------------------
  2184. -- Background disabled.
  2185. -- --------------------------------------------------------------
  2186.  
  2187. if (mask & 0x08) == 0 then
  2188.  
  2189. local universal =
  2190. ppuRead(0x3F00)
  2191.  
  2192. for x = 0, NES_W - 1 do
  2193. row[x] = universal
  2194. opaque[x] = false
  2195. end
  2196.  
  2197. return
  2198. end
  2199.  
  2200. -- --------------------------------------------------------------
  2201. -- Starting nametable from PPUCTRL.
  2202. -- --------------------------------------------------------------
  2203.  
  2204. local baseNT =
  2205. control & 3
  2206.  
  2207. -- Background pattern table.
  2208. local patternBase
  2209.  
  2210. if (control & 0x10) ~= 0 then
  2211. patternBase = 0x1000
  2212. else
  2213. patternBase = 0x0000
  2214. end
  2215.  
  2216. local scrollX =
  2217. ppuScrollX & 0xFF
  2218.  
  2219. local scrollY =
  2220. ppuScrollY & 0xFF
  2221.  
  2222. for x = 0, NES_W - 1 do
  2223.  
  2224. -- ------------------------------------------------------------
  2225. -- World coordinates.
  2226. --
  2227. -- Horizontal background space is 512 pixels.
  2228. -- Vertical background space is 480 pixels.
  2229. -- ------------------------------------------------------------
  2230.  
  2231. local worldX =
  2232. scrollX + x
  2233.  
  2234. local worldY =
  2235. scrollY + scanY
  2236.  
  2237. worldX =
  2238. worldX % 512
  2239.  
  2240. worldY =
  2241. worldY % 480
  2242.  
  2243. local ntX =
  2244. math.floor(worldX / 256)
  2245.  
  2246. local ntY =
  2247. math.floor(worldY / 240)
  2248.  
  2249. -- Determine logical nametable.
  2250. --
  2251. -- PPUCTRL selects the upper-left logical nametable.
  2252. --
  2253. -- Moving right toggles bit 0.
  2254. -- Moving down toggles bit 1.
  2255.  
  2256. local nt =
  2257. (baseNT ~
  2258. (ntX == 1 and 1 or 0) ~
  2259. (ntY == 1 and 2 or 0)) &
  2260. 3
  2261.  
  2262. local localX =
  2263. worldX % 256
  2264.  
  2265. local localY =
  2266. worldY % 240
  2267.  
  2268. local tileX =
  2269. math.floor(localX / 8)
  2270.  
  2271. local tileY =
  2272. math.floor(localY / 8)
  2273.  
  2274. local fineX =
  2275. localX & 7
  2276.  
  2277. local fineY =
  2278. localY & 7
  2279.  
  2280. local ntBase =
  2281. getNametableBase(nt)
  2282.  
  2283. -- ------------------------------------------------------------
  2284. -- Tile number.
  2285. -- ------------------------------------------------------------
  2286.  
  2287. local tileAddr =
  2288. ntBase +
  2289. tileY * 32 +
  2290. tileX
  2291.  
  2292. local tile =
  2293. ppuRead(tileAddr)
  2294.  
  2295. -- ------------------------------------------------------------
  2296. -- Pattern data.
  2297. -- ------------------------------------------------------------
  2298.  
  2299. local patternAddr =
  2300. patternBase +
  2301. tile * 16 +
  2302. fineY
  2303.  
  2304. local lo =
  2305. ppuRead(patternAddr)
  2306.  
  2307. local hi =
  2308. ppuRead(patternAddr + 8)
  2309.  
  2310. local bit =
  2311. 7 - fineX
  2312.  
  2313. local c =
  2314. ((lo >> bit) & 1) |
  2315. (((hi >> bit) & 1) << 1)
  2316.  
  2317. if x < 8 and (mask & 0x02) == 0 then
  2318. c = 0
  2319. end
  2320.  
  2321. -- ------------------------------------------------------------
  2322. -- Attribute table.
  2323. -- ------------------------------------------------------------
  2324.  
  2325. local attrAddr =
  2326. ntBase +
  2327. 0x3C0 +
  2328. math.floor(tileY / 4) * 8 +
  2329. math.floor(tileX / 4)
  2330.  
  2331. local attr =
  2332. ppuRead(attrAddr)
  2333.  
  2334. local quadrant =
  2335. ((math.floor(tileY / 2) & 1) * 2) +
  2336. (math.floor(tileX / 2) & 1)
  2337.  
  2338. local shift =
  2339. quadrant * 2
  2340.  
  2341. local pal =
  2342. (attr >> shift) & 3
  2343.  
  2344. -- ------------------------------------------------------------
  2345. -- Palette.
  2346. -- ------------------------------------------------------------
  2347.  
  2348. local paletteIndex
  2349.  
  2350. if c == 0 then
  2351. paletteIndex = 0
  2352. opaque[x] = false
  2353. else
  2354. paletteIndex =
  2355. pal * 4 + c
  2356.  
  2357. opaque[x] = true
  2358. end
  2359.  
  2360. row[x] =
  2361. ppuRead(
  2362. 0x3F00 + paletteIndex
  2363. )
  2364. end
  2365. end
  2366.  
  2367. -- ================================================================
  2368. -- SPRITE RENDERING
  2369. -- ================================================================
  2370.  
  2371. local function renderSpritesScanline(scanY)
  2372.  
  2373. local mask =
  2374. ppuRegs[1] or 0
  2375.  
  2376. -- Sprite rendering disabled.
  2377. if (mask & 0x10) == 0 then
  2378. return
  2379. end
  2380.  
  2381. local control =
  2382. ppuRegs[0] or 0
  2383.  
  2384. local spritePattern
  2385.  
  2386. if (control & 0x08) ~= 0 then
  2387. spritePattern = 0x1000
  2388. else
  2389. spritePattern = 0
  2390. end
  2391.  
  2392. local spriteHeight
  2393.  
  2394. if (control & 0x20) ~= 0 then
  2395. spriteHeight = 16
  2396. else
  2397. spriteHeight = 8
  2398. end
  2399.  
  2400. local row =
  2401. nesFrame[scanY]
  2402.  
  2403. local bgOpaque =
  2404. nesBgOpaque[scanY]
  2405.  
  2406. local spritesOnLine = 0
  2407. local sprite0Hit = false
  2408.  
  2409. -- Draw low-priority sprites first.
  2410. -- Sprite 0 is highest priority.
  2411. --
  2412. -- This also means later sprite pixels can overwrite
  2413. -- earlier sprite pixels.
  2414.  
  2415. for i = 63, 0, -1 do
  2416.  
  2417. local base =
  2418. i * 4
  2419.  
  2420. -- NES OAM Y coordinate is one less than the visible
  2421. -- sprite position.
  2422.  
  2423. local sy =
  2424. (oam[base] or 0) + 1
  2425.  
  2426. local tile =
  2427. oam[base + 1] or 0
  2428.  
  2429. local attr =
  2430. oam[base + 2] or 0
  2431.  
  2432. local sx =
  2433. oam[base + 3] or 0
  2434.  
  2435. local r =
  2436. scanY - sy
  2437.  
  2438. if r >= 0 and r < spriteHeight then
  2439.  
  2440. spritesOnLine = spritesOnLine + 1
  2441. if spritesOnLine > 8 then
  2442. ppuRegs[2] = ppuRegs[2] | 0x20
  2443. end
  2444.  
  2445. local flipH =
  2446. (attr & 0x40) ~= 0
  2447.  
  2448. local flipV =
  2449. (attr & 0x80) ~= 0
  2450.  
  2451. local priorityBehind =
  2452. (attr & 0x20) ~= 0
  2453.  
  2454. local palNum =
  2455. attr & 3
  2456.  
  2457. local sourceY
  2458.  
  2459. if flipV then
  2460. sourceY =
  2461. spriteHeight - 1 - r
  2462. else
  2463. sourceY = r
  2464. end
  2465.  
  2466. local tileIndex
  2467. local patBase
  2468.  
  2469. if spriteHeight == 16 then
  2470.  
  2471. -- 8x16 sprites:
  2472. --
  2473. -- bit 0 selects pattern table.
  2474. -- tile number bit 0 is ignored.
  2475.  
  2476. patBase =
  2477. (tile & 1) * 0x1000
  2478.  
  2479. tileIndex =
  2480. (tile & 0xFE) +
  2481. math.floor(sourceY / 8)
  2482.  
  2483. sourceY =
  2484. sourceY & 7
  2485.  
  2486. else
  2487.  
  2488. patBase =
  2489. spritePattern
  2490.  
  2491. tileIndex =
  2492. tile
  2493.  
  2494. end
  2495.  
  2496. local addr =
  2497. patBase +
  2498. tileIndex * 16 +
  2499. sourceY
  2500.  
  2501. local lo =
  2502. ppuRead(addr)
  2503.  
  2504. local hi =
  2505. ppuRead(addr + 8)
  2506.  
  2507. for xx = 0, 7 do
  2508.  
  2509. local sourceX
  2510.  
  2511. if flipH then
  2512. sourceX = xx
  2513. else
  2514. sourceX = 7 - xx
  2515. end
  2516.  
  2517. local b1 =
  2518. (lo >> sourceX) & 1
  2519.  
  2520. local b2 =
  2521. (hi >> sourceX) & 1
  2522.  
  2523. local c =
  2524. b1 | (b2 << 1)
  2525.  
  2526. -- Color zero is transparent.
  2527. if c ~= 0 then
  2528.  
  2529. local px =
  2530. sx + xx
  2531.  
  2532. if px >= 0 and px < NES_W then
  2533.  
  2534. -- Sprite 0 hit ignores sprite priority: it is a
  2535. -- collision between opaque sprite-0 and background
  2536. -- pixels. The leftmost 8 pixels are clipped according
  2537. -- to PPUMASK.
  2538. if
  2539. i == 0 and
  2540. not sprite0Hit and
  2541. bgOpaque[px] and
  2542. (px >= 8 or ((mask & 0x02) ~= 0 and (mask & 0x04) ~= 0))
  2543. then
  2544. ppuRegs[2] = ppuRegs[2] | 0x40
  2545. sprite0Hit = true
  2546. end
  2547.  
  2548. if px >= 8 or (mask & 0x04) ~= 0 then
  2549. -- Background has priority over this sprite
  2550. -- when sprite priority bit is set.
  2551.  
  2552. if
  2553. not priorityBehind or
  2554. not bgOpaque[px]
  2555. then
  2556.  
  2557. row[px] =
  2558. ppuRead(
  2559. 0x3F10 +
  2560. palNum * 4 +
  2561. c
  2562. )
  2563.  
  2564. end
  2565. end
  2566. end
  2567. end
  2568.  
  2569. end
  2570. end
  2571. end
  2572. end
  2573.  
  2574. -- ================================================================
  2575. -- FRAME RENDER
  2576. -- ================================================================
  2577.  
  2578. local function renderNESFrame()
  2579.  
  2580. for y = 0, NES_H - 1 do
  2581.  
  2582. renderBackgroundScanline(y)
  2583.  
  2584. renderSpritesScanline(y)
  2585.  
  2586. end
  2587. end
  2588.  
  2589. -- ================================================================
  2590. -- VRAM
  2591. -- ================================================================
  2592.  
  2593. local VRAM = {}
  2594.  
  2595. for y = 0, VRAM_H - 1 do
  2596.  
  2597. local row = {}
  2598.  
  2599. for x = 0, VRAM_W - 1 do
  2600. row[x] = 0
  2601. end
  2602.  
  2603. VRAM[y] = row
  2604. end
  2605.  
  2606. -- ================================================================
  2607. -- SCALE NES -> 320x200
  2608. -- ================================================================
  2609.  
  2610. local function scaleToVRAM()
  2611.  
  2612. for dy = 0, VRAM_H - 1 do
  2613.  
  2614. local ny =
  2615. math.floor(
  2616. dy * NES_H / VRAM_H
  2617. )
  2618.  
  2619. if ny >= NES_H then
  2620. ny = NES_H - 1
  2621. end
  2622.  
  2623. local srcRow =
  2624. nesFrame[ny]
  2625.  
  2626. local dstRow =
  2627. VRAM[dy]
  2628.  
  2629. for dx = 0, VRAM_W - 1 do
  2630.  
  2631. local nx =
  2632. math.floor(
  2633. dx * NES_W / VRAM_W
  2634. )
  2635.  
  2636. if nx >= NES_W then
  2637. nx = NES_W - 1
  2638. end
  2639.  
  2640. dstRow[dx] =
  2641. colorForNES(
  2642. srcRow[nx]
  2643. )
  2644. end
  2645. end
  2646. end
  2647.  
  2648. -- ================================================================
  2649. -- BRAILLE BLIT
  2650. -- ================================================================
  2651.  
  2652. local runChars = {}
  2653.  
  2654. local function blitVRAMToGPU()
  2655.  
  2656. local _gpu = gpu
  2657. local _braille = brailleChars
  2658. local _VRAM = VRAM
  2659.  
  2660. local _CW =
  2661. math.min(CW, drawW)
  2662.  
  2663. local _CH =
  2664. math.min(CH, drawH)
  2665.  
  2666. local lastBg = -1
  2667. local lastFg = -1
  2668.  
  2669. for cy = 1, _CH do
  2670.  
  2671. local vY =
  2672. (cy - 1) * 4
  2673.  
  2674. local row0 =
  2675. _VRAM[vY]
  2676.  
  2677. local row1 =
  2678. _VRAM[vY + 1]
  2679.  
  2680. local row2 =
  2681. _VRAM[vY + 2]
  2682.  
  2683. local row3 =
  2684. _VRAM[vY + 3]
  2685.  
  2686. local runStart = 0
  2687. local runBg = -1
  2688. local runFg = -1
  2689. local runLen = 0
  2690.  
  2691. for cx = 1, _CW do
  2692.  
  2693. local vX =
  2694. (cx - 1) * 2
  2695.  
  2696. local p1 = row0[vX] or 0
  2697. local p2 = row0[vX + 1] or 0
  2698. local p3 = row1[vX] or 0
  2699. local p4 = row1[vX + 1] or 0
  2700. local p5 = row2[vX] or 0
  2701. local p6 = row2[vX + 1] or 0
  2702. local p7 = row3[vX] or 0
  2703. local p8 = row3[vX + 1] or 0
  2704.  
  2705. local bg
  2706. local fg
  2707. local char
  2708.  
  2709. if
  2710. p1 == p2 and
  2711. p1 == p3 and
  2712. p1 == p4 and
  2713. p1 == p5 and
  2714. p1 == p6 and
  2715. p1 == p7 and
  2716. p1 == p8
  2717. then
  2718.  
  2719. bg = p1
  2720. fg = p1
  2721. char = " "
  2722.  
  2723. else
  2724.  
  2725. cellPix[1] = p1
  2726. cellPix[2] = p2
  2727. cellPix[3] = p3
  2728. cellPix[4] = p4
  2729. cellPix[5] = p5
  2730. cellPix[6] = p6
  2731. cellPix[7] = p7
  2732. cellPix[8] = p8
  2733.  
  2734. bg, fg =
  2735. chooseColors()
  2736.  
  2737. if fg == bg then
  2738.  
  2739. char = " "
  2740.  
  2741. else
  2742.  
  2743. local pattern = 0
  2744.  
  2745. if p1 == fg then
  2746. pattern = pattern + W1
  2747. end
  2748.  
  2749. if p2 == fg then
  2750. pattern = pattern + W2
  2751. end
  2752.  
  2753. if p3 == fg then
  2754. pattern = pattern + W3
  2755. end
  2756.  
  2757. if p4 == fg then
  2758. pattern = pattern + W4
  2759. end
  2760.  
  2761. if p5 == fg then
  2762. pattern = pattern + W5
  2763. end
  2764.  
  2765. if p6 == fg then
  2766. pattern = pattern + W6
  2767. end
  2768.  
  2769. if p7 == fg then
  2770. pattern = pattern + W7
  2771. end
  2772.  
  2773. if p8 == fg then
  2774. pattern = pattern + W8
  2775. end
  2776.  
  2777. char =
  2778. _braille[pattern]
  2779. end
  2780. end
  2781.  
  2782. if
  2783. runLen > 0 and
  2784. bg == runBg and
  2785. fg == runFg and
  2786. cx == runStart + runLen
  2787. then
  2788.  
  2789. runLen =
  2790. runLen + 1
  2791.  
  2792. runChars[runLen] =
  2793. char
  2794.  
  2795. else
  2796.  
  2797. if runLen > 0 then
  2798.  
  2799. if runBg ~= lastBg then
  2800. _gpu.setBackground(runBg)
  2801. lastBg = runBg
  2802. end
  2803.  
  2804. if runFg ~= lastFg then
  2805. _gpu.setForeground(runFg)
  2806. lastFg = runFg
  2807. end
  2808.  
  2809. _gpu.set(
  2810. runStart,
  2811. cy,
  2812. table.concat(
  2813. runChars,
  2814. "",
  2815. 1,
  2816. runLen
  2817. )
  2818. )
  2819. end
  2820.  
  2821. runStart = cx
  2822. runBg = bg
  2823. runFg = fg
  2824. runLen = 1
  2825.  
  2826. runChars[1] = char
  2827. end
  2828. end
  2829.  
  2830. if runLen > 0 then
  2831.  
  2832. if runBg ~= lastBg then
  2833. _gpu.setBackground(runBg)
  2834. lastBg = runBg
  2835. end
  2836.  
  2837. if runFg ~= lastFg then
  2838. _gpu.setForeground(runFg)
  2839. lastFg = runFg
  2840. end
  2841.  
  2842. _gpu.set(
  2843. runStart,
  2844. cy,
  2845. table.concat(
  2846. runChars,
  2847. "",
  2848. 1,
  2849. runLen
  2850. )
  2851. )
  2852. end
  2853. end
  2854. end
  2855.  
  2856. -- ================================================================
  2857. -- SPRITE 0 TIMING HELPER
  2858. -- ================================================================
  2859.  
  2860. local function spriteOverflowOnScanline(scanY)
  2861. if (ppuRegs[1] & 0x10) == 0 then
  2862. return false
  2863. end
  2864.  
  2865. local height = (ppuRegs[0] & 0x20) ~= 0 and 16 or 8
  2866. local count = 0
  2867.  
  2868. for i = 0, 63 do
  2869. local sy = (oam[i * 4] or 0) + 1
  2870. if scanY >= sy and scanY < sy + height then
  2871. count = count + 1
  2872. if count > 8 then
  2873. return true
  2874. end
  2875. end
  2876. end
  2877.  
  2878. return false
  2879. end
  2880.  
  2881. local function sprite0HitX(scanY)
  2882.  
  2883. if (ppuRegs[1] & 0x18) ~= 0x18 then
  2884. return nil
  2885. end
  2886.  
  2887. local y = (oam[0] or 0) + 1
  2888. local tile = oam[1] or 0
  2889. local attr = oam[2] or 0
  2890. local sx = oam[3] or 0
  2891.  
  2892. local control = ppuRegs[0] or 0
  2893. local height = (control & 0x20) ~= 0 and 16 or 8
  2894. local row = scanY - y
  2895.  
  2896. if row < 0 or row >= height then
  2897. return nil
  2898. end
  2899.  
  2900. if (scanY < 0 or scanY >= NES_H) then
  2901. return nil
  2902. end
  2903.  
  2904. if (attr & 0x80) ~= 0 then
  2905. row = height - 1 - row
  2906. end
  2907.  
  2908. local patBase
  2909. local tileIndex
  2910.  
  2911. if height == 16 then
  2912. patBase = (tile & 1) * 0x1000
  2913. tileIndex = (tile & 0xFE) + math.floor(row / 8)
  2914. row = row & 7
  2915. else
  2916. patBase = (control & 0x08) ~= 0 and 0x1000 or 0
  2917. tileIndex = tile
  2918. end
  2919.  
  2920. local addr = patBase + tileIndex * 16 + row
  2921. local lo = ppuRead(addr)
  2922. local hi = ppuRead(addr + 8)
  2923.  
  2924. local scrollX = ppuScrollX & 0xFF
  2925. local scrollY = ppuScrollY & 0xFF
  2926. local baseNT = control & 3
  2927.  
  2928. for xx = 0, 7 do
  2929. local sourceX = (attr & 0x40) ~= 0 and xx or (7 - xx)
  2930. local c = ((lo >> sourceX) & 1) | (((hi >> sourceX) & 1) << 1)
  2931.  
  2932. if c ~= 0 then
  2933. local px = sx + xx
  2934. if px >= 0 and px < NES_W then
  2935. if px >= 8 or ((ppuRegs[1] & 0x06) == 0x06) then
  2936. local worldX = (scrollX + px) % 512
  2937. local worldY = (scrollY + scanY) % 480
  2938. local ntX = math.floor(worldX / 256)
  2939. local ntY = math.floor(worldY / 240)
  2940. local nt = (baseNT ~ (ntX == 1 and 1 or 0) ~ (ntY == 1 and 2 or 0)) & 3
  2941. local localX = worldX % 256
  2942. local localY = worldY % 240
  2943. local tileX = math.floor(localX / 8)
  2944. local tileY = math.floor(localY / 8)
  2945. local fineX = localX & 7
  2946. local fineY = localY & 7
  2947. local ntBase = 0x2000 + nt * 0x400
  2948. local tileNo = ppuRead(ntBase + tileY * 32 + tileX)
  2949. local bgBase = (control & 0x10) ~= 0 and 0x1000 or 0
  2950. local bgAddr = bgBase + tileNo * 16 + fineY
  2951. local bgLo = ppuRead(bgAddr)
  2952. local bgHi = ppuRead(bgAddr + 8)
  2953. local bit = 7 - fineX
  2954. local bgc = ((bgLo >> bit) & 1) | (((bgHi >> bit) & 1) << 1)
  2955.  
  2956. if bgc ~= 0 then
  2957. return px
  2958. end
  2959. end
  2960. end
  2961. end
  2962. end
  2963.  
  2964. return nil
  2965. end
  2966.  
  2967. -- ================================================================
  2968. -- PPU TIMING
  2969. -- ================================================================
  2970.  
  2971. local function triggerNMI()
  2972.  
  2973. if (ppuRegs[0] & 0x80) ~= 0 then
  2974. nmiPending = true
  2975. end
  2976. end
  2977.  
  2978. local function ppuStep(ppuCycles)
  2979.  
  2980. for _ = 1, ppuCycles do
  2981.  
  2982. -- NTSC has one skipped dot on odd frames when rendering is
  2983. -- enabled. This is what produces the ~60.0988 Hz cadence.
  2984. if
  2985. ppuScanline == 261 and
  2986. ppuCycle == 339 and
  2987. (ppuRegs[1] & 0x18) ~= 0 and
  2988. (ppuFrame & 1) ~= 0
  2989. then
  2990. ppuCycle = 0
  2991. ppuScanline = 0
  2992. ppuFrame = ppuFrame + 1
  2993. ppuRegs[2] = ppuRegs[2] & 0x7F
  2994. else
  2995. ppuCycle = ppuCycle + 1
  2996.  
  2997. -- Sprite 0 hit is latched during visible rendering. This
  2998. -- computes the first opaque overlap for the current scanline
  2999. -- and exposes it at the corresponding pixel dot.
  3000. if ppuScanline < 240 and ppuCycle == 1 then
  3001. if spriteOverflowOnScanline(ppuScanline) then
  3002. ppuRegs[2] = ppuRegs[2] | 0x20
  3003. end
  3004. end
  3005.  
  3006. if
  3007. ppuScanline < 240 and
  3008. ppuCycle >= 1 and
  3009. ppuCycle <= 256 and
  3010. (ppuRegs[2] & 0x40) == 0
  3011. then
  3012. local hitX = sprite0HitX(ppuScanline)
  3013. if hitX ~= nil and ppuCycle == hitX + 1 then
  3014. ppuRegs[2] = ppuRegs[2] | 0x40
  3015. end
  3016. end
  3017.  
  3018. -- VBlank starts at scanline 241, dot 1.
  3019. if ppuScanline == 241 and ppuCycle == 1 then
  3020. ppuRegs[2] = ppuRegs[2] | 0x80
  3021. nmiOccurredThisVBlank = false
  3022. triggerNMI()
  3023. end
  3024.  
  3025. -- VBlank ends at dot 1 of pre-render.
  3026. if ppuScanline == 261 and ppuCycle == 1 then
  3027. ppuRegs[2] = ppuRegs[2] & 0x9F
  3028. nmiOccurredThisVBlank = false
  3029. end
  3030.  
  3031. if ppuCycle >= PPU_CYCLES_PER_SCANLINE then
  3032. ppuCycle = 0
  3033. ppuScanline = ppuScanline + 1
  3034.  
  3035. if ppuScanline >= PPU_SCANLINES then
  3036. ppuScanline = 0
  3037. ppuFrame = ppuFrame + 1
  3038. ppuRegs[2] = ppuRegs[2] & 0x7F
  3039. end
  3040. end
  3041. end
  3042. end
  3043. end
  3044.  
  3045. -- ================================================================
  3046. -- INPUT
  3047. -- ================================================================
  3048.  
  3049. local running = true
  3050.  
  3051. local function keyDown(code)
  3052.  
  3053. if code == keyboard.keys.q then
  3054. running = false
  3055.  
  3056. elseif code == keyboard.keys.w then
  3057. buttons.UP = true
  3058.  
  3059. elseif code == keyboard.keys.s then
  3060. buttons.DOWN = true
  3061.  
  3062. elseif code == keyboard.keys.a then
  3063. buttons.LEFT = true
  3064.  
  3065. elseif code == keyboard.keys.d then
  3066. buttons.RIGHT = true
  3067.  
  3068. elseif code == keyboard.keys.j then
  3069. buttons.A = true
  3070.  
  3071. elseif code == keyboard.keys.k then
  3072. buttons.B = true
  3073.  
  3074. elseif code == keyboard.keys.space then
  3075. buttons.SELECT = true
  3076.  
  3077. elseif code == keyboard.keys.enter then
  3078. buttons.START = true
  3079. end
  3080. end
  3081.  
  3082. local function keyUp(code)
  3083.  
  3084. if code == keyboard.keys.w then
  3085. buttons.UP = false
  3086.  
  3087. elseif code == keyboard.keys.s then
  3088. buttons.DOWN = false
  3089.  
  3090. elseif code == keyboard.keys.a then
  3091. buttons.LEFT = false
  3092.  
  3093. elseif code == keyboard.keys.d then
  3094. buttons.RIGHT = false
  3095.  
  3096. elseif code == keyboard.keys.j then
  3097. buttons.A = false
  3098.  
  3099. elseif code == keyboard.keys.k then
  3100. buttons.B = false
  3101.  
  3102. elseif code == keyboard.keys.space then
  3103. buttons.SELECT = false
  3104.  
  3105. elseif code == keyboard.keys.enter then
  3106. buttons.START = false
  3107. end
  3108. end
  3109.  
  3110. local function pollEvents()
  3111.  
  3112. while true do
  3113.  
  3114. local ev =
  3115. {event.pull(0)}
  3116.  
  3117. if not ev[1] then
  3118. break
  3119. end
  3120.  
  3121. if ev[1] == "key_down" then
  3122.  
  3123. -- ev[3] is char, ev[4] is the scancode from keyboard.keys
  3124. keyDown(ev[4])
  3125.  
  3126. elseif ev[1] == "key_up" then
  3127.  
  3128. keyUp(ev[4])
  3129.  
  3130. end
  3131. end
  3132. end
  3133.  
  3134. -- ================================================================
  3135. -- PPU RESET
  3136. -- ================================================================
  3137.  
  3138. local function ppuReset()
  3139.  
  3140. for i = 0, 7 do
  3141. ppuRegs[i] = 0
  3142. end
  3143.  
  3144. ppuAddr = 0
  3145. ppuAddrLatch = false
  3146.  
  3147. ppuDataBuffer = 0
  3148.  
  3149. ppuScrollLatch = false
  3150. ppuScrollX = 0
  3151. ppuScrollY = 0
  3152.  
  3153. oamAddr = 0
  3154.  
  3155. ppuCycle = 0
  3156. ppuScanline = 0
  3157. ppuFrame = 0
  3158.  
  3159. nmiOccurredThisVBlank = false
  3160. nmiPending = false
  3161. oamDmaPending = false
  3162. end
  3163.  
  3164. -- ================================================================
  3165. -- RESET
  3166. -- ================================================================
  3167.  
  3168. ppuReset()
  3169. cpuReset()
  3170.  
  3171. -- ================================================================
  3172. -- TITLE
  3173. -- ================================================================
  3174.  
  3175. gpu.setBackground(0x000000)
  3176. gpu.setForeground(0xFFFFFF)
  3177.  
  3178. gpu.fill(
  3179. 1,
  3180. 1,
  3181. drawW,
  3182. drawH,
  3183. " "
  3184. )
  3185.  
  3186. local title =
  3187. "OC-NES PPU / NROM / BRAILLE"
  3188.  
  3189. if drawW >= #title then
  3190. gpu.set(
  3191. 1,
  3192. 1,
  3193. title
  3194. )
  3195. end
  3196.  
  3197. computer.pullSignal(0.5)
  3198.  
  3199. -- ================================================================
  3200. -- MAIN LOOP
  3201. -- ================================================================
  3202.  
  3203. local nextFrame =
  3204. computer.uptime()
  3205.  
  3206. while running do
  3207.  
  3208. pollEvents()
  3209.  
  3210. -- --------------------------------------------------------------
  3211. -- Run one NES CPU frame.
  3212. --
  3213. -- Every CPU cycle advances the PPU by 3 cycles.
  3214. -- This is much better than waiting until the end of the frame
  3215. -- before setting VBlank/NMI.
  3216. -- --------------------------------------------------------------
  3217.  
  3218. local used = 0
  3219. local frameTarget =
  3220. CPU_CYCLES_FRAME + ((ppuFrame & 1) == 0 and CPU_FRAME_ALTERNATE or 0)
  3221.  
  3222. while
  3223. used < frameTarget and
  3224. running
  3225. do
  3226.  
  3227. local cycles = cpuStep()
  3228.  
  3229. used = used + cycles
  3230. cpuTotalCycles = cpuTotalCycles + cycles
  3231.  
  3232. ppuStep(cycles * 3)
  3233.  
  3234. if oamDmaPending then
  3235. oamDmaPending = false
  3236.  
  3237. -- The DMA is 513 cycles, plus one alignment cycle when the
  3238. -- transfer begins on the opposite CPU phase.
  3239. local dmaCycles =
  3240. 513 + (cpuTotalCycles & 1)
  3241.  
  3242. used = used + dmaCycles
  3243. cpuTotalCycles = cpuTotalCycles + dmaCycles
  3244. ppuStep(dmaCycles * 3)
  3245. end
  3246.  
  3247. if nmiPending then
  3248. local nmiCycles = serviceNMI()
  3249. used = used + nmiCycles
  3250. cpuTotalCycles = cpuTotalCycles + nmiCycles
  3251. ppuStep(nmiCycles * 3)
  3252. end
  3253. end
  3254.  
  3255. -- --------------------------------------------------------------
  3256. -- Render the completed frame.
  3257. -- --------------------------------------------------------------
  3258.  
  3259. renderNESFrame()
  3260.  
  3261. scaleToVRAM()
  3262.  
  3263. blitVRAMToGPU()
  3264.  
  3265. -- --------------------------------------------------------------
  3266. -- Frame pacing.
  3267. -- --------------------------------------------------------------
  3268.  
  3269. nextFrame =
  3270. nextFrame + FRAME_TIME
  3271.  
  3272. local now =
  3273. computer.uptime()
  3274.  
  3275. local wait =
  3276. nextFrame - now
  3277.  
  3278. if wait > 0 then
  3279.  
  3280. computer.pullSignal(wait)
  3281.  
  3282. else
  3283.  
  3284. nextFrame = now
  3285.  
  3286. end
  3287. end
  3288.  
  3289. -- ================================================================
  3290. -- EXIT
  3291. -- ================================================================
  3292.  
  3293. gpu.setBackground(0x000000)
  3294. gpu.setForeground(0xFFFFFF)
  3295.  
  3296. gpu.fill(
  3297. 1,
  3298. 1,
  3299. drawW,
  3300. drawH,
  3301. " "
  3302. )
  3303.  
  3304. gpu.set(
  3305. 1,
  3306. 1,
  3307. "NES EMULATOR EXITED"
  3308. )
  3309.  
  3310. computer.pullSignal(0.25)
Advertisement
Add Comment
Please, Sign In to add comment