' DRR-NES v0.30
' A DRR Software Production

' Copyright (c) 1998, 1999 DRR
' All rights reserved.

CONST DRR.Software.Version$ = "v0.30"
CONST DRR.Software.Build$ = "build 0.30.0028 (99-02-27) -- delta 1"





























TYPE x86Registers
  AX AS INTEGER: BX AS INTEGER: CX AS INTEGER: DX AS INTEGER
  BP AS INTEGER: SI AS INTEGER: DI AS INTEGER: Flags AS INTEGER
  DS AS INTEGER: ES AS INTEGER
END TYPE
DECLARE SUB ByeBye ()
DECLARE SUB Core6502 ()
DECLARE SUB DrawBitmap (BitmapStyle%, FileName$, ColorsUsed%, BitmapX%, BitmapY%)
DECLARE SUB Debugger ()
DECLARE SUB DrawNESscreen ()
DECLARE SUB DrawObject (WhichObject%, x1%, y1%, x2%, y2%)
DECLARE SUB DrawScrolling (NameTableAddress%, Hscroll%, Vscroll%)
DECLARE SUB DrawText (TextFont%, TextToType$, TextX%, TextY%, TextColor%)
DECLARE SUB InGameControls ()
DECLARE SUB InitializeDRRNES ()
DECLARE SUB InitializeMouse ()
DECLARE SUB InitializeScreen (WhichMode%)
DECLARE SUB InitializeXMS ()
DECLARE SUB InterruptX (InterruptNumber%, InRegisters AS x86Registers, OutRegisters AS x86Registers)
DECLARE SUB LoadCHRROM (SourceBank%, DestinationBank%, LoadSize%)
DECLARE SUB LoadPRGROM (SourceBank%, DestinationBank%)
DECLARE SUB LoadROM (ROMFileName$)
DECLARE FUNCTION MemoryRead% (Address&)
DECLARE SUB MemoryWrite (Address&, Value%)
DECLARE SUB MouseDriver (AX%, BX%, CX%, DX%)
DECLARE FUNCTION Pop% ()
DECLARE SUB Push (Value%)
DECLARE FUNCTION TextBox$ (Column%, Row%, Length%)
DECLARE SUB UpdateProgressBar ()
DECLARE SUB WaitVblank (Waits%)

'6502 variables
DIM SHARED RegisterA AS INTEGER, RegisterP AS INTEGER, RegisterPC AS LONG
DIM SHARED RegisterS AS INTEGER, RegisterX AS INTEGER, RegisterY AS INTEGER
DIM SHARED Cycles AS LONG, Instruction AS STRING * 3, SkipLoop AS INTEGER
DIM SHARED Opcode AS INTEGER, Argument1 AS INTEGER, Argument2 AS INTEGER
DIM SHARED AbsoluteOpcode AS INTEGER, AddressingMode AS INTEGER
'memory variables
DIM SHARED PatternTables(8191) AS INTEGER, NameTables(4095) AS INTEGER
DIM SHARED RAM(2047) AS INTEGER, SpriteRAM(255) AS INTEGER
DIM SHARED MemoryRegisters(31) AS LONG
DIM SHARED BackgroundPalette(15) AS INTEGER, SpritePalette(15) AS INTEGER
REDIM SHARED ROM(18431) AS INTEGER
'PPU variables
DIM SHARED HorizontalScroll AS INTEGER, VerticalScroll AS INTEGER
DIM SHARED Frameskip AS INTEGER, SkippedFrames AS INTEGER
DIM SHARED Vblank AS INTEGER, VideoPage AS INTEGER
DIM SHARED AttributeTable(15, 15) AS INTEGER, Tile(15) AS INTEGER
DIM SHARED FPScounter AS INTEGER, ProgramTimer(1) AS SINGLE
DIM SHARED Mirroring(3) AS INTEGER
'ROM variables
DIM SHARED CHRROMbanks AS INTEGER, PRGROMbanks AS INTEGER, NESFileSize AS LONG
DIM SHARED Flags1 AS INTEGER, Flags2 AS INTEGER, OriginalMirroring AS INTEGER
DIM SHARED NESROMname AS STRING, Trainer AS INTEGER
'mapper variables
DIM SHARED Mapper AS INTEGER, CurrentPRGROMbank(1) AS INTEGER
DIM SHARED CurrentCHRROMbank(7) AS INTEGER, WhichMapperRegister AS INTEGER
DIM SHARED PRGROMswitching AS INTEGER
DIM SHARED MapperRegister AS INTEGER, MapperRegisterBit AS INTEGER
DIM SHARED OldPRGROMbank(1) AS INTEGER, CHRROMswitching AS INTEGER
'debugger variables
DIM SHARED DebuggerFlag AS INTEGER, DebuggerCount AS INTEGER, Frames AS LONG
DIM SHARED MemoryContents(5, 1) AS LONG, OldRegisterPC AS LONG
DIM SHARED PaletteCycler AS SINGLE, DisassemblyText AS STRING * 59
DIM SHARED DisasmFile AS STRING, DisasmStatus AS LONG
DIM SHARED DisasmPosition AS LONG, ExecuteCPU AS INTEGER
DIM SHARED OldDisasmPosition AS LONG, GraphicScroll(240, 16) AS INTEGER
'other variables
DIM SHARED Controller(1 TO 2) AS INTEGER
DIM SHARED Font0 AS STRING * 4096, Font1 AS STRING * 4096
DIM SHARED Byte AS STRING * 1, PowerOfTwo(7) AS INTEGER, VideoMode AS INTEGER
DIM SHARED ExtendedKeyPressed AS INTEGER
'secret variables
DIM SHARED CoolMessage AS INTEGER, CoolMessageText AS STRING
DIM SHARED CoolMessageText2 AS STRING, CoolMessageText3 AS STRING
DIM SHARED FlipColors AS INTEGER, DisplayVersion AS INTEGER
'mouse variables
DIM SHARED Mouse(10, 18) AS INTEGER
DIM SHARED MouseBackground(10, 18) AS INTEGER
DIM SHARED MouseMask(10, 18) AS INTEGER
DIM SHARED MouseX AS INTEGER, MouseY AS INTEGER
DIM SHARED OldMouseX AS INTEGER, OldMouseY AS INTEGER
DIM SHARED MouseButtons AS INTEGER, OldMouseButtons AS INTEGER
DIM SHARED NoMouse AS INTEGER

CONST FrameCycles% = 29829, VblankCycles% = 26414

'CALL InitializeXMS: END
ON ERROR GOTO ErrorHandler
RANDOMIZE TIMER: ExecuteCPU = 1
FOR TwoPower% = 0 TO 7: PowerOfTwo(TwoPower%) = 2 ^ TwoPower%: NEXT
InitializeScreen 12: InitializeDRRNES: InitializeMouse

DrawBitmap 2, "DRR-NES.BMP", 11, 0, 0
DrawText 1, DRR.Software.Version, 0, 464, 10
DrawText 0, "DRR-NES is copyright (c) 1998, 1999 DRR. All rights reserved.", 152, 464, 10
DrawText 1, "For the latest information, visit the official DRR-NES page on DRR's Dimension @", 0, 432, 4
DrawText 1, "-------------------------- http://drr.dragonfire.net --------------------------", 4, 448, 4
WaitVblank 20
FOR PaletteFlash% = 3 TO 5
  OUT 967, PaletteFlash%
  Red% = INP(969): Green% = INP(969): Blue% = INP(969)
  OUT 968, PaletteFlash%
  OUT 969, 63: OUT 969, 63: OUT 969, 56
  WaitVblank 6
  OUT 968, PaletteFlash%
  OUT 969, Red%: OUT 969, Green%: OUT 969, Blue%
NEXT PaletteFlash%
WaitVblank 30

LINE (56, 240)-(583, 303), 13, BF
DrawObject 40, 54, 236, 587, 307
DrawText 1, "Open an NES ROM image...", 64, 241, 0
DrawText 1, "Open an NES ROM image...", 65, 240, 0
DrawText 1, "Open an NES ROM image...", 65, 241, 0
DrawText 1, "Open an NES ROM image...", 64, 240, 8
DrawText 1, "Enter *.NES ROM filename (including path)....", 80, 254, 0

FileName$ = LTRIM$(RTRIM$(UCASE$(TextBox$(80, 272, 60))))
LINE (72, 256)-(567, 287), 13, BF
DrawObject 21, 78, 270, 561, 289
DrawText 1, STRING$(60, "°"), 80, 272, 10
ByteCounter% = LEN(FileName$)
IF ByteCounter% = 0 THEN ERROR 253
DO
  ByteCounter% = ByteCounter% - 1
  IF MID$(FileName$, ByteCounter%, 1) = "\" OR ByteCounter% = 1 THEN EXIT DO
LOOP
NESROMname = RIGHT$(FileName$, LEN(FileName$) - ByteCounter%)
LoadROM FileName$
LINE (10, 320)-(209, 399), 13, BF
LINE (230, 320)-(429, 399), 13, BF
DrawObject 40, 10, 320, 209, 399
DrawObject 40, 230, 320, 429, 399
DrawText 1, "Controller 1", 18, 328, 0
DrawText 1, CHR$(24), 42, 344, 0
DrawText 1, CHR$(27) + "   " + CHR$(26) + "    V  B    N  M", 26, 360, 0
DrawText 1, CHR$(25), 42, 376, 0
DrawText 1, "Controller 2", 238, 328, 0
DrawText 1, "W", 262, 344, 0
DrawText 1, "A   D    1  2    3  4", 246, 360, 0
DrawText 1, "S", 262, 376, 0
DrawText 1, "ÓÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄKeyboard ControlsÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ½", 10, 408, 10
DrawText 0, "Press any key to start...", 440, 320, 4
DrawText 1, "[F1] toggles between the", 440, 336, 10
DrawText 1, "debugger/NES screen", 440, 352, 10
DrawText 1, "[F2] toggles the FPS", 440, 368, 10
DrawText 1, "counter & messages", 440, 384, 10
DrawText 1, "[Esc] exits", 440, 400, 10
DO
  PressedButton$ = UCASE$(INKEY$)
  SELECT CASE PressedButton$
    CASE CHR$(0) + CHR$(&H3B): DebuggerFlag = -1: FPScounter = NOT FPScounter: EXIT DO
    CASE CHR$(0) + CHR$(&H3C): EXIT DO
    CASE CHR$(27): ByeBye
    CASE ""
    CASE ELSE: FPScounter = NOT FPScounter: EXIT DO
  END SELECT
LOOP

IF DebuggerFlag = 0 THEN InitializeScreen 14
Core6502

ErrorHandler:
  SELECT CASE ERR
  CASE 52, 53, 75, 253
    DrawText 0, "Invalid filename, terminating program...", 80, 254, 0
    SLEEP: SCREEN 0: WIDTH 80, 25: SYSTEM
  CASE 250
    InitializeScreen 12
    COLOR 5: PRINT "DRR"; : COLOR 3: PRINT "NES": COLOR 14
    PRINT "Copyright (c) 1998, 1999 DRR -- All rights reserved.": PRINT
    COLOR 1: PRINT "**** An invalid opcode was executed. ****"
    PRINT NESROMname + " attempted to execute the invalid opcode " + HEX$(Opcode) + "."
    PRINT "Press any key to exit DRR-NES."
    SLEEP: SCREEN 0: WIDTH 80, 25: SYSTEM
  CASE 254
    InitializeScreen 12
    COLOR 5: PRINT "DRR"; : COLOR 3: PRINT "NES": COLOR 14
    PRINT "Copyright (c) 1998, 1999 DRR -- All rights reserved.": PRINT
    COLOR 1: PRINT "**** One or more of the DRR-NES files is missing. ****"
    PRINT "Make sure that you have ALL of the DRR-NES files in the same directory."
    PRINT "DRR-NES will now exit so that you can fix the problem."
    SLEEP: SCREEN 0: WIDTH 80, 25: SYSTEM
  CASE 76, 255
    CLS : COLOR 5: PRINT "DRR"; : COLOR 3: PRINT "NES": COLOR 14
    PRINT "Copyright (c) 1998, 1999 DRR -- All rights reserved.": PRINT
    COLOR 1: PRINT "**** The directory path now listed in C:\DRR-NES.DAT is either incorrect, or"
    PRINT "some/all of the files necessary to run DRR-NES are missing. *****"
    COLOR 15: PRINT : INPUT "Type the drive/directory path to this program or type EXIT to exit: ", Path$
    Path$ = LTRIM$(RTRIM$(UCASE$(Path$)))
    IF RIGHT$(FileName$, 1) = "\" THEN FileName$ = LEFT$(FileName$, LEN(FileName$) - 1)
    IF Path$ = "EXIT" THEN SYSTEM
    PUT #1, 2, Path$
    RESUME NEXT
  CASE ELSE
    SCREEN 0: WIDTH 80, 25
    PRINT "Warning: An error"; ERR; "occured. Program terminated."
    END
  END SELECT

FadeColors: DATA 8, 1032, 2056, 1024, 524288, 524294, 2048, 525570, 327680, 132102, 516, 131586, 263172, 394758, 526344
PaletteColors: DATA 0, 63, 8255, 16191, 8192, 4128768, 4128816, 16128, 4138509, 2621440, 1056816, 4128, 1052688, 2105376, 3158064, 4144959

MousePicture:
  DATA 00,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  DATA 12,00,-1,-1,-1,-1,-1,-1,-1,-1,-1
  DATA 12,14,00,-1,-1,-1,-1,-1,-1,-1,-1
  DATA 12,14,14,00,-1,-1,-1,-1,-1,-1,-1
  DATA 12,15,14,14,00,-1,-1,-1,-1,-1,-1
  DATA 12,15,14,14,14,00,-1,-1,-1,-1,-1
  DATA 12,15,15,14,14,14,00,-1,-1,-1,-1
  DATA 12,15,15,14,14,14,14,00,-1,-1,-1
  DATA 12,15,15,15,14,14,14,14,00,-1,-1
  DATA 12,15,15,15,14,14,14,14,14,00,-1
  DATA 12,15,15,15,00,00,00,00,00,00,00
  DATA 12,15,15,00,15,14,00,-1,-1,-1,-1
  DATA 12,15,00,-1,12,15,14,00,-1,-1,-1
  DATA 12,00,-1,-1,12,15,14,00,-1,-1,-1
  DATA 00,-1,-1,-1,-1,12,15,14,00,-1,-1
  DATA -1,-1,-1,-1,-1,12,15,14,00,-1,-1
  DATA -1,-1,-1,-1,-1,-1,12,15,14,00,-1
  DATA -1,-1,-1,-1,-1,-1,12,14,14,00,-1
  DATA -1,-1,-1,-1,-1,-1,-1,00,00,00,-1

NESpalette:
  DATA 117,117,117, 039,027,143, 000,000,171, 071,000,159
  DATA 143,000,159, 171,000,019, 167,000,000, 127,011,000
  DATA 067,047,000, 000,071,000, 000,081,000, 000,063,023
  DATA 027,063,095, 000,000,000, 000,000,000, 000,000,000

  DATA 188,188,188, 000,115,239, 035,059,239, 131,000,243
  DATA 191,000,191, 231,000,091, 219,043,000, 203,079,015
  DATA 139,115,000, 000,151,000, 000,171,000, 000,147,059
  DATA 000,131,139, 000,000,000, 000,000,000, 000,000,000

  DATA 255,255,255, 063,191,255, 095,151,255, 167,139,253
  DATA 247,123,255, 255,119,183, 255,119,099, 255,155,059
  DATA 243,191,063, 131,211,019, 079,223,075, 088,248,152
  DATA 000,235,219, 000,000,000, 000,000,000, 000,000,000

  DATA 255,255,255, 171,231,255, 199,215,255, 215,203,255
  DATA 255,199,255, 255,199,219, 255,191,179, 255,219,171
  DATA 255,231,163, 227,255,163, 171,243,191, 179,255,207
  DATA 159,255,243, 000,000,000, 000,000,000, 000,000,000

  DATA 000,000,000,255,000,000,000,255,000,000,000,255
  DATA 051,153,255,000,000,153

