Propeller Coprocessor for a ZX81 Computer

BeanBean Posts: 7,997
edited 2019-07-13 - 15:48:57 in Customer Projects
I've had the idea for a while about making coprocessor for the ZX81 from the propeller by basically connecting the propeller pins directly (almost) to the ZX81 bus.

I wanted the propeller to be memory-mapped because ZX81 BASIC doesn't have anyway to "talk" to I/O ports. And I wanted it to be usable from BASIC.

I wasn't sure if the propeller would be fast enough, but with the right code, it handles it with no problem.

The ZX81 has it's ROM at address 0 to 8191, and it's RAM starts at 16384. So I memory mapped the propeller right at the 8K memory location.
Because I wanted to use as many of the propeller pin as possible for user I/O the module is only partially decode.
It only decodes A15,A14,A13,A12 and A0 and A1. So the module's address repeats every 4 bytes between 8K and 16K, and I only have 4 address that I can read/write.
The base address is 8192,8193,8194, and 8195.
I also needed to connect the /WR /RD and /MREQ pins as well as the 8 data bits (D0 thru D7). So it takes a lot of pins to pull it off.

With only 4 memory address available, I took a "register" type approach to using the module. There are 128 16-bit registers.
To write to a register first you write the register number to 8192, then you write the LSB to 8193 and the MSB to 8194.
To read from a register first you write the register number+128 to 8192, then read the LSB from 8193 and the MSB from 8194.

For example to write 400 to register 1:
POKE 8192,1
POKE 8193,144
POKE 8194,1

For example to read from register 1:
POKE 8192,1+128
PRINT PEEK(8193)+256*PEEK(8194)

I have reserved memory address 8195 for possible register bank switching in the future.

The first couple registers set the user I/O pins and such, but I plan to support many of the objects in the OBEX like VGA, WII controllers, Sound chips, etc.

I have attached a photo of the first revision (REV -) of the board. I am currently working on REV A. The 16 pin chips are just resistor packs, so the only active ICs are the propeller and EEPROM.

Rev - had a limited number of user I/O pins because I decode more of the address bits. REV A

Soon I will post a schematic of REV A and a photo when I get the PCBs and get it built.

I posted the schematic. Forgot to mention that I added 16K RAM to the Rev A board because I don't have a pass-thru of the bus connector.

Here is the base code from Rev -
DEVICE P8X32A, XTAL1, PLL16X
FREQ 80_000_000   ' 5MHz * 16

p_Data        PIN 7..0
p_Data0       PIN 0
p_Data1       PIN 1
p_Data2       PIN 2
p_Data3       PIN 3
p_Data4       PIN 4
p_Data5       PIN 5
p_Data6       PIN 6
p_Data7       PIN 7

p_MREQn       PIN 8
p_IORQn       PIN 9
p_RDn         PIN 10
p_WRn         PIN 11

p_Address     PIN 19..12
p_A0          PIN 12
p_A1          PIN 13
p_A2          PIN 14
p_A3          PIN 15
p_A12         PIN 16
p_A13         PIN 17
p_A14         PIN 18
p_A15         PIN 19

p_IO          PIN 27..20
p_IO0         PIN 20
p_IO1         pin 21
p_IO2         pin 22
p_IO3         pin 23
p_IO4         pin 24
p_IO5         pin 25
p_IO6         pin 26
p_IO7         pin 27

p_SCL         PIN 28
p_SDA         PIN 29
p_SerialOut   PIN 30
p_SerialIn    PIN 31


haw_Registers  HUB WORD(128) = 0 ' 
hab_RegChanged HUB BYTE(256) = 0 ' 0 if not changed, 1 if changed
hw_ReadValueLSB   HUB WORD          ' 256 means not set
hw_ReadValueMSB   HUB WORD          ' 256 means not set

ZX81Write     TASK AUTO ' ZX81 writes to memory address 8192+
ZX81ReadLSB   TASK AUTO ' ZX81 reads from memory address 8193
ZX81ReadMSB   TASK AUTO ' ZX81 reads from memory address 8194

PROGRAM Start

regChangedPtr VAR LONG
regIndex      VAR LONG = 0
regValue      VAR LONG

Start:
  ' Use p_IO0 for sound
  COUNTERA 32,p_IO0,0,0,0
  LOW p_IO0

  ' This cog just monitors the registers to see if any of them have changed.
  ' If they do change, then it does whatever that register affects.
  DO 
    FOR regIndex = 0 TO 127
      RDBYTE hab_regChanged(regIndex), regValue
      IF regValue = 1 THEN ' If this register has changed, 
        ' Reset register changed status
        WRBYTE hab_regChanged(regIndex), 0
        ' Get register value
        RDWORD haw_Registers(regIndex), regValue

        IF regIndex = 0 THEN
          ' Process register 0 that changed, IO state
          OUTPUT p_IO
          p_IO = regValue
        ELSEIF regIndex = 1 THEN
          ' Process register 1 that changed, sound on IO0
          regValue = regValue << 11
          FRQA = regValue  
        ENDIF
      ENDIF ' regChanged = 1
      
    NEXT regIndex

  LOOP ' Forever  

