Sunday, November 18, 2012

Today I've made Z80 to execute the first command. And used Atmega as Z80 debug processor ;-)

First, the idea: burn flash rom (described below) with the trivialest program one can image, install cpu,
install flash, feed the processor with a clock source from ATmega, and install ATmega
as data bus spy - write all the data bus to RS232.

It's easy.


Сегодня Z80 у меня выполнил первую осмысленную программу. И попользовал Atmega как Debug Processor. Надо этот термин запатентовать ;-)
Прожигаем флешку (рассказывал как), и суем туда наипростейшую программулину, суем Z80, сум флешку, кормим ее тиканьем от ATmegавой ноги, и пользуем саму AtMegу как сниффер шины данных.

First, burn flash (AT29C256) with the following :
C3 00 00 FF FF FF FF to the end of chip (JP 0 ; NOP; NOP; NOP ...)

Next, connect data bus from CPU to ROM and port B of the atmega.
Here are legs (Atmega, Z80, 29C256) in triplets:
(14,15,11)(15,15,12)(16,12,13)(17,8,15)
(18,7,16)(19,9,17)(9,10,18)(10,13,19).

Connect address bus from CPU to flash.
Flash: leg 1 - +5, 20 - gnd, 22 OE - to CPU's RD(leg 21);
Cpu: 21 - RD to flash 22 OE and to INT0 of Atmega (leg 4),
22 - WR to INT1 of Atmega (leg 5).
It means that if Z80 reads, it reads from ROM (and causes interrupt on ATmega to read
the data bus, filled by flash), and if it writes, it writes to ATmega by interrupting it on INT1
That also means that *no* address decoding is performed, and no IO/MEM decoding is
performed, too.
So if you do OUT 0x7F,A or LD (0x1488),A - no matter - you write to ATmega.

Burn atmega with the program (attached here somewhere aroud (UPD: at the end of this post!)) and lets try to read it.
It install 4 Interrupt vectors:
ISR(USART_RXC_vect) - doing nothing in this project, do not care.
ISR(TIMER1_COMPA_vect) - blinks led on PORTC 0 and provides clock to Z80
ISR (INT0_vect) - external interrupt, catches RD from Z80.
ISR (INT1_vect) - external interrupt, catches WR from Z80 (I do not care about it, BTW)

But lets start from main().
DDRC = _BV(PC0) ; // Enabling output for LED on leg 23 of atmega
TIMSK = _BV(OCIE1A); // Timer stuff
TCCR1B = _BV(CS12) // 256 prescale
| _BV(WGM12); // CTC mode, TOP = OCR1A
// OCR1A = 825; // count up to TOP 1hz with 8 meg system clock BLINK FAST
OCR1A = 25; // count up to TOP 1hz with 8 meg system clock BLINK FAST
//Install timer of atmega to blink. I do not remember where I got this code - but it works.
/* Com port settings */
USART_Init(MYUBRR);
UCSRB |= (1 << RXCIE); // enable UDRempty intr.

/* Setting IRQ0 handling - read data bus on read */
DDRD &= ~(1 << DDD2); // Declare INT0/PortD 2 as input
PORTD |= (1 << PORTD2); // turn On the Pull-up
MCUCR &= ~(1 << ISC00); // Tell the interrupts to cause on falling edge only
MCUCR |= (1 << ISC01);
GICR |= (1 << INT0); // Turns on INT0
/* Setting IRQ1 handling - read data bus on write */
DDRD &= ~(1 << DDD3); // Clear the PD3 pin
PORTD |= (1 << PORTD3); // turn On the Pull-up
MCUCR &= ~(1 << ISC10); // Oh no, falling edge only
MCUCR |= (1 << ISC11);
GICR |= (1 << INT1); // Turns on INT0
DDRB = 0b00000000; // Configure as input for data bus
static unsigned PROGMEM char b[] = "\n\r**SP: RESETTED**\n\r";
USART_PUTS_my(b);
while(1) {}

And after all we get:
READS: 00
READS: 00
READS: C3
READS: 00
READS: 00
READS: C3
READS: 00
READS: 00
READS: C3
READS: 00
READS: 00
READS: C3
READS: 00
READS: 00
READS: C3
READS: 00
READS: 00
READS: C3
That's means the Z80 CPU is working! (if not - short the reset pin on Z80)
And, more, that means we assembled the computer.