SUB ByeBye
  CLOSE #1
  IF DisasmStatus THEN CLOSE #2: KILL "Disasm.dis"
  SCREEN 0: WIDTH 80, 25: SYSTEM
END SUB

SUB Core6502
  DEF SEG = VARSEG(ROM(0))
  RegisterPC = PEEK(VARPTR(ROM(0)) + 36861) * 256& + PEEK(VARPTR(ROM(0)) + 36860)
  RegisterP = 32: RegisterS = 255: MemoryRegisters(2) = 64: Cycles = 7
  ProgramTimer(0) = TIMER
  DO
    OldRegisterPC = RegisterPC
    IF Cycles >= FrameCycles THEN
      SkipLoop = 0
      IF DebuggerFlag = 2 THEN DebuggerFlag = 1
      MemoryRegisters(2) = MemoryRegisters(2) AND 127: Vblank = NOT Vblank
      MemoryRegisters(2) = MemoryRegisters(2) OR 64
      Frames = Frames + 1
      DrawNESscreen
      IF DebuggerFlag = 0 THEN InGameControls
      Cycles = Cycles - FrameCycles + 7
      IF SkippedFrames = 0 THEN ProgramTimer(0) = TIMER
    ELSEIF Vblank = 0 AND Cycles >= VblankCycles THEN
      SkipLoop = 0
      IF DebuggerFlag = 2 THEN DebuggerFlag = 1
      MemoryRegisters(2) = MemoryRegisters(2) OR 128: Vblank = NOT Vblank
      MemoryRegisters(2) = MemoryRegisters(2) AND 191
      IF MemoryRegisters(0) AND 128 THEN
        Push RegisterPC \ 256
        Push RegisterPC AND 255
        Push RegisterP
        DEF SEG = VARSEG(ROM(0))
        RegisterPC = PEEK(VARPTR(ROM(0)) + 36859) * 256& + PEEK(VARPTR(ROM(0)) + 36858)
        Cycles = Cycles + 7
      END IF
    END IF
    DEF SEG = VARSEG(ROM(0))
    Opcode = PEEK(VARPTR(ROM(0)) + RegisterPC - 28672)
    Argument1 = PEEK(VARPTR(ROM(0)) + RegisterPC - 28671)
    Argument2 = PEEK(VARPTR(ROM(0)) + RegisterPC - 28670)
    AbsoluteOpcode = Opcode
    AddressingMode = Opcode AND 31
    SELECT CASE AddressingMode
      CASE 0 'immed.
        AddressingMode = 35
        SELECT CASE Opcode
          CASE &H0 'brk
            Push RegisterPC \ 256
            Push RegisterPC AND 255
            RegisterP = RegisterP OR 16
            Push RegisterP
            DEF SEG = VARSEG(ROM(0))
            RegisterPC = PEEK(VARPTR(ROM(0)) + 36863) * 256& + PEEK(VARPTR(ROM(0)) + 36862)
            RegisterPC = RegisterPC + 1
            RegisterP = RegisterP OR 4
            Cycles = Cycles + 7
          CASE &H20 'jsr abs.
            RegisterPC = RegisterPC + 2
            Push RegisterPC \ 256
            Push RegisterPC AND 255
            RegisterPC = Argument2 * 256& + Argument1
            Cycles = Cycles + 6
            AddressingMode = 32
          CASE &H40 'rti imp.
            RegisterP = Pop% AND 239
            RegisterPC = Pop%
            RegisterPC = RegisterPC + Pop% * 256&
            Cycles = Cycles + 6
          CASE &H60 'rts imp.
            RegisterPC = Pop% + 1
            RegisterPC = RegisterPC + Pop% * 256&
            Cycles = Cycles + 6
          CASE &H80
            ERROR 250
          CASE ELSE
            AddressingMode = 0
            MemoryAddress& = -1
            AbsoluteOpcode = Opcode + 12
            RegisterPC = RegisterPC + 2
        END SELECT
      CASE 1 '(zero pg., x)
        MemoryAddress& = Argument1 + RegisterX AND 255
        MemoryAddress& = MemoryRead%((MemoryAddress& + 1) AND 255) * 256& + MemoryRead%(MemoryAddress&)
        AbsoluteOpcode = Opcode + 12: Cycles = Cycles + 4: RegisterPC = RegisterPC + 2
      CASE 2 'immed.
        IF Opcode = &HA2 THEN
          MemoryAddress& = -1
          AbsoluteOpcode = Opcode + 12: RegisterPC = RegisterPC + 2
        ELSE
          ERROR 250
        END IF
      CASE 4 TO 6 'zero pg.
        MemoryAddress& = Argument1
        AbsoluteOpcode = Opcode + 8: Cycles = Cycles + 1: RegisterPC = RegisterPC + 2
      CASE 8
        SELECT CASE Opcode
          CASE &H8 'php imp.
            Push RegisterP OR 32: Cycles = Cycles + 3
          CASE &H28 'plp imp.
            RegisterP = Pop%: Cycles = Cycles + 4
          CASE &H48 'pha imp.
            Push RegisterA: Cycles = Cycles + 3
          CASE &H68 'pla imp.
            RegisterA = Pop%: Cycles = Cycles + 4
          CASE &H88 'dey imp.
            RegisterY = RegisterY - 1 AND 255
            IF RegisterY THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
            IF RegisterY AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
            Cycles = Cycles + 2
          CASE &HA8 'tay imp.
            RegisterY = RegisterA
            IF RegisterY THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
            IF RegisterY AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
            Cycles = Cycles + 2
          CASE &HC8 'iny imp.
            RegisterY = RegisterY + 1 AND 255
            IF RegisterY THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
            IF RegisterY AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
            Cycles = Cycles + 2
          CASE &HE8 'inx imp.
            RegisterX = RegisterX + 1 AND 255
            IF RegisterX THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
            IF RegisterX AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
            Cycles = Cycles + 2
        END SELECT
        RegisterPC = RegisterPC + 1
      CASE 9 'immed.
        MemoryAddress& = -1
        AbsoluteOpcode = Opcode + 4: RegisterPC = RegisterPC + 2
      CASE 10 'accum.
        SELECT CASE Opcode
          CASE &H8A 'txa imp.
            RegisterA = RegisterX
            IF RegisterA THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
            IF RegisterA AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
            Cycles = Cycles + 2: RegisterPC = RegisterPC + 1
          CASE &HAA 'tax imp.
            RegisterX = RegisterA
            IF RegisterX THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
            IF RegisterX AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
            Cycles = Cycles + 2: RegisterPC = RegisterPC + 1
          CASE &HCA 'dex imp.
            RegisterX = RegisterX - 1 AND 255
            IF RegisterX THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
            IF RegisterX AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
            Cycles = Cycles + 2: RegisterPC = RegisterPC + 1
          CASE &HEA 'nop imp.
            Cycles = Cycles + 2: RegisterPC = RegisterPC + 1
          CASE ELSE
            AddressingMode = 10
            MemoryAddress& = -2
            AbsoluteOpcode = Opcode + 4
            Cycles = Cycles - 2
            RegisterPC = RegisterPC + 1
        END SELECT
        AddressingMode = 36
      CASE 12 TO 14 'abs.
        MemoryAddress& = Argument2 * 256& + Argument1
        AbsoluteOpcode = Opcode: Cycles = Cycles + 2: RegisterPC = RegisterPC + 3
      CASE 16 'rel.
        IF Argument1 AND 128 THEN Argument1 = Argument1 - 256
        SELECT CASE Opcode
          CASE &H10 'bpl rel.
            IF SkipLoop = 1 AND Argument1 = -5 THEN
              IF Vblank = 0 THEN Cycles = VblankCycles ELSE Cycles = FrameCycles
              SkipLoop = -1
            END IF
            IF (RegisterP AND 128) = 0 THEN RegisterPC = RegisterPC + Argument1
          CASE &H30 'bmi rel.
            IF RegisterP AND 128 THEN RegisterPC = RegisterPC + Argument1
          CASE &H50 'bvc rel.
            IF (RegisterP AND 64) = 0 THEN RegisterPC = RegisterPC + Argument1
          CASE &H70 'bvs rel.
            IF RegisterP AND 64 THEN RegisterPC = RegisterPC + Argument1
          CASE &H90 'bcc rel.
            IF (RegisterP AND 1) = 0 THEN RegisterPC = RegisterPC + Argument1
          CASE &HB0 'bcs rel.
            IF RegisterP AND 1 THEN RegisterPC = RegisterPC + Argument1
          CASE &HD0 'bne rel.
            IF (RegisterP AND 2) = 0 THEN RegisterPC = RegisterPC + Argument1
          CASE &HF0 'beq rel.
            IF RegisterP AND 2 THEN RegisterPC = RegisterPC + Argument1
        END SELECT
        AbsoluteOpcode = Opcode: Cycles = Cycles + 2: RegisterPC = RegisterPC + 2
      CASE 17 '(zero pg.), y
        MemoryAddress& = MemoryRead%(Argument1 + 1) * 256& + MemoryRead%(Argument1 + 0) + RegisterY
        AbsoluteOpcode = Opcode - 4: Cycles = Cycles + 3: RegisterPC = RegisterPC + 2
      CASE 20 TO 22 'zero pg., x or zero pg., y
        IF Opcode = &H96 OR Opcode = &HB6 THEN
          MemoryAddress& = Argument1 + RegisterY AND 255
        ELSE
          MemoryAddress& = Argument1 + RegisterX AND 255
        END IF
        AbsoluteOpcode = Opcode - 8: Cycles = Cycles + 2: RegisterPC = RegisterPC + 2
      CASE 24
        SELECT CASE Opcode
          CASE &H18 'clc imp.
            RegisterP = RegisterP AND 254
          CASE &H38 'sec imp.
            RegisterP = RegisterP OR 1
          CASE &H58 'cli imp.
            RegisterP = RegisterP AND 251
          CASE &H78 'sei imp.
            RegisterP = RegisterP OR 4
          CASE &H98 'tya imp.
            RegisterA = RegisterY
            IF RegisterA THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
            IF RegisterA AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
          CASE &HB8 'clv imp.
            RegisterP = RegisterP AND 191
          CASE &HD8 'cld imp.
            RegisterP = RegisterP AND 247
          CASE &HF8 'sed imp.
            RegisterP = RegisterP OR 8
        END SELECT
        Cycles = Cycles + 2: RegisterPC = RegisterPC + 1
      CASE 25 'abs., y
        MemoryAddress& = Argument1 + Argument2 * 256& + RegisterY
        AbsoluteOpcode = Opcode - 12: Cycles = Cycles + 2: RegisterPC = RegisterPC + 3
      CASE 26 'imp.
        SELECT CASE Opcode
          CASE &H9A 'txs imp.
            RegisterS = RegisterX
            Cycles = Cycles + 2: RegisterPC = RegisterPC + 1
          CASE &HBA 'tsx imp.
            RegisterX = RegisterS
            IF RegisterX THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
            IF RegisterX AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
            Cycles = Cycles + 2: RegisterPC = RegisterPC + 1
          CASE ELSE
            ERROR 250
        END SELECT
      CASE 28 TO 30 'abs., x
        IF Opcode = &HBE THEN
          MemoryAddress& = Argument1 + Argument2 * 256& + RegisterY
        ELSE
          MemoryAddress& = Argument1 + Argument2 * 256& + RegisterX
        END IF
        AbsoluteOpcode = Opcode - 16: Cycles = Cycles + 2: RegisterPC = RegisterPC + 3
      CASE 3, 7, 11, 15, 19, 23, 27, 31
        ERROR 250
    END SELECT
    SELECT CASE AbsoluteOpcode
      CASE &HC
        ERROR 250
      CASE &HD 'ora
        RegisterA = RegisterA OR MemoryRead%(MemoryAddress&)
        IF RegisterA THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF RegisterA AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 2
      CASE &HE 'asl
        MemoryData% = MemoryRead%(MemoryAddress&)
        IF MemoryData% AND 128 THEN RegisterP = RegisterP OR 1 ELSE RegisterP = RegisterP AND 254
        MemoryData% = MemoryData% * 2 AND 255
        MemoryWrite MemoryAddress&, MemoryData%
        IF MemoryData% THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF MemoryData% AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 4
      CASE &H2C 'bit
        MemoryData% = MemoryRead%(MemoryAddress&)
        RegisterP = (RegisterP AND 61) OR (MemoryData% AND 192)
        IF (RegisterA AND MemoryData%) = 0 THEN RegisterP = RegisterP OR 2
        Cycles = Cycles + 2
      CASE &H2D 'and
        RegisterA = RegisterA AND MemoryRead%(MemoryAddress&)
        IF RegisterA THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF RegisterA AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 2
      CASE &H2E 'rol
        MemoryData% = MemoryRead%(MemoryAddress&) * 2 + (RegisterP AND 1)
        IF MemoryData% AND 256 THEN RegisterP = RegisterP OR 1 ELSE RegisterP = RegisterP AND 254
        MemoryData% = MemoryData% AND 255
        MemoryWrite MemoryAddress&, MemoryData%
        IF MemoryData% THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF MemoryData% AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 4
      CASE &H4C 'jmp
        IF MemoryAddress& = RegisterPC - 3 THEN
          IF Vblank = 0 THEN Cycles = VblankCycles ELSE Cycles = FrameCycles
          SkipLoop = -1
        END IF
        RegisterPC = MemoryAddress&
        Cycles = Cycles + 1
      CASE &H4D 'eor
        RegisterA = RegisterA XOR MemoryRead%(MemoryAddress&)
        IF RegisterA THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF RegisterA AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 2
      CASE &H4E 'lsr
        MemoryData% = MemoryRead%(MemoryAddress&)
        IF MemoryData% AND 1 THEN RegisterP = RegisterP OR 1 ELSE RegisterP = RegisterP AND 254
        MemoryData% = MemoryData% \ 2
        MemoryWrite MemoryAddress&, MemoryData%
        IF MemoryData% THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        Cycles = Cycles + 4
      CASE &H6C 'jmp ind. (&H6C or &H7C)
        MemoryData% = ((MemoryAddress& AND 255) + 1 AND 255) + MemoryAddress& AND 65280
        RegisterPC = MemoryRead%(MemoryAddress& + 1) * 256& + MemoryRead%(MemoryAddress&)
        Cycles = Cycles + 3
        IF Opcode = &H6C THEN AddressingMode = 33 ELSE AddressingMode = 34
      CASE &H6D 'adc
        MemoryData% = MemoryRead%(MemoryAddress&)
        UnsignedA% = RegisterA + MemoryData% + (RegisterP AND 1)
        IF RegisterA AND 128 THEN RegisterA = RegisterA - 256
        IF MemoryData% AND 128 THEN MemoryData% = MemoryData% - 256
        RegisterA = RegisterA + MemoryData% + (RegisterP AND 1)
        IF RegisterA < -128 OR RegisterA > 127 THEN RegisterP = RegisterP OR 64 ELSE RegisterP = RegisterP AND 191
        IF UnsignedA% < 256 THEN RegisterP = RegisterP AND 254 ELSE RegisterP = RegisterP OR 1
        RegisterA = UnsignedA% AND 255
        IF RegisterA THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF RegisterA AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 2
      CASE &H6E 'ror
        MemoryData% = MemoryRead%(MemoryAddress&) + (RegisterP AND 1) * 256
        IF MemoryData% AND 1 THEN RegisterP = RegisterP OR 1 ELSE RegisterP = RegisterP AND 254
        MemoryData% = MemoryData% \ 2
        MemoryWrite MemoryAddress&, MemoryData%
        IF MemoryData% THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF MemoryData% AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 4
      CASE &H8C 'sty
        MemoryWrite MemoryAddress&, RegisterY
        Cycles = Cycles + 2
      CASE &H8D 'sta
        MemoryWrite MemoryAddress&, RegisterA
        Cycles = Cycles + 2
      CASE &H8E 'stx
        MemoryWrite MemoryAddress&, RegisterX
        Cycles = Cycles + 2
      CASE &HAC 'ldy
        RegisterY = MemoryRead%(MemoryAddress&)
        IF RegisterY THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF RegisterY AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 2
      CASE &HAD 'lda
        IF Argument1 = 2 AND Argument2 = 32 THEN SkipLoop = 1
        RegisterA = MemoryRead%(MemoryAddress&)
        IF RegisterA THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF RegisterA AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 2
      CASE &HAE 'ldx
        IF Argument1 = 2 AND Argument2 = 32 THEN SkipLoop = 1
        RegisterX = MemoryRead%(MemoryAddress&)
        IF RegisterX THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF RegisterX AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 2
      CASE &HCC 'cpy
        MemoryData% = RegisterY - MemoryRead%(MemoryAddress&)
        IF MemoryData% >= 0 THEN RegisterP = RegisterP OR 1 ELSE RegisterP = RegisterP AND 254
        MemoryData% = MemoryData% AND 255
        IF MemoryData% THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF MemoryData% AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 2
      CASE &HCD 'cmp
        MemoryData% = RegisterA - MemoryRead%(MemoryAddress&)
        IF MemoryData% >= 0 THEN RegisterP = RegisterP OR 1 ELSE RegisterP = RegisterP AND 254
        MemoryData% = MemoryData% AND 255
        IF MemoryData% THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF MemoryData% AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 2
      CASE &HCE 'dec
        MemoryData% = MemoryRead%(MemoryAddress&) - 1 AND 255
        MemoryWrite MemoryAddress&, MemoryData%
        IF MemoryData% THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF MemoryData% AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 4
      CASE &HEC 'cpx
        MemoryData% = RegisterX - MemoryRead%(MemoryAddress&)
        IF MemoryData% >= 0 THEN RegisterP = RegisterP OR 1 ELSE RegisterP = RegisterP AND 254
        MemoryData% = MemoryData% AND 255
        IF MemoryData% THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF MemoryData% AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 2
      CASE &HED 'sbc
        MemoryData% = MemoryRead%(MemoryAddress&)
        UnsignedA% = RegisterA - MemoryData% - (RegisterP AND 1 XOR 1)
        IF RegisterA AND 128 THEN RegisterA = RegisterA - 256
        IF MemoryData% AND 128 THEN MemoryData% = MemoryData% - 256
        RegisterA = RegisterA - MemoryData% - (RegisterP AND 1 XOR 1)
        IF RegisterA < -128 OR RegisterA > 127 THEN RegisterP = RegisterP OR 64 ELSE RegisterP = RegisterP AND 191
        IF UnsignedA% < 0 THEN RegisterP = RegisterP AND 254 ELSE RegisterP = RegisterP OR 1
        RegisterA = UnsignedA% AND 255
        IF RegisterA THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF RegisterA AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 2
      CASE &HEE 'inc
        MemoryData% = MemoryRead%(MemoryAddress&) + 1 AND 255
        MemoryWrite MemoryAddress&, MemoryData%
        IF MemoryData% THEN RegisterP = RegisterP AND 253 ELSE RegisterP = RegisterP OR 2
        IF MemoryData% AND 128 THEN RegisterP = RegisterP OR 128 ELSE RegisterP = RegisterP AND 127
        Cycles = Cycles + 4
    END SELECT
    IF DebuggerFlag = 3 THEN
      DebuggerCount = DebuggerCount - 1
      IF DebuggerCount = 0 THEN DebuggerFlag = 1
    END IF
    IF DebuggerFlag < 0 OR DebuggerFlag = 1 THEN Debugger
  LOOP
