-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclient.asm
More file actions
380 lines (294 loc) · 7.58 KB
/
client.asm
File metadata and controls
380 lines (294 loc) · 7.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
; pc98-serial-hdd-emulator - emulator client / BIOS patch
[bits 16]
[org 0x0000]
[cpu 8086]
%include "config.inc"
BIOS_STATUS_CODE_EQUIPMENT_CHECK: equ 0x40
BIOS_STATUS_CODE_NOT_WRITABLE: equ 0x70
EMULATOR_READ_COMMAND_OPCODE: equ 0x01
EMULATOR_READ_RESPONSE_OPCODE: equ 0x81
EMULATOR_GEOMETRY_COMMAND_OPCODE: equ 0x02
EMULATOR_GEOMETRY_RESPONSE_OPCODE: equ 0x82
UART_DATA_PORT: equ 0x30
UART_CMD_PORT: equ 0x32
[section .text]
_entry:
; This is where we're dropped after an "int 0x1b" instruction.
; We have caller flags, CS and IP pushed in that order onto the stack
; Check if the interrupt was made for our drive
; That will change FLAGS, but since the "PC9800 Technical Data Book - BIOS"
; book doesn't tell that input flags are used, we will not store them.
cmp al, DRIVE_ID
jz client_main
cmp al, (DRIVE_ID & 0x7F) ; MSB means LBA access mode
jz client_main
; The call is directed to a drive that we aren't shadowing.
_original_interrupt_handler_jump:
jmp word 0:0
client_main:
; The handler are sorted in most-to-least-probable-call order.
cmp ah, 0x06
jz read_handler
cmp ah, 0x04
jz sense_handler
; Special case of SENSE: if SENSE is called and the most significant
; bit is set, we have to return the drive geometry.
cmp ah, 0x84
jz extended_sense_handler
cmp ah, 0x05
jz write_handler
cmp ah, 0x03
jz initialize_handler
cmp ah, 0x07
jz recalibrate_handler
; We didn't find a valid handler. Return a more a less valid error code.
push bp
push cx
mov bp, command_buffer
mov [cs:bp], byte 0x03
mov [cs:bp+1], word ax
mov cx, 3
call uart_send_packet
pop cx
pop bp
mov ah, BIOS_STATUS_CODE_EQUIPMENT_CHECK
jmp error_return
; A stub handler that simulates a successful call (CF cleared)
success_stub:
mov ax, DRIVE_ID ; AH: status (0 is OK), AL: drive ID
; Function postlude that clears CF, indicating that the function was successful
success_return:
push bp
mov bp, sp
and byte [bp+6], ~0x01 ; Clear the CF bit in the caller FLAGS
pop bp
iret
; A stub handler that simulates "disk not writable" errors
not_writable_stub:
mov ax, (BIOS_STATUS_CODE_NOT_WRITABLE << 8) + DRIVE_ID
; Function postlude that sets CF, indicating an error in the function
error_return:
push bp
mov bp, sp
or byte [bp+6], 0x01 ; Set the CF bit in the caller FLAGS
pop bp
iret
; AH=0x03 handler - INITIALIZE
initialize_handler:
call uart_init
call wait_some
; We use the INITIALIZE handler to set up the emulated disk geometry.
; Send a GEOMETRY command to the remote machine
push bp
push ax
push cx
mov bp, command_buffer
mov [cs:bp], byte EMULATOR_GEOMETRY_COMMAND_OPCODE
mov [cs:bp+1], byte 0
mov cx, 2
call uart_send_packet
; Handle the response
call uart_read_byte
cmp al, EMULATOR_GEOMETRY_RESPONSE_OPCODE
jnz initialize_handler.failure
; ... and that the status byte is cleared.
call uart_read_byte
cmp al, 0
jnz initialize_handler.failure
; Read the next 8 bytes, which contain our geometry specs
mov cx, 8
mov bp, emulated_drive_lba_sector_count
initialize_handler.geometry_read_loop:
call uart_read_byte
mov [cs:bp], al
inc bp
loop initialize_handler.geometry_read_loop
pop cx
pop ax
pop bp
xor ah, ah
jmp success_return
initialize_handler.failure:
pop cx
pop ax
pop bp
mov ah, BIOS_STATUS_CODE_EQUIPMENT_CHECK
jmp error_return
; AH=0x04/0x84 handler - SENSE
; Returns what do we know about the fixed disk (size and geometry)
; Output registers:
; AH -> Four lowest bits tell the drive size (0000 -> 5MB)
; If input AH.7 is set,
; BX -> Sector length
; CX -> Cylinder count
; DH -> Heads count
; DL -> Sector per cylinder count
extended_sense_handler:
; If set, we come from the 0x84 Sense+Geometry code: update BX/CX/DX to the
; expected values
mov bx, 256
mov cx, [cs:emulated_drive_cylinder_count]
mov dh, [cs:emulated_drive_head_count]
mov dl, [cs:emulated_drive_sector_count]
push ax
push ds
xor ax, ax
mov ds, ax
; Get the LBA sector count, and swap values to be in big endian
mov ax, [cs:emulated_drive_lba_sector_count]
xchg ah, al
mov [0x588], ax
mov ax, [cs:emulated_drive_lba_sector_count+2]
xchg ah, al
mov [0x586], ax
pop ds
pop ax
sense_handler:
mov ah, 0x00 ; bit 4 is set to indicate we're in RO mode
jmp success_return
recalibrate_handler:
mov ah, 0x00
jmp success_return
; AH=0x06 handler - READ
; Input registers:
; BX -> Sector count
; CX -> Start cylinder index
; DH -> Head index
; DL -> Sector index
; ES:BP -> Data buffer
read_handler:
push ax
push bx
push cx
push dx
push bp
; Build the READ command
mov bp, command_buffer
mov [cs:bp], byte EMULATOR_READ_COMMAND_OPCODE
mov [cs:bp+1], al
mov [cs:bp+2], word bx
mov [cs:bp+4], word cx
mov [cs:bp+6], word dx
; Send the READ command
mov cx, 8
call uart_send_packet
; We can now wait for the command bytes.
; Check that we do have a response for our READ command...
call uart_read_byte
cmp al, EMULATOR_READ_RESPONSE_OPCODE
jnz read_handler.failure
; ... and that the status byte is cleared.
call uart_read_byte
cmp al, 0
jnz read_handler.failure
; Read the byte count word
call uart_read_byte
mov cl, al
call uart_read_byte
mov ch, al
pop bp
push bp
; And, finally, enter the data read loop:
read_handler.loop:
call uart_read_byte
mov [es:bp], al
inc bp
loop read_handler.loop
pop bp
pop dx
pop cx
pop bx
pop ax
; Clean the status byte in AH, then return success
xor ah, ah
jmp success_return
read_handler.failure:
pop bp
pop dx
pop cx
pop bx
pop ax
mov ah, BIOS_STATUS_CODE_EQUIPMENT_CHECK
jmp error_return
write_handler:
mov ah, BIOS_STATUS_CODE_NOT_WRITABLE
jmp error_return
; UART-related functions
; uart_init - sets up the built-in Intel 8251 UART
uart_init:
push ax
; Perform a full reset of the UART, as explained in the datasheet
out UART_CMD_PORT, al ; Write three times 0x00 to the UART command port
call wait_some
out UART_CMD_PORT, al
call wait_some
out UART_CMD_PORT, al
call wait_some
mov al, 8 ; 8 clocks on 10MHz bus, clock prescaler is 16: this will give us 19200bps
out 0x75, al
xor al, al
out 0x75, al ; Ghetto reset i8253 channel 2, which drives the baud generator
call wait_some
; Do an internal reset
mov al, 0x40
out UART_CMD_PORT, al
call wait_some
; We're finally in the MODE state. Set up asynchronous mode.
mov al, 0x4E ; 1 stop bit, no parity, 8 bits of data, system clock is x16 the baud rate
out UART_CMD_PORT, al
call wait_some
; And reset the possible errors flags, while enabling the receiver and transmitter
mov al, 0x15
out UART_CMD_PORT, al
call wait_some
pop ax
ret
; uart_send_packet - send a "packet" of bytes through the UART. Packet data must be
; stored in the command buffer.
; Input registers:
; CX -> packet length, in bytes.
uart_send_packet:
push ax
push si
mov si, command_buffer
_send_loop:
; Loop until TxEMPTY and TxRDY are set
in al, UART_CMD_PORT
and al, 0x04
jz _send_loop
cs lodsb
out UART_DATA_PORT, al
loop _send_loop
pop si
pop ax
ret
; uart_read_byte - blocks until a byte is read from the onboard UART.
; Output registers:
; AL -> read byte
uart_read_byte:
; Make /RTS low, so that hardware flow control works as expected.
mov al, 0x25 ; RTS flag set, TxEN/RxEN set
out UART_CMD_PORT, al
in al, UART_CMD_PORT
and al, 0x02
jz uart_read_byte
in al, UART_DATA_PORT
push ax
mov al, 0x05 ; RTS flag reset, TxEN/RxEN still set
out UART_CMD_PORT, al
pop ax
ret
wait_some:
push cx
mov cx, 0x4000
_ws_loop:
nop
loop _ws_loop
pop cx
ret
[section .bss vstart=512]
command_buffer: resb 32
emulated_drive_lba_sector_count: resw 2
emulated_drive_cylinder_count: resw 1
emulated_drive_sector_count: resb 1
emulated_drive_head_count: resb 1