Home - Features - Downloads - Schematics - I/O Registers - Memory Map - Leave a Message

ZX-Badaloc Reloaded


ZX-Badaloc   Reloaded

A Spartan-3E FPGA implementation of the ZX-Badaloc Spectrum clone

Supported I/O Registers List


ZX-Spectrum standard registers:

$FE (254): Keyboard / Tape / Speaker / Border color

D7        Always '1'
D6        Tape Ear in
D5        Always '1'
D4:D0     Fully functional Keyboard from PS/2 data

D4        Speaker out
D3        Tape Mic out
D2:D0   Border Color


$1FFD (8189): ZX-Spectrum 128K +2A / +3 special mode register

D2:D0   Read back written data
D3        BTN3 Button
D7:D4   SW3:SW0

D0        Paging mode. 0 = normal; 1 = +2A/+3 special paging
D3        disk motor (not implemented)
D4        printer strobe (not implemented)

Normal mode (D0 = '0'):
D1        Ignored
D2        High bit (A15) of rom_bank

Special mode (D0 = '1'):
D2:D1   Selects a paging mode out of four possible (see the +3 documentation)


$7FFD (32765): ZX-Spectrum 128K Memory Control

Read / Write:
D5        1FFD / 7FFD Registers Lock: '1' = Locked (no further write access allowed)
D4        low bit (A14) of rom bank
D3        Video Bank select
D2:D0   128K Ram Bank Select


ZX-Badaloc Custom Registers:

$1F (31): Kempston Joystick and SD-Card chip select

D7:D0        8 bit Kempston Joystick out of 16 inputs provided by 74HC166 interface. LSB or MSB bank selectable (see below)

D7:D4        If = '1010' the Kempston port will read the 8 MSB bits instead of the LSB (default)
                 If = '1011' the Programmable Joystick registers are write-enabled in memory (32 bytes)
D1:D0        SPI Chip select enable: "10" = sd_card 1, "01" = sd_card 2, "00" = offboard M25P16 Flash chip select, "11" all disabled
The first sd-card slot has been installed and successfully tested


$3F (63): SPI Interface Data Register

Read: 8 bit from selected SPI device (see $1F register)
Write: 8 bit to selected SPI device (see $1F register)

$5F (95): NMI Enable on RS-232 reception, RS-232 Status Register

D7:D4     Always read "0000"
D3          rs-232 TX Status: '0' = idle, '1' = register full (tx in progress)
D2          rs-232 Framing Error: '1' = Error
D1          rs-232 Overrun Error: '1' = Error
D0          rs-232 RX Status: '1' = Data is Available for read on port $7F

D7:D1     Not used
D0          NMI Enable when rs-232 data is received: '1' = Enabled


$7F (127): RS-232 (Female DB9 Connector) Data Registers

Read: 8 bit RS-232 RX Register
Write: 8 bit RS-232 TX Register


$24DF (9439): Rom Bank / Z80 Clock Select

Read / Write:
D7:D6     ZX-Badaloc shadow ram rd/wr control: meaningless on this FPGA implementation: not implemented
D5:D3     ZX-Badaloc high rom bank address select (A18:A16) - A15 and A14 come from 1FFD and 7FFD standard registers
              32 rom banks (16K each) can be selected into the 0-3FFF memory area combining these 5 bits.
D2          Z80 Clock / Screen / INT Doubler: '1' = Z80 Clock *2, INT = 100Hz, VGA 100 Hz instead of 50Hz
D1:D0     Z80 Clock Select: "00" = 3.54MHz; "01" = 7.08MHz; "10" = 14.16MHz; "11" = 21.25MHz


$34DF (13535): Various ZX-Badaloc control flags

Read / Write:
D7        Fastpage address space (see fastpage register): '0' = 0-$3FFF (standard); '1' = $C000-$FFFF (new in this FPGA version)
D6        Custom Registers Lock bit: '1' = Locked
D5        Context Switch inibith: '1' = no context switch takes place on $66 (NMI Vector) opcode fetch (see ZX-Badaloc documentation)
D4        Parallel Flash Write Enable (parallel flash is not used on this clone)
D3        Writing '1' Removes Context Switch. Always read as '0'
D2        Writing '1' Forces a Context Switch. Always read as '0'
D1        Further Z80 INT Frequency Doubler: '1' = Active (50 -> 100Hz or 100 -> 200Hz depending on D2 of $24DF register)
D0        Spectrum Type: '0' = 48K; '1' = 128K (Video Timings)

NOTE: The context switch is a special ZX-Badaloc feature which swaps-in a custom ROM when an NMI is being serviced (opcode fetch at address $66). This enables the RS-232 or sd-card snapshots and other special features.


$44DF (17631): Register Lock Disable, FPGA BootLoader Swap Out and Address $25 trap control

Writing $AA (170) resets both bit D6 of $34DF register (reactivating WR access to all Custom registers) and bit D5 of $7FFD register (reactivating WR access to all standard registers).

It also disables forever the VHDL-Coded Z80 bootloader rom from address space 0 - $3FFF and activates the standard memory (for operating mode).

Starting from V1.05, bit D0 has special meaning as it disables ('0') or enables ('1') the memory trap at address $25, the "rom services" vector (that allows any program (even a basic program) to access bootrom functions). This trap is now disabled by default to improve compatibility but can be enabled at any time by writing $AB (D0 SET) to the $44DF register.


$54DF (21727): FASTPAGE control register

