' This program tests a Propeller booting up a 5V Z80 CPU in a minimalist system fitted with 128kB SRAM. ' ' 27 August 2013 rogloh ' ' Minimal circuit is set up with signals connected as follows: ' ' Z80 pin Propeller Direction (relative to Prop) ' D0 P0 Bidirectional ' D1 P1 Bidirectional ' D2 P2 Bidirectional ' D3 P3 Bidirectional ' D4 P4 Bidirectional ' D5 P5 Bidirectional ' D6 P6 Bidirectional ' D7 P7 Bidirectional ' A0 P8 Input from Z80 ' ~RD P9 Input from Z80 (but prop can also override if required) ' ~WR P10 Input from Z80 (but prop can also override if required) ' ~IORQ P11 Input from Z80 ' ~WAIT P12 Output to Z80 ' ~RESET P13 Output to Z80 ' CLOCK P14 Output to Z80 ' ' ' 2.2kΩ resistors are fitted between between all 5V Z80 output signals and 3.3V propeller input pins from P0-P11 for current limiting. ' P9 pin also directly connects to SRAM ~OE signal for reading, allowing the prop to override the Z80 if/when necessary. ' P10 pin also directly connects to SRAM ~WE signal for writing, allowing the prop to override the Z80 if/when necessary. ' The Z80 ~MREQ signal connects to SRAM's ~CE1, and SRAM's CE2 is unused and strapped high. ' ' The Z80's 16 bit address bus and 8 bit data bus are connected directly to the corresponding 5V SRAM pins. ' The SRAM A16 signal is unused and strapped low (only 64kB memory used). ' 4.7kΩ pullup resistors are connected between these Z80 signals and 5V : ~NMI, ~WAIT, ~RESET, ~BUSRQ, ~INT, ~RD, ~WR, ~IORQ, ~MREQ ' Upon a Z80 reset the Z80 assembly language opcodes for booting are loaded into the SRAM at a nominated start address. ' When all opcodes are loaded, the boot code will be executed at a nominated entry point. ' The Z80 stack pointer will be left pointing at the lowest address of the loaded boot code. ' The HL and PC registers are both set to the entry point of the boot code as it begins to run. ' ' Due to the way stack push operations always use 16 bit words on the Z80, the boot code also needs to be 16 bit aligned so it can be read in as words. ' A zero (NOP opcode) is automatically padded at the end of the boot code to resolve this if required. ' ' The example bootloader program used here for testing simply writes out a string to an I/O output port. ' This IO write access is intercepted by the Propeller which then writes the data from the Z80 to a serial port, waiting whenever the transmit buffer is full. CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 addr0_pin = 8 read_pin = 9 write_pin = 10 iorq_pin = 11 wait_pin = 12 reset_pin = 13 clock_pin = 14 tx_pin = 30 rx_pin = 31 Z80_BOOT_START_ADDR = $FF00 Z80_BOOT_ENTRY_ADDR = $FF00 OBJ uart : "FullDuplexSerial" VAR long stack[256] long cog_id long write_data PUB start cognew(startx, @stack) PUB startx uart.start(rx_pin, tx_pin, 0, 115200) boot_end := @bootcode_end uart.str(String("Sending Z80 boot opcodes totalling ")) uart.dec(boot_len) uart.str(String(" bytes...")) uart.tx(13) uart.tx(10) cog_id := cognew(@boot_z80, @write_data) repeat write_data := 0 repeat until write_data <> 0 uart.tx(write_data & $ff) DAT org 0 boot_z80 mov iowrite_mask, write_pinmask 'setup masks and output pin directions or iowrite_mask, iorq_pinmask mov outa, wait_pinmask or outa, read_pinmask mov dira, wait_pinmask 'set ~WAIT high or dira, clock_pinmask 'allow prop to drive the Z80 clock or dira, reset_pinmask 'reset Z80 mov stackptr, boot_start_addr test boot_len, #1 wz if_nz add boot_len, #1 '} align to a 16 bit word if required if_nz add boot_end, #1 '} add stackptr, boot_len mov codeptr, boot_end 'code reading begins at end of boot code mov lastptr, codeptr ' sub codeptr, #2 sub lastptr, boot_len ' mov ctra, #0 'start with clock off movd ctra, #$ff movs ctra, #clock_pin mov phsa, #0 mov frqa, freq_2M5 'prepare for a 2.5MHz clock rate mov state, #state0 'initialize the state machine mov clock_count, #10 wz 'set clock counter and also clear Z flag initially (gets used below) toggle_clock xor outa, clock_pinmask 'toggle the clock pin signal high while in reset nop nop nop xor outa, clock_pinmask 'toggle the clock pin low while in reset nop nop djnz clock_count, #toggle_clock or outa, reset_pinmask 'release reset signal to Z80 movi ctra, oscillator 'let the counter control the clock pin now, and clock @2.5MHz feed_opcodes waitpeq zero, read_pinmask 'wait for ~RD to go low andn outa, wait_pinmask 'drop ~WAIT low to buy time while we figure out the opcode to send or dira, read_pinmask 'drive SRAM ~OE high to stop SRAM from driving the data bus jmp state 'state machine gets next opcode/encoded bootcode data got_opcode or dira, #$ff 'enable output drivers on data bus pins movs outa, opcode 'output the opcode on the data bus pins or outa, wait_pinmask 'release ~WAIT to let Z80 continue to read the opcode nop '} nop '} add some 50ns nop delays here for fine tuning (critical bus timing here) nop '} @2.5MHz Z80 clock freq, the period is 400ns = 8 propeller NOPs to account for wait delay cycle nop '} nop '} nop '} nop '} nop '} nop '} nop '} add 4 more cycles = 200ns or 1/2 Z80 clock cycle before turning off bus in the worst case nop '} andn dira, #$ff 'tri-state the data bus from the prop side before Z80 tries to drive it during writes andn dira, read_pinmask 'also let Z80 control ~OE again if_nz jmp #feed_opcodes 'keep going until we are done ' The boot code is now loaded, and the Z80 will now start to execute it. ' Some sample PASM code follows to decode a port write operation and to write the output port data from the Z80 to prop memory. wait_io_write 'jmp #wait_io_write or outa, wait_pinmask ' let the Z80 run again without wait active waitpne zero, iowrite_mask waitpeq zero, iowrite_mask ' prop waits for ~IORQ low, and ~WR low andn outa, wait_pinmask ' drop ~WAIT low while we do our operation movs iodata, ina ' read data from bus or iodata, #$100 ' set flag bit to indicate data is ready wrlong iodata, par ' write data to prop memory poll_loop rdlong iodata, par wz ' poll for write completion, data is cleared when read by reader if_nz jmp #poll_loop jmp #wait_io_write ' continue ' The opcode to be returned is determined by a state machine. ' Only four different Z80 instructions are needed to install the boot code anywhere in memory and start it running. ' ' LD SP, xxyy - once on entry to point to point SP to last boot code address + 1 ' ' LD HL, wwzz } These 2 instructions are repeated until all boot data is written plus the final entry point address. ' PUSH HL } ' ' RET - once to exit and begin running at desired code entry point state0 mov opcode, #$31 ' LD SP, xxyy mov state, #state1 jmp #got_opcode state1 mov opcode, stackptr ' yy value above (LSB) mov state, #state2 jmp #got_opcode state2 shr opcode, #8 ' xx value above (MSB) mov state, #state3 jmp #got_opcode state3 mov opcode, #$21 ' LD HL, wwzz mov state, #state4 jmp #got_opcode state4 rdword opcode, codeptr ' zz value above (LSB) mov state, #state5 jmp #got_opcode state5 shr opcode, #8 ' ww value above (MSB) mov state, #state6 jmp #got_opcode state6 mov opcode, #$E5 ' PUSH HL - MSB is stored at higher address in memory (I expect!) mov state, #state7 jmp #got_opcode state7 cmp codeptr, lastptr wz ' Z flag is set when we've reached the end of the code, otherwise repeat from state 3 sub codeptr, #2 if_nz jmp #state3 mov opcode, #$21 wz ' clear Z flag which we just set mov state, #state8 jmp #got_opcode state8 mov opcode, boot_entry_addr mov state, #state9 jmp #got_opcode state9 shr opcode, #8 mov state, #state10 jmp #got_opcode state10 mov opcode, #$E5 ' PUSH HL mov state, #state11 jmp #got_opcode state11 xor opcode, opcode wz ' set Z flag to indicate we are done mov opcode, #$C9 ' RET jmp #got_opcode zero long 0 state long 0 clock_count long 0 opcode long 0 iodata long 0 codeptr long 0 stackptr long 0 lastptr long 0 read_pinmask long (1 << read_pin) write_pinmask long (1 << write_pin) iorq_pinmask long (1 << iorq_pin) wait_pinmask long (1 << wait_pin) reset_pinmask long (1 << reset_pin) clock_pinmask long (1 << clock_pin) iowrite_mask long 0 oscillator long %0_00100_000 freq_2M5 long $08000000 'MSB toggles every 16 clocks @ 80MHz, overall output freq = 80M/32 = 2.5MHz boot_len long @bootcode_end - @bootcode_start boot_end long 0 ' patched at runtime boot_start_addr long Z80_BOOT_START_ADDR boot_entry_addr long Z80_BOOT_ENTRY_ADDR fit bootcode_start ' file 'bootcode.raw' byte $3E, "H" ' LD A, 'H' byte $D3, $00 ' OUT (0), A byte $3E, "E" ' LD A, 'E' byte $D3, $00 ' OUT (0), A byte $3E, "L" ' LD A, 'L' byte $D3, $00 ' OUT (0), A byte $3E, "L" ' LD A, 'L' byte $D3, $00 ' OUT (0), A byte $3E, "O" ' LD A, 'O' byte $D3, $00 ' OUT (0), A byte $3E, " " ' LD A, ' ' byte $D3, $00 ' OUT (0), A byte $3E, "W" ' LD A, 'W' byte $D3, $00 ' OUT (0), A byte $3E, "O" ' LD A, 'O' byte $D3, $00 ' OUT (0), A byte $3E, "R" ' LD A, 'R' byte $D3, $00 ' OUT (0), A byte $3E, "L" ' LD A, 'L' byte $D3, $00 ' OUT (0), A byte $3E, "D" ' LD A, 'D' byte $D3, $00 ' OUT (0), A byte $3E, 13 ' LD A, 13 byte $D3, $00 ' OUT (0), A byte $3E, 10 ' LD A, 10 byte $D3, $00 ' OUT (0), A byte $C3, $00, $FF ' JMP $FF00 bootcode_end byte $0 ' an extra NOP is read from here if required for 16 bit alignment {{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ TERMS OF USE: MIT License │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ │files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ │modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ │is furnished to do so, subject to the following conditions: │ │ │ │The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ │ │ │THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ │WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ │COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ │ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }}