#define RET_LABEL(label)        \
	jmp label##_done

#define CALL_LABEL(label)       \
	jmp label               ;\
label##_done:

#define CALLSP(func) \
	lea     0f, %esp        ; \
	jmp func                ; \
0:

#define RETSP \
	jmp *%esp


#include "console.inc"
#include "pci.inc"
#include "ramtest.inc"

jmp llshell_out

// (c) 2004 Bryan Chafy,  This program is released under the GPL

// LLShell, A low level interactive debug shell
// Designed to be an interactive shell that operates with zero
// system resources.  For example at initial boot.

// to use, jump to label "low_level_shell"
// set %esp to the return address for exiting


#define UART_BASEADDR $0x3f8
#define resultreg %esi
#define subroutinereg %edi
#define freqtime $2193  // 1.93 * freq
#define timertime $6000
.equ	sys_IOPL, 110

// .data
// .text

welcome:
 	.string "\r\n! Low Level Shell (LLShell)  (c)2004 Bryan Chafy \r\n\r\n"
prompt:
	.string "\r\n!> "
badcmd:
 	.string "bad command\r\n"
sorry:
 	.string "sorry, not yet implemented\r\n"
cmds:
       	.string "\r\nList of commands:\r\n \
\r\nbeep                    -- pc speaker beep \
\r\nrst (or RST)            -- reset \
\r\nout(b,w,l) <val> <port> -- raw out val at port \
\r\nin(b,w,l)  <port>       -- show raw port value \
\r\njmp  <address>          -- jmp to address (llshell addr is in eax) \
\r\ncall <address>          -- funcion call (assumes a working stack) \
\r\ncli                     -- clear interrupts \
\r\nsti                     -- enable interrupts \
\r\npush <value>            -- push value onto stack \
\r\npop                     -- pop from stack and display \
\r\nwm(b,w,l) <addr> <val>  -- write mem \
\r\ndm   <addr> <lines>     -- dump mem  \
\r\nmcp  <src> <dst> <size> -- mem copy  \
\r\nmpat <pat> <dst> <size> -- mem pattern \
\r\nmemt <begin> <end>      -- memory test \
\r\npcir(b,w,l) <loc>       -- pci read config \
\r\npciw(b,w,l) <loc> <val> -- pci write config  \
\r\ndl   <addr> <size>      -- memory download (display xor cheksum at completion) \
\r\ncram <addr> <size>      -- enable cache to be ram (experimental) \
\r\nbaud <val>              -- change baudrate (not yet implemented)  \
\r\nexit                    -- exit shell \
\r\nAll values in hex (0x prefixing ok)  \
\r\n"

cr:
	.string "\r\n"
spaces:
	.string "   "

// .globl _start
//ASSUME CS:@CODE, DS:@DATA

// _start:

// call ioperms

low_level_shell:

mov $preamble,subroutinereg
jmp beep
preamble:
mov $welcome,resultreg
mov $readcommand,subroutinereg
jmp displaystring

readcommand:
mov $prompt,resultreg
mov $rcmd,subroutinereg
jmp displaystring

rcmd:
mov $readcommand,subroutinereg
movl $0x0, resultreg

readchar:
mov  UART_BASEADDR+5,%dx
in   %dx, %al
and  $0x9f,%al
test $0x01,%al
jz   readchar
mov  UART_BASEADDR,%dx
in   %dx,%al             //char in al
xchg %al,%ah

send_char:
mov  UART_BASEADDR+5,%dx
us_wait:
in   %dx,%al
test $0x20,%al
jz us_wait
mov  UART_BASEADDR,%dx
xchg %al,%ah
out  %al,%dx            // output char

cmp  $0x0D,%al      //CR
jnz  eval_char
mov  $0x0A,%ah
jmp  send_char

eval_char:
cmp  $0x20,%al      //space
jz   cmdtable
cmp  $0x0A,%al      //CR
jz   cmdtable
cmp  $0x08,%al      //BS
jnz  additup
//subit:
shr  $0x8,resultreg
jmp  readchar
additup:
shl  $0x8,resultreg
and  $0xff,%eax
add  %eax,resultreg
jmp  readchar

cmdtable:
mov resultreg,%eax
cmp $0,%eax
jz  readcommand
cmp $0x74657374,%eax
jz  dotest
cmp $0x68656c70,%eax
jz  dohelp
cmp $0x0000003f,%eax
jz  dohelp
cmp $0x6f757462,%eax
jz  dooutb
cmp $0x6f757477,%eax
jz  dooutw
cmp $0x6f75746c,%eax
jz  dooutd
cmp $0x00696e62,%eax
jz doinb
cmp $0x00696e77,%eax
jz doinw
cmp $0x00696e6c,%eax
jz doind
cmp $0x63697262,%eax
jz pcirb
cmp $0x63697277,%eax
jz pcirw
cmp $0x6369726c,%eax
jz pcirl
cmp $0x63697762,%eax
jz pciwb
cmp $0x63697777,%eax
jz pciww
cmp $0x6369776c,%eax
jz pciwl
cmp $0x00776d62,%eax
jz  wmemb
cmp $0x00776d77,%eax
jz  wmemw
cmp $0x00776d6c,%eax
jz  wmeml
cmp $0x0000646d,%eax
jz  dodmem
cmp $0x6d656d74,%eax
jz  memt                    // mem test
cmp $0x00727374,%eax
jz  rst                     // reset
cmp $0x00525354,%eax
jz  RST
cmp $0x62656570,%eax
jz  beep
cmp $0x0000646c,%eax
jz  dodl                    // download to mem <loc> <size>
cmp $0x006a6d70,%eax
jz  jmpto                   // jump to location (eax holds return addr)
cmp $0x62617564,%eax
jz  baud                    // change baudrate
cmp $0x00696e74,%eax
jz  doint                   // trigger an interrupt
cmp $0x63616c6c,%eax
jz  callto                  // call assumes memory
cmp $0x70757368,%eax
jz  dopush                  // assumes mem
cmp $0x00706f70,%eax
jz  dopop                   // assumes mem
cmp $0x6372616d,%eax
jz  cram                    // cache ram trick <location> <size>
cmp $0x006d6370,%eax
jz  mcp                     // mem copy <src> <dst> <size>
cmp $0x6d706174,%eax
jz  dopattern
cmp $0x00636c69,%eax
jz docli
cmp $0x00737469,%eax
jz dosti
cmp $0x65786974,%eax
jz  doexit
mov $badcmd,resultreg
mov $readcommand,subroutinereg
jmp displaystring


readnibbles:
movl $0x0, resultreg
readit:
mov  UART_BASEADDR+5,%dx
in   %dx, %al
and  $0x9f,%al
test $0x1,%al
jz   readit
mov  UART_BASEADDR,%dx
in   %dx,%al
xchg %al,%ah

sendchar:
mov  UART_BASEADDR+5,%dx
us_waitit:
in   %dx,%al
test $0x20,%al
jz   us_waitit
mov  UART_BASEADDR,%dx
xchg %al,%ah
out  %al,%dx            // output char

cmp  $0x78,%al
jz   readit
cmp  $0x0D,%al      //CR
jnz  evalchar
mov  $0x0A,%ah
jmp  sendchar

evalchar:
cmp  $0x20,%al        //space
jz   gosub
cmp  $0x0A,%al      //CR
jz   gosub
cmp  $0x08,%al      //BS
jnz  processchar
//subit:
shr  $0x04,resultreg
jmp  readit
processchar:
cmp  $0x3A,%al
jl   subnum
cmp  $0x47,%al
jl   subcaps
//sublc:
sub  $0x57,%al
jmp  additupn
subcaps:
sub  $0x37,%al
jmp  additupn
subnum:
sub  $0x30,%al
additupn:
shl  $0x04,resultreg
and  $0xf,%eax
add  %eax,resultreg
jmp  readit

gosub:
jmp  *subroutinereg

//intersubcall
// eax,edx,esi,edi

// ebx,ecx,ebp,esp(?)
// ds,es,fs,gs

dotest:
mov $ramtest,resultreg
mov $test1a,subroutinereg
jmp displayhex
test1a:
mov $welcome,resultreg
mov $readcommand,subroutinereg
jmp displayhex

dodmem:

movl $dmem1a, subroutinereg
jmp  readnibbles
dmem1a:
mov  resultreg,%ebx    // address
movl $dmem1b, subroutinereg
jmp  readnibbles
dmem1b:
mov  resultreg,%ecx    // length

dmemloop:
mov %ebx,resultreg
mov $daddr1,subroutinereg
jmp displayhex
daddr1:
mov $spaces,resultreg
mov $startshowm,subroutinereg
jmp displaystring

startshowm:
mov (%ebx),resultreg
mov $showm1,subroutinereg
jmp displayhexlinear
showm1:
add $0x04,%ebx
mov (%ebx),resultreg
mov $showm2,subroutinereg
jmp displayhexlinear
showm2:
add $0x04,%ebx
mov (%ebx),resultreg
mov $showm3,subroutinereg
jmp displayhexlinear
showm3:
add $0x04,%ebx
mov (%ebx),resultreg
mov $showa0,subroutinereg
jmp displayhexlinear

showa0:
sub $0xC,%ebx
mov (%ebx),resultreg
mov $showa1,subroutinereg
jmp displayasciilinear
showa1:
add $0x04,%ebx
mov (%ebx),resultreg
mov $showa2,subroutinereg
jmp displayasciilinear
showa2:
add $0x04,%ebx
mov (%ebx),resultreg
mov $showa3,subroutinereg
jmp displayasciilinear
showa3:
add $0x04,%ebx
mov (%ebx),resultreg
mov $doneshow,subroutinereg
jmp displayasciilinear
doneshow:
mov $cr,resultreg
mov $doneshow1,subroutinereg
jmp displaystring
doneshow1:
dec %cx
cmp $0x0,%cx
jz  exitdmem
add $0x04,%ebx
jmp dmemloop
exitdmem:
jmp readcommand

dooutb:
// out val,port
movl $outb1a, subroutinereg
jmp  readnibbles
outb1a:
mov  resultreg,%ebx
movl $outb1b, subroutinereg
jmp  readnibbles
outb1b:
mov  resultreg,%edx
mov  %ebx,%eax
out  %al,%dx
jmp  readcommand

dooutw:
// out val,port
movl $outw1a, subroutinereg
jmp  readnibbles
outw1a:
mov  resultreg,%ebx
movl $outw1b, subroutinereg
jmp  readnibbles
outw1b:
mov  resultreg,%edx
mov  %ebx,%eax
out  %ax,%dx
jmp  readcommand

dooutd:
// out val,port
movl $outd1a, subroutinereg
jmp  readnibbles
outd1a:
mov  resultreg,%ebx
movl $outd1b, subroutinereg
jmp  readnibbles
outd1b:
mov  resultreg,%edx
mov  %ebx,%eax
out  %eax,%dx
jmp  readcommand

wmemb:
movl $wmemba, subroutinereg
jmp  readnibbles
wmemba:
mov  resultreg,%ebx
movl $wmembb, subroutinereg
jmp  readnibbles
wmembb:
mov  resultreg,%eax
mov  %al,(%ebx)
jmp  readcommand

wmemw:
movl $wmemwa, subroutinereg
jmp  readnibbles
wmemwa:
mov  resultreg,%ebx
movl $wmemwb, subroutinereg
jmp  readnibbles
wmemwb:
mov  resultreg,%eax
mov  %ax,(%ebx)
jmp  readcommand

wmeml:
movl $wmemla, subroutinereg
jmp  readnibbles
wmemla:
mov  resultreg,%ebx
movl $wmemlb, subroutinereg
jmp  readnibbles
wmemlb:
mov  resultreg,%eax
mov  %eax,(%ebx)
jmp  readcommand

doinb:
// in port
movl $inb1a, subroutinereg
jmp  readnibbles
inb1a:
mov  resultreg,%edx
mov  $0x0,%eax
in   %dx,%al
mov  %eax,resultreg
mov  $readcommand,subroutinereg
jmp  displayhex

doinw:
// in port
movl $inw1a, subroutinereg
jmp  readnibbles
inw1a:
mov  resultreg,%edx
mov  $0x0,%eax
in   %dx,%ax
mov  %eax,resultreg
mov  $readcommand,subroutinereg
jmp  displayhex

doind:
// in port
movl $ind1a, subroutinereg
jmp  readnibbles
ind1a:
mov  resultreg,%edx
in   %dx,%eax
mov  %eax,resultreg
mov  $readcommand,subroutinereg
jmp  displayhex

jmpto:
movl $jmp1a, subroutinereg
jmp  readnibbles
jmp1a:
mov  $readcommand,%eax
jmp  *resultreg

callto:
movl $call1a, subroutinereg
jmp  readnibbles
call1a:
mov  $readcommand,%eax
call *resultreg
jmp  readcommand

dopush:
movl $push1a, subroutinereg
jmp  readnibbles
push1a:
mov  resultreg,%eax
push %eax
jmp  readcommand

doint:
movl $int1a, subroutinereg
jmp  readnibbles
int1a:
mov  resultreg,%eax
// need to lookup int table?
// int  %eax
jmp  readcommand

