Difference between revisions of "GABE"

From C256 Foenix Wiki
Jump to navigation Jump to search
(GABE Random Number Generator Status and Control ($AF:E886))
 
(20 intermediate revisions by 4 users not shown)
Line 79: Line 79:
 
| $00:0128 || $00:012B || ADDER32_R || Result of 32-bit addition (32-bits)
 
| $00:0128 || $00:012B || ADDER32_R || Result of 32-bit addition (32-bits)
 
|}
 
|}
 +
 +
=== GABE Keyboard Ports ===
 +
 +
{| class="wikitable"
 +
|-
 +
! Address !! R/W !! Name !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 !! Description
 +
|-
 +
| $AF:1060 || X || KBD_DATA || D7 || D6 || D5 || D4 || D3 || D2 || D1 || D0 || Keyboard data port
 +
|-
 +
| $AF:1061 || X || PORTB || D7 || D6 || D5 || D4 || D3 || D2 || D1 || D0
 +
|-
 +
| $AF:1064 || R || KBD_STATUS || PAR_E || RX_TO || TX_TO || KBD_INH || CMD || SYS || IN_F || OUT_F || Keyboard Status
 +
|-
 +
| $AF:1064 || W || KBD_CMD || CMD7 || CMD6 || CMD5 || CMD4 || CMD3 || CMD2 || CMD1 || CMD0 || Keyboard Command
 +
|}
 +
 +
==== GABE Keyboard Status Flags ====
 +
 +
;PAR_E
 +
:Parity error
 +
 +
;RX_TO
 +
:Time out reading data
 +
 +
;TX_TO
 +
:Time out transmitting data
 +
 +
;CMD
 +
:If 0, then data written to input buffer is data for PS/2 device. If 1, then data written to input buffer is data for PS/2 controller command
 +
 +
;SYS
 +
:Flag to the firmware that the keyboard has past POST
 +
 +
;IN_F
 +
:Input buffer is full
 +
 +
;OUT_F
 +
:Output buffer is full
  
 
=== GABE Floating Point Math Coprocessor ($AF:E200 – AFE20F) ===
 
=== GABE Floating Point Math Coprocessor ($AF:E200 – AFE20F) ===
Line 203: Line 241:
 
| $10 || Division by 0.0 was attempted
 
| $10 || Division by 0.0 was attempted
 
|}
 
|}
 +
 +
=== GABE Timer Registers ($00:0160 – $00:017F) ===
 +
 +
* Timer0 and Timer1 count system clock transitions (14318180Hz)
 +
* Timer2 is clocked from VICKY's Start-of-Frame output (50hz).
 +
* To create interrupts at a set interval for timer 0 and 1 divide the clock frequency by the desired interval. So if one wishes 4 interrupts every second use 14318180 divided by 4.
 +
 +
{| class="wikitable"
 +
|-
 +
!Start Address
 +
!End Address
 +
!Register Name
 +
!Additional Info
 +
|-
 +
!colspan="4" style="text-align:left;" | Timer 0
 +
|-
 +
| $00:0160 || || TIMER0_CTRL_REG || Timer 0 control register
 +
|-
 +
| $00:0161 || || TIMER0_CHARGE_L || Timer 0 precharge register lower byte, loaded when counting down
 +
|-
 +
| $00:0162 || || TIMER0_CHARGE_M || Timer 0 precharge register middle byte, loaded when counting down
 +
|-
 +
| $00:0163 || || TIMER0_CHARGE_H || Timer 0 precharge register high byte, loaded when counting down
 +
|-
 +
| $00:0164 || || TIMER0_CMP_REG || Timer 0 compare register
 +
|-
 +
| $00:0165 || || TIMER0_CMP_L || Timer 0 compare register lower byte, loaded when counting up
 +
|-
 +
| $00:0166 || || TIMER0_CMP_M || Timer 0 compare register middle byte, loaded when counting up
 +
|-
 +
| $00:0167 || || TIMER0_CMP_H || Timer 0 compare register high byte, loaded when counting up
 +
|-
 +
!colspan="4" style="text-align:left;" | Timer 1
 +
|-
 +
| $00:0168 || || TIMER1_CTRL_REG || Timer 1 control register
 +
|-
 +
| $00:0169 || || TIMER1_CHARGE_L || Timer 1 precharge register lower byte, loaded when counting down
 +
|-
 +
| $00:016A || || TIMER1_CHARGE_M || Timer 1 precharge register middle byte, loaded when counting down
 +
|-
 +
| $00:016B || || TIMER1_CHARGE_H || Timer 1 precharge register high byte, loaded when counting down
 +
|-
 +
| $00:016C || || TIMER1_CMP_REG || Timer 1 compare register
 +
|-
 +
| $00:016D || || TIMER1_CMP_L || Timer 1 compare register lower byte, loaded when counting up
 +
|-
 +
| $00:016E || || TIMER1_CMP_M || Timer 1 compare register middle byte, loaded when counting up
 +
|-
 +
| $00:016F || || TIMER1_CMP_H || Timer 1 compare register high byte, loaded when counting up
 +
|-
 +
!colspan="4" style="text-align:left;" | Timer 2
 +
|-
 +
| $00:0170 || || TIMER2_CTRL_REG || Timer 2 control register
 +
|-
 +
| $00:0171 || || TIMER2_CHARGE_L || Timer 2 precharge register lower byte, loaded when counting down
 +
|-
 +
| $00:0172 || || TIMER2_CHARGE_M || Timer 2 precharge register middle byte, loaded when counting down
 +
|-
 +
| $00:0173 || || TIMER2_CHARGE_H || Timer 2 precharge register high byte, loaded when counting down
 +
|-
 +
| $00:0174 || || TIMER2_CMP_REG || Timer 2 compare register
 +
|-
 +
| $00:0175 || || TIMER2_CMP_L || Timer 2 compare register lower byte, loaded when counting up
 +
|-
 +
| $00:0176 || || TIMER2_CMP_M || Timer 2 compare register middle byte, loaded when counting up
 +
|-
 +
| $00:0177 || || TIMER2_CMP_H || Timer 2 compare register high byte, loaded when counting up
 +
|-
 +
|}
 +
 +
{| class="wikitable"
 +
|-
 +
!Register
 +
!Value
 +
!Name
 +
!Additional Info
 +
|-
 +
| TIMER<X>_CTRL_REG || 0x01 || TMR_EN || Enabled the timer
 +
|-
 +
| TIMER<X>_CTRL_REG || 0x02 || TMR_SCLR || Set when counting up
 +
|-
 +
| TIMER<X>_CTRL_REG || 0x04 || TMR_SLOAD|| Set when counting down
 +
|-
 +
| TIMER<X>_CTRL_REG || 0x08 || TMR_UPDWN|| Set when counting up (1 = Up, 0 = Down)
 +
|-
 +
| TIMER<X>_CMP_REG || 0x01 || TMR_CMP_RECLR|| Set to one for it to cycle when Counting up
 +
|-
 +
| TIMER<X>_CMP_REG || 0x02 || TMR_CMP_RELOAD|| Set to one for it to reload when Counting Down
 +
|-
 +
|}
 +
 +
==== C pseudo code for counting up ====
 +
TIMER1_CHARGE_L = 0x00;<br />
 +
TIMER1_CHARGE_M = 0x00;<br />
 +
TIMER1_CHARGE_H = 0x00;<br />
 +
TIMER1_CMP_L = clockValue & 0xFF;<br />
 +
TIMER1_CMP_M = (clockValue >> 8) & 0xFF;<br />
 +
TIMER1_CMP_H = (clockValue >> 16) & 0xFF;<br />
 +
<br />
 +
TIMER1_CMP_REG = TMR_CMP_RECLR;<br />
 +
TIMER1_CTRL_REG = TMR_EN | TMR_UPDWN | TMR_SCLR;<br />
 +
 +
==== C pseudo code for counting down ====
 +
