Created
February 29, 2024 00:35
-
-
Save wohali/25a44181564e16b70d7c0fe6dd30da21 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 .title PDP11GUI RK05 sector loader/writer for RK11 controller | |
2 | |
3 ; This code reads or writes one sector from a RK05 into memory | |
4 ; It is used by PDP11GUI to access a RK05 disc | |
5 ; | |
6 ; See | |
7 ; RK11-D and RK11-E moving head disk drive controller manual (Aug 1973, DEC-11-HRKDA-B-D).pdf | |
8 ; | |
9 ; !!! DO NOT CHANGE file name, | |
10 ; !!! entry labels, | |
11 ; !!! sector buffer address, or | |
12 ; !!! register meaning | |
13 ; | |
14 ; call conventions, stack, entry points: see "pdp11gui_main.mac" | |
15 | |
16 ; framework | |
17 .include pdp11gui_main.mac | |
1 .title pdp11gui_main | |
2 | |
3 .asect | |
4 | |
5 ; | |
6 ; ---------------------------------------------------- | |
7 ; Common main entry point for all PDP11GUI drivers | |
8 ; | |
9 | |
10 ; Communication between PDP11GUI and driver | |
11 ; | |
12 ; 1. Principal operation | |
13 ; =============== | |
14 ; Working cycle is always: | |
15 ; - request: tramsit buffer to driver | |
16 ; operation | |
17 ; response: send another buffer back to pdp11gui | |
18 ; "request" is transmission PDP11GUI -> driver | |
19 ; "response" is transmission driver -> PDP11GUI | |
20 ; | |
21 ; 2. Format of data transmission | |
22 ; ========================================== | |
23 ; | |
24 ; 2.1. Generic transmission container format: | |
25 ; =========================================== | |
26 ; Entity for transmission and addressing is always word. | |
27 ; transmission :== <transmission_word_count> <checksum> <opcode> <block0>, <block1>, ... | |
28 ; block := <block_word_count> <data0, ..data 1,,, data> | |
29 ; <transmission_word_count> = len(checksum) + len (opcode) + len(block1) + len(block2) .... | |
30 ; = 2 + blockcount * (1 + block word count) | |
31 ; | |
32 ; - opcode: "command" on pdp11gui -> driver; "status" on driver -> pdp11gui | |
33 ; - "transmitted data maybe more then "<transmission_word_len>", because only whole tripletts can be transmitted. | |
34 ; - blockcount depends on opcode: 0, 1 or 2 blocks are used | |
35 ; requests: block 0 = parameters | |
36 ; block 1= drive data (if opcode = write) | |
37 ; response: block 0 = error status (if opcode != 0) | |
38 ; block 0 = driver data (if opcode = 0) | |
39 ; | |
40 ; - memory layout of transmission buffer: | |
41 ; xmbffr: = . ; base for buffer frame | |
42 ; xmwc: .blkw 1 ; transmission wordcount: exact wordcount, excluding len itself and checksum, but including opcode | |
43 ; xmcs: .blkw 1 ; transmission checksumbuffer-2: buffer checksum. checksum of all data words in rxbuff | |
44 ; xmbuff: = . ; base for buffer payload | |
45 ; ; buffer is max 2 blocks: 1st = 8 parameters, 2nd = 16kwords drive data long | |
46 ; ; Buffer is located at end of code, can grow up tu 48kbyte boundary | |
47 014004 xmopcd = xmbuff ; transmission opcode = word 0 of buffer | |
48 ; | |
49 ; | |
50 ; 2.2 Data buffer layouts for requests: | |
51 ; ====================================== | |
52 ; | |
53 ; 2.2.1 Layout | |
54 ; =============== | |
55 ; | |
56 ; For data transfer requests, block 0 contains parameters, these are shadowed to "reqprm" | |
57 ; (so results can be written into transmission buffer without overwriting parameters | |
58 ; | |
59 000100 . = 100 ; do not use trap vector space | |
60 000100 reqprm: .blkw 10 ; copy of parameters: longest parameter block is 8 words | |
61 | |
62 ; The parameter layout is almost the same same for all disk types and | |
63 ; disk operations. | |
64 ; pdp11gui must transmit these 7 words in block 0! | |
65 000100 prcba = reqprm+0 ; xmbuff+4: controller base address | |
66 000102 prunit = reqprm+2 ; xmbuff+6 ; parameter 1: unit number of disc: 0 or 1 | |
67 000104 prflags = reqprm+4 ; xmbuff+10 ; parameter 5: flags | |
68 ; bit 0 = #1 = "suppress data transmission after read" | |
69 ; bits 1:3 are "PDP11GUI "DriverDeviceType" | |
70 ; bit 1 = #2 = | |
71 ; RX02 driver: 0 = single, 1 = double density | |
72 ; RK611: 0= RK06, 1 = RK07 | |
73 ; bit 2 = #4 = | |
74 ; RX02 driver: 0 = RX01, 1 = RX02 | |
75 000106 prwlen = reqprm+6 ; xmbuff+12 count of word to read/write | |
76 000110 prcyl = reqprm+10 ; xbuff+14 ; parameter 2: cylinder, always starting with 0 | |
77 000112 prhead = reqprm+12 ; xbuff+16 ; parameter 3: head, always starting with 0 | |
78 000114 prsect = reqprm+14 ; xbuff+20 ; parameter 4: sector. always starting with 0 | |
79 ; MSCP is different: starting block # instead of cyl/head/sector | |
80 000110 prstb0 = reqprm+10 ; start block number lo word | |
81 000112 prstb1 = reqprm+12 ; start block number hi word | |
82 | |
83 | |
84 ; for Halt, 1st param is monitor entry address, or 0. | |
85 000100 prmnea = reqprm+0 ; xmbuff+4: MoNitor Entry Address | |
86 | |
87 | |
88 | |
89 | |
90 | |
91 ; <param0,param1, ...> | |
92 ; block 1 contains drive data (on disk write) | |
93 000120 req0wc: .blkw 1 ; addr of len word of block0/1 | |
94 000122 req0dt: .blkw 1 ; address of 1st data word for block 0/1, depending on opcode | |
95 ; - reqdat: "request disk data", start adress of block 1, including len | |
96 000124 req1wc: .blkw 1 | |
97 000126 req1dt: .blkw 1 | |
98 ; | |
99 ; 2.2.2 request opcodes: | |
100 ; ====================== | |
101 ; addr = xmopcd | |
102 000001 opecho = 1 ; echo block, with inverted data. | |
103 000002 opinit = 2 ; | |
104 000003 opread = 3 ; read multiple sectors in one track | |
105 000004 opchek = 4 ; check multiple sectors in one track | |
106 000005 opwrite = 5 ; write " " " | |
107 000006 ophalt = 6 ; halt driver, or jump back to monitor (M9312 console emulator) | |
108 000007 oprest = 7 ; execute Bus RESET | |
109 | |
110 ; | |
111 ; 2.2.3 parameters | |
112 ; =============== | |
113 ; 2.2.3.1 opcode opinit | |
114 ; +0 <controller_addr> | |
115 ; +1 <unitnr> | |
116 ; | |
117 ; 2.2.3.2 opcodes opread,opwrite,opchek | |
118 ; RL02,RX02,RM03: | |
119 ; | |
120 ; reqprm | |
121 ; word offset parameter | |
122 ; +0 <controller_addr> | |
123 ; +1 <unitnr> | |
124 ; +2 <flags> | |
125 ; +3 <count of words to read/write> | |
126 ; +4 <cylinder> | |
127 ; +5 <head> | |
128 ; +6 <sector> | |
129 | |
130 ; MSCP is slightly different: | |
131 ; +4 <start block lo word> | |
132 ; +5 <start block hi word> | |
133 ; +6 0 | |
134 ; | |
135 ; | |
136 ; 2.3 Data buffer layouts for responses: | |
137 ; ====================================== | |
138 ; | |
139 ; 2.3.1 Layout | |
140 ; =============== | |
141 ; only block 0 and opcode ist used. | |
142 ; - rspsta: opcode, as "response status" | |
143 014004 rspsta = xmopcd ; status (0=OK; else error location) | |
144 | |
145 ; - rspwc: wordcount for response is calculated in "doresp" | |
146 ; rspwc = xmbuff+2 ; len of data, driver codes counts data here | |
147 ; - rspdat: start adress of block 0 for response, including len. used for error and result disk data | |
148 014010 rspdat = xmbuff+4 ; 1st data word of block 0; data: error vector, or disk data | |
149 ; on jump to "doresp", r3 must contain next unwritten addr in resdat | |
150 ; this is used to calculate len of whole transmission | |
151 | |
152 | |
153 ; | |
154 ; 2.3.2 Response status | |
155 ; =============== | |
156 ; 2.3.2.1 Error | |
157 ; status != 0: Error. error code contains "location" in driver source code | |
158 ; data are additional info (controller registers) | |
159 ; len is fixed as symbol "errwcnt", but depends on disk controller hardware | |
160 ; | |
161 ; 2.3.2.1 opcode "opinit" | |
162 ; status = 0: OK. | |
163 ; + 0 drive sub type. Is interpreted depending on driver type. | |
164 ; 2.3.2.1.1 RL01,2, RM03, RX02: | |
165 ; disk sub type is transmitted back. | |
166 ; 2.3.2.1.2 MSCP | |
167 ; [0] unit identifer 1st lo (from ONLINE) | |
168 ; [1] unit identifer 1st hi (from ONLINE) | |
169 ; [2] unit identifer 2nd lo (from ONLINE) | |
170 ; [3] unit identifer 2nd hi (from ONLINE) | |
171 ; [4] media type identifier lo (from ONLINE) | |
172 ; [5] media type identifier hi (from ONLINE) | |
173 ; [6] reserved (from ONLINE) | |
174 ; [7] reserved (from ONLINE) | |
175 ; [8] blockcount lo (from ONLINE) | |
176 ; [9] blockcount hi (from ONLINE) | |
177 ; [10] volume serial num lo (from ONLINE) | |
178 ; [11] volume serial num hi (from ONLINE) | |
179 ; [12] cylinder size (from GET UNIT STATUS) | |
180 ; [13] group size (from GET UNIT STATUS) | |
181 ; [14] cylinder size (from GET UNIT STATUS) | |
182 ; [15] reserved (from GET UNIT STATUS) | |
183 ; [16] RCT size (from GET UNIT STATUS) | |
184 ; [17] RBNS/copies (from GET UNIT STATUS) | |
185 | |
186 ; 2.3.2.2 opread, opcheck | |
187 ; status = 0: OK. | |
188 ; 2.3.2.3 opwrite | |
189 ; status = 0: OK. | |
190 ; + 0..len-1 disk data | |
191 | |
192 ; | |
193 ; 3. Code modules | |
194 ; =============== | |
195 ; Code is split in common code (block transfer for request and responses) | |
196 ; and disk specfic implementation of read, write and error | |
197 ; | |
198 ; 3.1 Common code: | |
199 ; =============== | |
200 ; 3.1.1 main entry | |
201 ; label "dofunc" at 10000 is main entry point | |
202 ; label "doresp" is called bei each driver function to transmit response back | |
203 ; to pdp11gui and to terminate | |
204 ; | |
205 ; 3.1.2 Register usage | |
206 ; ==================== | |
207 ; R0 working accumulator | |
208 ; R3 is pointer in data buffer for read/write | |
209 ; R4 is the controller base register | |
210 ; R5 is always an "location mark" for a certain code postion | |
211 ; on error it is the error reason. | |
212 ; Carry-flag C is error indicator, see below | |
213 ; | |
214 ; 3.1.3 Error handling | |
215 ; ==================== | |
216 ; The Carry flag "C" is used as a error indicator | |
217 ; if C is set after a sub routine call, an error occured, | |
218 ; and reqsta, reqwc and reqdata contains already error information. r3 points to end of data | |
219 ; The caller must return then immediately | |
220 ; Example: | |
221 ; | |
222 ; sub1: | |
223 ; ... | |
224 ; tst something | |
225 ; bne 9$ ; error detected: create Exception | |
226 ; ... | |
227 ; clc ; no error, clear carry | |
228 ; return | |
229 ; | |
230 ; 9$ ; ERROR | |
231 ; call @#chkerr ; prepare block buffer wit herror data, sets carry with "sec | |
232 ; return | |
233 ; | |
234 ; caller: | |
235 ; ... | |
236 ; call @#sub1 | |
237 ; bcs 9$ ; error: R0..R5 contain error codes, exit | |
238 ; .... | |
239 ; 9$: return | |
240 | |
241 | |
242 | |
243 ; -------------------------------------------------------- | |
244 ; catch vectors. halting here means error! | |
245 000000 . = 0 | |
246 000000 000002 .word trprv ;Reserved vector | |
247 000002 000000 trprv: halt | |
248 | |
249 000004 . = 004 ; Time-out/system error vector | |
250 000004 000006 .word trpbe ; Bus Error trap | |
251 000006 000000 trpbe: halt | |
252 | |
253 000010 . = 010 ; Illegal and reserved instruction vector | |
254 000010 000012 .word trpri ; Reserved instruction trap | |
255 000012 000000 trpri: halt | |
256 | |
257 000014 . = 014 ; BPT instruction vector | |
258 000014 000016 .word trpbpt | |
259 000016 000000 trpbpt: halt | |
260 | |
261 000020 . = 020 ; IOT instruction vector | |
262 000020 000022 .word trpiot | |
263 000022 000000 trpiot: halt | |
264 | |
265 000024 . = 024 ; Power fail vector | |
266 000024 000026 .word trppf | |
267 000026 000000 trppf: halt | |
268 | |
269 000030 . = 030 ; EMT instruction vector | |
270 000030 000032 .word trpwmt | |
271 000032 000000 trpwmt: halt | |
272 | |
273 000034 . = 034 ; TRAP instruction vector | |
274 000034 000036 .word trptrp | |
275 000036 000000 trptrp: halt | |
276 | |
277 | |
278 | |
279 ; -------------------------------------------------------- | |
280 010000 . = 10000 | |
281 | |
282 010000 stack = . | |
283 | |
284 ; main entry point | |
285 dofunc: | |
286 010000 012706 010000 mov #stack,sp ; setup stack | |
287 ; 1) receive data. only register values are evaluated | |
288 010004 004737 011206 call @#blkrcv ; todo: do not ignore errors | |
289 | |
290 | |
291 ; checksum ? | |
292 010010 004737 012250 call @#calccs ; calculate checksum to R0 | |
293 010014 020037 014002 cmp r0,@#xmcs ; compare with received checksum | |
294 010020 001414 beq 1$ | |
295 010022 012703 014004 mov #rspsta,r3 ; write into block 0: status, wordcount,len val0, val1, val ,,, | |
296 010026 012723 000001 mov #1,(r3)+ ; set opcode = error location #1 | |
297 010032 012723 000002 mov #2,(r3)+ ; word count: 2 more result words | |
298 010036 010023 mov r0,(r3)+ ; calculated checksum | |
299 010040 013723 014002 mov @#xmcs,(r3)+ ; received checksum | |
300 010044 000261 sec ; set error exception flag | |
301 010046 000137 010272 jmp @#doresp ; r3 = end of transmission buffer | |
302 | |
303 1$: | |
304 ; calculate addresses of wordcount and data for block 0 | |
305 010052 012703 014006 mov #xmbuff+2,r3 | |
306 010056 010337 000120 mov r3,@#req0wc ; addr of len of block 0 = "ptr to len" | |
307 010062 062703 000002 add #2,r3 | |
308 010066 010337 000122 mov r3,@#req0dt ; addr of data in block 0 | |
309 | |
310 | |
311 ; eval opcodes | |
312 ; 1) opcodes without parameters in block 0 | |
313 010072 013700 014004 mov @#xmopcd,r0 | |
314 | |
315 010076 020027 000007 cmp r0,#oprest | |
316 010102 001006 bne 10$ | |
317 010104 000005 reset | |
318 ; now return "OK" without data | |
319 010106 012703 014010 mov #rspdat,r3 ; point to data start = no data | |
320 010112 000241 clc ; clear error exception flag | |
321 010114 000137 010272 jmp @#doresp | |
322 | |
323 010120 020027 000001 10$: cmp r0,#opecho | |
324 010124 001002 bne 20$ | |
325 010126 000137 012436 jmp @#doecho ; echo block, implemented by serialxfer | |
326 | |
327 | |
328 20$: ; 2) opcodes with parameters in block 0, and optional data in block 1 | |
329 ; copy driver parameters from buffer to shadow | |
330 010132 013703 000120 mov @#req0wc,r3 ; point to len of block 0 | |
331 010136 004737 010336 call @#dupprm ; on exit: r3 = start of block 1, if any | |
332 ; calculate addresses wordcount and data for block 1 (used only for "write") | |
333 010142 010337 000124 mov r3,@#req1wc ; addr of len of block 1 | |
334 010146 062703 000002 add #2,r3 | |
335 010152 010337 000126 mov r3,@#req1dt ; addr of start of block 1 | |
336 | |
337 010156 020027 000006 30$: cmp r0,#ophalt | |
338 010162 001005 bne 40$ | |
339 ; exit driver with HALT or jump to monitor | |
340 010164 013700 000100 mov @#prmnea,r0 ; load argument for HALT | |
341 010170 001001 bne 31$ | |
342 010172 000000 halt ; if 0: execute HALT | |
343 010174 010007 31$: mov r0,pc ; if address: jump to MoNitor Entry Address | |
344 | |
345 010176 020027 000002 40$: cmp r0,#opinit | |
346 010202 001002 bne 50$ | |
347 010204 000137 010360 jmp @#doinit ; "initialize disk" implemented by driver | |
348 010210 020027 000003 50$: cmp r0,#opread | |
349 010214 001002 bne 60$ | |
350 010216 000137 010452 jmp @#doread ; "read sectors" implemented by driver | |
351 010222 020027 000004 60$: cmp r0,#opchek | |
352 010226 001002 bne 70$ | |
353 010230 000137 010460 jmp @#dochek ; "check sectors" implemented by driver | |
354 010234 020027 000005 70$: cmp r0,#opwrit | |
355 010240 001002 bne 80$ ; unknown opcode | |
356 010242 000137 010554 jmp @#dowrit ; "write sectors" implemented by driver | |
357 | |
358 80$: ; illegal opcode: generate error | |
359 010246 012703 014004 mov #rspsta,r3 ; write into buffer: status, wordcount,len val0, val1, val ,,, | |
360 010252 012723 000002 mov #2,(r3)+ ; set error location #2 | |
361 010256 012723 000001 mov #1,(r3)+ ; word count: 1 result words | |
362 010262 010023 mov r0,(r3)+ ; the illegal opcode | |
363 010264 000261 sec ; set error exception flag | |
364 010266 000137 010272 jmp @#doresp | |
365 | |
366 | |
367 ; --- doresp - end current operation and send result in response transmission | |
368 ; rspsta, rspdat are filled with error data or regular disk data. rspwc is calulated | |
369 ; r3: points to word after result buffer, is used to calculated | |
370 ; len of block 0 and transmission buffer word count. If noting is sent back, r3 == rspdat | |
371 ; Carry C = error flag. | |
372 ; Carry set: return exception, do not clear rspsta | |
373 ; Carry clear: return normal result | |
374 ; rspsta is cleared ("OK") | |
375 ; data is transmitted | |
376 | |
377 doresp: | |
378 ; in case of error, buffer is already filled with error data | |
379 010272 103001 bcc 8$ ; no carry, no error | |
380 ; -- error! error information is already in buffer | |
381 ; halt ; option to stop on error | |
382 010274 000402 br 9$ | |
383 | |
384 ; -- no error. data buffer and len is filled | |
385 010276 005037 014004 8$: clr @#rspsta ; clear status = OK | |
386 9$: ; transmit buffer back | |
387 ; r3 = buffer end: calculate transmission buffer count | |
388 ; transmission buffer: checksum + opcode + len + data | |
389 010302 162703 014010 sub #rspdat,r3 ; subtract buffer base. r3 = bytes in response data | |
390 010306 000241 clc | |
391 010310 006003 ror r3 ; bytes-> word: (unsigned)r3 /= 2 | |
392 ; asr r3 ; r3 = words in response data | |
393 010312 010337 014006 mov r3,@#rspdat-2 ; save data count in len field of block 0 | |
394 010316 062703 000003 add #3,r3 ; include response len (block 0) and checksum+opcode | |
395 010322 010337 014000 mov r3,@#xmwc | |
396 010326 004737 011536 call @#blkxmt ; transmit data, checksum is also calculated | |
397 | |
398 ; now wait for next command | |
399 010332 000137 010000 jmp @#dofunc | |
400 | |
401 ; halt ; only regular halt | |
402 | |
403 | |
404 | |
405 | |
406 ; --- dupprm | |
407 ; parameters in input block 0 must be copied to global variables area "reqprm" | |
408 ; since request buffer content may be overwritten bei read/error operation | |
409 ; driver must define global variables for parameter | |
410 ; framework uses "prmsta" and "prmlen" to copy parameters from transmission | |
411 ; input: r3 on len of block 0 | |
412 ; Exit: r3 is after block 0. if block0 is empty | |
413 dupprm: | |
414 ; copy input params from buffer to | |
415 010336 012702 000100 mov #reqprm,r2 : start of parameter buffer | |
416 010342 012301 mov (r3)+,r1 ; r1: word count := word len of block 0 | |
417 010344 001403 beq 9$ ; r1 = 0: stop | |
418 1$: | |
419 010346 012322 mov (r3)+,(r2)+ ; { *dst++ = *src++ | |
420 010350 005301 dec r1 | |
421 010352 001375 bne 1$ ; } while (--r1) | |
422 ; sob r1,1$ ; SOB not standard! (11/45?) | |
423 9$: | |
424 010354 000207 return ; r3 points to next word after block 0 | |
425 | |
426 | |
427 | |
428 ; | |
429 ; | |
430 ; 3.2 Driver specific code: | |
431 ; ======================== | |
432 ; | |
433 ; map parameters to buffer positions. example pdp11gui_rl02.mac | |
434 ; prcba = reqprm+2 ; parameter 0: controller base address | |
435 ; prunit = reqprm+4 ; parameter 1: unit number of disc: 0 or 1 | |
436 ; prcyl = reqprm+6 ; parameter 2: cylinder | |
437 ; prhead = reqprm+10 ; parameter 3: head, always 0 | |
438 ; prsect = reqprm+12 ; parameter 4: sector: 0..25 (not: 1..26!) | |
439 ; prflags = reqprm+14 ; parameter 5: flags | |
440 ; ; bit 0 = #1 = "suppress data transmission after read" | |
441 ; ; bit 1 = #2 = double density | |
442 ; ; bit 2 = #4 = RX02 | |
443 ; .... | |
444 ; map error registers to buffer | |
445 ; ;ressta = rxbuff+0 ; result status. common to all drivers, already defined | |
446 ; ;reswc = rxbuff+2 ; 2nd word is len. common to all drivers. here always "1", only RXES is returned | |
447 ; errcs = rspdat+0 ; param 0: RXCS register | |
448 ; | |
449 ; .include pdp11gui_main.mac | |
450 ; doinit: | |
451 ; ... | |
452 ; doread: | |
453 ; ... | |
454 ; dochek: | |
455 ; ... | |
456 ; dowrit: | |
457 ; ... | |
458 ; | |
459 ; ; -- error routine: check Hardware for error. | |
460 ; ; r5: "location mark" | |
461 ; X if ok: Carry clear | |
462 ; ; if error: Carry set, and buffer positions "err*" already set. | |
463 ; ; Transmission buffer and input parmeters are overwritten ! | |
464 ; on exit: R3 points to end of error buffer! | |
465 ; ;chkerr: | |
466 ; tst .... ; test a bit 15 | |
467 ; bpl 1$ ; no error | |
468 ; ; error! | |
469 ; ; input parameter in transmission buffer are now overwritten ! | |
470 ; mov r5,@#ressta ; set error code | |
471 ; mov #rspdat,r3 | |
472 ; mov #error_wordcount,@#reswc ; set word count. alwys the sameset error code | |
473 ; ... query "error_wordcount" controller registers and save in buffer locations "(r3)+" | |
474 ; sec ; set error flag | |
475 ; return ; r3 points to next word after error data | |
476 ; 1$: ; no error. do not overwrite transmission buffer | |
477 ; clc ; clear error flag | |
478 ; return | |
479 ; | |
480 ; | |
481 ; | |
481 | |
18 | |
19 ; Flags in parameter "prflags": not used | |
20 ; prtrack is 0..202 | |
21 ; prhead is 0..1 | |
22 ; prsector is 0..11 | |
23 | |
24 ; controller register offsets against base address | |
25 | |
26 000000 rkds =+0 ; drive status register | |
27 000002 rker =+2 ; error register | |
28 000004 rkcs =+4 ; control status register | |
29 000006 rkwc =+6 ; word count register | |
30 000010 rkba =+10 ; current bus address register | |
31 000012 rkda =+12 ; disk address register | |
32 000016 rkdb =+16 ; data buffer register | |
33 | |
34 | |
35 ; function opcodes (Bit 3:1 in rkcs), with GO bit set | |
36 000001 fncrst =0*2+1 ; Control Reset | |
37 000003 fnwrit =1*2+1 ; Write | |
38 000005 fnread =2*2+1 ; Read | |
39 000007 fnwchk =3*2+1 ; Write Check | |
40 000011 fnseek =4*2+1 ; Seek | |
41 000013 fnrchk =5*2+1 ; Read Check | |
42 000015 fndrst =6*2+1 ; Drive Reset | |
43 000017 fnwlck =7*2+1 ; Write Lock | |
44 | |
45 | |
46 ; global variables for this driver | |
47 010356 flgchk: .blkw 1 ; 1 = do not transmit read data back (= do "check operation") | |
48 | |
49 ; --------------- entry for "get drive information -------------------- | |
50 ; reset drive and reset controller | |
51 | |
52 doinit: ; RK11 executes "Control Reset", then "Drive Reset" | |
53 010360 013704 000100 mov @#prcba,r4 ; r4 = always controller base address | |
54 010364 012764 000001 000004 mov #fncrst,rkcs(r4) ; reset controller | |
55 | |
56 010372 105764 000004 1$: tstb rkcs(r4) ; check controller ready | |
57 010376 100375 bpl 1$ ; loop while 0 | |
58 | |
59 ; select drive | |
60 010400 005002 clr r2 | |
61 010402 005003 clr r3 | |
62 010404 004737 010704 call @#setreg ; set all data, only drive selectin rkda matters here | |
63 010410 012764 000015 000004 mov #fndrst,rkcs(r4) ; reset drive | |
64 | |
65 010416 032764 000100 000000 2$: bit #100,rkds(r4) ; wait for Read/Write/seek READY | |
66 010424 001774 beq 2$ | |
67 | |
68 | |
69 010426 012703 014010 mov #rspdat,r3 ; if no error: return no data | |
70 010432 012705 000101 mov #101,r5 ; error location #101 | |
71 010436 004737 010626 call @#chkerr ; wait and check | |
72 010442 004737 010626 call @#chkerr ; permanent errors ? | |
73 | |
74 | |
75 ; carry is error flag | |
76 010446 000137 010272 jmp @#doresp | |
77 | |
78 | |
79 ; --------------- entry for read and / check-------------------- | |
80 | |
81 doread: | |
82 010452 005037 010356 clr @#flgchk ; | |
83 010456 000403 br dordch | |
84 dochek: | |
85 010460 012737 000001 010356 mov #1,@#flgchk ; set flag to inhibit data transmission | |
86 dordch: | |
87 | |
88 010466 013704 000100 mov @#prcba,r4 ; r4 = always controller base address | |
89 | |
90 010472 012705 000102 mov #102,r5 ; error location | |
91 | |
92 010476 012703 014010 mov #rspdat,r3 ; r3 = start of result block | |
93 010502 013702 000106 mov @#prwlen,r2 ; wordcount to read | |
94 | |
95 010506 004737 010704 call @#setreg ; setup registers for read/write. input=r2,r3,unit/track/gead/sector | |
96 | |
97 010512 004737 011020 call @#readsc ; read one or many sectors | |
98 010516 103414 bcs 9$ ; error exit | |
99 ; now "prwlen" words have been read into "rspdat". advance r3 | |
100 010520 012703 014010 mov #rspdat,r3 ; reset r3 to begin of data of block 0 = "empty" | |
101 010524 013702 000106 mov @#prwlen,r2 ; wordcount to read | |
102 010530 060203 add r2,r3 | |
103 010532 060203 add r2,r3 ; r3 += byte count | |
104 | |
105 ; exit without error, but optionally suppress data | |
106 ; -- writing output params destroys input params! | |
107 010534 005737 010356 tst @#flgchk | |
108 010540 001402 beq 1$ | |
109 010542 012703 014010 mov #rspdat,r3 ; reset r3 to begin of data of block 0 = "empty" | |
110 1$: | |
111 010546 000241 clc ; clear error flag | |
112 9$: | |
113 010550 000137 010272 jmp @#doresp ; r3 = end of buffer | |
114 | |
115 | |
116 ; --------------- entry for write -------------------- | |
117 | |
118 dowrit: | |
119 010554 013704 000100 mov @#prcba,r4 ; r4 = always controller base address | |
120 | |
121 ; r3 = pointer in result buffer = data area of request block 1 | |
122 | |
123 010560 012705 000105 mov #105,r5 ; error location | |
124 010564 013703 000126 mov @#req1dt,r3 ; r3 = data of request data block 1 | |
125 010570 013702 000124 mov @#req1wc,r2 ; r2 = word count to write | |
126 010574 011202 mov (r2),r2 ; r2 is addr of len | |
127 010576 004737 010704 call @#setreg ; setup registers for read/write. input=r2,r3 | |
128 | |
129 010602 012705 000107 mov #107,r5 ; error location | |
130 010606 004737 011052 call @#writsc ; write one or many sectors | |
131 010612 103403 bcs 9$ ; error exit | |
132 | |
133 ; exit without error and without response data | |
134 010614 012703 014010 mov #rspdat,r3 ; r3 = start of result block 0 = no data | |
135 010620 000241 clc ; clear error flag | |
136 9$: | |
137 010622 000137 010272 jmp @#doresp | |
138 | |
139 | |
140 | |
141 ; -------- check for error | |
142 ; 1) checks error bits in rkcs | |
143 ; 2) if error: return 4 registers | |
144 ; r5 must contain error location | |
145 ; result: 1st word = rkds, 2nd word=rker. 3rd= rkcs, 4th=rkda | |
146 chkerr: | |
147 ; verify controller ready | |
148 010626 105764 000004 0$: tstb rkcs(r4) ; test for "controller ready" | |
149 010632 100375 bpl 0$ ; wait | |
150 | |
151 010634 016400 000004 mov rkcs(r4),r0 | |
152 010640 100402 bmi 1$ ; bit 15 = Any error | |
153 | |
154 010642 000241 clc | |
155 010644 000207 return ; CSR = R1 = 0: no error | |
156 | |
157 1$: ; error! | |
158 010646 012703 014004 mov #rspsta,r3 ; r3 = pointer to response block 0 | |
159 010652 010523 mov r5,(r3)+ ; result status = error location | |
160 010654 012723 000004 mov #4,(r3)+ ; 4 error words following | |
161 | |
162 010660 016423 000000 mov rkds(r4),(r3)+ ; 1st word = rkds | |
163 010664 016423 000002 mov rker(r4),(r3)+ ; 2nd word=rker. | |
164 010670 016423 000004 mov rkcs(r4),(r3)+ ;3rd= rkcs, | |
165 010674 016423 000012 mov rkda(r4),(r3)+ ; 4th=rkda | |
166 010700 000261 sec ; error flag | |
167 010702 000207 return | |
168 | |
169 | |
170 ; -------------------------------------------------- | |
171 ; setreg | |
172 ; set up controller registers: | |
173 ; fills DA with cylinder, head, sector | |
174 ; sets BA with address R3 | |
175 ; sets WC | |
176 ; | |
177 ; r2 = word count (not complemented) | |
178 ; r3 = buffer address | |
179 ; read: r3 = response buffer | |
180 ; write: r3 = request buffer 1 | |
181 ; r4 = controller base | |
182 ; r5 = error location | |
183 setreg: | |
184 ; --- setup disk address | |
185 010704 013701 000102 mov @#prunit,r1 ; 3 bit drive select -> bits <15:13> | |
186 010710 042701 177770 bic #177770,r1 ; r1 &= 07 | |
187 010714 000241 clc | |
188 010716 006001 ror r1 ; rotate <2:0> into <15:13> through carry | |
189 010720 006001 ror r1 | |
190 010722 006001 ror r1 | |
191 010724 006001 ror r1 | |
192 010726 013700 000110 mov @#prcyl,r0 ; 8bit cylinder addr -> bits <12:5> | |
193 010732 042700 177400 bic #177400,r0 ; r0 &= 0377 | |
194 010736 006300 asl r0 | |
195 010740 006300 asl r0 | |
196 010742 006300 asl r0 | |
197 010744 006300 asl r0 | |
198 010746 006300 asl r0 | |
199 010750 050001 bis r0,r1 | |
200 010752 013700 000112 mov @#prhead,r0 | |
201 010756 001402 beq 1$ | |
202 010760 052701 000020 bis #20,r1 ; head 1: set bit 4 "SUR" | |
203 1$: | |
204 010764 013700 000114 mov @#prsect,r0 | |
205 010770 042700 177760 bic #177760,r0 ; r0 &= 017 | |
206 010774 050001 bis r0,r1 ; add to result | |
207 010776 010164 000012 mov r1,rkda(r4) | |
208 | |
209 ; --- setup word count | |
210 011002 010201 mov r2,r1 ; r1 = word count | |
211 011004 005401 neg r1 | |
212 011006 010164 000006 mov r1,rkwc(r4) ; 2s complement | |
213 | |
214 ; --- setup data bus address | |
215 011012 010364 000010 mov r3,rkba(r4) | |
216 ; address extension bits <5:4> MEX in rkcs must be 0 | |
217 | |
218 011016 000207 return | |
219 | |
220 | |
221 | |
222 ;----------------------------------------------------------------------- | |
223 ; readsc - read sectors | |
224 ; r4 is controller base addr | |
225 ; r5 is error location | |
226 ; setreg must have been run | |
227 readsc: ; read sectors | |
228 011020 012764 000005 000004 mov #fnread,rkcs(r4) ; read and GO | |
229 | |
230 011026 105764 000004 0$: tstb rkcs(r4) ; check controller ready | |
231 011032 100375 bpl 0$ ; loop while 0 | |
232 | |
233 011034 032764 000100 000000 1$: bit #100,rkds(r4) ; wait for Read/Write/seek READY | |
234 011042 001774 beq 1$ | |
235 | |
236 011044 004737 010626 call @#chkerr ; carry = error | |
237 011050 000207 return | |
238 | |
239 | |
240 ;----------------------------------------------------------------------- | |
241 ; writsc -write sectors | |
242 ; r4 is controller base addr | |
243 ; R5 = error location | |
244 writsc: ; write sectors | |
245 011052 012764 000003 000004 mov #fnwrit,rkcs(r4) ; write and GO | |
246 | |
247 011060 105764 000004 0$: tstb rkcs(r4) ; check controller ready | |
248 011064 100375 bpl 0$ ; loop while 0 | |
249 | |
250 011066 032764 000100 000000 1$: bit #100,rkds(r4) ; wait for Read/Write/seek READY | |
251 011074 001774 beq 1$ | |
252 | |
253 011076 004737 010626 call @#chkerr ; carry = error | |
254 011102 000207 return | |
255 | |
256 | |
257 .include pdp11gui_aux.mac | |
1 .title aux | |
2 | |
3 ; utilities | |
4 | |
5 ; memclr | |
6 ; clears r0..r5 | |
7 regclr: | |
8 011104 005000 clr r0 | |
9 011106 005001 clr r1 | |
10 011110 005002 clr r2 | |
11 011112 005003 clr r3 | |
12 011114 005004 clr r4 | |
13 011116 005005 clr r5 | |
14 011120 000207 return | |
15 | |
16 ; memclr | |
17 ; clears R2 words from R0 | |
18 ; Return: R2 is 0, R0 is incremeneted | |
19 memclr: | |
20 1$: | |
21 011122 005702 tst r2 ; while(n) { | |
22 011124 001403 beq 2$ | |
23 011126 005020 clr (r0)+ ; *dst++ = 0 | |
24 011130 005302 dec r2 ; n-- | |
25 011132 000773 br 1$ ; } | |
26 2$: | |
27 011134 000207 return | |
28 | |
29 ; memset | |
30 ; fills memory with double-word pattern | |
31 ; sets R3 words from R0 to value in R1,R2 | |
32 ; r3 must be even! | |
33 ; Return: R3 is 0, R0 is incremented | |
34 ; Speed is essential for RLE decoding | |
35 ; Speed calculation: n*3 cycles | |
36 memset: | |
37 011136 005703 tst r3 | |
38 011140 001404 beq 9$ ; already 0 | |
39 1$: | |
40 011142 010120 mov r1,(r0)+ ; *dst++ = pattern_1 | |
41 011144 005303 dec r3 | |
42 011146 010220 mov r2,(r0)+ ; *dst++ = pattern_2 | |
43 011150 077304 sob r3,1$ | |
44 9$: | |
45 011152 000207 return | |
46 | |
47 | |
48 ; memcpy | |
49 ; copy R2 words from R0 to R1 | |
50 ; Return: R0, R1 are incremented, R2 is 0 | |
51 memcpy: | |
52 1$: | |
53 011154 005702 tst r2 ; while(n) { | |
54 011156 001403 beq 2$ | |
55 011160 012021 mov (r0)+,(r1)+ ; *dst++ = *src++ | |
56 011162 005302 dec r2 ; n-- | |
57 011164 000773 br 1$ ; } | |
58 2$: | |
59 011166 000207 return | |
60 | |
61 ; warte 64k cpu cycles | |
62 wait64k: | |
63 011170 010046 mov r0,-(sp) | |
64 011172 012700 100000 mov #100000,r0 r0 ; loop hat 2 cycles: load with 32k | |
65 011176 005300 1$: dec r0 | |
66 011200 001376 bne 1$ | |
67 011202 012600 mov (sp)+,r0 | |
68 011204 000207 return | |
69 | |
69 | |
258 | |
259 .include pdp11gui_serialxfer.mac | |
1 .title serialxfer | |
2 | |
3 ; high speed buffer access over serial port 0 | |
4 ; | |
5 ; Compression: | |
6 ; like uuencode / base64 | |
7 ; Each serial char defines 6 bits, char is in range 0x20 .. 0x5f | |
8 ; 8 chars defines 3 words | |
9 ; Char # : <.0..> <..1..> <..2..> <.3..> <.4..> <..5..> <..6..> <.7..> | |
10 ; Char Bits : 543210 54 3210 5432 10 543210 543210 54 3210 5432 10 543210 | |
11 ; bytes : 765432 10 7654 3210 76 543210 765432 10 7654 3210 76 543210 | |
12 ; byte # : <...0...> <...1...> <...2...> <..3....> <...4...> <...5...> | |
13 ; word # : <...msb0....lsb0..> <...msb1....lsb1..> <..msb2.....lsb2..> | |
14 ; | |
15 ; bytes : 000000 00 0000 0000 76 543210 765432 10 7654 3210 76 543210 | |
16 | |
17 ; Layer1: Complete transmission format, encodes a list of 16 bit words | |
18 ; <STARTCHAR> [<RLECHAR>] <char octet> ... <ENDCHAR> | |
19 ; | |
20 ; - characters ' ' = 0x20 = 040 to '_' = 0x5F = 0137 encode data | |
21 ; - characters below #$20 are to be ignored | |
22 ; - '{' = 0x7b = 0173 is STARTCHAR, '}' = 0x7D = 0175 is ENDCHAR | |
23 ; - all other chars instead of START are ignored. '~' is used as delay char. | |
24 ; - RLE encoding: a repeating pattern of 2 words is recognized. | |
25 ; The indicator char RLECHAR '|' before char[0] modifies the meaning of | |
26 ; the next 8 char/3 word block: | |
27 ; '|', char[0], ... char[7] => RLE, word[0], word[1], word[2] | |
28 ; with word[0] = block_len, word[1] = pattern_1, word[2] = pattern_2 | |
29 ; ! Number of encoded words = block_len => block_len always even ! | |
30 ; - RLE Block length processing time: | |
31 ; PDP-11 CPU must copy <blocklen> words within one character transmission time. | |
32 ; blocklen is limited by high baudrates and slow CPUs. | |
33 ; processing time per RLE block: 100 cycles fix, + 6 micros per word copy | |
34 ; Time of one char transmission: | |
35 ; 38400 baud , 10 bit/char: t = 1000000/38400*10 = 260 micro sec per char | |
36 ; - after { and before first data char, the 16 bit sum of all data words | |
37 ; in buffer is send | |
38 ; - transmit | |
39 ; 1. send <CR> { | |
40 ; 2. send data octets | |
41 ; 3. send "} <CR>" | |
42 ; - receive: | |
43 ; 1. discard characters, until "{" is received | |
44 ; 3. receive and decode char-octets, until '}' is received | |
45 ; 4. discard "}" | |
46 ; | |
47 ; For blockread,write, this convention is used: | |
48 ; - Operation is always | |
49 ; 1. receive data | |
50 ; 2. execute code | |
51 ; 3. transmit data | |
52 ; | |
53 ; BlockRead: | |
54 ; 1. receive checksum word | |
55 ; 2. receive block of 6 words, wich contain values for R0..R5 | |
56 ; 3. start BlockRead-Procedure with R0..R5 | |
57 ; 4. send block of checksum + 6 + bufferlen words. | |
58 ; word[0] is checksum, words[1..6] contain exit values for R0..R5 | |
59 ; BlockWrite | |
60 ; 1. receive block of 1 +6 + bufferlen words, | |
61 ; 2. start BlockWrite-Procedure with R0..R5 and write buffer | |
62 | |
63 ; This source get's .INCLUded, so define no offset! | |
64 ; | |
65 | |
66 ; .asect | |
67 ; .=1400 | |
68 | |
69 ; -------------------------------------------------- | |
70 | |
71 | |
72 | |
73 | |
74 ; -------------------------------------------------- | |
75 ; BLKRCV - block receive | |
76 ; Receive coded characters, and save resulting words in buffer | |
77 ; Input | |
78 ; Result: buffer at "rxbffr" filled with triplets | |
79 ; rxbfcs checksum as transmitted | |
80 ; rxbfln = word count as received | |
81 blkrcv: | |
82 ; mov #100000,r2 ; trace rcv chars | |
83 | |
84 011206 012700 014000 mov #xmbffr,r0 ; start of buffer frame | |
85 011212 005001 clr r1 ; counter | |
86 ; test daten | |
87 ; mov #201,r1 ; test data word count = 129 = 0x81 | |
88 ; mov #rcvtst,r2 ; ptr to test data | |
89 | |
90 ; wait for '{' == 173 | |
91 0$: | |
92 011214 004737 012612 call @#rcvchr | |
93 011220 122705 000173 cmpb #'{,r5 | |
94 011224 001373 bne 0$ | |
95 | |
96 1$: ; do { | |
97 011226 005002 clr r2 ; clear RLE indicator | |
98 011230 005004 clr r4 ; assemble word[0] | |
99 011232 004737 012530 call @#rcvbit ; r5 = char[0] | |
100 011236 103534 bcs 3$ ; carry set: end | |
101 011240 102004 bvc 2$ ; overflow not set: receive 3 literal words | |
102 ; RLE indicator: next 3 words are <repeat> <value> <don't care> | |
103 011242 005202 inc r2 ; set RLE indicator | |
104 011244 004737 012530 call @#rcvbit ; receive again: r5 = char[0] | |
105 011250 103527 bcs 3$ ; carry set: end | |
106 2$: | |
107 011252 012703 000012 mov #12,r3 | |
108 011256 004737 012120 call @#sl5or4 ; r4 |= (r5 << 10) | |
109 011262 004737 012530 call @#rcvbit ; r5 = char[1] | |
110 011266 103520 bcs 3$ ; carry set: end | |
111 011270 012703 000004 mov #4,r3 | |
112 011274 004737 012120 call @#sl5or4 ; r4 |= (r5 << 4) | |
113 011300 004737 012530 call @#rcvbit ; r5 = char[2] | |
114 011304 103511 bcs 3$ ; carry set: end | |
115 011306 012703 000002 mov #2,r3 | |
116 011312 004737 012144 call @#sr5or4 ; r4 |= (r5 >> 2) | |
117 011316 010420 mov r4,(r0)+ ; word[0] fertig | |
118 011320 005201 inc r1 | |
119 | |
120 011322 005004 clr r4 ; assemble word[1] | |
121 011324 012703 000016 mov #16,r3 | |
122 011330 004737 012120 call @#sl5or4 ; r4 |= (r5 << 14) | |
123 011334 004737 012530 call @#rcvbit ; r5 = char[3] | |
124 011340 103473 bcs 3$ ; carry set: end | |
125 011342 012703 000010 mov #10,r3 | |
126 011346 004737 012120 call @#sl5or4 ; r4 |= (r5 << 8) | |
127 011352 004737 012530 call @#rcvbit ; r5 = char[4] | |
128 011356 103464 bcs 3$ ; carry set: end | |
129 011360 012703 000002 mov #2,r3 | |
130 011364 004737 012120 call @#sl5or4 ; r4 |= (r5 << 2) | |
131 011370 004737 012530 call @#rcvbit ; r5 = char[5] | |
132 011374 103455 bcs 3$ ; carry set: end | |
133 011376 012703 000004 mov #4,r3 | |
134 011402 004737 012144 call @#sr5or4 ; r4 |= (r5 >> 4) | |
135 011406 010420 mov r4,(r0)+ ; word[1] fertig | |
136 011410 005201 inc r1 | |
137 | |
138 011412 005004 clr r4 ; assemble word[2] | |
139 011414 012703 000014 mov #14,r3 | |
140 011420 004737 012120 call @#sl5or4 ; r4 |= (r5 << 12) | |
141 011424 004737 012530 call @#rcvbit ; r5 = char[6] | |
142 011430 103437 bcs 3$ ; carry set: end | |
143 011432 012703 000006 mov #6,r3 | |
144 011436 004737 012120 call @#sl5or4 ; r4 |= (r5 << 6) | |
145 011442 004737 012530 call @#rcvbit ; r5 = char[7] | |
146 011446 103430 bcs 3$ ; carry set: end | |
147 011450 012703 000000 mov #0,r3 | |
148 011454 004737 012120 call @#sl5or4 ; r4 |= (r5 << 0) | |
149 | |
150 011460 010420 mov r4,(r0)+ | |
151 011462 005201 inc r1 | |
152 | |
153 ; 3 words received. Interpret as RLE? | |
154 011464 005702 tst r2 ; if (!is_rle) | |
155 011466 001657 beq 1$ ; continue ; | |
156 ; last triplett was RLE block, not 3 data words | |
157 011470 162700 000006 sub #6,r0 ; remove last triplett from data buffer | |
158 011474 162701 000003 sub #3,r1 | |
159 011500 010105 mov r1,r5 ; save r1=word count | |
160 011502 011004 mov (r0),r4 ; r4 = rle_count | |
161 011504 016001 000002 mov 2(r0),r1 ; rle pattern_1 | |
162 011510 016002 000004 mov 4(r0),r2 ; rle pattern 2 | |
163 011514 010403 mov r4,r3 ; count=rle_block_len. Must be even! | |
164 011516 004737 011136 call @#memset ; r0=ptr, r1 = value1, r2=value2, r3=count. | |
165 ; r0 is now at next unwritten buffer pos, r2=0 | |
166 011522 010501 mov r5,r1 ; restore word count | |
167 011524 060401 add r4,r1 ; word count += rel_count | |
168 011526 000637 br 1$ ; } while(! aborted) | |
169 | |
170 3$: ; end of transmission | |
171 ; r1 is actual word count, it could be | |
172 ; checked here against transmitted word count | |
173 | |
174 ; checksum must be processed by caller | |
175 011530 000241 clc ; clear error flag | |
176 | |
177 011532 000207 return | |
178 | |
179 | |
180 | |
181 | |
182 | |
183 ; -------------------------------------------------- | |
184 ; BLKXMT - block transmit | |
185 ; Get words from buffer and send as coded characters | |
186 ; Input: | |
187 ; rxbfwc = total size (len+checksum+opcode+params+data) | |
188 ; Result: | |
189 ; R0: buffer address (overwritten) | |
190 ; R1: word count (multiple of 3) | |
191 | |
192 011534 rsp0en: .blkw 1 ; pointer to word after nd of block 0 | |
193 | |
194 blkxmt: | |
195 ; clear out fill words at buffer end | |
196 011536 013700 014000 mov @#xmwc,r0 ; wordcount without len | |
197 011542 005200 inc r0 | |
198 011544 006300 asl r0 ; r0 = byte len of buffer without fill chars | |
199 011546 062700 014000 add #xmbffr,r0 ; r0 = bufferend | |
200 011552 010037 011534 mov r0,@#rsp0en ; save end of buffer | |
201 011556 005020 clr (r0)+ ; clear next 3 words | |
202 011560 005020 clr (r0)+ | |
203 011562 005020 clr (r0)+ | |
204 | |
205 ; calculate and save checksum | |
206 011564 004737 012250 call @#calccs | |
207 011570 010037 014002 mov r0,@#xmcs | |
208 | |
209 011574 012700 014000 mov #xmbffr,r0 ; transmit | |
210 011600 013701 014000 mov @#xmwc,r1 | |
211 011604 062701 000002 add #2,r1 ; transmit word count, checksum, rxbuff | |
212 | |
213 ; sende <cr>,{ | |
214 011610 012705 000015 mov #15,r5 | |
215 011614 004737 012476 call @#xmtchr ; send cr | |
216 011620 012705 000173 mov #'{,r5 ; 173 | |
217 011624 004737 012476 call @#xmtchr ; send start character '{' | |
218 | |
219 1$: ; do { | |
220 | |
221 ; try rle compression of next data words | |
222 ; r5 = word after buffer end | |
223 011630 013705 011534 mov @#rsp0en,r5 ; r5 = ptr to word after data buffer end | |
224 011634 004737 012300 call @#encrle ; encode rle compression, data r0, count = r1, r5 = limit | |
225 011640 103406 bcs 2$ ; if not successful: r0, r1 unchanged, carry not set | |
226 ; rle compression successful. | |
227 ; r0 += blocklen-3 (last 3 word in buffer at rle end are used as buffer | |
228 ; for rle triplett. | |
229 ; triplett[r0] contains rle triplett. | |
230 ; r1 -= rle_count | |
231 011642 062701 000003 add #3,r1 ; // subroutine trpxmt decrements r1 | |
232 | |
233 | |
234 ; rle triplett was written into data buffer at r0. | |
235 011646 012705 000174 mov #'|,r5 | |
236 011652 004737 012476 call @#xmtchr ; transmit '|': rle indicator | |
237 | |
238 2$: ; send next simple data triplett, or rle triplet | |
239 | |
240 011656 004737 011702 call @#trpxmt ; transmit triple (= 3 words) from r0 | |
241 | |
242 3$: ; simple triplett or rle triplett send | |
243 011662 005701 tst r1 | |
244 011664 003361 bgt 1$ ; } while(count > 0) | |
245 | |
246 011666 012705 000175 mov #'},r5 ; 175 | |
247 011672 004737 012476 call @#xmtchr ; send end character '}' | |
248 | |
249 ; Wartepause, damit bei kommendem HALT der serial xmt buffer frei ist? | |
250 ; bewirkt receive timeout?? | |
251 ; call @#wait64k ; wait ca. 64 millisek | |
252 011676 000241 clc ; clear error flag | |
253 011700 000207 return | |
254 | |
255 ; -------------------------------------------------- | |
256 ; trpxmt - TRiPle TransMT | |
257 ; encode and transmit next triple = 3 words from r0 | |
258 ; globals: r0: dta ptr, inkremented += 3 | |
259 ; r1: count, -= 3 | |
260 ; r3,r4,r5 : changed, scratch | |
261 ; | |
262 trpxmt: | |
263 011702 012004 mov (r0)+,r4 ; r4 = word[0] | |
264 011704 005301 dec r1 ; count-- | |
265 011706 012703 000012 mov #12,r3 | |
266 011712 005005 clr r5 | |
267 011714 004737 012170 call @#sr4or5 ; r5 = (r4 >> 10) & 0x3f | |
268 011720 004737 012466 call @#xmtbit ; send char[0] | |
269 011724 012703 000004 mov #4,r3 | |
270 011730 005005 clr r5 | |
271 011732 004737 012170 call @#sr4or5 ; r5 = (r4 >> 4) & 0x3f | |
272 011736 004737 012466 call @#xmtbit ; send char[1] | |
273 011742 012703 000002 mov #2,r3 | |
274 011746 005005 clr r5 | |
275 011750 004737 012220 call @#sl4or5 ; r5 = (r4 << 4) & 0x3f // set upper 4 bits of r5 | |
276 | |
277 011754 012004 mov (r0)+,r4 ; r4 = word[1] | |
278 011756 005301 dec r1 ; count-- | |
279 011760 012703 000016 mov #16,r3 | |
280 011764 004737 012170 call @#sr4or5 ; r5 |= (r4 >> 14) & 0x3f // set lower 2 bits of r5 | |
281 011770 004737 012466 call @#xmtbit ; send char[2] | |
282 011774 012703 000010 mov #10,r3 | |
283 012000 005005 clr r5 | |
284 012002 004737 012170 call @#sr4or5 ; r5 = (r4 >> 8) & 0x3f | |
285 012006 004737 012466 call @#xmtbit ; send char[3] | |
286 012012 012703 000002 mov #2,r3 | |
287 012016 005005 clr r5 | |
288 012020 004737 012170 call @#sr4or5 ; r5 = (r4 >> 2) & 0x3f | |
289 012024 004737 012466 call @#xmtbit ; send char[4] | |
290 012030 012703 000004 mov #4,r3 | |
291 012034 005005 clr r5 | |
292 012036 004737 012220 call @#sl4or5 ; r5 = (r4 << 4) & 0x3f // set upper 2 bits of r5 | |
293 012042 012004 mov (r0)+,r4 ; r4 = word[2] | |
294 012044 005301 dec r1 ; count-- | |
295 012046 012703 000014 mov #14,r3 | |
296 012052 004737 012170 call @#sr4or5 ; r5 |= (r4 >> 12) & 0x3f | |
297 012056 004737 012466 call @#xmtbit ; send char[5] | |
298 012062 012703 000006 mov #6,r3 | |
299 012066 005005 clr r5 | |
300 012070 004737 012170 call @#sr4or5 ; r5 = (r4 >> 6) & 0x3f | |
301 012074 004737 012466 call @#xmtbit ; send char[6] | |
302 012100 012703 000000 mov #0,r3 | |
303 012104 005005 clr r5 | |
304 012106 004737 012170 call @#sr4or5 ; r5 = r4 & 0x3f | |
305 012112 004737 012466 call @#xmtbit ; send char[7] | |
306 012116 000207 return | |
307 | |
308 | |
309 ; -------------------------------------------------- | |
310 ; sr5or4 | |
311 ; sl5or4 | |
312 ; shift r5 by r3 left/right and OR to r4 | |
313 ; r5 not changed | |
314 sl5or4: | |
315 012120 010546 mov r5,-(sp) | |
316 012122 005703 tst r3 ; while(r3) { | |
317 1$: | |
318 012124 001404 beq 2$ | |
319 012126 000241 clc ; clr carry | |
320 012130 006105 rol r5 ; r5 <<=1 | |
321 012132 005303 dec r3 | |
322 012134 000773 br 1$ ; } | |
323 2$: | |
324 012136 050504 bis r5,r4 ; r4 |= r5 | |
325 012140 012605 mov (sp)+,r5 | |
326 012142 000207 return | |
327 | |
328 sr5or4: | |
329 012144 010546 mov r5,-(sp) | |
330 012146 005703 tst r3 ; while(r3) { | |
331 1$: | |
332 012150 001404 beq 2$ | |
333 012152 000241 clc ; clr carry | |
334 012154 006005 ror r5 ; r5 >>=1 | |
335 012156 005303 dec r3 | |
336 012160 000773 br 1$ ; } | |
337 2$: | |
338 012162 050504 bis r5,r4 ; r4 |= r5 | |
339 012164 012605 mov (sp)+,r5 | |
340 012166 000207 return | |
341 | |
342 | |
343 ; -------------------------------------------------- | |
344 ; sr4or5 | |
345 ; sl4or5 | |
346 ; shift r4 by r3 left/right and OR bits 5..0 to r5 | |
347 ; r4 not changed | |
348 sr4or5: | |
349 012170 010446 mov r4,-(sp) | |
350 012172 005703 tst r3 ; while(r3) { | |
351 1$: | |
352 012174 001404 beq 2$ | |
353 012176 000241 clc ; clr carry | |
354 012200 006004 ror r4 ; r4 >>= r3 | |
355 012202 005303 dec r3 | |
356 012204 000773 br 1$ ; } | |
357 2$: | |
358 012206 042704 000300 bic #300,r4 | |
359 012212 050405 bis r4,r5 ; r5 |= (r4 & 0x3f) | |
360 012214 012604 mov (sp)+,r4 | |
361 012216 000207 return | |
362 | |
363 sl4or5: | |
364 012220 010446 mov r4,-(sp) | |
365 012222 005703 tst r3 ; while(r3) { | |
366 012224 001404 1$: beq 2$ | |
367 012226 000241 clc ; clr carry | |
368 012230 006104 rol r4 ; r4 <<= r3 | |
369 012232 005303 dec r3 | |
370 012234 000773 br 1$ ; } | |
371 2$: | |
372 012236 042704 000300 bic #300,r4 | |
373 012242 050405 bis r4,r5 ; r5 |= (r4 & 0x3f) | |
374 012244 012604 mov (sp)+,r4 | |
375 012246 000207 return | |
376 | |
377 | |
378 | |
379 ; --------------------------------------------------- | |
380 ; calccs | |
381 ; calculate sum of all data words in buffer "rxbuff" | |
382 ; (without checksum itself) | |
383 ; and return in r0 | |
384 ; changes r0, r1, r2 | |
385 calccs: | |
386 012250 005000 clr r0 | |
387 012252 012702 014004 mov #xmbuff,r2 ; r2 point to second word of buffer, after checksum | |
388 012256 013701 014000 mov @#xmwc,r1 ; r1 = word counter, includes checksum | |
389 012262 005301 dec r1 | |
390 012264 001403 beq 9$ ; r1 == 0: exit | |
391 1$: | |
392 012266 062200 add (r2)+,r0 ; | |
393 012270 005301 dec r1 | |
394 012272 001375 bne 1$ ; loop while r1 > 0 | |
395 9$: | |
396 012274 000207 return | |
397 | |
398 | |
399 ; --------------------------------------------------------- | |
400 ; encrle | |
401 ; try to ENCode data at r0 as RLE block | |
402 ; r0: pointer into buffer | |
403 ; r1: word count | |
404 ; r5: pointer to word after buffer end | |
405 ; return | |
406 ; carry clear: compression succesfull | |
407 ; r0 points to | |
408 ; rle triplett: <repeat> <pattern_1> <pattern_2> is written | |
409 ; directly before the next uncompressable word in data buffer: | |
410 ; r0 points to rle triplett. | |
411 ; r1 is decremented by rle block len | |
412 ; carry set : compression unsuccessful: | |
413 ; less than 3 equal data words. | |
414 ; data and r0, r1 unchanged | |
415 ; | |
416 ; Since buffer data is overwritten with rle triplett, | |
417 ; rle must find at least 4 equal data words. | |
418 ; original buffer w w w | |
419 ; Over written with repeat pattern_1 pattern_2 | |
420 012276 encrl1: .blkw 1 ; backup r0 | |
421 | |
422 encrle: ; encode rle compression, data r0, count = r1 | |
423 ; sec ; disable RLE encoding | |
424 ; return | |
425 | |
426 ; limit: rle block len max 256 | |
427 012300 010037 012276 mov r0,@#encrl1 ; backup original r0 | |
428 012304 010002 mov r0,r2 ; r2 = data ptr | |
429 012306 010004 mov r0,r4 ; r4 = ptr to repeated pattern | |
430 012310 012703 000002 mov #2,r3 ; block_len | |
431 012314 062702 000004 add #4,r2 ; skip to word behind pattern | |
432 ; loop, until | |
433 1$: | |
434 ; r2 still two words before buffer end? | |
435 012320 010200 mov r2,r0 | |
436 012322 062700 000002 add #2,r0 | |
437 012326 020005 cmp r0,r5 ; if (r2+1) < r5 then .... | |
438 012330 103017 bhis 2$ ; stop, if r2 reached end of buffer | |
439 | |
440 ; are the 2 words at r2 identical to the pattern at r4 ? | |
441 012332 021214 cmp (r2),(r4) | |
442 012334 001015 bne 2$ ; not identical | |
443 012336 026264 000002 000002 cmp 2(r2),2(r4) | |
444 012344 001011 bne 2$ ; not identical | |
445 012346 060227 000004 add r2,#4 ; skip compressed words | |
446 | |
447 012352 062703 000002 add #2,r3 ; inc block_len by 2 words | |
448 012356 062702 000004 add #4,r2 ; inc data pointer by 2 words | |
449 ; limit len of rle blocks. PC CPU must decompress whole block within | |
450 ; one character receive time. PD11 CPU can 32 loops, modern x86 surely > 1000 | |
451 012362 020327 000377 cmp r3,#377 ; more than 256 encoded words? | |
452 012366 003754 ble 1$ ; no: try again | |
453 | |
454 2$: ; end of compression loop | |
455 012370 020327 000004 cmp r3,#4 | |
456 012374 003414 ble 4$ ; to few data compressed: compression failure | |
457 | |
458 ; compression success: | |
459 ; overwrite data buffer with rle triplett: <block_len> <pattern_1> <pattern_2> | |
460 012376 010200 mov r2,r0 | |
461 012400 162700 000006 sub #6,r0 ; point r0 to triplet directly before end of block in data buffer | |
462 ; sub #6: triplett len | |
463 012404 160301 sub r3,r1 ;r1 = words left in buffer | |
464 | |
465 012406 010310 mov r3,(r0) ; repeat count | |
466 012410 011460 000002 mov (r4),2(r0) ; pattern_1 | |
467 012414 016460 000002 000004 mov 2(r4),4(r0) ; pattern_2 | |
468 012422 000241 clc ; clear carry | |
469 012424 000403 br 9$ | |
470 4$: ;compression failure. restore r0 | |
471 012426 013700 012276 mov @#encrl1,r0 ; restore original r0 | |
472 012432 000261 sec ; set carry | |
473 9$: ; exit | |
474 012434 000207 return | |
475 | |
476 | |
477 ; ---------------------------------------- | |
478 ; doecho - block transfer test | |
479 ; empfang, bufferwords um 1 erhöhen, senden | |
480 ; works on block 0, which has format <len> <data ...> | |
481 ; before | |
482 doecho: | |
483 ; called by framework, buffer is received and checked | |
484 012436 013703 000122 mov @#req0dt,r3 ; buffer base of block 0 | |
485 012442 013701 000120 mov @#req0wc,r1 ; len of block 0 | |
486 012446 011101 mov (r1),r1 ; req0wc is address of len | |
487 012450 001403 beq 9$ ; buffer len = 0?: exit | |
488 ; complement every word in buffer (checksum is recalculated) | |
489 1$: | |
490 012452 005123 com (r3)+ | |
491 012454 005301 dec r1 | |
492 012456 001375 bne 1$ | |
493 9$: | |
494 ; response buffer = request buffer | |
495 012460 000241 clc ; no error. | |
496 ; r3 = point to word after result buffer | |
497 012462 000137 010272 jmp @#doresp | |
498 | |
499 | |
500 ; --------------------------------------------------- | |
501 ; Select serial low level I/O driver | |
502 .include pdp11gui_serialio_dl11.mac | |
1 .title serialio_dl11 | |
2 | |
3 ; Low level serial I/O for DEC DL11 at standard address. | |
4 ; To be included by "pdp11gui_serialxfer.mac" | |
5 ; Implements the functions: | |
6 ; xmtbit | |
7 ; xmtchr | |
8 ; rcvbit | |
9 ; rcvchr | |
10 ; | |
11 | |
12 177560 kbs = 177560 | |
13 | |
14 ; -------------------------------------------------- | |
15 ; xmtbit: transmit 6 bits in r5 | |
16 ; xmtchr: transmit character in r5 | |
17 xmtbit: | |
18 ; ; send CR every 64 characters | |
19 ; mov r1,-(sp) ; save r1 | |
20 ; bic #177677,r1 ; bit #100 set? | |
21 ; bne 0$ | |
22 ; mov r5,-(sp) | |
23 ; mov #15,r5 | |
24 ; call @#xmtchr | |
25 ; mov (sp)+,r5 | |
26 ;0$: | |
27 ; mov (sp)+,r1 ; restore r1 | |
28 | |
29 012466 042705 000300 bic #300,r5 ; r5 &= 0x3f | |
30 012472 062705 000040 add #40,r5 ; offset 0x20 = octal 40 | |
31 xmtchr: | |
32 012476 010046 mov r0,-(sp) | |
33 012500 012700 177560 mov #kbs,r0 ; r0 points to serial port #0 | |
34 | |
35 1$: | |
36 012504 105760 000004 tstb 4(r0) ; ready to transmit? | |
37 012510 100375 bpl 1$ ; no, loop | |
38 012512 110560 000006 movb r5,6(r0) ; transmit data | |
39 ; wait until it is out (so char is out even if next char is HALT) | |
40 2$: | |
41 012516 105760 000004 tstb 4(r0) ; ready to transmit? | |
42 012522 100375 bpl 2$ ; no, loop | |
43 | |
44 012524 012600 mov (sp)+,r0 | |
45 012526 000207 return | |
46 | |
47 ; -------------------------------------------------- | |
48 ; rcvbit: 6 bit code empfangen | |
49 ; carry set: END OF BLOCK empfangen '}', test with "bcs" | |
50 ; overflow set: REPEAT empfangen '|' , test with "bvs" | |
51 ; delay char '~' is implicitely ignored | |
52 ; rcvchr: character empfangen | |
53 ; modifies only R5! | |
54 rcvbit: | |
55 1$: | |
56 012530 004737 012612 call @#rcvchr | |
57 012534 000242 clv ; clear overflow | |
58 012536 122705 000175 cmpb #'},r5 ; '}' = 175 | |
59 012542 001417 beq 2$ | |
60 012544 122705 000174 cmpb #'|,r5 ; '|' = 174 | |
61 012550 001416 beq 3$ | |
62 012552 120527 000040 cmpb r5,#40 ; char below 0x20? | |
63 012556 103764 blo 1$ ; yes: receive next char | |
64 012560 120527 000140 cmpb r5,#140 ; other chars above 0x60 (delay)? | |
65 012564 103361 bhis 1$ ; ignore | |
66 ; mov r5,(r2)+ ; to diag buffer | |
67 012566 162705 000040 sub #40,r5 ; subtract offset | |
68 012572 042705 000300 bic #300,r5 ; r5 &= 0x3f | |
69 012576 000241 clc ; clr carry = valid char | |
70 012600 000207 return | |
71 2$: | |
72 012602 000261 sec ; set carry = end of block | |
73 012604 000207 return | |
74 3$: ; set Overflow | |
75 012606 000262 sev | |
76 012610 000207 return | |
77 | |
78 ; modifies only R5! | |
79 ; halts with error if overrun | |
80 rcvchr: | |
81 012612 010046 mov r0,-(sp) | |
82 012614 012700 177560 mov #kbs, r0 ; r0 points to serial port #0 | |
83 ; TEST: r2 zeigt auf byte aus testdata | |
84 ; clr r5 ; TEST | |
85 ; movb (r2),r5 ; TEST | |
86 ; inc r2 ; TEST | |
87 ; br 2$ ; TEST | |
88 012620 005760 000002 tst 2(r0) ; test bit 15 = error | |
89 012624 100005 bpl 1$ ; no error | |
90 012626 012705 000015 mov #15,r5 ; error location | |
91 012632 016005 000002 mov 2(r0),r5 ; receiver = error | |
92 ; An error in the serial interface means: loss of contact. | |
93 ; So return of error result is impossible. | |
94 ; Use the only remaining option is: | |
95 012636 000000 halt | |
96 1$: | |
97 012640 105710 tstb (r0) ; character received? | |
98 012642 100376 bpl 1$ ; no, loop | |
99 012644 116005 000002 movb 2(r0),r5 ; read rcv buffer | |
100 2$: | |
101 | |
102 012650 042705 177600 bic #177600,r5 ; mask to 7 bits | |
103 012654 012600 mov (sp)+,r0 | |
104 012656 000207 return | |
105 | |
105 | |
503 ; .include pdp11gui_serialio_k8060.mac | |
504 | |
505 | |
506 | |
507 ; --------------------------------------------------- | |
508 ; rxbuff - receive transmit buffer | |
509 ; - data part are remaining words. | |
510 | |
511 ; debugging: buffer immer an der selben adresse | |
512 014000 . = 014000 | |
513 | |
514 ; ------- transmission buffer header --------------------------- | |
515 014000 xmbffr = . ; base for buffer frame | |
516 014000 xmwc: .blkw 1 ; transmission wordcount: exact wordcount, excluding len itself and checksum, but including opcode | |
517 014002 xmcs: .blkw 1 ; transmission checksumbuffer-2: buffer checksum. checksum of all data words in rxbuff | |
518 014004 xmbuff = . ; base for buffer payload | |
519 ; buffer is max 2 blocks: 1st = 8 parameters, 2nd = 16kwords drive data long | |
520 ; Buffer is located at end of code, can grow up tu 48kbyte boundary | |
521 ; ------- transmission buffer data --------------------------- | |
522 014004 .blkw 10 ; approx space for opcode + parameters, maybe more or less | |
523 014024 .blkw 40000 | |
524 ; data part of buffer for 16 kWords = 32 kBytes | |
525 ; addr is approx 13000 | |
526 ; if machine has only 32kBytes, mem end is at word addr 40000, | |
527 ; and buffer can keep 040000-013000 = 025000 = 10752d words | |
528 114024 xmbufe = . ; End address | |
529 114024 .blkw 10 ; extra space, to fill triplets | |
530 | |
530 | |
260 | |
261 .end | |
261 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment