-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathboot.cpp
110 lines (95 loc) · 6.22 KB
/
boot.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <Arduino.h>
#include "pins.h"
extern byte iCount;
const byte LD_HL = 0x36; // Opcode of the Z80 instruction: LD(HL), n
const byte INC_HL = 0x23; // Opcode of the Z80 instruction: INC HL
const byte LD_HLnn = 0x21; // Opcode of the Z80 instruction: LD HL, nn
// ------------------------------------------------------------------------------
// Z80 boot routines
// ------------------------------------------------------------------------------
void pulseClock(byte numPulse)
// Generate <numPulse> clock pulses on the Z80 clock pin.
// The steady clock level is LOW, e.g. one clock pulse is a 0-1-0 transition
{
for (iCount = 0; iCount < numPulse; iCount++)
// Generate one clock pulse
{
// Send one impulse (0-1-0) on the CLK output
digitalWrite(CLK, HIGH);
digitalWrite(CLK, LOW);
}
}
// ------------------------------------------------------------------------------
void loadByteToRAM(byte value)
// Load a given byte to RAM using a sequence of two Z80 instructions forced on the data bus.
// The RAM_CE2 signal is used to force the RAM in HiZ, so the Atmega can write the needed instruction/data
// on the data bus. Controlling the clock signal and knowing exactly how many clocks pulse are required it is possible control the
// whole loading process.
// In the following "T" are the T-cycles of the Z80 (See the Z80 datashet).
// The two instruction are "LD (HL), n" and "INC (HL)".
{
// Execute the LD(HL),n instruction (T = 4+3+3). See the Z80 datasheet and manual.
// After the execution of this instruction the <value> byte is loaded in the memory address pointed by HL.
pulseClock(1); // Execute the T1 cycle of M1 (Opcode Fetch machine cycle)
digitalWrite(RAM_CE2, LOW); // Force the RAM in HiZ (CE2 = LOW)
DDRA = 0xFF; // Configure Z80 data bus D0-D7 (PA0-PA7) as output
PORTA = LD_HL; // Write "LD (HL), n" opcode on data bus
pulseClock(2); // Execute T2 and T3 cycles of M1
DDRA = 0x00; // Configure Z80 data bus D0-D7 (PA0-PA7) as input...
PORTA = 0xFF; // ...with pull-up
pulseClock(2); // Complete the execution of M1 and execute the T1 cycle of the following
// Memory Read machine cycle
DDRA = 0xFF; // Configure Z80 data bus D0-D7 (PA0-PA7) as output
PORTA = value; // Write the byte to load in RAM on data bus
pulseClock(2); // Execute the T2 and T3 cycles of the Memory Read machine cycle
DDRA = 0x00; // Configure Z80 data bus D0-D7 (PA0-PA7) as input...
PORTA = 0xFF; // ...with pull-up
digitalWrite(RAM_CE2, HIGH); // Enable the RAM again (CE2 = HIGH)
pulseClock(3); // Execute all the following Memory Write machine cycle
// Execute the INC(HL) instruction (T = 6). See the Z80 datasheet and manual.
// After the execution of this instruction HL points to the next memory address.
pulseClock(1); // Execute the T1 cycle of M1 (Opcode Fetch machine cycle)
digitalWrite(RAM_CE2, LOW); // Force the RAM in HiZ (CE2 = LOW)
DDRA = 0xFF; // Configure Z80 data bus D0-D7 (PA0-PA7) as output
PORTA = INC_HL; // Write "INC(HL)" opcode on data bus
pulseClock(2); // Execute T2 and T3 cycles of M1
DDRA = 0x00; // Configure Z80 data bus D0-D7 (PA0-PA7) as input...
PORTA = 0xFF; // ...with pull-up
digitalWrite(RAM_CE2, HIGH); // Enable the RAM again (CE2 = HIGH)
pulseClock(3); // Execute all the remaining T cycles
}
// ------------------------------------------------------------------------------
void loadHL(word value)
// Load "value" word into the HL registers inside the Z80 CPU, using the "LD HL,nn" instruction.
// In the following "T" are the T-cycles of the Z80 (See the Z80 datashet).
{
// Execute the LD dd,nn instruction (T = 4+3+3), with dd = HL and nn = value. See the Z80 datasheet and manual.
// After the execution of this instruction the word "value" (16bit) is loaded into HL.
pulseClock(1); // Execute the T1 cycle of M1 (Opcode Fetch machine cycle)
digitalWrite(RAM_CE2, LOW); // Force the RAM in HiZ (CE2 = LOW)
DDRA = 0xFF; // Configure Z80 data bus D0-D7 (PA0-PA7) as output
PORTA = LD_HLnn; // Write "LD HL, n" opcode on data bus
pulseClock(2); // Execute T2 and T3 cycles of M1
DDRA = 0x00; // Configure Z80 data bus D0-D7 (PA0-PA7) as input...
PORTA = 0xFF; // ...with pull-up
pulseClock(2); // Complete the execution of M1 and execute the T1 cycle of the following
// Memory Read machine cycle
DDRA = 0xFF; // Configure Z80 data bus D0-D7 (PA0-PA7) as output
PORTA = lowByte(value); // Write first byte of "value" to load in HL
pulseClock(3); // Execute the T2 and T3 cycles of the first Memory Read machine cycle
// and T1, of the second Memory Read machine cycle
PORTA = highByte(value); // Write second byte of "value" to load in HL
pulseClock(2); // Execute the T2 and T3 cycles of the second Memory Read machine cycle
DDRA = 0x00; // Configure Z80 data bus D0-D7 (PA0-PA7) as input...
PORTA = 0xFF; // ...with pull-up
digitalWrite(RAM_CE2, HIGH); // Enable the RAM again (CE2 = HIGH)
}
// ------------------------------------------------------------------------------
void singlePulsesResetZ80()
// Reset the Z80 CPU using single pulses clock
{
digitalWrite(RESET_, LOW); // Set RESET_ active
pulseClock(6); // Generate twice the needed clock pulses to reset the Z80
digitalWrite(RESET_, HIGH); // Set RESET_ not active
pulseClock(2); // Needed two more clock pulses after RESET_ goes HIGH
}