END SUB

SUB Debugger
MouseDriver 7, 0, 0, 629: MouseDriver 8, 0, 0, 461
DO
'***** if necessary, initialize the screen mode & debugger display ***********
  IF DebuggerFlag = -1 THEN
    InitializeScreen 12
    DrawBitmap 1, "DRR-NES.bmp", 11, 320, 344
    DrawText 0, "DRR-NES v0.30 -- Copyright (c) 1998, 1999 DRR. All rights reserved.", 0, 464, 10
    DrawObject 0, 0, 0, 267, 463
    DrawObject 20, 246, 24, 261, 39
    DrawObject 20, 246, 40, 261, 423
    DrawObject 20, 246, 424, 261, 439
    'DrawText 1, "[F1] switches to the main screen.", 0, 292, 15
    'DrawText 1, "[F2] executes until Vblank changes.", 0, 308, 15
    'DrawText 1, "[F3] executes 100 instructions.", 0, 324, 14
    'DrawText 1, "[F4]-[F9] switch memory addresses.", 0, 340, 14
    'DrawText 1, "[F10] views the ROM statistics.", 0, 356, 13
    'DrawText 1, "The controller keys are toggled.", 0, 372, 13
    'DrawText 1, "[Esc] quits the program.", 0, 388, 10
    'DrawText 1, "Any other key continues tracing.", 0, 404, 4
    FlipColors = 0
    IF DisasmStatus = 0 THEN
      OPEN "Disasm.dis" FOR BINARY AS #2: CLOSE #2
      KILL "Disasm.dis"
      OPEN "Disasm.dis" FOR BINARY AS #2
      DisasmText$ = "DRR-NES disassembly of " + LEFT$(NESROMname + SPACE$(12), 12) + CHR$(13) + CHR$(10) + CHR$(13) + CHR$(10) + "Address/Contents Instruction      A   X   Y   S   P" + CHR$(13) + CHR$(10)
      PUT #2, 1, DisasmText$
      OldDisasmPosition = -1
    END IF
    MouseX = 320: MouseY = 240
    OldMouseX = 320: OldMouseY = 240
  END IF

'***** if necessary, redraw some of the windows ******************************
  IF DebuggerFlag = -2 OR DebuggerFlag = -1 THEN
    LINE (288, 0)-(639, 331), 0, BF
    DrawObject 40, 288, 0, 639, 79
    DrawObject 40, 288, 100, 639, 179
    DrawObject 40, 288, 200, 639, 263
    DrawObject 40, 288, 284, 639, 331
    LINE (292, 4)-(635, 75), 13, BF
    LINE (292, 104)-(635, 175), 13, BF
    LINE (292, 204)-(635, 259), 13, BF
    LINE (292, 288)-(635, 327), 13, BF
    DrawText 0, "Disassembly of " + NESROMname, 6, 4, 0
    DrawText 0, "see DRR-NES.TXT for instructions", 6, 444, 0
    DrawText 0, "NES 6502 CPU", 296, 8, 0
    DrawText 0, "NES PPU", 296, 108, 0
    DrawText 0, "Memory Contents", 296, 208, 0
    DrawText 0, "Controller Status", 296, 292, 0
    DrawText 1, "(buttons are toggled)", 464, 292, 0
    DrawText 1, "[F4]-[F9] change addresses", 424, 208, 0
    DebuggerFlag = 1
  END IF

'***** clear the windows *****************************************************
  LINE (248, 42)-(259, 421), 13, BF
  DrawText 1, CHR$(24), 250, 24, 0
  DrawText 1, CHR$(25), 250, 424, 0
  IF DisasmStatus - DisasmPosition THEN
    BoxPosition! = 364 * (1 - ((DisasmStatus - DisasmPosition) / DisasmStatus))
  ELSE
    BoxPosition! = 364
  END IF
  BoxPosition! = INT(BoxPosition!)
  DrawObject 20, 248, BoxPosition! + 42, 259, BoxPosition! + 57
  LINE (292, 224)-(635, 259), 13, BF
  LINE (292, 308)-(635, 327), 13, BF
  IF ExecuteCPU THEN
    LINE (292, 24)-(635, 75), 13, BF
    LINE (292, 124)-(635, 175), 13, BF
    DrawText 1, "$    :", 296, 24, 0
    DrawText 1, "A:     X:     Y:     S:", 296, 40, 0
    DrawText 1, "P: ________  PC:       Cycles:", 296, 56, 0
    DrawText 1, "Frames:           $2000: ________", 296, 124, 0
    DrawText 1, "$2001: ________  $2002: ________", 296, 140, 0
    DrawText 1, "SPR-RAM: $    Scroll:    /     VRAM: $", 296, 156, 0
  END IF
  DrawText 1, "p1:", 296, 308, 0: DrawText 1, "p2:", 472, 308, 0
  DisasmText$ = SPACE$(58) + CHR$(13) + CHR$(10)
'***** part 1 of displaying the current opcode/instruction *******************
  IF ExecuteCPU THEN
  SELECT CASE AbsoluteOpcode
    CASE &H0: OpcodeText$ = "BRK"
    CASE &H8: OpcodeText$ = "PHP"
    CASE &HD: OpcodeText$ = "ORA"
    CASE &HE: OpcodeText$ = "ASL"
    CASE &H10: OpcodeText$ = "BPL"
    CASE &H18: OpcodeText$ = "CLC"
    CASE &H20: OpcodeText$ = "JSR"
    CASE &H28: OpcodeText$ = "PLP"
    CASE &H2C: OpcodeText$ = "BIT"
    CASE &H2D: OpcodeText$ = "AND"
    CASE &H2E: OpcodeText$ = "ROL"
    CASE &H30: OpcodeText$ = "BMI"
    CASE &H38: OpcodeText$ = "SEC"
    CASE &H40: OpcodeText$ = "RTI"
    CASE &H48: OpcodeText$ = "PHA"
    CASE &H4C: OpcodeText$ = "JMP"
    CASE &H4D: OpcodeText$ = "EOR"
    CASE &H4E: OpcodeText$ = "LSR"
    CASE &H50: OpcodeText$ = "BVC"
    CASE &H58: OpcodeText$ = "CLI"
    CASE &H60: OpcodeText$ = "RTS"
    CASE &H68: OpcodeText$ = "PLA"
    CASE &H6C: OpcodeText$ = "JMP"
    CASE &H6D: OpcodeText$ = "ADC"
    CASE &H6E: OpcodeText$ = "ROR"
    CASE &H70: OpcodeText$ = "BVS"
    CASE &H78: OpcodeText$ = "SEI"
    CASE &H7C: OpcodeText$ = "JMP"
    CASE &H88: OpcodeText$ = "DEY"
    CASE &H8A: OpcodeText$ = "TXA"
    CASE &H8C: OpcodeText$ = "STY"
    CASE &H8D: OpcodeText$ = "STA"
    CASE &H8E: OpcodeText$ = "STX"
    CASE &H90: OpcodeText$ = "BCC"
    CASE &H98: OpcodeText$ = "TYA"
    CASE &H9A: OpcodeText$ = "TXS"
    CASE &HA8: OpcodeText$ = "TAY"
    CASE &HAA: OpcodeText$ = "TAX"
    CASE &HAC: OpcodeText$ = "LDY"
    CASE &HAD: OpcodeText$ = "LDA"
    CASE &HAE: OpcodeText$ = "LDX"
    CASE &HB0: OpcodeText$ = "BCS"
    CASE &HB8: OpcodeText$ = "CLV"
    CASE &HBA: OpcodeText$ = "TSX"
    CASE &HC8: OpcodeText$ = "INY"
    CASE &HCA: OpcodeText$ = "DEX"
    CASE &HCC: OpcodeText$ = "CPY"
    CASE &HCD: OpcodeText$ = "CMP"
    CASE &HCE: OpcodeText$ = "DEC"
    CASE &HD0: OpcodeText$ = "BNE"
    CASE &HD8: OpcodeText$ = "CLD"
    CASE &HE8: OpcodeText$ = "INX"
    CASE &HEA: OpcodeText$ = "NOP"
    CASE &HEC: OpcodeText$ = "CPX"
    CASE &HED: OpcodeText$ = "SBC"
    CASE &HEE: OpcodeText$ = "INC"
    CASE &HF0: OpcodeText$ = "BEQ"
    CASE &HF8: OpcodeText$ = "SED"
  END SELECT

'***** part 2 of displaying the current opcode/instruction *******************
  SELECT CASE AddressingMode
    CASE 0, 2, 9 'immed.
      OpcodeText$ = OpcodeText$ + " #$" + RIGHT$("0" + HEX$(Argument1), 2)
      OpcodeBytes% = 2
    CASE 1 '(zero pg., x)
      OpcodeText$ = OpcodeText$ + " ($" + RIGHT$("0" + HEX$(Argument1), 2) + ", X)"
      OpcodeBytes% = 2
    CASE 4 TO 6 'zero pg.
      OpcodeText$ = OpcodeText$ + " $" + RIGHT$("0" + HEX$(Argument1), 2)
      OpcodeBytes% = 2
    CASE 8, 24, 35 'imp.
      OpcodeBytes% = 1
    CASE 10 'accum.
      OpcodeText$ = OpcodeText$ + " A"
      OpcodeBytes% = 1
    CASE 12 TO 14, 32 'abs.
      OpcodeText$ = OpcodeText$ + " $" + RIGHT$("0" + HEX$(Argument2), 2) + RIGHT$("0" + HEX$(Argument1), 2)
      OpcodeBytes% = 3
    CASE 16 'rel.
      IF Argument1 < 0 THEN
        OpcodeText$ = OpcodeText$ + " -$"
      ELSE
        OpcodeText$ = OpcodeText$ + " +$"
      END IF
      OpcodeText$ = OpcodeText$ + RIGHT$("0" + HEX$(ABS(Argument1)), 2)
      OpcodeBytes% = 2
    CASE 17 '(zero pg.), y
      OpcodeText$ = OpcodeText$ + " ($" + RIGHT$("0" + HEX$(Argument1), 2) + "), Y"
      OpcodeBytes% = 2
    CASE 20 TO 22 'zero pg., x or zero pg., y
      IF Opcode = &H96 OR Opcode = &HB6 THEN
        OpcodeText$ = OpcodeText$ + " $" + RIGHT$("0" + HEX$(Argument1), 2) + ", Y"
      ELSE
        OpcodeText$ = OpcodeText$ + " $" + RIGHT$("0" + HEX$(Argument1), 2) + ", X"
      END IF
      OpcodeBytes% = 2
    CASE 25 'abs., y
      OpcodeText$ = OpcodeText$ + " $" + RIGHT$("0" + HEX$(Argument2), 2) + RIGHT$("0" + HEX$(Argument1), 2) + ", Y"
      OpcodeBytes% = 3
    CASE 28 TO 30 'abs., x or abs., y
      IF Opcode = &HBE THEN
        OpcodeText$ = OpcodeText$ + " $" + RIGHT$("0" + HEX$(Argument2), 2) + RIGHT$("0" + HEX$(Argument1), 2) + ", Y"
      ELSE
        OpcodeText$ = OpcodeText$ + " $" + RIGHT$("0" + HEX$(Argument2), 2) + RIGHT$("0" + HEX$(Argument1), 2) + ", X"
      END IF
      OpcodeBytes% = 3
    CASE 33 '(abs.)
      OpcodeText$ = OpcodeText$ + " ($" + RIGHT$("0" + HEX$(Argument2), 2) + RIGHT$("0" + HEX$(Argument1), 2) + ")"
      OpcodeBytes% = 3
    CASE 34 '(abs., x)
      OpcodeText$ = OpcodeText$ + " ($" + RIGHT$("0" + HEX$(Argument2), 2) + RIGHT$("0" + HEX$(Argument1), 2) + ", X)"
      OpcodeBytes% = 3
  END SELECT
  MID$(DisasmText$, 1, 6) = "$" + RIGHT$("000" + HEX$(OldRegisterPC), 4) + ":"
  MID$(DisasmText$, 18, 14) = OpcodeText$
  DrawText 1, MID$(DisasmText$, 2, 4), 304, 24, 0
  DrawText 1, OpcodeText$, 432, 24, 0
  MID$(DisasmText$, 8, 2) = RIGHT$("0" + HEX$(Opcode), 2)
  IF OpcodeBytes% >= 2 THEN MID$(DisasmText$, 11, 2) = RIGHT$("0" + HEX$(Argument1), 2)
  IF OpcodeBytes% = 3 THEN MID$(DisasmText$, 14, 2) = RIGHT$("0" + HEX$(Argument2), 2)
  DrawText 1, MID$(DisasmText$, 8, 8), 352, 24, 0