END



TASK ZX81Write
mask       VAR LONG
match      VAR LONG
regAddr    VAR LONG = 0
busAddress VAR LONG
busData    VAR LONG
pinsValue  VAR LONG
temp       VAR LONG

'          +----A15
'          |+---A14
'          ||+--A13
'          |||+-A12
'          |||| +----A3
'          |||| |+---A2
'          |||| ||+--A1
'          |||| |||+-A0
'          |||| |||| +-----WRn
'          |||| |||| |+----RDn
'          |||| |||| ||+---IORQn
'          |||| |||| |||+--MREQn
'          |||| |||| ||||
'          |||| |||| ||||   DATA
'          |||| |||| |||| /------\
  mask =  %1111_0000_1111_00000000 ' 1=check state, 0=ignore state
  match = %0010_0000_0110_00000000 ' Pin states if writing to address 8192 thru 8207
  ' 8192=Select Register to write
  ' 8193=Write value to selected Register
  ' 8194=Read Register (does not change write register value)
 
  DO
    WAITPEQ match,mask
    pinsValue = INA
    
    busAddress = pinsValue >> 12
    busAddress = busAddress AND 15
    busData = pinsValue AND 255

    IF busAddress = 0 THEN ' 8192 (set/read register)
      regAddr = busData AND $7F
      IF busData > $7F THEN
        ' Read register
        RDWORD haw_Registers(regAddr), busData
        temp = busData AND $FF
        WRWORD hw_ReadValueLSB, temp
        busData = busData >> 8
        WRWORD hw_ReadValueMSB, busData
      ENDIF
    ELSEIF busAddress = 1 THEN ' 8193 (set LSB)
      RDWORD haw_registers(regAddr), temp
      temp = temp ANDN $FF
      temp = temp OR busData
      WRWORD haw_registers(regAddr), temp
      WRBYTE hab_RegChanged(regAddr), 1
    ELSEIF busAddress = 2 THEN ' 8194 (set MSB)
      RDWORD haw_registers(regAddr), temp
      temp = temp ANDN $FF00
      busData = busData << 8
      temp = temp OR busData
      WRWORD haw_registers(regAddr), temp
      WRBYTE hab_RegChanged(regAddr), 1
    ENDIF
  LOOP

ENDTASK


TASK ZX81ReadLSB
mask    VAR LONG
match   VAR LONG
temp    VAR LONG

'          +----A15
'          |+---A14
'          ||+--A13
'          |||+-A12
'          |||| +----A3
'          |||| |+---A2
'          |||| ||+--A1
'          |||| |||+-A0
'          |||| |||| +-----WRn
'          |||| |||| |+----RDn
'          |||| |||| ||+---IORQn
'          |||| |||| |||+--MREQn
'          |||| |||| ||||
'          |||| |||| ||||   DATA
'          |||| |||| |||| /------\
  mask =  %1111_1111_1111_00000000
  match = %0010_0001_1010_00000000 ' Memory read 8193

  DO
    WRWORD hw_ReadValueLSB, 256
    DO
      RDWORD hw_ReadValueLSB, temp
    LOOP UNTIL temp < 256
    p_Data = temp
    WAITPEQ match,mask
    OUTPUT p_Data
    WAITPNE match,mask
    INPUT p_Data
  LOOP
ENDTASK


TASK ZX81ReadMSB
mask    VAR LONG
match   VAR LONG
temp    VAR LONG

'          +----A15
'          |+---A14
'          ||+--A13
'          |||+-A12
'          |||| +----A3
'          |||| |+---A2
'          |||| ||+--A1
'          |||| |||+-A0
'          |||| |||| +-----WRn
'          |||| |||| |+----RDn
'          |||| |||| ||+---IORQn
'          |||| |||| |||+--MREQn
'          |||| |||| ||||
'          |||| |||| ||||   DATA
'          |||| |||| |||| /------\
  mask =  %1111_1111_1111_00000000
  match = %0010_0010_1010_00000000 ' Memory read 8194

  DO
    WRWORD hw_ReadValueMSB, 256
    DO
      RDWORD hw_ReadValueMSB, temp
    LOOP UNTIL temp < 256
    p_Data = temp
    WAITPEQ match,mask
    OUTPUT p_Data
    WAITPNE match,mask
    INPUT p_Data
  LOOP
ENDTASK



Bean

Comments

Sign In or Register to comment.