diff --git a/build.settings b/build.settings index fe76808d..4cc34d04 100644 --- a/build.settings +++ b/build.settings @@ -19,3 +19,7 @@ DskDir = 'dsk' # Set the data start address for variables declared in our code. DataAddress = '0x6000' + +# Specific compiler related. +#PeepHoleFile = './build/sdcc/common/peep_hole_z80.def' +PeepHoleFile = '' diff --git a/build/build.py b/build/build.py index 8a177d8b..0bd602ff 100755 --- a/build/build.py +++ b/build/build.py @@ -112,6 +112,7 @@ class Project: DirIncludes = [] DirLibs = [] LinkLibs = [] + FilePeepHole = '' _loadAddress = '' _startAddress = '' DataAddress = '' @@ -157,7 +158,7 @@ def _setDirs(self, workdir='current'): filePy = os.path.join(self.DirPrj, os.path.splitext(os.path.basename(self.SettingsFile))[0] + 'Settings.py') shutil.copy(self.SettingsFile, filePy) - from buildSettings import IntermediatesDir, ProjectName, OutputDir, ObjectDir, TempDir, SourcesDir, ResourcesDir, DskDir, IncDir, LibDir, LibDir, LibLink, DataAddress + from buildSettings import IntermediatesDir, ProjectName, OutputDir, ObjectDir, TempDir, SourcesDir, ResourcesDir, DskDir, IncDir, LibDir, LibDir, LibLink, DataAddress, PeepHoleFile os.remove(filePy) self.DirIntermediates = self.getRealPath(os.path.join(IntermediatesDir, workdir)) @@ -172,6 +173,7 @@ def _setDirs(self, workdir='current'): self.DirLibs = self.getRealPath(ensureList(LibDir)) self.LinkLibs = ensureList(LibLink) self.DataAddress = DataAddress + self.FilePeepHole = PeepHoleFile self._binFilename = os.path.join(self.DirOutput, self.Name) + '.bin' self._dskFilename = os.path.join(self.DirDsk, self.Name) + '.dsk' self._symFilename = os.path.join(self.DirObject, self.Name) + '.noi' @@ -289,7 +291,7 @@ def _checkSettings(self): print ('\n\n= CHECKING PROJECT SETTINGS =') isProjectOk = self.existDir(self.DirOutput) if isProjectOk else False isProjectOk = self.existDir(self.DirSources) if isProjectOk else False - isProjectOk = self.existDir(self.DirResources) if isProjectOk else False + isProjectOk = self.existDir(self.DirResources) if isProjectOk else False isProjectOk = self.existDir(self.DirIncludes) if isProjectOk else False isProjectOk = self.existDir(self.DirLibs) if isProjectOk else False isProjectOk = self.existDir(self.DirDsk) if isProjectOk else False @@ -367,7 +369,7 @@ def _assemble(self): bMissingFiles = False; for srcDir in self.DirSources: filelist = os.listdir(srcDir) - command = ASM_EXEC + ' -o ' + os.path.join(self.DirObject, '%s') + ' ' + os.path.join(srcDir, '%s') + command = ASM_EXEC + ' -b -w -p -y -o ' + os.path.join(self.DirObject, '%s') + ' ' + os.path.join(srcDir, '%s') print ('\n= ASSEMBLING FILES =') print (' - Files:') @@ -429,6 +431,9 @@ def _compile(self): if self.existDir(incDir): COMMAND += '-I ' + incDir + ' ' + if self.FilePeepHole != '': + COMMAND += ' --vc -Wa -p,-w -Wl -m,-w,-u,-p,-M,-I,-X --fverbose-asm -S --peep-asm --peep-file ' + self.FilePeepHole + print ('\n= COMPILING =') print (' - Files:') for srcDir in self.DirSources: @@ -474,6 +479,9 @@ def _link(self): for libDir in self.DirLibs: if self.existDir(libDir): libDirs += ' -L ' + libDir + ' ' + + if self.FilePeepHole != '': + COMMAND += ' --vc -Wa -p,-w -Wl -m,-w,-u,-p,-M,-I,-X --fverbose-asm -S --peep-asm --peep-file ' + self.FilePeepHole print ('\n= LINKING =') print (' - Libraries:') diff --git a/build/sdcc/common/peep_hole_z80.def b/build/sdcc/common/peep_hole_z80.def new file mode 100644 index 00000000..2a7c15f7 --- /dev/null +++ b/build/sdcc/common/peep_hole_z80.def @@ -0,0 +1,6 @@ +replace { + xor a, a +} by { + xor a + xor a +} diff --git a/doc/Z80_manual_zilog.pdf b/doc/Z80_manual_zilog.pdf new file mode 100644 index 00000000..3e40135b Binary files /dev/null and b/doc/Z80_manual_zilog.pdf differ diff --git a/doc/el_gran_libro_del_floppy_cpc664-128_rolf_bruckmann_jorg_schieb.pdf b/doc/el_gran_libro_del_floppy_cpc664-128_rolf_bruckmann_jorg_schieb.pdf new file mode 100644 index 00000000..a9bb05ca Binary files /dev/null and b/doc/el_gran_libro_del_floppy_cpc664-128_rolf_bruckmann_jorg_schieb.pdf differ diff --git a/doc/uPD765_NEC_Datasheet.pdf b/doc/uPD765_NEC_Datasheet.pdf new file mode 100644 index 00000000..de4ba336 Binary files /dev/null and b/doc/uPD765_NEC_Datasheet.pdf differ diff --git a/src/SpriteFont.inc b/src/SpriteFont.inc deleted file mode 100644 index 0959e7d4..00000000 --- a/src/SpriteFont.inc +++ /dev/null @@ -1,359 +0,0 @@ -#define CHARSET_WIDTH_BYTES 2 -#define CHARSET_HEIGHT 8 -#define CHARSET_COUNT 43 -const U8 g_Charset[CHARSET_COUNT * CHARSET_HEIGHT * CHARSET_WIDTH_BYTES] = { - 0x3E, 0x2B, - 0x29, 0x29, - 0xA9, 0xA9, - 0x89, 0x89, - 0x89, 0x89, - 0xA9, 0xA9, - 0x3E, 0x2B, - 0x03, 0x03, - 0x03, 0x29, - 0x16, 0x29, - 0x17, 0xA9, - 0x03, 0x89, - 0x03, 0x89, - 0x03, 0xA9, - 0x03, 0x29, - 0x03, 0x03, - 0x3E, 0x2B, - 0x29, 0x29, - 0x17, 0xA9, - 0x6E, 0x2B, - 0x9D, 0x03, - 0xA9, 0x03, - 0x3C, 0x29, - 0x03, 0x03, - 0x3C, 0x2B, - 0x03, 0x29, - 0x03, 0xA9, - 0x46, 0x2B, - 0x03, 0x89, - 0x03, 0xA9, - 0x3C, 0x2B, - 0x03, 0x03, - 0x29, 0x29, - 0x29, 0x29, - 0xA9, 0xA9, - 0x89, 0x89, - 0x6E, 0x89, - 0x03, 0xA9, - 0x03, 0x29, - 0x03, 0x03, - 0x3C, 0x29, - 0x29, 0x03, - 0xBD, 0x03, - 0x6E, 0x2B, - 0x17, 0x89, - 0x03, 0xA9, - 0x3C, 0x2B, - 0x03, 0x03, - 0x3E, 0x2B, - 0xBD, 0xA9, - 0x89, 0x03, - 0xCC, 0x2B, - 0xA9, 0xA9, - 0x29, 0x29, - 0x3E, 0x2B, - 0x03, 0x03, - 0x3C, 0x29, - 0x17, 0x29, - 0x03, 0xA9, - 0x46, 0x89, - 0x03, 0x89, - 0x03, 0xA9, - 0x03, 0x29, - 0x03, 0x03, - 0x3E, 0x2B, - 0x29, 0x29, - 0xA9, 0xA9, - 0x6E, 0x2B, - 0x89, 0x89, - 0xA9, 0xA9, - 0x3E, 0x2B, - 0x03, 0x03, - 0x3E, 0x2B, - 0x29, 0x29, - 0xA9, 0xA9, - 0x6E, 0x89, - 0x03, 0x89, - 0xBD, 0xA9, - 0x3E, 0x2B, - 0x03, 0x03, - 0x07, 0x83, - 0xC1, 0x83, - 0x42, 0x03, - 0x42, 0x03, - 0xC0, 0x81, - 0x94, 0x81, - 0x48, 0x09, - 0x03, 0x03, - 0x16, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x46, 0x03, - 0x46, 0x03, - 0x56, 0x03, - 0x16, 0x03, - 0x03, 0x03, - 0x16, 0x03, - 0x16, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x56, 0x03, - 0x16, 0x03, - 0x03, 0x03, - 0x16, 0x03, - 0x16, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x46, 0x03, - 0x7E, 0x03, - 0x29, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x56, 0x03, - 0x46, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x03, 0x03, - 0x56, 0x03, - 0x16, 0x03, - 0x03, 0x03, - 0x3E, 0x2B, - 0x29, 0x29, - 0xA9, 0xA9, - 0x03, 0xA9, - 0x46, 0x03, - 0x03, 0x03, - 0x16, 0x03, - 0x03, 0x03, - 0x3E, 0x2B, - 0x29, 0x29, - 0xA9, 0xA9, - 0xCC, 0x89, - 0xCC, 0x89, - 0xA9, 0xA9, - 0x29, 0x29, - 0x03, 0x03, - 0x3C, 0x2B, - 0x29, 0x29, - 0xA9, 0xA9, - 0xCC, 0x03, - 0x89, 0x89, - 0xA9, 0xA9, - 0x3C, 0x2B, - 0x03, 0x03, - 0x3E, 0x2B, - 0x29, 0x29, - 0xA9, 0x03, - 0x89, 0x03, - 0x89, 0x03, - 0xA9, 0xA9, - 0x3E, 0x2B, - 0x03, 0x03, - 0x3C, 0x2B, - 0x29, 0x29, - 0xA9, 0xA9, - 0x89, 0x89, - 0x89, 0x89, - 0xA9, 0xA9, - 0x3C, 0x2B, - 0x03, 0x03, - 0x3E, 0x29, - 0x29, 0x03, - 0xA9, 0x03, - 0xCC, 0x03, - 0x89, 0x03, - 0xA9, 0x03, - 0x3E, 0x29, - 0x03, 0x03, - 0x3E, 0x29, - 0x29, 0x03, - 0xA9, 0x03, - 0xCC, 0x03, - 0x89, 0x03, - 0xA9, 0x03, - 0x29, 0x03, - 0x03, 0x03, - 0x3E, 0x2B, - 0x29, 0x29, - 0xA9, 0x03, - 0x9D, 0x2B, - 0x89, 0x89, - 0xA9, 0xA9, - 0x3E, 0x2B, - 0x03, 0x03, - 0x29, 0x29, - 0x29, 0x29, - 0xA9, 0xA9, - 0xCC, 0x89, - 0x89, 0x89, - 0xA9, 0xA9, - 0x29, 0x29, - 0x03, 0x03, - 0x3E, 0x2B, - 0x16, 0x03, - 0x56, 0x03, - 0x46, 0x03, - 0x46, 0x03, - 0x56, 0x03, - 0x3E, 0x2B, - 0x03, 0x03, - 0x03, 0x29, - 0x03, 0x29, - 0x03, 0xA9, - 0x03, 0x89, - 0x03, 0x89, - 0x3D, 0xA9, - 0x3E, 0x2B, - 0x03, 0x03, - 0x29, 0x29, - 0x29, 0x29, - 0xFC, 0x2B, - 0xCC, 0x03, - 0xCC, 0x03, - 0xBD, 0xA9, - 0x29, 0x29, - 0x03, 0x03, - 0x29, 0x03, - 0x29, 0x03, - 0xA9, 0x03, - 0x89, 0x03, - 0x89, 0x03, - 0xBD, 0x03, - 0x3C, 0x29, - 0x03, 0x03, - 0x29, 0x29, - 0x3C, 0x29, - 0xBD, 0xA9, - 0x89, 0x89, - 0x89, 0x89, - 0xA9, 0xA9, - 0x29, 0x29, - 0x03, 0x03, - 0x29, 0x29, - 0x3D, 0x29, - 0xFC, 0xA9, - 0xCC, 0x89, - 0xCC, 0x89, - 0xFC, 0xA9, - 0x3D, 0x29, - 0x03, 0x03, - 0x3E, 0x2B, - 0x29, 0x29, - 0xA9, 0xA9, - 0x89, 0x89, - 0x89, 0x89, - 0xA9, 0xA9, - 0x3E, 0x2B, - 0x03, 0x03, - 0x3C, 0x2B, - 0x29, 0x29, - 0xA9, 0xA9, - 0x89, 0x89, - 0xCC, 0xA9, - 0xA9, 0x03, - 0x29, 0x03, - 0x03, 0x03, - - 0x3E, 0x2B, - 0x29, 0x29, - 0xA9, 0xA9, - 0x89, 0x89, - 0x89, 0x89, - 0x89, 0xA9, - 0xBC, 0x2B, - 0x02, 0x2B, - - 0x3C, 0x2B, - 0x29, 0x29, - 0xA9, 0xA9, - 0xCC, 0x2B, - 0x9D, 0x89, - 0xA9, 0xA9, - 0x29, 0x29, - 0x03, 0x03, - - 0x3E, 0x2B, - 0x29, 0x29, - 0xBD, 0x03, - 0x6E, 0x2B, - 0x17, 0x89, - 0xA9, 0xA9, - 0x3E, 0x2B, - 0x03, 0x03, - - 0x3C, 0x29, - 0x16, 0x03, - 0x56, 0x03, - 0x46, 0x03, - 0x46, 0x03, - 0x56, 0x03, - 0x16, 0x03, - 0x03, 0x03, - - 0x29, 0x29, - 0x29, 0x29, - 0xA9, 0xA9, - 0x89, 0x89, - 0x89, 0x89, - 0xA9, 0xA9, - 0x3E, 0x2B, - 0x03, 0x03, - - 0x29, 0x29, - 0x29, 0x29, - 0xA9, 0xA9, - 0x89, 0x89, - 0x89, 0x89, - 0x7C, 0x29, - 0x16, 0x03, - 0x03, 0x03, - - 0x29, 0x29, - 0x29, 0x29, - 0xA9, 0xA9, - 0xCC, 0x89, - 0xCC, 0x89, - 0xFC, 0xA9, - 0x29, 0x29, - 0x03, 0x03, - - 0x29, 0x29, - 0x29, 0x29, - 0x7E, 0x2B, - 0x46, 0x03, - 0x6E, 0x2B, - 0xA9, 0xA9, - 0x29, 0x29, - 0x03, 0x03, - - 0x29, 0x29, - 0x29, 0x29, - 0xA9, 0xA9, - 0x46, 0x89, - 0x03, 0x89, - 0xA9, 0xA9, - 0x3E, 0x2B, - 0x03, 0x03, - - 0x3C, 0x29, - 0x17, 0x29, - 0x56, 0x2B, - 0x6E, 0x03, - 0x9D, 0x03, - 0xA9, 0x03, - 0x3C, 0x29, - 0x03, 0x03, -}; diff --git a/src/crt0_cpc.s b/src/crt0_cpc.s index 792fc2a6..314f43b6 100644 --- a/src/crt0_cpc.s +++ b/src/crt0_cpc.s @@ -31,11 +31,7 @@ __bootstrap:: ; Tag used by the build script to identify the load/entry point. _old_int:: .dw #0x0000 _uKeyPressed:: .db #0x00 _uSelectedOption:: .db #0x00 -_uTrack:: .db #0x10 -_uSectorID:: .db #0xC1 -_uFoundErrorSectorID:: .db #0x00 -_uMotor:: .db #0x00 -_uDrive:: .db #0x00 +_g_bSectorIDNotFound:: .db #0x00 _uRPMs:: .dw #0x0000 _uRPMsDec:: .db #0x00 _uLoops:: .dw #0x0000 @@ -78,7 +74,7 @@ _szInfoMsg:: SCR_MODE 2 ASCII_AT 28, 18, ^|"USE IT AT YOUR OWN RISK"| CURSOR_AT 1, 24 - .ascii "DskTest v2.0-RC1\r\nFrancisco Jos" + .ascii "DskTest v2.0-RC3\r\nFrancisco Jos" CHAR_ACCENT ^|"e"| .ascii " S" CHAR_ACCENT ^|"a"| diff --git a/src/fdc_funcs.inc b/src/fdc_funcs.inc new file mode 100644 index 00000000..86adc5a7 --- /dev/null +++ b/src/fdc_funcs.inc @@ -0,0 +1,334 @@ +// FDC u765 commands +#define FDC_CMD_SEEK 0b00001111 +#define FDC_CMD_RECALIBRATE 0b00000111 +#define FDC_CMD_SENSE_DRIVE_STATUS 0b00000100 +#define FDC_CMD_SENSE_INTERRUPT_STATUS 0b00001000 +#define FDC_CMD_READ_SECTOR 0b01000110 + + +/* + FDC u765 Register flags +*/ +// FDC u7665 master port flags +#define FDC_REG_MASTER_READY_TO_SEND_RECEIVE 0b10000000 +#define FDC_REG_MASTER_READY_TO_SEND 0b01000000 +#define FDC_REG_MASTER_EXECUTION_STAGE 0b00100000 +#define FDC_REG_MASTER_BUSY_ON_ANY_COMMAND 0b00010000 +#define FDC_REG_MASTER_BUSY_ON_SEEK_RECALIBRATE_DRIVE3 0b00001000 +#define FDC_REG_MASTER_BUSY_ON_SEEK_RECALIBRATE_DRIVE2 0b00000100 +#define FDC_REG_MASTER_BUSY_ON_SEEK_RECALIBRATE_DRIVE1 0b00000010 +#define FDC_REG_MASTER_BUSY_ON_SEEK_RECALIBRATE_DRIVE0 0b00000001 + +// FDC u765 Status Register 0 - ST0 +#define FDC_REG_ST0_INTERRUPTION_CODE 0b11000000 +#define FDC_REG_ST0_END_SEEK 0b00100000 +#define FDC_REG_ST0_DRIVE_ERROR 0b00010000 +#define FDC_REG_ST0_NOT_READY_ON_RW 0b00001000 +#define FDC_REG_ST0_HEAD 0b00000100 +#define FDC_REG_ST0_DRIVE_NUMBER 0b00000011 + +// FDC u765 Status Register 0 - ST0 - Results +#define FDC_REG_ST0_RESULT_INTERRUPTION_CODE_NOT_READY 0b11000000 +#define FDC_REG_ST0_RESULT_INTERRUPTION_CODE_INTERRUPTED_COMMAND 0b01000000 +#define FDC_REG_ST0_RESULT_INTERRUPTION_CODE_INVALID_COMMAND 0b10000000 +#define FDC_REG_ST0_RESULT_INTERRUPTION_CODE_OK 0b00000000 + +#define FDC_REG_ST0_RESULT_HEAD_0 0b00000000 +#define FDC_REG_ST0_RESULT_HEAD_1 0b00000100 + +#define FDC_REG_ST0_RESULT_DRIVE_NUMBER_3 0b00000011 +#define FDC_REG_ST0_RESULT_DRIVE_NUMBER_2 0b00000010 +#define FDC_REG_ST0_RESULT_DRIVE_NUMBER_1 0b00000001 +#define FDC_REG_ST0_RESULT_DRIVE_NUMBER_0 0b00000000 + +// FDC u765 Status Register 1 - ST1 +#define FDC_REG_ST1_END_OF_TRACK 0b10000000 +#define FDC_REG_ST1_NOT_USED6 0b01000000 +#define FDC_REG_ST1_CRC_ERROR 0b00100000 +#define FDC_REG_ST1_DATA_OVERRUN 0b00010000 +#define FDC_REG_ST1_NOT_USED3 0b00001000 +#define FDC_REG_ST1_SECTOR_NOT_FOUND 0b00000100 +#define FDC_REG_ST1_WRITE_PROTECTED 0b00000010 +#define FDC_REG_ST1_NO_ADDRESS_MARK 0b00000001 + +// FDC u765 Status Register 2 - ST2 +#define FDC_REG_ST2_NOT_USED7 0b10000000 +#define FDC_REG_ST2_DELETED_ADDRESS_MARK 0b01000000 +#define FDC_REG_ST2_CRC_ERROR_IN_DATA_FIELD 0b00100000 +#define FDC_REG_ST2_WRONG_TRACK 0b00010000 +#define FDC_REG_ST2_SCAN_EQUAL 0b00001000 +#define FDC_REG_ST2_SCAN_SECTOR_NOT_FOUND 0b00000100 +#define FDC_REG_ST2_BAD_TRACK 0b00000010 +#define FDC_REG_ST2_NO_ADDRESS_MARK 0b00000001 + +// FDC u7665 Status Register 3 - FDC_CMD_DRIVE_STATUS result flags +#define FDC_REG_ST3_FAULT 0b10000000 +#define FDC_REG_ST3_WRITE_PROTECTED 0b01000000 +#define FDC_REG_ST3_READY 0b00100000 +#define FDC_REG_ST3_TRACK0 0b00010000 +#define FDC_REG_ST3_DOUBLE_SIDE 0b00001000 +#define FDC_REG_ST3_HEAD 0b00000100 +#define FDC_REG_ST3_UNIT_SELECT 0b00000011 + +#define FDC_REG_ST3_RESULT_UNIT_SELECT3 0b00000011 +#define FDC_REG_ST3_RESULT_UNIT_SELECT2 0b00000010 +#define FDC_REG_ST3_RESULT_UNIT_SELECT1 0b00000001 +#define FDC_REG_ST3_RESULT_UNIT_SELECT0 0b00000000 + +__sfr __banked __at(0xFA7E) fdc_motor_port; +__sfr __banked __at(0xFB7E) fdc_master_register_port; +__sfr __banked __at(0xFB7F) fdc_data_port; + +extern U8 g_fdc_u8MotorOn; +extern U8 g_fdc_u8TrackSelection; +extern U8 g_fdc_u8SectorSelection; + +extern U8 g_fdc_u8HeadDriveSelection; + +extern U8 ST0; +extern U8 ST1; +extern U8 ST2; +extern U8 ST3; + +// +// Function used as a simple initialized variables +// container. +// +void fdc_variables(void) __naked { +__asm + _g_fdc_u8MotorOn: .db #0 ; Motor status (0-OFF 1-ON) + + _g_fdc_u8TrackSelection: .db #0 ; Selected track (0-79) + _g_fdc_u8SectorSelection: .db #0xC1 ; Selected track (0-79) + + _g_fdc_u8HeadDriveSelection: .db #0 ; 0b00000 Head Drive | b2 - Head number | b0-1 - Drive number | + ; XXXXX X XX + + ; FDC status registers + _ST0: .db #0 + _ST1: .db #0 + _ST2: .db #0 + _ST3: .db #0 +__endasm; +} + +// +// Polls FDC's master register until it is ready to send +// or receive data. +// +void fdc_wait_ready_to_send_receive_data(void) { + while(!(FDC_REG_MASTER_READY_TO_SEND_RECEIVE & fdc_master_register_port)); +} + +// +// Polls FDC's master register until a command execution +// stage ends. +// +void fdc_wait_end_of_command_execution(void) { + while(FDC_REG_MASTER_EXECUTION_STAGE & fdc_master_register_port); +} + +// +// Sends data to the FDC controller +// +// NOTE - This function simply waits for the FDC controller +// until it is ready to send OR receive data. It's up to the +// programmer to use this function at correct places. +// It's redundant to check for the specific FDC_REG_MASTER_READY_TO_SEND +// flag since the programmer must know what she/he is doing. +// +void fdc_sendData(U8 uData) { + fdc_wait_ready_to_send_receive_data(); + fdc_data_port = uData; +} + +// +// Read data from the FDC controller +// +// NOTE - This function simply waits for the FDC controller +// until it is ready to send OR receive data. It's up to the +// programmer to use this function at correct places. +// It's redundant to check for the specific FDC_REG_MASTER_READY_TO_SEND +// flag since the programmer must know what she/he is doing. +// +U8 fdc_getData(void) { + fdc_wait_ready_to_send_receive_data(); + return fdc_data_port; +} + +U8 fdc_cmd_sense_interrupt_status(void) { + fdc_sendData(FDC_CMD_SENSE_INTERRUPT_STATUS); + ST0 = fdc_getData(); + fdc_getData(); //Track position + return ST0; +} + +// +// Executes the sense interrupt status command +// to get the ST0 register status. +// It's mainly intended to be used during the +// execution stage of specific commands like seek +// OR recalibrate. +// Here it is used to wait until the end of the +// seek command. +// +// TODO - refactor this function to be targetted +// for different commands. i.e.: Simply store the +// result in ST0 and leave it up to the programmer +// what to do with the result. +// +void fdc_wait_seek_command_to_finish(void) /* __naked */ { + while(!(FDC_REG_ST0_END_SEEK & fdc_cmd_sense_interrupt_status())); +} + +// +// Wait a bit - 180 interrupts - 0.6 seconds +// +void fdc_MotorWait(void) { +__asm + LD B,#180 + + MotorWAIT: + HALT + DJNZ MotorWAIT +__endasm; +} + + +// +// Switch FDC motor on including the waiting time. +// Waiting time is required to ensure the spin be constant. +// +void fdc_TurnMotorOn(void) { + fdc_motor_port = g_fdc_u8MotorOn = MOTOR_ON; + fdc_MotorWait(); +} + + +// +// Turn FDC motor off. +// +void fdc_TurnMotorOff(void) { + fdc_motor_port = g_fdc_u8MotorOn = MOTOR_OFF; +} + + +static void fdc_toggleMotor(void) { + g_fdc_u8MotorOn ? fdc_TurnMotorOff() : fdc_TurnMotorOn(); +} + +// +// Recalibrate current drive. +// Moves the head to track 0. +// +void fdc_Calibrate(void) /* __naked */ { + fdc_sendData(FDC_CMD_RECALIBRATE); + fdc_sendData(g_fdc_u8HeadDriveSelection); + fdc_wait_seek_command_to_finish(); + fdc_MotorWait(); + // TODO - perform check to see if the command was successful, retry several times when not +} + + +// +// Moves the head to the specified track +// +void fdc_GoToTrack(void) { + fdc_Calibrate(); + fdc_sendData(FDC_CMD_SEEK); + fdc_sendData(g_fdc_u8HeadDriveSelection); + fdc_sendData(g_fdc_u8TrackSelection); + fdc_wait_seek_command_to_finish(); + + // TODO - perform checks to see if the seek command was successful and if not perform a calibrate command and retry +} + + + +// +// Retrieve the ST3 status register. +// +U8 fdc_get_drive_status(void) { + fdc_sendData(FDC_CMD_SENSE_DRIVE_STATUS); + // fdc_sendData(g_fdc_u8DriveSelection); // NOTE - It looks like sending head drive selection byte works fine as well but keep an eye in case something does not work fine at the end. In theory and according to the FDC documentation here we should only send the drive number bits (b1-0) BUT since we are not supporting (still) double headed drives the value is the same. + fdc_sendData(g_fdc_u8HeadDriveSelection); + fdc_wait_ready_to_send_receive_data(); + + ST3 = fdc_getData(); + return ST3; +} + +// +// Ask if the specified drive is available +// and stores the result in uResult. +// Return true if the drive is ready, +// that means, the drive is on and with an +// inserted disc on it ready to operate. +// +bool fdc_DriveReady(void) { + return FDC_REG_ST3_READY & fdc_get_drive_status(); +} + + +void fdc_toggleDrives(void) { + U8 u8MotorOn = g_fdc_u8MotorOn; + fdc_TurnMotorOn(); + + do { + g_fdc_u8HeadDriveSelection = !g_fdc_u8HeadDriveSelection; // Drive+Head | b0-1 - Drive number | b2 - Head number + } while(!fdc_DriveReady()); + + if (!u8MotorOn) { + fdc_TurnMotorOff(); + } +} + + +// +// Moves the head to the specified track and try +// to find a sector with the specified Sector ID. +// +bool fdc_FindSector(void) { + // START - Command + fdc_sendData(FDC_CMD_READ_SECTOR); + // Command parameters + fdc_sendData(g_fdc_u8HeadDriveSelection); + fdc_sendData(g_fdc_u8TrackSelection); + fdc_sendData(0); // HEAD selection is hardcoded to 0. Not supported in the classic CPCs anyways. Sorry for the other (very uncommon) CPC 3" double headed drives. + fdc_sendData(g_fdc_u8SectorSelection); + fdc_sendData(0x02); + fdc_sendData(g_fdc_u8SectorSelection); + fdc_sendData(0x52); + fdc_sendData(0xFF); + fdc_wait_ready_to_send_receive_data(); + + // At this point we have data ready to be read coming from the + // execution stage of the command. This can be sector data + // OR an error indicating the sector was not found. + + // Since we are not interested in the content of the sector we + // wait until the command finishes. If we would like to retrieve + // the data stored in the sector, we would here implement the logic + // to copy the data to memory positions. i.e.: + // + // - while(fdc_wait_ready_to_send_receive_data()) + // wait for data to be read + // read the data from the data port + // store data in memory position + // + // we simply ignore this and wait for the end of the command + // to extract the result we are interested in. + fdc_wait_end_of_command_execution(); + ST0 = fdc_getData(); + ST1 = fdc_getData(); + ST2 = fdc_getData(); + + // Ignore the rest of the unuseful data for our purposes. + fdc_getData(); + fdc_getData(); + fdc_getData(); + fdc_getData(); + + // Set to true/false if the sector was found. + return ST1 & (FDC_REG_ST1_SECTOR_NOT_FOUND | FDC_REG_ST1_NO_ADDRESS_MARK); +} diff --git a/src/main.c b/src/main.c index 118fd253..6c4a4dfd 100644 --- a/src/main.c +++ b/src/main.c @@ -1,15 +1,14 @@ //////////////////////////////////////////////////////////////////////// // main.c -// Dsk Test - Small tool for aiding in diagnosing disk drive(s) problems. +// DskTest - Small tool for aiding in diagnosing disk drive(s) problems. // Francisco José Sánchez (pacomix@hotmail.com) //////////////////////////////////////////////////////////////////////// #include "main.h" #include "firmware.h" -#include "utils.inc" - - +#include "fdc_funcs.inc" +// Variables defined in crt0_cpc.s to avoid static initialization. extern U8 uKeyPressed; extern U8 g_szBytes[6]; // Temp buffer used to convert from integer/byte to ascii extern U16 g_sTime; // Time variable. Contains amount of interruptions happened. @@ -17,17 +16,17 @@ extern U8 g_realTime[5]; extern U8 g_realConstant18000[5]; extern U8 g_realHalf[5]; -extern U8 uTrack; -extern U8 uSectorID; -extern U8 uFoundErrorSectorID; +// extern U8 uTrack; +// extern U8 uSectorID; +extern bool g_bSectorIDNotFound; extern U16 uRPMs; extern U8 uRPMsDec; extern U16 uLoops; extern U8 uPartialSecs; extern U16 uPartialInts; extern U8 g_realLoops[5]; -extern U8 uMotor; -extern U8 uDrive; +// extern U8 uMotor; +// extern U8 uDrive; #define OPTION_COUNT 6 extern const U8 szOptions; @@ -136,24 +135,24 @@ void printInt(U16 uByte) { static void printStatusDrives(void) { // Current selected drive firm_set_cursor_at(POS_X_STAT_DRIVE, POS_Y_STAT_DRIVE); - firm_put_char(65 + uDrive); + firm_put_char(65 + g_fdc_u8HeadDriveSelection); } static void printStatusMotor(void) { - printText(uMotor ? "\x1F\x15\x2ON!" : "\x1F\x15\x2OFF"); + printText(g_fdc_u8MotorOn ? "\x1F\x15\x2ON!" : "\x1F\x15\x2OFF"); } static void printStatusTrack(void) { // Current selected track firm_set_cursor_at(POS_X_STAT_TRACK, POS_Y_STAT_TRACK); - printInt((U16)uTrack); + printInt((U16)g_fdc_u8TrackSelection); } static void printStatusSectorID(void) { // Current selected Sector ID and result firm_set_cursor_at(POS_X_STAT_SECTI, POS_Y_STAT_SECTI); - printInt((U16)uSectorID); - printText(uFoundErrorSectorID ? "\x1F\x24\x04NO!" : "\x1F\x24\x04YES"); + printInt((U16)g_fdc_u8SectorSelection); + printText(g_bSectorIDNotFound ? "\x1F\x24\x04NO!" : "\x1F\x24\x04YES"); } static void printStatusUpdSecs(void) { @@ -214,79 +213,44 @@ void printLabels(void) { } -void myTurnMotorOn(void) { - fdc_TurnMotorOn(); - uMotor = MOTOR_ON; - - if (uFoundErrorSectorID) { - g_sTime = 1; - uLoops = 0; - printText("\x1F\x09\x05RUNNING!"); - } -} - - -void myTurnMotorOff(void) { - fdc_TurnMotorOff(); - uMotor = MOTOR_OFF; - if (g_sTime != 0) { - printText("\x1F\x09\x05STOPPED!"); - g_sTime = 0; - } -} - -static void ToggleMotor(void) { - uMotor ? myTurnMotorOff() : myTurnMotorOn(); -} - -void toggleDrives(void) { - // myTurnMotorOn(); - fdc_TurnMotorOn(); - - do { - uDrive = !uDrive; - } while(!fdc_DriveReady(uDrive)); - fdc_SelectDrive(uDrive, 0); - - if (!uMotor) { - fdc_TurnMotorOff(); - } -} - static void startRPMs(void) { U8 counter; - printText("\x1F\x09\x05STARTING"); - myTurnMotorOn(); + printText("\x1F\x0A\x05STARTING"); + fdc_TurnMotorOn(); + fdc_GoToTrack(); + do { // Look for a missing address mark error track and sector - uSectorID++; - printStatusSectorID(); - for(counter = 0, uFoundErrorSectorID = true; counter != 15 && uFoundErrorSectorID; counter++) { - fdc_FindSector(uSectorID, uTrack, &uFoundErrorSectorID); + counter = 0; + g_fdc_u8SectorSelection++; + while(counter++ < 8) { + g_bSectorIDNotFound = fdc_FindSector(); } - } while(counter != 15); - printStatusSectorID(); + printStatusSectorID(); + } while(!g_bSectorIDNotFound); // Start measuring - printText("\x1F\x09\x05RUNNING!"); + printText("\x1F\x0A\x05RUNNING!"); uLoops = 0; g_sTime = 1; // Start measuring } static void measureRPMs(void) { - if (g_sTime != 0) { + if (g_sTime) { // FindSector with a wrong sector ID will finish after 2 full rotations // of the disc, so uLoops will end up having the number of rotations / 2. // Start with syncing the hole... - fdc_FindSector(uSectorID, uTrack, &uFoundErrorSectorID); + fdc_FindSector(); + // ...and start the measurement. enable_my_int(); - fdc_FindSector(uSectorID, uTrack, &uFoundErrorSectorID); + fdc_FindSector(); disable_my_int(); uLoops++; - // Print stats every TWO seconds. + // Print stats every uPartialSecs if (g_sTime > (uPartialSecs * 150)) { + g_sTime--; printStatusRPMs(); g_sTime = 1; uLoops = 0; @@ -297,7 +261,7 @@ static void measureRPMs(void) { void main(void) { - toggleDrives(); + fdc_toggleDrives(); // firm_set_palette_color(0, 0b0000001100000011); // firm_set_palette_color(1, 0b0001100000011000); @@ -317,49 +281,62 @@ void main(void) { } else if (uKeyPressed == CHAR_CURSOR_LEFT) { if (OPT_TRACK == uSelectedOption) { - uTrack -= uTrack > 0 ? 1 : 0; + g_fdc_u8TrackSelection--; + } else if (OPT_SECTI == uSelectedOption) { - uSectorID -= uSectorID > 0 ? 1 : 0; + g_fdc_u8SectorSelection--; + } else if (OPT_UPD == uSelectedOption) { uPartialSecs -= 1; uPartialInts -= 300; + } } else if (uKeyPressed == CHAR_CURSOR_RIGHT) { if (OPT_TRACK == uSelectedOption) { - uTrack += uTrack < 41 ? 1 : 0; + g_fdc_u8TrackSelection++; + } else if (OPT_SECTI == uSelectedOption) { - uSectorID += uSectorID < 255 ? 1 : 0; + g_fdc_u8SectorSelection++; + } else if (OPT_UPD == uSelectedOption) { uPartialSecs += 1; uPartialInts += 300; + } } else if (uKeyPressed == CHAR_ENTER_BIG || uKeyPressed == CHAR_ENTER_SMALL || uKeyPressed == CHAR_COPY) { if (OPT_DRIVE == uSelectedOption) { - toggleDrives(); + fdc_toggleDrives(); } else if (OPT_MOTOR == uSelectedOption) { - ToggleMotor(); - //uMotor ? myTurnMotorOff() : myTurnMotorOn(); Replacing this only call sums up 2 bytes + fdc_toggleMotor(); } else if (OPT_TRACK == uSelectedOption) { - fdc_GoToTrack(uTrack); + fdc_GoToTrack(); } else if (OPT_SECTI == uSelectedOption) { - fdc_FindSector(uSectorID, uTrack, &uFoundErrorSectorID); + fdc_GoToTrack(); + g_bSectorIDNotFound = fdc_FindSector(); } else if (OPT_RPM == uSelectedOption) { - startRPMs(); - - } else if (OPT_UPD == uSelectedOption) { - if (g_sTime) { - g_sTime = 1; - uLoops = 0; - printStatusRPMs(); + if (!g_sTime) { + startRPMs(); + } else { + fdc_toggleMotor(); } } } + + // Update measure rpm status based on motor status + if (!g_fdc_u8MotorOn) { + printText("\x1F\x0A\x05 "); + g_sTime = 0; + } else if (g_sTime) { + g_sTime = 1; + uLoops = 0; + printText("\x1F\x0A\x05RUNNING!"); + } printLabels(); } diff --git a/src/main.h b/src/main.h index db8dcd4c..bc8f4932 100644 --- a/src/main.h +++ b/src/main.h @@ -1,4 +1,9 @@ -#include "types.h" +typedef unsigned char U8; +typedef unsigned short int U16; +typedef signed char S8; +typedef signed short int S16; +typedef unsigned long int U32; +typedef signed long int I32; #define false 0 #define true 1 diff --git a/src/utils.inc b/src/utils.inc deleted file mode 100644 index 3950b32b..00000000 --- a/src/utils.inc +++ /dev/null @@ -1,365 +0,0 @@ - -void _storage(U16* pIndexHoleCounter) __naked { - (void) pIndexHoleCounter; - -__asm - ; - ; Wait for the end of the current instruction (using ST0). - ; - WAITEND: - LD A,#0b00001000 - CALL PUTFDC - CALL GETFDC ;Get ST0 - LD (ST0),A - CALL GETFDC - XOR A - LD (ST1),A ;Reset ST1 and ST2 - LD (ST2),A - - ; Check if instruction is finished (Bit 5 of ST0) - LD A,(ST0) - BIT 5,A ;Instruction over ? - JR Z,WAITEND - RET - - - ; - ; Send data to FDC - ; A - data to send - ; - PUTFDC: - ex af,af' ; 'Backup register a - LD BC,#0xFB7E - - PUTFD2: - IN A,(C) - JP P,PUTFD2 ; Wait until drive is ready to accept data - - ex af,af' ; 'Recover our data to be sent from register a - inc c - OUT (C),A ; Send the data - RET - - - ; - ; Get data from FDC - ; - ; Returns - ; A - data from FDC - ; - GETFDC: - LD BC,#0xFB7E - - GETFD2: - IN A,(C) - JP P,GETFD2 ; Wait until the FDC is ready to send the data - - inc c - IN A,(C) ; Read the data - RET - - - ; Variables - FDCMOTOR: .db #0 ; Motor status (0-OFF 1-ON) - FDCDRIVE: .db #0 ; Selected drive (0-3) - FDCHEAD: .db #0 ; Head used (0-1) - FDCIDDR: .db #0 ; Drive+Head | b0-1 - Drive number | b2 - Head number - - ST0: .db #0 ; Buffer to store data sent back by the FDC - ST1: .db #0 - ST2: .db #0 -__endasm; -} - - -// -// Wait a bit - 180 interrupts - 0.6 seconds -// -void fdc_MotorWait(void) { -__asm - LD B,#180 - - MotorWAIT: - HALT - DJNZ MotorWAIT -__endasm; -} - - -// -// Switch FDC motor on including the waiting time. -// Waiting time is required to ensure the spin be constant. -// -void fdc_TurnMotorOn(void) { -__asm - LD BC,#0xFA7E - LD A,#1 - OUT (C),A - call _fdc_MotorWait -__endasm; -} - - -// -// Turn FDC off. -// -void fdc_TurnMotorOff(void) { -__asm - LD BC,#0xFA7E - XOR A - OUT (C),A - call _fdc_MotorWait -__endasm; -} - - -// -// Moves the head to the specified track -// -void fdc_GoToTrack(U8 uTrack) { // REG a: uTrack - uTrack; -__asm - PUSH AF - - LD A, #0b00001111 - CALL PUTFDC ; Send command to move the head? (TODO - Verify this and create proper defines) - LD A, (FDCIDDR) - CALL PUTFDC ; Tell the drive and head to use. - - POP AF - CALL PUTFDC ; Send the track parameter for the command - - CALL WAITEND ; Wait until the command is fully executed -__endasm; -} - - -// -// Sets the drive and head to use for the commands. -// -// Reg a - drive (0-3) -// Reg l - head (0-1) -// -void fdc_SelectDrive(U8 uDrive, U8 uHead) { // REG a: uDrive REG l: uHead - (void) uDrive; - (void) uHead; - -__asm - FDCVARS: - ld (FDCDRIVE),a - ld c,a - ld a,l - ld (FDCHEAD),a - rla - rla - and #0b00000100 - or c - ld (FDCIDDR),a -__endasm; -} - -// -// Recalibrate current drive. Moves the head to track 0. -// -void fdc_Calibrate(void) __naked { -__asm - call Calibrate_Sub - call Calibrate_Sub - jp Calibrate_END - -Calibrate_Sub: - LD A,#0b00000111 - CALL PUTFDC ; Command to move the head? (TODO - Verify this and create proper defines) - - ld a, (FDCIDDR) - CALL PUTFDC ; send the the drive and head parameter to use for the command - - CALL WAITEND - -Calibrate_END: - ret -__endasm; -} - - -// -// Ask if the specified drive is available and stores the result in uResult. -// -bool fdc_DriveReady(U8 uDrive) { // REG a: uDrive Exit: REG a: uResult - (void) uDrive; - -__asm - push af ; Save uDrive (Reg a) - - ld a, #0b00000100 - call PUTFDC ; Send Sense drive command - - pop af ; Recover uDrive again - call PUTFDC ; and send the target drive parameter for the command. - - LD BC, #0xFB7E ; Master register port -WAITCMDAV: - IN A, (C) ; Wait until the command is processed by the FDC - AND #0b10000000 ; Wait until the command phase is finished. That is when - ; the drive is ready to send us data. - JR Z, #WAITCMDAV ; NOTE: That could mean that the sector id was found and - ; it is starting with the transfer or that the sector - ; was not found and we can start reading the results. - - CALL GETFDC ; Read the result and put it in the ST1 variable and ret - LD (ST1), A - AND #0b00100000 - -__endasm; -} - - -// -// Moves the head to the specified track and try to find a sector with the -// specified Sector ID. -// -// REG a: uSectorID -// REG l: uTrack -// Stack: uFoundSectorID -// -void fdc_FindSector(U8 uSectorID, U8 uTrack, U8* uFoundSectorID) { - (void) uSectorID; - (void) uTrack; - (void) uFoundSectorID; - -__asm - ; Saves the IX reg. since we use it to access the stack. - push ix - ld ix, #0 - add ix, sp - - ; - ; Read sector. - ; - ; Reg A - Track - ; Reg B - Sector ID to search for - ; - ; Returns - ; Reg A | State | 0-ok 1-disc missing 2-read fail 3-file not found - ; Carry | 1-ok - ; - ; HL=Where new data should be loaded (LOADWHER) - ; - ld B, A ; uSectorID - ld A, l ; uTrack - - READSECT: - LD (RSPIST+1),A ; Hardcode the track number in the code. See tag RSPIST - ; TODO - all the info can be put using ld (VAR+XXX),A in the configureFDC, GoToTrack, etc... functions - PUSH BC ; Save the SectorID since PUTFDC modifies BC - - ; READ Sector command: 06 - ; Parameter bytes: - ; HU b0,1=Unit/Drive Number, b2=Physical Head Number, other bits zero - ; TR Track-ID (usually same value as TP) - ; HD Head-ID - ; SC First Sector-ID (sector you want to read) - ; SZ Sector Size (80h shl n) (default=02h for 200h bytes) - ; LS Last Sector-ID (should be same as SC when reading a single sector) - ; GP Gap (default=2Ah except command 0D: default=52h) - ; SL Sectorlen if SZ=0 (default=FFh) - - ; Send the command plus the required parameters to the FDC - LD A, #0b01000110 ; Send read sector command - CALL PUTFDC - - ; Send the required parameters. - LD A, (FDCIDDR) ; HU - CALL PUTFDC - - RSPIST: ; Tag to modify the parameter of the LD A,#0 instruction below. - LD A, #0 ; TR - CALL PUTFDC - - XOR A ; HD ; TODO - Give support for double headed drives. - CALL PUTFDC - - POP BC ; SC - LD A, B - PUSH AF - CALL PUTFDC - - LD A, #2 ; SZ - CALL PUTFDC - - POP AF ; LS - CALL PUTFDC - - LD A, #0x52 ; GP - CALL PUTFDC - - LD A, #0xFF ; SL - CALL PUTFDC - - ; Wait until command is processed by the FDC - ; TODO consider to put this in the _storage function since it is used in more than one place. - LD BC, #0xFB7E ; Master register port - WAITCMDEND: - IN A, (C) ; Wait until the command is processed by the FDC - AND #0b10000000 ; Wait until the command phase is finished. That is when the drive is ready to send us data. - JR Z, #WAITCMDEND ; NOTE: That could mean that the sector id was found and it is starting with the transfer or - ; that the sector was not found and we can start - ; reading the results. - - ; At this point the FDC has processed the command and started executing it. - ; Now we wait until the execution phase is finished - WAITEXEEND: - IN A, (C) - AND #0b00100000 ; At this point if the sector does exist it will read - ; it and the bit 5 will be reset once it finishes. - ; But if the sector does not exist, it will enter this - ; state with the bit 5 already reset. - ; TODO - Write here the FDC docu. - JR NZ, #WAITEXEEND ; Wait until the command is finished executing. - - ; Retrieve the result bytes through the data register. We ignore most of the - ; results since we do not need them. - ; TODO - Add here the FDC info for the other values. - CALL GETFDC - CALL GETFDC - LD (ST1), A ; This is the result we are interested in. - ; Missing Addres Mark flag or SectorID not found. - ; Set after 2 index pulses if SectorID is not found. - CALL GETFDC - CALL GETFDC - CALL GETFDC - CALL GETFDC - CALL GETFDC - - ; Store the result in the function parameter. - ; TODO - Consider changing this to use return registers instead. - ld l, 4 (IX) - ld h, 5 (IX) - - LD A, (ST1) - AND #0b00000101 - - ;JR Z, #FindSector_FOUND - - ; Not found red color - FindSector_NOTFOUND: - ;ld bc,#0x7f10 - ;out (c),c - ;ld c,#0x55 - ;out (c),c - ;JP FindSector_END - - ; Found. Green color - FindSector_FOUND: - ;ld bc,#0x7f10 - ;out (c),c - ;ld c,#0x56 - ;out (c),c - - FindSector_END: - LD (HL), A - - ; Recover the IX register at the end - pop ix -__endasm; -}