doenter:
//setup stack frame


dopop:
movl $readcommand, subroutinereg
pop  resultreg
jmp  displayhex

docli:
cli
jmp  readcommand

dosti:
sti
jmp  readcommand


displaystring:
// resultreg= pointer to string terminated by \0
dsloop:
movb  (resultreg),%ah
cmp  $0x0, %ah
jz   displaystringexit
mov  UART_BASEADDR+5,%dx
us_waits:
in   %dx,%al
test $0x20,%al
jz us_waits
mov  UART_BASEADDR,%dx
xchg %al,%ah
out  %al,%dx            // output char
inc  resultreg
jmp  dsloop
displaystringexit:
jmp  *subroutinereg

displayhexlinear:
mov  resultreg,%eax
xchg %al,%ah
rol  $0x10,%eax
xchg %al,%ah
mov  %eax,resultreg
displayhex:
rol  $0x10,%ecx
mov  $0x8,%cx
dhloop:
cmp  $0xf,%cl
je   exitdisplayhex
rol  $0x04,resultreg
movl resultreg,%eax
and  $0xf,%al
cmp  $0xa,%al
jl   addnum
//addcaps
add $0x37,%al
jmp  outcharhex
addnum:
add  $0x30,%al
outcharhex:
xchg %al,%ah
mov  UART_BASEADDR+5,%dx
us_waith:
in   %dx,%al
test $0x20,%al
jz us_waith
mov  UART_BASEADDR,%dx
xchg %al,%ah
out  %al,%dx            // output char
dec  %cx
cmp  $0x0,%cx
jne  dhloop
mov  $0x20,%al
mov  $0x10,%cl
jmp  outcharhex
exitdisplayhex:
rol  $0x10,%ecx
jmp  *subroutinereg

displayasciilinear:
mov  resultreg,%eax
xchg %al,%ah
rol  $0x10,%eax
xchg %al,%ah
mov  %eax,resultreg
displayascii:
rol  $0x10,%ecx
mov  $0x4,%cx
daloop:
rol  $0x08,resultreg
movl resultreg,%eax
cmp  $0x7e,%al
jg   unprintable
cmp  $0x20,%al
jl   unprintable
jmp  outcharascii
unprintable:
mov  $0x2e,%al          // dot
outcharascii:
xchg %al,%ah
mov  UART_BASEADDR+5,%dx
us_waita:
in   %dx,%al
test $0x20,%al
jz us_waita
mov  UART_BASEADDR,%dx
xchg %al,%ah
out  %al,%dx            // output char
dec  %cx
cmp  $0x0,%cx
jne  daloop
rol  $0x10,%ecx
jmp  *subroutinereg

rst:
cli
movb $0x0fe,%al
out  %al,$0x64
hlt

RST:
cli
lidt %cs:0x03fff
int  $0x3
hlt


beep:
mov  timertime,%eax
rol  $0x10,%eax
mov  $0xb6,%al
out  %al,$0x43
mov  freqtime,%ax
out  %al,$0x42
xchg %al,%ah
out  %al,$0x42

in   $0x61,%al
or   $0x03,%al
out  %al,$0x61

//timer here
timer:
in   $0x42,%al
// xchg %al,%ah
in   $0x42,%al
// xchg %al,%ah
cmp  $0x0,%al
jnz  timer
rol  $0x10,%eax
dec  %ax
cmp  $0x0,%ax;
rol  $0x10,%eax
jnz  timer
// timer

in   $0x61,%al
and  $0xfc,%al
out  %al,$0x61
jmp  *subroutinereg

dohelp:
mov $cmds,resultreg
mov $readcommand,subroutinereg
jmp displaystring

memt:
movl $memt1, subroutinereg
jmp  readnibbles
memt1:
mov  resultreg,%ecx
movl $memt2, subroutinereg
jmp  readnibbles
memt2:
mov  resultreg,%ebx
xchg %ecx,%eax
mov  $readcommand,%esp   // internal to linux bios
jmp ramtest

pcirb:
movl $pcirb1, subroutinereg
jmp  readnibbles
pcirb1:
mov  resultreg,%eax
PCI_READ_CONFIG_BYTE
and  $0xff,%eax
mov  %eax,resultreg
mov  $readcommand,subroutinereg
jmp  displayhex

pcirw:
movl $pcirw1, subroutinereg
jmp  readnibbles
pcirw1:
mov  resultreg,%eax
PCI_READ_CONFIG_WORD
and  $0xffff,%eax
mov  %eax,resultreg
mov  $readcommand,subroutinereg
jmp  displayhex

