diff --git a/README.md b/README.md index 28fb480..9789ac7 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,31 @@ -##MOS6502 Emulator in Delphi## - -This is the Delphi / Pascal port of the C++ implementation for the MOS Technology 6502 CPU by [Gianluca Ghettini](https://github.com/gianlucag/mos6502). The code is written to be more readable than fast, however some minor tricks have been introduced to greatly reduce the overall execution time. - -Main features: - - * 100% coverage of legal opcodes - * decimal mode implemented - * read/write bus callback - * jump table opcode selection - -# mos6502-delphi - -The port was written with minor changes to the original file: the run(N) function was replaced by a single Step() function - if you need to run more than one cycle, just put that function inside a loop. - -# 6502 functional test - -The [6502 functional test](https://github.com/Klaus2m5/6502_65C02_functional_tests) (version 16-aug-2013) by Klaus Dormann is included. - -# C64 emulator - -A very basic C64 emulator is included. You need to download the BASIC ROM [basic.901226-01.bin](http://www.commodore.ca/manuals/funet/cbm/firmware/computers/c64/basic.901226-01.bin) and the Kernal ROM [kernal.901227-03.bin](http://www.commodore.ca/manuals/funet/cbm/firmware/computers/c64/kernal.901227-03.bin) and put both files inside the ROMs folder. Install the commodore [CBM.ttf](https://github.com/bobsummerwill/VICE/raw/master/data/fonts/CBM.ttf) font found in the VICE package. - -The C64 Emulator uses a symbolic keyboard translation thus any keyboard layout should work. - -This C64 emulator is just a a very basic 6502/6510 emulation example and is not feature complete. Please take a look at [VICE - Versatile Commodore Emulator](http://vice-emu.sourceforge.net/) instead. - +##MOS6502 Emulator in Delphi## + +This is the Delphi / Pascal port of the C++ implementation for the MOS Technology 6502 CPU by [Gianluca Ghettini](https://github.com/gianlucag/mos6502). The code is written to be more readable than fast, however some minor tricks have been introduced to greatly reduce the overall execution time. + +Main features: + + * 100% coverage of legal opcodes + * decimal mode implemented + * read/write bus callback + * jump table opcode selection + +# mos6502-delphi + +The port was written with minor changes to the original file: the run(N) function was replaced by a single Step() function - if you need to run more than one cycle, just put that function inside a loop. + +# 6502 functional test + +The [6502 functional test](https://github.com/Klaus2m5/6502_65C02_functional_tests) (version 16-aug-2013) by Klaus Dormann is included. + +# C64 emulator + +A very basic C64 emulator is included. You need to download the BASIC ROM [basic.901226-01.bin](http://www.commodore.ca/manuals/funet/cbm/firmware/computers/c64/basic.901226-01.bin) and the Kernal ROM [kernal.901227-03.bin](http://www.commodore.ca/manuals/funet/cbm/firmware/computers/c64/kernal.901227-03.bin) and put both files inside the ROMs folder. Install the commodore [CBM.ttf](https://github.com/bobsummerwill/VICE/raw/master/data/fonts/CBM.ttf) font found in the VICE package. + +The C64 Emulator uses a symbolic keyboard translation thus any keyboard layout should work. + +This C64 emulator is just a a very basic 6502/6510 emulation example and is not feature complete. Please take a look at [VICE - Versatile Commodore Emulator](http://vice-emu.sourceforge.net/) instead. + +# VIC-20 emulator + +Based on the C64 emulator, a VIC-20 emulator is now included. You need to download the BASIC ROM [basic.901486-01.bin](http://www.commodore.ca/manuals/funet/cbm/firmware/computers/vic20/basic.901486-01.bin) and the Kernal ROM [kernal.901486-07.bin](http://www.commodore.ca/manuals/funet/cbm/firmware/computers/vic20/kernal.901486-07.bin) and put both files inside the ROMs folder. Changes compared to the C64 source are Addr and Keyboard matrix. + diff --git a/VIC-20/ROMs/README.md b/VIC-20/ROMs/README.md new file mode 100644 index 0000000..92deec7 --- /dev/null +++ b/VIC-20/ROMs/README.md @@ -0,0 +1 @@ +Put your BASIC and Kernal ROMs here \ No newline at end of file diff --git a/VIC-20/Source/FormV20.dfm b/VIC-20/Source/FormV20.dfm new file mode 100644 index 0000000..cfadc22 --- /dev/null +++ b/VIC-20/Source/FormV20.dfm @@ -0,0 +1,23 @@ +object FrmVC20: TFrmVC20 + Left = 0 + Top = 0 + BorderStyle = bsDialog + Caption = 'VIC-20 Emu' + ClientHeight = 393 + ClientWidth = 645 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + KeyPreview = True + OldCreateOrder = False + Position = poScreenCenter + OnCreate = FormCreate + OnDestroy = FormDestroy + OnKeyPress = FormKeyPress + OnKeyUp = FormKeyUp + PixelsPerInch = 96 + TextHeight = 13 +end diff --git a/VIC-20/Source/FormV20.pas b/VIC-20/Source/FormV20.pas new file mode 100644 index 0000000..239ad65 --- /dev/null +++ b/VIC-20/Source/FormV20.pas @@ -0,0 +1,96 @@ +unit FormV20; + +interface + +uses + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, + Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VIC20, Vcl.StdCtrls, Vcl.ExtCtrls; + +const + ScreenZoom = 2; + ScreenWidth = 176 * ScreenZoom; // 320 + ScreenHeight = 184 * ScreenZoom; // 200 + + ColorTable: array [0 .. 2] of Cardinal = ($801010, $D0A0A0, $D0A0A0); + +type + TFrmVC20 = class(TForm) + procedure FormCreate(Sender: TObject); + procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure FormKeyPress(Sender: TObject; var Key: Char); + procedure FormDestroy(Sender: TObject); + private + { Private declarations } + procedure OnScreenWrite(var Msg: TMessage); message WM_SCREEN_WRITE; + public + { Public declarations } + LastKey: Char; + VC20: TVC20; + end; + +var + FrmVC20: TFrmVC20; + +implementation + +{$R *.dfm} + +procedure TFrmVC20.FormCreate(Sender: TObject); +begin + ClientWidth := ScreenWidth; + ClientHeight := ScreenHeight; + + Canvas.Font.Name := 'cbm'; + Canvas.Font.Height := 8 * ScreenZoom; + Canvas.Brush.Style := bsSolid; + + VC20 := TVC20.Create; + VC20.WndHandle := Handle; + VC20.LoadROM('..\ROMs\kernal.901486-06.bin', $E000); // E000-FFFF + VC20.LoadROM('..\ROMs\basic.901486-01.bin', $C000); // C000-DFFF + VC20.Exec; +end; + +procedure TFrmVC20.FormDestroy(Sender: TObject); +begin + VC20.Free; +end; + +procedure TFrmVC20.FormKeyPress(Sender: TObject; var Key: Char); +begin + VC20.SetKey(Key, 1); + LastKey := Key; +end; + +procedure TFrmVC20.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); +begin + if LastKey <> #0 then + VC20.SetKey(LastKey, 0); + LastKey := #0; +end; + +procedure TFrmVC20.OnScreenWrite(var Msg: TMessage); +var + Addr: Word; + Value: Integer; + X, Y: Integer; + Flag: Cardinal; + Q1: Byte; + Sc: Char; +begin + Addr := Msg.WParam; + Value := Msg.LParam; + + Y := (Addr div 22); //40 + X := (Addr - Y*22); //40 + + Flag := (Value shr 7) and $FF; + Q1 := Value and $7F; + Sc := Char((Q1 + 32 * (ord(Q1 < 32) * 2 + ord(Q1 > 63) + ord(Q1 > 95)))); + + Canvas.Font.Color := ColorTable[1 - Flag]; + Canvas.Brush.Color := ColorTable[Flag]; + Canvas.TextOut(X * (8*ScreenZoom), Y * (8*ScreenZoom), Sc); +end; + +end. diff --git a/VIC-20/Source/VIC20.pas b/VIC-20/Source/VIC20.pas new file mode 100644 index 0000000..10f870f --- /dev/null +++ b/VIC-20/Source/VIC20.pas @@ -0,0 +1,212 @@ +unit VIC20; + +interface + +uses + System.Classes, WinApi.Messages, MOS6502; + +const + WM_SCREEN_WRITE = WM_USER + 0; + + CIA1 = $9120; // $DC00; + + // VIC-20 keyboard matrix + KEY_TRANSLATION = '2q'#0' '#27#0#0'1'+ + '4esz'#0'aw3'+ + '6tfcxdr5'+ + '8uhbvgy7'+ + '0okmnji9'+ + '-@:.,lp+'+ + #0#0'='#0'/;*£'+ + #0#0#0#0#0#0#13#8+ + + '"'#0#0#0#0#0#0'!'+ + '$'#0#0#0#0#0#0'#'+ + '&'#0#0#0#0#0#0'%'+ + '('#0#0#0#0#0#0''''+ + '0'#0#0#0#0#0#0')'+ + '_'#0'[>= $2000) then // $A000 // treat anything above as ROM + Exit; + + Memory[Adr] := Value; + + // video RAM + if (Adr >= $1E00) and (Adr <= $1FF9) then // $400 - $07E7 + PostMessage(WndHandle, WM_SCREEN_WRITE, Adr - $1E00, Value); // $400 +end; + +constructor TVC20.Create; +begin + inherited Create(BusRead, BusWrite); + + // create 64kB memory table + GetMem(Memory, 65536); + + Thread := TVC20Thread.Create(Self); +end; + +destructor TVC20.Destroy; +begin + if TimerHandle <> 0 then + Timekillevent(TimerHandle); + Thread.Terminate; + Thread.WaitFor; + FreeMem(Memory); + inherited; +end; + +procedure TVC20.Exec; +begin + Reset; + Thread.Start; +end; + +function TVC20.KeyRead: Byte; +var + Row, Col, Cols: Byte; +begin + Result := 0; + Cols := Memory[CIA1]; + for Col := 0 to 7 do + if Cols and (1 shl Col) = 0 then // a 0 indicates a column read + for Row := 0 to 7 do + if KeyMatrix[7 - Col, Row] = 1 then + Result := Result + (1 shl Row); + Result := not Result; +end; + +procedure TVC20.LoadROM(Filename: String; Addr: Word); +var + Stream: TFileStream; +begin + Stream := TFileStream.Create(Filename, fmOpenRead); + try + Stream.Read(Memory[Addr], Stream.Size); + finally + Stream.Free; + end; +end; + +procedure TVC20.SetKey(Key: Char; Value: Byte); +var + KeyPos: Integer; +begin + KeyPos := Pos(Key, KEY_TRANSLATION) - 1; + if KeyPos >= 0 then + begin + // always release last key on keypress + if Value = 1 then + begin + SetKey(LastKey, 0); + LastKey := Key; + end; + + if KeyPos > 63 then // set right shift on/off + begin + KeyMatrix[3, 6] := Value; // 1, 4 + Dec(KeyPos, 64); + end; + + KeyMatrix[KeyPos mod 8, KeyPos div 8] := Value; + end; +end; + +{ TVC20Thread } + +constructor TVC20Thread.Create(VC20Instance: TVC20); +begin + inherited Create(True); + VC20 := VC20Instance; +end; + +procedure TVC20Thread.Execute; +begin + while (not Terminated) do + begin + if VC20.InterruptRequest then + begin + VC20.InterruptRequest := False; + VC20.IRQ; + end; + VC20.Step; + Sleep(0); + end; +end; + +end. diff --git a/VIC-20/Source/VIC20Emu.dpr b/VIC-20/Source/VIC20Emu.dpr new file mode 100644 index 0000000..3a86fa4 --- /dev/null +++ b/VIC-20/Source/VIC20Emu.dpr @@ -0,0 +1,16 @@ +program VIC20Emu; + +uses + Vcl.Forms, + FormV20 in 'FormV20.pas' {FrmVC20}, + VIC20 in 'VIC20.pas', + MOS6502 in '..\..\Source\MOS6502.pas'; + +{$R *.res} + +begin + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TFrmVC20, FrmVC20); + Application.Run; +end.