Do you like asm?
I am building a z80 emulator!
Arcade games
Consoles
Game Boy
ZX Spectrum
BASIC was crap
- No procedures
- GO TO (jmp)
- GO SUB (call)
- No memory management
- PEEK and POKE
- All vars are global
- Only FOR loops
Z80 ASM
- Hand compile BASIC (change BASIC to Assembly)
- Hand assemble (look up a printed table, POKE away)
- Without books or internet (basically no English books in Italy in the 80's). Learn by decompiling the ROM
80286 ASM
- Similar to Z80
- Hand compile (e.g. Pascal -> Assembly)
- Automatic assembler!
- BBS! Access to people who KNOW assembly!
Registers
- 7 8bit real registers A,B,C,D,E,H,L
- 2 16bit addressing registers IX, IY
- various "strange" registers
- limited alternate versions of all the above
- interrupt register I
- raster register R
- flag register F
- stack pointer SP
- program counter PC
CISC instruction set
- DJNZ d ("END FOR")
- decrement B
- compare 0
- jump relative "d" if not equal
- LDIR ("BLIT")
- copy the byte at address HL at the address DE
- increment HL
- increment DE
- decrement BC
- repeat until BC = 0
Interrupts
- Non Maskable (Z80 will always execute)
- always jump to ROM location 0x0066
- Maskable (Z80 can be prevented from executing)
- jump to ROM localtion 0x0042
- execute instruction on the data bus (typically a RST)
- look up an address in a memory table and jump
var ram = new byte[65536];
// Load a ROM image
Array.Clear(ram, 0, ram.Length);
Array.Copy(File.ReadAllBytes("48.rom"), ram, 16384);
// Ports is something you supply to emulate I/O ports
var ports = new SamplePorts();
// Set up memory layout
var myZ80 = new Z80(new Memory(ram, 16384), ports);
// Run
while (!myZ80.Halted)
{
myZ80.Parse();
}
// Show the registers
Console.WriteLine(myZ80.DumpState());
The five stages of denial testing
"Who needs testing?"
"Does this work at all?"
public void Main()
{
var asm = new byte[]{ 0x21, 0x00, 0x00 }; // LD HL, $0000
var z80 = new z80(asm);
z80.Parse();
z80.DumpState();
}
"Funny..."
[Test]
public void DoesLDHLWork()
{
var asm = new byte[]{ 0x21, 0x00, 0x00, 0x76 }; // LD HL, $0000 ; HALT
var testSystem = new TestSystem(asm);
testSystem.Run();
Assert.AreEqual(0, testSystem.HL);
}
[Test]
public void DoesLDIXWork()
{
var asm = new byte[]{ 0xDD, 0x21, 0x00, 0x00, 0x76 }; // LD IX, $0000 ; HALT
var testSystem = new TestSystem(asm);
testSystem.Run();
Assert.AreEqual(0, testSystem.IX);
}
// Moar
"Who tests the tests?"
[Test]
public void Test_LD_HL_nn()
{
asm.LoadReg16Val(2, 0x1942);
asm.Halt();
en.Run();
Assert.AreEqual(asm.Position, en.PC);
Assert.AreEqual(0x19, en.H);
Assert.AreEqual(0x42, en.L);
}
[Test]
public void Test_LD_DE_nn()
{
asm.LoadReg16Val(1, 0x1942);
asm.Halt();
en.Run();
Assert.AreEqual(asm.Position, en.PC);
Assert.AreEqual(0x19, en.D);
Assert.AreEqual(0x42, en.E);
}
"OMG Flags"
[Test]
[TestCase(0, 0x44, 0x11)]
[TestCase(0, 0x44, 0x0F)]
[TestCase(0, 0x44, 0xFF)]
[TestCase(0, 0x44, 0x01)]
[TestCase(0, 0xF4, 0x11)]
// Many, many more test cases
public void Test_ADD_A_r(byte reg, byte val, byte val2)
{
asm.LoadRegVal(7, val);
asm.LoadRegVal(reg, val2);
asm.AddAReg(reg);
asm.Halt();
en.Run();
Assert.AreEqual(asm.Position, en.PC);
var trueSum = (val + val2);
var byteSum = trueSum % 256;
var sbyteSum = (sbyte)byteSum;
Assert.AreEqual(byteSum, en.A);
Assert.AreEqual(sbyteSum < 0, en.FlagS, "Flag S contained the wrong value");
Assert.AreEqual(en.A == 0x00, en.FlagZ, "Flag Z contained the wrong value");
Assert.AreEqual((0x0F & val2 + 0x0F & val) > 0x0F, en.FlagH, "Flag H contained the wrong value");
// if both operands are positive and result is negative or if both are negative and result is positive
var overflow = (val < 0x7F == val2 < 0x7F) && (val < 0x7F == sbyteSum < 0);
Assert.AreEqual(overflow, en.FlagP, "Flag P contained the wrong value");
Assert.AreEqual(trueSum > 0xFF, en.FlagC, "Flag C contained the wrong value");
}
The Project
z80 - a Z80 emulator that works in real time written in C#.
-
Z80 Emulator
-
Z80 Assembler backend
-
2270 tests (98% coverage)
Opcodes
- 8-bit load group (e.g. LD A, 0x42)
- 16-bit load group (e.g. POP HL)
- Exchange, Block Transfer, and Search group (e.g. EX AF, AF')
- 8-Bit Arithmetic Group (e.g. ADD 0x23)
- General-Purpose Arithmetic and CPU Control Groups (e.g. NOP, HALT, ...)
- 16-Bit Arithmetic Group (e.g. ADD HL, 0x2D5F, ...)
- Rotate and Shift Group (e.g. RLCA, RLA, ...)
- Bit Set, Reset, and Test Group (BIT, SET, RES)
- Jump Group (JP nn, JR e, DJNZ e, ...)
- Call and Return Group (CALL, RET, RST)
- Undocumented opcodes (CB, DDCB, FDCB, ED)
- Input and Output Group (IN, OUT, ...)
OTHER FEATURES
- Address and Data bus
- R register counts machine cycles (approximately)
- Interrupts
- Other pins
Undocumented stuff
- Undocumented opcodes (DD, FD)
- Undocumented effects (BIT, Memory Block Instructions, I/O Block Instructions, 16 Bit I/O ports, Block Instructions, Bit Additions, DAA Instruction)
New features
- An assembler frontend thus having a full z80 assembler
- A disassembler based on the current CPU emulator code
https://github.com/sklivvz/z80
Thank you
Do you like asm?
By Marco Cecconi
Do you like asm?
I am building a Z80 emulator!
- 683