Mirror of CollapseOS
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

292 行
6.7KB

  1. ; floppy
  2. ;
  3. ; Implement a block device around a TRS-80 floppy. It uses SVCs supplied by
  4. ; TRS-DOS to do so.
  5. ;
  6. ; *** Floppy buffers ***
  7. ;
  8. ; The dual-buffer system is exactly the same as in the "sdc" module. See
  9. ; comments there.
  10. ;
  11. ; *** Consts ***
  12. ; Number of sector per cylinder. We only support single density for now.
  13. .equ FLOPPY_SEC_PER_CYL 10
  14. .equ FLOPPY_MAX_CYL 40
  15. .equ FLOPPY_BLKSIZE 256
  16. ; *** Variables ***
  17. ; This is a pointer to the currently selected buffer. This points to the BUFSEC
  18. ; part, that is, two bytes before actual content begins.
  19. .equ FLOPPY_BUFPTR FLOPPY_RAMSTART
  20. ; Sector number currently in FLOPPY_BUF1. Little endian like any other z80 word.
  21. .equ FLOPPY_BUFSEC1 @+2
  22. ; Whether the buffer has been written to. 0 means clean. 1 means dirty.
  23. .equ FLOPPY_BUFDIRTY1 @+2
  24. ; The contents of the buffer.
  25. .equ FLOPPY_BUF1 @+1
  26. ; second buffer has the same structure as the first.
  27. .equ FLOPPY_BUFSEC2 @+FLOPPY_BLKSIZE
  28. .equ FLOPPY_BUFDIRTY2 @+2
  29. .equ FLOPPY_BUF2 @+1
  30. .equ FLOPPY_RAMEND @+FLOPPY_BLKSIZE
  31. ; *** Code ***
  32. floppyInit:
  33. ; Make sure that both buffers are flagged as invalid and not dirty
  34. xor a
  35. ld (FLOPPY_BUFDIRTY1), a
  36. ld (FLOPPY_BUFDIRTY2), a
  37. dec a
  38. ld (FLOPPY_BUFSEC1), a
  39. ld (FLOPPY_BUFSEC2), a
  40. ret
  41. ; Returns whether D (cylinder) and E (sector) are in proper range.
  42. ; Z for success.
  43. _floppyInRange:
  44. ld a, e
  45. cp FLOPPY_SEC_PER_CYL
  46. jp nc, unsetZ
  47. ld a, d
  48. cp FLOPPY_MAX_CYL
  49. jp nc, unsetZ
  50. xor a ; set Z
  51. ret
  52. ; Read sector index specified in E and cylinder specified in D and place the
  53. ; contents in buffer pointed to by (FLOPPY_BUFPTR).
  54. ; If the operation is a success, updates buffer's sector to the value of DE.
  55. ; Z on success
  56. floppyRdSec:
  57. call _floppyInRange
  58. ret nz
  59. push bc
  60. push hl
  61. ld a, 0x28 ; @DCSTAT
  62. ld c, 1 ; hardcoded to drive :1 for now
  63. rst 0x28
  64. jr nz, .end
  65. ld hl, (FLOPPY_BUFPTR) ; HL --> active buffer's sector
  66. ld (hl), e ; sector
  67. inc hl
  68. ld (hl), d ; cylinder
  69. inc hl ; dirty
  70. inc hl ; data
  71. ld a, 0x31 ; @RDSEC
  72. rst 0x28 ; sets proper Z
  73. .end:
  74. pop hl
  75. pop bc
  76. ret
  77. ; Write the contents of buffer where (FLOPPY_BUFPTR) points to in sector
  78. ; associated to it. Unsets the the buffer's dirty flag on success.
  79. ; Z on success
  80. floppyWrSec:
  81. push ix
  82. ld ix, (FLOPPY_BUFPTR) ; IX points to sector
  83. xor a
  84. cp (ix+2) ; dirty flag
  85. pop ix
  86. ret z ; don't write if dirty flag is zero
  87. push hl
  88. push de
  89. push bc
  90. ld hl, (FLOPPY_BUFPTR) ; sector
  91. ld e, (hl)
  92. inc hl ; cylinder
  93. ld d, (hl)
  94. call _floppyInRange
  95. jr nz, .end
  96. ld c, 1 ; drive
  97. ld a, 0x28 ; @DCSTAT
  98. rst 0x28
  99. jr nz, .end
  100. inc hl ; dirty
  101. xor a
  102. ld (hl), a ; undirty the buffer
  103. inc hl ; data
  104. ld a, 0x35 ; @WRSEC
  105. rst 0x28 ; sets proper Z
  106. .end:
  107. pop bc
  108. pop de
  109. pop hl
  110. ret
  111. ; Considering the first 15 bits of EHL, select the most appropriate of our two
  112. ; buffers and, if necessary, sync that buffer with the floppy. If the selected
  113. ; buffer doesn't have the same sector as what EHL asks, load that buffer from
  114. ; the floppy.
  115. ; If the dirty flag is set, we write the content of the in-memory buffer to the
  116. ; floppy before we read a new sector.
  117. ; Returns Z on success, NZ on error
  118. floppySync:
  119. push de
  120. ; Given a 24-bit address in EHL, extracts the 16-bit sector from it and
  121. ; place it in DE, following cylinder and sector rules.
  122. ; EH is our sector index, L is our offset within the sector.
  123. ld d, e ; cylinder
  124. ld a, h ; sector
  125. ; Let's process D first. Because our maximum number of sectors is 400
  126. ; (40 * 10), D can only be either 0 or 1. If it's 1, we set D to 25 and
  127. ; add 6 to A
  128. inc d \ dec d
  129. jr z, .loop1 ; skip
  130. ld d, 25
  131. add a, 6
  132. .loop1:
  133. cp FLOPPY_SEC_PER_CYL
  134. jr c, .loop1end
  135. sub FLOPPY_SEC_PER_CYL
  136. inc d
  137. jr .loop1
  138. .loop1end:
  139. ld e, a ; write final sector in E
  140. ; Let's first see if our first buffer has our sector
  141. ld a, (FLOPPY_BUFSEC1) ; sector
  142. cp e
  143. jr nz, .notBuf1
  144. ld a, (FLOPPY_BUFSEC1+1) ; cylinder
  145. cp d
  146. jr z, .buf1Ok
  147. .notBuf1:
  148. ; Ok, let's check for buf2 then
  149. ld a, (FLOPPY_BUFSEC2) ; sector
  150. cp e
  151. jr nz, .notBuf2
  152. ld a, (FLOPPY_BUFSEC2+1) ; cylinder
  153. cp d
  154. jr z, .buf2Ok
  155. .notBuf2:
  156. ; None of our two buffers have the sector we need, we'll need to load
  157. ; a new one.
  158. ; We select our buffer depending on which is dirty. If both are on the
  159. ; same status of dirtiness, we pick any (the first in our case). If one
  160. ; of them is dirty, we pick the clean one.
  161. push de ; --> lvl 1
  162. ld de, FLOPPY_BUFSEC1
  163. ld a, (FLOPPY_BUFDIRTY1)
  164. or a ; is buf1 dirty?
  165. jr z, .ready ; no? good, that's our buffer
  166. ; yes? then buf2 is our buffer.
  167. ld de, FLOPPY_BUFSEC2
  168. .ready:
  169. ; At this point, DE points to one of our two buffers, the good one.
  170. ; Let's save it to FLOPPY_BUFPTR
  171. ld (FLOPPY_BUFPTR), de
  172. pop de ; <-- lvl 1
  173. ; We have to read a new sector, but first, let's write the current one
  174. ; if needed.
  175. call floppyWrSec
  176. jr nz, .end ; error
  177. ; Let's read our new sector in DE
  178. call floppyRdSec
  179. jr .end
  180. .buf1Ok:
  181. ld de, FLOPPY_BUFSEC1
  182. ld (FLOPPY_BUFPTR), de
  183. ; Z already set
  184. jr .end
  185. .buf2Ok:
  186. ld de, FLOPPY_BUFSEC2
  187. ld (FLOPPY_BUFPTR), de
  188. ; Z already set
  189. ; to .end
  190. .end:
  191. pop de
  192. ret
  193. ; Flush floppy buffers if dirty and then invalidates them.
  194. ; We invalidate them so that we allow the case where we swap disks after a
  195. ; flush. If we didn't invalidate the buffers, reading a swapped disk after a
  196. ; flush would yield data from the previous disk.
  197. floppyFlush:
  198. ld hl, FLOPPY_BUFSEC1
  199. ld (FLOPPY_BUFPTR), hl
  200. call floppyWrSec
  201. ld hl, FLOPPY_BUFSEC2
  202. ld (FLOPPY_BUFPTR), hl
  203. call floppyWrSec
  204. call floppyInit
  205. xor a ; ensure Z
  206. ret
  207. ; *** blkdev routines ***
  208. ; Make HL point to its proper place in FLOPPY_BUF.
  209. ; EHL currently is a 24-bit offset to read in the floppy. E=high byte,
  210. ; HL=low word. Load the proper sector in memory and make HL point to the
  211. ; correct data in the memory buffer.
  212. _floppyPlaceBuf:
  213. call floppySync
  214. ret nz ; error
  215. ; At this point, we have the proper buffer in place and synced in
  216. ; (FLOPPY_BUFPTR). Only L is important
  217. ld a, l
  218. ld hl, (FLOPPY_BUFPTR)
  219. inc hl ; sector MSB
  220. inc hl ; dirty flag
  221. inc hl ; contents
  222. ; DE is now placed on the data part of the active buffer and all we need
  223. ; is to increase DE by L.
  224. call addHL
  225. ; Now, HL points exactly at the right byte in the active buffer.
  226. xor a ; ensure Z
  227. ret
  228. floppyGetB:
  229. push hl
  230. call _floppyPlaceBuf
  231. jr nz, .end ; NZ already set
  232. ; This is it!
  233. ld a, (hl)
  234. cp a ; ensure Z
  235. .end:
  236. pop hl
  237. ret
  238. floppyPutB:
  239. push hl
  240. push af ; --> lvl 1. let's remember the char we put,
  241. ; _floppyPlaceBuf destroys A.
  242. call _floppyPlaceBuf
  243. jr nz, .error
  244. ; HL points to our dest. Recall A and write
  245. pop af ; <-- lvl 1
  246. ld (hl), a
  247. ; Now, let's set the dirty flag
  248. ld a, 1
  249. ld hl, (FLOPPY_BUFPTR)
  250. inc hl ; sector MSB
  251. inc hl ; point to dirty flag
  252. ld (hl), a ; set dirty flag
  253. xor a ; ensure Z
  254. jr .end
  255. .error:
  256. ; preserve error code
  257. ex af, af'
  258. pop af ; <-- lvl 1
  259. ex af, af'
  260. call unsetZ
  261. .end:
  262. pop hl
  263. ret