retroforth/experimental/386.s
crc 13291550ff experimental: native/386: fix stack init, putc of -1 clears display; clear display on startup
FossilOrigin-Name: 8d665c814570586161ace316134e9d37097f3ea8f5e53232537b38dc835d01f8
2018-01-31 17:50:19 +00:00

923 lines
19 KiB
ArmAsm

; ___ ___ _____ ___ ___ __ ____ ___ __
; | _ \ __|_ _| _ \/ _ \ / / |__ /( _ ) / /
; | / _| | | | / (_) | / / |_ \/ _ \/ _ \
; |_|_\___| |_| |_|_\\___/ /_/ |___/\___/\___/
;
; This is the minimal startup & I/O needed to run a basic RETRO
; instance on x86 hardware.
; =============================================================
MODULEALIGN equ 1<<0
MEMINFO equ 1<<1
FLAGS equ MODULEALIGN | MEMINFO
MAGIC equ 0x1BADB002
CHECKSUM equ -(MAGIC + FLAGS)
section .mbheader
align 4
MultiBootHeader:
dd MAGIC
dd FLAGS
dd CHECKSUM
section .data
gdt:
dw (.end-.dummy)
dd .dummy
dw 0
.dummy: dw 0,0,0,0
dw 0xffff,0x0000
db 0x00,0x9a,0xcf,0x00
dw 0xffff,0x0000
db 0x00,0x92,0xcf,0x00
.end:
section .text
extern main
global loader
global _start
global putchar
global getchar
STACKSIZE equ 0x4000
align 4
loader:
_start:
mov esp, stack+STACKSIZE
push dword 0
push dword 0
call vClear
call vHome
jmp main
jmp $
align 4
putchar:
mov eax, [esp+4]
cmp eax, -1
jz clearscreen
call vEmit
ret
clearscreen:
call vClear
call vHome
ret
align 4
getchar:
call key
push eax
call vEmit
pop eax
ret
section .text
key:
xor eax, eax ; clear eax
.a: in al, 64h ; Is any data waiting?
test al, 1 ; Is character = ASCII 0?
jz .a ; Yes? Try again
in al, 60h ; Otherwise, read scancode
xor edx, edx ; edx: 0=make, 1=break
test al, 80h ; Is character = HEX 80h?
jz .b ; Skip the next line
inc edx ; Update edx
.b: and al, 7Fh ; Filters to handle
cmp al, 39h ; the ignored keys
ja .a ; We just try another key
mov ecx, [board] ; Load the keymap
mov al, [ecx+eax] ; Get the key ASCII char
or al, al ; Is is = 0?
js .shift ; No, use CAPITALS
jz .a ; Ignore 0's
or dl, dl ; Filter for break code
jnz .a ; Ignore break code
jmp .done ; Skip to end
.shift: mov ecx, [edx*4 + .shifts] ; Load the CAPITAL keymap
mov [board], ecx ; Store into BOARD pointer
jmp .a ; And try again
.done: ret
section .data
.shifts dd shift, alpha
board dd alpha
alpha:
db 0,27,"1234567890-=",8 ;00-0E
db 9,"qwertyuiop[]",10 ;0F-1C
db 0,"asdfghjkl;'`" ;1D-29
db -1,"\zxcvbnm,./",-1,"*",0,32,-2 ;2A-3A
shift:
db 0,27,"!@#$%^&*()_+",8 ;00-0E
db 9,"QWERTYUIOP{}",10 ;0F-1C
db 0,'ASDFGHJKL:"~' ;1D-29
db -1,"|ZXCVBNM<>?",-1,"*",0,32,-2 ;2A-3A
section .text
;---------------------------------------------------------------
; This code is based on the video driver for FTS/Forth. Samuel
; Falvo II has kindly granted permission for me to redistribute
; and modify it without restrictions. Thank you Sam!
;---------------------------------------------------------------
; Current VGA text-mode screen location, along with
; some descriptive attributes
video_base: dd 0xB8000
video_width: dd 80
video_height: dd 25
; Current VGA text-mode color attribute.
; NOTE that it's shifted left by 8 bits, ready for
; logical ORing with a character code.
video_attributes: dd 0x0700
; Current VGA cursor position. Also dictates where
; the next character will be displayed via EMIT or
; TYPE. These probably don't need to be 32-bits wide,
; but it's more convenient for asm coding purposes if
; they are.
video_cursorX: dd 0
video_cursorY: dd 0
; I/O ports for the VGA device in question
video_io: dd 0x3D4
;****** vChangeAttributes *************************************
;
; NAME
; vChangeAttributes
;
; SYNOPSIS
; mov eax,andMask
; mov ebx,xorMask
; call vChangeAttributes
; mov [oldAttributes],eax
;
; FUNCTION
; This function can be used to clear, set, replace, or toggle
; all video attributes flags, depending on the settings of
; EAX and EBX. The attribute flags currently defined are as
; follows:
;
; Bit 0-7: Used to force character code bits to 1.
; Bit 8-11: Foreground color
; Bit 12-14: Background color
; Bit 15: Blink flag
; Bit 16-31: Unused -- leave clear.
;
; Note that for normal text output operation, bits 7-0 must be
; left 0.
;
; INPUTS
; EAX This register contains the AND-mask. This mask is
; applied first. Typically used to "zero out" a sub-field
; to be set via EBX.
;
; EBX This register contains the XOR-mask. This mask is
; applied second.
;
; RESULT
; EAX contains the *former* set of attributes.
;
; NOTE
; To query the current attributes without changing them,
; clear EAX and EBX, like this:
;
; xor eax,eax
; xor ebx,ebx
; call vChangeAttributes
; mov [currentAttributes],eax
;
; BUGS
;
; SEE ALSO
;
;**************************************************************
vChangeAttributes:
xchg eax,[video_attributes]
and [video_attributes],eax
xor [video_attributes],ebx
ret
;****** vRethinkCursor ****************************************
;
; NAME
; vRethinkCursor
;
; SYNOPSIS
; call vRethinkCursor
;
; FUNCTION
; Updates the video hardware to relocate the displayed
; text cursor to match expectations of the running environment.
;
; INPUTS
;
; RESULT
;
; BUGS
; This function is undefined if video_cursorX and/or
; video_cursorY is outside the normal addressible bounds
; of the current display.
;
; SEE ALSO
; vCR, vLF, vMoveTo
;
;**************************************************************
vRethinkCursor:
push eax
push ebx
push edx
; Compute offset
; ASSUMES: 0 <= video_cursorX < video_width
; ASSUMES: 0 <= video_cursorY < video_height
mov eax,[video_cursorY]
imul eax,[video_width]
add eax,[video_cursorX]
mov ebx,eax
; Write low offset byte
mov al, 0x0f
mov dx, [video_io]
out dx, al
mov al, bl ;AL = offset low
inc edx ;0x3d5
out dx, al
; Write high offset byte
mov al, 0x0e ;Index reg.: cursor pos (high byte)
dec edx ;0x3d4
out dx, al
mov al, bh ;AL = offset high
inc edx ;0x3d5
out dx, al
pop edx
pop ebx
pop eax
ret
;****** vClear ************************************************
;
; NAME
; vClear
;
; SYNOPSIS
; call vClear
;
; FUNCTION
; Fills the entire screen with space characters, using the
; currently set attribute. The cursor position is NOT
; affected by this call.
;
; INPUTS
;
; RESULT
;
; BUGS
;
; SEE ALSO
; vHome
;
;**************************************************************
vClear:
push eax
push ebx
push ecx
mov eax,[video_base]
mov ebx,[video_height]
imul ebx,[video_width]
mov ecx,[video_attributes]
or ecx,0x20
.more: mov [eax],cx
add eax,2
dec ebx
or ebx,ebx
jnz .more
pop ecx
pop ebx
pop eax
ret
;****** vHome *************************************************
;
; NAME
; vHome
;
; SYNOPSIS
; call vHome
;
; FUNCTION
; Locates the cursor in the upper left-hand corner of the
; display.
;
; INPUTS
;
; RESULT
;
; BUGS
;
; SEE ALSO
; vCR, vLF, vClear
;
;**************************************************************
vHome:
mov [video_cursorY],dword 0
; fall-through to vCR intentional
;****** vCR ***************************************************
;
; NAME
; vCR
;
; SYNOPSIS
; call vCR
;
; FUNCTION
; Performs the glass-tty equivalent of a carriage return.
;
; INPUTS
;
; RESULT
;
; SEE ALSO
; vLF, vHome
;
;**************************************************************
vCR:
mov [video_cursorX],dword 0
jmp vRethinkCursor
;****** vLF ***************************************************
;
; NAME
; vLF
;
; SYNOPSIS
; call vLF
;
; FUNCTION
; Performs the glass-tty equivalent of a line feed. Note
; that the horizontal cursor position is NOT affected by
; this function.
;
; INPUTS
;
; RESULT
;
; BUGS
;
; SEE ALSO
; vHome, vCR
;
;**************************************************************
vLF:
push eax
mov eax,[video_cursorY]
inc eax
cmp eax,[video_height]
jb .scrolling_unnecessary
dec eax
call vScrollUp
.scrolling_unnecessary:
mov [video_cursorY],eax
call vRethinkCursor
pop eax
call vCR
ret
;****** vScrollUp *********************************************
;
; NAME
; vScrollUp
;
; SYNOPSIS
; call vScrollUp
;
; FUNCTION
; Scrolls the whole screen up one line, filling in the bottom
; with spaces in the currently set attribute. This function
; does NOT change the current cursor location.
;
; INPUTS
;
; RESULT
;
; BUGS
;
; SEE ALSO
; vChangeAttributes, vCR, vLF
;
;**************************************************************
vScrollUp:
push eax
push ebx
push ecx
; Scroll the display up one line
xor eax,eax ; Get offset for (0,1)
lea ebx,[eax+1]
call vOffsetAt ; EAX := source
mov ebx,[video_base] ; EBX := destination address
mov ecx,[video_width] ; ECX := number of bytes to move
imul ecx,[video_height]
sub ecx,[video_width]
shl ecx,1
call uMoveDown
; Overwrite the garbage at the bottom.
xor eax,eax
mov ebx,[video_height]
dec ebx
call vOffsetAt
mov ebx,[video_width]
mov ecx,[video_attributes]
or cl,0x20
.more: mov [eax],cx
add eax,2
dec ebx
or ebx,ebx
jnz .more
pop ecx
pop ebx
pop eax
ret
;****** vOffsetAt *********************************************
;
; NAME
; vOffsetAt
;
; SYNOPSIS
; mov eax,Xcoord
; mov ebx,Ycoord
; call vOffsetAt
; mov [memLocation],eax
;
; FUNCTION
; Given an X and Y coordinate on the display (with the upper-
; left-hand corner set to (0,0)), return a memory address
; with which a character and attribute pair can be stored.
;
; INPUTS
; EAX The horizontal coordinate
; EBX The vertical coordinate
;
; RESULT
; Byte address of the corresponding character in the video
; frame buffer.
;
; BUGS
; Strange things happen if the X or Y coordinate are out of
; bounds for the current VGA frame buffer size.
;
; SEE ALSO
;
;**************************************************************
vOffsetAt:
push ebx
imul ebx,[video_width]
add eax,ebx
shl eax,1
add eax,[video_base]
pop ebx
ret
;****** vEmit *************************************************
;
; NAME
; vEmit
;
; SYNOPSIS
; mov al,charToEmit
; call vEmit
;
; FUNCTION
; Displays the character in al to the screen at the current
; cursor position. This function DOES interpret the following
; control codes:
;
; $08 Backspace -- moves cursor to the left one position
; $09 Tab -- moves cursor to next tab stop (every 8 chars)
; $0A Line feed -- advances cursor down one line
; $0C Form feed -- clears the screen and homes the cursor
; $0D Carriage Return -- Restores cursor to column 0
;
; INPUTS
; AL Character to emit to the display
;
; RESULT
;
; BUGS
;
; SEE ALSO
; vRawEmit
;
;**************************************************************
vEmit:
cmp al,0x08
jz vBS
cmp al,0x09
jz vTAB
cmp al,0x0A
jz vLF
cmp al,0x0C
jz vFF
cmp al,0x0D
jz vCR
; flow-through to vRawEmit is intentional.
;****** vRawEmit **********************************************
;
; NAME
; vRawEmit
;
; SYNOPSIS
; mov al,charToEmit
; call vRawEmit
;
; FUNCTION
; Displays the character in al to the screen at the current
; cursor position. This function DOES NOT interpret any
; control codes.
;
; INPUTS
; AL Character to emit to the display
;
; RESULT
;
; BUGS
;
; SEE ALSO
; vEmit
;
;**************************************************************
vRawEmit:
push eax
push ebx
push ecx
and eax,0x000000FF
or eax,[video_attributes]
mov ecx,eax
mov eax,[video_cursorX]
mov ebx,[video_cursorY]
call vOffsetAt
mov [eax],cx
pop ecx
pop ebx
pop eax
; fall-through to vBumpCursor is intentional.
;****** vBumpCursor *******************************************
;
; NAME
; vBumpCursor
;
; SYNOPSIS
; call vBumpCursor
;
; FUNCTION
; Advances the cursor one character to the right.
; If wrap-around occurs, it will further drop the cursor
; down one line, if possible. If not, the screen is
; scrolled.
;
; INPUTS
;
; RESULT
;
; BUGS
;
; SEE ALSO
; vEmit, vRawEmit, vLF, vCR
;
;**************************************************************
vBumpCursor:
push eax
mov eax,[video_cursorX]
inc eax
cmp eax,[video_width]
jnb .wrapAround
mov [video_cursorX],eax
pop eax
jmp vRethinkCursor
.wrapAround:
pop eax
call vCR
jmp vLF
;****** vType *************************************************
;
; NAME
; vType
;
; SYNOPSIS
; mov eax,msgBuffer
; mov ebx,msgLength
; call vType
;
; FUNCTION
; Displays the text stored in msgBuffer, consisting of msgLength
; bytes. It is safe to type a buffer of zero bytes.
;
; INPUTS
; EAX Pointer to the buffer to display
; EBX Length of the buffer. If 0, no text is displayed.
;
; RESULT
;
; BUGS
;
; SEE ALSO
; vEmit, vRawEmit
;
;**************************************************************
vType:
push eax
push ebx
push ecx
mov ecx,eax
.again: or ebx,ebx
jz .done
mov al,[ecx]
inc ecx
call vEmit
dec ebx
jmp .again
.done: pop ecx
pop ebx
pop eax
ret
;****** vBS ***************************************************
;
; NAME
; vBS
;
; SYNOPSIS
; call vBS
;
; FUNCTION
; Emulates the glass-tty equivalent of a backspace on a
; teletype by moving the cursor to the left, if possible.
;
; INPUTS
;
; RESULT
;
; BUGS
;
; SEE ALSO
; vTAB
;
;**************************************************************
vBS: push eax
call vRawBS
mov al,' '
call vEmit
pop eax
; fall-through to vBumpCursor is intentional.
vRawBS: push eax
mov eax,[video_cursorX]
or eax,eax
jz .asFarAsItllGo
dec eax
mov [video_cursorX],eax
call vRethinkCursor
.asFarAsItllGo:
pop eax
ret
;****** vTAB **************************************************
;
; NAME
; vTAB
;
; SYNOPSIS
; call vTAB
;
; FUNCTION
; Advances the cursor to the next tabstop, which is currently
; every 8 characters.
;
; INPUTS
;
; RESULT
;
; BUGS
;
; SEE ALSO
; vBS, vCR, vLF
;
;**************************************************************
vTAB: push eax
mov eax,[video_cursorX]
and eax,7
jz .moveAnother8
.oneMoreSpace:
mov al,0x20
call vEmit
mov eax,[video_cursorX]
and eax,7
jnz .oneMoreSpace
pop eax
ret
.moveAnother8:
mov al,0x20
call vEmit
call vEmit
call vEmit
call vEmit
call vEmit
call vEmit
call vEmit
call vEmit
pop eax
ret
;****** vPage / vFF *******************************************
;
; NAME
; vPage
;
; AKA
; vFF
;
; SYNOPSIS
; call vPage ; or...
; call vFF
;
; FUNCTION
; Clears the screen and homes the cursor, thus emulating
; a form-feed operation on a teletype (hence the name; it
; delivers a "new page").
;
; INPUTS
;
; RESULT
;
; BUGS
;
; SEE ALSO
; vTAB, vCR, vLF, vEmit
;
;**************************************************************
vPage:
vFF:
call vClear
jmp vHome
;****** uMoveDown *********************************************
;
; NAME
; uMoveDown
;
; SYNOPSIS
; mov eax,srcAddress
; mov ebx,dstAddress
; mov ecx,nbrOfBytes
; call uMoveDown
;
; FUNCTION
; Moves ECX bytes from EBX to EAX. This move is assumed
; correct if and only if EBX <= EAX. Otherwise, memory
; is filled with a repeating pattern.
;
; INPUTS
; EAX source address
; EBX destination address
; ECX number of bytes to move
;
; RESULT
;
; BUGS
;
; SEE ALSO
; uMoveUp, uMove
;
;**************************************************************
uMoveDown:
push eax
push ebx
push ecx
push edx
cmp ecx,4
jb .no32bits
.more32bits:
mov edx,[eax]
add eax,4
mov [ebx],edx
add ebx,4
sub ecx,4
cmp ecx,4
jae .more32bits
.no32bits:
cmp ecx,2
jb .no16bits
.more16bits:
mov dx,[eax]
add eax,2
mov [ebx],dx
add ebx,2
sub ecx,2
cmp ecx,2
jae .more16bits
.no16bits:
or ecx,ecx
jz .done
.morebytes:
mov dl,[eax]
inc eax
mov [ebx],dl
inc ebx
dec ecx
or eax,eax
jnz .morebytes
.done:
pop edx
pop ecx
pop ebx
pop eax
ret
section .bss
align 4
stack:
resb STACKSIZE
stack_ptr: