////////////////////////////////////////////////////////////////////// // // // observerVRAM.pas: VRAM observer (all video modes) // // // // The contents of this file are subject to the Bottled Light // // Public License Version 1.0 (the "License"); you may not use this // // file except in compliance with the License. You may obtain a // // copy of the License at http://www.bottledlight.com/BLPL/ // // // // Software distributed under the License is distributed on an // // "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or // // implied. See the License for the specific language governing // // rights and limitations under the License. // // // // The Original Code is the Mappy VM User Interface, released // // April 1st, 2003. The Initial Developer of the Original Code is // // Bottled Light, Inc. Portions created by Bottled Light, Inc. are // // Copyright (C) 2001-2003 Bottled Light, Inc. All Rights Reserved. // // // // Author(s): // // Michael Noland (joat), michael@bottledlight.com // // // // Changelog: // // 1.0: First public release (April 1st, 2003) // // // // Notes: // // When this observer is active, it slows everything down // // considerably, its probably a good candidate for optimization. // // // ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// unit observerVRAM; /////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// interface //////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, IniFiles, CheckLst, StdCtrls, ComCtrls, Menus, ExtCtrls, Math, ClipBrd, CpuObservers, console, nexus, AddressSpace; ////////////////////////////////////////////////////////////////////// type TjdevVRAMViewer = class(TCpuObserver) pages: TPageControl; pTileMap: TTabSheet; pTiles: TTabSheet; pSprites: TTabSheet; pFramebuffer: TTabSheet; rMode3: TRadioButton; rMode4fb0: TRadioButton; rMode5fb0: TRadioButton; Label1: TLabel; rMode5fb1: TRadioButton; rMode4fb1: TRadioButton; cbScreenSize: TComboBox; cbTileMap: TComboBox; cbTileSet: TComboBox; cbBackgrounds: TComboBox; loadBGButton: TButton; r16Colors: TRadioButton; r256Colors: TRadioButton; lMapLocation: TLabel; lTilesetLocation: TLabel; lScreenSize: TLabel; lGeneral: TLabel; rWraparound: TCheckBox; lColorMode: TLabel; rRotScale: TCheckBox; lxferSettings: TLabel; bevel: TBevel; image: TPaintBox; horizScroll: TScrollBar; vertScroll: TScrollBar; cbTilesetTiles: TComboBox; lTilesetLocationTiles: TLabel; r256ColorTiles: TRadioButton; r16ColorTiles: TRadioButton; lColorModeTiles: TLabel; lColorModeSprites: TLabel; r16ColorSprites: TRadioButton; r256ColorSprites: TRadioButton; saveBGButton: TButton; lPosition: TLabel; lPosX: TLabel; paletteBase: TScrollBar; lPaletteBase: TLabel; lPaletteBaseDisp: TLabel; lPaletteBase2: TLabel; lPaletteBaseDisp2: TLabel; paletteBase2: TScrollBar; pLayerSheet: TTabSheet; activeLayers: TCheckListBox; Label2: TLabel; bResetLayers: TButton; Label3: TLabel; lLayerPos: TLabel; lLayerColor: TLabel; lLayerLayer: TLabel; bSaveToFile: TButton; bCopyToClipboard: TButton; saveDialog: TSaveDialog; magnifier: TTrackBar; lMagnifier: TLabel; bSaveLayers: TButton; lTileMapCR: TLabel; procedure FormCreate(Sender: TObject); procedure toggleRotScaleMode(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure UpdateObserverX(Sender: TObject); procedure paletteBaseChange(Sender: TObject); procedure loadBGSettings(Sender: TObject); procedure SaveBGSettings(Sender: TObject); procedure paletteBase2Change(Sender: TObject); procedure UpdateScrollDisp(Sender: TObject); procedure reloadLayersFromDispCR(Sender: TObject); procedure imageMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure SaveToFile(Sender: TObject); procedure CopyToClipboard(Sender: TObject); procedure SaveLayersToDispCR(Sender: TObject); procedure imagePaint(Sender: TObject); private // banks is only valid in the functions below banks: TvmMemoryLock1; backups: array[0..REGISTERS_MASK] of byte; myScreen: TBitmap; active: boolean; mult: integer; layerWatchX, layerWatchY: integer; tileWidth, tileHeight: integer; lastFx, lastFy: integer; zoom: integer; pageIndex: integer; procedure RenderTileMap; procedure RenderTiles; procedure RenderSprites; procedure RenderFramebuffer; procedure RenderLayers; function PackBGSettings: uint16; procedure UnpackBGSettings(cr: uint16); procedure DispScreen; procedure DoScrollbar; public procedure UpdateObserver; override; class function OCaption: string; override; procedure LoadSettings(ini: TIniFile); override; procedure SaveSettings(ini: TIniFile); override; end; ////////////////////////////////////////////////////////////////////// var jdevVRAMViewer: TjdevVRAMViewer; ////////////////////////////////////////////////////////////////////// implementation /////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// {$R *.DFM} const binarySt: array[0..3] of string = ('%00', '%01', '%10', '%11'); normalSizes: array[0..3] of TPoint = ((x:256; y:256), (x:512; y:256), (x:256; y:512), (x:512; y:512)); rotScaleSizes: array[0..3] of TPoint = ((x:128; y:128), (x:256; y:256), (x:512; y:512), (x:1024; y:1024)); ////////////////////////////////////////////////////////////////////// class function TjdevVRAMViewer.OCaption: string; begin Result := 'Video Viewer'; end; ////////////////////////////////////////////////////////////////////// const layerStrings: array[0..5] of string = ( 'BG0', 'BG1', 'BG2', 'BG3', 'Sprite', 'Backdrop' ); ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.UpdateObserver; var x, y: integer; line: Puint16; begin // Precautions if not active then Exit; active := false; // Lock VM memory and make some backups vmLockMemory(banks); Move(banks.iospace^, backups, REGISTERS_MASK+1); // Render the observer display banks.iospace^[BLEND_S1] := 0; case pages.ActivePageIndex of 0: RenderTileMap; 1: RenderTiles; 2: RenderSprites; 3: RenderFramebuffer; 4: RenderLayers; end; // Remap the colors to BGR, because windows is stupid if pages.ActivePageIndex in [0, 3, 4] then for y := 0 to myScreen.Height - 1 do begin line := myScreen.ScanLine[y]; for x := 0 to myScreen.Width - 1 do begin line^ := (line^ shr 10) and $1f + (line^ and $1f) shl 10 + ((line^ shr 5) and $1f) shl 5; Inc(line); end; end; // Draw it DispScreen; // Restore the backups and unlock VM memory Move(backups, banks.iospace^, REGISTERS_MASK+1); vmUnlockMemory(banks); active := true; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.FormCreate(Sender: TObject); var i: integer; begin HelpContext := LinkHelp('video_viewer.html'); if (pageIndex >= 0) and (pageIndex < pages.PageCount) then pages.ActivePageIndex := pageIndex; if (zoom > 0) and (zoom <= magnifier.Max) then magnifier.Position := zoom; // Add entries to the tile map box, in increments of 2kb for i := 0 to 31 do cbTileMap.Items.Add(Format('$%2.2x ($%8.8x)', [i, $06000000+i*2048])); cbTileMap.ItemIndex := 0; // Set up the tileset address boxes and the screen size box for i := 0 to 3 do begin cbTileSet.Items.Add(Format('%s ($%8.8x)', [binarySt[i], $06000000+i*16384])); cbTileSetTiles.Items.Add(Format('%s ($%8.8x)', [binarySt[i], $06000000+i*16384])); end; cbTileSet.ItemIndex := 0; cbTileSetTiles.ItemIndex := 0; // Take care of the backgrounds avaiable for transfer for i := 0 to 3 do cbBackgrounds.Items.Add(Format('Background %d', [i])); cbBackgrounds.ItemIndex := 0; // Set up the frame buffer selection on the frame buffer page rMode4fb0.Checked := true; // Set up the color mode on each page r256Colors.Checked := true; r16Colors.Checked := not r256Colors.Checked; r256ColorTiles.Checked := true; r256ColorSprites.Checked := true; // Set up the general settins on the tile maps page rRotScale.Checked := false; rWraparound.Checked := false; rWraparound.Enabled := false; // Set up the default screen sizes for i := 0 to 3 do cbScreenSize.Items.Add(Format('%s (%dx%d)', [binarySt[i], normalSizes[i].x, normalSizes[i].y])); cbScreenSize.ItemIndex := 0; // Set up the screen myScreen := TBitmap.Create; myScreen.PixelFormat := pf15bit; myScreen.width := 256; myScreen.height := 256; // Set up the layers page reloadLayersFromDispCR(Sender); layerWatchX := -1; layerWatchY := -1; active := true; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.toggleRotScaleMode(Sender: TObject); var i: integer; sizeIndex: integer; begin sizeIndex := cbScreenSize.ItemIndex; if sizeIndex < 0 then sizeIndex := 0; if rRotScale.Checked then begin // Toggle some things rWraparound.Enabled := true; r16Colors.Enabled := false; r256Colors.Checked := true; r16Colors.Checked := not r256Colors.Checked; // Take care of the screen size cbScreenSize.Items.Clear; for i := 0 to 3 do cbScreenSize.Items.Add(Format('%s (%dx%d)', [binarySt[i], rotScaleSizes[i].x, rotScaleSizes[i].y])); cbScreenSize.ItemIndex := 0; // Take care of the backgrounds avaiable for transfer cbBackgrounds.Clear; for i := 2 to 3 do cbBackgrounds.Items.Add(Format('Background %d', [i])); cbBackgrounds.ItemIndex := 0; end else begin // Toggle some things rWraparound.Enabled := false; r16Colors.Enabled := true; // Take care of the screen size cbScreenSize.Items.Clear; for i := 0 to 3 do cbScreenSize.Items.Add(Format('%s (%dx%d)', [binarySt[i], normalSizes[i].x, normalSizes[i].y])); cbScreenSize.ItemIndex := 0; // Take care of the backgrounds avaiable for transfer cbBackgrounds.Clear; for i := 0 to 3 do cbBackgrounds.Items.Add(Format('Background %d', [i])); cbBackgrounds.ItemIndex := 0; end; cbScreenSize.ItemIndex := sizeIndex; // Redraw UpdateScrollDisp(Sender); UpdateObserver; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.FormDestroy(Sender: TObject); begin myScreen.Free; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.UpdateObserverX(Sender: TObject); begin zoom := magnifier.Position; pageIndex := pages.ActivePageIndex; DoScrollbar; UpdateObserver; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.paletteBaseChange(Sender: TObject); begin lPaletteBaseDisp.Caption := Format('%d ($%2.2x)', [paletteBase.Position, paletteBase.Position*16]); UpdateObserver; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.loadBGSettings(Sender: TObject); var index: integer; begin if cbBackgrounds.ItemIndex < 0 then cbBackgrounds.ItemIndex := 0; index := cbBackgrounds.ItemIndex; if rRotScale.Checked then Inc(index, 2); UnpackBGSettings(vmReadHalfword($04000000 + BG0_CR + index shl 1)); UpdateObserver; end; ////////////////////////////////////////////////////////////////////// function TjdevVRAMViewer.PackBGSettings: uint16; var cr: uint16; begin cr := 0; if cbTileSet.ItemIndex < 0 then cbTileSet.ItemIndex := 0; cr := cr or (cbTileSet.ItemIndex shl 2); if rWraparound.Checked then cr := cr or (1 shl 13); if r256Colors.Checked then cr := cr or TEXT_BG_256COLORS; if cbTileMap.ItemIndex < 0 then cbTileMap.ItemIndex := 0; cr := cr or (cbTileMap.ItemIndex shl 8); if cbScreenSize.ItemIndex < 0 then cbScreenSize.ItemIndex := 0; cr := cr or (cbScreenSize.ItemIndex shl 14); Result := cr; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.UnpackBGSettings(cr: uint16); begin // Pack the tile set r256Colors.Checked := cr and TEXT_BG_256COLORS <> 0; rWraparound.Checked := cr and (1 shl 13) <> 0; cbTileSet.ItemIndex := (cr shr 2) and 3; cbTileMap.ItemIndex := (cr shr 8) and $1F; cbScreenSize.ItemIndex := cr shr 14; if rRotScale.Checked then r256Colors.Checked := true; r16Colors.Checked := not r256Colors.Checked; DoScrollbar; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.SaveBGSettings(Sender: TObject); begin if cbBackgrounds.ItemIndex < 0 then cbBackgrounds.ItemIndex := 0; vmWriteHalfword($04000000 + BG0_CR + cbBackgrounds.ItemIndex shl 1, PackBGSettings); end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.paletteBase2Change(Sender: TObject); begin lPaletteBaseDisp2.Caption := Format('%d ($%2.2x)', [paletteBase2.Position, paletteBase2.Position*16]); UpdateObserver; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.UpdateScrollDisp(Sender: TObject); begin if rRotScale.Checked then begin lPosX.Caption := Format('X: %d.0, Y: %d.0', [horizScroll.position, vertScroll.position]); end else begin lPosX.Caption := Format('X: %d, X: %d', [horizScroll.position, vertScroll.position]); end; UpdateObserver; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.reloadLayersFromDispCR(Sender: TObject); var da: byte; index: integer; begin da := vmReadByte($04000000 + DISPLAY_ACTIVE); for index := 0 to 7 do activeLayers.Checked[index] := da and (1 shl index) <> 0; UpdateObserver; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.SaveLayersToDispCR(Sender: TObject); var da: byte; index: integer; begin da := 0; for index := 0 to 7 do if activeLayers.Checked[index] then da := da or (1 shl index); vmWriteByte($04000000 + DISPLAY_ACTIVE, da); end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.imageMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin layerWatchX := X; layerWatchY := Y; lLayerPos.Caption := Format('Position: (%d, %d)', [layerWatchX, layerWatchY]); lLayerLayer.Caption := 'Top layer: '; lLayerColor.Caption := 'Color: '; lLayerPos.Visible := true; lLayerLayer.Visible := true; lLayerColor.Visible := true; UpdateObserver; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.SaveToFile(Sender: TObject); begin saveDialog.Filter := 'Bitmap Images|*.bmp|All files|*.*'; saveDialog.DefaultExt := 'bmp'; if saveDialog.Execute then myScreen.SaveToFile(saveDialog.FileName); end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.CopyToClipboard(Sender: TObject); var myFormat: word; data: THandle; pal: HPalette; begin myScreen.SaveToClipBoardFormat(myFormat, data, pal); clipboard.SetAsHandle(myFormat, data); end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.DispScreen; var x, y: integer; begin if pages.activePageIndex = 0 then begin image.Canvas.StretchDraw(Rect(0, 0, mult*256, mult*256), myScreen); x := lastFx; y := lastFy; // Draw the main piece image.Canvas.DrawFocusRect(Rect(x*mult, y*mult, (x+240)*mult-1, (y+160)*mult-1)); // Display the (bottom) left wraparound portion if any if x+240 >= tileWidth then begin x := x - tileWidth; image.Canvas.DrawFocusRect(Rect(x*mult, y*mult, (x+240)*mult-1, (y+160)*mult-1)); end; // Draw the top wraparound portions, if any if y+160 >= tileHeight then begin // Draw the top (left) wraparound portion y := y - tileHeight; image.Canvas.DrawFocusRect(Rect(x*mult, y*mult, (x+240)*mult-1, (y+160)*mult-1)); // Draw the top right wraparound portion (if any) if x <> lastFx then begin x := lastFx; image.Canvas.DrawFocusRect(Rect(x*mult, y*mult, (x+240)*mult-1, (y+160)*mult-1)); end; end; end else begin image.Canvas.StretchDraw(Rect(-mult*horizScroll.Position, -mult*vertScroll.Position, 256*mult-mult*horizScroll.Position, 256*mult-mult*vertScroll.Position), myScreen); end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.imagePaint(Sender: TObject); begin DispScreen; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.DoScrollbar; var i: integer; begin mult := magnifier.position; case pages.ActivePageIndex of 0: begin i := cbScreenSize.ItemIndex; if i = -1 then i := 0; if rRotScale.Checked then begin horizScroll.Max := Max(rotScaleSizes[i].x - 1 - image.width div mult, 0); vertScroll.Max := Max(rotScaleSizes[i].y - 1 - image.height div mult, 0); r256Colors.Checked := true; tileWidth := rotScaleSizes[i].x; tileHeight := rotScaleSizes[i].y; end else begin horizScroll.Max := Max(normalSizes[i].x - 1 - image.width div mult, 0); vertScroll.Max := Max(normalSizes[i].y - 1 - image.height div mult, 0); tileWidth := normalSizes[i].x; tileHeight := normalSizes[i].y; end; end; 1: begin horizScroll.Max := Max(127 - image.width div mult, 0); vertScroll.Max := Max(127 - image.height div mult, 0); end; 2: begin horizScroll.Max := Max(255 - image.width div mult, 0); vertScroll.Max := Max(127 - image.height div mult, 0); end; 3, 4: begin horizScroll.Max := Max(239 - image.width div mult, 0); vertScroll.Max := Max(159 - image.height div mult, 0); end; end; horizScroll.Enabled := horizScroll.Max <> 0; vertScroll.Enabled := vertScroll.Max <> 0; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.RenderTileMap; var x, y: integer; line: Puint16; begin // Render a tile map Puint16(@(banks.iospace^[BG2_CR]))^ := PackBgSettings; banks.iospace^[DISPLAY_ACTIVE] := 4; if rRotScale.Checked then begin banks.iospace^[DISPLAY_CR] := 1; Puint16(@(banks.iospace^[BG2_A]))^ := 256; Puint16(@(banks.iospace^[BG2_C]))^ := 0; Puint32(@(banks.iospace^[BG2_X]))^ := horizScroll.Position shl 8; Puint32(@(banks.iospace^[BG2_Y]))^ := vertScroll.Position shl 8; end else begin banks.iospace^[DISPLAY_CR] := 0; Puint16(@(banks.iospace^[BG2_X0]))^ := horizScroll.Position; Puint16(@(banks.iospace^[BG2_Y0]))^ := vertScroll.Position; end; // Draw the screen for y := 0 to 255 do begin line := vmDrawScanline(y, 256); Move(line^, myScreen.ScanLine[y]^, 256*2); Inc(Puint32(@(banks.iospace^[BG2_Y]))^, 256); end; // Draw some selection windows if rRotScale.Checked then begin x := Puint32(@(backups[BG2_X + cbBackgrounds.ItemIndex*$10]))^ shr 8; y := Puint32(@(backups[BG2_Y + cbBackgrounds.ItemIndex*$10]))^ shr 8; end else begin x := Puint16(@(backups[BG0_X0 + cbBackgrounds.ItemIndex*4]))^; y := Puint16(@(backups[BG0_Y0 + cbBackgrounds.ItemIndex*4]))^; end; x := x - horizScroll.Position; y := y - vertScroll.Position; lastFx := x; lastFy := y; lTileMapCR.Caption := 'Current CR: $' + IntToHex(PackBgSettings, 4); end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.RenderTiles; var x, y, rbase, base, palBase: integer; pix: uint16; line: Puint16; begin // BG tile rendering // Render tiles if cbTileSetTiles.ItemIndex < 0 then cbTileSetTiles.ItemIndex := 0; rbase := cbTileSetTiles.ItemIndex * $4000; if r256ColorTiles.Checked then begin // Draw the tiles as 256 color ones for y := 0 to 127 do begin base := rbase + (y and 7) shl 3 + (y shr 3) shl 10; line := @(myScreen.ScanLine[y]^); for x := 0 to 127 do begin pix := banks.VRAM[base + x and 7 + (x shr 3) shl 6]; pix := Puint16(@(banks.palette[pix shl 1]))^; line^ := (pix shr 10) and $1f + (pix and $1f) shl 10 + ((pix shr 5) and $1f) shl 5; Inc(line); end; // Fill the rest of the line with backdrop pix := Puint16(@(banks.palette[0]))^; pix := (pix shr 10) and $1f + (pix and $1f) shl 10 + ((pix shr 5) and $1f) shl 5; for x := 128 to 255 do begin line^ := pix; Inc(line); end; end; // Fill the rest of the lines with backdrop pix := Puint16(@(banks.palette[0]))^; pix := (pix shr 10) and $1f + (pix and $1f) shl 10 + ((pix shr 5) and $1f) shl 5; for y := 128 to 255 do begin line := @(myScreen.ScanLine[y]^); for x := 0 to 255 do begin line^ := pix; Inc(line); end; end; end else begin // Draw the tiles as 16 color ones palBase := paletteBase.Position*32; for y := 0 to 127 do begin base := rbase + (y and 7) shl 2 + (y shr 3) shl 10; line := @(myScreen.ScanLine[y]^); for x := 0 to 255 do begin pix := banks.VRAM[base + (x and 7) shr 1 + (x shr 3) shl 5]; if Odd(x) then pix := pix shr 4 else pix := pix and $F; pix := Puint16(@(banks.palette[pix shl 1 + palBase]))^; line^ := (pix shr 10) and $1f + (pix and $1f) shl 10 + ((pix shr 5) and $1f) shl 5; Inc(line); end; end; // Fill the rest of the lines with backdrop pix := Puint16(@(banks.palette[0]))^; pix := (pix shr 10) and $1f + (pix and $1f) shl 10 + ((pix shr 5) and $1f) shl 5; for y := 128 to 255 do begin line := @(myScreen.ScanLine[y]^); for x := 0 to 255 do begin line^ := pix; Inc(line); end; end; end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.RenderSprites; var x, y, rbase, base, palBase: integer; pix: uint16; line: Puint16; begin // Sprite tile rendering // Render sprites if r256ColorSprites.Checked then begin rbase := $10000; palBase := 512; // Draw the tiles as 256 color ones for y := 0 to 127 do begin base := rbase + (y and 7) shl 3 + (y shr 3) shl 11; line := @(myScreen.ScanLine[y]^); for x := 0 to 255 do begin pix := banks.VRAM[base + x and 7 + (x shr 3) shl 6]; pix := Puint16(@(banks.palette[pix shl 1 + palBase]))^; line^ := (pix shr 10) and $1f + (pix and $1f) shl 10 + ((pix shr 5) and $1f) shl 5; Inc(line); end; end; // Fill the rest of the lines with backdrop pix := Puint16(@(banks.palette[0]))^; pix := (pix shr 10) and $1f + (pix and $1f) shl 10 + ((pix shr 5) and $1f) shl 5; for y := 128 to 255 do begin line := @(myScreen.ScanLine[y]^); for x := 0 to 255 do begin line^ := pix; Inc(line); end; end; end else begin // Draw the tiles as 16 color ones palBase := paletteBase2.Position*32+512; for y := 0 to 255 do begin base := $10000 + (y and 7) shl 2 + (y shr 3) shl 10; line := @(myScreen.ScanLine[y]^); for x := 0 to 255 do begin pix := banks.VRAM[base + (x and 7) shr 1 + (x shr 3) shl 5]; if Odd(x) then pix := pix shr 4 else pix := pix and $F; pix := Puint16(@(banks.palette[pix shl 1 + palBase]))^; line^ := (pix shr 10) and $1f + (pix and $1f) shl 10 + ((pix shr 5) and $1f) shl 5; Inc(line); end; end; end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.RenderFramebuffer; var x, y: integer; pix: uint16; line: Puint16; begin // Framebuffer rendering // Render the frame buffer banks.iospace^[DISPLAY_ACTIVE] := 4; if rMode4fb0.Checked then banks.iospace^[DISPLAY_CR] := 4 else if rMode4fb1.Checked then banks.iospace^[DISPLAY_CR] := 4 or DISPLAY_CR_FB else if rMode5fb0.Checked then banks.iospace^[DISPLAY_CR] := 5 else if rMode5fb1.Checked then banks.iospace^[DISPLAY_CR] := 5 or DISPLAY_CR_FB else banks.iospace^[DISPLAY_CR] := 3; Puint32(@(banks.iospace^[BG2_X]))^ := 0; Puint32(@(banks.iospace^[BG2_Y]))^ := 0; for y := 0 to 159 do begin // Render a bitmapped scanline line := vmDrawScanline(y, 256); Move(line^, myScreen.ScanLine[y]^, 256*2); Inc(Puint32(@(banks.iospace^[BG2_Y]))^, 256); // Fill the rest of the line with backdrop line := @(PWordArray(line)^[240]); pix := Puint16(@(banks.palette[0]))^; for x := 240 to 255 do begin line^ := pix; Inc(line); end; end; // Fill the rest of the lines with backdrop pix := Puint16(@(banks.palette[0]))^; for y := 160 to 255 do begin line := @(myScreen.ScanLine[y]^); for x := 0 to 255 do begin line^ := pix; Inc(line); end; end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.RenderLayers; var x, y: integer; pix: uint16; line: Puint16; begin // Layer rendering pix := 0; for y := 0 to 7 do if activeLayers.Checked[y] then pix := pix or (1 shl y); banks.iospace^[DISPLAY_ACTIVE] := pix; for y := 0 to 159 do begin // Draw line := vmDrawScanline(y, 256); Move(line^, myScreen.ScanLine[y]^, 256*2); Inc(Puint32(@(banks.iospace^[BG2_Y]))^, 256); // Layer watch if (y = layerWatchY) and (layerWatchX > -1) and (layerWatchX < 240) then begin pix := PWordArray(line)^[layerWatchX]; lLayerLayer.Caption := Format('Top layer: %s', [layerStrings[vmGetLayerID(layerWatchX)] ]); lLayerColor.Caption := Format('Color: (%d, %d, %d)', [(pix shr 10) and $1F, (pix shr 5) and $1F, pix and $1F]); end; // Fill the rest of the line with backdrop line := @(PWordArray(line)^[240]); pix := Puint16(@(banks.palette[0]))^; for x := 240 to 255 do begin line^ := pix; Inc(line); end; end; // Fill the rest of the lines with backdrop pix := Puint16(@(banks.palette[0]))^; for y := 160 to 255 do begin line := @(myScreen.ScanLine[y]^); for x := 0 to 255 do begin line^ := pix; Inc(line); end; end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.LoadSettings(ini: TIniFile); begin inherited; zoom := ini.ReadInteger(OCaption, 'Zoom', 1); pageIndex := ini.ReadInteger(OCaption, 'LastPage', 0); end; ////////////////////////////////////////////////////////////////////// procedure TjdevVRAMViewer.SaveSettings(ini: TIniFile); begin inherited; ini.WriteInteger(OCaption, 'Zoom', zoom); ini.WriteInteger(OCaption, 'LastPage', pageIndex); end; ////////////////////////////////////////////////////////////////////// initialization RegisterViewer(TjdevVRAMViewer); end. //////////////////////////////////////////////////////////////////////