'***** the CPU registers *****************************************************
  MID$(DisasmText$, 35, 14) = RIGHT$("0" + HEX$(RegisterA), 2) + "  " + RIGHT$("0" + HEX$(RegisterX), 2) + "  " + RIGHT$("0" + HEX$(RegisterY), 2) + "  " + RIGHT$("0" + HEX$(RegisterS), 2)
  DrawText 1, MID$(DisasmText$, 35, 2), 320, 40, 0
  DrawText 1, MID$(DisasmText$, 39, 2), 376, 40, 0
  DrawText 1, MID$(DisasmText$, 43, 2), 432, 40, 0
  DrawText 1, MID$(DisasmText$, 47, 2), 488, 40, 0
  DrawText 1, RIGHT$("000" + HEX$(RegisterPC), 4), 432, 56, 0
  MID$(DisasmText$, 51, 8) = "--------"
  IF RegisterP AND 128 THEN MID$(DisasmText$, 51, 1) = "N"
  IF RegisterP AND 64 THEN MID$(DisasmText$, 52, 1) = "V"
  MID$(DisasmText$, 53, 1) = "1"
  IF RegisterP AND 16 THEN MID$(DisasmText$, 54, 1) = "B"
  IF RegisterP AND 8 THEN MID$(DisasmText$, 55, 1) = "D"
  IF RegisterP AND 4 THEN MID$(DisasmText$, 56, 1) = "I"
  IF RegisterP AND 2 THEN MID$(DisasmText$, 57, 1) = "Z"
  IF RegisterP AND 1 THEN MID$(DisasmText$, 58, 1) = "C"
  DrawText 1, MID$(DisasmText$, 51, 8), 320, 56, 0

'***** PPU registers and other miscellaneous stuff **************************
  DrawText 1, STR$(Cycles), 536, 56, 0
  DrawText 1, STR$(Frames), 352, 124, 0
  FOR PowerCount% = 7 TO 0 STEP -1
    IF MemoryRegisters(0) AND PowerOfTwo(PowerCount%) THEN DrawText 1, "X", 496 + 8 * (7 - PowerCount%), 124, 0
  NEXT PowerCount%
  FOR PowerCount% = 7 TO 0 STEP -1
    IF MemoryRegisters(1) AND PowerOfTwo(PowerCount%) THEN DrawText 1, "X", 352 + 8 * (7 - PowerCount%), 140, 0
  NEXT PowerCount%
  FOR PowerCount% = 7 TO 0 STEP -1
    IF MemoryRegisters(2) AND PowerOfTwo(PowerCount%) THEN DrawText 1, "X", 488 + 8 * (7 - PowerCount%), 140, 0
  NEXT PowerCount%
  DrawText 1, RIGHT$("0" + HEX$(MemoryRegisters(3)), 2), 376, 156, 0
  DrawText 1, RIGHT$("00" + RIGHT$(STR$(HorizontalScroll), LEN(STR$(HorizontalScroll)) - 1), 3), 472, 156, 0
  DrawText 1, RIGHT$("00" + RIGHT$(STR$(VerticalScroll), LEN(STR$(VerticalScroll)) - 1), 3), 504, 156, 0
  DrawText 1, RIGHT$("000" + HEX$(MemoryRegisters(6)), 4), 600, 156, 0
  END IF

'***** memory contents window ************************************************
  FOR WhichMC% = 0 TO 5
    SELECT CASE MemoryContents(WhichMC%, 0)
      CASE 1
        SELECT CASE MemoryContents(WhichMC%, 1)
          CASE -32768 TO -1
            DEF SEG = VARSEG(ROM(0))
            Value% = PEEK(VARPTR(ROM(0)) + MemoryContents(WhichMC%, 1) + 36864)
          CASE 0 TO 8191
            Value% = RAM(MemoryContents(WhichMC%, 1) AND 2047)
          CASE 28672 TO 32767
            DEF SEG = VARSEG(ROM(0))
            Value% = PEEK(VARPTR(ROM(0)) + MemoryContents(WhichMC%, 1) - 28672)
        END SELECT
        DrawText 1, "cpu$" + RIGHT$("000" + HEX$(MemoryContents(WhichMC%, 1)), 4) + ":" + RIGHT$("0" + HEX$(Value%), 2), 296 + ((WhichMC% MOD 3) * 8 * 14), 224 + ((WhichMC% \ 3) * 16), 0
      CASE 2
        SELECT CASE MemoryContents(WhichMC%, 1)
          CASE 0 TO 8191
            Value% = PatternTables(MemoryContents(WhichMC%, 1))
          CASE 8192 TO 12287
            Value% = NameTables(MemoryContents(WhichMC%, 1) - 8192)
          CASE 16128 TO 16143
            Value% = BackgroundPalette(MemoryContents(WhichMC%, 1) AND 15)
          CASE 16144 TO 16159
            Value% = SpritePalette(MemoryContents(WhichMC%, 1) AND 15)
        END SELECT
        DrawText 1, "ppu$" + RIGHT$("000" + HEX$(MemoryContents(WhichMC%, 1)), 4) + ":" + RIGHT$("0" + HEX$(Value%), 2), 296 + ((WhichMC% MOD 3) * 8 * 14), 224 + ((WhichMC% \ 3) * 16), 0
      CASE 3
        Value% = SpriteRAM(MemoryContents(WhichMC%, 1))
        DrawText 1, "spr$" + RIGHT$("0" + HEX$(MemoryContents(WhichMC%, 1)), 2) + ":" + RIGHT$("0" + HEX$(Value%), 2), 296 + ((WhichMC% MOD 3) * 8 * 14), 224 + ((WhichMC% \ 3) * 16), 0
    END SELECT
  NEXT WhichMC%
  '***** the controllers *******************************************************
  IF Controller(1) AND 16 THEN DrawText 1, CHR$(24), 320, 308, 0
  IF Controller(1) AND 64 THEN DrawText 1, CHR$(27), 328, 308, 0
  IF Controller(1) AND 128 THEN DrawText 1, CHR$(26), 336, 308, 0
  IF Controller(1) AND 32 THEN DrawText 1, CHR$(25), 344, 308, 0
  IF Controller(1) AND 4 THEN DrawText 1, "Sel", 360, 308, 0
  IF Controller(1) AND 8 THEN DrawText 1, "Str", 392, 308, 0
  IF Controller(1) AND 2 THEN DrawText 1, "B", 424, 308, 0
  IF Controller(1) AND 1 THEN DrawText 1, "A", 440, 308, 0
  IF Controller(2) AND 16 THEN DrawText 1, CHR$(24), 504, 308, 0
  IF Controller(2) AND 64 THEN DrawText 1, CHR$(27), 512, 308, 0
  IF Controller(2) AND 128 THEN DrawText 1, CHR$(26), 520, 308, 0
  IF Controller(2) AND 32 THEN DrawText 1, CHR$(25), 528, 308, 0
  IF Controller(2) AND 4 THEN DrawText 1, "Sel", 544, 308, 0
  IF Controller(2) AND 8 THEN DrawText 1, "Str", 576, 308, 0
  IF Controller(2) AND 2 THEN DrawText 1, "B", 608, 308, 0
  IF Controller(2) AND 1 THEN DrawText 1, "A", 624, 308, 0
 
'***** draw the disassembly window *******************************************
  PUT #2, DisasmStatus * 60 + 93, DisasmText$
  IF OldDisasmPosition = DisasmPosition - 1 THEN
    FOR Disassembly% = 1 TO 25
      GET (6, Disassembly% * 16 + 24)-(245, Disassembly% * 16 + 39), GraphicScroll
      PUT (6, Disassembly% * 16 + 8), GraphicScroll, PSET
    NEXT Disassembly%
    GET #2, DisasmPosition * 60 + 93, DisassemblyText
    IF LEFT$(DisassemblyText, 1) = "*" THEN DisassemblyText = SPACE$(31)
    LINE (6, 424)-(245, 439), 13, BF
    DrawText 1, MID$(DisassemblyText, 1, 6) + MID$(DisassemblyText, 8, 24), 6, 424, 0
  ELSEIF OldDisasmPosition = DisasmPosition + 1 THEN
    FOR Disassembly% = 24 TO 0 STEP -1
      GET (6, Disassembly% * 16 + 24)-(245, Disassembly% * 16 + 39), GraphicScroll
      PUT (6, Disassembly% * 16 + 40), GraphicScroll, PSET
    NEXT Disassembly%
    LINE (6, 24)-(245, 39), 13, BF
    IF DisasmPosition - 25 >= 0 THEN
      GET #2, (DisasmPosition - 25) * 60 + 93, DisassemblyText
      IF LEFT$(DisassemblyText, 1) = "*" THEN DisassemblyText = SPACE$(31)
      DrawText 1, MID$(DisassemblyText, 1, 6) + MID$(DisassemblyText, 8, 24), 6, 24, 0
    END IF
  ELSEIF OldDisasmPosition = DisasmPosition THEN
    GET #2, DisasmPosition * 60 + 93, DisassemblyText
    IF LEFT$(DisassemblyText, 1) = "*" THEN DisassemblyText = SPACE$(31)
    LINE (6, 424)-(245, 439), 13, BF
    DrawText 1, MID$(DisassemblyText, 1, 6) + MID$(DisassemblyText, 8, 24), 6, 424, 0
  ELSE
    LINE (6, 24)-(245, 439), 13, BF
    FOR Disassembly% = 0 TO 25
      IF DisasmPosition - Disassembly% >= 0 THEN
        GET #2, (DisasmPosition - Disassembly%) * 60 + 93, DisassemblyText
        IF LEFT$(DisassemblyText, 1) = "*" THEN DisassemblyText = SPACE$(31)
        DrawText 1, MID$(DisassemblyText, 1, 6) + MID$(DisassemblyText, 8, 24), 6, (25 - Disassembly%) * 16 + 24, 0
      END IF
    NEXT Disassembly%
  END IF
  OldDisasmPosition = DisasmPosition
  IF ExecuteCPU THEN
    IF DisasmPosition = DisasmStatus THEN DisasmPosition = DisasmPosition + 1
    DisasmStatus = DisasmStatus + 1
  END IF

'***** Check the keyboard and other miscellaneous stuff **********************
  ExecuteCPU = 0
  DO
    'GET (MouseX, MouseY)-(MouseX + 10, MouseY + 18), MouseBackground
    'WaitVblank 1
    'PUT (MouseX, MouseY), MouseMask, AND
    'PUT (MouseX, MouseY), Mouse, OR
    IF FlipColors THEN
      IF PaletteCycler < 32 THEN PaletteCycler = PaletteCycler + .25 ELSE PaletteCycler = 0
      IF PaletteCycler >= 16 THEN PaletteColor% = 32 - INT(PaletteCycler) ELSE PaletteColor% = INT(PaletteCycler)
      IF FlipColors = 1 THEN
        OUT 968, 0
        OUT 969, 63 - PaletteColor%
        OUT 969, 63 - PaletteColor%
        OUT 969, 63 - PaletteColor%
        OUT 968, 13
        OUT 969, (PaletteColor% \ 2) + 24
        OUT 969, (PaletteColor% \ 2) + 24
        OUT 969, (PaletteColor% \ 2) + 24
      ELSE
        OUT 968, 0
        OUT 969, 0
        OUT 969, 0
        OUT 969, PaletteColor%
      END IF
    END IF
    'WaitVblank 1
    PressedButton$ = UCASE$(INKEY$)
    SELECT CASE PressedButton$
      CASE CHR$(0) + CHR$(&H3B)
        IF DisasmPosition = DisasmStatus THEN DisasmPosition = DisasmPosition + 3
        SkipText$ = "****  The user exited debugger mode for at least one  ****" + CHR$(13) + CHR$(10)
        PUT #2, DisasmStatus * 60 + 93, SkipText$
        DisasmStatus = DisasmStatus + 1
        SkipText$ = "****      frame, so a very large section of this      ****" + CHR$(13) + CHR$(10)
        PUT #2, DisasmStatus * 60 + 93, SkipText$
        DisasmStatus = DisasmStatus + 1
        SkipText$ = "****               disassembly is missing.            ****" + CHR$(13) + CHR$(10)
        PUT #2, DisasmStatus * 60 + 93, SkipText$
        DisasmStatus = DisasmStatus + 1
        InitializeScreen 14: DebuggerFlag = 0
        ExecuteCPU = 1: EXIT DO
      CASE CHR$(0) + CHR$(&H3C)
        IF DisasmPosition = DisasmStatus THEN DisasmPosition = DisasmPosition + 2
        SkipText$ = "****    The disassembly was paused until the Vblank   ****" + CHR$(13) + CHR$(10)
        PUT #2, DisasmStatus * 60 + 93, SkipText$
        DisasmStatus = DisasmStatus + 1
        SkipText$ = "****                  status changed.                 ****" + CHR$(13) + CHR$(10)
        PUT #2, DisasmStatus * 60 + 93, SkipText$
        DisasmStatus = DisasmStatus + 1
        DebuggerFlag = 2
        ExecuteCPU = 1: EXIT DO
      CASE CHR$(0) + CHR$(&H3D)
        IF DisasmPosition = DisasmStatus THEN DisasmPosition = DisasmPosition + 1
        SkipText$ = "**** The disassembly was paused for 100 instructions. ****" + CHR$(13) + CHR$(10)
        PUT #2, DisasmStatus * 60 + 93, SkipText$
        DisasmStatus = DisasmStatus + 1
        DebuggerFlag = 3: DebuggerCount = 100
        ExecuteCPU = 1: EXIT DO
      CASE CHR$(0) + CHR$(&H3E) TO CHR$(0) + CHR$(&H43)
        WhichMC% = ASC(RIGHT$(PressedButton$, 1)) - 62
        LINE (296, 224)-(635, 255), 13, BF
        DrawText 1, "Press a key to select the address type:", 296, 224, 0
        DrawText 1, "[C]PU, [P]PU, or [S]prite Address", 296, 240, 0
        DO
          SELECT CASE UCASE$(INKEY$)
            CASE "C": MemoryContents(WhichMC%, 0) = 1: BoxSpaces% = 4: EXIT DO
            CASE "P": MemoryContents(WhichMC%, 0) = 2: BoxSpaces% = 4: EXIT DO
            CASE "S": MemoryContents(WhichMC%, 0) = 3: BoxSpaces% = 2: EXIT DO
          END SELECT
        LOOP
        LINE (296, 224)-(635, 255), 13, BF
        DrawText 1, "Enter the address in hexadecimal:", 296, 232, 0
        MemoryContents(WhichMC%, 1) = VAL("&H" + TextBox$(600, 232, BoxSpaces%))
        EXIT DO
      CASE CHR$(0) + CHR$(&H44)
        LINE (288, 0)-(639, 331), 13, BF
        DrawObject 40, 288, 0, 639, 331
        DrawText 0, "NES ROM Information", 296, 8, 0
        DrawText 1, "File name: " + NESROMname + " (" + RIGHT$(STR$(NESFileSize), LEN(STR$(NESFileSize)) - 1) + " bytes)", 296, 24, 0
        DrawText 1, "Mapper number:" + STR$(Mapper), 296, 40, 0
        DrawText 1, "16KB PRG-ROM banks:" + STR$(PRGROMbanks), 296, 56, 0
        DrawText 1, "8KB CHR-ROM banks:" + STR$(CHRROMbanks), 296, 72, 0
        SELECT CASE OriginalMirroring
          CASE -1
            DrawText 1, "Name Table Mirroring: Single-screen @ $2000", 296, 88, 0
          CASE 0
            DrawText 1, "Name Table Mirroring: Horizontal", 296, 88, 0
          CASE 1
            DrawText 1, "Name Table Mirroring: Vertical", 296, 88, 0
        END SELECT
        IF Flags1 AND 2 THEN DrawText 1, "SRAM: Present", 296, 104, 0 ELSE DrawText 1, "SRAM: Not present", 296, 104, 0
        IF Flags1 AND 4 THEN DrawText 1, "512 byte trainer: Present", 296, 120, 0 ELSE DrawText 1, "512 byte trainer: Not present", 296, 120, 0
        DrawText 0, "Press any key to continue...", 296, 136, 0
        DrawText 0, "PRGROM bank @ $8000:" + STR$(CurrentPRGROMbank(0)), 296, 152, 0
        DrawText 0, "PRGROM bank @ $C000:" + STR$(CurrentPRGROMbank(1)), 296, 168, 0
        DrawText 0, "PRGROM switching:" + STR$(PRGROMswitching), 296, 184, 0
        DebuggerFlag = -2
        SLEEP
        EXIT DO
      CASE CHR$(0) + CHR$(&H47)
        DisasmPosition = 0
        EXIT DO
      CASE CHR$(0) + CHR$(&H49)
        IF DisasmPosition > 0 THEN DisasmPosition = DisasmPosition - 1
        EXIT DO
      CASE CHR$(0) + CHR$(&H4F)
        DisasmPosition = DisasmStatus
        EXIT DO
      CASE CHR$(0) + CHR$(&H51)
        IF DisasmPosition < DisasmStatus THEN DisasmPosition = DisasmPosition + 1
        EXIT DO
      CASE CHR$(9)
        LINE (288, 0)-(639, 331), 13, BF
        DrawObject 40, 288, 0, 639, 331
        DrawText 0, "Don't you have anything better to do than", 296, 8, 0
        DrawText 0, "push random keys looking for secrets?", 296, 24, 0
        DrawText 1, "Well, I'll tell you this. When you're", 296, 40, 0
        DrawText 1, "in regular (non-debugger) mode, press", 296, 56, 0
        DrawText 1, "[F10] whenever you get bored from the", 296, 72, 0
        DrawText 1, "sheer slowness of this emulator.", 296, 88, 0
        DrawText 0, "Press any key for a cool surprise...", 296, 120, 0
        DebuggerFlag = -2
        SLEEP
        IF FlipColors = 1 THEN FlipColors = 2 ELSE FlipColors = 1
        IF FlipColors = 1 THEN
          PALETTE 15, 0
          PALETTE 14, 1052688
          PALETTE 13, 2105376
          PALETTE 12, 3158064
          PALETTE 0, 4144959
        ELSE
          PALETTE 0, 0
          PALETTE 12, 1052688
          PALETTE 13, 2105376
          PALETTE 14, 3158064
          PALETTE 15, 4144959
        END IF
        EXIT DO
      CASE CHR$(0) + "H": Controller(1) = Controller(1) XOR 16: EXIT DO
      CASE CHR$(0) + "K": Controller(1) = Controller(1) XOR 64: EXIT DO
      CASE CHR$(0) + "M": Controller(1) = Controller(1) XOR 128: EXIT DO
      CASE CHR$(0) + "P": Controller(1) = Controller(1) XOR 32: EXIT DO
      CASE "V": Controller(1) = Controller(1) XOR 4: EXIT DO
      CASE "B": Controller(1) = Controller(1) XOR 8: EXIT DO
      CASE "N": Controller(1) = Controller(1) XOR 2: EXIT DO
      CASE "M": Controller(1) = Controller(1) XOR 1: EXIT DO
      CASE "W": Controller(2) = Controller(2) XOR 16: EXIT DO
      CASE "A": Controller(2) = Controller(2) XOR 64: EXIT DO
      CASE "S": Controller(2) = Controller(2) XOR 128: EXIT DO
      CASE "D": Controller(2) = Controller(2) XOR 32: EXIT DO
      CASE "1": Controller(2) = Controller(2) XOR 4: EXIT DO
      CASE "2": Controller(2) = Controller(2) XOR 8: EXIT DO
      CASE "3": Controller(2) = Controller(2) XOR 2: EXIT DO
      CASE "4": Controller(2) = Controller(2) XOR 1: EXIT DO
      CASE CHR$(27): ByeBye
      CASE ""
      CASE ELSE: ExecuteCPU = 1: EXIT DO
    END SELECT
    'PUT (MouseX, MouseY), MouseBackground, PSET
    'OldMouseX = MouseX: OldMouseY = MouseY: OldMouseButtons = MouseButtons
    'MouseDriver 3, MouseButtons, MouseX, MouseY
  LOOP
  DEF SEG = 0: POKE &H41A, PEEK(&H41C)
  IF ExecuteCPU = 1 THEN EXIT DO
LOOP
END SUB

SUB DrawBitmap (BitmapStyle%, FileName$, ColorsUsed%, BitmapX%, BitmapY%)
  FileHandle% = FREEFILE
  OPEN FileName$ FOR BINARY AS FileHandle%
  IF LOF(FileHandle%) = 0 THEN CLOSE FileHandle%: KILL FileName$: ERROR 254
  GET FileHandle%, 19, BitmapWidth%
  GET FileHandle%, 23, BitmapHeight%
  SEEK FileHandle%, 55
  PictureData$ = INPUT$(1024, FileHandle%)
  FOR PaletteColor% = 0 TO ColorsUsed%
    FourBytes$ = MID$(PictureData$, PaletteColor% * 4 + 1, 4)
    OUT 968, PaletteColor%
    OUT 969, ASC(MID$(FourBytes$, 3, 1)) \ 4
    OUT 969, ASC(MID$(FourBytes$, 2, 1)) \ 4
    OUT 969, ASC(FourBytes$) \ 4
  NEXT PaletteColor%
  GET FileHandle%, 11, BitmapOffset%
  SEEK FileHandle%, BitmapOffset% + 1
  SELECT CASE BitmapStyle%
  CASE 1
    BitmapWidth% = (((BitmapWidth% - 1) OR 7) + 1) \ 2
    FOR Row% = BitmapY% + BitmapHeight% - 1 TO BitmapY% STEP -1
      RowData$ = INPUT$(BitmapWidth%, FileHandle%)
      FOR Column% = 0 TO BitmapWidth% - 1
        TwoPixels% = ASC(MID$(RowData$, Column% + 1, 1))
        PSET (Column% * 2 + BitmapX%, Row%), TwoPixels% \ 16
        PSET (Column% * 2 + BitmapX% + 1, Row%), TwoPixels% AND 15
      NEXT Column%
    NEXT Row%
  CASE 2
    BitmapWidth% = (((BitmapWidth% - 1) OR 7) + 1) \ 2
    FOR Row% = (BitmapHeight% - 1) * 2 TO 0 STEP -2
      RowData$ = INPUT$(BitmapWidth%, FileHandle%)
      FOR Column% = 0 TO (BitmapWidth% - 1) * 2 STEP 2
        TwoPixels% = ASC(MID$(RowData$, Column% \ 2 + 1, 1))
        PSET (Column% * 2, Row%), TwoPixels% \ 16
        PSET (Column% * 2 + 1, Row%), TwoPixels% \ 16
        PSET (Column% * 2, Row% + 1), TwoPixels% \ 16
        PSET (Column% * 2 + 1, Row% + 1), TwoPixels% \ 16
        PSET (Column% * 2 + 2, Row%), TwoPixels% AND 15
        PSET (Column% * 2 + 3, Row%), TwoPixels% AND 15
        PSET (Column% * 2 + 2, Row% + 1), TwoPixels% AND 15
        PSET (Column% * 2 + 3, Row% + 1), TwoPixels% AND 15
      NEXT Column%
    NEXT Row%
  END SELECT
  CLOSE FileHandle%
END SUB

SUB DrawNESscreen
  IF VideoPage THEN DEF SEG = &HA000 ELSE DEF SEG = &HA4F0
  SkippedFrames = SkippedFrames + 1
  IF SkippedFrames >= Frameskip + 1 THEN SkippedFrames = 0 ELSE EXIT SUB
  IF DebuggerFlag = 0 THEN
    SELECT CASE MemoryRegisters(1) AND 224
      CASE 0: BGcolor% = 65
      CASE 32: BGcolor% = 66
      CASE 64: BGcolor% = 67
      CASE 128: BGcolor% = 68
    END SELECT
    OUT &H3C4, 2: OUT &H3C5, 15
    FOR ClearByte% = 0 TO 19199: POKE ClearByte%, BGcolor%: NEXT
    IF MemoryRegisters(1) AND 8 THEN
      IF MemoryRegisters(0) AND 16 THEN PatternTableAddress% = 4096 ELSE PatternTableAddress% = 0
      IF HorizontalScroll OR VerticalScroll THEN
        Htable% = (MemoryRegisters(0) AND 1) * 1024
        Vtable% = (MemoryRegisters(0) AND 2) * 1024
        DrawScrolling Htable% + Vtable%, 256 - HorizontalScroll, -VerticalScroll
        DrawScrolling 1024 - Htable% + Vtable%, -HorizontalScroll, -VerticalScroll
        DrawScrolling 2048 + Htable% - Vtable%, 256 - HorizontalScroll, 240 - VerticalScroll
        DrawScrolling 3072 - Htable% - Vtable%, -HorizontalScroll, 240 - VerticalScroll
      ELSE
        NameTableAddress% = (MemoryRegisters(0) AND 3) * 1024 + Mirroring(MemoryRegisters(0) AND 3)
        AttributeTableAddress% = NameTableAddress% + 960
        FOR AttributeY% = 0 TO 14 STEP 2
          FOR AttributeX% = 0 TO 14 STEP 2
            AttributeColor% = NameTables(AttributeTableAddress%)
            AttributeTableAddress% = AttributeTableAddress% + 1
            AttributeTable(AttributeX%, AttributeY%) = (AttributeColor% AND 3) * 4
            AttributeTable(AttributeX% + 1, AttributeY%) = AttributeColor% AND 12
            AttributeTable(AttributeX%, AttributeY% + 1) = (AttributeColor% AND 48) \ 4
            AttributeTable(AttributeX% + 1, AttributeY% + 1) = (AttributeColor% AND 192) \ 16
          NEXT AttributeX%
        NEXT AttributeY%
        IF (MemoryRegisters(1) AND 2) = 0 THEN BGcutoff% = 8
        FOR TileY% = 8 TO 224 STEP 8
          FOR TileX% = BGcutoff% TO 248 STEP 8
            PixelColorHigh% = AttributeTable(TileX% \ 16, TileY% \ 16)
            TileAddress% = PatternTableAddress% + NameTables(NameTableAddress% + TileY% * 4 + TileX% \ 8) * 16
            FOR TileByte% = 0 TO 15: Tile(TileByte%) = PatternTables(TileAddress% + TileByte%): NEXT
            FOR PixelX% = 0 TO 7
              OUT &H3C4, 2: OUT &H3C5, PowerOfTwo(3 - PixelX% AND 3)
              CurrentPixelX% = (TileX% - PixelX% + 39) \ 4
              FOR PixelY% = 0 TO 7
                IF Tile(PixelY%) AND PowerOfTwo(PixelX%) THEN PixelColor% = 1 ELSE PixelColor% = 0
                IF Tile(PixelY% + 8) AND PowerOfTwo(PixelX%) THEN PixelColor% = PixelColor% + 2
                POKE CurrentPixelX% + (TileY% + PixelY%) * 80, BackgroundPalette(PixelColorHigh% + PixelColor%) + 1
              NEXT PixelY%
            NEXT PixelX%
          NEXT TileX%
        NEXT TileY%
      END IF
    END IF
    IF MemoryRegisters(1) AND 16 THEN
      IF (MemoryRegisters(1) AND 4) = 0 THEN SpriteCutOff% = 8
      MemoryRegisters(2) = MemoryRegisters(2) OR 64
      IF MemoryRegisters(0) AND 32 THEN TwoSprites% = 1
      IF MemoryRegisters(0) AND 8 THEN SpriteTableAddress% = 4096 ELSE SpriteTableAddress% = 0
      FOR Sprite% = 0 TO 252 STEP 4
        TileAddress% = SpriteTableAddress% + (SpriteRAM(Sprite% + 1) - TwoSprites% AND 255) * 16
        SpriteFlags% = SpriteRAM(Sprite% + 2): PixelColorHigh% = (SpriteFlags% AND 3) * 4
        SpriteX% = SpriteRAM(Sprite% + 3)
        SpriteY% = SpriteRAM(Sprite%) + 1
        FOR TileByte% = 0 TO 15: Tile(TileByte%) = PatternTables(TileAddress% + TileByte%): NEXT
        IF SpriteFlags% AND 128 THEN FlipY% = 7: StepY% = -1 ELSE FlipY% = 0: StepY% = 1
        IF SpriteFlags% AND 64 THEN FlipX% = 0: StepX% = 1 ELSE FlipX% = 7: StepX% = -1
        DoubleYflip% = 0
        IF FlipY% = 7 AND TwoSprites% = 1 THEN SpriteY% = SpriteY% + 8: FlipY% = 0: StepY% = 1: DoubleYflip% = 1
        FOR NumberOfSpritesToDraw% = 0 TO TwoSprites%
          ActualX% = FlipX%
          FOR PixelX% = 0 TO 7
            IF SpriteX% + ActualX% >= SpriteCutOff% AND SpriteX% + ActualX% <= 255 THEN
              OUT &H3C4, 2: OUT &H3C5, PowerOfTwo((SpriteX% + ActualX% + 32) AND 3)
              CurrentPixelX% = (SpriteX% + ActualX% + 32) \ 4
              ActualY% = FlipY%
              FOR PixelY% = 0 TO 7
                IF SpriteY% + ActualY% >= 8 AND SpriteY% + ActualY% <= 231 THEN
                  IF Tile(PixelY%) AND PowerOfTwo(PixelX%) THEN PixelColor% = 1 ELSE PixelColor% = 0
                  IF Tile(PixelY% + 8) AND PowerOfTwo(PixelX%) THEN PixelColor% = PixelColor% + 2
                  IF PixelColor% THEN POKE CurrentPixelX% + (SpriteY% + ActualY%) * 80, SpritePalette(PixelColorHigh% + PixelColor%) + 1
                END IF
                ActualY% = ActualY% + StepY%
              NEXT PixelY%
            END IF
            ActualX% = ActualX% + StepX%
          NEXT PixelX%
          IF TwoSprites% THEN
            TileAddress% = SpriteTableAddress% + SpriteRAM(Sprite% + 1) * 16
            FOR TileByte% = 0 TO 15: Tile(TileByte%) = PatternTables(TileAddress% + TileByte%): NEXT
            IF DoubleYflip% = 0 THEN SpriteY% = SpriteY% + 8 ELSE SpriteY% = SpriteY% - 8
          END IF
        NEXT NumberOfSpritesToDraw%
      NEXT Sprite%
    END IF
    IF FPScounter THEN
      ProgramTimer(1) = TIMER - ProgramTimer(0)
      DrawText 1, "Frameskip:" + STR$(Frameskip), 2, 414, 70
      DrawText 1, "Frameskip:" + STR$(Frameskip), 0, 416, 69
      IF ProgramTimer(1) = 0 THEN
        DrawText 1, "QB returned infinity so it's over 18 fps", 2, 446, 70
        DrawText 1, "QB returned infinity so it's over 18 fps", 0, 448, 69
      ELSE
        ProgramTimer(1) = 1 / ProgramTimer(1)
        ProgramTimer(1) = INT(ProgramTimer(1) * 100 * (Frameskip + 1)) / 100
        DrawText 1, "Watch QB go!" + STR$(ProgramTimer(1)) + "/60 fps", 2, 446, 70
        DrawText 1, "Watch QB go!" + STR$(ProgramTimer(1)) + "/60 fps", 0, 448, 69
      END IF
      IF DisplayVersion THEN
        DrawText 1, DRR.Software.Build, 1, 0, 70
        DrawText 1, DRR.Software.Build, 0, 2, 69
      END IF
    END IF
    IF CoolMessage THEN
      IF CoolMessage = 1 THEN
        WhichCoolMessage% = INT(RND * 15)
        SELECT CASE WhichCoolMessage%
          CASE 0
            CoolMessageText = "It doesn't matter..."
            CoolMessageText2 = ""
            CoolMessageText3 = ""
          CASE 1
            CoolMessageText = "Hey, stop pushing [F10]!"
            CoolMessageText2 = "It hurts!"
            CoolMessageText3 = ""
          CASE 2
            CoolMessageText = "Tell DRR that you saw this message, and"
            CoolMessageText2 = "you could win $500! (yeah right)"
            CoolMessageText3 = ""
          CASE 3
            CoolMessageText = "Yeah, baby, yeah!"
            CoolMessageText2 = ""
            CoolMessageText3 = ""
          CASE 4
            CoolMessageText = "For the last time, I know that DRR-NES"
            CoolMessageText2 = "is super slow!"
            CoolMessageText3 = ""
          CASE 5
            CoolMessageText = "I'll rewrite the 6502 CPU core"
            CoolMessageText2 = "in ASM someday..."
            CoolMessageText3 = ""
          CASE 6
            CoolMessageText = "Hi Mom and Dad and Eric and John and"
            CoolMessageText2 = "everybody else!"
            CoolMessageText3 = ""
          CASE 7
            CoolMessageText = "Come on, go to my web site at:"
            CoolMessageText2 = "http://drr.dragonfire.net"
            CoolMessageText3 = ""
          CASE 8
            CoolMessageText = "My brother Michael is the most"
            CoolMessageText2 = "annoying person to ever exist!"
            CoolMessageText3 = ""
          CASE 9
            CoolMessageText = "The N64 rules! The PSX sucks!"
            CoolMessageText2 = ""
            CoolMessageText3 = ""
          CASE 10
            CoolMessageText = CHR$(14) + " Da da da... " + CHR$(14)
            CoolMessageText2 = ""
            CoolMessageText3 = ""
          CASE 11
            CoolMessageText = "Macintoshes and Linux and Amiga"
            CoolMessageText2 = "and any other lame OS that"
            CoolMessageText3 = "nobody uses all SUCK!"
          CASE 12
            CoolMessageText = "Oh my god, they killed Kenny!"
            CoolMessageText2 = "You bastards!"
            CoolMessageText3 = "BEEF-CAKE!"
          CASE 13
            CoolMessageText = "You must be really bored..."
            CoolMessageText2 = ""
            CoolMessageText3 = ""
          CASE 14
            CoolMessageText = "Kick Bill Clinton's sorry ass out of"
            CoolMessageText2 = "office!"
            CoolMessageText3 = ""
          CASE 15
            CoolMessageText = "Hi."
            CoolMessageText2 = ""
            CoolMessageText3 = ""
        END SELECT
      END IF
      DrawText 1, CoolMessageText, 1, 0, 70
      DrawText 1, CoolMessageText, 0, 2, 69
      DrawText 1, CoolMessageText2, 1, 32, 70
      DrawText 1, CoolMessageText2, 0, 34, 69
      DrawText 1, CoolMessageText3, 1, 64, 70
      DrawText 1, CoolMessageText3, 0, 66, 69
      CoolMessage = CoolMessage + 1
      IF CoolMessage = 10 THEN CoolMessage = 0
    END IF
    IF VideoPage THEN OUT &H3D4, 12: OUT &H3D5, &H0 ELSE OUT &H3D4, 12: OUT &H3D5, &H4F
    VideoPage = NOT VideoPage
  END IF