Read / Write:
D7        Paged-in memory Write Enable: '1' = enabled. When 'RAM' type is paged, writing is always enabled
D6        Page-in enable: when '1', the selected 16K memory block will appear in address space 0-$3FFF (required by ResiDOS)
D5        RAM/ROM select: '0' = RAM is paged; '1' = ROM is paged. Only meaningful on the original zx-badaloc clone, see notes
D4:D0   Bank Select: one 16K bank out of 32 (512K) can be selected for paging by these 5 bits.

NOTE: As stated above, the FPGA version of the clone only rely on a pseudo-static RAM chip. Hence, this register controls the access to a 1MB memory space which can be paged-in, combining the original zx-badaloc 512K of RAM and 512K of ROM. In other words, the D5 bit is just one more bank select address bit (so 64 banks = 1MB can be accessed). The bootrom will copy rom images from the SPI flash onto the 'rom' banks (the upper half of the megabyte in the pseudo-static ram chip).

For compatibility reason, however, these consideration might be taken into account:
When the clone operates normally (D6 = '0' = fastpage is not enabled) the RAM acts as the main memory for both 'ROM' and 'RAM'. Up to 512K of 'ROM' can be accessed in the 0-$3FFF area by combining A18:A16 from $24DF (nonstandard) register and A15, A14 from $1FFD and $7FFD registers respectively (this allows 128K and +2 / +3 machines to run because rom selection works as on the original hardware). The HARD-CODED half of the 512K block in memory will be the UPPER ONE, so if the user wants to alter a ROM content on the fly by using the fastpage access, D5 should be '1'.

The other 512K space (the lower half of the megabyte) is used as RAM memory. The first 128K will be mapped as in the 128K spectrum and can be accessed using the RAM Bank bits in the standard $7FFD register, while the remainder is for general purpose and can only be accessed through fastpage register. ResiDOS by Garry Lancaster uses fastpaging to access the entire 512K RAM area and requires D5 to be low. If the user wants to alter a memory bank which will reflect to RAM through fastpaging, the D5 bit should be '0'.


$64DF (25823): RAM MSB address bank (was "HI-RES on the original zx-badaloc)

Read / Write:
D7:D3     Not used
D2:D0     Upper memory address select: Up to 8 blocks (1 megabyte each) can be selected by this register.

This is an MSB extension of the FASTPAGE register. When fastpaging is enabled, these additional 3 bits provide access to 8MB of RAM space.
NOTE: the ram chip is organyzed as 8Mx16 bit. In current implementation, only the lower nibble is used. This is why the total available memory is 8MB instead of 16MB. If a lower/uppler byte select will be implemented, then the $64DF register will have one more useful bit in order to address a 16MB space.

This is an MSB extension of the FASTPAGE register. When fastpaging is enabled, these additional 3 bits provide access to 8MB of RAM space.

$BF3B (48955): ULA+ Register Port

Write Only:
D7:D6     Color Register group select:
              when "00": Palette Group Select: the sub-group specifies one of 64 possible color registers, accessible via the Data Port.
              when "01": the ULA+ enable/disable bit can be written on D0 of the Data Port.
              when "11": (nonstandard): the color currently pointed by sub-group select can be read on port $FF3B (see below)
D5:D0     Color Register sub-group select: this is the 0-63 color number to be written / read.

$FF3B (65339): ULA+ Data Port

Read / Write when D7:D6  of the Register Port is "00", otherwise hidden.

The color format for read/write is:

D7:D5    Green intensity (8 levels)
D4:D2    Red intensity (8 levels)
D1:D0    Blue Intensity (4 levels)

When D7:D6  of the Register Port is "01":

D7:D1    Ignored
D0:        '0' disables the ULA+ mode; '1' enables ULA+ mode.

The available ULA+ 64 colors are splitted into four 16-colors banks. To select the bank to be displayed on a certain 8x8 cell on the screen, two bits from the zx-spectrum's screen attribute byte are used: Flash and Brightness. When they are both '00', bank 0 is selected. When Flash is '0' and Bright is '1', bank 1 is selected, and so on.

Then, the first eight colors in the selected bank are used for INK pixels and the remaining eight for PAPER pixels.

Example: setting the INK color n. 2 (which is normally RED on zx-spectrum) in the first bank of ULA+ color table:

OUT 48955, 2      this selects color n. 2 in the first color bank
OUT 65339, desired color byte in the format GGGRRRBB (from D7 to D0)

This color will be used for ink pixels when INK 2 is selected on a cell where Flash and Bright are both '0'.

$FFFD (65533): AY-3-8912 Sound Chip Address/Data register

Selects the AY-3-8912 register to be read or written

Read Data Register (currently selected)

$BFFD (49149): AY-3-8912 Sound Chip Data register

Write Data Register (currently selected)

Read Data Register (currently selected)

Memory Mapped Registers:

When D7:D4 in $1F register are equal to '1011' (Programmable Joystick Write Enable), 32 bytes of key-map data for the 16 joystick inputs can be written in memory at any 32 bytes boudary. Only A4:A0 are tested, so any memory write will also store data in the joystick registers. Hence, it is possible to write 'over' a ram array (to keep a copy of stored data) or write on a 'ROM' area, to avoid corrupting ram content.

Each joystick input has a pair of data bytes:

Byte 0    ZX-Spectrum keyboard eight COLUMNS (on Z80 Address lines): D7:D0 = A15:A8. A bit SET means address column PRESSED
Byte 1    ZX-Spectrum keyboard five ROWS, readable on $FE port: D4:D0 = D4:D0. A bit SET means key PRESSED with the COLUMN byte

First pair of bytes stores keymap data for Joystick Input 1; second pair is for joy input 2 and so on.

It is imperative to perform joystick programming with interrupts disabled and in pure assembly, to avoid any undesired write operation which would certainly corrupt joystick setting.