TIMER1_CHARGE_L = (clockValue >> 0) & 0xFF;<br />
 +
TIMER1_CHARGE_M = (clockValue >> 8) & 0xFF;<br />
 +
TIMER1_CHARGE_H = (clockValue >> 16) & 0xFF;<br />
 +
TIMER1_CMP_L = 0x00;<br />
 +
TIMER1_CMP_M = 0x00;<br />
 +
TIMER1_CMP_H = 0x00;<br />
 +
<br />
 +
TIMER1_CMP_REG = TMR_CMP_RELOAD;<br />
 +
TIMER1_CTRL_REG = TMR_EN | TMR_SLOAD;<br />
 +
<br />
  
 
=== GABE Interrupt Control Registers ($00:0140 &ndash; $00:014B) ===
 
=== GABE Interrupt Control Registers ($00:0140 &ndash; $00:014B) ===
Line 331: Line 482:
 
| 3 || $80 || FNX3_INT07_TBD || TBD
 
| 3 || $80 || FNX3_INT07_TBD || TBD
 
|}
 
|}
 
=== GABE SN76489 Interface ($AF:AFF100) ===
 
 
GABE provides the interface to the SN76489 sound chip on the Foenix FMX. This chip uses only a single write-only address: $AF:AFF100. The SN76489 has four voices: three tone generators, and one noise generator. Each voice has a frequency and an attenuation setting. For full details on how to use the SN76489 chip, please consult the official documentation, but here is a brief summary.
 
 
==== Playing a Tone ====
 
 
To change the frequency of a tone generator, two bytes are sent to the chip in order:
 
 
{| class="wikitable"
 
|-
 
! !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0
 
|-
 
| First || 1 ||colspan="2"| Channel || 0 || F3 || F2 || F1 || F0
 
|-
 
| Second || 0 || x || F9 || F8 || F7 || F6 || F5 || F4
 
|}
 
 
The actual frequency played depends on the clock driving the chip: freq = N / (32 * F), where N = 3575800 (for the FMX), and F is the 10-bit number sent to the chip. The "Channel" number above must be 0, 1, or 3.
 
 
==== Play a Noise ====
 
 
To play a noise, only a single byte is written to the chip:
 
 
{| class="wikitable"
 
|-
 
! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0
 
|-
 
| 1 || 1 || 1 || 0 || x || NF|| NF1 || NF0
 
|}
 
 
The NF bit controls the type of noise generated, and the NF bits control the base frequency of the noise.
 
 
==== Setting the Volume ====
 
 
The volumes of each of the four voices can be set independently. To set the volume of a single voice, a single byte is sent to the chip:
 
 
{| class="wikitable"
 
|-
 
! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0
 
|-
 
| 1 ||colspan="2"| Channel || 1 || A3 || A2 || A1 || A0
 
|}
 
 
The "Channel" bits are the number of the voice to change: 0, 1, 2, or 3. The four bit value A is the level of attenuation, where 0 is fully on, and 15 is fully off.
 
  
 
=== GABE SID Emulation ($AF:E400 &ndash; $AF:E41F) ===
 
=== GABE SID Emulation ($AF:E400 &ndash; $AF:E41F) ===
Line 440: Line 546:
 
|}
 
|}
  
=== GABE Joystick Registers ($AF:E800 &ndash; $AF:E804) ===
+
=== GABE Trinity Joystick/Gamepad and DIP Switch Registers ($AF:E800 &ndash; $AF:E80E) ===
 +
 
 +
GABE provides access to the Trinity chip, which provides access to the joystick ports and the DIP switches.
 +
The joystick ports can be used in two modes: default mode, where they work like Atari style joysticks, and NES mode
 +
where they can interface with either NES or SNES game pads.
 +
 
 +
[[File:Joystick Ports.jpg|800px|FMX Joystick Ports Configuration]]
 +
 
 +
{| class="wikitable"
 +
|-
 +
! Address !! Name !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 !! Description
 +
|-
 +
| $AF:E800 || JOYSTICK0 || BUTTON2 || BUTTON1 || 0 || BUTTON0 || RIGHT || LEFT || DOWN || UP || Left-most joystick port
 +
|-
 +
| $AF:E801 || JOYSTICK1 || BUTTON2 || BUTTON1 || 0 || BUTTON0 || RIGHT || LEFT || DOWN || UP || Next to joystick port 0
 +
|-
 +
| $AF:E802 || JOYSTICK2 || 0 || 0 || 0 || BUTTON0 || RIGHT || LEFT || DOWN || UP || Next to joystick port 1
 +
|-
 +
| $AF:E803 || JOYSTICK3 || 0 || 0 || 0 || BUTTON0 || RIGHT || LEFT || DOWN || UP || Right-most joystick port
 +
|-
 +
| $AF:E804 || JOYSTICK_MODE || NES_TRIG || 0 || 0 || 0 || NES_DONE || NES_JOY || NES_EN1 || NES_EN0 || Control SNES compatibility
 +
|-
 +
| $AF:E805 || REVOFPCB_C ||colspan="8"| "C" || Board identification code byte (expect "C")
 +
|-
 +
| $AF:E806 || REVOFPCB_4 ||colspan="8"| "4" || Board identification code byte (expect "4")
 +
|-
 +
| $AF:E807 || REVOFPCB_A ||colspan="8"| "A" || Board identification code byte (expect "A")
 +
|-
 +
| $AF:E808 || NES_SNES0_DAT_LO ||colspan="8"| see below || Port 0: NES or SNES gamepad data (low byte)
 +
|-
 +
| $AF:E809 || SNES0_DAT_HI ||colspan="8"| see below|| Port 0: NES or SNES gamepad data (highbyte)
 +
|-
 +
| $AF:E80A || NES_SNES1_DAT_LO ||colspan="8"| see below || Port 1: NES or SNES gamepad data (low byte)
 +
|-
 +
| $AF:E80B || SNES1_DAT_HI ||colspan="8"| see below || Port 1: NES or SNES gamepad data (highbyte)
 +
|-
 +
| $AF:E80C || CFP9301_REV ||colspan="8"| revision || Trinity revision code
 +
|-
 +
| $AF:E80D || DIP_USER ||colspan="8"| user || Switches 3 through 5. User defined meaning.
 +
|-
 +
| $AF:E80E || DIP_BOOTMODE || HD_INSTALLED || 0 || 0 || 0 || 0 || 0 || BM1 || BM0 || Boot control switches.
 +
|}
 +
 
 +
{| class="wikitable"
 +
|+ SNES mode
 +
|-
 +
! Address !! Name !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 !! Description
 +
|-
 +
| $AF:E808 || NES_SNES0_DAT_LO || B || Y || SELECT || START || UP || DOWN || LEFT || RIGHT || NES data if NES_JOY = 1 and NES_EN0 = 1
 +
|-
 +
| $AF:E809 || SNES0_DAT_HI || 0 || 0 || 0 || 0 || A || X || L || R || NES data if NES_JOY = 1 and NES_EN0 = 1)
 +
|-
 +
| $AF:E80A || NES_SNES1_DAT_LO || B || Y || SELECT || START || UP || DOWN || LEFT || RIGHT || NES data if NES_JOY = 1 and NES_EN1 = 1
 +
|-
 +
| $AF:E80B || SNES1_DAT_HI || 0 || 0 || 0 || 0 || A || X || L || R || NES data if NES_JOY = 1 and NES_EN1 = 1
 +
|}
 +
 
 +
{| class="wikitable"
 +
|+ NES mode
 +
|-
 +
! Address !! Name !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 !! Description
 +
|-
 +
| $AF:E808 || NES_SNES0_DAT_LO || A || B || SELECT || START || UP || DOWN || LEFT || RIGHT || NES data if NES_JOY = 0 and NES_EN0 = 1
 +
|-
 +
| $AF:E809 || SNES0_DAT_HI ||colspan="2"| N/A || Unused if NES_EN0 = 0
 +
|-
 +
| $AF:E80A || NES_SNES1_DAT_LO || A || B || SELECT || START || UP || DOWN || LEFT || RIGHT || NES data if NES_JOY = 0 and NES_EN1 = 1
 +
|-
 +
| $AF:E80B || SNES1_DAT_HI ||colspan="2"| N/A || Unused in NES mode
 +
|}
 +
 
 +
