NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: Deskman243 on June 22, 2023, 04:43:24 PM
-
Greetings everyone
Today I was designing builds for my program in the assembler hoping to get a verification for this subject. Right now I have a certain configuration file where my code runs seemingly well however if I try to change the address of one of the files I lose functionality(especially obvious as this file is a graphic component). I had used to use NASM's ORG instruction for most programs but I use ld for my configurations whenever the file is larger than 512b (this was the subject of my previous post).
To surmize I would like to share the design here as an illustration for the purpose of review. The main program is just a function of ordinary NASM and x86 instructions modeled for the 1 MB BIOS function platform and outlined simply by the files and figure below. One other quick observation to clarify is that most of the stage files correspond to the paths shown.
Boot => Stage1.asm (16-32 gate function) => Stage2.asm (BIOS_16 code fitting) =>Stage3.asm (BIOS graphics functions)
+(secondary path test) test2.asm
The question is essentially a product of changing the aforementioned path by any means. In the build file you can observe that BOTH ld and ORG instructions however the fit for stage3 graphics is tight as is so I'm trying to move this to earlier segments that 0xA000. If I change this to anywhere other than that segment (0x9000,0x8000) I just get a blank screen. Naturally speaking I know I have to change a module in each file as outlined below.
Disk_Read(boot.asm)=>Jump_From_Gate(Stage1.asm)=>Jump_Into_BIOS16_Segment(Stage2.asm)=>Stage3_Addressment
From what I understand if it runs for one address it should run for any address but it doesn't. I know that there's a difference between the two file types but the inferences here look kinda conspicuous as is. That's why I was very curious and hoping here if anyone else understood what happened.I'm posting the full source and I'd be really happy and available to correspond any further. One more thing was I had to add stage3 attach stage3 due to file constraints. Cheers!
boot.asm
; ReTimerOS
; Copyright (C) 2022,2023 Christopher Hoy
;
; This file is part of ReTimerOS
; ReTimerOS is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <https://www.gnu.org/licenses/>.
[org 0x7c00]
[bits 16]
mov cx,0
mov ds,cx
mov es,cx
cld
mov si,msgString
call kprint
mov cx,0x1000
mov es,cx
mov bx,0x0000_0000
;mov bx,0x0000_D000
mov cl,5
mov bx,msgString
;mov bx,0x0000_D000
mov bx,0x0000_0000
mov cl,5
mov al,1
call read_section
;mov cx,0xD00
mov cx,0x1000
mov es,cx
mov bx,0
;mov al,1
mov cl,5
mov al,1
call read_section
mov cx,0
mov ds,cx
mov es,cx
;mov bx,0x0000_9000
mov bx,0x0000_A000
mov cl,4
mov bx,msgString
;mov bx,0x0000_9000
mov bx,0x0000_A000
mov cl,4
mov al,0x30
call read_section
;mov cx,0x900
mov cx,0xA00
mov es,cx
mov bx,0
;mov al,1
mov cl,4
;mov bx,0x0000_8000
mov al,0x30
call read_section
mov bx,0x0000_8000
mov cl,3
mov bx,msgString
mov bx,0x0000_8000
mov cl,3
mov al,1
call read_section
mov cx,0x800
mov es,cx
mov bx,0
mov cl,3
;mov bx,0x0000_8000
mov al,1
call read_section
mov bx,0x0000_7e00
mov bx,0x0000_7e00
mov cl,2
mov bx,msgString
mov bx,0x0000_7e00
mov cl,2
mov al,1
call read_section
mov cx,0x7e0
mov es,cx
mov bx,0
mov cl,2
;mov bx,0x0000_8000
mov al,1
call read_section
mov bx,0x0000_7e00
;mov cx,0
;mov es,cx
pusha
jmp 0x7e00
;jmp 0x9200
;jmp 0xD000
jmp $
kprint:
cld
mov ah,0x0E
kstring:
lodsb
int 0x10
cmp al,0x00
jnz kstring
ret
read_section:
mov ah,0x02
; mov al,1
mov ch,0
mov dh,0
int 0x13
jc .disk_check
ret
.disk_check:
mov si,checkString
call kprint
jmp $
.done:
ret
;section .data
align 4
BOOT_DRIVE: db 0
msgString: db 'Game platform premier',10,13,0
checkString: db 'Section did not load',10,13,0
checkString2: db 'disk complete',10,13,0
times 510 -($-$$) db 0
dw 0xaa55
stage1.asm
;%define REBASE_ADDRESS(A) (0x7E00 + ((A) - protectedGate1))
%define BUILD_GDT_DESC(bounds,base,access,flags) \
((( base & 0x00FFFFFF) << 16) | \
(( base & 0xFF000000) << 32) | \
( bounds & 0x0000FFFF) | \
(( bounds & 0x000F0000) << 32) | \
(( access & 0xFF) << 40) | \
(( flags & 0x0F) << 52))
;[ORG 0x7E00]
[BITS 16]
;section .text
protectedGate1:
;mov bx,0x0000_9000
mov si,String
call kprint
cld
cli
in al,0x92
or al,2
out 0x92,al
lgdt[gdt32Ptr]
lidt[IdtPipe]
mov eax, cr0
or eax,1
mov cr0, eax
; mov [saved_segment],ds
; jmp code32_post:REBASE_ADDRESS(__protected_mode_32)
jmp code32_post:__protected_mode_32
; jmp $
; jmp code32_post:__protected_mode_32
kprint:
cld
mov ah,0x0E
kstring:
lodsb
int 0x10
cmp al,0x00
jnz kstring
ret
;section .text
[bits 32]
; 32 bit protected mode
__protected_mode_32:use32
; mov ax, 0x10
mov ax,data32_post
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; mov ss, ax
; restate cr3
mov cr3, ebx
; restate esp
mov esp, edx
mov cx,[gate_voucher]
cmp word [gate_voucher],0
jnz loopcheck
mov word [gate_voucher],1
jmp BIOS32_PREP
loopcheck:
hlt
jmp loopcheck
; jmp 0x8000
BIOS32_PREP:use32
pusha
;pushf
; save current esp to edx
mov edx, esp
; jumping to 16 bit protected mode
; disable interrupts
cli
; clear cr3 by saving cr3 data in ebx register
xor ecx, ecx
mov ebx, cr3
mov cr3, ecx
jmp code16_post:__protected_mode_16
[bits 16]
; 16 bit protected mode
__protected_mode_16:use16
; jumping to 16 bit real mode
xor eax,eax
xor ecx,ecx
; mov ax, 0x38
mov cx,data16_post
; mov ax,0
mov ds, cx
mov es, cx
mov fs, cx
mov gs, cx
mov ss, cx
; turn off protected mode
; set bit 0 to 0
mov eax, cr0
and al, ~0x01
mov cr0, eax
; jmp 0x8000
jmp 0xA000
;section .data
String: db 'platform 2',10,13,0
checkString: db 'check',10,13,0
gdt32:
dq BUILD_GDT_DESC(0,0,0,0)
gdt32code:
dq BUILD_GDT_DESC(0x0000ffff,0,10011010b,1100b)
gdt32data:
dq BUILD_GDT_DESC(0x0000ffff,0,10010010b,1100b)
gdt16code:
dq BUILD_GDT_DESC(0x0000ffff,0,10011010b,1000b)
gdt16data:
dq BUILD_GDT_DESC(0x0000ffff,0,10010010b,1000b)
.stub1:
code32_post: equ gdt32code -gdt32
data32_post: equ gdt32data -gdt32
;.stub:
code16_post: equ gdt16code -gdt32
data16_post: equ gdt16data -gdt32
; tss32_post: equ gdt32tss -gdt32
gdt32Len: equ $-gdt32
gdt32Ptr: dw gdt32Len-1
dd gdt32
save_cr0 dd 0
save_cr3 dd 0
saved_segment resd 0
gate_voucher dw 0
saved_stack resw 0
IdtPipe:
dw 0x03ff
dd 0
times 512-($-$$) db 0
;%include 'gdt.inc'
stage2.asm
stage3.asm ( pretty lengthy from the graphic methods and had to attach seperately instead)
stage4.asm (resides as a proof of concept)
[org 0x10000]
[BITS 16]
_prep_module2:
_vm_module:
sub dword [_vidmem_ptr],_VIDEO_TEXT_ADDR
mov si,_vm_str
call _printstr_task
_loopcheck: jmp $
_vm_str: db 'long test 1',0
_printstr_task:
push cx
mov bp,0b800h
mov es,bp
mov bp,[_vidmem_ptr]
_prep:
mov cx,0
mov es,cx
mov ax,_ROWS
mov bx,es:0x044a
mul bx
add ax,_COLS1
; shl ax,1
mov bp,ax
add bp,[_vidmem_ptr]
;check even and odd placements
; wizard tactics 1.7
add bp,0x690
; wizard tactics 1.8
; times 140 inc bp
; wizard tactics 1.9
times 20 inc bp
;boundary
;check even and odd placements
mov cx,0b800H
mov es,cx
mov ch,_COLOR_ATTR_BSC
jmp _gettext
_output:
pop ax
mov dx,ax
mov es:[bp],dx
add bp,byte 2
_gettext:
mov cl,byte [ds:si]
inc si
push cx
test cl,cl
jnz _output
mov [_vidmem_ptr],bp
pop word [bp]
pop ax
mov cx,0
mov ds,cx
ret
jmp $
_VIDEO_TEXT_ADDR EQU 0XB8000
_COLOR_ATTR_PSC EQU 0X6A
_COLOR_ATTR_BSC EQU 0X5F
_PM_MODE_STACK EQU 0X80000
_VM_STACK_SEG EQU 0X0000
_VM_STACK_OFS EQU 0X0000
_VM_CS_SEG EQU 0X0000
_FLAGS_VM_CMP EQU 17
_FLAGS_CMP1 EQU 1
_FLAGS_CMP_IF EQU 9
_RING0_PROC_STACK_SIZE EQU 2048
;TSS_IO_MAP_SIZE EQU 0
_TSS_IO_MAP_SIZE EQU 0x400/8
;VM_STACK_ADDRESS EQU vidmem_address
_ROWS EQU 25
_COLS1 EQU 80
_BACK1 EQU 05h
_FRONT1 EQU 12h
_CHECKER EQU 0
align 4
_vidmem_ptr: dd _VIDEO_TEXT_ADDR
_pm_str: db 'protected mode string ',0
_pm_str_length: equ $-_pm_str
;vidmem_address: dw 0
times 512-($-$$) db 0
run.sh
nasm -f bin -g -o boot.bin boot.asm
nasm -f elf -g stage1.asm -o stage1.o
nasm -f elf -g stage2.asm -o stage2.o
nasm -f elf -g stage3.asm -o stage3.o
nasm -f bin -g test2.asm -o test2.bin
ld -g -m elf_i386 -Ttext 0x7e00 stage1.o -o stage1.bin --oformat binary
ld -g -m elf_i386 -Ttext 0x8000 stage2.o -o stage2.bin --oformat binary
ld -g -m elf_i386 -Ttext 0xA000 stage3.o -o stage3.bin --oformat binary
dd if=/dev/zero of=os.bin bs=512 count=2811
cat boot.bin \
stage1.bin \
stage2.bin \
stage3.bin \
test2.bin \
os.bin>os.img
rm -rf *\.bin
qemu-system-i386 -fda os.img
(1 extra config file)
gdt.inc
%define BUILD_GDT_DESC(bounds,base,access,flags) \
((( base & 0x00FFFFFF) << 16) | \
(( base & 0xFF000000) << 32) | \
( bounds & 0x0000FFFF) | \
(( bounds & 0x000F0000) << 32) | \
(( access & 0xFF) << 40) | \
(( flags & 0x0F) << 52))
section .data
gdt32:
dq BUILD_GDT_DESC(0,0,0,0)
gdt32code:
dq BUILD_GDT_DESC(0x0000ffff,0,10011010b,1100b)
gdt32data:
dq BUILD_GDT_DESC(0x0000ffff,0,10010010b,1100b)
gdt16code:
dq BUILD_GDT_DESC(0x0000ffff,0,10011010b,1000b)
gdt16data:
dq BUILD_GDT_DESC(0x0000ffff,0,10010010b,1000b)
;gdt16tss:dq BUILD_GDT_DESC(0x8230,TSS_SIZE-1,10001001b,0000b) & 0 << 22
;gdt32tss:
; dq BUILD_GDT_DESC(TSS_SIZE-1,0x8280,10001001b,0000b)
; dq BUILD_GDT_DESC(TSS_SIZE-1,0x9260,10001001b,0000b)
.stub1:
code32_post: equ gdt32code -gdt32
data32_post: equ gdt32data -gdt32
;.stub:
code16_post: equ gdt16code -gdt32
data16_post: equ gdt16data -gdt32
; tss32_post: equ gdt32tss -gdt32
gdt32Len: equ $-gdt32
gdt32Ptr: dw gdt32Len-1
dd gdt32
IdtPipe:
dw 0x03ff
dd 0
-
With legacy master boot record I prefer to encode everything in a single file:
; mbr.asm
bits 16
; int 0x19 entry point.
; label is here just because of child labels (.???).
_start:
; Normalize DS, so out offsets start at 0.
; This could be useful if I intended to move these sectors to 0x60:0
; at beginning of RAM after Extended BIOS data area. Notice ES sill points
; to segment 0. If you need to access BIOS data areas, use ES.
mov ax,0x7c0
mov ds,ax
jmp 0x7c0:.begin ; Normalize CS:(E)IP too.
.begin:
cld ; just to make sure direction is up.
; Calculate how many extra sectors will be loaded.
lea ax,[after_last_sector_offset]
lea bx,[next_sector_offset]
sub ax,bx
mov cl,9
shr ax,cl ; AX = # of sectors to load. (max 127).
; Load additional sectors using LBA.
; Notice DL is untouched.
mov [dap.num_sectors],ax
mov [dap.buffer_addr],bx
mov [dap.buffer_addr+2],ds
mov ah,0x42
lea si,[dap]
int 0x13
jc loader_error
; We can still do somethings here after loading the
; sectors.
; jump to code in the next sectors.
jmp next_sector_offset
loader_error:
lea si,[errmsg]
call puts
halt:
hlt
jmp halt
; Print a string using TTY.
; Input: DS:SI = ptr to asciiz string.
puts:
; push bx ; preserve BX is I was using an ABI.
mov bx,7
.loop:
lodsb
test al,al
jz .exit
mov ah,0x0e
int 0x10
jmp .loop
.exit:
; pop bx
ret
errmsg:
db `Error loading sectors from disk.\r\n`,0
; Structure used by service 0x42, int 0x13.
dap:
dw dap_size
.num_sectors:
dw 0
.buffer_addr:
dd 0
dq 1 ; next sector is 1.
dap_size equ $ - dap
times 510 - ($ - $$) db 0
dw 0xaa55 ; end of mbr signature.
; this mark the entry point for code in next sectors.
next_sector_offset:
mov ax,3
int 0x10
lea si,[msg]
call puts
jmp halt
msg:
db `Never gonna give you up.\r\n`
db `Never gonna let you down.\r\n`,0
; this label must be aligned by 512 boundary (1 sector)
; to avoid complex (meh!) calculations...
align 512
after_last_sector_offset:
So, I don't need sections and a linker script.
$ nasm -fbin -o mbr.bin mbr.asm
$ qemu-system-i386 -drive file=mbr.bin,index=0,format=raw
(https://i.postimg.cc/gkd6gxcJ/Untitled.png)
-
Aww that's really cool. I wish that would run on my code above. I don't think I got the right readings because it won't even boot other than the error text haha . Just to check before where you have the insertion line on the dap configuration for sections I put
dap:
dw dap_size
.num_sectors:
dw 0
.buffer_addr:
dd 0
dq 0x10000 ; next sector is 1.
dap_size equ $ - dap
I was only trying to make 1 MB for a standard program . Do you know how I could manage that here?
-
Aww that's really cool. I wish that would run on my code above. I don't think I got the right readings because it won't even boot other than the error text haha.
Are you using a very, very, old PC? If this is the case, LBA may not be available to your BIOS.
I was only trying to make 1 MB for a standard program . Do you know how I could manage that here?
1 MiB? 2048 sectors? Not possible to read that at 1 read, as it is in documentation you can read, maximum, 64 KiB (if the offset is 0)...
-
Good news everyone!
Status update for the answer to this article was the ds segment for the graphics component had to be coincidentally referenced. An important distinction is that 0A000h was piped as a test segment for both file address AND segments. This meant simply that only the file address references need changing.
Now I can go back to looking at the 2nd file inference for the a16-19 Data line references that I have been researching in my previous posts. Anyhow I hope this answer serves anyone else too and hope everyone has a good rest of your day. If anyone else would like a more recent version of this program let me know!
-
Good news everyone!
Status update for the answer to this article was the ds segment for the graphics component had to be coincidentally referenced. An important distinction is that 0A000h was piped as a test segment for both file address AND segments. This meant simply that only the file address references need changing.
Now I can go back to looking at the 2nd file inference for the a16-19 Data line references that I have been researching in my previous posts. Anyhow I hope this answer serves anyone else too and hope everyone has a good rest of your day. If anyone else would like a more recent version of this program let me know!
1 MiB? 2048 sectors? Not possible to read that at 1 read, as it is in documentation you can read, maximum, 64 KiB (if the offset is 0)...
-
Updated code jumping to protected mode. No linker scripts, just NASM.
$ make
$ make run
-
dap:
dw dap_size
.num_sectors:
dw 0
.buffer_addr:
dd 0
dq 0x10000 ; next sector is 1.
dap_size equ $ - dap
Notice the last DQWORD is the initial sector (numbered from 0 to N). Here you are trying to read sector #65536. What I did earlier was to read sectors following the sector 0 (hence "next sector is 1" comment). If the sector is invalid, service 0x42 will return with Carry set. See Ralf Brown's Interrupt List (google it) for more info.
Take a look at the latest code I posted here...
PS: LBA used in service 0x42 is LBA48 (if supported), then the sectors have a limit of 48 bits. The bigger disk could have 2^48 sectors or 2^57 bytes (128 PiB). If the controller supports only LBA28 then the disk is limited to 2^28 sectors or 2^37 bytes (128 GiB). And, an sector index in LBA always start at 0 (not 1, as in CHS model).
-
Good day everyone
Here I got more news about the model version. The funny thing is there's still a bug in the current code that I have found in a specific area. Now that the segments were relatively settled I tried putting more conventional code into the recent model.
Here I think it's very important to state the inferences of these additional code.
First I put a TIMES pad function into the larger graphical function file. The good news is that this actually retains the model as is and therefore happily runs. However the second time I try to test this on the other stage file size this actually changed the path of the program.When I run it on the debugger I was amazed by the difference between the two file instructions.
I'm mainly referring back to the original access path figure from earlier. From here I basically just change the corresponding amount of sections in boot.asm(Disk Read Functions) from any of the other Stage files. Just to clarify I have a 0x30 (48) configuration for stage3.asm but when I tried changing Stage2.asm from a single 512 section to two 512=1024 segments it suddenly fails.
Disk_Read(boot.asm)=>Jump_From_Gate(Stage1.asm)=>Jump_Into_BIOS16_Segment(Stage2.asm)=>Stage3_Addressment
Quick check here my build file here is run3.sh and can be run simply by
sudo chmod +x run3.sh
sudo ./run3.sh
I'm posting the model for review again to get this model a conclusion once and for all and I'm more than happy to settle this proof of concept. Again cheers and hope y'all have a good one from here!
Revision 2
This was the new test configuration
Disk_Read(boot.asm) => Jump_From_Gate(Stage1.asm)
=>Jump_Into_BIOS16_Segment(Stage2.asm @ 0x8000)
=>test2.asm(data number fill pad @ 0x8200)
=>Stage3_Addressment( @ 0x8400)
make clean
nasm -f bin -g -o boot5.bin boot5.asm
nasm -f elf -g -o boot.o boot.asm
nasm -f elf -g stage1.asm -o stage1.o
nasm -f elf -g stage2.asm -o stage2.o
nasm -f elf -g stage3.asm -o stage3.o
nasm -f elf -g stage4.asm -o stage4.o
nasm -f bin -g test2.asm -o test2.bin
ld -T link.ld boot.o -o boot.bin
ld -g -m elf_i386 -Ttext 0x7e00 stage1.o -o stage1.bin --oformat binary
ld -g -m elf_i386 -Ttext 0x8000 stage4.o -o stage4.bin --oformat binary
ld -g -m elf_i386 -Ttext 0x9000 stage2.o -o stage2.bin --oformat binary
dd if=/dev/zero of=ospad1.bin bs=512 count=50
dd if=/dev/zero of=os.bin bs=512 count=2876
cat boot5.bin \
stage1.bin \
stage4.bin \
test2.bin \
os.bin>os.img
rm -rf *\.bin
qemu-system-i386 -fda os.img
-
GDB check 1
Here where the yields I got from the configuration above from gdb
The evidence shows that the settings duplicate test2.bin @ 0x8200 AND @ 0x8400.