[OpenBIOS] r778 - cpu/x86/pc/biosload
svn at openbios.org
svn at openbios.org
Thu Jan 17 10:19:13 CET 2008
Author: wmb
Date: 2008-01-17 10:19:13 +0100 (Thu, 17 Jan 2008)
New Revision: 778
Added:
cpu/x86/pc/biosload/callbios.fth
cpu/x86/pc/biosload/rmenter.fth
Log:
x86 real mode support - initial checkin
Added: cpu/x86/pc/biosload/callbios.fth
===================================================================
--- cpu/x86/pc/biosload/callbios.fth (rev 0)
+++ cpu/x86/pc/biosload/callbios.fth 2008-01-17 09:19:13 UTC (rev 778)
@@ -0,0 +1,211 @@
+purpose: Call real-mode BIOS interrupts from protected mode
+
+\ For now we assume the following descriptors:
+\ 08 - 16-bit code 0..ffff
+\ 10 - 16-bit data 0..ffffffff
+\ 18 - 16-bit data 0..ffff
+\ 20 - 32-bit code 0..ffffffff
+\ 28 - 32-bit data 0..ffffffff
+
+\ We assume that we can use memory from h# 200 to h# 3ff
+
+\ Bounce buffer at h# 90000
+
+h# 8 constant rm-code-desc
+h# 18 constant rm-data-desc
+h# 20 constant pm-code-desc
+h# 28 constant pm-data-desc
+
+h# 002 constant rm-flagval \ Flags for IRET
+
+\ Low-memory addresses
+h# 200 constant rm-go \ Real mode enter code
+h# 280 constant rm-return \ Real mode return code
+h# 300 constant rm-idt \ For loading RM IDT with LIDT
+h# 308 constant rm-sp \ For loading RM SP with LSS
+h# 30c constant pm-sp-save \ Save/restore area for PM SP
+h# 310 constant pm-gdt-save \ For loading PM GDT with LGDT
+h# 318 constant pm-idt-save \ For loading PM IDT with LIDT
+h# 3c0 constant rm-regs \ Real-mode register buffer (in/out)
+rm-regs constant rm-gs
+rm-gs wa1+ constant rm-fs
+rm-fs wa1+ constant rm-es
+rm-es wa1+ constant rm-ds
+rm-ds wa1+ constant rm-di
+rm-di la1+ constant rm-si
+rm-si la1+ constant rm-bp
+rm-bp la1+ constant rm-xx \ Would be stack pointer
+rm-xx la1+ constant rm-bx
+rm-bx la1+ constant rm-dx
+rm-dx la1+ constant rm-cx
+rm-cx la1+ constant rm-ax
+rm-ax la1+ constant rm-flags
+rm-flags la1+ constant rm-target \ Full pointer for RM call target address
+rm-target la1+ constant rm-retloc \ Full pointer to rm-return
+rm-retloc la1+ constant rm-rflags \ Flags in case of return via IRET
+
+label rmint-enter
+ real-mode
+ \ This must be copied to low memory
+
+ rm-data-desc # ax mov \ 16-bit data segment
+ ax ds mov ax es mov ax fs mov ax gs mov ax ss mov
+
+ pm-gdt-save #) sgdt \ So we can get back
+ pm-idt-save #) sidt \ So we can get back
+
+ rm-idt #) lidt
+ cr0 ax mov h# fe # al and ax cr0 mov \ Enter real mode
+
+ here 5 + rmint-enter - rm-go + 0 #) far jmp \ Jump to set cs
+
+ \ Now we are running in real mode; fix segments again
+ cs ax mov ax ds mov ax es mov ax fs mov ax gs mov
+
+ rm-sp #) sp lss
+
+ \ Load the 16-bit registers from the rm-regs area
+ gs pop fs pop es pop ds pop op: popa op: popf
+
+ far ret \ Now we are running the real-mode target code
+end-code
+here rmint-enter - constant /rmint-enter
+
+label rmint-exit
+ rm-target # sp mov \ Set the stack pointer to the top of the rm reg area
+
+ \ Copy the real-mode registers to the buffer
+ op: pushf op: pusha ds push es push fs push gs push
+
+ cli
+ cs ax mov ax ds mov
+
+ op: pm-gdt-save #) lgdt
+ cr0 ax mov 1 # al or ax cr0 mov
+
+ here 5 + rmint-exit - rm-return + pm-code-desc #) far jmp
+
+ protected-mode
+
+ pm-data-desc # ax mov ax ds mov ax es mov ax gs mov ax gs mov ax ss mov
+ pm-idt-save #) lidt
+
+ pm-sp-save #) sp mov
+ popf
+ popa
+c;
+here rmint-exit - constant /rmint-exit
+
+\ Must set up the registers area with register values and the target address
+code rm-call ( -- )
+ pusha
+ pushf
+
+ sp pm-sp-save #) mov
+ cld
+ cli
+ sp sp xor
+ rm-go rm-code-desc #) far jmp
+c;
+
+: >seg:off ( linear -- offset segment ) lwsplit d# 12 lshift ;
+: seg:off! ( linear adr -- ) >r >seg:off r@ 2+ w! r> w! ;
+
+0 value bios-prepped?
+: ?prep-bios-call ( -- )
+ bios-prepped? if exit then true to bios-prepped?
+ rmint-enter rm-go /rmint-enter move
+ rmint-exit rm-return /rmint-exit move
+ h# ffff rm-idt w!
+ 0 rm-idt 2+ l!
+ rm-regs rm-sp seg:off! \ Setup real-mode full pointer for lss
+ rm-return rm-retloc seg:off! \ Setup return address full pointer
+ rm-flagval rm-retloc 4 + w! \ Flags for return
+;
+
+: dr
+ ." ax: " rm-ax ? 2 spaces
+ ." bx: " rm-bx ? 2 spaces
+ ." cx: " rm-cx ? 2 spaces
+ ." dx: " rm-dx ? 2 spaces
+ ." bp: " rm-bp ? 2 spaces
+ ." si: " rm-si ? 2 spaces
+ ." di: " rm-di ? 2 spaces
+;
+
+: { ( int# -- ) ?prep-bios-call rm-regs d# 44 erase 4 * @ rm-target ! ;
+: } rm-call ;
+
+: ax rm-ax ! ;
+: ah rm-ax 1+ c! ;
+: al rm-ax c! ;
+: bx rm-bx l! ;
+: cx rm-cx l! ;
+: dx rm-dx l! ;
+: cf@ rm-flags @ 1 and ;
+: es:di >seg:off rm-es w! rm-di l! ;
+: video-mode ( mode# -- )
+ d# 55 set-tick-limit h# 10 { 3 al bx } d# 10 set-tick-limit
+;
+: text-mode 3 video-mode ;
+: 1024x768x8 h# 105 video-mode ;
+: 1024x768x16 h# 116 video-mode ;
+: apm-connect16 h# 15 { h# 5302 ax 0 bx } ;
+: apm-power-off apm-connect16 h# 15 { h# 5307 ax 1 bx 3 cx } ;
+: bios-memsize ( -- n )
+ h# 15 { h# e801 ax }
+ rm-ax @ h# 400 * rm-bx @ h# 10000 * + h# 100000 +
+;
+: .bios-memory-map ( -- )
+ 0
+ begin ( continuation )
+ h# 15 { bx h# e820 ax h# 10000 es:di d# 20 cx " PAMS" drop @ dx }
+ rm-flags @ 1 and abort" Not supported"
+ rm-cx @
+ h# 10000 rm-cx @ bounds ?do i @ 9 u.r 4 +loop cr
+ rm-bx @ ( continuation' )
+ ?dup 0= until
+;
+: bios-config-b@ ( adr -- b ) lwsplit h# 1a { h# b108 ax bx rm-di ! } rm-cx @ ;
+: bios-config-w@ ( adr -- w ) lwsplit h# 1a { h# b109 ax bx rm-di ! } rm-cx @ ;
+: bios-config-l@ ( adr -- l ) lwsplit h# 1a { h# b10a ax bx rm-di ! } rm-cx @ ;
+: bios-config-b! ( b adr -- ) lwsplit h# 1a { h# b10b ax bx rm-di ! rm-cx ! } ;
+: bios-config-w! ( w adr -- ) lwsplit h# 1a { h# b10c ax bx rm-di ! rm-cx ! } ;
+: bios-config-l! ( l adr -- ) lwsplit h# 1a { h# b10d ax bx rm-di ! rm-cx ! } ;
+: disk-status ( -- stat ) h# 13 { h# 0100 ax } rm-ax @ ;
+0 value sect/trk
+0 value trk/head
+0 value #heads
+: get-drive-params ( -- )
+ h# 13 { 8 ah h# 80 dx }
+ rm-flags @ 1 and if true exit then
+ rm-cx @ wbsplit ( cl ch )
+ over h# 3f and to sect/trk ( cl ch )
+ swap 6 rshift bwjoin 1+ to trk/head ( )
+ rm-dx @ 8 rshift 1+ to #heads ( )
+;
+: #drive-sectors ( -- n )
+ h# 13 { h# 15 ah h# 80 dx }
+ rm-dx @ rm-cx @ wljoin
+;
+: reset-disks ( -- ) h# 13 { 0 ah h# 80 dx } ;
+: reset-hard-disks ( -- ) h# 13 { h# 0d ah h# 80 dx } ;
+: do-rw ( sector1 cyl0 head0 drive0 #sectors ah-val -- )
+ h# 13 { ah al ( sector1 cyl0 head0 drive0 )
+ swap bwjoin dx ( sector1 cyl0 )
+ wbsplit ( sector1 cyl.lo cyl.hi )
+ 6 lshift rot or ( cyl.lo cyl.hi|sector )
+ swap bwjoin cx
+ h# 10000 >seg:off rm-es w! bx
+ }
+;
+: read-sectors ( sector1 cyl0 head0 drive0 #sectors -- ) 2 do-rw ;
+: write-sectors ( sector1 cyl0 head0 drive0 #sectors -- ) 3 do-rw ;
+
+: lbn>sch ( lbn -- sector cyl head )
+ sect/trk /mod swap 1+ swap ( sector rem )
+ trk/head /mod ( sector cyl head )
+ dup #heads > abort" LBN out of range"
+;
+: lbn-read-sectors ( lbn drive #sectors -- ) 2>r lbn>sch 2r> read-sectors ;
+: lbn-write-sectors ( lbn drive #sectors -- ) 2>r lbn>sch 2r> write-sectors ;
Added: cpu/x86/pc/biosload/rmenter.fth
===================================================================
--- cpu/x86/pc/biosload/rmenter.fth (rev 0)
+++ cpu/x86/pc/biosload/rmenter.fth 2008-01-17 09:19:13 UTC (rev 778)
@@ -0,0 +1,214 @@
+: >seg:off ( linear -- offset segment ) lwsplit d# 12 lshift ;
+: seg:off! ( linear adr -- ) >r >seg:off r@ wa1+ w! r> w! ;
+: seg:off> ( offset segment -- linear ) 4 lshift + ;
+: seg:off@ ( adr -- linear ) dup w@ swap wa1+ w@ seg:off> ;
+
+h# 8 constant rm-cs
+h# 18 constant rm-ds
+h# 20 constant pm-cs
+h# 28 constant pm-ds
+
+h# 0.0000 constant rm-base
+: +rm ( offset -- adr ) rm-base + ;
+
+\ Place for the initial registers upon entry to real mode.
+\ The real-mode stack pointer will start here
+h# f00 +rm constant 'rm-regs
+\ (00) 4 * 2: GS,FS,ES,DS
+\ (08) 8 * 4: EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX
+\ (28) 1 * 4: CS:IP of return address
+\ (2c) 1 * 2: flags
+\ size is 2e
+
+h# f30 +rm constant 'rm-idt \ For loading RM IDT with LIDT
+h# f38 +rm constant 'rm-sp \ SS:SP For loading RM SP with LSS
+h# f3c +rm constant 'pm-sp \ Save/restore area for PM SP
+h# f40 +rm constant 'pm-gdt \ For loading PM GDT with LGDT
+h# f48 +rm constant 'pm-idt \ For loading PM IDT with LGDT
+
+h# f50 +rm constant 'rm-to-pm
+h# fa0 +rm constant 'pm-to-rm
+h# ff0 +rm constant 'rm-enter
+
+: caller-regs 'rm-sp seg:off@ ;
+struct
+ 2 field >rm-gs
+ 2 field >rm-fs
+ 2 field >rm-es
+ 2 field >rm-ds
+ 4 field >rm-edi
+ 4 field >rm-esi
+ 4 field >rm-ebp
+ 4 field >rm-exx
+ 4 field >rm-ebx
+ 4 field >rm-edx
+ 4 field >rm-ecx
+ 4 field >rm-eax
+ 4 field >rm-retaddr
+ 2 field >rm-flags
+drop
+
+: rm-ah@ caller-regs >rm-eax 1+ c@ ;
+: rm-ah! caller-regs >rm-eax 1+ c! ;
+: rm-al@ caller-regs >rm-eax c@ ;
+: rm-al! caller-regs >rm-eax c! ;
+: rm-ax@ caller-regs >rm-eax w@ ;
+: rm-ax! caller-regs >rm-eax w! ;
+: rm-dh@ caller-regs >rm-edx 1+ c@ ;
+: rm-dh! caller-regs >rm-edx 1+ c! ;
+: rm-dl@ caller-regs >rm-edx c@ ;
+: rm-dl! caller-regs >rm-edx c! ;
+: rm-dx@ caller-regs >rm-edx w@ ;
+: rm-dx! caller-regs >rm-edx w! ;
+
+: rm-set-cf caller-regs >rm-flags dup w@ 1 or swap w! ;
+: rm-clr-cf caller-regs >rm-flags dup w@ 1 or swap w! ;
+
+\ 80ff0 is the target address of the interrupt vector
+\ We use different segment:offset representations of that address in
+\ the vector table, so the handler code can determine the vector
+\ number by inspecting the code segment register value
+\ 00: 8000:0ff0
+\ 01: 8001:0fe0
+\ ...
+\ ff: 80ff:0000
+
+: make-vector-table ( -- )
+ h# 100 0 do
+ h# ff0 i 4 lshift - i /l* w! \ Set offset
+ rm-base 4 rshift i + i /l* wa1+ w! \ Set segment
+ loop
+;
+
+label rm-to-pm
+ real-mode
+
+ \ Stack: (high address)
+ \ flags (from INT)
+ \ CS:IP return address (from INT)
+ \ (low) EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX (high)
+ \ (low) GS,FS,ES,DS (high)
+ \ CS for vector
+
+ cli \ This is unnecessary since we got here from an INT
+ cs push ds pop
+
+ sp 'rm-sp #) mov
+ ss 'rm-sp 2+ #) mov
+
+ op: 'pm-gdt #) lgdt
+ cr0 ax mov 1 # al or ax cr0 mov \ Enter protected mode
+
+ here 5 + rm-to-pm - 'rm-to-pm + pm-cs #) far jmp
+
+ protected-mode
+
+ ax ax xor pm-ds # al mov ax ds mov ax es mov ax gs mov ax gs mov ax ss mov
+ 'rm-idt #) sidt
+ 'pm-idt #) lidt
+ 'pm-sp #) sp mov
+
+ popa
+ popf
+c;
+here rm-to-pm - constant /rm-to-pm
+
+label pm-to-rm
+ \ Interrupts must be off. We don't have a stack at the moment.
+ \ We got here via a far jmp to a 16-bit code segment, so we are
+ \ using the 16-bit instruction set, but we're not yet in real mode
+ \ The assembler uses "real-mode" to mean "16-bit code".
+ real-mode
+
+ \ This must be copied to low memory
+
+ ax ax xor rm-ds # al mov \ 16-bit data segment
+ ax ds mov ax es mov ax fs mov ax gs mov ax ss mov
+
+ 'pm-gdt #) sgdt \ So we can get back
+ 'pm-idt #) sidt \ So we can get back
+ 'rm-idt #) lidt
+ cr0 ax mov h# fe # al and ax cr0 mov \ Enter real mode
+
+ here 5 + pm-to-rm - 'pm-to-rm + >seg:off #) far jmp \ Jump to set cs
+
+ \ Now we are running in real mode; fix segments again
+ cs ax mov ax ds mov ax es mov ax fs mov ax gs mov
+
+ 'rm-sp #) sp lss \ Now we are back on the caller stack
+
+ \ Load the 16-bit registers from the rm-regs area
+ gs pop fs pop es pop ds pop op: popa
+
+ iret \ Now we are back to the caller
+end-code
+here pm-to-rm - constant /pm-to-rm
+
+code rm-return ( -- )
+ protected-mode
+ pushf
+ pusha
+ sp 'pm-sp #) mov
+
+ cli
+ sp sp xor
+ 'pm-to-rm rm-cs #) far jmp
+end-code
+
+\ This is the common target of all the real-mode interrupt vectors.
+\ It lives at 8.0ff0. Upon entry, the code segment register contains
+\ 80xx where xx is the vector number, and the IP contains 0yy0 where
+\ yy is ff - vector_number.
+label rm-enter
+ real-mode
+ op: pusha ds push es push fs push gs push
+ cs push \ Save the Code Segment value
+ 'rm-to-pm >seg:off #) far jmp \ Normalize the CS value
+ protected-mode
+end-code
+here rm-enter - constant /rm-enter
+
+h# 400 buffer: saved-rm-vectors
+
+0 value rm-prepped?
+: prep-rm ( -- )
+ rm-prepped? if exit then true to rm-prepped?
+ 0 saved-rm-vectors h# 400 move
+ make-vector-table
+
+ rm-enter 'rm-enter /rm-enter move
+ rm-to-pm 'rm-to-pm /rm-to-pm move
+ pm-to-rm 'pm-to-rm /pm-to-rm move
+
+ h# ffff 'rm-idt w! 0 'rm-idt wa1+ l! \ Limit and base
+;
+
+: rm-init-program ( pc -- )
+ prep-rm
+ 'rm-regs h# 2e erase ( pc )
+ 'rm-regs h# 28 + seg:off! ( )
+ 'rm-regs 'rm-sp seg:off! \ Initial stack pointer below regs
+;
+
+: handle-bios-call ( -- )
+ 'rm-sp seg:off@ w@ h# ff and ( int# )
+ 'rm-sp w@ 2+ 'rm-sp w! ( int# )
+ ." Interrupt " . cr
+ interact
+;
+: rm-go ( -- )
+ \ Load boot image at 7c00
+ h# 7c00 rm-init-program
+ begin
+ rm-return
+ handle-bios-call
+ again
+;
+label xx h# 99 # al mov al h# 80 # out begin again end-code
+here xx - constant /xx
+: put-xx ( adr -- ) xx swap /xx move ;
+: get-mbr
+ " /ide at 0" open-dev >r
+ h# 7c00 h# 3f 1 " read-blocks" r@ $call-method .
+ r> close-dev
+;
More information about the OpenBIOS
mailing list