==== NES Control Bits ====
 +
 
 +
;NES_TRIG
 +
:Set to one to trigger the serializer to read the gamepad.
 +
 
 +
;NES_DONE
 +
:Poll to see if the serializer has finished reading the gamepad.
 +
 
 +
;NES_JOY
 +
:If 1, sets SNES compatibility. If 0, sets NES compatibility.
 +
 
 +
;NES_EN1
 +
:If 1, enables NES/SNES compatibility on port 1. If 0, joysticks work like Atari joysticks
 +
 
 +
;NES_EN0
 +
:If 1, enables NES/SNES compatibility on port 0. If 0, joysticks work like Atari joysticks
 +
 
 +
==== Boot Mode ====
 +
 
 +
Switches 0 and 1 allow you to select the boot mode for the FMX. Note that the switches are active low:
 +
a switch in the "On" position registers as a 0.
 +
 
 +
{| class="wikitable"
 +
|-
 +
! BM1 || BM0 !! Boot Mode
 +
|-
 +
| 0 || 0 || Boots from the IDE drive DIP switch 8 must be set (HD_INSTALLED).
 +
|-
 +
| 0 || 1 || Boots from the SD card
 +
|-
 +
| 1 || 0 || Boots from the floppy drive
 +
|-
 +
| 1 || 1 || Boots to BASIC
 +
|}
  
 
=== GABE IDE Registers ($AF:E830 &ndash; $AF:E839) ===
 
=== GABE IDE Registers ($AF:E830 &ndash; $AF:E839) ===
Line 459: Line 669:
 
| $AF:E834 || W || IDE_CLDR_LO || C7 || C6 || C5 || C4 || C3 || C2 || C1 || C0 || Cylinder Number (low)
 
| $AF:E834 || W || IDE_CLDR_LO || C7 || C6 || C5 || C4 || C3 || C2 || C1 || C0 || Cylinder Number (low)
 
|-
 
|-
| $AF:E834 || W || IDE_CLDR_HI || C15 || C14 || C13 || C12 || C11 || C10 || C9 || C8 || Cylinder Number (high)
+
| $AF:E835 || W || IDE_CLDR_HI || C15 || C14 || C13 || C12 || C11 || C10 || C9 || C8 || Cylinder Number (high)
 
|-
 
|-
| $AF:E835 || W || IDE_HEAD || 1 || 0 || 1 || M/S || H3 || H2 || H1 || H0 || Head number and Master (0) / Slave(1) device
+
| $AF:E836 || W || IDE_HEAD || 1 || 0 || 1 || M/S || H3 || H2 || H1 || H0 || Head number and Master (0) / Slave(1) device
 
|-
 
|-
| $AF:E836 || W || IDE_CMD_STAT || CMD7 || CMD6 || CMD5 || CMD4 || CMD3 || CMD2 || CMD1 || CMD0 || Send a command to the drive
+
| $AF:E837 || W || IDE_CMD_STAT || CMD7 || CMD6 || CMD5 || CMD4 || CMD3 || CMD2 || CMD1 || CMD0 || Send a command to the drive
 
|-
 
|-
| $AF:E836 || R || IDE_CMD_STAT || BSY || DRDY || DF || DSC || DRQ || CORR || IDX || ERR || Get the status of device. Reading this will clear the Interrupt Registers
+
| $AF:E837 || R || IDE_CMD_STAT || BSY || DRDY || DF || DSC || DRQ || CORR || IDX || ERR || Get the status of device. Reading this will clear the Interrupt Registers
 
|-
 
|-
| $AF:E837 || X || IDE_DATA_LO || D7 || D6 || D5 || D4 || D3 || D2 || D1 || D0 || 16-bit data transfer registers (low byte)
+
| $AF:E838 || X || IDE_DATA_LO || D7 || D6 || D5 || D4 || D3 || D2 || D1 || D0 || 16-bit data transfer registers (low byte)
 
|-
 
|-
| $AF:E838 || X || IDE_DATA_HI || D15 || D14 || D13 || D12 || D11 || D10 || D9 || D8 || 16-bit data transfer registers (high byte)
+
| $AF:E839 || X || IDE_DATA_HI || D15 || D14 || D13 || D12 || D11 || D10 || D9 || D8 || 16-bit data transfer registers (high byte)
 
|}
 
|}
  
Line 568: Line 778:
 
; GABE_CTRL_WRM_RST  
 
; GABE_CTRL_WRM_RST  
 
: Triggers a warm reset of the board (GABE_RST_AUTH0 must be set to $AD and GABE_RST_AUTH1 to $DE in order to trigger the reset).
 
: Triggers a warm reset of the board (GABE_RST_AUTH0 must be set to $AD and GABE_RST_AUTH1 to $DE in order to trigger the reset).
 +
 +
==== GABE Random Number Generator Status and Control ($AF:E886) ====
 +
 +
{| class="wikitable"
 +
|-
 +
! R/W !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0
 +
|-
 +
| R || GABE_RNG_LFSR_DONE || || || || || || ||
 +
|-
 +
| W || || || || || || || GABE_RNG_CTRL_DV || GABE_RNG_CTRL_EN
 +
|}
 +
 +
; GABE_RNG_LFSR_DONE
 +
: Indicates that output = seed database.
 +
 +
; GABE_RNG_CTRL_DV
 +
: After setting the seed value, toggle this bit for the seed be registered.
 +
 +
; GABE_RNG_CTRL_EN
 +
: Enable the LFSR block.
  
 
==== GABE System Status Register ($AF:E887) ====
 
==== GABE System Status Register ($AF:E887) ====
Line 599: Line 829:
 
| 1 || 1 || Reserved
 
| 1 || 1 || Reserved
 
|}
 
|}
 +
 +
=== GABE SDC Interface ($AF:EA00) ===
 +
 +
