📄 drivers.asm
字号:
;now we've got the range of blocks that'll be needed ;in the cache to satisfy this request. Write them ;into the file_info for this file. mov dptr, #msg_req_clusters1 lcall pstr_d mov r0, #cluster_offset lcall phex24_at_r0 mov dptr, #msg_to lcall pstr_d mov r0, #5 lcall phex24_at_r0 lcall newline_d mov a, file_desc lcall fd_to_ptr movx a, @dptr ;set "caching" bit in flags orl a, #00000010b movx @dptr, a inc dptr inc dptr ;skip unused byte mov a, cluster_offset+0 movx @dptr, a ;store "fcache_first_cluster_offset" inc dptr mov a, cluster_offset+1 movx @dptr, a inc dptr mov a, cluster_offset+2 movx @dptr, a inc dptr mov a, r5 movx @dptr, a ;store "fcache_last_cluster_offset" inc dptr mov a, r6 movx @dptr, a inc dptr mov a, r7 movx @dptr, a inc dptr ;fetch the block number of the first cluster that's ;already cached for this file (will be zero if none) movx a, @dptr mov r2, a inc dptr movx a, @dptr mov r3, a ;the goal now is the figure out "fcache_prev_cluster", ;which should be the block number holding the last ;already cached cluster that comes before ;"fcache_first_cluster_offset". mov r6, #0 ;r6/r7 hold last block attempted mov r7, #0 mov a, r3 jnz fcache_find_prev_loop mov a, r2 jz fcache_prev_knownfcache_find_prev_loop: lcall block_desc_addr ;get the block_info inc dptr ;skip "next_block" inc dptr inc dptr ;skip flags inc dptr ;skip unused byte clr c movx a, @dptr ;compare cluster offsets subb a, cluster_offset+0 inc dptr movx a, @dptr subb a, cluster_offset+1 inc dptr movx a, @dptr subb a, cluster_offset+2 jnc fcache_prev_known ;if (file_info.cluster_offset >= ; cluster_offset) mov a, r2 mov r6, a ;this block is before the first mov a, r3 ;cluster needed, so copy its mov r7, a ;number to r6/r7 inc dptr inc dptr movx a, @dptr mov r2, a ;read next cluster location inc dptr movx a, @dptr mov r3, a jnz fcache_find_prev_loop mov a, r2 jnz fcache_find_prev_loop ;when we get here, r6/r7 will hold the block number ;for the "fcache_prev_cluster" entry in file_info.fcache_prev_known: mov dptr, #msg_prev_clust lcall pstr_d mov r0, #6 lcall phex16_at_r0 lcall newline_d mov a, file_desc lcall fd_to_ptr mov a, #10 lcall add_a_to_dptr mov a, r6 movx @dptr, a ;store "fcache_prev_cluster" inc dptr mov a, r7 movx @dptr, a clr fcw_is_waiting ;hopefully everything that will be needed to get this ;file properly cached has been set up. ljmp return_addr ;this function actually does the work of loading a file ;into the cache. Calling here will cause IDE read ;requests to be generated, which will make the drive ;start spinning if it was in sleep mode. All that ius ;needed is the file descriptor (in r4). The specifics ;of what is to be loaded must have been set up by calling ;"file_cache" before calling here. Usually, this function ;will need to be called repetitively until it returns a ;status that the loading is complete, or an error (out ;of memory) ; return values: ; DPL=0 some cached, keep calling ; DPL=1 all requested data is cached ; DPL=2 ran out of memoryfile_cache_work: mov dptr, #msg_fcw1 lcall pstr_d mov a, r4 lcall phex_d mov a, #',' lcall cout_d mov a, r4 mov file_desc, a ;check that the low priority request queue has enough space ;to add enough requests for an entire cluster, and if it ;doesn't, we need to stop now before malloc'ing the memory ;to hold this cluster. This check was moved to be the very ;first thing, because in the common case where the main ;program called file_cache_work repetitively, the rreq queue ;is rapidly filled, so most of the calls will be returned ;right away by this function until there's at least enough ;room in the low priority rreq queue to hold the requests ;for another clusterfcache_queue_check: mov r0, blocks_per_cluster inc r0 ;actually, make sure there's ;always one extra space available ;(ide_sleep doesn't properly handle ;a full queue case, yet....) ;- disable ide interrupt here mov r4, clust_rreq_head mov r5, clust_rreq_tail ;- reenable ide interrupt here mov a, r4 cpl a jz fcache_qck_ok ;ok if queue complete empty cpl afcache_qck_1: inc a cjne a, #clust_rreq_size, fcache_qck_2 clr afcache_qck_2: cjne a, 5, fcache_qck_nxt ;if we get here, there won't be enough room in the queue mov dptr, #msg_queue_full lcall pstr_d mov dpl, #0 ljmp return_addrfcache_qck_nxt: djnz r0, fcache_qck_1fcache_qck_ok: ;the first thing we need to do is find a cluster number ;and it's cluster offset, that's at or before the first ;cluster offset required to be read. There are two ;cases, either "file_info[fd].fcache_prev_cluster" will ;give us a block number where we get this, or if that ;fails, we've got to start at the beginning of the file. mov a, file_desc lcall fd_to_ptr movx a, @dptr ;read flags jb acc.1, fcache_start ;if we get here, this file isn't currently "caching", so ;return now with an indication that there's nothing to do mov dpl, #1 ljmp return_addrfcache_start: mov a, #10 lcall add_a_to_dptr movx a, @dptr mov prev_cluster_at_block+0, a inc dptr movx a, @dptr mov prev_cluster_at_block+1, a jnz fcache_prev mov a, prev_cluster_at_block+0 jnz fcache_prev ;the file_info didn't have any indication of a previous ;cached cluster, so we'll just start at the beginningfcache_no_prev: inc dptr movx a, @dptr mov cluster+0, a ;get file_info.first_cluster_number inc dptr movx a, @dptr mov cluster+1, a inc dptr movx a, @dptr mov cluster+2, a inc dptr movx a, @dptr mov cluster+3, a clr a mov cluster_offset+0, a ;cluster_offset is zero mov cluster_offset+1, a mov cluster_offset+2, a mov a, file_desc lcall fd_to_ptr mov a, #8 lcall add_a_to_dptr movx a, @dptr mov next_cluster_at_block+0, a inc dptr movx a, @dptr mov next_cluster_at_block+1, a mov dptr, #msg_fcw2 lcall pstr_d mov dptr, #msg_fcw3 lcall pstr_d lcall newline_d ljmp fcache_seek_cluster ;the file_info had a previous cluster block number, so ;we'll look it up to get the info we need.fcache_prev: mov dptr, #msg_fcw3 lcall pstr_d mov a, #'=' lcall cout_d mov r0, #prev_cluster_at_block lcall phex16_at_r0 lcall newline_d mov r2, prev_cluster_at_block+0 mov r3, prev_cluster_at_block+1 lcall block_desc_addr ;get the block_info inc dptr ;skip "next_block" inc dptr inc dptr ;skip flags inc dptr ;skip unused byte movx a, @dptr mov cluster_offset+0, a inc dptr movx a, @dptr mov cluster_offset+1, a inc dptr movx a, @dptr mov cluster_offset+2, a inc dptr inc dptr ;skip "unused" byte movx a, @dptr mov next_cluster_at_block+0, a inc dptr movx a, @dptr mov next_cluster_at_block+1, a inc dptr inc dptr ;skip "number_of_bytes" inc dptr movx a, @dptr mov cluster+0, a inc dptr movx a, @dptr mov cluster+1, a inc dptr movx a, @dptr mov cluster+2, a inc dptr movx a, @dptr mov cluster+3, afcache_seek_cluster: ;we need to traverse the cluster chain until "cluster_offset" ;is equal to "file_info[fd].fcache_first_cluster_offset". mov dptr, #msg_fcseek1 lcall pstr_d mov r0, #cluster lcall phex32_at_r0 mov dptr, #msg_fcseek2 lcall pstr_d mov r0, #cluster_offset lcall phex24_at_r0 mov dptr, #msg_fcseek3 lcall pstr_d mov a, file_desc lcall fd_to_ptr inc dptr inc dptr movx a, @dptr push acc inc dptr movx a, @dptr push acc inc dptr movx a, @dptr lcall phex_d pop acc lcall phex_d pop acc lcall phex_d lcall newline_d ;check if the next cluster number is really an end of file mark. mov a, cluster+3 cjne a, #0x0F, fcache_seek2 ;Found EOF marker, which means we've read all of the file that ;there was to read. The main program must have requested more ;than was actually available if we get here. ;technically, we ought to check for 0x0FFFFFF8 to 0x0FFFFFFF ;but no drive could have a cluster number that large.... using ;512 byte clusters (very unlikely to have a small cluster with ;such a large drive) would need to have 120 gig of clusters. ;It's probably safe to just check the upper byte and assume ;it's an end of file (or bad sector), and we need to stop. ;If we get here, the file has been completely read and we're done. ;does any cleanup need to be done? mov a, file_desc lcall fd_to_ptr movx a, @dptr anl a, #11111101b ;clear "caching" bit in flags movx @dptr, a mov dpl, #1 ljmp return_addrfcache_seek2: mov a, file_desc lcall fd_to_ptr inc dptr inc dptr mov r6, dpl ;keep pointer to "fcache_first_cluster_offset" mov r7, dph movx a, @dptr ;get "fcache_first_cluster_offset" cjne a, cluster_offset+0, fcache_seek_fwd inc dptr movx a, @dptr cjne a, cluster_offset+1, fcache_seek_fwd inc dptr movx a, @dptr cjne a, cluster_offset+2, fcache_seek_fwd ajmp fcache_read_clusterfcache_seek_fwd: ;the comparison above only did an equality test of ;"cluster_offset" to the target offset stored in ;file_info[fd].fcache_first_cluster_offset. We get here when ;they were not equal. The values of "cluster_offset" will ;usually be a relatively small number (5 meg file with 4k ;clusters is 1221 clusters, relative to a 24 bit integer), ;and it should always be less than the target. If something ;goes horribly wrong (memory corruption via DMA), it's quite ;likely to make its first appearance as "cluster_offset" ;being some unrealisticly large number, that's bigger than ;the target. A test should be done here to see if the ;cluster_offset is larger than the target... and if it is ;something went very wrong and we probably need to abort ;back to paulmon2 right now, but maybe print a dump ;of the first 32 blocks (all the block_info) and perhaps ;8000 to 8FFF. mov dpl, r6 ;dptr points to "target" mov dph, r7 clr c movx a, @dptr inc dptr subb a, cluster_offset+0 ;compute target - cluster_offset movx a, @dptr inc dptr subb a, cluster_offset+1 ;compute target - cluster_offset movx a, @dptr inc dptr subb a, cluster_offset+2 ;compute target - cluster_offset jnc fcache_seek_ok ;if we get here, cluster_offset was greater than the target ;(which was file_info[fd].fcache_first_cluster_offset), which ;makes something has gone horribly wrong. mov dptr, #msg_coffset_error lcall pstr ljmp serious_errorfcache_seek_ok: mov a, cluster_offset+0 add a, #1 mov cluster_offset+0, a mov a, cluster_offset+1 addc a, #0 mov cluster_offset+1, a mov a, cluster_offset+2 addc a, #0 mov cluster_offset+2, a lcall fat32_next_cluster cjne a, #1, fcache_seek_nope ajmp fcache_seek_clusterfcache_seek_nope: ;fat32_next_cluster returned either 0 (try again later) or ;2 (out of memory), cjne a, #2, fcache_seek_nope2 sjmp fcache_out_of_memoryfcache_seek_nope2: mov dpl, a ljmp return_addrmsg_coffset_error: .db 13,10,"Error: Cluster offset > target cluster offset",13,10,0 ;when we get here, we've found a cluster that needs to ;be read. We get both the "cluster" and "cluster_offset", ;and "prev_cluster_at_block" gives us the block that's ;holding the previous cluster, so we can link it to this ;one (or zero if none, so we'll have to link to the file_info).fcache_read_cluster: mov dptr, #msg_read_clust1 lcall pstr_d mov r0, #cluster lcall phex32_at_r0 mov dptr, #msg_read_clust2 lcall pstr_d mov r0, #cluster_offset lcall phex24_at_r0 mov a, #')' lcall cout_d lcall newline_d ;TODO: (this bug will show up someday....) ;actually, it's possible that this cluster is already ;in the cache, sitting as the next cluster in the linked ;list... it's be pretty silly to read it if it's already ;in the list. Need to check for this case and jump to ;fcache_finishup if we don't actually need to read it. ;now call malloc to get the memory for this cluster ;this is the place that actually allocates the vast majority ;of memory used by the mp3 player. mov a, blocks_per_cluster lcall malloc_blocks mov reg6, r2 ;save block # in r6/r7 mov reg7, r3 jnc fcache_rdclust2fcache_out_of_memory: ;if we get here, we're out of memory. mov dptr, #msg_no_mem lcall pstr mov a, file_desc lcall fd_to_ptr movx a, @dptr anl a, #11111101b ;clear "caching" bit in flags movx @dptr, a mov dpl, #2 ljmp return_addrfcache_rdclust2: ;convert the cluster number into an LBA sector number clr c mov a, cluster+0 ;first, subtract 2 subb a, #2 mov f32_sector+0, a mov a, cluster+1 subb a, #0 mov f32_sector+1, a mov a, cluster+2 subb a, #0 mov f32_sector+2, a mov a, cluster+3 anl a, #15 subb a, #0 mov f32_sector+3, a ;now multiply by sectors_per_cluster ;always power of two, so use shifts mov r0, sectors_per_clusterf32_c2l_1: mov a, r0 clr c rrc a mov r0, a jz f32_c2l_2 clr c mov a, f32_sector+0 rlc a mov f32_sector+0, a mov a, f32_sector+1 rlc a mov f32_sector+1, a mov a, f32_sector+2 rlc a mov f32_sector+2, a mov a, f32_sector+3 rlc a mov f32_sector+3, a sjmp f32_c2l_1f32_c2l_2: ;now just add the LBA of the first data sector mov dptr, #first_data_sector movx a, @dptr add a, f32_sector+0 mov f32_sector+0, a inc dptr movx a, @dptr addc a, f32_sector+1 mov f32_sector+1, a inc dptr movx a, @dptr addc a, f32_sector+2 mov f32_sector+2, a inc dptr movx a, @dptr addc a, f32_sector+3 mov f32_sector+3, a ;now "f32_sector" has the LBA to read ;we need to do something with "prev_cluster_at_block". ;if it's zero, then there is no previous cluster and ;we need to update the file_info. If there is a previous ;block, then we just write this number into its ;block_info.next_cluster field. mov a, prev_cluster_at_block+0 jnz fcache_prev_fixup mov a, prev_cluster_at_block+1 jnz fcache_prev_fixup mov a, file_desc lcall fd_to_ptr mov a, #8 lcall add_a_to_dptr mov a, r6 ;store r6/r7 as file_info.first_cached_cluster movx @dptr, a inc dptr mov a, r7 movx @dptr, a sjmp fcache_rdc_rreqfcache_prev_fixup: mov r2, prev_cluster_at_block+0 mov r3, prev_cluster_at_block+1fcache_prev_fixup_loop: lcall block_desc_addr mov a, #8 lcall add_a_to_dptr mov a, r6 movx @dptr, a ;store r6/r7 as block_info.next_cluster inc dptr mov a, r7 movx @dptr, a lcall next_block mov a, r2 ;write it into all blocks of that cluster jnz fcache_prev_fixup_loop mov a, r3 jnz fcache_prev_fixup_loop ;now we'll
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -