Note the Russian Spelling of Blya-Blya-Blya!
20140118 | 20140325 |
20140108
Getting Reset, AVR (MCU) lowers RESET+HALT of 68008 (CPU) and begins to click
clocks in main loop, which leads to the state called Boot Mode and raises
CPU's RESET+HALT some clicks after that.
Getting falling font of DS the MCU's INT0 is accrued; while serving INT, main
program waits until INT0 finished, so clock does not click, and there is no reason to play DTACK
UPD 20160101: It's not so in case of EC000. It hates this sort of low freqs.
You cannot step-by-step debug it this way. While it loads from ATmega, it's unable
to fall down where you print all that debug stuff via serial. So you have to
play DTACK with EC000, and that's not as easy, but is easy, anyway
INT0 feeds next byte of program to the CPU data bus (if CPU reads it), and leaves it there, returning
from INT0 handler and making the main clock clicks.
When main loop detects that DS is not active, it removes data from data bus.
So here it is:
While in boot mode, CPU should transfer some initial program to memory,
then MCU should leave boot mode and send reset. After reset, CPU should execute the
main program already in RAM. It can later switch the clock source to run from a fast quartz.
/* Interrupt handling - DS gone low */
void ISR0()
{
if (RWline is UP) {
/* feed a byte to CPU */
writing_on_bus = 1;
write_on_bus(bytefeeder());
} else {
/* CPU is writing byte */
}
}
void main()
{
bootmode = 1;
reset_CPU(); reset_ticks = RESET_TICKS_VAL;
install_int0_handler(DS_line_does_down);
while(1)
{
click_clock();
/* may be some minimal delay, but CPU should be much faster then MCU*/
if (reset_ticks > 0)
{
if (!(reset_ticks--))
{
release_reset_CPU(); // reset - up!
}
}
if (DS_line_got_high)
{
if (writing_on_bus) {
switch_data_bus_to_read_mode();
writing_on_bus = 0;
}
}
}
}
20140116
Note 1. We have serial on ATmega, and you can use MAX232 or whatever you like
to connect it to COM port or USB.
UPD: Keep in mind I utilize hardware RTS/flow control later in this project; so I'm not sure USB
converters will do the job.
20150114 UPD: Later, in 2015, I utilize CTS signal also. In 2014 version I called RTS as 'CTS' (both
on the scheme and in AVR firmware), but it really, really was CTS signal, telling unix host side
to pause the transmission. Since 2015 (with versions above 2501) I call RTS signal, yes, RTS, and
also I mind CTS. The CTS processing is semi-clever: if in boot time CTS is high, I assume CTS
is not connected and ignore it. If it is low, I (you guessed) mind it, and pause the transmission
when it is high telling CPU 'not ready'.
Note 2. It is mentioned that interrupts are used to determine /DS signal. Generally,
you do not need that because while CPU is clocked from MCU they are fully synchronized
(and when CPU will be clocked from external source there will be no way to talk with MCU
because it is slow). AVR interrupts are used because the code is clearer.
20140108
The scheme for Running first program
The GCC source or AVR program
Warning! This file was updated at 20140416. It has a error - DEBUG
was called
DEDUG
in .h file
Note The quartz I use for AVR is 12Mhz quartz.
It can be 16Mhz [UPD: strange, I've tried and failed (!) [UPD2: because I should also
add two tiny capacitors around it!]] or like that
[should be configured in sources] (but not lower than 8Mhz - because the serial
speed I use is 38400; if you will be happy with 2400 you can even use internal
AVR clock and say that in AVR sources)
The result of running program:
(* is a clock tick , is a comment)
**********************************************************************************************[skipped]********
********************************************************************************************
Calling release reset
[Released reset]
**************************** clock after reset
INT: R Int happens when /DS goes low; R means that CPU is READING
State 0000***** "State 00" is some debug message, the other byte is actual byte
Releasing bus*** 5 clock changes /DS from low to high; 'Releasing bus' - MCU put data bus to high impedance.
INT: R
State 0000*****
Releasing bus***
INT: R
State 0000*****
Releasing bus***
INT: R
State 0008***** fed with 00000008; really the address does not matter, but it must be even, -
Releasing bus*** the first thing I've got was a trap leading write 14 bytes to somewhere
INT: R
State 0000*****
Releasing bus***
INT: R
State 0000*****
Releasing bus***
INT: R
State 0000*****
Releasing bus***
INT: R
State 00C0***** fed by stack pointer
Releasing bus***
INT: R
State 0020*****
Releasing bus***
INT: R
State 007C*****
Releasing bus*******
INT: R
State 0000*****
Releasing bus***
INT: R
State 0000*****
Releasing bus***
INT: R
State 0000*****
Releasing bus***
INT: R
State 0000***** fed with movea.l #0,A0
Releasing bus***
INT: R
State 0020*****
Releasing bus***
INT: R
State 003C*****
Releasing bus***
INT: R
State 00AA*****
Releasing bus***
INT: R
State 0055*****
Releasing bus***
INT: R
State 0033*****
Releasing bus***
INT: R
State 00CC***** fed with move.l #$aa5533cc,D0
Releasing bus***
INT: R
State 0020*****
Releasing bus***
INT: R
State 0080***** move.l D0,(A0)
Releasing bus***
INT: R
State 004E*****
Releasing bus***
INT: R
State 0071***** fed with NOP - that's prefetching
Releasing bus*****
INT: WAA******** Writing data! Whuuuuaaaaaauhuhhhh!
INT: W55********
INT: W33********
INT: WCC******
INT: R
State 004E*****
Releasing bus***
INT: R
State 0071*****
Releasing bus***
INT: R
State .....
First, note the 74138. It decodes higher address lines. It's all clear with it. Also, that it clear that RAM should start on Addr 0h, so put the /A0000 line to /CE of RAM.
Look at Atmega. The most significant of the new connections is the /BOOTMODE line.
It is used to inform RAM to behave the following way: when /BOOTMODE is active, CPU
should read not from RAM, but from MCU. It is AND-NOTed with R/W, so when
/BOOTMODE is active (0) and READ(1) happens, the output of 7400 leg 8 is not active (high).
When /BOOTMODE is not active and READ goes high, the output of 7400/8 goes low, and,
ORed with /DS, this is fed (as /MEMRD) to /OE pin of RAM.
7432 also ORs /DS and R//W to get /MEMWR.
Sure, we'll use ATmega as uart, so we should connect one of the lines from 74138 (ORed with /DS) to one of ATmega's leg (again, as interrupt, while that's not absolutely needed). And add an address line (A1) to get two registers for read and for write. It's all clear that one pair is read byte from line and write byte to line, the other read register is status saying "we have a byte in input" and "it's clear to send the next char". The extra writing register is a HEX output. (UPD 201506: do not rely on it, I've found much better idea - read and write the MCU's NVRAM by CPU, but currently do not use it)
So here will be an algorithm (bold is new code):
/* Interrupt handling - DS gone low */
void ISR0()
{
if (RWline is UP) {
/* feed a byte to CPU */
writing_on_bus = 1;
b = bytefeeder();
if (b != -1)
{
write_on_bus(b);
} else { // The boot 68k program is ended!
bootmode = 0; /* and /BOOTMODE leg*/
reset_CPU();
reset_ticks = RESET_TICKS_VAL;
uninstall_IRQ_0_handler(); /* or you can leave it if you plan to look at data
bus transfers while debugging read/write from memory; in this case it [may]
conflict with serial IRQ */
}
} else {
/* CPU is writing byte to memory*/
}
}
void ISR2(USART_RXC_vect) // Serial event: a char came from terminal to AVR
{
byte_came = byte_from_serial();
byte_in_rx_que = 0x02; // Flag to be ORed when we'll tell CPU the status
}
void ISR1() // interrupt happened on /AVRSERAIL goes low
{
if (RWline is UP)
{
/* CPU is reading from serial */
writing_on_bus = 1;
if (A2_line_is_up) // CPU reads status
{
write_on_bus (clear_to_send | byte_in_rx_que);
} else
{
write_on_bus(byte_in_rx_que); // last recieved byte from serial
byte_in_rx_que = 0;
}
} else /*CPU is writing to bus*/
{
if (A2_line_is_up)
{
serial_send_hex(byte_from_data_bus());
} else
{
serial_send(byte_from_data_bus());
}
}
}
void main()
{
bootmode = 1; /* as well as /BOOTMODE leg */
byte_in_rx_que = 0; // No pending serial bytes
reset_CPU(); reset_ticks = RESET_TICKS_VAL;
install_int0_handler(DS_line_does_down);
install_int1_handler(AVRSERIAL_goes_down); // Both can be done when leaving bootmode
install_serial_rx_handler();
while(1)
{
click_clock();
if (reset_ticks > 0)
{
if (!(reset_ticks--))
{
release_reset_CPU(); // reset - up!
}
}
if (DS_line_got_high)
{
if (writing_on_bus) {
switch_data_bus_to_read_mode();
writing_on_bus = 0;
}
}
}
}
What do we feed to cpu, you may ask?
Here it is (in file bytefeeder.c):
uint8_t PROGMEM prologue[] = {
0x00,0x00,0x10,0x00 // 0 dc.l stack pointer
,0x00,0x00,0x00,0x08 // 0 dc.l start
,0x20,0x7C ,0x00,0x00,0x00,0x00 // 8 movea.l #0,A0
};
, then, in loop,
uint8_t PROGMEM program_loop_bytes[] = {
0x30, 0xFC, 0, 0, // move.w #XX,(A0)+
0x4e, 0x71 // NOP
};
, replacing zeroes by the bytes from the actual program. The trailing NOP is needed because
68008 prefetches one command before executing the prevous (as shown in Chapter II)bin2c.tcl
script.
While what you see in the 68K code is very experimental (this is my first attempt to write 68K code),
at least here you can see an example (in byteio_fast.asm) file how to read and write bytes via AVR UART emulation
uartstat equ $A0002
uartdata equ $A0000
slowclock equ $C0000
fastclock equ $E0000
;------------------ RX char
rx_char:
bsr rx_notempt ; while rx is not empty, ie, no char
beq rx_char ; loop
move.b d0,(slowclock) ; switching clock slow
move.b (uartdata),d0 ; read from uart
move.b d0,(fastclock) ; switching clock fast
cmpi.b #CTRLC,d0 ; CTRL-C?
beq warm ; warm reset
rts
;------------------ Test input for a char (NOT ZERO if IS)
rx_notempt:
move.b d0,(slowclock) ; switching clock slow
move.b (uartstat),d0
move.b d0,(fastclock) ; switching clock fast
andi.b #$02,d0 ; bit 1 is 1? (I've told you that it is my first attempt to write 68k code!)
rts
;------------------ Wait for clear to send -------
tx_rdy_w:
move.l d0,-(sp)
tx_rdy_w1:
bsr tx_rdy
bne tx_rdy_w1
move.l (sp)+,d0
rts
;------------------ TX char
tx_char:
bsr tx_rdy_w
move.b d0,(slowclock) ; switching clock slow
move.b d0,(uartdata)
move.b d0,(fastclock) ; switching clock fast
rts
;----------------- Returns TX ready (Z)
tx_rdy:
move.b d0,(slowclock) ; switching clock slow
move.b (uartstat),d0
move.b d0,(fastclock) ; switching clock fast
andi.b #$01,d0
rts
The most interesting in this code is that it is working. I do not know what to tell you about this code,
may be the only thing: do not forget to switch the clock fast in yours main program
move.b #7,$80000 ; CS - up, Dataout - up, CLK - up
move.b #2,$80000 ; CS - down, Dataout - up, CLK - down
manage that bits this way (but better keep higher bits high - we may have some extra SPI devices in future), and#define WITH_CTS
to use this code
(enabled by default) and 256 bytes ring boofer for handling input bytes from serial.BF
Blink SDCLK fast (with fullspeed clock)BS
Blink SDCLK slow (with AVRCLK)BLINK
Blink until key pressed (fullspeed clock)ON
Set SDCLK to 0 (or 1)
OFF
Set SDCLK to 0 (or 1)
1
Set SDCLK to 1, SDDATAIN to 0, SCCS to 0)
2
Set SDCLK to 0, SDDATAIN to 1, SCCS to 0)
4
Set SDCLK to 0, SDDATAIN to 0, SCCS to 1)
IN
Tell SDDATAOUT status (or inverted SDDATAOUT?)
DUMP[optional address]
Dump memory from address (or continue to dump from that address if
no address given)
BOOT
Try to init the SD card
S[srecord]
Parce S-Records. It understands S1,S2,S3 records and ignores all the others
f_myfunc
subrotine which should return to monitor with rts
.commands:
and it's address (f_myfunc
) to commands_vector
When called, A1 will point to the first byte after you's command mnemonic in input buffer, so if was called as
myfuncbebebe
, A1 will point to "bebebe"
GOaddress
monitor command , which goes to the specified address.C-Kermit>
(if you use C-Kermit as do I) 's terminal mode, I can press
[CTRL
]+[\
] [B
] , and
get a reset!
68000 Assembler - version 2.71.F3s (Sep 21, 2004)
Copyright 1985 by Brian R. Anderson
AmigaDOS conversion copyright 1991 by Charlie Gibbs.
Adapted for use with Fargo by David Ellsworth.
Bugfixes and additions by Julien Muchembled, Paul Froissart and Kevin Kofler
asmx multi-assembler version 2.0b5
Copyright 1998-2007 Bruce Tomlin
- buggy as shit. May be vasm is good - but I'm not ready to write linker scripts just to get SRecord from CPM15000.SR
- that's an CP/M in S-Records (which is
loaded from address $15000).README.TXT
which says
that to create a BIOS I should figure out _init
(== ORG of BIOS == $1B000) and
_ccp
($150BC) symbols in CPM15000.MAP
(and that differs from what is
written in "System Guide"; there said you should patch Srecords file [for 1.0 and 1.1 CP/M 68k vers.]).
- constat
- conin
- conout
, but it was not enough; setexc
also a must for CP/M to show you
it's A:>
. The first thing it does, it installs an TRAP handlers for almost all the TRAPS!
go15000
getseg
call at all. And then I was able to run DDT68000
,
but during read it complained "Cannot write sector" (and it was not writing sector at
all!).
That was because I forgot flush
call.
BIOS
,which
contains BIOS (sort of ugly bios), and Disk
,
which contains pretty empty disk image and diskdefs
for cpmtools.
(local)
The
disk image itself contains A: - P: drives, cpmtools with this diskdefs
are able to write to A: . Write there a whole CP/M distribution.assemble
script, you'll get bios.s
.
Next, load it and CPM15000.SR
via cut-and-paste to SBC console.
set line /dev/ttyS0
set car off
set speed 38400
set flow rts
set transmit timeout 10
(and set input echo on
, but I forgot what it is for and if it is really needed)
So from SBC prompt say * boot
bios.s
and CPM15000.SR
by
C-Kermit> transmit CPM15000.SR
C-Kermit> transmit bios.s
go15000
and get A:
.
CPMLDR
and CPM.SYS
.
Sure, we will create them on the board. But I refuse to learn CP/M ed!RELOC
X.SUB
scriptscan't open 0:AS68SYMB.DAT
, ensure you have AS68INIT from DISK3 and run
AS68 -I AS68INIT
for every TPA change (or system relocation; look at Programmers Guide)
/usr/local/bin/cpmls -f sga /vol/dev/dsk/c2t0d0/unknown_format
CPM.SYS
and CPMLDR.SYS
.UUDECODE
program B:
), and
able to transmit uuencoded files there. That's not too fast, but quite good for transferring
sources.bios.asm
file on the board. Here what to be done:
AS_SRECORD
, AS_CPMLDR_BIOS
write_sector_loop_2_exit_ok:
to
the 8 chars lables like WRSL2E1:
;
to *
defs.h
which should be
#include
d in the beginning of bios.asm
. Also,include "dph.asm"
to became #include "dph.h"
and run it via cpp
and then transfer it into SBC's disk* boot
* go1080
System Guide
(the system is
$18000 based; do not forget to mention memrgn
) ; also, there is readme for whole
the disk and readme on disk b:
; there is mkldr.sub
which should run from disk c:
- that's
linker batch to generate cpmldr.sys
.XPUTBOOT b:CPMLDR.SYS A:
- and you'll get the bootable media. I use
distributed xputboot
because I'm lazy to rewrite it;cpmldr.sys
and watched what I've got - and $1080 was the starting address. Why not?bios.asm
unix file, the only difference are the defines. The
problem is that I cannot edit them on board with emacs - it has not enough
memory.
diskdef sga
seclen 512
tracks 512
sectrk 32
blocksize 4096
maxdir 512
boottrk 1
os 2.2
end
/usr/local/bin/cpmls -f sga /vol/dev/dsk/c2t0d0/unknown_format
, on Linux just like /dev/sdb
diskmap i: 21
[map disk i: to be 21th disk])
stty.h
is missing in CP/M 68K 1.3;
author (George Harvey) of GKermit port for CP/M-68 says it is really missing and should be looked at
1.2 release (or even 1.1)68kavrYYMR \ / \/ ------ > project name, 68kavr YY Year, 1990 based (DtZ's tradition; 24 for 2014) M Month - '1' for jan, '9' for Sep, 'b' for Nov R Release this month
move.w#$80#$7FFF,d4 ; and now just wait for **NOT!!** 0 writefinl2: move.l d5,-(sp) ;DELETE THIS PUSH? move.l d4,-(sp) ;DELETE THIS PUSH? ; bsr sd_read_byte IFNE SD_WRITE_DEBUG bsr sd_read_byte_debug ; will return in D0 ENDC IFEQ SD_WRITE_DEBUG bsr sd_read_byte ; will return in D0 ENDC move.l (sp)+,d4 ; DELETE THIS POP? move.l (sp)+,d5 ; DELETE THIS POP? cmpi.b #0,d0 ; "Non zero byte to be returned"!beqbne write_ok ; 20141122 was beq! dbra d4,writefinl2
boot
command loads first 16K of disk not to $1000, but $8000, so to start CP/M
you should type GO8080
(that's not a joke! ;-) )Flag counter for 68kavr Epoch I only |