GABE provides block-level access to the SPI interface of the SD card. Currently, there are other registers in that provide access to the SD card through a CH376S chip, but that interface should be considered deprecated in favor of this interface. Most FMX boards do not have the CH376S chip, leaving its position unpopulated on the board.
 +
 +
{| class="wikitable"
 +
! Address !! R/W !! Name !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 !! Description
 +
|-
 +
| $AF:EA00 || R || SDC_VERSION_REG || colspan="8"|version || Should read 12
 +
|-
 +
| $AF:EA01 || W || SDC_CONTROL_REG || x || x || x || x || x || x || x || RESET || Control register: RESET = 1 resets the core logic. Self clearing.
 +
|-
 +
| $AF:EA02 || W || SDC_TRANS_TYPE_REG ||x || x || x || x || x || x || colspan="2"|TRANS || Sets the type of transaction: 0 = Direct, 1 = INIT SD, 2 = Read Block, 3 = Write block
 +
|-
 +
| $AF:EA03 || W || SDC_TRANS_CONTROL_REG || x || x || x || x || x || x || x || START || Start transaction bit.
 +
|-
 +
| $AF:EA04 || R || SDC_TRANS_STATUS_REG || x || x || x || x || x || x || x || BUSY || Status of the transaction. BUSY = 1 indicates a transaction is in progress.
 +
|-
 +
| $AF:EA05 || R || SDC_TRANS_ERROR_REG || x || x || colspan="2"|WR_ERR || colspan="2"|RD_ERR || colspan="2"|TRANS_ERR || Error conditions for read, write, and transaction.
 +
|-
 +
| $AF:EA06 || RW || SDC_DIRECT_ACCESS_REG || colspan="8"|data || SPI Direct Read and Write - Set DATA before initiating direct Access Transaction
 +
|-
 +
| $AF:EA07 || W || SDC_SD_ADDR_7_0_REG || A7 || A6 || A5 || A4 || A3 || A2 || A1 || A0 || Set the ADDR before a block read or block write.
 +
|-
 +
| $AF:EA08 || W || SDC_SD_ADDR_15_8_REG || A15 || A14 || A13 || A12 || A11 || A10 || A9 || A8 || ADDR[8:0] always should be 0, since each block is 512 bytes
 +
|-
 +
| $AF:EA09 || W || SDC_SD_ADDR_23_16_REG || A23 || A22 || A21 || A20 || A19 || A18 || A7 || A6
 +
|-
 +
| $AF:EA0A || W || SDC_SD_ADDR_31_24_REG || A32 || A31 || A30 || A29 || A28 || A26 || A25 || A24
 +
|-
 +
| $AF:EA0B || W || SDC_SPI_CLK_DEL_REG || colspan="8"|clock delay
 +
|-
 +
| $AF:EA10 || R || SDC_RX_FIFO_DATA_REG || colspan="8"|data || Data from block read
 +
|-
 +
| $AF:EA12 || R || SDC_RX_FIFO_DATA_CNT_HI || CNT15 || CNT14 || CNT13 || CNT12 || CNT11 || CNT10 || CNT9 || CNT8 || How many bytes in the read FIFO (HI)
 +
|-
 +
| $AF:EA13 || R || SDC_RX_FIFO_DATA_CNT_LO || CNT7 || CNT6 || CNT5 || CNT4 || CNT3 || CNT2 || CNT1 || CNT0 || How many bytes in the read FIFO (LOW)
 +
|-
 +
| $AF:EA14 || W || SDC_RX_FIFO_CTRL_REG || x || x || x || x || x || x || x || CLEAR || CLEAR = 1, clear the read FIFO. Bit is self-clearing.
 +
|-
 +
| $AF:EA20 || W || SDC_TX_FIFO_DATA_REG || colspan="8"|data || Write data block here.
 +
|-
 +
| $AF:EA21 || W || SDC_TX_FIFO_CTRL_REG || x || x || x || x || x || x || x || CLEAR || CLEAR = 1, clear the write FIFO. Bit is self-clearing.
 +
|}
 +
 +
==== GABE: SDC_TRANS_ERROR_REG ($AF:EA05) ====
 +
 +
Lists any error conditions on reads, writes, or general transactions:
 +
 +
{| class="wikitable"
 +
|+ TRANS_ERR codes [1:0]
 +
! Code !! Condition
 +
|-
 +
| 00 || No INIT error
 +
|-
 +
| 01 || INIT CMD0 error
 +
|-
 +
| 10 || INIT CMD1 error
 +
|}
 +
 +
{| class="wikitable"
 +
|+ RD_ERR codes [3:2]
 +
! Code !! Condition
 +
|-
 +
| 00 || No read error
 +
|-
 +
| 01 || CMD error
 +
|-
 +
| 10 || Token error
 +
|}
 +
 +
{| class="wikitable"
 +
|+ WR_ERR codes [5:4]
 +
! Code !! Condition
 +
|-
 +
| 00 || No write error
 +
|-
 +
| 01 || CMD error
 +
|-
 +
| 10 || Data error
 +
|-
 +
| 11 || Busy error
 +
|}
 +
 +
=== GABE SN76489 Interface ($AF:F100) ===
 +
 +
GABE provides the interface to the SN76489 sound chip on the Foenix FMX. This chip uses only a single write-only address: $AF:F100. The SN76489 has four voices: three tone generators, and one noise generator. Each voice has a frequency and an attenuation setting. For full details on how to use the SN76489 chip, please consult the official documentation, but here is a brief summary.
 +
 +
==== Playing a Tone ====
 +
 +
To change the frequency of a tone generator, two bytes are sent to the chip in order:
 +
 +
{| class="wikitable"
 +
|-
 +
! !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0
 +
|-
 +
| First || 1 ||colspan="2"| Channel || 0 || F3 || F2 || F1 || F0
 +
|-
 +
| Second || 0 || x || F9 || F8 || F7 || F6 || F5 || F4
 +
|}
 +
 +
The actual frequency played depends on the clock driving the chip: freq = N / (32 * F), where N = 3575800 (for the FMX), and F is the 10-bit number sent to the chip. The "Channel" number above must be 0, 1, or 3.
 +
 +
==== Play a Noise ====
 +
 +
To play a noise, only a single byte is written to the chip:
 +
 +
{| class="wikitable"
 +
|-
 +
! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0
 +
|-
 +
| 1 || 1 || 1 || 0 || x || NF|| NF1 || NF0
 +
|}
 +
 +
The NF bit controls the type of noise generated, and the NF bits control the base frequency of the noise.
 +
 +
==== Setting the Volume ====
 +
 +
The volumes of each of the four voices can be set independently. To set the volume of a single voice, a single byte is sent to the chip:
 +
 +
{| class="wikitable"
 +
|-
 +
! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0
 +
|-
 +
| 1 ||colspan="2"| Channel || 1 || A3 || A2 || A1 || A0
 +
|}
 +
 +
The "Channel" bits are the number of the voice to change: 0, 1, 2, or 3. The four bit value A is the level of attenuation, where 0 is fully on, and 15 is fully off.

Latest revision as of 07:16, 2 August 2021

GABE -- The System Control, I/O, and Sound Chip

"GABE" is the name of the system controller chip of the C256 Foenix RevC board, and performs several functions including system bus management, math coprocessing, I/O, and sound. The RevB version of the C256 had two chips "Gavin" and "Beatrix" that were combined into a single master chip called "GABE".

GABE has several key functions:

  • System start up
  • Debug interface control and bus mastering
  • I/O interfacing
  • Math coprocessor
  • Sound chip interfacing and emulation

GABE System Startup Process

When the C256 Foenix powers up, GABE takes control and coordinates the startup process. It first copies the flash data out of flash and into system RAM, starting at bank $38 on the FMX ($18 on the RevB). GABE also copies the first bank of flash data down to bank $00 of system RAM before control is given to the CPU and the CPU's RESET is triggered. This process ensures that the kernel, hardware vectors, and interrupt handlers are all in place prior to the CPU taking control and resetting the first time on power up.

The mapping of flash memory locations to system RAM is thus:

Flash Range RAM Range
$F8:0000...$FF:FFFF $38:0000...$3F:FFFF
$F8:0000...$F8:FFFF $00:0000...$00:FFFF

GABE Integer Math Coprocessor ($00:0100 – $00:012B)

GABE provides an easy to use integer math coprocessor in the form of two 16-multipliers (one signed, one unsigned), two 16-bit dividers (one signed, one unsigned), and a single 32-bit adder. Using the coprocessor is straight-forward: the operands are written to the correct registers, and the result is read from the output registers. The operations are all performed within a single CPU clock cycle, so no waiting is required.

Start Address End Address Register Name Additional Info
$00:0100 $00:0101 UNSIGNED_MULT_A A operand for unsigned multiplication (16-bit)
$00:0102 $00:0103 UNSIGNED_MULT_B B operand for unsigned multiplication (16-bit)
$00:0104 $00:0107 UNSIGNED_MULT_RESULT Result for unsigned multiplication (32-bit)
$00:0108 $00:0109 SIGNED_MULT_A A operand for signed multiplication (16-bit)
$00:010A $00:010B SIGNED_MULT_B B operand for signed multiplication (16-bit)
$00:010C $00:010F SIGNED_MULT_RESULT Result for signed multiplication (32-bit)
$00:0110 $00:0111 UNSIGNED_DIV_DEM Denominator for unsigned division (16-bit)
$00:0112 $00:0113 UNSIGNED_DIV_NUM Numerator for unsigned division (16-bit)
$00:0114 $00:0115 UNSIGNED_DIV_QUO Quotient for unsigned division (16-bit)
$00:0116 $00:0117 UNSIGNED_DIV_REM Remainder for unsigned division (16-bit)
$00:0118 $00:0119 SIGNED_DIV_DEM Denominator for signed division (16-bit)
$00:011A $00:011B SIGNED_DIV_NUM Numerator for signed division (16-bit)
$00:011C $00:011D SIGNED_DIV_QUO Quotient for signed division (16-bit)
$00:011E $00:011F SIGNED_DIV_REM Remainder for signed division (16-bit)
$00:0120 $00:0123 ADDER32_A A operand for 32-bit addition (32-bits)
$00:0124 $00:0127 ADDER32_B B operand for 32-bit addition (32-bits)
$00:0128 $00:012B ADDER32_R Result of 32-bit addition (32-bits)

GABE Keyboard Ports

Address R/W Name 7 6 5 4 3 2 1 0 Description
$AF:1060 X KBD_DATA D7 D6 D5 D4 D3 D2 D1 D0 Keyboard data port
$AF:1061 X PORTB D7 D6 D5 D4 D3 D2 D1 D0
$AF:1064 R KBD_STATUS PAR_E RX_TO TX_TO KBD_INH CMD SYS IN_F OUT_F Keyboard Status
$AF:1064 W KBD_CMD CMD7 CMD6 CMD5 CMD4 CMD3 CMD2 CMD1 CMD0 Keyboard Command

GABE Keyboard Status Flags

PAR_E
Parity error
RX_TO
Time out reading data
TX_TO
Time out transmitting data
CMD
If 0, then data written to input buffer is data for PS/2 device. If 1, then data written to input buffer is data for PS/2 controller command
SYS
Flag to the firmware that the keyboard has past POST
IN_F
Input buffer is full
OUT_F
Output buffer is full

GABE Floating Point Math Coprocessor ($AF:E200 – AFE20F)

GABE provides a floating point math coprocessor, providing addition, subtraction, multiplication, and division for 32-bit floating point numbers. Additionally, the coprocessor allows for conversion between a fixed point format and 32-bit IEEE 754 floating point format. The floating point coprocessor is a little more complex than the integer coprocessor, with each math unit having input multiplexers that control where the inputs come from, and with the output registers having their own multiplexer. Finally, the output of the multiplier and divider units can be used directly as inputs to the adder/subtracter.

FMX Floating Point Math Coprocessor

Start Address End Address Register Name Additional Information
$AF:E200 FP_MATH_CTRL0 Controls input multiplexers, Adder function, and Adder multiplexers
$AF:E201 FP_MATH_CTRL1 Controls output multiplexers
$AF:E202 FP_MATH_CTRL2 Unused: reserved
$AF:E203 FP_MATH_CTRL3 Unused: reserved
$AF:E204 FP_MATH_MULT_STAT Status of the multiplier
$AF:E205 FP_MATH_DIV_STAT Status of the divider
$AF:E206 FP_MATH_ADD_STAT Status of the adder/subtracter
$AF:E207 FP_MATH_CONV_STAT Status of the converters
$AF:E208 $AF:E20B FP_MATH_INPUT0 Input 0 (write only)
$AF:E20C $AF:E20F FP_MATH_INPUT1 Input 1 (write only)
$AF:E208 $AF:E20B FP_MATH_OUTPUT_FP Result in IEEE 754 format (read only)
$AF:E20C $AF:E20F FP_MATH_OUTPUT_FIXED Result in 20.12 fixed point format (read only)

GABE Register FP_MATH_CTRL0 ($AF:E200)

7 6 5 4 3 2 1 0
FP_MATH_CTRL0_ADD_IN1_MUX FP_MATH_CTRL0_ADD_IN0_MUX FP_MATH_CTRL0_ADD_SUB FP_MATH_CTRL0_INPUT1_MUX FP_MATH_CTRL0_INPUT0_MUX
FP_MATH_CTRL0_INPUT0_MUX
If 0, Input0 is assumed to be in IEEE format. If 1, it is assumed to be in 20.12 fixed point format and will be converted to IEEE.
FP_MATH_CTRL0_INPUT1_MUX
If 0, Input1 is assumed to be in IEEE format. If 1, it is assumed to be in 20.12 fixed point format and will be converted to IEEE.
FP_MATH_CTRL0_ADD_SUB
If 0, the adder will perform a subtract operation. If 1, it will perform an addition.
FP_MATH_CTRL0_ADD_IN0_MUX
Selects the source for input 0 of the adder unit.
FP_MATH_CTRL0_ADD_IN1_MUX
Selects the source for input 1 of the adder unit.
Adder Source Selector Codes
Control Bits Input
00 Input 0
01 Input 1
10 Output of the multiplier
11 Output of the divider

GABE Register FP_MATH_CTRL1 ($AF:E201)

7 6 5 4 3 2 1 0
FP_MATH_CTRL1_OUTPUT_MUX0
FP_MATH_CTRL1_OUTPUT_MUX0
Selects which functional block provides the output result:
Result Source Selector Codes
Control Bits Output Source
00 Result of multiplier is the routed to the output registers
01 Result of divider is the routed to the output registers
10 Result of adder is the routed to the output registers
11 1.0 is routed to the output registers

GABE Registers: FP Status Registers ($AF:E204 – $AF:E207)

These registers contain the status flags for the various functional units. The status flags are all roughly similar across the four units:

Status Codes
Code Meaning
$01 Result was not a number (NAN)
$02 Overflow
$04 Underflow
$08 Result was 0.0
$10 Division by 0.0 was attempted

GABE Timer Registers ($00:0160 – $00:017F)

  • Timer0 and Timer1 count system clock transitions (14318180Hz)
  • Timer2 is clocked from VICKY's Start-of-Frame output (50hz).
  • To create interrupts at a set interval for timer 0 and 1 divide the clock frequency by the desired interval. So if one wishes 4 interrupts every second use 14318180 divided by 4.
Start Address End Address Register Name Additional Info
Timer 0
$00:0160 TIMER0_CTRL_REG Timer 0 control register
$00:0161 TIMER0_CHARGE_L Timer 0 precharge register lower byte, loaded when counting down
$00:0162 TIMER0_CHARGE_M Timer 0 precharge register middle byte, loaded when counting down
$00:0163 TIMER0_CHARGE_H Timer 0 precharge register high byte, loaded when counting down
$00:0164 TIMER0_CMP_REG Timer 0 compare register
$00:0165 TIMER0_CMP_L Timer 0 compare register lower byte, loaded when counting up
$00:0166 TIMER0_CMP_M Timer 0 compare register middle byte, loaded when counting up
$00:0167 TIMER0_CMP_H Timer 0 compare register high byte, loaded when counting up
Timer 1
$00:0168 TIMER1_CTRL_REG Timer 1 control register
$00:0169 TIMER1_CHARGE_L Timer 1 precharge register lower byte, loaded when counting down
$00:016A TIMER1_CHARGE_M Timer 1 precharge register middle byte, loaded when counting down
$00:016B TIMER1_CHARGE_H Timer 1 precharge register high byte, loaded when counting down
$00:016C TIMER1_CMP_REG Timer 1 compare register
$00:016D TIMER1_CMP_L Timer 1 compare register lower byte, loaded when counting up
$00:016E TIMER1_CMP_M Timer 1 compare register middle byte, loaded when counting up
$00:016F TIMER1_CMP_H Timer 1 compare register high byte, loaded when counting up
Timer 2
$00:0170 TIMER2_CTRL_REG Timer 2 control register
$00:0171 TIMER2_CHARGE_L Timer 2 precharge register lower byte, loaded when counting down
$00:0172 TIMER2_CHARGE_M Timer 2 precharge register middle byte, loaded when counting down
$00:0173 TIMER2_CHARGE_H Timer 2 precharge register high byte, loaded when counting down
$00:0174 TIMER2_CMP_REG Timer 2 compare register
$00:0175 TIMER2_CMP_L Timer 2 compare register lower byte, loaded when counting up
$00:0176 TIMER2_CMP_M Timer 2 compare register middle byte, loaded when counting up
$00:0177 TIMER2_CMP_H Timer 2 compare register high byte, loaded when counting up
Register Value Name Additional Info
TIMER<X>_CTRL_REG 0x01 TMR_EN Enabled the timer
TIMER<X>_CTRL_REG 0x02 TMR_SCLR Set when counting up
TIMER<X>_CTRL_REG 0x04 TMR_SLOAD Set when counting down
TIMER<X>_CTRL_REG 0x08 TMR_UPDWN Set when counting up (1 = Up, 0 = Down)
TIMER<X>_CMP_REG 0x01 TMR_CMP_RECLR Set to one for it to cycle when Counting up
TIMER<X>_CMP_REG 0x02 TMR_CMP_RELOAD Set to one for it to reload when Counting Down

C pseudo code for counting up

TIMER1_CHARGE_L = 0x00;
TIMER1_CHARGE_M = 0x00;
TIMER1_CHARGE_H = 0x00;
TIMER1_CMP_L = clockValue & 0xFF;
TIMER1_CMP_M = (clockValue >> 8) & 0xFF;
TIMER1_CMP_H = (clockValue >> 16) & 0xFF;

TIMER1_CMP_REG = TMR_CMP_RECLR;
TIMER1_CTRL_REG = TMR_EN | TMR_UPDWN | TMR_SCLR;

C pseudo code for counting down

TIMER1_CHARGE_L = (clockValue >> 0) & 0xFF;
TIMER1_CHARGE_M = (clockValue >> 8) & 0xFF;
TIMER1_CHARGE_H = (clockValue >> 16) & 0xFF;
TIMER1_CMP_L = 0x00;
TIMER1_CMP_M = 0x00;
TIMER1_CMP_H = 0x00;

TIMER1_CMP_REG = TMR_CMP_RELOAD;
TIMER1_CTRL_REG = TMR_EN | TMR_SLOAD;

GABE Interrupt Control Registers ($00:0140 – $00:014B)

There are four types of interrupt control register that GABE provides: pending, polarity, edge detection, and mask. Each interrupt that is supported has a bit position in each of the 24 or 32 bits provided by the register types.

Pending
The pending registers indicate if an interrupt of a particular type has been triggered and needs processing. An interrupt handler should also write to this register to clear the pending flag, once the interrupt has been processed.
Polarity
This register indicates if the interrupt is triggered by a high or low signal on the input to GABE.
Edge
This register indicates if the interrupt is triggered by an transition (edge) or by a high or low value.
Mask
This register indicates if the associated interrupt will trigger an IRQ to the processor. Interrupt signals with a mask bit of 0 will be ignored, while those with a mask bit of 1 will trigger an interrupt to the CPU.


Start Register Description Additional Info
$00:0140 INT_PENDING_REG0 Interrupt pending #0
$00:0141 INT_PENDING_REG1 Interrupt pending #1
$00:0142 INT_PENDING_REG2 Interrupt pending #2
$00:0143 INT_PENDING_REG3 Interrupt pending #3---FMX Model only
$00:0144 INT_POL_REG0 Interrupt polarity #0
$00:0145 INT_POL_REG1 Interrupt polarity #1
$00:0146 INT_POL_REG2 Interrupt polarity #2
$00:0147 INT_POL_REG3 Interrupt polarity #3---FMX Model only
$00:0148 INT_EDGE_REG0 Enable Edge Detection #0
$00:0149 INT_EDGE_REG1 Enable Edge Detection #1
$00:014A INT_EDGE_REG2 Enable Edge Detection #2
$00:014B INT_EDGE_REG3 Enable Edge Detection #3---FMX Model only
$00:0148 INT_MASK_REG0 Enable Interrupt #0
$00:0149 INT_MASK_REG1 Enable Interrupt #1
$00:014A INT_MASK_REG2 Enable Interrupt #2
$00:014B INT_MASK_REG3 Enable Interrupt #3---FMX Model only

There are several interrupts the GABE can handle, distributed over four blocks:

Block Bit Name Function
0 $01 FNX0_INT00_SOF Start of Frame @60Hz
0 $02 FNX0_INT01_SOL Start of Line (programmable)
0 $04 FNX0_INT02_TMR0 Timer 0
0 $08 FNX0_INT03_TMR1 Timer 1
0 $10 FNX0_INT04_TMR2 Timer 2
0 $20 FNX0_INT05_RTC Real Time Clock
0 $40 FNX0_INT06_FCC Floppy Drive Controller
0 $80 FNX0_INT07_MOUSE Mouse Interrupt (INT12 in SuperIO IOspace)
1 $01 FNX1_INT00_KBD Keyboard Interrupt
1 $02 FNX1_INT01_SC0 VICKY_II (INT2) Sprite 2 Sprite Collision
1 $04 FNX1_INT02_SC1 VICKY_II (INT3) Sprite 2 Tiles Collision
1 $08 FNX1_INT03_COM2 Serial Port 2 (Internal)
1 $10 FNX1_INT04_COM1 Serial Port 1 (External)
1 $20 FNX1_INT05_MPU401 Midi Controller Interrupt
1 $40 FNX1_INT06_LPT Parallel Port
1 $80 FNX1_INT07_SDCARD SD Card Controller Interrupt (CH376S, if present)
2 $01 FNX2_INT00_OPL3 OPL3
2 $02 FNX2_INT01_GABE_INT0 GABE (INT0) - TBD
2 $04 FNX2_INT02_GABE_INT1 GABE (INT1) - TBD
2 $08 FNX2_INT03_SDMA VICKY_II (INT4)
2 $10 FNX2_INT04_VDMA VICKY_II (INT5) -- Vicky DMA completion
2 $20 FNX2_INT05_GABE_INT2 GABE (INT2) - TBD
2 $40 FNX2_INT06_EXT External Expansion
2 $80 FNX2_INT07_SDCARD_INS SDCARD Insertion
3 $01 FNX3_INT00_OPN2 OPN2
3 $02 FNX3_INT01_OPM OPM
3 $04 FNX3_INT02_IDE HDD IDE Interrupt
3 $08 FNX3_INT03_TBD TBD
3 $10 FNX3_INT04_TBD TBD
3 $20 FNX3_INT05_TBD TBD
3 $40 FNX3_INT06_TBD TBD
3 $80 FNX3_INT07_TBD TBD

GABE SID Emulation ($AF:E400 – $AF:E41F)

GABE emulates the SID sound chip. Currently, the FMX provides one SID unit. Although the real SID chip provides analog to digital conversion for two potentiometer inputs for paddles, the C256's paddle inputs are provided through a different chip, so the POT registers listed below are not useful.

For complete information on how to use these registers, please consult documentation on the SID chips.

All registers are write-only, except where noted otherwise.