Note: it's a very slow computer. It runs around ~10hz, saying because ATmega has to send several bytes over a serial link (4800 baud in this example) while Z80 executes ONE command.
UPD: the code



Шьем флешку (AT29C256) следущим:
C3 00 00 FF FF FF FF to the end of chip (JP 0 ; NOP; NOP; NOP ...)
Распаиваем data bus - триплеты ног (Atmega,Z80,29C256)
(14,15,11)(15,15,12)(16,12,13)(17,8,15)
(18,7,16)(19,9,17)(9,10,18)(10,13,19).

Распаиваем адресную шину от Z80 до флешки.
Далее:
Флешка: нога 1 - +5, 20 - gnd, 22 OE - на CPU's RD(нога 21);
Cpu: 21 - RD к флешкиной 22 OE и на INT0 Atmegи (нога 4),
22 - WR на INT1 Atmegи (нога 5).

Понятно из этого что если проц что-то читает - то из флешки (и дергает ATmegу за INT0, которая радостно читает в этот момент данные которые родила фешка), а если пишет - то вообще безусловно попадает в ATMegа, дергая ее за INT1.

Заливаем а ATMegу программулину (где-то тут надо ее залить (UPD: залил, в конце!)) и давайте читать программулю.
Она устанавливает 4 вектора прерываний:
ISR(USART_RXC_vect) - это щаз нам пох
ISR(TIMER1_COMPA_vect) - моргает ledом на PORTC 0 и клочит Z80на CLK (нога 6)
ISR (INT0_vect) - внешнее перывание, ловит RD от Z80.
ISR (INT1_vect) - внешнее перывание, ловит WR от Z80

Давате почитаем от main()
DDRC = _BV(PC0) ; // Нога 23 - это LED моргалка и клоки для Z80
TIMSK = _BV(OCIE1A); // Timer stuff
TCCR1B = _BV(CS12) // 256 prescale
| _BV(WGM12); // CTC mode, TOP = OCR1A
// OCR1A = 825; // count up to TOP 1hz with 8 meg system clock BLINK FAST
OCR1A = 25; // count up to TOP 1hz with 8 meg system clock BLINK FAST
// Это инициализация таймера. Спер откуда-то, работает.

/* Это ком порт - сеттинги */
USART_Init(MYUBRR);
UCSRB |= (1 << RXCIE); // enable UDRempty intr.

/* Натравливаем INT0 на падение уровня на ноге INT0 */
DDRD &= ~(1 << DDD2); // Declare INT0/PortD 2 as input
PORTD |= (1 << PORTD2); // turn On the Pull-up
MCUCR &= ~(1 << ISC00); // Tell the interrupts to cause on falling edge only
MCUCR |= (1 << ISC01);
GICR |= (1 << INT0); // Turns on INT0
/* То же самое про INT1 */
DDRD &= ~(1 << DDD3); // Clear the PD3 pin
PORTD |= (1 << PORTD3); // turn On the Pull-up
MCUCR &= ~(1 << ISC10); // Oh no, falling edge only
MCUCR |= (1 << ISC11);
GICR |= (1 << INT1); // Turns on INT0
DDRB = 0b00000000; //Объявлем регистр B - вводом, и все его 8 бит будут тырить наш Data Bus

static unsigned PROGMEM char b[] = "\n\r**SP: RESETTED**\n\r";
USART_PUTS_my(b);
while(1) {}

Должны увидеть
READS: 00
READS: 00
READS: C3
READS: 00
READS: 00
READS: C3
READS: 00
READS: 00
READS: C3
READS: 00
READS: 00
READS: C3
READS: 00
READS: 00
READS: C3
READS: 00
READS: 00
READS: C3

Это значит что проц работает - читает комманду JP 0000. (а если нет - то коротните reset на ноге 26 у Z80)
А так же это значит что мы собрали компьютер ;-)

Кстати, заметим, что при этом достаточно тормозной (герц 10). Например потому что atmega должна перекинуть пару байт по rs232 на скорости 4800.

UPD: the code

No comments:

Post a Comment