Mirror of CollapseOS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

351 lines
7.2KB

  1. ; lcd
  2. ;
  3. ; Implement PutC on TI-84+ (for now)'s LCD screen.
  4. ;
  5. ; The screen is 96x64 pixels. The 64 rows are addressed directly with CMD_ROW
  6. ; but columns are addressed in chunks of 6 or 8 bits (there are two modes).
  7. ;
  8. ; In 6-bit mode, there are 16 visible columns. In 8-bit mode, there are 12.
  9. ;
  10. ; Note that "X-increment" and "Y-increment" work in the opposite way than what
  11. ; most people expect. Y moves left and right, X moves up and down.
  12. ;
  13. ; *** Z-Offset ***
  14. ;
  15. ; This LCD has a "Z-Offset" parameter, allowing to offset rows on the
  16. ; screen however we wish. This is handy because it allows us to scroll more
  17. ; efficiently. Instead of having to copy the LCD ram around at each linefeed
  18. ; (or instead of having to maintain an in-memory buffer), we can use this
  19. ; feature.
  20. ;
  21. ; The Z-Offet goes upwards, with wrapping. For example, if we have an 8 pixels
  22. ; high line at row 0 and if our offset is 8, that line will go up 8 pixels,
  23. ; wrapping itself to the bottom of the screen.
  24. ;
  25. ; The principle is this: The active line is always the bottom one. Therefore,
  26. ; when active row is 0, Z is FNT_HEIGHT+1, when row is 1, Z is (FNT_HEIGHT+1)*2,
  27. ; When row is 8, Z is 0.
  28. ;
  29. ; *** 6/8 bit columns and smaller fonts ***
  30. ;
  31. ; If your glyphs, including padding, are 6 or 8 pixels wide, you're in luck
  32. ; because pushing them to the LCD can be done in a very efficient manner.
  33. ; Unfortunately, this makes the LCD unsuitable for a Collapse OS shell: 6
  34. ; pixels per glyph gives us only 16 characters per line, which is hardly
  35. ; usable.
  36. ;
  37. ; This is why we have this buffering system. How it works is that we're always
  38. ; in 8-bit mode and we hold the whole area (8 pixels wide by FNT_HEIGHT high)
  39. ; in memory. When we want to put a glyph to screen, we first read the contents
  40. ; of that area, then add our new glyph, offsetted and masked, to that buffer,
  41. ; then push the buffer back to the LCD. If the glyph is split, move to the next
  42. ; area and finish the job.
  43. ;
  44. ; That being said, it's important to define clearly what CURX and CURY variable
  45. ; mean. Those variable keep track of the current position *in pixels*, in both
  46. ; axes.
  47. ;
  48. ; *** Requirements ***
  49. ; fnt/mgm
  50. ;
  51. ; *** Constants ***
  52. .equ LCD_PORT_CMD 0x10
  53. .equ LCD_PORT_DATA 0x11
  54. .equ LCD_CMD_6BIT 0x00
  55. .equ LCD_CMD_8BIT 0x01
  56. .equ LCD_CMD_DISABLE 0x02
  57. .equ LCD_CMD_ENABLE 0x03
  58. .equ LCD_CMD_XDEC 0x04
  59. .equ LCD_CMD_XINC 0x05
  60. .equ LCD_CMD_YDEC 0x06
  61. .equ LCD_CMD_YINC 0x07
  62. .equ LCD_CMD_COL 0x20
  63. .equ LCD_CMD_ZOFFSET 0x40
  64. .equ LCD_CMD_ROW 0x80
  65. .equ LCD_CMD_CONTRAST 0xc0
  66. ; *** Variables ***
  67. ; Current Y position on the LCD, that is, where re're going to spit our next
  68. ; glyph.
  69. .equ LCD_CURY LCD_RAMSTART
  70. ; Current X position
  71. .equ LCD_CURX @+1
  72. ; two pixel buffers that are 8 pixels wide (1b) by FNT_HEIGHT pixels high.
  73. ; This is where we compose our resulting pixels blocks when spitting a glyph.
  74. .equ LCD_BUF @+1
  75. .equ LCD_RAMEND @+FNT_HEIGHT*2
  76. ; *** Code ***
  77. lcdInit:
  78. ; Initialize variables
  79. xor a
  80. ld (LCD_CURY), a
  81. ld (LCD_CURX), a
  82. ; Clear screen
  83. call lcdClrScr
  84. ; We begin with a Z offset of FNT_HEIGHT+1
  85. ld a, LCD_CMD_ZOFFSET+FNT_HEIGHT+1
  86. call lcdCmd
  87. ; Enable the LCD
  88. ld a, LCD_CMD_ENABLE
  89. call lcdCmd
  90. ; Hack to get LCD to work. According to WikiTI, we're not sure why TIOS
  91. ; sends these, but it sends it, and it is required to make the LCD
  92. ; work. So...
  93. ld a, 0x17
  94. call lcdCmd
  95. ld a, 0x0b
  96. call lcdCmd
  97. ; Set some usable contrast
  98. ld a, LCD_CMD_CONTRAST+0x34
  99. call lcdCmd
  100. ; Enable 8-bit mode.
  101. ld a, LCD_CMD_8BIT
  102. call lcdCmd
  103. ret
  104. ; Wait until the lcd is ready to receive a command
  105. lcdWait:
  106. push af
  107. .loop:
  108. in a, (LCD_PORT_CMD)
  109. ; When 7th bit is cleared, we can send a new command
  110. rla
  111. jr c, .loop
  112. pop af
  113. ret
  114. ; Send cmd A to LCD
  115. lcdCmd:
  116. out (LCD_PORT_CMD), a
  117. jr lcdWait
  118. ; Send data A to LCD
  119. lcdDataSet:
  120. out (LCD_PORT_DATA), a
  121. jr lcdWait
  122. ; Get data from LCD into A
  123. lcdDataGet:
  124. in a, (LCD_PORT_DATA)
  125. jr lcdWait
  126. ; Turn LCD off
  127. lcdOff:
  128. push af
  129. ld a, LCD_CMD_DISABLE
  130. call lcdCmd
  131. out (LCD_PORT_CMD), a
  132. pop af
  133. ret
  134. ; Set LCD's current column to A
  135. lcdSetCol:
  136. push af
  137. ; The col index specified in A is compounded with LCD_CMD_COL
  138. add a, LCD_CMD_COL
  139. call lcdCmd
  140. pop af
  141. ret
  142. ; Set LCD's current row to A
  143. lcdSetRow:
  144. push af
  145. ; The col index specified in A is compounded with LCD_CMD_COL
  146. add a, LCD_CMD_ROW
  147. call lcdCmd
  148. pop af
  149. ret
  150. ; Send the glyph that HL points to to the LCD, at its current position.
  151. ; After having called this, the LCD's position will have advanced by one
  152. ; position
  153. lcdSendGlyph:
  154. push af
  155. push bc
  156. push hl
  157. push ix
  158. ld a, (LCD_CURY)
  159. call lcdSetRow
  160. ld a, (LCD_CURX)
  161. srl a \ srl a \ srl a ; div by 8
  162. call lcdSetCol
  163. ; First operation: read the LCD memory for the "left" side of the
  164. ; buffer. We assume the right side to always be empty, so we don't
  165. ; read it. After having read each line, compose it with glyph line at
  166. ; HL
  167. ; Before we start, what is our bit offset?
  168. ld a, (LCD_CURX)
  169. and 0b111
  170. ; that's our offset, store it in C
  171. ld c, a
  172. ld a, LCD_CMD_XINC
  173. call lcdCmd
  174. ld ix, LCD_BUF
  175. ld b, FNT_HEIGHT
  176. ; A dummy read is needed after a movement.
  177. call lcdDataGet
  178. .loop1:
  179. ; let's go get that glyph data
  180. ld a, (hl)
  181. ld (ix), a
  182. call .shiftIX
  183. ; now let's go get existing pixel on LCD
  184. call lcdDataGet
  185. ; and now let's do some compositing!
  186. or (ix)
  187. ld (ix), a
  188. inc hl
  189. inc ix
  190. djnz .loop1
  191. ; Buffer set! now let's send it.
  192. ld a, (LCD_CURY)
  193. call lcdSetRow
  194. ld hl, LCD_BUF
  195. ld b, FNT_HEIGHT
  196. .loop2:
  197. ld a, (hl)
  198. call lcdDataSet
  199. inc hl
  200. djnz .loop2
  201. ; And finally, let's send the "right side" of the buffer
  202. ld a, (LCD_CURY)
  203. call lcdSetRow
  204. ld a, (LCD_CURX)
  205. srl a \ srl a \ srl a ; div by 8
  206. inc a
  207. call lcdSetCol
  208. ld hl, LCD_BUF+FNT_HEIGHT
  209. ld b, FNT_HEIGHT
  210. .loop3:
  211. ld a, (hl)
  212. call lcdDataSet
  213. inc hl
  214. djnz .loop3
  215. ; Increase column and wrap if necessary
  216. ld a, (LCD_CURX)
  217. add a, FNT_WIDTH+1
  218. ld (LCD_CURX), a
  219. cp 96-FNT_WIDTH
  220. jr c, .skip ; A < 96-FNT_WIDTH
  221. call lcdLinefeed
  222. .skip:
  223. pop ix
  224. pop hl
  225. pop bc
  226. pop af
  227. ret
  228. ; Shift glyph in (IX) to the right C times, sending carry into (IX+FNT_HEIGHT)
  229. .shiftIX:
  230. dec c \ inc c
  231. ret z ; zero? nothing to do
  232. push bc ; --> lvl 1
  233. xor a
  234. ld (ix+FNT_HEIGHT), a
  235. .shiftLoop:
  236. srl (ix)
  237. rr (ix+FNT_HEIGHT)
  238. dec c
  239. jr nz, .shiftLoop
  240. pop bc ; <-- lvl 1
  241. ret
  242. ; Changes the current line and go back to leftmost column
  243. lcdLinefeed:
  244. push af
  245. ld a, (LCD_CURY)
  246. call .addFntH
  247. ld (LCD_CURY), a
  248. call lcdClrLn
  249. ; Now, lets set Z offset which is CURROW+FNT_HEIGHT+1
  250. call .addFntH
  251. add a, LCD_CMD_ZOFFSET
  252. call lcdCmd
  253. xor a
  254. ld (LCD_CURX), a
  255. pop af
  256. ret
  257. .addFntH:
  258. add a, FNT_HEIGHT+1
  259. cp 64
  260. ret c ; A < 64? no wrap
  261. ; we have to wrap around
  262. xor a
  263. ret
  264. ; Clears B rows starting at row A
  265. ; B is not preserved by this routine
  266. lcdClrX:
  267. push af
  268. call lcdSetRow
  269. .outer:
  270. push bc ; --> lvl 1
  271. ld b, 11
  272. ld a, LCD_CMD_YINC
  273. call lcdCmd
  274. xor a
  275. call lcdSetCol
  276. .inner:
  277. call lcdDataSet
  278. djnz .inner
  279. ld a, LCD_CMD_XINC
  280. call lcdCmd
  281. xor a
  282. call lcdDataSet
  283. pop bc ; <-- lvl 1
  284. djnz .outer
  285. pop af
  286. ret
  287. lcdClrLn:
  288. push bc
  289. ld b, FNT_HEIGHT+1
  290. call lcdClrX
  291. pop bc
  292. ret
  293. lcdClrScr:
  294. push bc
  295. ld b, 64
  296. call lcdClrX
  297. pop bc
  298. ret
  299. lcdPutC:
  300. cp LF
  301. jp z, lcdLinefeed
  302. cp BS
  303. jr z, .bs
  304. push hl
  305. call fntGet
  306. jr nz, .end
  307. call lcdSendGlyph
  308. .end:
  309. pop hl
  310. ret
  311. .bs:
  312. ld a, (LCD_CURX)
  313. or a
  314. ret z ; going back one line is too complicated.
  315. ; not implemented yet
  316. sub FNT_WIDTH+1
  317. ld (LCD_CURX), a
  318. ret