//mmprgn.bcpl

get "mdecl.d"

external [
// OS
Wss; Wns; Resets; Puts; CallSwat; Noop; DoubleAdd; MoveBlock; Zero

// MASM
StrSize; ErrorProtect

// MDISP
Blink

// MRGN
CharInputRoutine

// MIOC
RTexttoData; MTexttoData; RDatatoStream; MDatatoStream
SimpleDVectoStream; SimpleTexttoDVec

// MMENU
CreateAction; MarkMenus; ItemStream; FormMenu
SkipCommandCount

// MSYMB
EvalAText; TVtoString; SearchBlocks

// MTXTBUF
InputTextBuffer

// MCMD
CmdCommentStream; DisplayError; WsMarkA; WssCSS

// Machine interface
MGetRegData; MGetMemData; MPutRegData; MPutMemData; @MEMNAM
AltRForms; AltMForms

// Defined here
MPDSwitchPhase; DoubleNeg; ShowAddr; ShowBadAddr
@LongOne; LongMinOne

// Defined here for init only
MPDAs; MPDAVec; MPDVs; MPDVVec; MPDEveryTime
MPDlist; InitMPDispRegions; MPDdeSelect
MPDrlx; MPDrcx; BugAddrAct; BugValAct; Plus1Act; Minus1Act
SkipVEql; SkipVGr; SkipVLs
]


manifest [
// form of the menu
StandardMenu = 1; AddressMenu = 2

// Phases
BadValuePhase = 1; PhaseA = 2; PhaseB = 3

// mouse buttons
TopButton = #4; MiddleButton = #1; BottomButton = #2
]


static
[
MPDAs; MPDAVec; MPDVs; MPDVVec
MPDPhase = PhaseA; OldMPDPhase = PhaseA
MPDrlx = 0; MPDrcx = 0; MPDlist = 0
@LongOne; LongMinOne

// Actions
BugAddrAct; BugValAct; Plus1Act; Minus1Act
]

let InitMPDispRegions() be
[
LongOne = table [ 0; 1 ]
LongMinOne = table [ -1; -1 ]
BugAddrAct = CreateAction("Addr",lv SetAddr,0,lv AddrMChange)
BugValAct = CreateAction("Val",lv SetValue,0,lv ValMChange)
Plus1Act = CreateAction("A+1",lv StepAddr,true,lv AddrMChange)
Minus1Act = CreateAction("A-1",lv StepAddr,false,lv AddrMChange)
]


and MPDSwitchPhase() be MPDPhase = ( OldMPDPhase eq PhaseA? PhaseB, PhaseA)


//This routine is called every time through DriverLoop. It is
//responsible for repainting all of the MPD menus which have changed.
//The various action routines and the initialization code set
//phase = BadValuePhase which forces painting. The microprocessor
//interface code reverses the current phase which causes only those
//items with changed values to paint
and MPDEveryTime(MPD,BuildingDisplay; numargs NA) be
[ while MPD ne 0 do
[ if MPDPhase ne MPD>>MPD.Phase then
[
test MPD>>MPD.Idle ne 0
ifso if MPD>>MPD.Phase eq BadValuePhase then
FormMenu(MPD>>MPD.MDFS,FormMPDmenu,MPD)
ifnot
[ let Val = vec ValSize; MoveBlock(Val,lv MPD>>MPD.Value,ValSize)
switchon MPD>>MPD.AVal.TypeStorage into
[
case RegTypeStorage:
MGetRegData(MPD>>MPD.AVal.X,lv MPD>>MPD.Value); endcase

case MemTypeStorage:
MGetMemData(MPD>>MPD.AVal.X,lv MPD>>MPD.AVal.Addr,lv MPD>>MPD.Value)
endcase

default: CallSwat()
]
if ((MPD>>MPD.MenuMode eq StandardMenu) & valof
[ for I = 0 to ValSize-1 do
[ if Val!I ne (lv MPD>>MPD.Value)!I then resultis true ]
resultis false
] ) % (MPD>>MPD.Phase eq BadValuePhase) % (NA > 1) then
FormMenu(MPD>>MPD.MDFS,FormMPDmenu,MPD)
]
MPD>>MPD.Phase = MPDPhase
]
MPD = MPD>>MPD.Rabove
]
OldMPDPhase = MPDPhase
]

and FormMPDmenu(S, MPD) be
[
if MPD>>MPD.Idle ne 0 do
[ for I = 1 to MPD>>MPD.TextSpace do Puts(S,$ )
MarkMenus(BugAddrAct); return
]
switchon MPD>>MPD.MenuMode into
[
case StandardMenu: ShowMPDNormalPict(S, MPD); return

case AddressMenu:
test MPD>>MPD.AVal.TypeStorage ne RegTypeStorage
ifso
[ WsMarkA(Plus1Act)
if ((lv MPD>>MPD.AVal.Addr)!0 ne 0) logor
((lv MPD>>MPD.AVal.Addr)!1 ne 0) do
[ WsMarkA(Minus1Act) ]
]
ifnot ShowMPDNormalPict(S,MPD)
return
]
]