END SUB

SUB DrawObject (WhichObject%, x1%, y1%, x2%, y2%)
  SELECT CASE WhichObject%
  CASE 0
    LINE (x1%, y1%)-(x2%, y2%), 13, BF
    LINE (x1%, y1%)-(x2% - 1, y1%), 15
    LINE (x1%, y1% + 1)-(x1%, y2% - 1), 15
    LINE (x1% + 1, y1% + 1)-(x2% - 2, y1% + 1), 14
    LINE (x1% + 1, y1% + 2)-(x1% + 1, y2% - 2), 14
    LINE (x2%, y1%)-(x2%, y2%), 0
    LINE (x1%, y2%)-(x2% - 1, y2%), 0
    LINE (x2% - 1, y1% + 1)-(x2% - 1, y2% - 1), 12
    LINE (x1% + 1, y2% - 1)-(x2% - 2, y2% - 1), 12
    LINE (x1% + 4, y1% + 22)-(x2% - 4, y1% + 22), 12
    LINE (x1% + 4, y1% + 23)-(x1% + 4, y2% - 22), 12
    LINE (x1% + 5, y1% + 23)-(x2% - 5, y1% + 23), 0
    LINE (x1% + 5, y1% + 24)-(x1% + 5, y2% - 23), 0
    LINE (x2% - 4, y1% + 23)-(x2% - 4, y2% - 22), 14
    LINE (x1% + 5, y2% - 22)-(x2% - 5, y2% - 22), 14
    LINE (x2% - 5, y1% + 24)-(x2% - 5, y2% - 23), 15
    LINE (x1% + 6, y2% - 23)-(x2% - 6, y2% - 23), 15
  CASE 10
    LINE (x1%, y1%)-(x2% - 1, y1%), 15
    LINE (x1%, y1% + 1)-(x1%, y2% - 1), 15
    LINE (x2%, y1%)-(x2%, y2%), 0
    LINE (x1%, y2%)-(x2% - 1, y2%), 0
  CASE 11
    LINE (x1%, y1%)-(x2% - 1, y1%), 0
    LINE (x1%, y1% + 1)-(x1%, y2% - 1), 0
    LINE (x2%, y1%)-(x2%, y2%), 15
    LINE (x1%, y2%)-(x2% - 1, y2%), 15
  CASE 20
    LINE (x1%, y1%)-(x2% - 1, y1%), 15
    LINE (x1%, y1% + 1)-(x1%, y2% - 1), 15
    LINE (x1% + 1, y1% + 1)-(x2% - 2, y1% + 1), 14
    LINE (x1% + 1, y1% + 2)-(x1% + 1, y2% - 2), 14
    LINE (x2%, y1%)-(x2%, y2%), 0
    LINE (x1%, y2%)-(x2% - 1, y2%), 0
    LINE (x2% - 1, y1% + 1)-(x2% - 1, y2% - 1), 12
    LINE (x1% + 1, y2% - 1)-(x2% - 2, y2% - 1), 12
  CASE 21
    LINE (x1%, y1%)-(x2% - 1, y1%), 0
    LINE (x1%, y1% + 1)-(x1%, y2% - 1), 0
    LINE (x1% + 1, y1% + 1)-(x2% - 2, y1% + 1), 12
    LINE (x1% + 1, y1% + 2)-(x1% + 1, y2% - 2), 12
    LINE (x2%, y1%)-(x2%, y2%), 15
    LINE (x1%, y2%)-(x2% - 1, y2%), 15
    LINE (x2% - 1, y1% + 1)-(x2% - 1, y2% - 1), 14
    LINE (x1% + 1, y2% - 1)-(x2% - 2, y2% - 1), 14
  CASE 40
    LINE (x1%, y1%)-(x2% - 1, y1%), 15
    LINE (x1%, y1% + 1)-(x1%, y2% - 1), 15
    LINE (x1% + 1, y1% + 1)-(x2% - 2, y1% + 1), 15
    LINE (x1% + 1, y1% + 2)-(x1% + 1, y2% - 2), 15
    LINE (x1% + 2, y1% + 2)-(x2% - 3, y1% + 2), 14
    LINE (x1% + 2, y1% + 3)-(x1% + 2, y2% - 3), 14
    LINE (x1% + 3, y1% + 3)-(x2% - 4, y1% + 3), 14
    LINE (x1% + 3, y1% + 4)-(x1% + 3, y2% - 4), 14
    LINE (x2%, y1%)-(x2%, y2%), 0
    LINE (x1%, y2%)-(x2% - 1, y2%), 0
    LINE (x2% - 1, y1% + 1)-(x2% - 1, y2% - 1), 0
    LINE (x1% + 1, y2% - 1)-(x2% - 2, y2% - 1), 0
    LINE (x2% - 2, y1% + 2)-(x2% - 2, y2% - 2), 12
    LINE (x1% + 2, y2% - 2)-(x2% - 3, y2% - 2), 12
    LINE (x2% - 3, y1% + 3)-(x2% - 3, y2% - 3), 12
    LINE (x1% + 3, y2% - 3)-(x2% - 4, y2% - 3), 12
  CASE 41
    LINE (x1%, y1%)-(x2% - 1, y1%), 0
    LINE (x1%, y1% + 1)-(x1%, y2% - 1), 0
    LINE (x1% + 1, y1% + 1)-(x2% - 2, y1% + 1), 0
    LINE (x1% + 1, y1% + 2)-(x1% + 1, y2% - 2), 0
    LINE (x1% + 2, y1% + 2)-(x2% - 3, y1% + 2), 12
    LINE (x1% + 2, y1% + 3)-(x1% + 2, y2% - 3), 12
    LINE (x1% + 3, y1% + 3)-(x2% - 4, y1% + 3), 12
    LINE (x1% + 3, y1% + 4)-(x1% + 3, y2% - 4), 12
    LINE (x2%, y1%)-(x2%, y2%), 15
    LINE (x1%, y2%)-(x2% - 1, y2%), 15
    LINE (x2% - 1, y1% + 1)-(x2% - 1, y2% - 1), 15
    LINE (x1% + 1, y2% - 1)-(x2% - 2, y2% - 1), 15
    LINE (x2% - 2, y1% + 2)-(x2% - 2, y2% - 2), 14
    LINE (x1% + 2, y2% - 2)-(x2% - 3, y2% - 2), 14
    LINE (x2% - 3, y1% + 3)-(x2% - 3, y2% - 3), 14
    LINE (x1% + 3, y2% - 3)-(x2% - 4, y2% - 3), 14
  END SELECT
END SUB

SUB DrawScrolling (NameTableAddress%, Hscroll%, Vscroll%)
  IF MemoryRegisters(0) AND 16 THEN PatternTableAddress% = 4096 ELSE PatternTableAddress% = 0
  IF (MemoryRegisters(1) AND 2) = 0 THEN BGcutoff% = 40 ELSE BGcutoff% = 32
  NameTableAddress% = NameTableAddress% + Mirroring(NameTableAddress% \ 1024)
  IF DebuggerFlag = 0 THEN
    AttributeTableAddress% = NameTableAddress% + 960
    FOR AttributeY% = 0 TO 14 STEP 2
      FOR AttributeX% = 0 TO 14 STEP 2
        AttributeColor% = NameTables(AttributeTableAddress%)
        AttributeTableAddress% = AttributeTableAddress% + 1
        AttributeTable(AttributeX%, AttributeY%) = (AttributeColor% AND 3) * 4
        AttributeTable(AttributeX% + 1, AttributeY%) = AttributeColor% AND 12
        AttributeTable(AttributeX%, AttributeY% + 1) = (AttributeColor% AND 48) \ 4
        AttributeTable(AttributeX% + 1, AttributeY% + 1) = (AttributeColor% AND 192) \ 16
      NEXT AttributeX%
    NEXT AttributeY%
    FOR TileY% = 0 TO 232 STEP 8
      IF TileY% + Vscroll% >= -7 AND TileY% + Vscroll% <= 239 THEN
        FOR TileX% = 0 TO 248 STEP 8
          IF TileX% + Hscroll% >= -8 AND TileX% + Hscroll% <= 255 THEN
            PixelColorHigh% = AttributeTable(TileX% \ 16, TileY% \ 16)
            TileAddress% = PatternTableAddress% + NameTables(NameTableAddress% + TileY% * 4 + TileX% \ 8) * 16
            FOR TileByte% = 0 TO 15: Tile(TileByte%) = PatternTables(TileAddress% + TileByte%): NEXT
            FOR PixelX% = 0 TO 7
              CurrentPixelX% = (TileX% - PixelX% + Hscroll% + 39)
              IF CurrentPixelX% >= BGcutoff% AND CurrentPixelX% < 288 THEN
                OUT &H3C4, 2: OUT &H3C5, PowerOfTwo(CurrentPixelX% AND 3)
                TwoExpPixelX% = PowerOfTwo(PixelX%)
                FOR PixelY% = 0 TO 7
                  CurrentPixelY% = TileY% + PixelY% + Vscroll%
                  IF CurrentPixelY% >= 8 AND CurrentPixelY% < 232 THEN
                    IF Tile(PixelY%) AND TwoExpPixelX% THEN PixelColor% = 1 ELSE PixelColor% = 0
                    IF Tile(PixelY% + 8) AND TwoExpPixelX% THEN PixelColor% = PixelColor% + 2
                    POKE CurrentPixelX% \ 4 + CurrentPixelY% * 80, BackgroundPalette(PixelColorHigh% + PixelColor%) + 1
                  END IF
                NEXT PixelY%
              END IF
            NEXT PixelX%
          END IF
        NEXT TileX%
      END IF
    NEXT TileY%
  END IF
END SUB

SUB DrawText (TextFont%, TextToType$, TextX%, TextY%, TextColor%)
  SELECT CASE VideoMode
  CASE 12
    IF TextFont% = 0 THEN DEF SEG = VARSEG(Font0): FontOffset% = VARPTR(Font0) ELSE DEF SEG = VARSEG(Font1): FontOffset% = VARPTR(Font1)
    FOR TextCharacter% = 1 TO LEN(TextToType$)
      X% = (TextCharacter% - 1) * 8 + TextX%
      ASCIIcode% = ASC(MID$(TextToType$, TextCharacter%, 1))
      FOR Y% = TextY% TO TextY% + 15
        TextPixels% = PEEK(FontOffset% + ASCIIcode% * 16& + Y% - TextY%)
        IF TextPixels% AND 128 THEN PSET (X%, Y%), TextColor%
        IF TextPixels% AND 64 THEN PSET (X% + 1, Y%), TextColor%
        IF TextPixels% AND 32 THEN PSET (X% + 2, Y%), TextColor%
        IF TextPixels% AND 16 THEN PSET (X% + 3, Y%), TextColor%
        IF TextPixels% AND 8 THEN PSET (X% + 4, Y%), TextColor%
        IF TextPixels% AND 4 THEN PSET (X% + 5, Y%), TextColor%
        IF TextPixels% AND 2 THEN PSET (X% + 6, Y%), TextColor%
        IF TextPixels% AND 1 THEN PSET (X% + 7, Y%), TextColor%
      NEXT Y%
    NEXT TextCharacter%
  CASE 14
    TextX% = TextX% \ 2: TextY% = TextY% \ 2
    IF TextFont% = 0 THEN DEF SEG = VARSEG(Font0): FontOffset% = VARPTR(Font0) ELSE DEF SEG = VARSEG(Font1): FontOffset% = VARPTR(Font1)
    FOR TextCharacter% = 1 TO LEN(TextToType$)
      CharacterX% = ((TextCharacter% - 1) * 8 + TextX%)
      ASCIIcode% = ASC(MID$(TextToType$, TextCharacter%, 1))
      FOR Y% = TextY% * 80 TO (TextY% + 15) * 80 STEP 80
        IF TextFont% = 0 THEN DEF SEG = VARSEG(Font0) ELSE DEF SEG = VARSEG(Font1)
        TextPixels% = PEEK(FontOffset% + ASCIIcode% * 16& + (Y% \ 80) - TextY%)
        IF VideoPage THEN DEF SEG = &HA000 ELSE DEF SEG = &HA4F0
        FOR X% = CharacterX% TO CharacterX% + 7
          OUT &H3C4, 2: OUT &H3C5, PowerOfTwo(X% AND 3)
          IF TextPixels% AND PowerOfTwo(7 - (X% - CharacterX%)) THEN POKE X% \ 4 + Y%, TextColor%
        NEXT X%
      NEXT Y%
    NEXT TextCharacter%
  END SELECT
END SUB

