home *** CD-ROM | disk | FTP | other *** search
- ** Machine-specific macros for the implementation-dependent
- ** Z80 instructions.
-
- ** These are intended for the Spectrum emulator only.
-
- XREF _SOUNDDATA
-
- INCLUDE "envdata.i"
-
- ** ---------------------------------------------------------------------
-
- ** I/O ports on the ZX Spectrum
- **
- ** In general:
- **
- ** The address bits 0 to 4 are usually set. At most one of them
- ** should be reset at any time. A reset bit signals an I/O operation.
- ** Bits 5 to 7 are normally set, and are not used by the standard
- ** hardware. The address bits 8 to 15 are in some cases (e.g. when
- ** reading the keyboard) used to give more information. Usually they
- ** are ignored.
- **
- ** Port xxFE (bit 0 reset)
- ** General I/O port
- ** Input:
- ** Bit 7 Unknown
- ** Bit 6 Value of EAR port
- ** Bit 5 Unknown
- ** Bits 4 to 0 Keyboard data
- **
- ** The keyboard rows are ordered from 0 (Caps Shift to V) through
- ** 3 (1 to 5), 4 (0 to 6) and 7 (Space to B).
- ** The bits 0 to 4 represent keys in a row, where bit 0 is the
- ** outermost key. A bit is low if the key is pressed, and high if
- ** it is not pressed.
- ** The address bits 8 to 15 correspond to rows 0 to 7 respectively,
- ** and specify which rows will be read. A reset bit means the row
- ** will be read. The selected rows are bitwise combined. In the
- ** result, each bit is reset if one of the bits in the same position
- ** was reset, and set if none of them were reset. Taking the bit
- ** pattern 11111 and bitwise AND:ing with all selected rows would
- ** give the same result.
- ** The value of bit 6 is not affected by the high address bits.
- **
- ** Output:
- ** Bits 7 to 5 Not used
- ** Bit 4 Speaker control
- ** Bit 3 MIC port control
- ** Bits 2 to 0 Border colour code
- **
- ** The speaker and MIC ports are active low.
- ** The colour codes are specified as on/off flags for the RGB
- ** components, where bits 2 through 0 correspond to Green, Red and
- ** Blue respectively, giving totally 8 different colours. There is
- ** no Bright mode flag for the border.
- **
- ** Port xxFD (bit 1 reset)
- ** Used for Microdrive, Networking or RS232C. I have no details.
- ** Input:
- ** Output:
- **
- ** Port xxFB (bit 2 reset)
- ** ZX Printer control port
- ** Input:
- ** Bit 7 High when stylus is in write position.
- ** Bit 6 Low only if the printer is present.
- ** Bits 5 to 0 Unknown
- **
- ** Output:
- ** Bit 7 High "for the actual printing".
- ** Bits 6 to 3 Unknown
- ** Bit 2 Low starts the motor.
- ** Bit 1 High slows the motor.
- ** Bit 0 Unknown
- **
- ** Port xxF7 (bit 3 reset)
- ** Used for Microdrive, Networking or RS232C. I have no details.
- ** Input:
- ** Output:
- **
- ** Port xxEF (bit 4 reset)
- ** Used for Microdrive, Networking or RS232C. I have no details.
- ** Input:
- ** Output:
- **
- ** Port xx1F (bits 0 to 4 set, bits 5 to 7 reset)
- ** Kempston interface port (external hardware)
- ** Input:
- ** Bits 7 to 5 Unknown
- ** Bit 4 Fire
- ** Bit 3 Up
- ** Bit 2 Down
- ** Bit 1 Left
- ** Bit 0 Right
- **
- ** Bits 4 to 0 are active high.
- **
- ** Output: Unknown
- **
-
- ** ---------------------------------------------------------------------
-
- ** The keyboard data:
- ** An array of 8 bytes (0 to 7) corresponding exactly to the
- ** keyboard rows 0 to 7. Bits 5 to 7 are zero.
-
- Kbd = Z80_Envdata+Envd_Kbd ;offset from control structure base
-
- ** ---------------------------------------------------------------------
-
- IFD VERBOSE
- LIST
- ** Compiling the machine.i file designed for the ZX Spectrum emulator.
- NOLIST
- ENDC
-
-
- ** A default read value, used by some lazy routines like Inir:
- DEFAULT_IN = $FF
-
-
- ;Macro for fast keyboard data reading.
- ;RowSpec (register) corresponds to high byte of I/O address.
- ;Dest (register) must be $FF before macro call.
- KBD_READ MACRO ;RowSpec, Dest
- add.b \1,\1
- bcs.s .7set
- and.b Kbd+7(TableB),\2 ;row 7
- .7set add.b \1,\1
- bcs.s .6set
- and.b Kbd+6(TableB),\2
- .6set add.b \1,\1
- bcs.s .5set
- and.b Kbd+5(TableB),\2
- .5set add.b \1,\1
- bcs.s .4set
- and.b Kbd+4(TableB),\2
- .4set add.b \1,\1
- bcs.s .3set
- and.b Kbd+3(TableB),\2
- .3set add.b \1,\1
- bcs.s .2set
- and.b Kbd+2(TableB),\2
- .2set add.b \1,\1
- bcs.s .1set
- and.b Kbd+1(TableB),\2
- .1set add.b \1,\1
- bcs.s .0set
- and.b Kbd+0(TableB),\2 ;row 0
- .0set
- ENDM
-
-
- **
- ** The instruction macros themselves:
- **
-
- ;sadly, one can't nest macros.
-
- In_r_1C1_subm MACRO
- cmp.b #$fe,C
- st \1 ;flags unaffected
- bne.s .end
- move.b B,d7
- KBD_READ d7,\1
- .end tst.b \1 ;V cleared
- putsr d7
- eor.b d7,d6
- and.w #1,d6
- eor.b d7,d6 ;keep old carry
- parity \1
- skip 1
- next
- ENDM
-
- In_A_1C1_mac MACRO
- In_r_1C1_subm A
- ENDM
- In_B_1C1_mac MACRO
- In_r_1C1_subm B
- ENDM
- In_C_1C1_mac MACRO
- In_r_1C1_subm C
- ENDM
- In_D_1C1_mac MACRO
- In_r_1C1_subm D
- ENDM
- In_E_1C1_mac MACRO
- In_r_1C1_subm E
- ENDM
- In_L_1C1_mac MACRO
- In_r_1C1_subm L
- ENDM
-
- In_H_1C1_mac MACRO
- swap d6 ;save flags
- move.w #$00ff,d6 ;for parity indexing
- cmp.b #$fe,C
- bne.s .end
- move.b B,d7
- KBD_READ d7,d6
- .end move.w HL,(Work)
- move.b d6,(Work) ;V cleared,N & Z tested
- putsr d7
- or.b Z80_Parity(TableB,d6.w),d7 ;set parity in d7
- move.w (Work),HL
- swap d6 ;get old flags
- eor.b d7,d6
- and.w #1,d6
- eor.b d7,d6 ;keep old carry
- skip 1
- next
- ENDM
-
- ;"Undocumented" instruction. Reads from the port but does not
- ;store the value. Flags are affected by the read value as usual,
- ;and this instruction is sometimes named "In F,(C)". The name
- ;"In (HL),(C)" would be symmetric, but is not correct.
- In_xx_1C1_mac MACRO
- swap d6 ;save flags
- move.w #$00ff,d6 ;for parity indexing
- cmp.b #$fe,C
- bne.s .end
- move.b B,d7
- KBD_READ d7,d6
- .end tst.b d6 ;V cleared,N & Z tested
- putsr d7
- or.b Z80_Parity(TableB,d6.w),d7 ;set parity in d7
- swap d6 ;forget the value, get old flags
- eor.b d7,d6
- and.w #1,d6
- eor.b d7,d6 ;keep old carry
- skip 1
- next
- ENDM
- ** ----
- In_A_1n1_mac MACRO
- getRPC
- getz d7,d7
- cmp.b #$fe,d7
- bne.s .notFE
- move.b A,d7
- st A
- KBD_READ d7,A
- .end skip 1
- next
-
- .notFE st A
- bra.s .end
- ENDM
- ** ----
-
- ** The block In instructions do not bother to 'read ports'.
- ** They simply "read" the default in value.
-
- Ind_mac MACRO
- move.b #DEFAULT_IN,d7
- putz d7,HL
- decw HL
- and.w #%1011,d6
- decb B
- bne.s .nz
- or.w #%0100,d6
- .nz skip 1
- next
- ENDM
- ** ----
- Indr_mac MACRO
- move.b #DEFAULT_IN,d7
- .loop putz d7,HL
- decw HL
- decb B
- bne.s .loop
- or.w #%0100,d6
- skip 1
- next
- ENDM
- ** ----
- Ini_mac MACRO
- move.b #DEFAULT_IN,d7
- putz d7,HL
- incw HL
- and.w #%1011,d6
- decb B
- bne.s .nz
- or.w #%0100,d6
- .nz skip 1
- next
- ENDM
- ** ----
- Inir_mac MACRO
- move.b #DEFAULT_IN,d7
- .loop putz d7,HL
- incw HL
- decb B
- bne.s .loop
- or.w #%0100,d6
- skip 1
- next
- ENDM
- ** ----
- Ld_A_R_mac MACRO
- getRPC ;take bits from RPC (since PPC is always even)
- move.b Z80_R(TableB),A
- eor.w d7,d6
- and.w #80,d6 ;keep bit 7 of stored R
- eor.w d7,d6 ;V is cleared, Z and N tested.
- putsr d7
- eor.w d7,d6
- and.w #1,d6 ;keep old carry
- eor.w d7,d6
- tst.b Z80_IFF(TableB) ;test IFF2
- beq.s .clear
- or.w #%0010,d6 ;set V if IFF2 set
- .clear skip 1
- next
- ENDM
- ** ----
- Ld_R_A_mac MACRO
- move.b A,Z80_R(TableB) ;(the bits 6-0 are never reused)
- skip 1
- next
- ENDM
- ** ----
-
- ** It is OK for Otir and Otdr to be a bit inefficient. The real Z80
- ** instruction does a complete re-execution each time it loops.
- ** If that is done here as well, the block output timing will be nicer.
-
- Otdr_mac MACRO
- .loop getz HL,d7
- cmp.b #$fe,C
- bne.s .end
- and.b #$10,d7 ;speaker(0 or 16)
- add.b d7,d7
- move.b d7,_SOUNDDATA
- move.b d7,_SOUNDDATA+1
- getz HL,d7
- and.b #8,d7 ;mic(0 or 8)
- add.b d7,d7
- add.b d7,d7
- move.b d7,_SOUNDDATA+2
- move.b d7,_SOUNDDATA+3
- .end decw HL
- decb B
- bne.s .loop
- or.w #%0100,d6 ;Set Z
- skip 1
- next
- ENDM
- ** ---
- Otir_mac MACRO
- .loop getz HL,d7
- cmp.b #$fe,C
- bne.s .end
- and.b #$10,d7 ;speaker(0 or 16)
- add.b d7,d7
- move.b d7,_SOUNDDATA
- move.b d7,_SOUNDDATA+1
- getz HL,d7
- and.b #8,d7 ;mic(0 or 8)
- add.b d7,d7
- add.b d7,d7
- move.b d7,_SOUNDDATA+2
- move.b d7,_SOUNDDATA+3
- .end incw HL
- decb B
- bne.s .loop
- or.w #%0100,d6 ;Set Z
- skip 1
- next
- ENDM
- ** ----
-
- ;no macro nesting, sadly
- Out_1C1_r_subm MACRO
- cmp.b #$fe,C
- bne.s .end
- move.b \1,d7 ;speaker
- and.b #$10,d7 ;(0 or 16)
- add.b d7,d7
- move.b d7,_SOUNDDATA
- move.b d7,_SOUNDDATA+1
- move.b \1,d7 ;mic
- and.b #8,d7 ;(0 or 8)
- add.b d7,d7
- add.b d7,d7
- move.b d7,_SOUNDDATA+2
- move.b d7,_SOUNDDATA+3
- .end skip 1
- next
- ENDM
-
- Out_1C1_A_mac MACRO
- Out_1C1_r_subm A
- ENDM
- Out_1C1_B_mac MACRO
- Out_1C1_r_subm B
- ENDM
- Out_1C1_C_mac MACRO
- Out_1C1_r_subm C
- ENDM
- Out_1C1_D_mac MACRO
- Out_1C1_r_subm D
- ENDM
- Out_1C1_E_mac MACRO
- Out_1C1_r_subm E
- ENDM
- Out_1C1_L_mac MACRO
- Out_1C1_r_subm L
- ENDM
-
- Out_1C1_H_mac MACRO
- cmp.b #$fe,C
- bne.s .end
- move.w HL,(Work)
- move.b (Work),d7 ;speaker
- and.b #$10,d7 ;(0 or 16)
- add.b d7,d7
- move.b d7,_SOUNDDATA
- move.b d7,_SOUNDDATA+1
- move.b (Work),d7 ;mic
- and.b #8,d7 ;(0 or 8)
- add.b d7,d7
- add.b d7,d7
- move.b d7,_SOUNDDATA+2
- move.b d7,_SOUNDDATA+3
- .end skip 1
- next
- ENDM
- ** ----
- ;"Undocumented" instruction. Seems to output a zero value
- ;to the port. The name "Out (C),(HL)" would be symmetric,
- ;but is not correct.
- Out_1C1_xx_mac MACRO
- cmp.b #$fe,C
- bne.s .end
- moveq #0,d7
- move.b d7,_SOUNDDATA
- move.b d7,_SOUNDDATA+1
- move.b d7,_SOUNDDATA+2
- move.b d7,_SOUNDDATA+3
- .end skip 1
- next
- ENDM
- ** ----
- Out_1n1_A_mac MACRO
- getRPC
- getz d7,d7 ;immediate data
- cmp.b #$fe,d7
- bne .end
- move.b A,d7 ;speaker
- and.b #$10,d7 ;(0 or 16)
- add.b d7,d7
- move.b d7,_SOUNDDATA
- move.b d7,_SOUNDDATA+1
- move.b A,d7 ;mic
- and.b #8,d7 ;(0 or 8)
- add.b d7,d7
- add.b d7,d7
- move.b d7,_SOUNDDATA+2
- move.b d7,_SOUNDDATA+3
- .end skip 1
- next
- ENDM
- ** ----
- Outd_mac MACRO
- getz HL,d7
- cmp.b #$fe,C
- bne.s .end
- and.b #$10,d7 ;speaker(0 or 16)
- add.b d7,d7
- move.b d7,_SOUNDDATA
- move.b d7,_SOUNDDATA+1
- getz HL,d7
- and.b #8,d7 ;mic(0 or 8)
- add.b d7,d7
- add.b d7,d7
- move.b d7,_SOUNDDATA+2
- move.b d7,_SOUNDDATA+3
- .end decw HL
- and.w #%1011,d6
- decb B
- bne.s .nz
- or.w #%0100,d6
- .nz skip 1
- next
- ENDM
- ** ----
- Outi_mac MACRO
- getz HL,d7
- cmp.b #$fe,C
- bne.s .end
- and.b #$10,d7 ;speaker(0 or 16)
- add.b d7,d7
- move.b d7,_SOUNDDATA
- move.b d7,_SOUNDDATA+1
- getz HL,d7
- and.b #$08,d7 ;mic(0 or 8)
- add.b d7,d7
- add.b d7,d7
- move.b d7,_SOUNDDATA+2
- move.b d7,_SOUNDDATA+3
- .end incw HL
- and.w #%1011,d6
- decb B
- bne.s .nz
- or.w #%0100,d6
- .nz skip 1
- next
- ENDM
- ** ----
-
- ** The Reti and Retn instructions do not themselves cause any signalling,
- ** but interrupt-controlling hardware could be watching the bus to see
- ** when an interrupt finishes.
-
- Reti_mac MACRO
- ;Do any "hardware" emulation first.
- getz ZSP,1(Work)
- incw ZSP
- getz ZSP,(Work)
- incw ZSP
- move.w (Work),d7
- makePPC
- testreq
- ENDM
- ** ----
- Retn_mac MACRO
- ;Do any "hardware" emulation first.
- getz ZSP,1(Work)
- incw ZSP
- getz ZSP,(Work)
- incw ZSP
- move.w (Work),d7
- makePPC
- move.b Z80_IFF(TableB),d7
- asr.b #1,d7 ;IFF2 -> IFF1 -> bit 5
- and.b #$C0,d7 ;reset bits 5-0
- move.b d7,Z80_IFF(TableB)
- testreq
- ENDM
-
- ** =====================================================================
-