and ShowOffset(S,MPD) be
[
let Offs = lv MPD>>MPD.AVal.Offset
let Sign = MPD>>MPD.AVal.Sign
if (Offs!0 ne 0) % (Offs!1 ne 0) % (Sign eq 0) do
[ Puts(S,(Sign ne 0 ? (Offs!0 < 0 ? $-,$+),$ ))
let Offset = vec 1; MoveBlock(Offset,Offs,2)
if Offset!0 < 0 then DoubleNeg(Offset)
SimpleDVectoStream(S,32,Offset,0,true)
]
]


and ShowBadAddr(Str; numargs NA) be
[
WssCSS((NA < 1 ? "Bad address",Str)); Blink()
]

and ShowMPDNormalPict(S, MPD) be
[
Resets(MPDAs); Resets(MPDVs)
if MPD>>MPD.NoName eq 0 then Wss(MPDAs, lv MPD>>MPD.AVal.SName)
switchon MPD>>MPD.AVal.TypeStorage into
[
case RegTypeStorage:
RDatatoStream(MPDVs,MPD>>MPD.AVal.X,lv MPD>>MPD.Value)
endcase

case MemTypeStorage:
if MPD>>MPD.NoName eq 0 then ShowOffset(MPDAs,MPD)
MDatatoStream(MPDVs,MPD>>MPD.AVal.X,lv MPD>>MPD.Value)
endcase

default: CallSwat()
]

//If the name and value do not fill the text space available, the
//code below leaves a null zone no bigger than 2 chars between them.
//If the name is less than 5 characters, extra blanks extend the name
//up to 5 characters. Any more blanks beyond that extend the value.
//If the value overlaps the name, then the name is truncated except that
//one character of name is always printed (unless the NoName flag is set,
//in which case no name characters are ever printed).
//Note: DVtoS routines never print leading 0’s and blanks

let ALim = MPDAVec!0//Size of name
let BlankSpace = MPD>>MPD.TextSpace - ALim - MPDVVec!0
let VStart = 1
if BlankSpace < 0 do//Overlap?
[ ALim = ALim + BlankSpace//Then truncate name
BlankSpace = 0
if ALim < 0 do//Name completely overlapped?
[ VStart = 1 - ALim; ALim = 0 ]
]
test MPD>>MPD.NoName ne 0
ifso [ BlankSpace = BlankSpace+ALim; ALim = 0 ]
ifnot//Extend name up to 5 chars
[ if ALim le 0 do [ ALim = 1; VStart = VStart+1 ]
for I = 1 to ALim do Puts(S,MPDAVec!I)
while (BlankSpace > 2) & (ALim < 5) do
[ Puts(S,$ ); BlankSpace = BlankSpace-1; ALim = ALim+1 ]
MarkMenus(BugAddrAct)
if BlankSpace > 0 do//But leave gap if possible
//2 char gap if value big enough
[ Puts(S,$ ); BlankSpace = BlankSpace-1
if (BlankSpace > 0) & ((MPDVVec!0 + BlankSpace) ge 5) do
[ Puts(S,$ ); BlankSpace = BlankSpace-1 ]
MarkMenus(0)
]
]
for I = 1 to BlankSpace do Puts(S, $ )//Rest of blanks extend value
for I = VStart to MPDVVec!0 do Puts(S, MPDVVec!I)
MarkMenus(BugValAct)
]

and MPDMChange(TopMenuMode,MiddleMenuMode,BottomMenuMode,MPD,MB) be
[
let NewMode = selecton MB into
[
case TopButton: TopMenuMode
case MiddleButton: MiddleMenuMode
case BottomButton: BottomMenuMode
default: StandardMenu
]
if MPD>>MPD.MenuMode ne NewMode do
[ MPD>>MPD.MenuMode = NewMode; MPD>>MPD.Phase = BadValuePhase ]
]


and AddrMChange(S, MPD, MB, N) be
MPDMChange(StandardMenu,StandardMenu,AddressMenu,MPD,MB)


and ValMChange(S, MPD, MB, N) be
MPDMChange(StandardMenu,StandardMenu,StandardMenu,MPD,MB)


and GetVal(S,MPD,V1,V2) = valof
[
SimpleTexttoDVec(InputTextBuffer,ValSize*16,V1)
SetValue(S,MPD,BottomButton)
SimpleTexttoDVec(InputTextBuffer,ValSize*16,V2)
for I = 0 to ValSize-1 do
[ let X = V1!I - V2!I
if X ne 0 then resultis X
]
resultis 0
]


//Invisible command (command files only)--skip if value in selected
//menu is equal to InputTextBuffer
and SkipVEql(S,MPD) be
[
let V1,V2 = vec ValSize-1,vec ValSize-1
if GetVal(S,MPD,V1,V2) eq 0 then SkipCommandCount = 1
]


