Closed ddtopham closed 3 years ago
I put comments for the key values. The instructions that detects the keys, cmp al, ...
, now have a comment telling which key that value means.
You can consult the table for the scan codes here: https://www.stanislavs.org/helppc/scan_codes.html
The key for the swap
function (currently tab
) and the key for calling the code on buffer (currently F12
) can be changed without problems at lines 155
and 207
.
The keys for reading and writing are special. Their values were chosen because they are also relevant for the int 0x13
. By choosing these values, I didn't have to set the AH
register before calling the interrupt. This coincidence ended up saving 4
bytes.
Since I chose to maintain the partition table alive, the space for code is really tight. At the moment, there is no free byte!
So, to change the keys for reading and writing, you can comment the partition table (from lines 288
to 295
). This will save 64
bytes!
You also will need to uncomment the lines 161
and 172
, to properly set the AH
register.
If you do this then you can change the keys at lines 164
and 168
. You have to consider that at this point, AH
= AL
and they both hold the scan code of the key pressed. If you want to use an ASCII key, you will have to save AL
after line 94
. Now that the partition table is gone, there is plenty of space for that.
It's been a while since I coded this, so I can't be sure that this is 100% correct.
Thank you, this seems to be working ok. I realized if I use similar scan codes to the Alt Fx keys, I can drop in replacements and not have to remove the partition table. i.e. Use PgUp for F8, PgDn for F9, and Ins for F12 (tab and arrows work fine already).
I don't quite get how to read in a sector. e.g. I am using a single 512-byte "disk", so there is no other sector. I tried various numbers to represent the current sector as an 8-byte LBA, but no luck "reading" or "writing" it so far. Should I able to read the boot sector itself and modify it, then write it back to change the editor itself?
If you read the first sector, edit and then save it, you'll have to reboot the system for the code to change, because the running code at 0x7c00
isn't affected at all, only the buffer at 0x8000
is. So a reboot will load the updated code in the right address.
The 8-byte address in the screen is the LBA address used in both read and write operations. It's displayed in little-endian
in the screen. The interrupt 0x13
with AH
= 0x42
or 0x43
demands an 8-byte address.
If a read or write fails, the cursor will be moved to a specific spot.
It is so hard to debug boot sector code since there is no debugger to see what is returned from functions. I think the int 16 0x42 is reading (when I press 0x49 PgUp), but I don't ever see anything but zeros in the editor, so perhaps not. When editor opens should it already be displaying the boot sector? From what I read, the MBR should be LBA 0. When I set the upper top numbers to all zeros, then try to read, I don't see anything. You mention is uses little endian, so to read the 2nd sector, would that be 0000000000000001 or 0100000000000000 ?
When booted, the editor should display the data from the buffer at 0x8000
, which initially all zeroes in most cases. The editor doesn't read anything from disk when initializing.
The second sector has LBA 1, so it's 0100000000000000
. This is actually the initial value for the LBA.
When you write something on the screen, in the LBA address or in the buffer, the data changed should have the color changed. When a read or write is triggered, the screen should be redrawn and the colors reset. That should happen even if the read/write failed. That will help to see if the page keys are actually working.
I added a second sector (so LBA 1 is valid) and put some data in it. As you described, the colors change, so I can enter some new chars in buffer, press button to write and do see the colors change from white to black and get reset in hex area. I can continue making more edits too I added a "division by zero" if CF was set after int 13. That didn't trigger an error, so all seemed OK, but still, when I exit and check the bin image, there are no changes recorded. Also, reading from that sector does not bring in the chars I had placed there. What is significance of the DAP - 0x7000 ? I also noticed an online suggestion to put the drive number in DL (disk number for emulator is 0x80), but that didn't seem to affect anything. Here is what I have so far:
org 0x7c00
bits 16
%define SECTORS 0x2
%define BCOLORAS 0x20
%define BCOLORH1 0x10
%define BCOLORH2 0x30
%define BCOLORCU 0x40
%define FCOLORDE 0x00
%define FCOLORED 0x0f
;mov ah, 0x5
;mov cx, 0x4200
;int 0x16
main:
cld
sti
xor ax, ax
mov bp, ax
mov sp, bp
mov ah, 0x07
mov ds, ax
mov ss, ax
mov ah, 0xb8
mov es, ax
mov ax, 0x3
int 0x10
mov ax, 0x1112
mov bl, 0x0
int 0x10
draw:
pusha
push es
mov bx, 0xb800 + 0xa*0x2
mov es, bx
mov ah, FCOLORDE
mov si, TARGET - 0x7000
mov di, 0x20
mov cl, 0x8
draw0:
lodsb
call type
dec cl
jnz draw0
mov bl, 0xa*0x4
mov es, bx
xor si, si
mov ch, 0x20
draw1:
mov cl, 0x10
mov di, 0x20
draw2:
lodsb
call type
dec cl
jnz draw2
add bx, 0xa
mov es, bx
dec ch
jnz draw1
pop es
popa
mloop:
and di, 0x7f
mloopc0:
mov ax, es
cmp ax, 0xb800 + 0xa*0x2
jnl mloopc2
mov ax, 0xb800 + 0xa * 0x23
mloopc2:
cmp ax, 0xb800 + 0xa * 0x24
jb mloopc3
mov ax, 0xb800 + 0xa*0x2
mloopc3:
mov es, ax
mov ax, [es:di]
push ax
and ah, 0x0f
or ah, BCOLORCU
mov [es:di], ax
xor ax, ax
int 0x16
pop word [es:di]
cmp al, 0x20
jb mloop1
mov bx, [es:di]
cmp bh, 0x10
jb mloop
cmp di, 0x40
jb mloop0
mov ah, al
call hexascii
and al, 0x0f
call hexascii
cmp ah, al
jne mloop
push di
stosb
mov cl, 0x1
mov si, mloop_addr - 0x7000 + 0x1
and di, 0xffff - 0x3
call read
call swap
mloop_addr:
mov ax, FCOLORED * 0x100 + '?'
call type
pop di
mloop_incdi:
inc di
inc di
jmp mloop
mloop0:
mov ah, FCOLORED
call type
jmp mloop
mloop1:
mov al, ah
cmp al, 0x4b
jne mloop2
dec di
dec di
jmp mloop
mloop2:
cmp al, 0x4d
je mloop_incdi
cmp al, 0x48
jne mloop3
mov cx, es
sub cx, 0xa
mov es, cx
mloop3:
cmp al, 0x50
jne mloop4
mov cx, es
add cx, 0xa
mov es, cx
mloop4:
cmp al, 0x0f
jne mloop5
call swap
mloop5:
cmp al, 0x51 ; = PgDn, F8 = 0x42
mov ah, 0x42 ; extended bios read
je mloop7
cmp al, 0x49 ; = PgUp, F9 = 0x43
mov ah, 0x43 ; extended bios write
jne mloop8
pusha
push es
push 0xb800
pop es
mov di, 0xa0*0x4 + 0x40
mov cl, 0x10
xor si, si
mloop6:
call read
add di, 0xa0
add si, 0x10
cmp si, 0x200
jne mloop6
pop es
popa
mloop7:
pusha
push es
push 0xb800 + 0xa*0x2
pop es
mov cl, 0x8
mov si, TARGET - 0x7000
mov di, 0x40
call read
pop es
mov al, 0x0
mov si, DAP - 0x7000
mov dl,0x80
int 0x13
jnc ok
mov bx,0
div bx
ok:
jc main
popa
jmp draw
mloop8:
cmp al, 0x52 ; = Ins F12 = 0x86
jne mloop
xor si, si
lodsw
cmp ax, 0x9090
jne mloop
call 0x7000
jmp mloop
read:
pusha
read0:
mov ax, [es:di]
stosw
call hexascii
shl al, 0x4
mov dh, al
mov ax, [es:di]
stosw
call hexascii
and al, 0x0f
or al, dh
mov [si], al
inc si
dec cl
jnz read0
popa
ret
type:
push ax
or ah, BCOLORAS
push di
stosw
xor ah, BCOLORAS ^ BCOLORH1
shr di, 0x2
jnc type0
xor ah, BCOLORH1 ^ BCOLORH2
type0:
pop di
call swap
push ax
shr al, 0x4
call hexascii
stosw
pop ax
and al, 0x0f
call hexascii
stosw
call swap
pop ax
ret
swap:
cmp di, 0x40
jl swap0
shr di, 0x2
swap0:
shl di, 0x1
ret
hexascii:
xor al, 0x30
cmp al, 0x3a
jb hexascii0
add al, 0x99
jc hexascii0
sub al, 0x92
hexascii0:
ret
DAP:
db 0x10
db 0x0
dw 0x1
dw 0x0
dw 0x0700
TARGET: dq 0x1
%if 0
times (0x1be - ($ - $$)) db 0xA3
PARTITION_STATUS: db 0x80
CHS_START: db 0x0, 0x0, 0x0
PARTITION_TYPE: db 0xff
CHS_END: db 0x0, 0x0, 0x0
LBA_START: dd 0x0
VOLUME_SIZE: dd SECTORS
%endif
times 0x1fe-($-$$) db 0x0
db 0x55, 0xaa
times 512 db 0x41
The reading and writing issue could be coming from the virtual machine. I really don't know. Is reading the MBR (LBA 0) working?
The DS:SI
address should point to the DAP structure to be used by the int 0x13
interrupt, which is after the hexascii
function in memory. The DS
register is set in the beginning of the program to 0x700
, and the SI
is set to DAP - 0x7000
.
The memory address of SEGMENT:OFFSET
points to the memory at SEGMENT * 16 + OFFSET
. The DAP
in compile-time is correct if the segment is 0x0
. This is why I subtract 0x7000 from DAP.
Also, I said previously that the buffer is at 0x8000
but it's in fact at 0x7000
, my bad.
The int 0x13
interrupt uses DL
to indicate what disk to use. When BIOS loads the bootsector code from a disk, the DL
register holds the disk number of the same disk. The editor code never uses this register for anything, only the interrupt does. So DL
always holds the disk number, unless it's changed after the F12
function.
You also posted the whole source code, but since you didn't put it between ```, the *
and some code got messed up.
Thank you for the details above. I understand much better.
Progress: I am not entirely sure why, but this morning I see different behavior! Now I can read the sectors. I do see the MBR and my second sector when I read them. I can change things and see the buffer char color change to black. When I read it back, I still see those changes.
But still, after exiting the virtual machine (TinyEMU), the disk is unchanged. There is no carry set after write so it appears to "write" but no change is actually made to disk.bin file.
I set first 2 bytes to 9090 and pressed key to execute code - as expected that hangs (because I don't have any "real" code in that second sector. (Can you suggest how you might use that? e.g. What code could you put in a second sector and verify that it runs correctly?)
Is there any way to "step" through the bootloader?
Maybe the VM is simply preserving the disk and writing in another file.
A nice test code is 90 90 B4 0E B0 21 CD 10 C3
, which means:
nop ;90
nop ;90
mov ah, 0x0e ;B4 0E
mov al, '!' ;b0 21
int 0x10 ;CD 10
ret ;C3
This code should type a !
at the "true" cursor, at top left. Since this code returns, you can keep editing and calling this code multiple times. The code that will run is the code at the buffer. If a change isn't saved, the buffer won't be updated. You can simply write
the code somewhere on disk. The buffer will be updated even if the operation failed.
About debugging: I only use qemu
, and I don't know how to step the code. I only know how to dump memory from RAM and registers.
That works perfectly! Very nice. I installed Qemu and found that the save does work there, so you are probably right that the TinyEMU emulator is blocking the write.
Thank you very much for helping me to get up to speed on this code. I learned a lot and can experiment with bootsectors more easily. Quite a challenge to fit an editor into 512 bytes.
I was able to get write sector to work by adding parameters to TinyEMU. You can now try this editor without installing anything to your computer at all! Thanks to Fabrice Bellard's wonderful JSLinux. It takes about 15-20 seconds for Alpine Linux to boot up in your browser, then you can try GrOS right away:
Hi Cristhian, I really like this idea. I was able to load the bin file into a wonderful browser virtual machine written by Fabrice Bellard and it runs well (JSLinux): but, because it is in browser, the Alt keys are intercepted by the browser and therefore can't be used. How could I change the source so that it uses some other keys for "save", "read" etc.?
I did read the source, but (no comments) so not clear to me where those keys are being read and decoded.