1 ; Filesystem functions for the Interaction with BIOS and Disks
3 ; Copyright (C) 2010 Frank Zoll
5 ; This file is part of avrcpm.
7 ; avrcpm is free software: you can redistribute it and/or modify it
8 ; under the terms of the GNU General Public License as published by
9 ; the Free Software Foundation, either version 3 of the License, or
10 ; (at your option) any later version.
12 ; avrcpm is distributed in the hope that it will be useful,
13 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ; GNU General Public License for more details.
17 ; You should have received a copy of the GNU General Public License
18 ; along with avrcpm. If not, see <http://www.gnu.org/licenses/>.
24 ; ---------------- Defines for the Filesystem Interface -------
26 ;*****************************************************
27 ;* Disk-Manager constants *
28 ;*****************************************************
30 ; Fields in the parttabl
32 .equ MAXDISKS = 8 ;Max number of Disks (partitions)
33 .equ PARTENTRY_SIZE = 9 ;Size of a Partitiontableentry
41 .equ dskType_None = 0 << 4
42 .equ dskType_CPM = 1 << 4
43 .equ dskType_FAT = 2 << 4
44 ; .equ dskType_RAM = 3 << 4
45 .equ dskType_MASK = 0xf << 4
47 ;*****************************************************
48 ;* CP/M to host disk constants *
49 ;*****************************************************
50 ; .equ blksize = 1024 ;CP/M allocation size
53 .equ HOSTSIZE = 512 ;host disk sector size
54 .equ HOSTBLK = HOSTSIZE/128 ;CP/M sects/host buff
55 .equ SECMSK = HOSTBLK-1 ;sector mask
56 .equ SECSHF = log2(HOSTBLK) ;sector shift
58 ;*****************************************************
59 ;* BDOS constants on entry to write *
60 ;*****************************************************
61 .equ WRALL = 0 ;write to allocated
62 .equ WRDIR = 1 ;write to directory
63 .equ WRUAL = 2 ;write to unallocated
64 .equ WRTMSK= 3 ;write type mask
66 ;----------------------------------------------- Start of Data Segment
70 ; The following 3 variables are copied from DRAM.
77 ndisks: .byte 1 ;Number of CP/M disks
79 ; The following 5 variables are accessed from 8080/z80 via the
80 ; virtual port interface. Don't change order.
83 bcbadr: .byte 2 ;adr of BiosControlBlock
84 seekdsk: .byte 1 ;seek disk number
85 seektrk: .byte 2 ;seek track number
86 seeksec: .byte 2 ;seek sector number
87 dmaadr: .byte 2 ;last dma address
88 hostpart: .byte 1 ;part of hostbuf to transfer
90 hdrsize: .byte 1 ;Image header size (offset)
91 cpmspt: .byte 1 ;CP/M sectors per track
92 secpblk: .byte 1 ;sectors per block (alloc size)
93 unacnt: .byte 1 ;unalloc rec cnt
94 unadsk: .byte 1 ;last unalloc disk
95 unalba: .byte 2 ;last unalloc disk block
96 unapart: .byte 1 ;last unalloc disk buffer part
98 erflag: .byte 1 ;error reporting
99 wrtype: .byte 1 ;write operation type
101 hostdsk: .byte 1 ;host disk number
102 hostlba: .byte 2 ;host sector number (relative to partition start)
103 hostparttbl: .byte PARTENTRY_SIZE*MAXDISKS ;host partition table (type, start sector, sector count)
105 hostbuf: .byte HOSTSIZE ;host buffer (from/to SD-card)
108 ; ------------------------------- Start of Code Segment
111 ;---------------------------------------------------------------------
122 printstring "drvtbl ("
149 lds temp2,biosdrvtbl+1
153 lds temp2,biosdirbuf+1
157 lds temp2,biosenddat+1
163 ; ====================================================================
164 ; ====================================================================
165 ; Function: Get a Pointer to a Partitiontable entry
166 ; ====================================================================
168 ; --------------------------------------------------------------------
169 ; Registers : [w] z Pointer to the Partitionentry
170 ; [r] zl Number of Diskentry to Read
173 ; --------------------------------------------------------------------
175 ; ====================================================================
178 ldi zh,PARTENTRY_SIZE
185 ; ====================================================================
186 ; ====================================================================
187 ; Function: Virtual Port Interface
188 ; ====================================================================
190 ; --------------------------------------------------------------------
194 ; --------------------------------------------------------------------
196 ; ====================================================================
205 rcall dsk_param_getadr
207 cpi temp3,bcbadr+1-biospar_base
212 cpi temp3,seekdsk-biospar_base
214 rcall dsk_param_getadr
229 ; rcall dbg_print_biosd
230 rcall dpb_drvtblclear
235 ; ====================================================================
236 ; Function: Check if disk exists
237 ; ====================================================================
239 ; --------------------------------------------------------------------
242 ; return 0, if selected disk not exist.
243 ; return !0, if disk exist
244 ; --------------------------------------------------------------------
246 ; ====================================================================
252 printstring "DiskCheck: "
256 brsh dsk_dchrd ;maybe ramdisk
261 ; No disks yet, need to init
263 rcall dpb_drvtblclear
269 rcall mgr_init_partitions ;disk chanched?
278 lcall mgr_prnt_parttbl
288 ; Check if selected disk # is less then # of disks.
296 printstring "Select: "
299 rcall dpb_drvtbl_entry_get
300 or temp,temp2 ;if !0, drive is allready initialized
304 rcall dpb_biosdph_get
310 rcall dpb_drvtbl_entry_get
320 ldi temp,0 ;error return
327 cpi temp,RAMDISKNR+RAMDISKCNT
330 ldi temp,0xff ;return ok
334 ldi temp,0 ;error return
338 ; ====================================================================
339 ; Function: Return status of last disk i/o function
340 ; ====================================================================
342 ; --------------------------------------------------------------------
345 ; --------------------------------------------------------------------
347 ; ====================================================================
353 ; ====================================================================
354 ; ====================================================================
363 .db 10,"<CPM_Disk>",0
365 ; DPBs for varios fixed formats
366 ; dpb data starts at 2. byte
368 dpbdat_avrcpm: ;(dpb243)
369 .db 0x00,0x1A ;sector offset, low(spt)
370 .db 0x00,0x03 ;high (spt), block shift
371 .db 0x07,0x00 ;bock mask, extent mask
372 .db 0xF2,0x00 ;disk size - 1,
373 .db 0x3F,0x00 ;dir max
374 .db 0xC0,0x00 ;alloc0, alloc1
375 .db 0x10,0x00 ;chk size
376 .db 0x02,0x00 ;offset
379 .db 0x02,0x80 ;sector offset, low(spt)
380 .db 0x00,0x05 ;high (spt), block shift
381 .db 0x1F,0x01 ;bock mask, extent mask
382 .db 0xFF,0x07 ;disk size - 1,
383 .db 0xFF,0x03 ;dir max
384 .db 0xFF,0x00 ;alloc0, alloc1
385 .db 0x00,0x01 ;chk size
386 .db 0x00,0x00 ;offset
389 .db 0x00,0x20 ;sector offset, low(spt)
390 .db 0x00,0x05 ;high (spt), block shift
391 .db 0x1F,0x01 ;bock mask, extent mask
392 .db 0xF9,0x07 ;disk size - 1,
393 .db 0xFF,0x03 ;dir max
394 .db 0xFF,0x00 ;alloc0, alloc1
395 .db 0x00,0x01 ;chk size
396 .db 0x06,0x00 ;offset
401 .db 0x04,0x0F ;block shift, bock mask
402 .db 0x00,0xFB ;extent mask, low(disk size -1),
403 .db 0x01,0xBF ;high(disk size -1), low(dir max)
404 .db 0x00,0xE0 ;high(dir max), alloc0
405 .db 0x00,0x30 ;alloc1, low(chk size)
406 .db 0x00,0x02 ;high(chk size), low(offset)
407 .db 0x00,0x00 ;high(offset), fill
410 .db 0x05,0x1F ;block shift, bock mask
411 .db 0x01,0xFD ;extent mask, low(disk size -1),
412 .db 0x07,0xFF ;high(disk size -1), low(dir max)
413 .db 0x01,0xF0 ;high(dir max), alloc0
414 .db 0x00,0x80 ;alloc1, low(chk size)
415 .db 0x00,0x02 ;high(chk size), low(offset)
416 .db 0x00,0x00 ;high(offset), fill
420 ; Copy the dpb data from flash memory, pointed to by Z, to temp ram.
436 ; Copy the dpb data, pointed to by Z to temp ram.
449 ; ====================================================================
450 ; Function: get drive table entry pointer for drive # in temp
451 ; ====================================================================
453 ; --------------------------------------------------------------------
457 ; --------------------------------------------------------------------
459 ; ====================================================================
470 ; ====================================================================
471 ; Function: get drive table entry for drive # in temp
472 ; ====================================================================
474 ; --------------------------------------------------------------------
478 ; --------------------------------------------------------------------
480 ; ====================================================================
482 dpb_drvtbl_entry_get:
484 rcall dpb_drvtbl_entry_p
488 ; ====================================================================
489 ; Function: Clear drive table (entries 0 to 7)
490 ; ====================================================================
492 ; --------------------------------------------------------------------
496 ; --------------------------------------------------------------------
498 ; ====================================================================
517 lds temp2,biosenddat+1
527 ; ====================================================================
528 ; Function: Test disk format: avrcpmhd
529 ; ====================================================================
531 ; --------------------------------------------------------------------
532 ; Registers : temp drive #
534 ; --------------------------------------------------------------------
535 ; Description: Not implemented yet.
536 ; ====================================================================
539 clr temp ; Test, return 'not found' for now.
543 ; ====================================================================
544 ; Function: Test disk format: YAZE
545 ; ====================================================================
547 ; --------------------------------------------------------------------
548 ; Registers : temp drive #
550 ; --------------------------------------------------------------------
551 ; Description: From the YAZE Doc:
553 ; The new disk header occupies the first 128 BYTES of the file and has the
557 ; 10 - 15 a null-terminated ascii comment (may be empty)
558 ; new 16 version (0 = yaze-1.06/1.10, 1 = yaze-ag-2.xx)
559 ; 17 - 31 a null-terminated ascii comment (may be empty)
560 ; 32 - 33 sectors per track
561 ; 34 block shift factor
564 ; 37 - 38 disk size max
565 ; 39 - 40 directory max
568 ; 43 - 44 check size (always zero)
569 ; 45 - 46 track offset
570 ; new 47 psh (used if version=1 and CP/M 3.1 is running)
571 ; new 48 phm ( " " " " " " " " " )
572 ; 49 - 127 unused (zeros)
573 ; ====================================================================
579 ldiw z,str_CPM_Disk*2
580 lpm temp2,z+ ; get length
586 ldi temp,1 ;1 sector header size
593 clr temp ;Not a YAZE disk image.
596 ; ====================================================================
597 ; Function: Test disk format: MyZ80
598 ; ====================================================================
600 ; --------------------------------------------------------------------
601 ; Registers : temp drive #
603 ; --------------------------------------------------------------------
604 ; Description: Test, if first 2 Sectors are filled with 0xE5,
605 ; and Size = 8192KB + 256Bytes.
606 ; ====================================================================
610 rcall dsk_getpartentry ;get partition entry
612 ldd temp2,z+PTAB_SIZE+1 ;check, if size is 16385 phys. sectors
616 brne dsk_tmyz80_not ;wrong size
632 clr temp ;Not a MyZ80 hard disk image.
635 ; ====================================================================
636 ; Function: Test disk format: simhd, simh altair 8800 hard disk format
637 ; ====================================================================
639 ; --------------------------------------------------------------------
640 ; Registers : temp drive #
642 ; --------------------------------------------------------------------
643 ; Description: Test, if Size = 8192 KB and
644 ; first 6 tracks are filled with 0xE5.
645 ; Actually, only the first phys. sector is tested, since
646 ; the other 47 sectors are not in memory at this time.
647 ; ====================================================================
651 rcall dsk_getpartentry ;get partition entry
653 ldd temp2,z+PTAB_SIZE+1 ;check, if size is 16384 phys. sectors
657 brne dsk_tsimhd_not ;wrong size
659 ldiw y,hostbuf+128-10
660 ldiw z,str_CPM_Disk*2
661 lpm temp2,z+ ; get length
663 breq dsk_tsimhd_found
682 clr temp ;Not a simhd hard disk image.
685 ; ====================================================================
687 ; ====================================================================
689 ; --------------------------------------------------------------------
690 ; Registers : temp3 drive #
692 ; --------------------------------------------------------------------
694 ; ====================================================================
698 ; Get first sector (512 byte) of current drive into hostbuf.
702 rcall dsk_readhost_lba
704 ; Test for variable format avrcpmhd.
706 rcall dsk_tst_avrcpmhd
709 ; Test for YAZE formats.
714 ; Test for simhd format.
717 ldiw z,dpbdat_simhd*2
720 ; Test for MyZ80 format.
723 ldiw z,dpbdat_myz80*2
726 ; No special image found. Use avrcpm.
728 ldiw z,dpbdat_avrcpm*2
736 ; ====================================================================
737 ; Function: Add CP/M image format data to partition table data
738 ; ====================================================================
740 ; --------------------------------------------------------------------
741 ; Registers : temp3 drive #
743 ; --------------------------------------------------------------------
745 ; ====================================================================
749 ; Test for known CP/M formats
752 breq dpb_imgd_err ;no known format detected
756 rcall dsk_getpartentry ;get partition entry
759 ; std y+12,_0 ;Test: set check size to 0
763 andi temp,~dskType_MASK
764 ldd temp2,z+PTAB_TYPE
765 andi temp2,dskType_MASK
771 tst temp ;more then 256 sectors per track?
772 brne dsk_imgprp_err ;todo: support 16 bit sector numbers
786 printstring ": Format not supported: Too much sectors per track! "
793 ; ====================================================================
795 ; ====================================================================
797 ; --------------------------------------------------------------------
798 ; Registers : temp drive #
802 ; --------------------------------------------------------------------
803 ; Description: Init CP/M data structures
805 ; -----------------------------------------------------------------
806 ; DPH: | XLT | | | |DIRBUF | DPB | CSV | ALV |
807 ; -----------------------------------------------------------------
808 ;offset: 0 2 4 6 8 10 12 14
810 ; -------------------------------------------------------------
811 ; DPB: | SPT |BSH|BLM|EXM| DSM | DRM |AL0|AL1| CKS | OFF |
812 ; -------------------------------------------------------------
813 ;offset: 0 2 3 4 5 7 9 10 11 13
814 ; ====================================================================
817 mov temp3,temp ;save disk #
829 rcall heap_get ;returns ptr to dph mem
834 movw y,temp ;save dph pointer
837 rcall dram_writew_pp ;XLT
840 lds temp2,biosdirbuf+1
841 rcall dram_writew_pp ;DIRBUF
851 ldiw z,tmpdpb+1 ;skip sector offset byte
855 cpi zl,low(tmpdpb + 16)
861 rcall dram_writew_pp ;DPB
863 ; get mem for dir check vector
865 lds temp,tmpdpb+12 ;cks
866 lds temp2,tmpdpb+12+1
873 rcall dram_writew_pp ;CSV
875 ; get mem for alloc vector
877 lds temp,tmpdpb+6 ;dsm
886 ror temp ;(dsm+1+7)/8
889 rcall dram_writew_pp ;ALV
891 ; success, insert DPH into drvtbl
894 rcall dpb_drvtbl_entry_p
898 ori temp,0xff ;return ok
907 eor temp,temp ;return 0 (+ Z-flag)
910 ; ====================================================================
912 ; ====================================================================
914 ; --------------------------------------------------------------------
917 ; --------------------------------------------------------------------
919 ; ====================================================================
921 ldd temp2,z+PTAB_TYPE
922 andi temp2,~dskType_MASK ;Lower nibble is image header size
925 sts cpmspt,temp2 ;CP/M sectors per track
935 sts secpblk,_tmp0 ;Sectors per block
939 ; ====================================================================
940 ; Function: Does a Disk interaction
941 ; ====================================================================
943 ; --------------------------------------------------------------------
947 ; --------------------------------------------------------------------
949 ; ====================================================================
968 printstring "dsk RD: "
972 printstring "dsk WR: "
976 printstring " HOME: "
980 printstring " BOOT: "
1002 ;See what has to be done.
1005 sbrc temp,WRITE_FUNC
1012 printstring "DISK I/O: Invalid Function code: "
1017 sts ndisks,_0 ;no active partitions
1019 cbi flags,hostact ;host buffer inactive
1020 sts unacnt,_0 ;clear unalloc count
1024 sbis flags,hostwrt ;check for pending write
1025 cbi flags,hostact ;clear host active flag
1038 brlt dsk_rw_noramdisk
1039 sbrc temp,WRITE_FUNC
1044 rcall dsk_getpartentry ;Get Paritiontableentry
1045 ld temp2,z ;Get Partitiontype
1046 andi temp2,dskType_MASK
1050 cpi temp2,dskType_None
1054 ; It must be a FAT16-Imagefile or CP/M Partition.
1056 sbrc temp,WRITE_FUNC
1059 sbi flags,readop ;Set read operation flag
1060 sbi flags,rsflag ;must read data
1062 ldi temp,WRUAL ;write type
1063 sts wrtype,temp ;treat as unalloc
1067 cbi flags,readop ;Not a read operation
1069 sts wrtype,temp ;save write type
1071 rcall dsk_setdrvparam ;todo: do this only if needed (disk change)
1073 ; Convert track/sector to an LBA address (in 128byte blocks)
1078 lds temp,hdrsize ;add image header size
1086 lds temp,seektrk+1 ;
1088 add xh,r0 ;temp3:xh:xl := sec + trk * SectorsPerTrack
1091 andi temp,SECMSK ;mask buffer number
1092 sts hostpart,temp ;save for later
1094 ; Convert from CP/M LBA blocks to host LBA blocks
1103 ;todo: temp3 should be 0 here.
1104 ;xh:xl = host block to seek
1107 rjmp dsk_rwoper ;to perform the read
1111 cbi flags,rsflag ;rsflag = 0
1113 cpi temp,WRUAL ;write unallocated?
1114 brne dsk_chkuna ;check for unalloc
1116 ; write to unallocated, set parameters
1118 lds temp,secpblk ;next unalloc recs (blocksize/128)
1119 lds temp2,hostpart ;
1121 cpi temp2,0 ;cpm sector on phys. sector boundary?
1123 sbi flags,rsflag ; no, rsflag = 1
1124 subi temp,HOSTBLK ;don't write
1125 lds _tmp0,hdrsize ; in next bock
1126 add temp,_tmp0 ; if there is a header
1129 lds temp,seekdsk ;disk to seek
1130 sts unadsk,temp ;unadsk = sekdsk
1131 sts unalba, xl ;unalba = seeklba (== hostlba)
1134 ; check for write to unallocated sector
1138 lds temp,unacnt ;any unalloc remain?
1140 breq dsk_alloc ;skip if not
1142 ; more unallocated records remain
1144 dec temp ;unacnt = unacnt-1
1146 lds temp,seekdsk ;same disk?
1148 cp temp,temp2 ;seekdsk = unadsk?
1149 brne dsk_alloc ;skip if not
1151 ; disks are the same
1155 cp temp,xl ;seeklba = unalba?
1157 brne dsk_alloc ;skip if not
1159 ; block address is the same
1161 lds _tmp0,hostpart ;same part?
1163 cp _tmp0,temp3 ;seekpart = unapart?
1164 brne dsk_alloc ;skip if not
1166 ; match, move to next sector for future ref
1168 inc temp3 ;next part
1171 brne dsk_noovf ;skip if no overflow
1173 ; overflow to next block
1175 subi temp, low(-1) ;unalba = unalba+1
1181 rjmp dsk_rwoper ;to perform the write
1183 ; not an unallocated record, requires pre-read
1186 sts unacnt,_0 ;unacnt = 0
1187 sbi flags,rsflag ;rsflag = 1
1190 ; Enter here to perform the read/write
1195 printstring ", flags|wtyp "
1201 printstring ", buf "
1208 rcall dsk_rw_hostbuf
1210 ; copy data to or from buffer
1213 lds temp,hostpart ;get buffer number (which part of hostbuf)
1216 add zl,r0 ;offset in hostbuf
1221 ldi temp3,128 ;length of move
1222 sbic flags,readop ;which way?
1223 rjmp dsk_rmove ;skip if read
1225 ; mark write operation
1226 sbi flags,hostwrt ;hostwrt = 1
1243 ; data has been moved to/from host buffer
1244 lds temp,wrtype ;write type
1245 cpi temp,WRDIR ;to directory?
1247 ret ;no further processing
1249 ; clear host buffer for directory write
1257 rcall dsk_writehost ;clear host buff
1258 cbi flags,hostwrt ;buffer written
1261 ; ====================================================================
1263 ; ====================================================================
1265 ; --------------------------------------------------------------------
1266 ; Registers : temp2:temp block to read (lba)
1269 ; --------------------------------------------------------------------
1271 ; ====================================================================
1276 printstring "Readhost LBA"
1278 sbi flags,rsflag ;must read data
1279 rcall dsk_rw_hostbuf
1280 lds temp,erflag ;returns 0, if ok
1284 ; ====================================================================
1285 ; Function: Get physical disk sector in hostbuf.
1286 ; ====================================================================
1288 ; --------------------------------------------------------------------
1289 ; Registers : temp2:temp host block to read/write (lba)
1292 ; --------------------------------------------------------------------
1294 ; ====================================================================
1296 ;xh:xl = host block to seek
1297 sts erflag,_0 ;no errors (yet)
1299 ; active host sector?
1301 in _tmp0,flags ;host active flag
1302 sbi flags,hostact ;always becomes 1
1303 sbrs _tmp0,hostact ;was it already?
1304 rjmp dsk_filhst ;fill host if not
1306 ; host buffer active, same as seek buffer?
1308 lds _tmp0,hostdsk ;same disk?
1309 cp temp3,_tmp0 ;seekdsk = hostdsk?
1312 ; same disk, same block?
1321 ;proper disk, but not correct sector
1322 sbis flags,hostwrt ;host written?
1327 rcall dsk_writehost ;clear host buff
1332 ; may have to fill the host buffer
1338 sbic flags,rsflag ;need to read?
1339 rcall dsk_readhost ;yes, if 1
1340 cbi flags,hostwrt ;no pending write
1345 ; ====================================================================
1346 ; Function: Does a Disk write operation
1347 ; ====================================================================
1349 ; --------------------------------------------------------------------
1351 ; Variables : [r] seekdsk Number of Disk to Read
1352 ; [r] seeksec Sector to read
1353 ; [r] seektrk Track to read
1354 ; --------------------------------------------------------------------
1356 ; ====================================================================
1359 rcall dsk_getpartentry
1361 andi temp,dskType_MASK
1364 ; Is it a FAT16 Diskimage ?
1365 cpi temp,dskType_FAT
1370 ; Is it a CP/M Partition ?
1371 cpi temp,dskType_CPM
1374 ; Disktype not supported -> Return
1377 ; ====================================================================
1378 ; Function: Does a Disk read operation
1379 ; ====================================================================
1381 ; --------------------------------------------------------------------
1383 ; Variables : [r] seekdsk Number of Disk to Read
1384 ; [r] seeksec Sector to read
1385 ; [r] seektrk Track to read
1386 ; --------------------------------------------------------------------
1388 ; ====================================================================
1393 printstring "readhost"
1395 rcall dbg_hexdump_line
1397 rcall dbg_hexdump_line
1401 rcall dsk_getpartentry
1403 andi temp,dskType_MASK
1406 ; Is it a FAT16 Diskimage ?
1407 cpi temp,dskType_FAT
1412 ; Is it a CP/M Partition ?
1413 cpi temp,dskType_CPM
1416 ; Disktype not supported -> Return
1420 ; vim:set ts=8 noet nowrap