Un-ignore software/asm/, software/tools/, and software/roms/ so that CI/CD builds on Jenkins can assemble Z80 ROMs, TZFS, and CP/M from source. Previously these files were only available on the dev machine. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
618 lines
15 KiB
NASM
618 lines
15 KiB
NASM
; V1.10
|
|
;
|
|
; To compile use:
|
|
;
|
|
; AS80 [1.31] - Assembler for 8080/8085/Z80 microprocessor.
|
|
;
|
|
; Available from:
|
|
; - http://www.falstaff.demon.co.uk/cross.html
|
|
; - ftp://ftp.simtel.net/pub/simtelnet/msdos/crossasm/as80_131.zip
|
|
; - and many Simtel mirrors.
|
|
;
|
|
; as80 -i -l -n -x2 -v -z mz-1e05.asm
|
|
|
|
|
|
|
|
|
|
;
|
|
;----< MFM Minifloppy control >----
|
|
;
|
|
;
|
|
; Call condition
|
|
;
|
|
; Case of disk initialize
|
|
; Drive N = IX+0 (0 - 3)
|
|
;
|
|
;
|
|
; Case of sequential read & write
|
|
; Drive N = IX+0 (0 - 3)
|
|
;
|
|
; Sector addrs = IX+1,2 (0 - $045F) H C S
|
|
; (0 - 1119) -> 70 x 16 sectors -> 2 x 35 x 16
|
|
; Byte size = IX+3,4
|
|
; Address = IX+5,6
|
|
; Next track = IX+7
|
|
; Next sector = IX+8
|
|
; Start track = IX+9
|
|
; Start sector = IX+10
|
|
;
|
|
;
|
|
; I/O Port address
|
|
;
|
|
CR EQU $D8 ; CommandRegister
|
|
TR EQU $D9 ; TrackRegister
|
|
SCR EQU $DA ; SeCtorRegister
|
|
DR EQU $DB ; DataRegister
|
|
DM EQU $DC ; DriveMotor
|
|
HS EQU $DD ; HeadSelect
|
|
|
|
|
|
|
|
TIMST EQU $0033
|
|
|
|
;
|
|
; Subroutine work
|
|
;
|
|
BPRO EQU $CF00
|
|
BUF EQU $11A3
|
|
BPARA EQU BPRO - 23 ; BootPARAmeter
|
|
|
|
|
|
CMD EQU BPARA + 11 ; CoMmanD
|
|
MTFG EQU CMD + 1 ; MoTorFlaG
|
|
CLBF0 EQU MTFG + 1
|
|
CLBF1 EQU CLBF0 + 1
|
|
CLBF2 EQU CLBF1 + 1
|
|
CLBF3 EQU CLBF2 + 1
|
|
VRFCNT EQU CLBF3 + 1 ; VeRiFyCouNT
|
|
STAFG EQU VRFCNT + 1 ; STAtusFlaG
|
|
|
|
|
|
;
|
|
;
|
|
;--------< Ercode map >--------
|
|
;
|
|
; 50 : Not ready
|
|
; 41 : Data error
|
|
; Track 80 err
|
|
; Write protect err
|
|
; Seek err
|
|
; CRC err
|
|
; Lost data
|
|
; 54 : Unformat
|
|
; Recode not found
|
|
; 56 : Invalid data
|
|
;
|
|
;
|
|
|
|
|
|
ORG $F000
|
|
|
|
|
|
MZ_1E05:
|
|
NOP
|
|
LD HL,$00AD
|
|
JR L_F007
|
|
FDX:
|
|
EX (SP),HL
|
|
L_F007:
|
|
LD (BPARA + 21),HL
|
|
XOR A
|
|
LD DE,0
|
|
CALL TIMST
|
|
CALL FDCC ; FD i/o check
|
|
JP NZ,NOTIO
|
|
LD DE,BPARA ; destination address
|
|
LD HL,BOOT ; source address
|
|
LD BC,11 ; 11 bytes
|
|
LDIR ; copy
|
|
SJP:
|
|
LD IX,BPARA
|
|
CALL BREAD ; read from drive 0, sector 0,
|
|
;
|
|
LD HL,BPRO ; compare this address
|
|
LD DE,IPLMC ; with the IPL MasterCode
|
|
LD B,7 ; this are 7 bytes : 3,'IPLPRO'
|
|
MCHECK:
|
|
LD C,(HL)
|
|
LD A,(DE)
|
|
CP C
|
|
JP NZ,MASTE ; not equal than MasterError
|
|
INC HL
|
|
INC DE
|
|
DJNZ MCHECK
|
|
; else Master was found
|
|
LD DE,IPLM0 ; 'IPL IS LOADING'
|
|
RST $18
|
|
LD DE,BPRO + 7 ; NAME
|
|
RST $18
|
|
LD HL,(BPRO + $16) ; TARGETADDRESS from BootBlock
|
|
LD A,H
|
|
OR L
|
|
JR NZ,L_F051 ; if it is != 0 than normal file
|
|
LD HL,(BPRO + $18) ; TARGETADDRESS from BootBlock
|
|
LD A,H
|
|
OR L
|
|
JR Z,L_F057 ; if it is also 0 than ROM replace file
|
|
L_F051:
|
|
XOR A ; else normal file,
|
|
LD HL,(BPRO + $18) ; TARGETADDRESS from BootBlock
|
|
JR L_F05C
|
|
L_F057:
|
|
LD A,$FF ; target is at $0000, bankswitching is needed
|
|
LD HL,$1200 ; for now use temporary buffer at $1200
|
|
L_F05C:
|
|
LD ($CEFD),A
|
|
|
|
LD (IX + 5),L ; set the TargetAddress
|
|
LD (IX + 6),H
|
|
|
|
LD HL,(BPRO + $14) ; BYTE SIZE from BootBlock
|
|
LD (IX + 3),L
|
|
LD (IX + 4),H
|
|
|
|
LD HL,(BPRO + $1E) ; START SECTOR from BootBlock
|
|
LD (IX + 1),L
|
|
LD (IX + 2),H
|
|
;
|
|
CALL BREAD
|
|
CALL MOFF
|
|
|
|
LD A,($CEFD)
|
|
CP $FF
|
|
JR NZ,L_F093
|
|
OUT ($E0),A
|
|
LD HL,$1200 ; SourceAddress
|
|
LD DE,(BPRO + $16) ; TargetAddress
|
|
LD BC,(BPRO + $14) ; ByteCounter
|
|
LDIR ; copy
|
|
L_F093:
|
|
LD BC,$0200 ; Default code
|
|
LD HL,(BPRO + $18) ; TARGET/EXECUTION ADDRESS from BootBlock
|
|
JP (HL)
|
|
|
|
MASTE:
|
|
CALL MOFF
|
|
LD DE,ERRM1 ; 'NOT MASTER'
|
|
JR ERRTR1
|
|
ERRTRT:
|
|
CP 50
|
|
NOTIO:
|
|
LD DE,IPLM3 ; 'MAKE READY FD'
|
|
JR Z,ERRTR1
|
|
LD DE,ERRM0 ; 'FD:LOADING ERROR'
|
|
ERRTR1:
|
|
CALL $0009
|
|
RST $18
|
|
LD SP,$10EE
|
|
LD HL,(BPARA + 21)
|
|
EX (SP),HL
|
|
RET
|
|
;
|
|
;
|
|
; PARAMETER SETTING
|
|
;
|
|
IPLMC:
|
|
DB $03 ; IPL MASTER FLAG
|
|
DB 'IPLPRO'
|
|
|
|
BOOT:
|
|
DB $00 ; DRIVE NO.
|
|
DW $0000 ; SECTOR ADDR.
|
|
DW $0100 ; IFM BYTE SIZE
|
|
DW BPRO ; IFM LOADING ADDR.
|
|
DW $0000 ; IX+7,8 (track 0, sector 0)
|
|
|
|
|
|
|
|
ERRM1:
|
|
DB 'FD:NOT MASTER',$0D
|
|
IPLM0:
|
|
DB 'IPL IS LOADING ',$0D
|
|
IPLM3:
|
|
DB 'MAKE READY FD',$0D
|
|
ERRM0:
|
|
DB 'FD:LOADING ERROR',$0D
|
|
|
|
FDCC:
|
|
LD A,$A5
|
|
LD B,A
|
|
OUT (TR),A
|
|
CALL DLY80U
|
|
IN A,(TR)
|
|
CP B
|
|
RET
|
|
|
|
L_F111:
|
|
DB $00, $00
|
|
;
|
|
;
|
|
; READY CHECK
|
|
;
|
|
READY:
|
|
LD A,(MTFG)
|
|
RRCA
|
|
CALL NC,MTON
|
|
LD A,(IX + 0) ; DRIVE NO SET
|
|
OR $84
|
|
OUT (DM),A ; DRIVE SELECT MOTON
|
|
XOR A
|
|
LD (CMD),A
|
|
CALL DLY60M
|
|
LD HL,0
|
|
REDY0:
|
|
DEC HL
|
|
LD A,H
|
|
OR L
|
|
JR Z,REDY1
|
|
IN A,(CR) ; STATUS GET
|
|
CPL
|
|
RLCA
|
|
JR C,REDY0
|
|
LD C,(IX + 0)
|
|
LD HL,CLBF0
|
|
LD B,$00
|
|
ADD HL,BC
|
|
BIT 0,(HL)
|
|
JR NZ,REDY2
|
|
CALL RCLB
|
|
SET 0,(HL)
|
|
REDY2:
|
|
RET
|
|
|
|
REDY1:
|
|
LD A,$32
|
|
JP ERJMP
|
|
;
|
|
;
|
|
; MOTOR ON
|
|
;
|
|
MTON:
|
|
LD A,$80
|
|
OUT (DM),A
|
|
LD B,16
|
|
MTD1:
|
|
CALL DLY60M
|
|
DJNZ MTD1
|
|
LD A,1
|
|
LD (MTFG),A
|
|
RET
|
|
;
|
|
;
|
|
; SEEK TREATMENT
|
|
;
|
|
SEEK:
|
|
LD A,$1B ; 1x = SEEK,
|
|
CALL CMDOT1 ; load head, no verify, max stepping rate
|
|
AND $99
|
|
RET
|
|
;
|
|
;
|
|
; MOTOR OFF
|
|
;
|
|
MOFF:
|
|
PUSH AF
|
|
CALL DLY1M ; 1000 US DELAY
|
|
XOR A
|
|
OUT (DM),A
|
|
LD (CLBF0),A
|
|
LD (CLBF1),A
|
|
LD (CLBF2),A
|
|
LD (CLBF3),A
|
|
LD (MTFG),A
|
|
POP AF
|
|
RET
|
|
;
|
|
;
|
|
; RECALIBRATION
|
|
;
|
|
RCLB:
|
|
LD A,$0B ; 0x = RESTORE (seek track 0)
|
|
CALL CMDOT1 ; load head, no verify, max stepping rate
|
|
AND $85
|
|
XOR $04
|
|
RET Z
|
|
|
|
L_F189:
|
|
JP ERROR
|
|
;
|
|
;
|
|
; COMMAND OUT ROUTINE
|
|
;
|
|
CMDOT1:
|
|
LD (CMD),A
|
|
CPL
|
|
OUT (CR),A
|
|
CALL BSYON
|
|
CALL DLY60M
|
|
IN A,(CR)
|
|
CPL
|
|
LD (STAFG),A
|
|
RET
|
|
;
|
|
;
|
|
; BUSY AND WAIT
|
|
;
|
|
BSYON:
|
|
PUSH DE
|
|
PUSH HL
|
|
CALL BSY0
|
|
BSYON2:
|
|
LD HL,$0000
|
|
BSYON0:
|
|
DEC HL
|
|
LD A,H
|
|
OR L
|
|
JR Z,BSYON1
|
|
IN A,(CR)
|
|
RRCA
|
|
JR NC,BSYON0
|
|
POP HL
|
|
POP DE
|
|
RET
|
|
;
|
|
BSYON1:
|
|
DEC E
|
|
JR NZ,BSYON2
|
|
BSYONE:
|
|
LD A,$29
|
|
POP HL
|
|
POP DE
|
|
JP ERJMP
|
|
;
|
|
BSYOFF:
|
|
PUSH DE
|
|
PUSH HL
|
|
CALL BSY0
|
|
BSYOF2:
|
|
LD HL,$0000
|
|
BSYOF0:
|
|
DEC HL
|
|
LD A,H
|
|
OR L
|
|
JR Z,BSYOF1
|
|
IN A,(CR) ; Status Register
|
|
RRCA
|
|
JR C,BSYOF0
|
|
POP HL
|
|
POP DE
|
|
RET
|
|
;
|
|
BSYOF1:
|
|
DEC E
|
|
JR NZ,BSYOF2
|
|
JR BSYONE
|
|
;
|
|
BSY0:
|
|
CALL DLY80U
|
|
LD E,$07
|
|
RET
|
|
;
|
|
;
|
|
; SEQUENTIAL READ
|
|
;
|
|
BREAD:
|
|
CALL CNVRT
|
|
CALL PARST1 ; HL = IX + 5,6 (TargetAddress)
|
|
RE8:
|
|
CALL SIDST
|
|
CALL SEEK
|
|
JP NZ,ERJMP
|
|
CALL PARST2 ; C = DataRegister
|
|
DI ; disable interrupts
|
|
LD A,$94 ; 9x = READ SECTOR, multiple records
|
|
CALL CMDOT2 ; compare for side 0, 15ms delay,
|
|
RE6: ; disable side select compare
|
|
LD B,0 ; ByteCounter = 0, to load 256 bytes of the sector
|
|
RE4:
|
|
IN A,(CR)
|
|
RRCA
|
|
JR C,RE3
|
|
RRCA
|
|
JR C,RE4
|
|
INI ; (HL) = in(C), B = B - 1 , HL = HL + 1
|
|
JR NZ,RE4
|
|
|
|
INC (IX + 8) ; NextSector = NextSector + 1
|
|
LD A,(IX + 8)
|
|
CP $11 ; if NextSector = 17
|
|
JR Z,L_F213 ; than end
|
|
DEC D ; else SectorCounter = SectorCounter - 1
|
|
JR NZ,RE6 ; if SectorCounter = 0
|
|
JR L_F214 ; than end
|
|
L_F213:
|
|
DEC D
|
|
L_F214:
|
|
CALL INTER
|
|
RE3:
|
|
EI ; enable interrupts
|
|
IN A,(CR)
|
|
CPL
|
|
LD (STAFG),A
|
|
AND $FF
|
|
JR NZ,ERROR
|
|
CALL ADJ ; adjust sector and track
|
|
JP Z,REND
|
|
LD A,(IX + 7) ; track
|
|
JR RE8
|
|
REND:
|
|
LD A,$80
|
|
OUT (DM),A ; motor on
|
|
RET
|
|
;
|
|
;
|
|
; PARAMETER SET
|
|
;
|
|
;
|
|
PARST1:
|
|
CALL READY
|
|
LD D,(IX + 4) ; D = bytes to read (highbyte) (256 bytes)
|
|
LD A,(IX + 3) ; A = bytes to read (lowbyte)
|
|
OR A ; if A = 0
|
|
JR Z,L_F23F ; than it's ok
|
|
INC D ; else read 256 bytes more (1 sector)
|
|
L_F23F:
|
|
LD A,(IX + 10) ; NextSector = StartSector
|
|
LD (IX + 8),A
|
|
|
|
LD A,(IX + 9) ; NextTrack = StartTrack
|
|
LD (IX + 7),A
|
|
|
|
LD L,(IX + 5) ; HL = TargetAddress
|
|
LD H,(IX + 6)
|
|
RET
|
|
|
|
;
|
|
;
|
|
; SIZE SEEK SET
|
|
;
|
|
SIDST:
|
|
SRL A
|
|
CPL
|
|
OUT (DR),A
|
|
JR NC,L_F25D ; NC than Head 0
|
|
LD A,1 ; else Head 1
|
|
JR L_F25E
|
|
L_F25D:
|
|
XOR A
|
|
L_F25E:
|
|
CPL
|
|
OUT (HS),A ; set HeadSelect
|
|
RET
|
|
;
|
|
;
|
|
; TRACK & SECTOR SET
|
|
;
|
|
PARST2:
|
|
LD C,DR
|
|
LD A,(IX + 7) ; A = NextTrack
|
|
SRL A
|
|
CPL
|
|
OUT (TR),A
|
|
LD A,(IX + 8) ; A = NextSector
|
|
CPL
|
|
OUT (SCR),A
|
|
RET
|
|
;
|
|
;
|
|
; ADJUST SECT & TRACK
|
|
;
|
|
ADJ:
|
|
LD A,(IX + 8) ; A = NextSector
|
|
CP 17 ; if NextSector = 17
|
|
JR NZ,L_F282 ; than the border is not reached
|
|
LD A,$01 ; else
|
|
LD (IX + 8),A ; NextSector = 1
|
|
INC (IX + 7) ; NextTrack = NextTrack + 1
|
|
L_F282:
|
|
LD A,D
|
|
OR A
|
|
RET
|
|
;
|
|
;
|
|
; COMMAND OUT & WAIT
|
|
;
|
|
CMDOT2:
|
|
LD (CMD),A
|
|
CPL
|
|
OUT (CR),A
|
|
CALL BSYOFF
|
|
RET
|
|
;
|
|
;
|
|
; FORCE INTERRUPT
|
|
;
|
|
INTER:
|
|
LD A,$D8
|
|
CPL
|
|
OUT (CR),A
|
|
CALL BSYON
|
|
RET
|
|
|
|
;
|
|
;
|
|
; STATUS CHECK
|
|
;
|
|
ERROR:
|
|
LD A,(CMD)
|
|
CP $0B ; Restore (seek track 0)
|
|
JR Z,ERCK1
|
|
CP $1B ; Seek
|
|
JR Z,ERCK1
|
|
CP $F4 ; Write track
|
|
JR Z,ERCK1
|
|
LD A,(STAFG)
|
|
BIT 7,A
|
|
JR NZ,ERRET
|
|
BIT 6,A
|
|
JR NZ,ERRET1
|
|
BIT 4,A
|
|
LD A,54
|
|
JR NZ,ERJMP
|
|
JR ERRET1
|
|
ERCK1:
|
|
LD A,(STAFG)
|
|
BIT 7,A
|
|
JR NZ,ERRET
|
|
ERRET1:
|
|
LD A,41
|
|
JR ERJMP
|
|
ERRET:
|
|
LD A,50
|
|
ERJMP:
|
|
CALL MOFF
|
|
JP ERRTRT
|
|
;
|
|
;
|
|
; SECTOR TO TRACK & SECTOR CONVERT
|
|
;
|
|
CNVRT:
|
|
LD B,0 ; TrackCounter = 0
|
|
LD DE,16 ; 16 sectors per track
|
|
LD L,(IX + 1) ; HL = SectorAddress
|
|
LD H,(IX + 2)
|
|
XOR A
|
|
TRANS0:
|
|
SBC HL,DE ; SectorAddress - SectorPerTrack
|
|
JR C,TRANS1 ; if < 0 than ready
|
|
INC B ; else TrackCounter = TrackCounter + 1
|
|
JR TRANS0 ; next try
|
|
|
|
TRANS1:
|
|
ADD HL,DE ; undo the last substraction
|
|
LD H,B
|
|
INC L ; adjust sector (sector is 1..16 and not 0..15)
|
|
LD (IX + 9),H ; set StartTrack
|
|
LD (IX + 10),L ; set StartSector
|
|
RET
|
|
|
|
;
|
|
;
|
|
; TIME DELAY ( 1m & 60m & 80u )
|
|
;
|
|
DLY80U:
|
|
PUSH DE
|
|
LD DE,15
|
|
JP DLYT
|
|
|
|
DLY1M:
|
|
PUSH DE
|
|
LD DE,160
|
|
JP DLYT
|
|
|
|
DLY60M:
|
|
PUSH DE
|
|
LD DE,8230
|
|
DLYT:
|
|
DEC DE
|
|
LD A,E
|
|
OR D
|
|
JR NZ,DLYT
|
|
POP DE
|
|
RET
|
|
|
|
|
|
ORG $FFF0
|
|
|
|
DB ' 84.03.14 V1.0A'
|