//Skip next command if value in selected menu is greater than text arg
and SkipVGr(S,MPD) be
[
let V1,V2 = vec ValSize-1,vec ValSize-1
if GetVal(S,MPD,V1,V2) < 0 then SkipCommandCount = 1
]


and SkipVLs(S,MPD) be
[
let V1,V2 = vec ValSize-1,vec ValSize-1
if GetVal(S,MPD,V1,V2) > 0 then SkipCommandCount = 1
]

and SetAddr(S,MPD,MBunion) be
[
NewStandard(MPD)
let X,AVal = 1,vec size AVal/16
if ((MBunion & MiddleButton) ne 0) & (MPD>>MPD.Idle eq 0) do
[ ShowAddr(lv MPD>>MPD.AVal); return ]
MPD>>MPD.Idle = 1
if InputTextBuffer!0 eq 0 then return
unless EvalAText(InputTextBuffer, lv X, AVal, false) do
[ ShowBadAddr(); return ]
MoveBlock(lv MPD>>MPD.AVal,AVal,size AVal/16)
switchon MPD>>MPD.AVal.TypeStorage into
[
case MemTypeStorage:
if not MGetMemData(MPD>>MPD.AVal.X,lv MPD>>MPD.AVal.Addr,
lv MPD>>MPD.Value) do [ ShowBadAddr(); return ]
case RegTypeStorage: endcase
default: CallSwat()
]
MPD>>MPD.Idle = 0
]


//The +1 and -1 menu items only come up when they are legal, but
//maybe the error checks here will be useful if keyboard stuff is added
and StepAddr(S, MPD, MBUnion, Sign) be
[
let AVec = vec 1; MoveBlock(AVec,lv MPD>>MPD.AVal.Addr,2)
DoubleAdd(AVec,(Sign ? LongOne,LongMinOne))
let DVec = vec 10
test MGetMemData(MPD>>MPD.AVal.X,AVec,DVec)
ifso
[ MoveBlock(lv MPD>>MPD.AVal.Addr,AVec,2)
DoubleAdd(lv MPD>>MPD.AVal.Offset, (Sign ? LongOne, LongMinOne))
NewStandard(MPD)
]
ifnot ShowBadAddr()
]


and ShowAddr(AVal) be
[
switchon AVal>>AVal.TypeStorage into
[
case RegTypeStorage:
WssCSS("Register ")
Wns(CmdCommentStream,AVal>>AVal.X,0,10)
endcase
case MemTypeStorage:
WssCSS(MEMNAM!(AVal>>AVal.X))
Puts(CmdCommentStream,$ )
SimpleDVectoStream(CmdCommentStream,32,lv AVal>>AVal.Addr,0,true)
WssCSS(" = ")
SearchBlocks(CmdCommentStream,AVal>>AVal.X,lv AVal>>AVal.Addr)
]
]

//TopButton going up evaluates input text and stores value.
//MiddleButton going up shows alternate value form on command comment line.
//BottomButton going up treats the value as input text.
and SetValue(S, MPD, MBunion) be
[
let X = MPD>>MPD.AVal.X//= RegX or MemX
let Value,TypeS = lv MPD>>MPD.Value,MPD>>MPD.AVal.TypeStorage
if (MBunion & TopButton) ne 0 do//Set value from InputTextBuffer
[ switchon TypeS into
[
case RegTypeStorage:
RTexttoData(X,InputTextBuffer,Value)
MPutRegData(X,Value); endcase

case MemTypeStorage:
MTexttoData(X,InputTextBuffer,Value)
MPutMemData(X,lv MPD>>MPD.AVal.Addr,Value); endcase

default: CallSwat(); endcase
]
NewStandard(MPD); return
]
if (MBunion & BottomButton) ne 0 do//Append to InputTextBuffer
[ Resets(MPDVs); switchon TypeS into
[
case RegTypeStorage: RDatatoStream(MPDVs,X,Value); endcase
case MemTypeStorage: MDatatoStream(MPDVs,X,Value); endcase
]
for I = 1 to MPDVVec!0 do CharInputRoutine(MPDVVec!I)
]
if (MBunion & MiddleButton) ne 0 do//Alternate printout
[ Resets(CmdCommentStream)
ErrorProtect((selecton TypeS into
[
case RegTypeStorage: AltRForms
case MemTypeStorage: AltMForms
] )!X ,CmdCommentStream,X,Value,lv MPD>>MPD.AVal.Addr)
]
]


and MPDdeSelect(MPD) be
[
if MPD>>MPD.MenuMode ne StandardMenu do NewStandard(MPD)
]


and NewStandard(MPD) be
[
MPD>>MPD.MenuMode = StandardMenu; MPD>>MPD.Phase = BadValuePhase
]

and DoubleNeg(Ptr) be
[
Ptr!1 = - Ptr!1
Ptr!0 = (not Ptr!0)+(Ptr!1 eq 0 ? 1,0)
]