pcirl:
movl $pcirl1, subroutinereg
jmp  readnibbles
pcirl1:
mov  resultreg,%eax
PCI_READ_CONFIG_DWORD
mov  %eax,resultreg
mov  $readcommand,subroutinereg
jmp  displayhex




pciwb:
movl $pciwb1, subroutinereg
jmp  readnibbles
pciwb1:
mov  resultreg,%ebx
movl $pciwb2, subroutinereg
jmp  readnibbles
pciwb2:
mov  resultreg,%edx
mov  %ebx,%eax
PCI_WRITE_CONFIG_BYTE
jmp  readcommand

pciww:
movl $pciww1, subroutinereg
jmp  readnibbles
pciww1:
mov  resultreg,%ebx
movl $pciww2, subroutinereg
jmp  readnibbles
pciww2:
mov  resultreg,%ecx
mov  %ebx,%eax
PCI_WRITE_CONFIG_WORD
jmp  readcommand

pciwl:
movl $pciwl1, subroutinereg
jmp  readnibbles
pciwl1:
mov  resultreg,%ebx
movl $pciwl2, subroutinereg
jmp  readnibbles
pciwl2:
mov  resultreg,%ecx
mov  %ebx,%eax
PCI_WRITE_CONFIG_DWORD
jmp  readcommand

cram:
//likely not working.  Just testing for now
movl $cram1, subroutinereg
jmp  readnibbles
cram1:
mov resultreg,%ebx
movl $cram1, subroutinereg
jmp  readnibbles
cram2:
mov resultreg,%ecx
// enable it
mov %cr0,%eax
and $0x9fffffff,%eax  // also try 0x0fff, 0x2ff(write back)...
mov %eax,%cr0
//wbinvd ??
cacheloop:
mov (%ebx),%eax
inc %ebx
loop cacheloop
// disable it
mov %cr0,%eax
or  $0x60000000,%eax
mov %eax,%cr0
//wbinvd ??

dodl:
movl $dl1, subroutinereg
jmp  readnibbles
dl1:
mov  resultreg,%ebx
movl $dl2, subroutinereg
jmp  readnibbles
dl2:
mov  resultreg,%ecx
mov  resultreg,subroutinereg
mov  %ebx,resultreg
dlloop:
mov  UART_BASEADDR+5,%dx
in   %dx, %al
and  $0x9f,%al
test $0x01,%al
jz   dlloop
mov  UART_BASEADDR,%dx
in   %dx,%al
mov  %al,(%ebx)
inc  %ebx
loop dlloop
csum:
mov subroutinereg,%ecx
shr $0x02,%ecx
mov resultreg,%ebx
mov $0x0,%eax
csumloop:
rol $0x03,%eax
mov (%ebx),%dl
xor  %dl,%al
inc  %ebx
loop csumloop
mov $readcommand,subroutinereg
mov %eax,resultreg
jmp displayhex

baud:
mov $sorry,resultreg
mov $readcommand,subroutinereg
jmp displaystring

mcp:
movl $mcp1, subroutinereg
jmp  readnibbles
mcp1:
mov  resultreg,%ebx
movl $mcp2, subroutinereg
jmp  readnibbles
mcp2:
mov  resultreg,%ecx
movl $mcp3, subroutinereg
jmp  readnibbles
mcp3:
mov  resultreg,%eax
xchg %ecx,%eax
mcploop:
mov  (%ebx),%dl
mov  %dl,(%eax)
inc  %ebx
inc  %eax
loop mcploop
jmp  readcommand

dopattern:
movl $pat1, subroutinereg
jmp  readnibbles
pat1:
mov  resultreg,%ebx
movl $pat2, subroutinereg
jmp  readnibbles
pat2:
mov  resultreg,%ecx
movl $pat3, subroutinereg
jmp  readnibbles
pat3:
mov  resultreg,%eax
xchg %ecx,%eax
patloop:
rol $0x08,%ebx
mov %bl,(%eax)
inc %eax
loop patloop
jmp readcommand


doexit:
         // LB specific:
RETSP    // if there's no stack yet, caller must set %esp manually
// RET_LABEL(low_level_shell)


//Linux OS Specific
ioperms:
movl	$sys_IOPL, %eax		# system-call ID-number
movl	$3, %ebx		# new value for IO0PL
int	$0x80			# enter the kernel
ret

llshell_out: