Commit of v5 code
This commit is contained in:
commit
84ff354f52
|
@ -0,0 +1,13 @@
|
||||||
|
IRQL = $3FE
|
||||||
|
IRQH = $3FF
|
||||||
|
|
||||||
|
;--------------------------------------
|
||||||
|
|
||||||
|
SW_SLOTXROM_ENA = $C006
|
||||||
|
SW_SLOTXROM_DIS = $C007
|
||||||
|
SW_SLOTXROM_R = $C015 ; Active low
|
||||||
|
|
||||||
|
SW_SLOT3ROM_DIS = $C00A
|
||||||
|
SW_SLOT3ROM_ENA = $C00B
|
||||||
|
SW_SLOT3ROM_R = $C017 ; Active high
|
||||||
|
|
|
@ -0,0 +1,440 @@
|
||||||
|
|
||||||
|
SPECTRUM128_STEREO = 1 ; L = A+Bx0.5, R = C'+B'x0.5
|
||||||
|
DUAL_MONO = 0 ; L = A+B+C, R=A'+B'+C'
|
||||||
|
|
||||||
|
|
||||||
|
!macro MB_Init1 .RegSongNum {
|
||||||
|
; Pre: A = Song# [0..NUM_SONGS-1]
|
||||||
|
; Post: .RegSongNum = Song#
|
||||||
|
;
|
||||||
|
|
||||||
|
jmp .skip_data
|
||||||
|
|
||||||
|
nSongNum: !byte 0 ; Song#
|
||||||
|
nFrameNum: !byte 0,0,0 ; Minute:Second:FrameNum (@ 50Hz)
|
||||||
|
;
|
||||||
|
nMaskA: !byte 0 ; Voice-A mask (0=enable)
|
||||||
|
nMaskB: !byte 0 ; Voice-B mask (0=enable)
|
||||||
|
nMaskC: !byte 0 ; Voice-C mask (0=enable)
|
||||||
|
;
|
||||||
|
nAttA: !byte 0 ; Attenuation of Voice-A
|
||||||
|
nAttB: !byte 1 ; Attenuation of Voice-B : B Volume / 2 (logarithmic, so: if (A) A--)
|
||||||
|
nAttC: !byte 0 ; Attenuation of Voice-C
|
||||||
|
;
|
||||||
|
pAYRegValues: !word AYRegValues ; For VU-meter
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
.skip_data:
|
||||||
|
lda nSongNum
|
||||||
|
cmp #NUM_SONGS
|
||||||
|
bcc SongNumOK
|
||||||
|
lda #255 ; Uninit
|
||||||
|
sta nSongNum
|
||||||
|
SongNumOK:
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
lda nMBBaseHi
|
||||||
|
beq FindMB
|
||||||
|
|
||||||
|
; Need to disable Timer1 IRQ before:
|
||||||
|
; . Scanning for MB card
|
||||||
|
; . Saving ZPBlock
|
||||||
|
|
||||||
|
lda #1<<6
|
||||||
|
MB0: sta CARD_BASE+SY6522_IER ; Disable Timer1 IRQ
|
||||||
|
|
||||||
|
FindMB:
|
||||||
|
|
||||||
|
;----------------------------------
|
||||||
|
|
||||||
|
+SaveRegs ZPBlock
|
||||||
|
|
||||||
|
jsr SF_GetMBSlot
|
||||||
|
bne GotMBSlot
|
||||||
|
|
||||||
|
; MB not found!
|
||||||
|
jmp InitExit2
|
||||||
|
|
||||||
|
GotMBSlot:
|
||||||
|
; Setup correct address in IRQ handler code:
|
||||||
|
|
||||||
|
!if SPECTRUM128_STEREO {
|
||||||
|
stx MBx1+2
|
||||||
|
stx MBx2+2
|
||||||
|
stx MBx3+2
|
||||||
|
stx MBx4+2
|
||||||
|
}
|
||||||
|
|
||||||
|
!if DUAL_MONO {
|
||||||
|
stx MB1+2
|
||||||
|
stx MB1b+2
|
||||||
|
stx MB2+2
|
||||||
|
stx MB2b+2
|
||||||
|
stx MB3+2
|
||||||
|
stx MB3b+2
|
||||||
|
stx MB4+2
|
||||||
|
stx MB4b+2
|
||||||
|
stx MB5+2
|
||||||
|
stx MB5b+2
|
||||||
|
stx MB6+2
|
||||||
|
stx MB6b+2
|
||||||
|
}
|
||||||
|
|
||||||
|
stx MB0+2
|
||||||
|
stx MB7+2
|
||||||
|
|
||||||
|
stx nMBBaseHi
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
lda #$07
|
||||||
|
ldy #SY6522_DDRB
|
||||||
|
sta (MBBase),y
|
||||||
|
ldy #SY6522_DDRB+$80
|
||||||
|
sta (MBBase),y
|
||||||
|
|
||||||
|
lda #$ff
|
||||||
|
ldy #SY6522_DDRA
|
||||||
|
sta (MBBase),y
|
||||||
|
ldy #SY6522_DDRA+$80
|
||||||
|
sta (MBBase),y
|
||||||
|
|
||||||
|
lda #AY_RESET
|
||||||
|
ldy #SY6522_ORB
|
||||||
|
sta (MBBase),y
|
||||||
|
ldy #SY6522_ORB+$80
|
||||||
|
sta (MBBase),y
|
||||||
|
|
||||||
|
ldx nSongNum
|
||||||
|
cpx #255
|
||||||
|
bne NotFini
|
||||||
|
|
||||||
|
jmp InitExit
|
||||||
|
|
||||||
|
NotFini:
|
||||||
|
lda SongTbl,x
|
||||||
|
sta .RegSongNum
|
||||||
|
}
|
||||||
|
|
||||||
|
;--------------------------------------
|
||||||
|
|
||||||
|
!macro MB_Init2 {
|
||||||
|
lda #0
|
||||||
|
sta nFrameNum+0
|
||||||
|
sta nFrameNum+1
|
||||||
|
sta nFrameNum+2
|
||||||
|
|
||||||
|
; Setup Timer1 IRQ to trigger at 50Hz
|
||||||
|
; Apple CLK = 1.022727 MHz, so set Timer1=0x4fe7
|
||||||
|
|
||||||
|
sei
|
||||||
|
|
||||||
|
lda #$e7
|
||||||
|
ldy #SY6522_TIMER1L_COUNTER
|
||||||
|
sta (MBBase),y
|
||||||
|
lda #$4f
|
||||||
|
ldy #SY6522_TIMER1H_COUNTER
|
||||||
|
sta (MBBase),y
|
||||||
|
|
||||||
|
lda #1<<6
|
||||||
|
ldy #SY6522_ACR
|
||||||
|
sta (MBBase),y ; Free running timer
|
||||||
|
|
||||||
|
lda #1<<7 | 1<<6
|
||||||
|
ldy #SY6522_IER
|
||||||
|
sta (MBBase),y ; Enable Timer1 IRQ
|
||||||
|
|
||||||
|
lda #<Interrupt ; ADDR_L
|
||||||
|
sta IRQL
|
||||||
|
lda #>Interrupt ; ADDR_H
|
||||||
|
sta IRQH
|
||||||
|
|
||||||
|
InitExit:
|
||||||
|
cli
|
||||||
|
|
||||||
|
+SaveRegs Z80Block
|
||||||
|
|
||||||
|
InitExit2:
|
||||||
|
+RestoreRegs ZPBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
;--------------------------------------
|
||||||
|
|
||||||
|
!macro SF_UpdateAY {
|
||||||
|
|
||||||
|
; Skyfox's routine to update AY regs:
|
||||||
|
|
||||||
|
SF_SelectReg:
|
||||||
|
MBx1: sta CARD_BASE+SY6522_ORA,x
|
||||||
|
lda #AY_LATCH
|
||||||
|
bne .l675e
|
||||||
|
|
||||||
|
SF_WriteReg:
|
||||||
|
MBx2: sta CARD_BASE+SY6522_ORA,x
|
||||||
|
lda #AY_WRITE
|
||||||
|
bne .l675e
|
||||||
|
|
||||||
|
SF_ChipReset:
|
||||||
|
lda #AY_RESET
|
||||||
|
|
||||||
|
.l675e:
|
||||||
|
MBx3: sta CARD_BASE+SY6522_ORB,x
|
||||||
|
lda #AY_INACTIVE
|
||||||
|
MBx4: sta CARD_BASE+SY6522_ORB,x
|
||||||
|
rts
|
||||||
|
}
|
||||||
|
|
||||||
|
;--------------------------------------
|
||||||
|
|
||||||
|
!if SPECTRUM128_STEREO {
|
||||||
|
; L = A+Bx0.5, R = C'+B'x0.5
|
||||||
|
;
|
||||||
|
|
||||||
|
!macro MB_WriteAYRegs .ay_regs_base {
|
||||||
|
; Enable SLOTXROM while accessing MB regs
|
||||||
|
lda SW_SLOTXROM_R
|
||||||
|
pha
|
||||||
|
bpl .ay_init ; branch if b7=0 (enabled)
|
||||||
|
sta SW_SLOTXROM_ENA
|
||||||
|
|
||||||
|
.ay_init:
|
||||||
|
ldy #$0D
|
||||||
|
|
||||||
|
.ay_loop:
|
||||||
|
lda .ay_regs_base,y
|
||||||
|
|
||||||
|
cpy #AY_ENABLE
|
||||||
|
bne .ay_cont
|
||||||
|
|
||||||
|
tax ; Save AY_ENABLE
|
||||||
|
ora #AY_DIS_C
|
||||||
|
sta .ay0_regs,y
|
||||||
|
|
||||||
|
txa ; Restore AY_ENABLE
|
||||||
|
ora #AY_DIS_A
|
||||||
|
sta .ay1_regs,y
|
||||||
|
|
||||||
|
dey
|
||||||
|
bpl .ay_loop ; branch always taken
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
.ay_cont:
|
||||||
|
sta .ay0_regs,y
|
||||||
|
sta .ay1_regs,y
|
||||||
|
dey
|
||||||
|
bpl .ay_loop
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
; Post processing
|
||||||
|
AYPostProc:
|
||||||
|
lda #0
|
||||||
|
sta .ay1_regs+AY_AVOL
|
||||||
|
sta .ay0_regs+AY_CVOL
|
||||||
|
|
||||||
|
;
|
||||||
|
; Attenuate AVOL
|
||||||
|
|
||||||
|
lda .ay_regs_base+AY_AVOL
|
||||||
|
sec
|
||||||
|
sbc nAttA
|
||||||
|
bpl .ay_set_a_vol
|
||||||
|
lda #0
|
||||||
|
.ay_set_a_vol
|
||||||
|
sta .ay0_regs+AY_AVOL
|
||||||
|
|
||||||
|
;
|
||||||
|
; Attenuate BVOL
|
||||||
|
|
||||||
|
lda .ay_regs_base+AY_BVOL
|
||||||
|
sec
|
||||||
|
sbc nAttB
|
||||||
|
bpl .ay_set_b_vol
|
||||||
|
lda #0
|
||||||
|
.ay_set_b_vol
|
||||||
|
sta .ay0_regs+AY_BVOL
|
||||||
|
sta .ay1_regs+AY_BVOL
|
||||||
|
|
||||||
|
;
|
||||||
|
; Attenuate CVOL
|
||||||
|
|
||||||
|
lda .ay_regs_base+AY_CVOL
|
||||||
|
sec
|
||||||
|
sbc nAttC
|
||||||
|
bpl .ay_set_c_vol
|
||||||
|
lda #0
|
||||||
|
.ay_set_c_vol
|
||||||
|
sta .ay1_regs+AY_CVOL
|
||||||
|
|
||||||
|
;
|
||||||
|
; User disable A/B/C
|
||||||
|
|
||||||
|
.ay_chk_maska:
|
||||||
|
lda nMaskA
|
||||||
|
beq .ay_chk_maskb
|
||||||
|
lda #0
|
||||||
|
sta .ay0_regs+AY_AVOL
|
||||||
|
|
||||||
|
lda .ay0_regs+AY_ENABLE
|
||||||
|
ora #AY_DIS_A
|
||||||
|
sta .ay0_regs+AY_ENABLE
|
||||||
|
|
||||||
|
.ay_chk_maskb:
|
||||||
|
lda nMaskB
|
||||||
|
beq .ay_chk_maskc
|
||||||
|
lda #0
|
||||||
|
sta .ay0_regs+AY_BVOL
|
||||||
|
sta .ay1_regs+AY_BVOL
|
||||||
|
|
||||||
|
lda .ay0_regs+AY_ENABLE
|
||||||
|
ora #AY_DIS_B
|
||||||
|
sta .ay0_regs+AY_ENABLE
|
||||||
|
|
||||||
|
lda .ay1_regs+AY_ENABLE
|
||||||
|
ora #AY_DIS_B
|
||||||
|
sta .ay1_regs+AY_ENABLE
|
||||||
|
|
||||||
|
.ay_chk_maskc:
|
||||||
|
lda nMaskC
|
||||||
|
beq .ay_chk_mask_done
|
||||||
|
lda #0
|
||||||
|
sta .ay1_regs+AY_CVOL
|
||||||
|
|
||||||
|
lda .ay1_regs+AY_ENABLE
|
||||||
|
ora #AY_DIS_C
|
||||||
|
sta .ay1_regs+AY_ENABLE
|
||||||
|
|
||||||
|
.ay_chk_mask_done:
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
ldx #0
|
||||||
|
ldy #$0D
|
||||||
|
.sf_loop0: tya
|
||||||
|
jsr SF_SelectReg
|
||||||
|
lda .ay0_regs,y
|
||||||
|
jsr SF_WriteReg
|
||||||
|
dey
|
||||||
|
bpl .sf_loop0
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
ldx #$80
|
||||||
|
ldy #$0D
|
||||||
|
.sf_loop1: tya
|
||||||
|
jsr SF_SelectReg
|
||||||
|
lda .ay1_regs,y
|
||||||
|
jsr SF_WriteReg
|
||||||
|
dey
|
||||||
|
bpl .sf_loop1
|
||||||
|
|
||||||
|
; Disable SLOTXROM if necessary
|
||||||
|
pla
|
||||||
|
bpl .ay_done ; branch if b7=0 (enabled)
|
||||||
|
sta SW_SLOTXROM_DIS
|
||||||
|
bmi .ay_done ; branch always taken
|
||||||
|
|
||||||
|
;--------------
|
||||||
|
|
||||||
|
.ay0_regs: !fill 14,0
|
||||||
|
.ay1_regs: !fill 14,0
|
||||||
|
|
||||||
|
+SF_UpdateAY
|
||||||
|
|
||||||
|
;--------------
|
||||||
|
|
||||||
|
.ay_done:
|
||||||
|
|
||||||
|
}
|
||||||
|
} ; !if SPECTRUM128_STEREO
|
||||||
|
|
||||||
|
;------------------
|
||||||
|
|
||||||
|
!if DUAL_MONO {
|
||||||
|
; L = A+B+C, R=A'+B'+C'
|
||||||
|
;
|
||||||
|
|
||||||
|
!macro MB_WriteAYRegs .ay_regs_base {
|
||||||
|
; Enable SLOTXROM while accessing MB regs
|
||||||
|
lda SW_SLOTXROM_R
|
||||||
|
pha
|
||||||
|
bpl .ay_init ; branch if b7=0 (enabled)
|
||||||
|
sta SW_SLOTXROM_ENA
|
||||||
|
|
||||||
|
.ay_init:
|
||||||
|
ldy #$0D
|
||||||
|
lda #<.ay_regs_base
|
||||||
|
sta TmpL
|
||||||
|
lda #>.ay_regs_base
|
||||||
|
sta TmpH
|
||||||
|
|
||||||
|
.ay_loop:
|
||||||
|
; Select AY reg
|
||||||
|
MB1: sty CARD_BASE+SY6522_ORA
|
||||||
|
MB1b: sty CARD_BASE+SY6522_ORA+$80
|
||||||
|
lda #$07 ; LATCH
|
||||||
|
MB2: sta CARD_BASE+SY6522_ORB
|
||||||
|
MB2b: sta CARD_BASE+SY6522_ORB+$80
|
||||||
|
lda #$04 ; INACTIVE
|
||||||
|
MB3: sta CARD_BASE+SY6522_ORB
|
||||||
|
MB3b: sta CARD_BASE+SY6522_ORB+$80
|
||||||
|
|
||||||
|
; Write AY reg
|
||||||
|
lda (TmpL),y
|
||||||
|
MB4: sta CARD_BASE+SY6522_ORA
|
||||||
|
MB4b: sta CARD_BASE+SY6522_ORA+$80
|
||||||
|
lda #$06 ; WRITE
|
||||||
|
MB5: sta CARD_BASE+SY6522_ORB
|
||||||
|
MB5b: sta CARD_BASE+SY6522_ORB+$80
|
||||||
|
lda #$04 ; INACTIVE
|
||||||
|
MB6: sta CARD_BASE+SY6522_ORB
|
||||||
|
MB6b: sta CARD_BASE+SY6522_ORB+$80
|
||||||
|
|
||||||
|
dey
|
||||||
|
bpl .ay_loop
|
||||||
|
|
||||||
|
; Disable SLOTXROM if necessary
|
||||||
|
pla
|
||||||
|
bpl .ay_done ; branch if b7=0 (enabled)
|
||||||
|
sta SW_SLOTXROM_DIS
|
||||||
|
|
||||||
|
.ay_done:
|
||||||
|
}
|
||||||
|
} ; !if DUAL_MONO
|
||||||
|
|
||||||
|
;--------------------------------------
|
||||||
|
|
||||||
|
!macro MB_ISR .isr_main {
|
||||||
|
; Pre:
|
||||||
|
; 6502 has pushed P
|
||||||
|
; Apple ROM has stored A to $45 (not Apple //e ROM!)
|
||||||
|
;
|
||||||
|
|
||||||
|
txa
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
pha
|
||||||
|
|
||||||
|
+SaveRegs ZPBlock
|
||||||
|
+RestoreRegs Z80Block
|
||||||
|
|
||||||
|
jsr .isr_main
|
||||||
|
|
||||||
|
+SaveRegs Z80Block
|
||||||
|
+RestoreRegs ZPBlock
|
||||||
|
|
||||||
|
lda #1<<6
|
||||||
|
MB7:
|
||||||
|
sta CARD_BASE+SY6522_IFR ; Clear Timer1 IRQ flag
|
||||||
|
|
||||||
|
pla
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
tax
|
||||||
|
|
||||||
|
lda $45
|
||||||
|
rti
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
; Mockingboard defines:
|
||||||
|
|
||||||
|
CARD_BASE = $C100
|
||||||
|
|
||||||
|
SY6522_ORB = 0
|
||||||
|
SY6522_ORA = 1
|
||||||
|
SY6522_DDRB = 2
|
||||||
|
SY6522_DDRA = 3
|
||||||
|
SY6522_TIMER1L_COUNTER = 4
|
||||||
|
SY6522_TIMER1H_COUNTER = 5
|
||||||
|
SY6522_TIMER2L_COUNTER = 8
|
||||||
|
SY6522_TIMER2H_COUNTER = 9
|
||||||
|
SY6522_ACR = $B
|
||||||
|
SY6522_IFR = $D
|
||||||
|
SY6522_IER = $E
|
||||||
|
|
||||||
|
AY_AFINE = 0
|
||||||
|
AY_ACOARSE = 1
|
||||||
|
AY_BFINE = 2
|
||||||
|
AY_BCOARSE = 3
|
||||||
|
AY_CFINE = 4
|
||||||
|
AY_CCOARSE = 5
|
||||||
|
AY_NOISEPER = 6
|
||||||
|
AY_ENABLE = 7
|
||||||
|
AY_AVOL = 8
|
||||||
|
AY_BVOL = 9
|
||||||
|
AY_CVOL = 10
|
||||||
|
AY_EFINE = 11
|
||||||
|
AY_ECOARSE = 12
|
||||||
|
AY_ESHAPE = 13
|
||||||
|
|
||||||
|
AY_ENA_A = %110110 ; Enable A (Noise & Tone)
|
||||||
|
AY_ENA_B = %101101 ; Enable B (Noise & Tone)
|
||||||
|
AY_ENA_C = %011011 ; Enable C (Noise & Tone)
|
||||||
|
|
||||||
|
AY_DIS_A = %001001 ; Disable A (Noise & Tone)
|
||||||
|
AY_DIS_B = %010010 ; Disable B (Noise & Tone)
|
||||||
|
AY_DIS_C = %100100 ; Disable C (Noise & Tone)
|
||||||
|
|
||||||
|
; AY inputs on BDIR|BC2|BC1:
|
||||||
|
AY_RESET = 0
|
||||||
|
AY_INACTIVE = 4
|
||||||
|
AY_READ = 5
|
||||||
|
AY_WRITE = 6
|
||||||
|
AY_LATCH = 7
|
|
@ -0,0 +1,260 @@
|
||||||
|
; 6502 -> Z80 macros
|
||||||
|
;
|
||||||
|
|
||||||
|
; C flag differences:
|
||||||
|
;
|
||||||
|
; A=1, A=2, A=3,
|
||||||
|
; CMP #2: CMP #2: CMP #2:
|
||||||
|
; SCZ SCZ SCZ
|
||||||
|
; 6502: 100 011 010
|
||||||
|
; Z80: 110 001 000
|
||||||
|
;
|
||||||
|
; So 6502_C = !(Z80_C)
|
||||||
|
;
|
||||||
|
|
||||||
|
; ld c,a
|
||||||
|
!macro LD .dst, .reg {
|
||||||
|
lda .reg
|
||||||
|
sta .dst
|
||||||
|
}
|
||||||
|
|
||||||
|
; ld c,80h
|
||||||
|
!macro LD_REG_IMM .dst, .src {
|
||||||
|
lda #.src
|
||||||
|
sta .dst
|
||||||
|
}
|
||||||
|
|
||||||
|
; ld ix,lf214
|
||||||
|
; ld hl,0008h
|
||||||
|
!macro LDW .dst, .const {
|
||||||
|
lda #<.const ; LSB
|
||||||
|
sta .dst
|
||||||
|
lda #>.const ; MSB
|
||||||
|
sta .dst + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
; ld a,(hl)
|
||||||
|
!macro LD_REG_INDIRECT .dst, .reg16 {
|
||||||
|
ldx #0
|
||||||
|
lda (.reg16,x)
|
||||||
|
sta .dst
|
||||||
|
}
|
||||||
|
|
||||||
|
; ld (0f1fch),a
|
||||||
|
!macro LD_INDIRECT_ABS .dst, .reg {
|
||||||
|
lda .reg
|
||||||
|
sta .dst
|
||||||
|
}
|
||||||
|
|
||||||
|
; ld a,(0f1fdh)
|
||||||
|
!macro LD_REG_INDIRECT_ABS .reg, .dst {
|
||||||
|
lda .dst
|
||||||
|
sta .reg
|
||||||
|
}
|
||||||
|
|
||||||
|
; ld (ix+19h),a
|
||||||
|
!macro LD_INDIRECT_OFFSET .reg16, .offset, .src {
|
||||||
|
ldy #.offset
|
||||||
|
lda .src
|
||||||
|
sta (.reg16),y
|
||||||
|
}
|
||||||
|
|
||||||
|
; ld (ix+19h),01h
|
||||||
|
!macro LD_INDIRECT_OFFSET_IMM .reg16, .offset, .src {
|
||||||
|
ldy #.offset
|
||||||
|
lda #.src
|
||||||
|
sta (.reg16),y
|
||||||
|
}
|
||||||
|
|
||||||
|
; ld h,(ix+15h)
|
||||||
|
!macro LD_REG_INDIRECT_OFFSET .dst, .reg16, .offset {
|
||||||
|
ldy #.offset
|
||||||
|
lda (.reg16),y
|
||||||
|
sta .dst
|
||||||
|
}
|
||||||
|
|
||||||
|
; ld (hl),a
|
||||||
|
!macro LD_INDIRECT .reg16, .src {
|
||||||
|
ldx #0
|
||||||
|
lda .src
|
||||||
|
sta (.reg16,x)
|
||||||
|
}
|
||||||
|
|
||||||
|
; ld (hl),00h
|
||||||
|
!macro LD_INDIRECT_IMM .reg16, .src {
|
||||||
|
ldx #0
|
||||||
|
lda #.src
|
||||||
|
sta (.reg16,x)
|
||||||
|
}
|
||||||
|
|
||||||
|
; ld hl,(0f1f6h)
|
||||||
|
; ld (0f1f6h),hl
|
||||||
|
!macro LDW_INDIRECT .dst, .src {
|
||||||
|
lda .src
|
||||||
|
sta .dst
|
||||||
|
lda .src+1
|
||||||
|
sta .dst+1
|
||||||
|
}
|
||||||
|
|
||||||
|
; inc hl
|
||||||
|
!macro INCW .reg16 {
|
||||||
|
inc .reg16
|
||||||
|
bne .j
|
||||||
|
inc .reg16 + 1
|
||||||
|
.j
|
||||||
|
}
|
||||||
|
|
||||||
|
; inc (hl)
|
||||||
|
!macro INC_INDIRECT .reg16 {
|
||||||
|
ldx #0
|
||||||
|
lda (.reg16,x)
|
||||||
|
tay ; No inca for 6502! (65C02 only)
|
||||||
|
iny
|
||||||
|
tya
|
||||||
|
sta (.reg16,x)
|
||||||
|
}
|
||||||
|
|
||||||
|
; dec hl
|
||||||
|
!macro DECW .reg16 {
|
||||||
|
dec .reg16
|
||||||
|
lda .reg16
|
||||||
|
cmp #$ff
|
||||||
|
bne .j
|
||||||
|
dec .reg16 + 1
|
||||||
|
.j
|
||||||
|
}
|
||||||
|
|
||||||
|
; dec (ix+11h)
|
||||||
|
!macro DEC_INDIRECT_OFFSET .reg, .offset {
|
||||||
|
ldy #.offset
|
||||||
|
lda (.reg),y
|
||||||
|
tax ; No deca for 6502! (65C02 only)
|
||||||
|
dex
|
||||||
|
txa
|
||||||
|
sta (.reg),y
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro JP_Z .label {
|
||||||
|
bne .j
|
||||||
|
jmp .label
|
||||||
|
.j
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro JP_NZ .label {
|
||||||
|
beq .j
|
||||||
|
jmp .label
|
||||||
|
.j
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro JP_C .label {
|
||||||
|
bcs .j ; bcs, as C flag is inverted
|
||||||
|
jmp .label
|
||||||
|
.j
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro JP_NC .label {
|
||||||
|
bcc .j ; bcc, as C flag is inverted
|
||||||
|
jmp .label
|
||||||
|
.j
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro RET_Z {
|
||||||
|
bne .j
|
||||||
|
rts
|
||||||
|
.j
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro RET_NZ {
|
||||||
|
beq .j
|
||||||
|
rts
|
||||||
|
.j
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro RET_C {
|
||||||
|
bcs .j ; bcs, as C flag is inverted
|
||||||
|
rts
|
||||||
|
.j
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro RET_NC {
|
||||||
|
bcc .j ; bcc, as C flag is inverted
|
||||||
|
rts
|
||||||
|
.j
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro PUSH16 .reg16 {
|
||||||
|
lda .reg16
|
||||||
|
pha
|
||||||
|
lda .reg16+1
|
||||||
|
pha
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro POP16 .reg16 {
|
||||||
|
pla
|
||||||
|
sta .reg16+1
|
||||||
|
pla
|
||||||
|
sta .reg16
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro ADDW .dst, .src {
|
||||||
|
clc
|
||||||
|
lda .dst
|
||||||
|
adc .src
|
||||||
|
sta .dst
|
||||||
|
lda .dst+1
|
||||||
|
adc .src+1
|
||||||
|
sta .dst+1
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro INVERT_CARRY {
|
||||||
|
php
|
||||||
|
pla
|
||||||
|
eor #$01 ; Invert C
|
||||||
|
pha
|
||||||
|
plp
|
||||||
|
}
|
||||||
|
|
||||||
|
; Pre: Ensure that CARRY is setup correctly
|
||||||
|
!macro SBCW .dst, .src {
|
||||||
|
lda .dst
|
||||||
|
sbc .src
|
||||||
|
sta .dst
|
||||||
|
lda .dst+1
|
||||||
|
sbc .src+1
|
||||||
|
sta .dst+1
|
||||||
|
}
|
||||||
|
|
||||||
|
; sub (hl)
|
||||||
|
!macro SUB_INDIRECT .reg16 {
|
||||||
|
ldx #0
|
||||||
|
sec
|
||||||
|
sbc (.reg16,x)
|
||||||
|
sta RegA
|
||||||
|
}
|
||||||
|
|
||||||
|
; ldi
|
||||||
|
; . (de++) <- (hl++)
|
||||||
|
; . bc--
|
||||||
|
!macro LDI {
|
||||||
|
ldx #0
|
||||||
|
lda (RegHL,x)
|
||||||
|
sta (RegDE,x)
|
||||||
|
inc RegL
|
||||||
|
bne .a
|
||||||
|
inc RegH
|
||||||
|
.a
|
||||||
|
inc RegE
|
||||||
|
bne .b
|
||||||
|
inc RegD
|
||||||
|
.b
|
||||||
|
dec RegBC
|
||||||
|
bne .j
|
||||||
|
dec RegBC+1
|
||||||
|
.j
|
||||||
|
}
|
||||||
|
|
||||||
|
; cp (ix+10h)
|
||||||
|
!macro CP_INDIRECT_OFFSET .reg, .offset {
|
||||||
|
ldy #.offset
|
||||||
|
cmp (.reg),y
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
RegA = $F0
|
||||||
|
RegF = RegA+1 ; Not used
|
||||||
|
RegBC = RegA+2 ; WORD
|
||||||
|
RegC = RegA+2
|
||||||
|
RegB = RegA+3
|
||||||
|
RegDE = RegA+4 ; WORD
|
||||||
|
RegE = RegA+4
|
||||||
|
RegD = RegA+5
|
||||||
|
RegHL = RegA+6 ; WORD
|
||||||
|
RegL = RegA+6
|
||||||
|
RegH = RegA+7
|
||||||
|
RegIX = RegA+8 ; WORD
|
||||||
|
|
||||||
|
TmpHL = RegA+10
|
||||||
|
TmpL = TmpHL
|
||||||
|
TmpH = TmpHL+1
|
||||||
|
|
||||||
|
MBBase = TmpHL+2 ; Mockingboard base (only used in INIT - not in INTERRUPT)
|
||||||
|
MBBaseL = MBBase
|
||||||
|
MBBaseH = MBBase+1
|
||||||
|
|
||||||
|
ZPSize = MBBaseH - RegA + 1
|
||||||
|
|
||||||
|
;--------------------------------------
|
||||||
|
|
||||||
|
!macro SaveRegs .block {
|
||||||
|
!set ZP = RegA
|
||||||
|
!do while ZP <= TmpH {
|
||||||
|
lda ZP
|
||||||
|
sta .block + ZP - RegA
|
||||||
|
!set ZP = ZP + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
!macro RestoreRegs .block {
|
||||||
|
!set ZP = RegA
|
||||||
|
!do while ZP <= TmpH {
|
||||||
|
lda .block + ZP - RegA
|
||||||
|
sta ZP
|
||||||
|
!set ZP = ZP + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,594 @@
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
Z80Regs8 = ('a','b','c','d','e','f','h','l')
|
||||||
|
Z80Regs16 = ('af', 'bc', 'de', 'hl', 'ix')
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
|
||||||
|
def GetLine(f):
|
||||||
|
szLine = f.readline()
|
||||||
|
while szLine != '':
|
||||||
|
if szLine[0] != ';':
|
||||||
|
return szLine
|
||||||
|
szLine = f.readline()
|
||||||
|
return ''
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def GetConst(S):
|
||||||
|
const = S
|
||||||
|
if re.match('\d', const): # 1st char is [0..9]
|
||||||
|
if const[len(const)-1] == 'h':
|
||||||
|
const = const[:len(const)-1] # Strip last char
|
||||||
|
|
||||||
|
if len(const) > 4 and const[0] == '0':
|
||||||
|
const = const[1:] # Strip leading zero
|
||||||
|
|
||||||
|
if len(const) > 2 and const[0] == '0':
|
||||||
|
const = const[1:] # Strip leading zero
|
||||||
|
|
||||||
|
const = '$' + const
|
||||||
|
|
||||||
|
return const
|
||||||
|
|
||||||
|
#--------------------------------------
|
||||||
|
|
||||||
|
# S = 0008h -> $0008
|
||||||
|
# S = label -> label
|
||||||
|
# S = labeh -> labeh
|
||||||
|
def GetAddr16(S):
|
||||||
|
return GetConst(S)
|
||||||
|
|
||||||
|
#--------------------------------------
|
||||||
|
|
||||||
|
# S = (0008h) -> $0008
|
||||||
|
# S = (label) -> label
|
||||||
|
# S = (labeh) -> labeh
|
||||||
|
def GetIndirectAddr16(S):
|
||||||
|
res = S[:len(S)-1] # Strip last char
|
||||||
|
res = res[1:] # Strip 1st char
|
||||||
|
return GetConst(res)
|
||||||
|
|
||||||
|
#--------------------------------------
|
||||||
|
|
||||||
|
def GetRegOffset(S):
|
||||||
|
reg = S[1:3] # Middle 2 chars
|
||||||
|
if reg not in Z80Regs16:
|
||||||
|
print 'Illegal Z80 reg: ' + reg
|
||||||
|
return ('', '')
|
||||||
|
|
||||||
|
offset = S[4:] # Strip 1st 4 chars: '(ix+'
|
||||||
|
offset = offset[:len(offset)-1] # Strip last char: ')'
|
||||||
|
|
||||||
|
if offset[len(offset)-1] == 'h':
|
||||||
|
offset = offset[:len(offset)-1] # Strip last char: 'h'
|
||||||
|
offset = '$' + offset
|
||||||
|
|
||||||
|
return reg, offset
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
|
||||||
|
def adc(S):
|
||||||
|
|
||||||
|
if S[2] in Z80Regs8 and S[3] not in Z80Regs8:
|
||||||
|
const = GetConst(S[3])
|
||||||
|
print '\tlda\t' + 'Reg' + S[2].upper()
|
||||||
|
print '\tadc\t' + '#' + const
|
||||||
|
print '\tsta\t' + 'Reg' + S[2].upper()
|
||||||
|
|
||||||
|
else:
|
||||||
|
print 'ADC error: unsupported addressing mode: ' + S[2], S[3]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def add(S):
|
||||||
|
|
||||||
|
ind2 = S[3][0] == '(' # S[3] is indirect
|
||||||
|
|
||||||
|
m2plus = re.search('\+', S[3])
|
||||||
|
|
||||||
|
if S[2] in Z80Regs8 and S[3] in Z80Regs8:
|
||||||
|
# add a,a
|
||||||
|
if S[2] == S[3]:
|
||||||
|
print '\tlda\t' + 'Reg' + S[2].upper()
|
||||||
|
print '\tasl\t'
|
||||||
|
print '\tsta\t' + 'Reg' + S[2].upper()
|
||||||
|
else:
|
||||||
|
print '\tclc\t'
|
||||||
|
print '\tlda\t' + 'Reg' + S[2].upper()
|
||||||
|
print '\tadc\t' + 'Reg' + S[3].upper()
|
||||||
|
print '\tsta\t' + 'Reg' + S[2].upper()
|
||||||
|
|
||||||
|
elif S[2] in Z80Regs8 and ind2 and m2plus:
|
||||||
|
# add a,(ix+18h)
|
||||||
|
reg, offset = GetRegOffset(S[3])
|
||||||
|
print '\tclc\t' + '\t; CARRY possibly wrong'
|
||||||
|
print '\tldy\t' + '#' + offset
|
||||||
|
print '\tlda\t' + 'Reg' + S[2].upper()
|
||||||
|
print '\tadc\t' + '(Reg' + reg.upper() + '),y'
|
||||||
|
print '\tsta\t' + 'Reg' + S[2].upper()
|
||||||
|
|
||||||
|
elif S[2] in Z80Regs8:
|
||||||
|
# add a,05h
|
||||||
|
const = GetConst(S[3])
|
||||||
|
print '\tclc\t' + '\t; CARRY possibly wrong'
|
||||||
|
print '\tlda\t' + 'Reg' + S[2].upper()
|
||||||
|
print '\tadc\t' + '#' + const
|
||||||
|
print '\tsta\t' + 'Reg' + S[2].upper()
|
||||||
|
|
||||||
|
elif S[2] in Z80Regs16 and S[3] in Z80Regs16:
|
||||||
|
# add hl,bc
|
||||||
|
print '\t+ADDW\t' + 'Reg' + S[2].upper() + ', ' + 'Reg' + S[3].upper()
|
||||||
|
|
||||||
|
else:
|
||||||
|
print 'ADD error: unsupported addressing mode: ' + S[2], S[3]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _and(S):
|
||||||
|
|
||||||
|
ind2 = S[2][0] == '(' # S[2] is indirect
|
||||||
|
|
||||||
|
m2plus = re.search('\+', S[2])
|
||||||
|
|
||||||
|
if S[2] in Z80Regs8:
|
||||||
|
# and e
|
||||||
|
print '\tand\t' + 'Reg' + S[2].upper()
|
||||||
|
|
||||||
|
elif ind2 and m2plus:
|
||||||
|
# and (ix+1bh)
|
||||||
|
reg, offset = GetRegOffset(S[2])
|
||||||
|
print '\tldy\t' + '#' + offset
|
||||||
|
print '\tand\t' + '(Reg' + reg.upper() + '),y'
|
||||||
|
|
||||||
|
else:
|
||||||
|
# and 3fh
|
||||||
|
const = GetConst(S[2])
|
||||||
|
print '\tand\t' + '#' + const
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def cp(S):
|
||||||
|
|
||||||
|
ind2 = S[2][0] == '(' # S[2] is indirect
|
||||||
|
|
||||||
|
m2plus = re.search('\+', S[2])
|
||||||
|
|
||||||
|
if ind2 and m2plus:
|
||||||
|
# cp (ix+10h)
|
||||||
|
reg, offset = GetRegOffset(S[2])
|
||||||
|
print '\t+CP_INDIRECT_OFFSET\t' + 'Reg' + reg.upper() + ', ' + offset
|
||||||
|
|
||||||
|
elif not ind2:
|
||||||
|
const = GetConst(S[2])
|
||||||
|
print '\tcmp\t' + '#' + const
|
||||||
|
|
||||||
|
else:
|
||||||
|
print 'CP error: unsupported addressing mode: ' + S[2]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def dec(S):
|
||||||
|
|
||||||
|
ind2 = S[2][0] == '(' # S[2] is indirect
|
||||||
|
|
||||||
|
m2plus = re.search('\+', S[2])
|
||||||
|
|
||||||
|
if S[2] in Z80Regs8:
|
||||||
|
print '\tdec\t' + 'Reg' + S[2].upper()
|
||||||
|
|
||||||
|
elif S[2] in Z80Regs16:
|
||||||
|
# dec hl
|
||||||
|
reg = GetAddr16(S[2])
|
||||||
|
print '\t+DECW\t' + 'Reg' + reg.upper()
|
||||||
|
|
||||||
|
elif ind2 and m2plus:
|
||||||
|
# dec (ix+11h)
|
||||||
|
reg, offset = GetRegOffset(S[2])
|
||||||
|
if reg == '':
|
||||||
|
return
|
||||||
|
print '\t+DEC_INDIRECT_OFFSET\t' + 'Reg' + reg.upper() + ', ' + offset
|
||||||
|
|
||||||
|
else:
|
||||||
|
print ';DEC error: unsupported addressing mode: ' + S[2]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def inc(S):
|
||||||
|
|
||||||
|
ind2 = S[2][0] == '(' # S[2] is indirect
|
||||||
|
|
||||||
|
if S[2] in Z80Regs8:
|
||||||
|
print '\tinc\t' + 'Reg' + S[2].upper()
|
||||||
|
return
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
if ind2:
|
||||||
|
# inc (hl)
|
||||||
|
reg = GetIndirectAddr16(S[2])
|
||||||
|
|
||||||
|
else:
|
||||||
|
# inc hl
|
||||||
|
reg = GetAddr16(S[2])
|
||||||
|
|
||||||
|
if reg not in Z80Regs16:
|
||||||
|
print 'INC error: Illegal Z80 reg: ' + reg
|
||||||
|
elif ind2:
|
||||||
|
print '\t+INC_INDIRECT\t' + 'Reg' + reg.upper()
|
||||||
|
else:
|
||||||
|
print '\t+INCW\t' + 'Reg' + reg.upper()
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def jp(S):
|
||||||
|
|
||||||
|
if S[3] == '':
|
||||||
|
print '\tjmp\t' + S[2]
|
||||||
|
return
|
||||||
|
|
||||||
|
if S[2] == 'z':
|
||||||
|
print '\t+JP_Z\t' + S[3]
|
||||||
|
elif S[2] == 'nz':
|
||||||
|
print '\t+JP_NZ\t' + S[3]
|
||||||
|
elif S[2] == 'c':
|
||||||
|
print '\t+JP_C\t' + S[3]
|
||||||
|
elif S[2] == 'nc':
|
||||||
|
print '\t+JP_NC\t' + S[3]
|
||||||
|
else:
|
||||||
|
print ';JP error: unsupported cc: ' + S[2]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def jr(S):
|
||||||
|
|
||||||
|
if S[3] == '':
|
||||||
|
print '\tjmp\t' + S[2]
|
||||||
|
return
|
||||||
|
|
||||||
|
if S[2] == 'z':
|
||||||
|
print '\tbeq\t' + S[3]
|
||||||
|
elif S[2] == 'nz':
|
||||||
|
print '\tbne\t' + S[3]
|
||||||
|
elif S[2] == 'c':
|
||||||
|
print '\tbcc\t' + S[3] # C inverted
|
||||||
|
elif S[2] == 'nc':
|
||||||
|
print '\tbcs\t' + S[3] # C inverted
|
||||||
|
else:
|
||||||
|
print 'JR error: unsupported cc: ' + S[2]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def ld(S):
|
||||||
|
|
||||||
|
m2 = S[2] in Z80Regs8
|
||||||
|
m3 = S[3] in Z80Regs8
|
||||||
|
|
||||||
|
m2plus = re.search('\+', S[2])
|
||||||
|
m3plus = re.search('\+', S[3])
|
||||||
|
|
||||||
|
len2 = len(S[2])
|
||||||
|
len3 = len(S[3])
|
||||||
|
|
||||||
|
ind2 = S[2][0] == '(' # S[2] is indirect
|
||||||
|
ind3 = S[3][0] == '(' # S[3] is indirect
|
||||||
|
|
||||||
|
if m2 and m3:
|
||||||
|
# ld c,a
|
||||||
|
print '\t+LD\t' + 'Reg' + S[2].upper() + ', ' + 'Reg' + S[3].upper()
|
||||||
|
return
|
||||||
|
|
||||||
|
elif m2 and not ind3:
|
||||||
|
# ld c,80h
|
||||||
|
const = GetConst(S[3])
|
||||||
|
print '\t+LD_REG_IMM\t' + 'Reg' + S[2].upper() + ', ' + const
|
||||||
|
return
|
||||||
|
|
||||||
|
elif m2 and ind3 and len3 == len('(xx)'):
|
||||||
|
# ld a,(hl)
|
||||||
|
reg = S[3][1:3] # Middle 2 chars
|
||||||
|
if reg not in Z80Regs16:
|
||||||
|
print 'Illegal Z80 reg: ' + reg
|
||||||
|
return
|
||||||
|
print '\t+LD_REG_INDIRECT\t' + 'Reg' + S[2].upper() + ', ' + 'Reg' + reg.upper()
|
||||||
|
return
|
||||||
|
|
||||||
|
elif m2 and ind3 and m3plus:
|
||||||
|
# ld h,(ix+15h)
|
||||||
|
reg, offset = GetRegOffset(S[3])
|
||||||
|
if reg == '':
|
||||||
|
return
|
||||||
|
print '\t+LD_REG_INDIRECT_OFFSET\t' + 'Reg' + S[2].upper() + ', ' + 'Reg' + reg.upper() + ', ' + offset
|
||||||
|
return
|
||||||
|
|
||||||
|
elif m2 and ind3:
|
||||||
|
# ld a,(0f1fdh)
|
||||||
|
# ld a,(lf1fd)
|
||||||
|
abs = GetIndirectAddr16(S[3])
|
||||||
|
print '\t+LD_REG_INDIRECT_ABS\t' + 'Reg' + S[2].upper() + ', ' + abs
|
||||||
|
return
|
||||||
|
|
||||||
|
elif S[2] in Z80Regs16 and ind3:
|
||||||
|
# ld hl,(lf1f6)
|
||||||
|
# ld hl,(0f1f6h)
|
||||||
|
src = GetIndirectAddr16(S[3])
|
||||||
|
print '\t+LDW_INDIRECT\t' + 'Reg' + S[2].upper() + ', ' + src
|
||||||
|
return
|
||||||
|
|
||||||
|
elif S[2] in Z80Regs16:
|
||||||
|
# ld ix,lf214
|
||||||
|
# ld hl,0008h
|
||||||
|
const = GetAddr16(S[3])
|
||||||
|
print '\t+LDW\t' + 'Reg' + S[2].upper() + ', ' + const
|
||||||
|
return
|
||||||
|
|
||||||
|
elif ind2 and m2plus and m3:
|
||||||
|
# ld (ix+19h),a
|
||||||
|
reg, offset = GetRegOffset(S[2])
|
||||||
|
if reg == '':
|
||||||
|
return
|
||||||
|
print '\t+LD_INDIRECT_OFFSET\t' + 'Reg' + reg.upper() + ', ' + offset + ', ' + 'Reg' + S[3].upper()
|
||||||
|
return
|
||||||
|
|
||||||
|
elif ind2 and m2plus and not m3:
|
||||||
|
# ld (ix+19h),01h
|
||||||
|
reg, offset = GetRegOffset(S[2])
|
||||||
|
if reg == '':
|
||||||
|
return
|
||||||
|
const = GetConst(S[3])
|
||||||
|
print '\t+LD_INDIRECT_OFFSET_IMM\t' + 'Reg' + reg.upper() + ', ' + offset + ', ' + const
|
||||||
|
return
|
||||||
|
|
||||||
|
elif ind2 and m3:
|
||||||
|
# ld (hl),a
|
||||||
|
# ld (0f1fch),a
|
||||||
|
# ld (lf1fc),a
|
||||||
|
Addr16 = GetIndirectAddr16(S[2])
|
||||||
|
if Addr16 in Z80Regs16:
|
||||||
|
print '\t+LD_INDIRECT\t' + 'Reg' + Addr16.upper() + ', ' + 'Reg' + S[3].upper()
|
||||||
|
else:
|
||||||
|
print '\t+LD_INDIRECT_ABS\t' + Addr16 + ', ' + 'Reg' + S[3].upper()
|
||||||
|
return
|
||||||
|
|
||||||
|
elif ind2 and not m3:
|
||||||
|
# ld (hl),00h
|
||||||
|
# ld (0f1f6h),hl
|
||||||
|
# ld (lf1f6h),hl
|
||||||
|
Addr16 = GetIndirectAddr16(S[2])
|
||||||
|
const = GetConst(S[3])
|
||||||
|
if Addr16 in Z80Regs16:
|
||||||
|
print '\t+LD_INDIRECT_IMM\t' + 'Reg' + Addr16.upper() + ', ' + const
|
||||||
|
elif const in Z80Regs16:
|
||||||
|
print '\t+LDW_INDIRECT\t' + Addr16 + ', ' + 'Reg' + const.upper()
|
||||||
|
else:
|
||||||
|
print 'Illegal Z80 reg: ' + Addr16
|
||||||
|
return
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
print 'LD error: No match for ' + S[2], S[3]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def orr(S):
|
||||||
|
|
||||||
|
if S[2] in Z80Regs8:
|
||||||
|
# or l
|
||||||
|
print '\tora\t' + 'Reg' + S[2].upper()
|
||||||
|
else:
|
||||||
|
# or 38h
|
||||||
|
const = GetConst(S[2])
|
||||||
|
print '\tora\t' + '#' + const
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def pop(S):
|
||||||
|
|
||||||
|
if S[2] in Z80Regs16:
|
||||||
|
# pop de
|
||||||
|
print '\t+POP16\t' + 'Reg' + S[2].upper()
|
||||||
|
else:
|
||||||
|
print 'POP error: unsupported addressing mode'
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def push(S):
|
||||||
|
|
||||||
|
if S[2] in Z80Regs16:
|
||||||
|
# push de
|
||||||
|
print '\t+PUSH16\t' + 'Reg' + S[2].upper()
|
||||||
|
else:
|
||||||
|
print 'PUSH error: unsupported addressing mode'
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def sbc(S):
|
||||||
|
|
||||||
|
if S[2] in Z80Regs16 and S[3] in Z80Regs16:
|
||||||
|
# sbc hl,bc
|
||||||
|
print '\t+SBCW\t' + 'Reg' + S[2].upper() + ', ' + 'Reg' + S[3].upper()
|
||||||
|
else:
|
||||||
|
print 'SBC error: unsupported addressing mode'
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def sub(S):
|
||||||
|
|
||||||
|
ind2 = S[2][0] == '(' # S[2] is indirect
|
||||||
|
|
||||||
|
if S[2] in Z80Regs8:
|
||||||
|
# sub l
|
||||||
|
print '\tsbc\t' + 'Reg' + S[2].upper() + '\t; CARRY possibly wrong'
|
||||||
|
|
||||||
|
elif ind2:
|
||||||
|
# sub (hl)
|
||||||
|
reg = GetIndirectAddr16(S[2])
|
||||||
|
if reg not in Z80Regs16:
|
||||||
|
print 'DEC error: Illegal Z80 reg: ' + Addr16
|
||||||
|
return
|
||||||
|
print '\t+SUB_INDIRECT\t' + 'Reg' + reg.upper()
|
||||||
|
|
||||||
|
else:
|
||||||
|
# sub 80h
|
||||||
|
const = GetConst(S[2])
|
||||||
|
print '\tsec\t' + '\t; CARRY possibly wrong'
|
||||||
|
print '\tsbc\t' + '#' + const
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def ret(S):
|
||||||
|
|
||||||
|
if S[2] == '':
|
||||||
|
print '\trts'
|
||||||
|
elif S[2] == 'z':
|
||||||
|
print '\t+RET_Z\t' + S[3]
|
||||||
|
elif S[2] == 'nz':
|
||||||
|
print '\t+RET_NZ\t' + S[3]
|
||||||
|
elif S[2] == 'c':
|
||||||
|
print '\t+RET_C\t' + S[3]
|
||||||
|
elif S[2] == 'nc':
|
||||||
|
print '\t+RET_NC\t' + S[3]
|
||||||
|
else:
|
||||||
|
print ';RET error: unsupported cc: ' + S[2]
|
||||||
|
|
||||||
|
print ''
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def xor(S):
|
||||||
|
|
||||||
|
ind2 = S[2][0] == '(' # S[2] is indirect
|
||||||
|
|
||||||
|
if S[2] in Z80Regs8:
|
||||||
|
print '\t+LD_REG_IMM\t' + 'Reg' + S[2].upper() + ', 0' + '\t; xor ' + S[2]
|
||||||
|
# elif ind2:
|
||||||
|
# reg = GetIndirectAddr16(S[2])
|
||||||
|
else:
|
||||||
|
print 'XOR error: unsupported addressing mode'
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
|
||||||
|
def Process(S):
|
||||||
|
if S[0] != '':
|
||||||
|
print S[0]
|
||||||
|
|
||||||
|
if S[1] == '':
|
||||||
|
return 1 # No opcode
|
||||||
|
elif S[1] == 'nop':
|
||||||
|
return 0 # Assume this is data section
|
||||||
|
|
||||||
|
elif S[1] == 'adc':
|
||||||
|
adc(S)
|
||||||
|
elif S[1] == 'add':
|
||||||
|
add(S)
|
||||||
|
elif S[1] == 'and':
|
||||||
|
_and(S)
|
||||||
|
elif S[1] == 'call':
|
||||||
|
print '\tjsr\t' + S[2]
|
||||||
|
elif S[1] == 'dec':
|
||||||
|
dec(S)
|
||||||
|
elif S[1] == 'cp':
|
||||||
|
cp(S)
|
||||||
|
elif S[1] == 'inc':
|
||||||
|
inc(S)
|
||||||
|
elif S[1] == 'jp':
|
||||||
|
jp(S)
|
||||||
|
elif S[1] == 'jr':
|
||||||
|
jr(S)
|
||||||
|
elif S[1] == 'ld':
|
||||||
|
ld(S)
|
||||||
|
elif S[1] == 'ldi':
|
||||||
|
print '\t+LDI'
|
||||||
|
elif S[1] == 'or':
|
||||||
|
orr(S)
|
||||||
|
elif S[1] == 'pop':
|
||||||
|
pop(S)
|
||||||
|
elif S[1] == 'push':
|
||||||
|
push(S)
|
||||||
|
elif S[1] == 'ret':
|
||||||
|
ret(S)
|
||||||
|
elif S[1] == 'sbc':
|
||||||
|
sbc(S)
|
||||||
|
elif S[1] == 'sub':
|
||||||
|
sub(S)
|
||||||
|
elif S[1] == 'xor':
|
||||||
|
xor(S)
|
||||||
|
else:
|
||||||
|
s = ';\t'
|
||||||
|
for i in range(len(S)):
|
||||||
|
s = s + S[i]
|
||||||
|
s = s + '\t; ** Unknown opcode **'
|
||||||
|
print s
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def hdr_code():
|
||||||
|
print ';ACME 0.85'
|
||||||
|
print ''
|
||||||
|
print '!cpu 6502 ; Compatible with all Apple2\'s'
|
||||||
|
print '!to \"TEST\", plain'
|
||||||
|
print '!sl \"TEST.labels\"'
|
||||||
|
print '*=$6000'
|
||||||
|
print ''
|
||||||
|
print '!source \"..\\Common\\Z80-Macros.a\"'
|
||||||
|
print '!source \"..\\Common\\ZP-Macros.a\"'
|
||||||
|
print '!source \"..\\Common\\AppleDefs.a\"'
|
||||||
|
print '!source \"..\\Common\\MockingboardDefs.a\"'
|
||||||
|
print ''
|
||||||
|
print ';------------------------------------------------------------------------------'
|
||||||
|
print ''
|
||||||
|
print '!zone code'
|
||||||
|
print ''
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def hdr_data():
|
||||||
|
print ''
|
||||||
|
print ';------------------------------------------------------------------------------'
|
||||||
|
print ''
|
||||||
|
print '!zone data'
|
||||||
|
print ''
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def help():
|
||||||
|
print 'ConvertZ80 v1.0.0'
|
||||||
|
print ''
|
||||||
|
print 'Usage: ConvertZ80.py <z80.asm>'
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
help()
|
||||||
|
return
|
||||||
|
|
||||||
|
hFileIn = open(sys.argv[1], 'r')
|
||||||
|
|
||||||
|
#if len(sys.argv) >= 3:
|
||||||
|
# hFileOut = open(sys.argv[2], 'w')
|
||||||
|
|
||||||
|
hdr_code()
|
||||||
|
|
||||||
|
n = 0
|
||||||
|
while n < 1000:
|
||||||
|
szLine = GetLine(hFileIn)
|
||||||
|
if szLine == '':
|
||||||
|
break
|
||||||
|
Split = re.split('\s+|,', szLine)
|
||||||
|
if Process(Split) == 0:
|
||||||
|
break
|
||||||
|
n = n + 1
|
||||||
|
|
||||||
|
hdr_data()
|
||||||
|
|
||||||
|
hFileIn.close()
|
||||||
|
#hFileOut.close()
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
main()
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
||||||
|
D:\Apple][\acme085\acme.exe cybernoid.a
|
||||||
|
D:\Apple][\InsertBIN2AWS\debug\InsertBIN2AWS.exe cybernoid 6000 cybernoid.aws
|
||||||
|
java -jar "D:\Apple][\Apple Commander\ac\AppleCommander-1.3.3.9-ac.jar" -d cybernoid ..\ReleaseDSK\Cybernoid.dsk
|
||||||
|
cat cybernoid | java -jar "D:\Apple][\Apple Commander\ac\AppleCommander-1.3.3.9-ac.jar" -p Cybernoid bin 24576 ..\ReleaseDSK\Cybernoid.dsk
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
||||||
|
D:\Apple][\acme085\acme.exe cybernoid2.a
|
||||||
|
D:\Apple][\InsertBIN2AWS\debug\InsertBIN2AWS.exe cybernoid2 6000 cybernoid2.aws
|
||||||
|
java -jar "D:\Apple][\Apple Commander\ac\AppleCommander-1.3.3.9-ac.jar" -d cybernoid2 ..\ReleaseDSK\Cybernoid.dsk
|
||||||
|
cat cybernoid2 | java -jar "D:\Apple][\Apple Commander\ac\AppleCommander-1.3.3.9-ac.jar" -p Cybernoid2 bin 24576 ..\ReleaseDSK\Cybernoid.dsk
|
|
@ -0,0 +1,45 @@
|
||||||
|
My motivation was to really push the MAME AY8910 emu code, as the Apple
|
||||||
|
Mockingboard music (Skyfox, Ultima) wasn't really exercising it that
|
||||||
|
hard.
|
||||||
|
|
||||||
|
So as a little R&D proj, I thought I'd convert Dave Rogers' Spectrum
|
||||||
|
128 Cybernoid routine (written in 1988).
|
||||||
|
|
||||||
|
Here's a summary of how I did it:
|
||||||
|
. I got cybernoid.ay from
|
||||||
|
http://www.worldofspectrum.org/projectay/gdmusic.htm
|
||||||
|
. Split it into bin's with AYSplitR
|
||||||
|
. Disassembled with Inkland's dz80w (www.inkland.org)
|
||||||
|
. I wrote 6502 macros to replace the z80 opcodes
|
||||||
|
. For cybernoid, I hand converted the z80 code to 6502 (using the
|
||||||
|
macros)
|
||||||
|
. I added a few extension to AppleWin's debugger to help debug the 6502
|
||||||
|
code (ACME symbol loading & ZP pointer support).
|
||||||
|
|
||||||
|
I use Skyfox's MB detection routine.
|
||||||
|
|
||||||
|
The Z80 regs are emulated with zero-page memory locations $F0..$F8
|
||||||
|
The playback routine is very inefficient, as it:
|
||||||
|
. saves the ZP memory
|
||||||
|
. restores the Z80 regs
|
||||||
|
. runs the IRQ handler
|
||||||
|
. saves the Z80 regs
|
||||||
|
. restores the ZP memory
|
||||||
|
|
||||||
|
This allows playback to work simultaneously with Applesoft & ProDOS. If
|
||||||
|
DOS3.3 doesn't disable IRQs around disk I/O, then it won't work on a
|
||||||
|
real Apple (under DOS3.3), but it'll still work on an emulator :-)
|
||||||
|
|
||||||
|
I profiled the IRQ handler and IIRC, it takes about 20% of the frame on
|
||||||
|
average. This is poor, but the code can easily be optimised. Remember
|
||||||
|
this was really just a proof of concept.
|
||||||
|
|
||||||
|
After this, I wrote a python script to do the Z80->6502, and quickly
|
||||||
|
converted Cybernoid-II.
|
||||||
|
|
||||||
|
Currently playback on real h/w produces noisy renditions of the tunes. I
|
||||||
|
have replaced my AY-register update routine with the slower one used by Skyfox
|
||||||
|
which gives better but not perfect results.
|
||||||
|
|
||||||
|
Tom
|
||||||
|
25 March 2006
|
|
@ -0,0 +1,6 @@
|
||||||
|
v5: 02/04/2006 - Z80 Emu: Fixed SBCW bug that affected portamento
|
||||||
|
UI: Added timer (M:S:F) & control over enable & volume
|
||||||
|
v4: 18/03/2006 - Disable Timer1 at INIT entrypoint
|
||||||
|
v3: 04/03/2006 - Use Skyfox's routine to update AY regs
|
||||||
|
v2: 21/02/2006 - Spectrum stereo (L=A+B/2, R=C'+B'/2)
|
||||||
|
v1: 13/02/2006 - Dual mono (L=A+B+C, R=A'+B'+C')
|
Loading…
Reference in New Issue