(* ______ ______ ______ /\___ \/\ __ \\ __ \ ____ \/__/ /\_\ __ \\ \/\ \ ______________________________________ | /\_____\\_____\\_____\ | | Zilog \/_____//_____//_____/ CPU Emulator - Pascal Binding | | Copyright (C) 2022-2023 Zoran Vučenović. | | | | Permission to use, copy, modify, and/or distribute this software | | for any purpose with or without fee is hereby granted. | | | | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL | | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED | | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL | | THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR | | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, | | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | | CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | | '===================================================================*) unit Z80; {$mode ObjFPC}{$H+} {$J-} {$inline on} {$PackRecords C} interface uses SysUtils, dynlibs; const DefaultZ80LibPath = {$if defined(mswindows)} 'Z80.dll'; {$elseif defined(darwin)} 'libZ80.dylib'; {$elseif defined(unix)} 'libZ80.so'; {$else} ''; {$endif} Z80_MAXIMUM_CYCLES = SizeUInt(SizeUInt(SizeUInt.MaxValue) - 30); Z80_MAXIMUM_CYCLES_PER_STEP = 23; Z80_HALT = $76; Z80_HOOK = $64; Z80_JP_WORD = $C3; Z80_NOP = $00; Z80_RET = $C9; Z80_SF = 128; Z80_ZF = 64; Z80_YF = 32; Z80_HF = 16; Z80_XF = 8; Z80_PF = 4; Z80_NF = 2; Z80_CF = 1; type PZ80 = ^TZ80; TZ80RegisterPair = packed record case Integer of 0: (word: UInt16); {$ifdef endian_little} 1: (bytes: packed record low: UInt8; high: UInt8 end); {$else} 1: (bytes: packed record high: UInt8; low: UInt8 end); {$endif} end; Z80Read = function(context: Pointer; address: UInt16): UInt8; cdecl; Z80Write = procedure(context: Pointer; address: UInt16; value: UInt8); cdecl; Z80Halt = procedure(context: Pointer; state: UInt8); cdecl; Z80Notify = procedure(context: Pointer); cdecl; Z80Illegal = function(cpu: PZ80; opcode: UInt8): UInt8; cdecl; TZ80 = record cycles: SizeUInt; cycle_limit: SizeUInt; context: Pointer; fetch_opcode: Z80Read; fetch: Z80Read; read: Z80Read; write: Z80Write; input: Z80Read; output: Z80Write; halt: Z80Halt; nop: Z80Read; nmia: Z80Read; inta: Z80Read; int_fetch: Z80Read; ld_i_a: Z80Notify; ld_r_a: Z80Notify; reti: Z80Notify; retn: Z80Notify; hook: Z80Read; illegal: Z80Illegal; data: array [0..3] of UInt8; ix_iy: array [0..1] of TZ80RegisterPair; pc: TZ80RegisterPair; sp: TZ80RegisterPair; xy: TZ80RegisterPair; memptr: TZ80RegisterPair; af: TZ80RegisterPair; af_: TZ80RegisterPair; bc: TZ80RegisterPair; bc_: TZ80RegisterPair; de: TZ80RegisterPair; de_: TZ80RegisterPair; hl: TZ80RegisterPair; hl_: TZ80RegisterPair; r: UInt8; i: UInt8; r7: UInt8; im: UInt8; request: UInt8; resume: UInt8; iff1: UInt8; iff2: UInt8; q: UInt8; options: UInt8; int_line: UInt8; halt_line: UInt8; end; const Z80_OPTION_OUT_VC_255 = 1; Z80_OPTION_LD_A_IR_BUG = 2; Z80_OPTION_HALT_SKIP = 4; Z80_OPTION_XQ = 8; Z80_OPTION_IM0_RETX_NOTIFICATIONS = 16; Z80_OPTION_YQ = 32; Z80_MODEL_ZILOG_NMOS = (Z80_OPTION_LD_A_IR_BUG or Z80_OPTION_XQ or Z80_OPTION_YQ); Z80_MODEL_ZILOG_CMOS = (Z80_OPTION_OUT_VC_255 or Z80_OPTION_XQ or Z80_OPTION_YQ); Z80_MODEL_NEC_NMOS = Z80_OPTION_LD_A_IR_BUG; Z80_MODEL_ST_CMOS = (Z80_OPTION_OUT_VC_255 or Z80_OPTION_LD_A_IR_BUG or Z80_OPTION_YQ); Z80_REQUEST_REJECT_NMI = 2; Z80_REQUEST_NMI = 4; Z80_REQUEST_INT = 8; Z80_REQUEST_SPECIAL_RESET = 16; Z80_RESUME_HALT = 1; Z80_RESUME_XY = 2; Z80_RESUME_IM0_XY = 3; Z80_HALT_EXIT = 0; Z80_HALT_ENTER = 1; Z80_HALT_EXIT_EARLY = 2; Z80_HALT_CANCEL = 3; var z80_power: procedure(self: PZ80; state: ByteBool); cdecl; z80_instant_reset: procedure(self: PZ80); cdecl; z80_special_reset: procedure(self: PZ80); cdecl; z80_int: procedure(self: PZ80; state: ByteBool); cdecl; z80_nmi: procedure(self: PZ80); cdecl; z80_execute: function(self: PZ80; cycles: SizeUInt): SizeUInt; cdecl; z80_run: function(self: PZ80; cycles: SizeUInt): SizeUInt; cdecl; function z80_r(const self: PZ80): UInt8; inline; function z80_refresh_address(const self: PZ80): UInt16; inline; function z80_in_cycle(const self: PZ80): UInt8; inline; function z80_out_cycle(const self: PZ80): UInt8; inline; function IsLibraryLoaded: Boolean; function LoadLibrary(const LibPath: String = ''): Boolean; function UnloadLibrary: Boolean; implementation procedure z80_break(self: PZ80); begin self^.cycle_limit := 0; end; function z80_r(const self: PZ80): UInt8; begin Result := (self^.r and 127) or (Self^.r7 and 128); end; function z80_refresh_address(const self: PZ80): UInt16; begin Result := UInt16(self^.i) shl 8; Result := Result or ((self^.r - 1) and 127) or (self^.r7 and 128); end; function z80_in_cycle(const self: PZ80): UInt8; begin if self^.data[0] = $DB then begin Result := 7; end else begin Result := 8 + (self^.data[1] shr 7); end; end; function z80_out_cycle(const self: PZ80): UInt8; begin if self^.data[0] = $D3 then begin Result := 7; end else begin Result := 8 + ((self^.data[1] shr 7) shl 2); end; end; procedure z80_wait(self: PZ80; cycles: SizeUInt); begin self^.cycles := self^.cycles + cycles; end; var LibHandle: TLibHandle; function IsLibraryLoaded: Boolean; begin Result := LibHandle <> NilHandle; end; function LoadLibrary(const LibPath: String): Boolean; var LibName: String; begin if LibHandle = NilHandle then begin Result := False; if LibPath = '' then begin LibName := DefaultZ80LibPath; end else begin LibName := LibPath; end; LibHandle := SafeLoadLibrary(LibName); if LibHandle = NilHandle then Exit; Pointer(z80_power) := GetProcAddress(LibHandle, 'z80_power'); Pointer(z80_instant_reset) := GetProcAddress(LibHandle, 'z80_instant_reset'); Pointer(z80_special_reset) := GetProcAddress(LibHandle, 'z80_special_reset'); Pointer(z80_int) := GetProcAddress(LibHandle, 'z80_int'); Pointer(z80_nmi) := GetProcAddress(LibHandle, 'z80_nmi'); Pointer(z80_execute) := GetProcAddress(LibHandle, 'z80_execute'); Pointer(z80_run) := GetProcAddress(LibHandle, 'z80_run'); end; Result := True; end; function UnloadLibrary: Boolean; begin if IsLibraryLoaded then begin if not dynlibs.UnloadLibrary(LibHandle) then Exit(False); LibHandle := NilHandle; end; z80_power := nil; z80_instant_reset := nil; z80_special_reset := nil; z80_int := nil; z80_nmi := nil; z80_execute := nil; z80_run := nil; Result := True; end; procedure Init; begin LibHandle := NilHandle; UnloadLibrary; end; initialization Init; finalization UnloadLibrary; end.