Start Address End Address Register Name Additional Information
$AF:E400 $AF:E401 SID0_V1_FREQ SID0 V1 Frequency (16-bits)
$AF:E402 $AF:E403 SID0_V1_PW SID0 V1 Pulse Width (12-bits)
$AF:E404 SID0_V1_CTRL SID0 V1 Control (8-bits)
$AF:E405 SID0_V1_ATCK_DECY SID0 V1 Attack/Decay (8-bits)
$AF:E406 SID0_V1_SSTN_RLSE SID0 V1 Sustain/Release (8-bits)
$AF:E407 $AF:E408 SID0_V2_FREQ SID0 V2 Frequency (16-bits)
$AF:E409 $AF:E40A SID0_V2_PW SID0 V2 Pulse Width (12-bits)
$AF:E40B SID0_V2_CTRL SID0 V2 Control (8-bits)
$AF:E40C SID0_V2_ATCK_DECY SID0 V2 Attack/Decay (8-bits)
$AF:E40D SID0_V2_SSTN_RLSE SID0 V2 Sustain/Release (8-bits)
$AF:E40E $AF:E40F SID0_V3_FREQ SID0 V3 Frequency (16-bits)
$AF:E410 $AF:E411 SID0_V3_PW SID0 V3 Pulse Width (12-bits)
$AF:E412 SID0_V3_CTRL SID0 V3 Control (8-bits)
$AF:E413 SID0_V3_ATCK_DECY SID0 V3 Attack/Decay (8-bits)
$AF:E414 SID0_V3_SSTN_RLSE SID0 V3 Sustain/Release (8-bits)
$AF:E415 SID0_FC_LO SID0 Filter Frequency LOW (3 bits)
$AF:E416 SID0_FC_HI SID0 Filter Frequency HIGH (8 bits)
$AF:E417 SID0_RES_FILT SID0 Resonance / Filter (8-bits)
$AF:E418 SID0_MODE_VOL SID0 Mode / Volume (8-bits)
$AF:E419 SID0_POT_X SID0 POT X (Read only, C256 - NOT USED)
$AF:E41A SID0_POT_Y SID0 POT Y (Read only, C256 - NOT USED)
$AF:E41B SID0_OSC3_RND SID0 OSC3 / RANDOM (Read only, 8-bits)
$AF:E41C SID0_ENV3 SID0 ENV3 (Read only, 8-bits)

GABE Trinity Joystick/Gamepad and DIP Switch Registers ($AF:E800 – $AF:E80E)

GABE provides access to the Trinity chip, which provides access to the joystick ports and the DIP switches. The joystick ports can be used in two modes: default mode, where they work like Atari style joysticks, and NES mode where they can interface with either NES or SNES game pads.

FMX Joystick Ports Configuration

Address Name 7 6 5 4 3 2 1 0 Description
$AF:E800 JOYSTICK0 BUTTON2 BUTTON1 0 BUTTON0 RIGHT LEFT DOWN UP Left-most joystick port
$AF:E801 JOYSTICK1 BUTTON2 BUTTON1 0 BUTTON0 RIGHT LEFT DOWN UP Next to joystick port 0
$AF:E802 JOYSTICK2 0 0 0 BUTTON0 RIGHT LEFT DOWN UP Next to joystick port 1
$AF:E803 JOYSTICK3 0 0 0 BUTTON0 RIGHT LEFT DOWN UP Right-most joystick port
$AF:E804 JOYSTICK_MODE NES_TRIG 0 0 0 NES_DONE NES_JOY NES_EN1 NES_EN0 Control SNES compatibility
$AF:E805 REVOFPCB_C "C" Board identification code byte (expect "C")
$AF:E806 REVOFPCB_4 "4" Board identification code byte (expect "4")
$AF:E807 REVOFPCB_A "A" Board identification code byte (expect "A")
$AF:E808 NES_SNES0_DAT_LO see below Port 0: NES or SNES gamepad data (low byte)
$AF:E809 SNES0_DAT_HI see below Port 0: NES or SNES gamepad data (highbyte)
$AF:E80A NES_SNES1_DAT_LO see below Port 1: NES or SNES gamepad data (low byte)
$AF:E80B SNES1_DAT_HI see below Port 1: NES or SNES gamepad data (highbyte)
$AF:E80C CFP9301_REV revision Trinity revision code
$AF:E80D DIP_USER user Switches 3 through 5. User defined meaning.
$AF:E80E DIP_BOOTMODE HD_INSTALLED 0 0 0 0 0 BM1 BM0 Boot control switches.
SNES mode
Address Name 7 6 5 4 3 2 1 0 Description
$AF:E808 NES_SNES0_DAT_LO B Y SELECT START UP DOWN LEFT RIGHT NES data if NES_JOY = 1 and NES_EN0 = 1
$AF:E809 SNES0_DAT_HI 0 0 0 0 A X L R NES data if NES_JOY = 1 and NES_EN0 = 1)
$AF:E80A NES_SNES1_DAT_LO B Y SELECT START UP DOWN LEFT RIGHT NES data if NES_JOY = 1 and NES_EN1 = 1
$AF:E80B SNES1_DAT_HI 0 0 0 0 A X L R NES data if NES_JOY = 1 and NES_EN1 = 1
NES mode
Address Name 7 6 5 4 3 2 1 0 Description
$AF:E808 NES_SNES0_DAT_LO A B SELECT START UP DOWN LEFT RIGHT NES data if NES_JOY = 0 and NES_EN0 = 1
$AF:E809 SNES0_DAT_HI N/A Unused if NES_EN0 = 0
$AF:E80A NES_SNES1_DAT_LO A B SELECT START UP DOWN LEFT RIGHT NES data if NES_JOY = 0 and NES_EN1 = 1
$AF:E80B SNES1_DAT_HI N/A Unused in NES mode

NES Control Bits

NES_TRIG
Set to one to trigger the serializer to read the gamepad.
NES_DONE
Poll to see if the serializer has finished reading the gamepad.
NES_JOY
If 1, sets SNES compatibility. If 0, sets NES compatibility.
NES_EN1
If 1, enables NES/SNES compatibility on port 1. If 0, joysticks work like Atari joysticks
NES_EN0
If 1, enables NES/SNES compatibility on port 0. If 0, joysticks work like Atari joysticks

Boot Mode

Switches 0 and 1 allow you to select the boot mode for the FMX. Note that the switches are active low: a switch in the "On" position registers as a 0.

BM1 BM0 Boot Mode
0 0 Boots from the IDE drive DIP switch 8 must be set (HD_INSTALLED).
0 1 Boots from the SD card
1 0 Boots from the floppy drive
1 1 Boots to BASIC

GABE IDE Registers ($AF:E830 – $AF:E839)

GABE provides an IDE interface to a hard drive (if installed). This is done through the Unity chip and uses standard IDE interface command, error, and status codes.

Address R/W Name 7 6 5 4 3 2 1 0 Description
$AF:E830 x IDE_DATA D7 D6 D5 D4 D3 D2 D1 D0 8-bit data read/write
$AF:E831 R IDE_ERROR BBK UNC MC IDNF MCR ABRT TK0NF AMNF Error code for the transaction
$AF:E832 W IDE_SECT_CNT SC7 SC6 SC5 SC4 SC3 SC2 SC1 SC0 Sector count
$AF:E833 W IDE_SECT_SRT SS7 SS6 SS5 SS4 SS3 SS2 SS1 SS0 Sector start (0 = 256), start at 1
$AF:E834 W IDE_CLDR_LO C7 C6 C5 C4 C3 C2 C1 C0 Cylinder Number (low)
$AF:E835 W IDE_CLDR_HI C15 C14 C13 C12 C11 C10 C9 C8 Cylinder Number (high)
$AF:E836 W IDE_HEAD 1 0 1 M/S H3 H2 H1 H0 Head number and Master (0) / Slave(1) device
$AF:E837 W IDE_CMD_STAT CMD7 CMD6 CMD5 CMD4 CMD3 CMD2 CMD1 CMD0 Send a command to the drive
$AF:E837 R IDE_CMD_STAT BSY DRDY DF DSC DRQ CORR IDX ERR Get the status of device. Reading this will clear the Interrupt Registers
$AF:E838 X IDE_DATA_LO D7 D6 D5 D4 D3 D2 D1 D0 16-bit data transfer registers (low byte)
$AF:E839 X IDE_DATA_HI D15 D14 D13 D12 D11 D10 D9 D8 16-bit data transfer registers (high byte)

GABE IDE Status Flags

BSY
Device is busy if set
DRDY
Device is ready for a command if set
DF
Device fault
DSC
Device seek complete. If set, the head has stopped moving and is over a track.
DRQ
Device is ready to transfer a data byte (either read or write) if set.
CORR
If set, indicates that data was corrected automatically.
IDX
A vendor specific status
ERR
If set, an error occurred with additional data in the error register.

GABE IDE Error Flags