SUB InGameControls
  'DO WHILE INP(&H64) AND 1
  '  IF ExtendedKeyPressed = 1 THEN
  '    ExtendedKeyPressed = 0
  '    SELECT CASE INP(&H60)
  '      CASE 72: Controller(1) = Controller(1) OR 16
  '      CASE 74: IF Frameskip > 0 THEN Frameskip = Frameskip - 1
  '      CASE 75: Controller(1) = Controller(1) OR 64
  '      CASE 77: Controller(1) = Controller(1) OR 128
  '      CASE 78: Frameskip = Frameskip + 1
  '      CASE 80: Controller(1) = Controller(1) OR 32
  '      CASE 200: Controller(1) = Controller(1) AND 239
  '      CASE 203: Controller(1) = Controller(1) AND 191
  '      CASE 205: Controller(1) = Controller(1) AND 127
  '      CASE 208: Controller(1) = Controller(1) AND 223
  '    END SELECT
  '  ELSE
  '    SELECT CASE INP(&H60)
  '      CASE 1: ByeBye
  '      CASE 2: Controller(2) = Controller(2) OR 4
  '      CASE 3: Controller(2) = Controller(2) OR 8
  '      CASE 4: Controller(2) = Controller(2) OR 2
   '     CASE 5: Controller(2) = Controller(2) OR 1
   '     CASE 17: Controller(2) = Controller(2) OR 16
   '     CASE 30: Controller(2) = Controller(2) OR 64
   '     CASE 31: Controller(2) = Controller(2) OR 32
   '     CASE 32: Controller(2) = Controller(2) OR 128
   '     CASE 47: Controller(1) = Controller(1) OR 4
   '     CASE 48: Controller(1) = Controller(1) OR 8
   '     CASE 49: Controller(1) = Controller(1) OR 2
   '     CASE 50: Controller(1) = Controller(1) OR 1
   '     CASE 59: DebuggerFlag = -1: Debugger: EXIT SUB
   '     CASE 60: FPScounter = NOT FPScounter
   '     CASE 70: ByeBye
   '     CASE 88: IF CoolMessage = 0 THEN CoolMessage = 1
   '     CASE 130: Controller(2) = Controller(2) AND 251
   '     CASE 131: Controller(2) = Controller(2) AND 247
   '     CASE 132: Controller(2) = Controller(2) AND 253
   '     CASE 133: Controller(2) = Controller(2) AND 254
   '     CASE 145: Controller(2) = Controller(2) AND 239
   '     CASE 158: Controller(2) = Controller(2) AND 191
   '     CASE 159: Controller(2) = Controller(2) AND 127
   '     CASE 160: Controller(2) = Controller(2) AND 223
   '     CASE 175: Controller(1) = Controller(1) AND 251
   '     CASE 176: Controller(1) = Controller(1) AND 247
   '     CASE 177: Controller(1) = Controller(1) AND 253
   '     CASE 178: Controller(1) = Controller(1) AND 254
   '     CASE 224: ExtendedKeyPressed = 1
   '     CASE 225: ByeBye
   '   END SELECT
   ' END IF
  'LOOP
  'EXIT SUB
  'old controls
  Controller(1) = 0: Controller(2) = 0
  SELECT CASE UCASE$(INKEY$)
    CASE "~": DisplayVersion = 1
    CASE CHR$(0) + "H": Controller(1) = Controller(1) OR 16
    CASE CHR$(0) + "K": Controller(1) = Controller(1) OR 64
    CASE CHR$(0) + "M": Controller(1) = Controller(1) OR 128
    CASE CHR$(0) + "P": Controller(1) = Controller(1) OR 32
    CASE "V": Controller(1) = Controller(1) OR 4
    CASE "B": Controller(1) = Controller(1) OR 8
    CASE "N": Controller(1) = Controller(1) OR 2
    CASE "M": Controller(1) = Controller(1) OR 1
    CASE "W": Controller(2) = Controller(2) OR 16
    CASE "A": Controller(2) = Controller(2) OR 64
    CASE "S": Controller(2) = Controller(2) OR 128
    CASE "D": Controller(2) = Controller(2) OR 32
    CASE "1": Controller(2) = Controller(2) OR 4
    CASE "2": Controller(2) = Controller(2) OR 8
    CASE "3": Controller(2) = Controller(2) OR 2
    CASE "4": Controller(2) = Controller(2) OR 1
    CASE "+": Frameskip = Frameskip + 1
    CASE "-": IF Frameskip > 0 THEN Frameskip = Frameskip - 1
    CASE CHR$(27): ByeBye
    CASE CHR$(0) + CHR$(&H3B): DebuggerFlag = -1: Debugger: EXIT SUB
    CASE CHR$(0) + CHR$(&H3C): FPScounter = NOT FPScounter
    CASE CHR$(0) + CHR$(&H44): IF CoolMessage = 0 THEN CoolMessage = 1
  END SELECT
  DEF SEG = 0: POKE &H41A, PEEK(&H41C)
  MouseDriver 3, MouseButtons, MouseX, MouseY
  'IF MouseButtons THEN ByeBye
END SUB

