Skip to content

Commit

Permalink
Implement 16-bit overflow, other cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy4495 committed Feb 11, 2023
1 parent 29afaed commit 6e10359
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 39 deletions.
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2022 Andreas Taylor
Copyright (c) 2022 - 2023 Andreas Taylor

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# MIT License
#
# 0.1 11/29/22 Andy4495 Initial Creation
# v0.1.0 02/11/23 Andy4495 Read for first "release"

SUBDIRS = src

Expand Down
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,24 @@ There are many other open source emulators available. This emulator is not meant

All opcodes have decoding and execution code implemented, but are not fully tested.

The following updates still need to be completed before I consider it a good first release:
The disassembler functionality should be very close to correct (i.e., displaying the correct mnemonic for each opcode).

- Flag update handling
- Overflow (V) Flag updates for 16-bit SBC HL, ss; ADC HL, ss
- Visual check of all opcode operations
I expect to find some defects in the opcode execution and in handling of the processor flags.

This is still a "pre-release" (version less than 1.0.0), because I expect to tweak the formatting of the program output while I test.

Next steps:

- More thorough testing of all opcode execution and processor flag updates
- Visual review of all opcode implementation
- Clean up compiler warnings (if any)
- Only a general test will be run on release 1.0 (no core dumps, cursory check of opcodes)
- A complete test of all opcodes will be done later
- Hooks to allow for automated testing. This may include:
- Updating command line options
- Updating output formatting
- Changing list of options in interactive mode
- Moving certain operations currently handled by the main program loop into the Z80 processor class