BBK
Bad block
UNC
Uncorrectable data error
MC
Media changed
IDNF
ID mark not found
MCR
Media change requested
ABRT
Command aborted
TK0NF
Track 0 not found
AMNF
Address mark not found

GABE Control Registers ($AF:E880 – $AF:E887)

Start Address Ending Address Register Description Additional Info
$AF:E880 GABE_MSTR_CTRL
$AF:E881 Reserved
$AF:E882 GABE_RST_AUTH0 Must Contain the BYTE $AD for Reset to Activate
$AF:E883 GABE_RST_AUTH1 Must Contain the BYTE $DE for Reset to Activate
$AF:E884 $AF:E885 GABE_RNG_DATASEED On read: 16-bit random data. On write, set 16-bit RNG seed.
$AF:E886 GABE_RNG_STATCTRL On read: 8-bit status. On write: 8-bit control
$AF:E887 GABE_SYS_STAT 8-bit system status

GABE Master Control Register ($AF:E880)

7 6 5 4 3 2 1 0
GABE_CTRL_WRM_RST GABE_CTRL_BUZZER GABE_CTRL_SDC_LED GABE_CTRL_PWR_LED
GABE_CTRL_PWR_LED
Turns the power LED (next to the reset button) on or off.
GABE_CTRL_SDC_LED
Turns the SDC activity LED (next to the SDC slot) on or off.
GABE_CTRL_BUZZER
Turns the built-in piezo buzzer on or off.
GABE_CTRL_WRM_RST
Triggers a warm reset of the board (GABE_RST_AUTH0 must be set to $AD and GABE_RST_AUTH1 to $DE in order to trigger the reset).

GABE Random Number Generator Status and Control ($AF:E886)

R/W 7 6 5 4 3 2 1 0
R GABE_RNG_LFSR_DONE
W GABE_RNG_CTRL_DV GABE_RNG_CTRL_EN
GABE_RNG_LFSR_DONE
Indicates that output = seed database.
GABE_RNG_CTRL_DV
After setting the seed value, toggle this bit for the seed be registered.
GABE_RNG_CTRL_EN
Enable the LFSR block.

GABE System Status Register ($AF:E887)

7 6 5 4 3 2 1 0
GABE_SYS_STAT_CPUX GABE_SYS_STAT_CPUA GABE_SYS_STAT_EXP GABE_SYS_STAT_MID1 GABE_SYS_STAT_MID0
GABE_SYS_STAT_CPUX
Indicates if the CPU's index registers are 8-bits or 16-bits wide.
GABE_SYS_STAT_CPUA
Indicates if the CPU's accumulator is 8-bits or 16-bits wide.
GABE_SYS_STAT_EXP
Indicates if the and expansion card is present (0).
GABE_SYS_STAT_MID1 and GABE_SYS_STAT_MID0
These two bits show the machine ID:
GABE_SYS_STAT_MID1 GABE_SYS_STAT_MID0 Machine
0 0 FMX - Development Platform
0 1 C256 Foenix - Dev Platform
1 0 C256 Foenix - User Version (65C816)
1 1 Reserved

GABE SDC Interface ($AF:EA00)

GABE provides block-level access to the SPI interface of the SD card. Currently, there are other registers in that provide access to the SD card through a CH376S chip, but that interface should be considered deprecated in favor of this interface. Most FMX boards do not have the CH376S chip, leaving its position unpopulated on the board.

Address R/W Name 7 6 5 4 3 2 1 0 Description
$AF:EA00 R SDC_VERSION_REG version Should read 12
$AF:EA01 W SDC_CONTROL_REG x x x x x x x RESET Control register: RESET = 1 resets the core logic. Self clearing.
$AF:EA02 W SDC_TRANS_TYPE_REG x x x x x x TRANS Sets the type of transaction: 0 = Direct, 1 = INIT SD, 2 = Read Block, 3 = Write block
$AF:EA03 W SDC_TRANS_CONTROL_REG x x x x x x x START Start transaction bit.
$AF:EA04 R SDC_TRANS_STATUS_REG x x x x x x x BUSY Status of the transaction. BUSY = 1 indicates a transaction is in progress.
$AF:EA05 R SDC_TRANS_ERROR_REG x x WR_ERR RD_ERR TRANS_ERR Error conditions for read, write, and transaction.
$AF:EA06 RW SDC_DIRECT_ACCESS_REG data SPI Direct Read and Write - Set DATA before initiating direct Access Transaction
$AF:EA07 W SDC_SD_ADDR_7_0_REG A7 A6 A5 A4 A3 A2 A1 A0 Set the ADDR before a block read or block write.
$AF:EA08 W SDC_SD_ADDR_15_8_REG A15 A14 A13 A12 A11 A10 A9 A8 ADDR[8:0] always should be 0, since each block is 512 bytes
$AF:EA09 W SDC_SD_ADDR_23_16_REG A23 A22 A21 A20 A19 A18 A7 A6
$AF:EA0A W SDC_SD_ADDR_31_24_REG A32 A31 A30 A29 A28 A26 A25 A24
$AF:EA0B W SDC_SPI_CLK_DEL_REG clock delay
$AF:EA10 R SDC_RX_FIFO_DATA_REG data Data from block read
$AF:EA12 R SDC_RX_FIFO_DATA_CNT_HI CNT15 CNT14 CNT13 CNT12 CNT11 CNT10 CNT9 CNT8 How many bytes in the read FIFO (HI)
$AF:EA13 R SDC_RX_FIFO_DATA_CNT_LO CNT7 CNT6 CNT5 CNT4 CNT3 CNT2 CNT1 CNT0 How many bytes in the read FIFO (LOW)
$AF:EA14 W SDC_RX_FIFO_CTRL_REG x x x x x x x CLEAR CLEAR = 1, clear the read FIFO. Bit is self-clearing.
$AF:EA20 W SDC_TX_FIFO_DATA_REG data Write data block here.
$AF:EA21 W SDC_TX_FIFO_CTRL_REG x x x x x x x CLEAR CLEAR = 1, clear the write FIFO. Bit is self-clearing.

GABE: SDC_TRANS_ERROR_REG ($AF:EA05)

Lists any error conditions on reads, writes, or general transactions:

TRANS_ERR codes [1:0]
Code Condition
00 No INIT error
01 INIT CMD0 error
10 INIT CMD1 error
RD_ERR codes [3:2]
Code Condition
00 No read error
01 CMD error
10 Token error
WR_ERR codes [5:4]
Code Condition
00 No write error
01 CMD error
10 Data error
11 Busy error

GABE SN76489 Interface ($AF:F100)

GABE provides the interface to the SN76489 sound chip on the Foenix FMX. This chip uses only a single write-only address: $AF:F100. The SN76489 has four voices: three tone generators, and one noise generator. Each voice has a frequency and an attenuation setting. For full details on how to use the SN76489 chip, please consult the official documentation, but here is a brief summary.

Playing a Tone

To change the frequency of a tone generator, two bytes are sent to the chip in order:

7 6 5 4 3 2 1 0
First 1 Channel 0 F3 F2 F1 F0
Second 0 x F9 F8 F7 F6 F5 F4

The actual frequency played depends on the clock driving the chip: freq = N / (32 * F), where N = 3575800 (for the FMX), and F is the 10-bit number sent to the chip. The "Channel" number above must be 0, 1, or 3.

Play a Noise

To play a noise, only a single byte is written to the chip:

7 6 5 4 3 2 1 0
1 1 1 0 x NF NF1 NF0

The NF bit controls the type of noise generated, and the NF bits control the base frequency of the noise.

Setting the Volume

The volumes of each of the four voices can be set independently. To set the volume of a single voice, a single byte is sent to the chip:

7 6 5 4 3 2 1 0
1 Channel 1 A3 A2 A1 A0

The "Channel" bits are the number of the voice to change: 0, 1, 2, or 3. The four bit value A is the level of attenuation, where 0 is fully on, and 15 is fully off.