SUB InitializeDRRNES
  OPEN "C:\DRR-NES.DAT" FOR RANDOM AS #1
  IF LOF(1) = 0 THEN
    CLS : COLOR 5: PRINT "DRR"; : COLOR 3: PRINT "NES": COLOR 14
    PRINT "Copyright (c) 1998, 1999 DRR -- All rights reserved.": PRINT
    COLOR 1: PRINT "**** First time setup ****"
    COLOR 15: PRINT : INPUT "Type the drive/directory path to this program or type EXIT to exit: ", FileName$
    FileName$ = LTRIM$(RTRIM$(UCASE$(FileName$)))
    IF RIGHT$(FileName$, 1) = "\" THEN FileName$ = LEFT$(FileName$, LEN(FileName$) - 1)
    IF FileName$ = "EXIT" THEN SCREEN 0: WIDTH 80, 25: SYSTEM
    Identifier$ = "DRR-NES Data File"
    PUT #1, 1, Identifier$: PUT #1, 2, FileName$
  END IF
  DO
    UhOh% = 0
    GET #1, 2, Path$
    CHDRIVE LEFT$(Path$, 1)
    CHDIR Path$
    OPEN "FONT0000.DSF" FOR BINARY AS #2
    IF LOF(2) = 0 THEN CLOSE #2: KILL "FONT0000.DSF": UhOh% = 1: ERROR 255
    IF UhOh% = 0 THEN
      OPEN "FONT0001.DSF" FOR BINARY AS #3
      IF LOF(3) = 0 THEN CLOSE #3: KILL "FONT0001.DSF": UhOh% = 1: ERROR 255
    END IF
  LOOP WHILE UhOh%
  SEEK #2, 51
  DEF SEG = VARSEG(Font0): FontOffset% = VARPTR(Font0)
  FOR ASCIIcode% = 0 TO 255
    SixteenBytes$ = INPUT$(16, #2)
    FOR FontByte% = 0 TO 15
      POKE FontOffset% + ASCIIcode% * 16& + FontByte%, ASC(MID$(SixteenBytes$, FontByte% + 1, 1))
    NEXT FontByte%
  NEXT ASCIIcode%
  SEEK #3, 51
  DEF SEG = VARSEG(Font1): FontOffset% = VARPTR(Font1)
  FOR ASCIIcode% = 0 TO 255
    SixteenBytes$ = INPUT$(16, #3)
    FOR FontByte% = 0 TO 15
      POKE FontOffset% + ASCIIcode% * 16& + FontByte%, ASC(MID$(SixteenBytes$, FontByte% + 1, 1))
    NEXT FontByte%
  NEXT ASCIIcode%
  CLOSE #1: CLOSE #2: CLOSE #3
END SUB

SUB InitializeMouse
  DEF SEG = 0
  MouseSegment& = 256& * PEEK(207) + PEEK(206)
  MouseOffset& = 256& * PEEK(205) + PEEK(204)
  DEF SEG = MouseSegment&
  IF (MouseSegment& = 0 AND MouseOffset& = 0) OR PEEK(MouseOffset&) = 207 THEN NoMouse = 1
  MouseDriver 7, 0, 0, 629: MouseDriver 8, 0, 0, 461
  RESTORE MousePicture
  FOR Y% = 0 TO 18
    FOR X% = 0 TO 10
      READ Mouse(X%, Y%)
      IF Mouse(X%, Y%) = -1 THEN Mouse(X%, Y%) = 0
      PSET (X%, Y%), Mouse(X%, Y%)
    NEXT X%
  NEXT Y%
  GET (0, 0)-(10, 18), Mouse
  RESTORE MousePicture
  FOR Y% = 0 TO 18
    FOR X% = 0 TO 10
      READ MouseMask(X%, Y%)
      IF MouseMask(X%, Y%) = -1 THEN MouseMask(X%, Y%) = 15 ELSE MouseMask(X%, Y%) = 0
      PSET (X%, Y%), MouseMask(X%, Y%)
    NEXT X%
  NEXT Y%
  GET (0, 0)-(10, 18), MouseMask
  CLS
END SUB

SUB InitializeScreen (WhichMode%)
  SELECT CASE WhichMode%
    CASE 12
      SCREEN 12: CLS
      RESTORE PaletteColors
      FOR PaletteColor% = 0 TO 15
        READ ColorValue&: OUT 968, PaletteColor%
        OUT 969, ColorValue& AND 63
        OUT 969, (ColorValue& \ 256) AND 63
        OUT 969, ColorValue& \ 65536
      NEXT PaletteColor%
      COLOR 15
    CASE 14
      SCREEN 13
      OUT &H3C4, &H4: OUT &H3C5, &H6
      OUT &H3C4, &H2: OUT &H3C5, &HF: CLS
      OUT &H3C4, 0: OUT &H3C5, &H1
      OUT &H3C2, &HE3
      OUT &H3C4, 0: OUT &H3C5, &H3
      OUT &H3D4, &H11: OUT &H3D5, INP(&H3D5) AND &H7F
      OUT &H3D4, &H6: OUT &H3D5, &HD
      OUT &H3D4, &H7: OUT &H3D5, &H3E
      OUT &H3D4, &H9: OUT &H3D5, &H41
      OUT &H3D4, &H10: OUT &H3D5, &HEA
      OUT &H3D4, &H11: OUT &H3D5, &HAC
      OUT &H3D4, &H12: OUT &H3D5, &HDF
      OUT &H3D4, &H14: OUT &H3D5, 0
      OUT &H3D4, &H15: OUT &H3D5, &HE7
      OUT &H3D4, &H16: OUT &H3D5, &H6
      OUT &H3D4, &H17: OUT &H3D5, &HE3
      OUT &H3D4, 12: OUT &H3D5, &H0
      IF VideoPage THEN OUT &H3D4, 12: OUT &H3D5, &H0 ELSE OUT &H3D4, 12: OUT &H3D5, &H4F
      RESTORE NESpalette
      FOR NESpaletteColor% = 1 TO 70
        READ Red%, Green%, Blue%: OUT 968, NESpaletteColor%
        OUT 969, Red% \ 4: OUT 969, Green% \ 4: OUT 969, Blue% \ 4
      NEXT NESpaletteColor%
  END SELECT
  VideoMode = WhichMode%
END SUB

SUB InitializeXMS
  CLS
  DIM XMSRegisters AS x86Registers
  XMSRegisters.AX = &H4300
  InterruptX &H2F, XMSRegisters, XMSRegisters
  IF XMSRegisters.AX AND 128 THEN PRINT "XMS available! :)" ELSE PRINT "XMS unavailable... :(": EXIT SUB
  XMSRegisters.AX = &H4310
  InterruptX &H2F, XMSRegisters, XMSRegisters
  PRINT "XMS address: "; RIGHT$("000" + HEX$(XMSRegisters.ES), 4); ":"; RIGHT$("000" + HEX$(XMSRegisters.BX), 4)
  ' Unfortunately, QBasic allows no way of directly calling the XMS APIs
  ' _and_ reading/writing the registers at the same time.
  ' The only way I see of accessing the extended memory is to write a short
  ' ASM program that does the dirty work for me. :S This will be very tough
  ' because I have very little experience in writing ASM. :(
END SUB

SUB LoadCHRROM (SourceBank%, DestinationBank%, LoadSize%)
  SEEK #1, PRGROMbanks * 16384& + Trainer + 17 + SourceBank% * LoadSize%
  CHRROM$ = INPUT$(LoadSize%, #1)
  PatternTableAddress% = DestinationBank% * LoadSize%
  FOR LoadByte% = 0 TO LoadSize% - 1
    PatternTables(PatternTableAddress% + LoadByte%) = ASC(MID$(CHRROM$, LoadByte% + 1, 1))
  NEXT LoadByte%
END SUB

SUB LoadPRGROM (SourceBank%, DestinationBank%)
  ROMSegment% = VARSEG(ROM(0)): ROMOffset& = VARPTR(ROM(0)) + DestinationBank% * 16384& + 4096
  SEEK #1, SourceBank% * 16384& + Trainer + 17: PRGROM$ = INPUT$(16384, #1)
  DEF SEG = ROMSegment%
  FOR LoadByte% = 0 TO 16383
    Value% = ASC(MID$(PRGROM$, LoadByte% + 1, 1))
    POKE ROMOffset& + LoadByte%, Value%
  NEXT LoadByte%
END SUB

SUB LoadROM (ROMFileName$)
  OPEN ROMFileName$ FOR BINARY AS #1
  IF LOF(1) = 0 THEN
    DrawText 0, "Invalid filename, terminating program...", 80, 254, 0
    CLOSE #1: KILL ROMFileName$: SLEEP: SCREEN 0: WIDTH 80, 25: SYSTEM
  END IF
  IF INPUT$(4, #1) <> "NES" + CHR$(26) THEN
    DrawText 0, "Invalid ROM header, terminating program...", 80, 254, 0
    CLOSE #1: KILL ROMFileName$: SLEEP: SCREEN 0: WIDTH 80, 25: SYSTEM
  END IF
  DrawText 1, "Loading ROM...", 80, 254, 1
  NESFileSize = LOF(1)
  PRGROMbanks = ASC(INPUT$(1, #1))
  CHRROMbanks = ASC(INPUT$(1, #1))
  Flags1 = ASC(INPUT$(1, #1))
  Flags2 = ASC(INPUT$(1, #1))
  OriginalMirroring = Flags1 AND 1
  IF Flags1 AND 4 THEN Trainer = 512
  IF Flags1 AND 8 THEN OriginalMirroring = -1
  IF OriginalMirroring = 1 THEN
    Mirroring(0) = 0: Mirroring(1) = 0
    Mirroring(2) = -2048: Mirroring(3) = -2048
  ELSEIF OriginalMirroring = 0 THEN
    Mirroring(0) = 0: Mirroring(1) = -1024
    Mirroring(2) = 0: Mirroring(3) = -1024
  ELSE
    Mirroring(0) = 0: Mirroring(1) = -1024
    Mirroring(2) = -2048: Mirroring(3) = -3072
  END IF
  Mapper = Flags1 \ 16 '+ (Flags2% AND 240)
  SEEK #1, 17
  IF Trainer THEN
    Buffer$ = INPUT$(512, #1)
    FOR LoadByte% = 0 TO 511
      Value% = ASC(MID$(Buffer$, LoadByte% + 1, 1))
      DEF SEG = VARSEG(ROM(0))
      POKE VARPTR(ROM(0)) + LoadByte%, Value%
    NEXT LoadByte%
  END IF
  SELECT CASE Mapper
    CASE 1, 2
      CurrentPRGROMbank(0) = 0: CurrentPRGROMbank(1) = PRGROMbanks - 1
      LoadPRGROM 0, 0: LoadPRGROM PRGROMbanks - 1, 1
      PRGROMswitching = 1
      IF CHRROMbanks THEN LoadCHRROM 0, 0, 8192
    CASE ELSE '0, 3, 7, 8, 11
      IF PRGROMbanks = 1 THEN LoadPRGROM 0, 1 ELSE LoadPRGROM 0, 0: LoadPRGROM 1, 1
      IF CHRROMbanks THEN LoadCHRROM 0, 0, 8192
    END SELECT
END SUB

FUNCTION MemoryRead% (Address&)
  SELECT CASE Address&
    CASE -2
      MemoryRead% = RegisterA
    CASE -1
      MemoryRead% = Argument1
    CASE &H0 TO &H1FFF
      MemoryRead% = RAM(Address&)
    CASE &H2000, &H2001
      MemoryRead% = MemoryRegisters(Address& AND 7)
    CASE &H2002
      MemoryRead% = MemoryRegisters(2)
      IF Vblank = 0 AND ((MemoryRegisters(0) AND 128) = 0) THEN MemoryRegisters(0) = MemoryRegisters(0) AND 252
      MemoryRegisters(2) = MemoryRegisters(2) AND 127
      MemoryRegisters(5) = 0
    CASE &H2004
      'IF Vblank THEN
        MemoryRead% = SpriteRAM(MemoryRegisters(3))
        MemoryRegisters(3) = MemoryRegisters(3) + 1 AND 255
      'END IF
    CASE &H2007
      'IF Vblank THEN
        IF MemoryRegisters(6) AND &H4000 THEN MemoryRegisters(6) = MemoryRegisters(6) - &H4000
        SELECT CASE MemoryRegisters(6)
          CASE &H0 TO &H1FFF
            MemoryRead% = PatternTables(MemoryRegisters(6))
          CASE &H2000 TO &H2FFF
            PPUaddress& = MemoryRegisters(6) - &H2000
            MemoryRead% = NameTables(PPUaddress& + Mirroring(PPUaddress& \ 1024))
          CASE &H3F00 TO &H3F0F
            MemoryRead% = BackgroundPalette(MemoryRegisters(6) AND 15)
          CASE &H3F10 TO &H3F1F
            MemoryRead% = SpritePalette(MemoryRegisters(6) AND 15)
          'CASE &H3F20 TO &H3FFF
          '  MemoryRegisters(6) = MemoryRegisters(6) AND &H3F1F
          '  MemoryRead% = MemoryRead%(Address&)
        END SELECT
        IF MemoryRegisters(7) THEN
          IF MemoryRegisters(0) AND 4 THEN MemoryRegisters(6) = MemoryRegisters(6) + 32 ELSE MemoryRegisters(6) = MemoryRegisters(6) + 1
        ELSE
          MemoryRegisters(7) = 1
        END IF
      'END IF
    CASE &H2008 TO &H3FFF
      MemoryRead% = MemoryRead%(Address& AND &H2007)
    CASE &H4016
      SELECT CASE MemoryRegisters(30)
        CASE 0 TO 7: IF Controller(1) AND PowerOfTwo(MemoryRegisters(30)) THEN MemoryRead% = &H41 ELSE MemoryRead% = 0
        CASE -1, 8 TO 17, 20 TO 22: MemoryRead% = 0
        CASE 18: MemoryRead% = 0
        CASE 19: MemoryRead% = 1
        CASE 23: MemoryRead% = 0: MemoryRegisters(30) = -2
      END SELECT
      MemoryRegisters(30) = MemoryRegisters(30) + 1
    CASE &H4017
      SELECT CASE MemoryRegisters(31)
        CASE 0 TO 7: IF Controller(2) AND PowerOfTwo(MemoryRegisters(31)) THEN MemoryRead% = &H41 ELSE MemoryRead% = 0
        CASE -1, 8 TO 17, 20 TO 22: MemoryRead% = 0
        CASE 18: MemoryRead% = 1
        CASE 19: MemoryRead% = 0
        CASE 23: MemoryRead% = 0: MemoryRegisters(31) = -2
      END SELECT
    CASE 28672 TO 29183, 32768 TO 65535
      DEF SEG = VARSEG(ROM(0))
      MemoryRead% = PEEK(VARPTR(ROM(0)) + Address& - 28672)
    CASE IS > 65535
      Address& = Address& AND 65535
      MemoryRead% = MemoryRead%(Address&)
  END SELECT
END FUNCTION

SUB MemoryWrite (Address&, Value%)
  SELECT CASE Address&
    CASE -2
      RegisterA = Value%
    CASE -1
      ERROR 250
    CASE &H0 TO &H1FFF
      RAM(Address&) = Value%
    CASE &H2000, &H2001, &H2003
      MemoryRegisters(Address& AND 7) = Value%
    CASE &H2004
      'IF Vblank THEN
        SpriteRAM(MemoryRegisters(3)) = Value%
        MemoryRegisters(3) = MemoryRegisters(3) + 1 AND 255
      'END IF
    CASE &H2005
      'IF Vblank THEN
        IF MemoryRegisters(5) THEN
          IF Value% < 240 THEN VerticalScroll = Value%
        ELSE
          HorizontalScroll = Value%
        END IF
        MemoryRegisters(5) = NOT MemoryRegisters(5)
      'END IF
    CASE &H2006
      'IF Vblank THEN
        MemoryRegisters(6) = (MemoryRegisters(6) AND 255) * 256 + Value%
        MemoryRegisters(7) = 0
      'END IF
    CASE &H2007
      'IF Vblank THEN
        IF MemoryRegisters(6) AND &H4000 THEN MemoryRegisters(6) = MemoryRegisters(6) - &H4000
        SELECT CASE MemoryRegisters(6)
          CASE &H0 TO &H1FFF
            PatternTables(MemoryRegisters(6)) = Value%
          CASE &H2000 TO &H2FFF
            PPUaddress& = MemoryRegisters(6) - &H2000
            NameTables(PPUaddress& + Mirroring(PPUaddress& \ 1024)) = Value%
          CASE &H3F00 TO &H3F1F
            IF Value% < &H40 THEN
              IF (MemoryRegisters(6) AND 3) = 0 THEN
                BackgroundPalette(0) = Value%
                BackgroundPalette(4) = Value%
                BackgroundPalette(8) = Value%
                BackgroundPalette(12) = Value%
                SpritePalette(0) = Value%
                SpritePalette(4) = Value%
                SpritePalette(8) = Value%
                SpritePalette(12) = Value%
              ELSE
                IF MemoryRegisters(6) AND &H10 THEN
                  SpritePalette(MemoryRegisters(6) AND 15) = Value%
                ELSE
                  BackgroundPalette(MemoryRegisters(6) AND 15) = Value%
                END IF
              END IF
            END IF
          'CASE &H3F20 TO &H3FFF
        END SELECT
        IF MemoryRegisters(0) AND 4 THEN MemoryRegisters(6) = MemoryRegisters(6) + 32 ELSE MemoryRegisters(6) = MemoryRegisters(6) + 1
      'END IF
    CASE &H2008 TO &H3FFF
      Address& = Address& AND &H2007
      MemoryWrite Address&, Value%
    CASE &H4014
      'IF Vblank THEN
        TransferAddress& = Value% * 256&
        FOR TransferByte% = 0 TO 255
          Value% = MemoryRead%(TransferAddress& + TransferByte%)
          SpriteRAM(TransferByte%) = Value%
        NEXT TransferByte%
      'END IF
    CASE &H4016
      IF Value% AND 1 THEN
        MemoryRegisters(30) = -1
      ELSEIF MemoryRegisters(30) = -1 THEN
        MemoryRegisters(30) = 0
      END IF
    CASE &H4017
      IF Value% AND 1 THEN
        MemoryRegisters(30) = -1
      ELSEIF MemoryRegisters(30) = -1 THEN
        MemoryRegisters(30) = 0
      END IF
    CASE IS > 65535
      Address& = Address& AND 65535
      MemoryWrite Address&, Value%
    CASE ELSE
      SELECT CASE Mapper
        CASE 1
          Address& = Address& \ 8192 - 4
          IF WhichMapperRegister <> Address& THEN
            WhichMapperRegister = Address&
            MapperRegisterBit = 0: MapperRegister = 0
          END IF
          IF Value% AND 1 THEN MapperRegister = MapperRegister OR PowerOfTwo(MapperRegisterBit)
          MapperRegisterBit = MapperRegisterBit + 1
          IF MapperRegisterBit = 5 THEN
            IF Address& = 0 THEN
              OldPRGROMbank(0) = CurrentPRGROMbank(0)
              OldPRGROMbank(1) = CurrentPRGROMbank(1)
              IF MapperRegister AND 2 THEN
                IF MapperRegister AND 1 THEN
                  Mirroring(0) = 0: Mirroring(1) = 0
                  Mirroring(2) = -2048: Mirroring(3) = -2048
                ELSE
                  Mirroring(0) = 0: Mirroring(1) = -1024
                  Mirroring(2) = 0: Mirroring(3) = -1024
                END IF
              ELSE
                Mirroring(0) = 0: Mirroring(1) = -1024
                Mirroring(2) = -2048: Mirroring(3) = -3072
              END IF
              IF MapperRegister AND 8 THEN
                IF MapperRegister AND 4 THEN
                  IF PRGROMswitching = 1 THEN CurrentPRGROMbank(0) = CurrentPRGROMbank(1)
                  CurrentPRGROMbank(1) = PRGROMbanks - 1
                ELSE
                  IF PRGROMswitching = 0 OR PRGROMswitching = 2 THEN CurrentPRGROMbank(1) = CurrentPRGROMbank(0)
                  CurrentPRGROMbank(0) = 0
                END IF
                IF OldPRGROMbank(0) <> CurrentPRGROMbank(0) THEN LoadPRGROM CurrentPRGROMbank(0), 0
                IF OldPRGROMbank(1) <> CurrentPRGROMbank(1) THEN LoadPRGROM CurrentPRGROMbank(1), 1
                IF MapperRegister AND 4 THEN PRGROMswitching = 1 ELSE PRGROMswitching = 0
              ELSE
                IF PRGROMswitching = 0 THEN CurrentPRGROMbank(0) = CurrentPRGROMbank(1)
                CurrentPRGROMbank(1) = -1
                IF OldPRGROMbank(0) <> CurrentPRGROMbank(0) THEN LoadPRGROM CurrentPRGROMbank(0) AND 14, 0
                IF OldPRGROMbank(1) <> CurrentPRGROMbank(1) THEN LoadPRGROM (CurrentPRGROMbank(0) AND 14) + 1, 1
                PRGROMswitching = 2
              END IF
              IF CHRROMswitching <> MapperRegister AND 16 THEN
                IF MapperRegister AND 16 THEN
                  CHRROMswitching = 1
                  CurrentCHRROMbank(0) = CurrentCHRROMbank(0)
                  CurrentCHRROMbank(1) = CurrentCHRROMbank(0) + 1
                ELSE
                  CHRROMswitching = 0
                  CurrentCHRROMbank(0) = CurrentCHRROMbank(0)
                END IF
              END IF
            ELSEIF Address& = 1 THEN
              IF CHRROMbanks <> 0 AND CurrentCHRROMbank(0) <> (MapperRegister AND 15) THEN
                CurrentCHRROMbank(0) = MapperRegister AND 15
                IF CHRROMswitching THEN
                  LoadCHRROM 0, CurrentCHRROMbank(0) \ 2, 4096
                ELSE
                  LoadCHRROM 0, CurrentCHRROMbank(0), 8192
                END IF
              END IF
            ELSEIF Address& = 2 THEN
              IF CHRROMbanks <> 0 AND CurrentCHRROMbank(1) <> (MapperRegister AND 15) THEN
                CurrentCHRROMbank(1) = MapperRegister AND 15
                IF CHRROMswitching THEN LoadCHRROM 1, CurrentCHRROMbank(1), 4096
              END IF
            ELSEIF Address& = 3 THEN
              IF PRGROMswitching = 1 THEN
                IF CurrentPRGROMbank(0) <> MapperRegister AND 15 THEN
                  CurrentPRGROMbank(0) = MapperRegister AND 15
                  LoadPRGROM CurrentPRGROMbank(0), 0
                END IF
              ELSEIF PRGROMswitching = 0 THEN
                IF CurrentPRGROMbank(1) <> MapperRegister AND 15 THEN
                  CurrentPRGROMbank(1) = MapperRegister AND 15
                  LoadPRGROM CurrentPRGROMbank(1), 1
                END IF
              ELSEIF PRGROMswitching = 2 THEN
                IF CurrentPRGROMbank(0) <> MapperRegister AND 14 THEN
                  CurrentPRGROMbank(0) = MapperRegister AND 14
                  LoadPRGROM CurrentPRGROMbank(0), 0
                  LoadPRGROM CurrentPRGROMbank(0) + 1, 1
                END IF
              END IF
            END IF
            MapperRegisterBit = 0: MapperRegister = 0
          END IF
        CASE 2
          IF Address& AND 32768 THEN
            IF Value% <> CurrentPRGROMbank(0) THEN
              LoadPRGROM Value%, 0
              CurrentPRGROMbank(0) = Value%
            END IF
          END IF
        CASE 3
          IF Address& AND 32768 THEN
            IF Value% <> CurrentCHRROMbank(0) THEN
              LoadCHRROM Value%, 0, 8192
              CurrentCHRROMbank(0) = Value%
            END IF
          END IF
        CASE 7
          IF Address& AND 32768 THEN
            IF Value% AND 15 <> CurrentPRGROMbank(0) THEN
              CurrentPRGROMbank(0) = Value% AND 15
              LoadPRGROM Value% * 2, 0
              LoadPRGROM Value% * 2 + 1, 1
            END IF
            IF Value% AND 16 THEN
              Mirroring(0) = 1024: Mirroring(1) = 0
              Mirroring(2) = -1024: Mirroring(3) = -2048
            ELSE
              Mirroring(0) = 0: Mirroring(1) = -1024
              Mirroring(2) = -2048: Mirroring(3) = -3072
            END IF
          END IF
        CASE 8
          IF Address& AND 32768 THEN
            IF Value% \ 8 <> CurrentPRGROMbank(0) THEN
              LoadPRGROM Value%, 0
              CurrentPRGROMbank(0) = Value% \ 8
            END IF
            IF Value% AND 7 <> CurrentCHRROMbank(0) THEN
              LoadCHRROM Value% AND 7, 0, 8192
              CurrentCHRROMbank(0) = Value% AND 7
            END IF
          END IF
        CASE 11
          IF Address& AND 32768 THEN
            IF Value% AND 15 <> CurrentPRGROMbank(0) THEN
              CurrentPRGROMbank(0) = Value% AND 15
              LoadPRGROM Value% * 2, 0
              LoadPRGROM Value% * 2 + 1, 1
            END IF
            IF Value% \ 16 <> CurrentCHRROMbank(0) THEN
              CurrentCHRROMbank(0) = Value% \ 16
              LoadCHRROM Value% \ 16, 0, 8192
            END IF
          END IF
      END SELECT
  END SELECT
END SUB

SUB MouseDriver (AX%, BX%, CX%, DX%)
  IF NoMouse THEN EXIT SUB
  DIM MouseRegisters AS x86Registers
  MouseRegisters.AX = AX%
  MouseRegisters.BX = BX%
  MouseRegisters.CX = CX%
  MouseRegisters.DX = DX%
  InterruptX &H33, MouseRegisters, MouseRegisters
  AX% = MouseRegisters.AX
  BX% = MouseRegisters.BX
  CX% = MouseRegisters.CX
  DX% = MouseRegisters.DX
END SUB

FUNCTION Pop%
  RegisterS = RegisterS + 1 AND 255
  Pop% = RAM(RegisterS + 256)
  'DEF SEG = VARSEG(RAM)
  'Pop% = PEEK(VARPTR(RAM) + RegisterS + 256)
END FUNCTION

SUB Push (Value%)
  'DEF SEG = VARSEG(RAM)
  'POKE VARPTR(RAM) + RegisterS + 256, Value%
  RAM(RegisterS + 256) = Value%
  RegisterS = RegisterS - 1 AND 255
END SUB

FUNCTION TextBox$ (Column%, Row%, Length%)
  DrawObject 21, Column% - 2, Row% - 2, Column% + Length% * 8 + 1, Row% + 17
  DrawText 1, STRING$(Length%, "°"), Column%, Row%, 10
  CursorPosition% = Column%
  DO
    DO
      PressedButton$ = INKEY$
      IF PressedButton$ <> "" THEN EXIT DO
    LOOP
    SELECT CASE ASC(PressedButton$)
      CASE 8
        IF CursorPosition% > Column% THEN
          TextString$ = LEFT$(TextString$, LEN(TextString$) - 1)
          CursorPosition% = CursorPosition% - 8
          LINE (CursorPosition%, Row%)-(CursorPosition% + 7, Row% + 15), 13, BF
          DrawText 1, "°", CursorPosition%, Row%, 10
        END IF
      CASE 13: EXIT DO
      CASE 32 TO 126
        IF CursorPosition% < Column% + Length% * 8 THEN
          TextString$ = TextString$ + PressedButton$
          DrawText 0, PressedButton$, CursorPosition%, Row%, 0
          CursorPosition% = CursorPosition% + 8
        END IF
    END SELECT
  LOOP
  TextBox$ = TextString$
END FUNCTION

SUB WaitVblank (Waits%)
  FOR Counter% = 1 TO Waits%
    WAIT &H3DA, 8, 8: WAIT &H3DA, 8
  NEXT Counter%
END SUB