The above items are planned to be completed before starting on the [Future Functionality](#future-functionality) items below.
See also the [Future Functionality](#future-functionality) items below.

## Usage

Expand Down Expand Up @@ -76,13 +84,15 @@ I have not tried it on other platforms, but there is no machine dependent code.
4. Loop:
- Fetch and decode instruction
- Execute instruction
- Display machine state (depending on configuration and menu choice)
- Continue loop until breakpoint or HALT reached
- Display machine state (depending on configuration and menu choice)

### Defining the CPU

The CPU opcodes are defined in several tables implemented with arrays of structs for the main and extended opcodes (`Z80_opcodes.h`). Each entry contains the size of the instruction, the opcode/data layout, and the instruction mnemonic. The opcode value is represented by the array index.

Zilog-documented and undocumented opcodes are defined and supported by the emulator.

The Z80 CPU is defined by a class (`Z80.h`). This class contains:

- An array representing the memory (RAM and ROM) available to the processor
Expand Down
68 changes: 62 additions & 6 deletions src/Z80.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
0.1 11/29/22 Andy4495 Initial Creation
0.2 12/22/22 Andy4495 Additional implementation
v0.1.0 02/11/23 Andy4495 Read for first "release"
*/

// Z80 core definitions
Expand Down Expand Up @@ -177,6 +178,61 @@ void Z80::update_V(INST_TYPE t, const unsigned char val1, const unsigned char va
}
}

void Z80::update_V16(INST_TYPE t, unsigned short val1, unsigned short val2) {
switch (t) {
case ADD:
// operands with different signs --> no overflow
if ((val1 & 0x8000) != (val2 & 0x8000)) clearFlag(PV_BIT);
else {
// both operands negative, result positive --> overflow
if ((val1 & 0x8000))
if ((val1 + val2) & 0x8000) clearFlag(PV_BIT);
else setFlag(PV_BIT);
else {
// both operands positive, result negative --> overflow
if ((val1 + val2) & 0x8000) setFlag(PV_BIT);
else clearFlag(PV_BIT);
}
}
break;
case ADC:
// operands with different signs --> no overflow
if ((val1 & 0x8000) != (val2 & 0x8000)) clearFlag(PV_BIT);
else {
// both operands negative, result positive --> overflow
if ((val1 & 0x8000))
if ((val1 + val2 + testFlag(C_BIT)) & 0x8000) clearFlag(PV_BIT);
else setFlag(PV_BIT);
else {
// both operands positive, result negative --> overflow
if ((val1 + val2 + testFlag(C_BIT)) & 0x8000) setFlag(PV_BIT);
else clearFlag(PV_BIT);
}
}
break;
case SUB:
// operands are same signs, no overflow
if ((val1 & 0x8000) == (val2 & 0x8000)) clearFlag(PV_BIT);
// with different signs, need to check result
else if (((int) val1 - (int) val2 > 0x7fff) || ((int) val1 - (int) val2 < -32768)) setFlag(PV_BIT);
else clearFlag(PV_BIT);
break;
case SBC:
// operands are same signs, no overflow
if ((val1 & 0x8000) == (val2 & 0x8000)) clearFlag(PV_BIT);
// with different signs, need to check result
else if (((int) val1 - (int) val2 - testFlag(C_BIT) > 0x7fff) ||
((int) val1 - (int) val2 - testFlag(C_BIT) < -32768)) {
setFlag(PV_BIT);
}
else clearFlag(PV_BIT);
break;
default: // NONE - Flag not affected
break;
}
}


void Z80::update_H(INST_TYPE t, const unsigned char val1, const unsigned char val2) {
switch (t) {
case ADD:
Expand Down Expand Up @@ -235,35 +291,35 @@ unsigned short Z80::getBC() {
}

unsigned short Z80::getDE() {
return (D<<8) + C;
return (D<<8) + E;
}

unsigned short Z80::getHL() {
return (H<<8) + L;
}

void Z80::setIX(unsigned short v) {
IXH = ((v & 0xff00)>8);
IXH = ((v & 0xff00)>>8);
IXL = v & 0x00ff;
}

void Z80::setIY(unsigned short v) {
IYH = ((v & 0xff00)>8);
IYH = ((v & 0xff00)>>8);
IYL = v & 0x00ff;
}

void Z80::setBC(unsigned short v) {
B = ((v & 0xff00)>8);
B = ((v & 0xff00)>>8);
C = v & 0x00ff;
}

void Z80::setDE(unsigned short v) {
D = ((v & 0xff00)>8);
D = ((v & 0xff00)>>8);
E = v & 0x00ff;
}

void Z80::setHL(unsigned short v) {
H = ((v & 0xff00)>8);
H = ((v & 0xff00)>>8);
L = v & 0x00ff;
}

Expand Down
2 changes: 2 additions & 0 deletions src/Z80.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
0.1 11/29/22 Andy4495 Initial Creation
0.2 12/22/22 Andy4495 Additional implementation
v0.1.0 02/11/23 Andy4495 Read for first "release"
*/

#ifndef Z80_H
Expand Down Expand Up @@ -90,6 +91,7 @@ class Z80 {
void update_C(INST_TYPE t, unsigned short val1, unsigned short val2);
void update_P(unsigned char v);
void update_V(INST_TYPE t, unsigned char val1, unsigned char val2);
void update_V16(INST_TYPE t, unsigned short val1, unsigned short val2);
void update_H(INST_TYPE t, unsigned char val1, unsigned char val2);
void update_Z(unsigned char val);
void update_S(unsigned char val);
Expand Down
1 change: 1 addition & 0 deletions src/Z80_execute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
0.1 11/29/22 Andy4495 Initial Creation
0.2 12/22/22 Andy4495 Additional implementation
v0.1.0 02/11/23 Andy4495 Read for first "release"
*/

// Z80 core definitions
Expand Down
7 changes: 5 additions & 2 deletions src/Z80_execute_bit_opcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
0.1 11/29/22 Andy4495 Initial Creation
0.2 12/22/22 Andy4495 Additional implementation
v0.1.0 02/11/23 Andy4495 Read for first "release"
*/

// Z80 core definitions
Expand Down Expand Up @@ -55,9 +56,10 @@ void Z80::execute_bit_opcode() { // IR[0] == 0xCB
cout << "Execution not defined: 0xcb" << hex << setw(2) << (unsigned int) IR[1] << endl;
break;
}
tempC = testFlag(C_BIT);
if (*r & 0x80) setFlag(C_BIT); else clearFlag(C_BIT);
*r = *r << 1;
*r = (*r & 0xFE) | testFlag(C_BIT);
*r = (*r & 0xFE) | tempC;
update_S(*r);
update_Z(*r);
clearFlag(H_BIT);
Expand Down Expand Up @@ -146,9 +148,10 @@ void Z80::execute_bit_opcode() { // IR[0] == 0xCB
cout << "Execution not defined: 0xcb" << hex << setw(2) << (unsigned int) IR[1] << endl;
break;
}
tempC = testFlag(C_BIT);
if (*r & 0x01) setFlag(C_BIT); else clearFlag(C_BIT);
*r = *r >> 1;
*r = (*r & 0x7f) | (testFlag(C_BIT) << 7);
*r = (*r & 0x7f) | (tempC << 7);
update_S(*r);
update_Z(*r);
clearFlag(H_BIT);
Expand Down
1 change: 1 addition & 0 deletions src/Z80_execute_index_opcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
0.1 11/29/22 Andy4495 Initial Creation
0.2 12/22/22 Andy4495 Additional implementation
v0.1.0 02/11/23 Andy4495 Read for first "release"
*/

// Z80 core definitions
Expand Down
5 changes: 4 additions & 1 deletion src/Z80_execute_ix_iy_bit_opcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
0.1 11/29/22 Andy4495 Initial Creation
0.2 12/22/22 Andy4495 Additional implementation
v0.1.0 02/11/23 Andy4495 Read for first "release"
*/

// Z80 core definitions
Expand Down Expand Up @@ -60,6 +61,7 @@ void Z80::execute_ix_iy_bit_opcode() { // IX: IR[0,1,2] = 0xDDCBdd, IY: IR[0,1,
break;
}
if (memory[index] & 0x80) setFlag(C_BIT); else clearFlag(C_BIT);
// Carry flag contains previous bit 7, so rotate into bit 0
memory[index] = ((memory[index] << 1) & 0xfe) | testFlag(C_BIT);
if (r != nullptr) {
*r = memory[index];
Expand Down Expand Up @@ -199,8 +201,9 @@ void Z80::execute_ix_iy_bit_opcode() { // IX: IR[0,1,2] = 0xDDCBdd, IY: IR[0,1,
cout << "Execution not defined: 0xXDCBdd" << hex << setw(2) << (unsigned int) IR[3] << endl;
break;
}
tempC = testFlag(C_BIT);
if (memory[index] & 0x01) setFlag(C_BIT); else clearFlag(C_BIT);
memory[index] = ((memory[index] << 1) & 0x7f) | (testFlag(C_BIT) << 7);
memory[index] = ((memory[index] << 1) & 0x7f) | (tempC << 7);
if (r != nullptr) {
*r = memory[index];
}
Expand Down
3 changes: 3 additions & 0 deletions src/Z80_execute_main_opcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
0.1 11/29/22 Andy4495 Initial Creation
0.2 12/22/22 Andy4495 Additional implementation
v0.1.0 02/11/23 Andy4495 Read for first "release"
*/

// Z80 core definitions
Expand Down Expand Up @@ -818,6 +819,7 @@ void Z80::execute_main_opcode() {
case 0x07:
if (A & 0x80) setFlag(C_BIT); else clearFlag(C_BIT);
A = A << 1;
// Carry contains previous bit 7, so rotate that over to bit 0
if (testFlag(C_BIT)) A |= 0x01; else A &= 0xfe;
clearFlag(H_BIT);
clearFlag(N_BIT);
Expand All @@ -837,6 +839,7 @@ void Z80::execute_main_opcode() {
case 0x0f:
if (A & 0x01) setFlag(C_BIT); else clearFlag(C_BIT);
A = A >> 1;
// Carry contains previous bit 0, so rotate that over to bit 7
if (testFlag(C_BIT)) A |= 0x80; else A &= 0x7f;
clearFlag(H_BIT);
clearFlag(N_BIT);
Expand Down
51 changes: 30 additions & 21 deletions src/Z80_execute_misc_opcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
0.1 11/29/22 Andy4495 Initial Creation
0.2 12/22/22 Andy4495 Additional implementation
v0.1.0 02/11/23 Andy4495 Read for first "release"
*/

// Z80 core definitions
Expand Down Expand Up @@ -487,32 +488,36 @@ void Z80::execute_misc_opcode() { // IR[0] = 0xED
case 0x4a: case 0x5a: case 0x6a: case 0x7a:
// Determine which register we are working on:
// Opcode 0 1 s s 1 0 1 0
TempC = testFlag(C_BIT);
switch ((IR[1] & 0x30) >> 4) {
case 0b00: // BC
if ((getHL() & 0x0fff) + (getBC() & 0x0fff) + testFlag(C_BIT) > 0xfff) setFlag(H_BIT); else clearFlag(H_BIT);
if ((unsigned int) getHL() + (unsigned int) getBC() + (unsigned int) testFlag(C_BIT) > (unsigned int) 0xffff) setFlag(C_BIT); else clearFlag(C_BIT);
setHL(getHL() + getBC() + testFlag(C_BIT));
if ((getHL() & 0x0fff) + (getBC() & 0x0fff) + TempC > 0xfff) setFlag(H_BIT); else clearFlag(H_BIT);
update_V16(ADC, getHL(), getBC());
if ((unsigned int) getHL() + (unsigned int) getBC() + (unsigned int) TempC > (unsigned int) 0xffff) setFlag(C_BIT); else clearFlag(C_BIT);
setHL(getHL() + getBC() + TempC); /// Temporary comment
break;
case 0b01: // DE
if ((getHL() & 0x0fff) + (getDE() & 0x0fff) + testFlag(C_BIT) > 0xfff) setFlag(H_BIT); else clearFlag(H_BIT);
if ((unsigned int) getHL() + (unsigned int) getDE() + (unsigned int) testFlag(C_BIT) > (unsigned int) 0xffff) setFlag(C_BIT); else clearFlag(C_BIT);
setHL(getHL() + getDE() + testFlag(C_BIT));
if ((getHL() & 0x0fff) + (getDE() & 0x0fff) + TempC > 0xfff) setFlag(H_BIT); else clearFlag(H_BIT);
update_V16(ADC, getHL(), getDE());
if ((unsigned int) getHL() + (unsigned int) getDE() + (unsigned int) TempC > (unsigned int) 0xffff) setFlag(C_BIT); else clearFlag(C_BIT);
setHL(getHL() + getDE() + TempC);
break;
case 0b10: // HL
if ((getHL() & 0x0fff) + (getHL() & 0x0fff) + testFlag(C_BIT) > 0xfff) setFlag(H_BIT); else clearFlag(H_BIT);
if ((unsigned int) getHL() + (unsigned int) getHL() + (unsigned int) testFlag(C_BIT) > (unsigned int) 0xffff) setFlag(C_BIT); else clearFlag(C_BIT);
setHL(getHL() + getHL() + testFlag(C_BIT));
if ((getHL() & 0x0fff) + (getHL() & 0x0fff) + TempC > 0xfff) setFlag(H_BIT); else clearFlag(H_BIT);
update_V16(ADC, getHL(), getHL());
if ((unsigned int) getHL() + (unsigned int) getHL() + (unsigned int) TempC > (unsigned int) 0xffff) setFlag(C_BIT); else clearFlag(C_BIT);
setHL(getHL() + getHL() + TempC);
break;
case 0b11: // SP
if ((getHL() & 0x0fff) + (SP & 0x0fff) + testFlag(C_BIT) > 0xfff) setFlag(H_BIT); else clearFlag(H_BIT);
if ((unsigned int) getHL() + (unsigned int) SP + (unsigned int) testFlag(C_BIT) > (unsigned int) 0xffff) setFlag(C_BIT); else clearFlag(C_BIT);
setHL(getHL() + SP + testFlag(C_BIT));
if ((getHL() & 0x0fff) + (SP & 0x0fff) + TempC > 0xfff) setFlag(H_BIT); else clearFlag(H_BIT);
update_V16(ADC, getHL(), SP);
if ((unsigned int) getHL() + (unsigned int) SP + (unsigned int) TempC > (unsigned int) 0xffff) setFlag(C_BIT); else clearFlag(C_BIT);
setHL(getHL() + SP + TempC);
break;
default: cout << "Invalid opcode: ADD HL, ss" << endl; break;
}
if (getHL() & 0x8000) setFlag(S_BIT); else clearFlag(S_BIT);
if (getHL() == 0) setFlag(Z_BIT); else clearFlag(Z_BIT);
/// Need to implement 16-bit overflow check
break;

// SBC HL, ss (0x42, 0x52, 0x62, 0x72)
Expand All @@ -523,23 +528,27 @@ void Z80::execute_misc_opcode() { // IR[0] = 0xED
// Opcode 0 1 s s 0 0 1 0
switch ((IR[1] & 0x30) >> 4) {
case 0b00: // BC
if ((getHL() & 0x0fff) < (getBC() & 0x0fff) + testFlag(C_BIT)) setFlag(H_BIT); else clearFlag(H_BIT);
if ((unsigned int) getHL() < (unsigned int) getBC() + testFlag(C_BIT)) setFlag(C_BIT); else clearFlag(C_BIT);
if ((getHL() & 0x0fff) < (getBC() & 0x0fff) + TempC) setFlag(H_BIT); else clearFlag(H_BIT);
update_V16(SBC, getHL(), getBC());
if ((unsigned int) getHL() < (unsigned int) getBC() + TempC) setFlag(C_BIT); else clearFlag(C_BIT);
setHL(getHL() - getBC() - TempC);
break;
case 0b01: // DE
if ((getHL() & 0x0fff) < (getDE() & 0x0fff) + testFlag(C_BIT)) setFlag(H_BIT); else clearFlag(H_BIT);
if ((unsigned int) getHL() < (unsigned int) getDE() + testFlag(C_BIT)) setFlag(C_BIT); else clearFlag(C_BIT);
if ((getHL() & 0x0fff) < (getDE() & 0x0fff) + TempC) setFlag(H_BIT); else clearFlag(H_BIT);
update_V16(SBC, getHL(), getDE());
if ((unsigned int) getHL() < (unsigned int) getDE() + TempC) setFlag(C_BIT); else clearFlag(C_BIT);
setHL(getHL() - getDE() - TempC);
break;
case 0b10: // HL
if ((getHL() & 0x0fff) < (getHL() & 0x0fff) + testFlag(C_BIT)) setFlag(H_BIT); else clearFlag(H_BIT);
if ((unsigned int) getHL() < (unsigned int) getHL() + testFlag(C_BIT)) setFlag(C_BIT); else clearFlag(C_BIT);
if ((getHL() & 0x0fff) < (getHL() & 0x0fff) + TempC) setFlag(H_BIT); else clearFlag(H_BIT);
update_V16(SBC, getHL(), getHL());
if ((unsigned int) getHL() < (unsigned int) getHL() + TempC) setFlag(C_BIT); else clearFlag(C_BIT);
setHL(getHL() - getHL() - TempC);
break;
case 0b11: // SP
if ((getHL() & 0x0fff) < (SP & 0x0fff) + testFlag(C_BIT)) setFlag(H_BIT); else clearFlag(H_BIT);
if ((unsigned int) getHL() < (unsigned int) SP + testFlag(C_BIT)) setFlag(C_BIT); else clearFlag(C_BIT);
if ((getHL() & 0x0fff) < (SP & 0x0fff) + TempC) setFlag(H_BIT); else clearFlag(H_BIT);
update_V16(SBC, getHL(), SP);
if ((unsigned int) getHL() < (unsigned int) SP + TempC) setFlag(C_BIT); else clearFlag(C_BIT);
setHL(getHL() - SP - TempC);
break;
default: cout << "Invalid opcode: ADD HL, ss" << endl; break;
Expand Down
1 change: 1 addition & 0 deletions src/Z80_fetch_and_decode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
0.1 11/29/22 Andy4495 Initial Creation
0.2 12/22/22 Andy4495 Additional implementation
v0.1.0 02/11/23 Andy4495 Read for first "release"
*/

// Z80 core definitions
Expand Down
1 change: 1 addition & 0 deletions src/Z80_opcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
0.1 11/29/22 Andy4495 Initial Creation
0.2 12/22/22 Andy4495 Additional implementation
v0.1.0 02/11/23 Andy4495 Read for first "release"
*/

// Z80 Opcodes
Expand Down

0 comments on commit 6e10359

Please sign in to comment.