1 Star 0 Fork 0

onepisYa / StudyAsm_WangShuang

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
t1 copy.asm 21.42 KB
一键复制 编辑 原始数据 按行查看 历史
onepisYa 提交于 2021-03-22 16:16 . bug
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935
assume cs:code, ds:data, ss:stack
data segment
;0123456789ABCDEF
SCREEN db 18 dup('1000000000000001')
db '1111111111111111'
SCREEN_ROW dw 19; 高度
SCREEN_COL dw 16; 宽度
SCREEN_LOCATION dw 160*3; 首地址
BLOCK_O db '1100'
db '1100'
db '0000'
db '0000'; 字符 和 颜色
; 这里 的 11 代表 两个像素 是 要画上去的
BLOCK_I db '0000'
db '1111'
db '0000'
db '0000'
db '0100'
db '0100'
db '0100'
db '0100'
BLOCK_ROW dw 4
BLOCK_COL dw 4
BLOCK_COLOR dw 2201h
isAllowMoveDown dw 0
BLOCK_TOP dw 0
BLOCK_NOW_STATUS dw 0
BLOCK_FIRST dw 0
BLOCK_MAX dw 0
data ends
stack segment STACK
db 128 dup (0)
stack ends
code segment
start:
mov ax, stack
mov ss, ax
mov sp, 128
call cpy_Tetris
call save_old_int9
call set_new_int9
mov bx, 0
push bx
mov bx, 7e00h
push bx
retf; 跳转到 0:7e00h 去执行
mov ax, 4c00h
int 21h
; =================================================================
set_new_int9:
mov bx , 0
mov es , bx
cli
mov word ptr es:[9*4+0], offset new_int9 - offset Tetris + 07e00h; ip
mov word ptr es:[9*4+2], 0; cs
sti
ret
; =================================================================
save_old_int9:
mov bx, 0
mov es, bx
push es:[9*4+0]
pop es:[200h]
push es:[9*4+2]
pop es:[200h+2]
ret
; =================================================================
cpy_Tetris:
mov bx, cs
mov ds, bx
mov si, offset Tetris
mov bx, 0
mov es, bx
mov di, 07e00h
mov cx, offset Tetris_end - offset Tetris
cld
rep movsb
ret
Tetris:
call init_reg
call clear_screen
call init_screen
nextBlcok:
call init_block_type
call init_block_location
call print_block
s:
jmp s
mov ax, 4c00h
int 21h
; =================================================================
init_block_type:
mov si , offset BLOCK_I
mov BLOCK_ROW , 4
mov BLOCK_COL , 4
mov isAllowMoveDown, 1
; 是否允许向下移动,判断什么时候 , 产生一个新的 方块。
mov BLOCK_NOW_STATUS, 0
mov BLOCK_FIRST, si
mov BLOCK_MAX, 1
ret
init_block_location:
mov bx, SCREEN_LOCATION
add bx, 7*2; 屏幕是 2个字节 一个点。 我们的 一行 有 14个字符。 从第七个字符开始显示。
ret
; -----------------------------------------------------------------
print_block:
; si = 方块的内存地址
; bx = 方块在屏幕上的位置
push si
push bx
push dx
;mov cx, BLOCK_ROW
mov cx, 4
mov dx, BLOCK_COLOR
loop_printBlock:
push cx
push si
push bx
mov cx, 4
;mov cx, BLOCK_COL
loop_PrintBlockRow:
cmp byte ptr ds:[si], '1'
; 比较 是不是 ds:[si] 是不是 '1'
jne nextPrintBlockCol
mov es:[bx], dx
;
; 回到初始位置
nextPrintBlockCol:
add bx, 2
inc si
loop loop_PrintBlockRow
pop bx
pop si
pop cx
add bx, 160; 屏幕的偏移每次 + 160
add si, 4
loop loop_printBlock
pop dx
pop bx
pop si
ret
; =================================================================
init_screen:
push si
push di
push cx
push dx
mov bx, data
mov ds, bx
mov bx, 0b800h
mov es, bx
mov si, offset SCREEN
mov di, SCREEN_LOCATION
mov cx, SCREEN_ROW
; 注意 直接定址表 写 offset 是 取地址。 不写的话 直接 访问 值
initScreenCol:
push cx
push si
push di
mov cx, SCREEN_COL
initScreenRow:
mov dx, 0
mov dl, ds:[si]
cmp dl, '1'
jne drawCol
mov dh, 00010001B
drawCol:
mov es:[di], dx
add di, 2; 访问 我们定义的背景 起始地址
inc si; 访问 SCREEN 里面的数据
loop initScreenRow
pop di
pop si
pop cx
add si, 16
add di, 160
loop initScreenCol
pop di
pop cx
pop dx
pop si
ret
; =================================================================
clear_screen:
mov cx, 2000
mov bx, 0
mov dx, 0700h
clearScreen:
mov es:[bx], dx
add bx, 2
loop clearScreen
ret
; =================================================================
init_reg:
mov bx, 0b800h
mov es, bx
mov bx, data
mov ds, bx
ret
; =================================================================
new_int9:
push ax
in al, 60h
pushf
call dword ptr cs:[200h]
cmp al, 04bh; left
je isLeft
cmp al, 04dh; right
je isRight
cmp al, 050h; down
je isDown
cmp al, 039h; blank 空格
je isBlank
cmp al, 3bh ; F1
jne int9Ret
call change_screen_color
int9Ret:
pop ax
iret
; =================================================================
isLeft:
; 记得 这里不要修改 bx 的值
mov di, 160*10+40*2
call isMoveLeft
mov byte ptr es:[di],"L"
jmp int9Ret
isRight:
mov di, 160*10+40*2
call isMoveRight
mov byte ptr es:[di],"R"
jmp int9Ret
isDown:
mov di, 160*10+40*2
call isMoveDown
cmp isAllowMoveDown, 0
je isNextBlcok
mov byte ptr es:[di],"D"
jmp int9Ret
isNextBlcok:
; 处理指令
pop ax
add sp, 4
popf
; iret => pop ip, pop cs, popf
jmp nextBlcok
;----------------------------------------
clear_old_block:
push bx
mov cx, BLOCK_ROW
loop_clearOldBlock:
push cx
push bx
mov cx, BLOCK_COL
loop_nextClearOldRow:
cmp word ptr es:[bx], 2201h
jne nextClearOldCol
mov word ptr es:[bx], 0030h
nextClearOldCol:
add bx, 2
loop loop_nextClearOldRow
pop bx
pop cx
add bx, 160
loop loop_clearOldBlock
pop bx
ret
; --------------------------------
isBlank:
mov di, 160*10+40*2
mov byte ptr es:[di],"B"
call isTransform
jmp int9Ret
; =================================================================
isTransform:
; 功能:判断变形 使用的形状, 并画出新的形状 ;
; 入口: 无 ;
; 出口: 无 ;
; 堆栈使用:无 ;
; 全局变量: BLOCK_NOW_STATUS BLOCK_MAX BLOCK_FISRT ;
; push 1
push bx
push si
push BLOCK_NOW_STATUS
inc BLOCK_NOW_STATUS
mov dx,BLOCK_NOW_STATUS
cmp dx, BLOCK_MAX
; 比较 status 和 max
; status <= max si+16 因为是 进来才 加上去的 status+1
jna checkTransform
; status > max
; 状态 归零
; 把 si 改为 fisrt 第一个形状
mov BLOCK_NOW_STATUS, 0
mov si, BLOCK_FIRST
; 然后 继续第二部判断。 是否可以 变形。
; 根据 bx 当前的位置 进行判断。
jmp drawTransform
checkTransform:
add si, 16
; --------------------------------
drawTransform:
; push 2
push si
; 进入到 这里面 si = first 或者 add si,16 当前形状 的 si +16
mov cx, BLOCK_ROW
loop_checkTransform:
; push 3
push cx
push bx
push si
mov cx, BLOCK_COL
loop_nextCheckTransformRow:
; 这个的 判断方式 也是 循环
; 遍历 当 bx 指向的
cmp byte ptr ds:[si], '1'
; 判断 下一个 形状的 数据 开始的地方是不是 1
jne nextCheckTransformCol
; 不是一 就 下一列
; 是 一 就 判断 bx 所在 位置 是不是 2201h
cmp word ptr es:[bx], 2201h
; 是 bx 所在位置 是 2201h 就 下一列
je nextCheckTransformCol
; 不是 2201h 判断这个位置是不是 0030H
cmp word ptr es:[bx], 0030h
; 有任何 一列 里面 si 所在 位置是 1 的 那一行 或者 列 在本轮循环 es:[bx] 有 不是 0030h 的 就 不允许 变形
jne notAllowTransform
nextCheckTransformCol:
add bx, 2
inc si
loop loop_nextCheckTransformRow
; pop 3
pop si
pop bx
pop cx
add si, 4
add bx, 160
loop loop_checkTransform
; pop 2
pop si; 恢复 si
add sp, 4
pop bx; 恢复 bx
; 内部的 pop 3 已经 处理过 栈了 ,不用再 pop 或者 add sp 了。
call clear_old_block
call print_block
ret
notAllowTransform:
add sp, 8
; pop 1
pop BLOCK_NOW_STATUS
pop si
pop bx
ret
; ================================================================
; ----------------------------------------------------
isMoveDown:
; 边界 暂时不考虑 超过 2*2的形状
mov cx, BLOCK_ROW
push bx
moveDown:
push cx
push bx
mov cx, BLOCK_COL
nextMoveDownRow:
; 按行判断
cmp word ptr es:[bx], 2201H
; 这个 判断原理就是 一个 4*4 的范围遍历 因为我们图形 都是在 4*4 以内的 大小
; 这个范围一定要比 占用 面积 最大的图形要大 一点
;行1 x1 x2 .. 这样依次遍历 如果 一列 的 两个都是 2201H 笑脸 那么 去下一列
;行2 x1 x2 ..
; 否则 这套遍历 判断出来 的 是 不准确的。
; 比较如果 屏幕 我们的背景区域 的像素 不是 2201H 笑脸 和 绿色
; 则继续判断一下个字符。
jne nextMoveDownCol
cmp word ptr es:[bx+160], 2201H
; 如果他的下面也是2201H 那么也是 跳过
je nextMoveDownCol
; 如果是我们就 要判断这个位置的下边的位置是不是0030h 黑色背景和空字符
cmp word ptr es:[bx+160], 0030H
; 如果有 任何一行 下边 不是 0030H 那么都是不允许移动的。
jne notAllowMoveDown
; 这里我们就 不能跳过 行了。 还要 往后面 去找 这一行的 后面 一列
jmp nextMoveDownCol
nextMoveDownCol:
; 移动到下一列
add bx, 2; 下一列
loop nextMoveDownRow
nextDownRow:
pop bx
pop cx
add bx, 160; 跳到下一行
loop moveDown
pop bx
call clear_old_block
add bx, 160; 换行显示
call print_block
ret
notAllowMoveDown:
;pop bx
;pop cx
; 或者 add sp,4 也是 可以的。
; 考虑 产生一个新的方块。
add sp, 4
pop bx
mov isAllowMoveDown, 0
call set_bottom_block
call get_row
call check_clear_row
ret
; 最后肯定是要进行跳转的 jmp nextBlock
; 跳转前我们必须 处理好, 这些会影响到 栈中 数据的指令 。
; 当 不允许方块 下落的时候 。 我们要跳转。
; 那么我们可以 设置一个 状态。 isAllowMoveDown 设置为 0
; 默认的时候为 1 , 在 isMoveDown 里面 对其进行判断
; 处理的 地方 放在 isMoveDown 里面
; 在初始化的时候 我们需要 将 isAllowMoveDown 初始化为 1
;===============================
check_clear_row:
; 检测 消行的 行数
mov cx, 2
loop_checkClearRow:
push cx
push bx
mov dx, bx ; 用 dx 寄存器 暂存 一下 bx 每轮 循环的 bx
mov cx, 14
loop_checkClearCol:
cmp word ptr es:[bx], 4432h; 4432h 是方块的一个点
jne nextCheckClearRow
add bx, 2
loop loop_checkClearCol
; 一整行都是 4432h 的时候 我们才 消行
call get_top_row
; 执行完 get_top_row 得到 bx = 有 4432h 的最上面的一行的首地址
; 然后dx等于 消行的那行的首地址 => 其实是 方块落下的 那个方块的 bx 的首地址。
mov bx, dx; 将 暂存的 bx 拿出来。 可以消行的那一行的 首地址
; bx = 3042-320
; 用于调试
; mov word ptr es:[bx], 5535h
; s3:
; jmp s3
; 用于调试
call clear_row
; 这里的 消行 之后的 bx 我们还是 保持不变的。
; 也就是 说进入 draw_new_screen 这个函数之后 bx 和 dx 是一样的。
call draw_new_screen
nextCheckClearRow:
pop bx
pop cx
add bx, 160
loop loop_checkClearRow
ret
;===============================
draw_new_screen:
mov bx, BLOCK_TOP
cmp bx, dx
; 如果两个值 一样 那么 我们就 什么都不做。
je drawNewScreenRet
; 在 执行 get_top_row 之前
; bx = dx = 3042 - 320 = 2722
; 执行之后 最上面的一行 就 变为了 bx = 3042-160 = 2882
; cx = 1
; 再 执行一次。消行 然后到 get_top_row 那里
; 在 执行 get_top_row 之前
; bx = dx =3042 - 160 =2882
; 执行之后 bx = 2882
call calculate_rows
mov cx, ax; 计算出 移动的行数。
; 这样cx 等于 0
; =================================================================
mov si, dx; si 是可以消行的 那行的 首地址
sub si, 160; si = si - 160 = 消行后 上面一行的 首地址 si = 2722 -160 =2562
mov di, dx; 可以消行的那行的 首地址 di = 2722
call drawNewScreen
call clear_row
; 清除最上面一行, 清除bx 所在行 的数据。 bx = 2882
drawNewScreenRet:
ret
; --------------------------------
calculate_rows:
; 功能: 计算 方块消行后,上方屏幕需要移动的行数
; 入口: bx = 有方块的颜色的最上方的行的首地址 , dx = 可以消行的 那行的 的 首地址
; 出口: ax = 计算出来的行数
mov ax, dx
sub ax, bx
mov dx, 0
mov cx, 160
div cx
; 然后我们用 (可以消行的那行的首地址-最上面那行有4432h的那行的首地址)/160 = 我们需要移动的行数
ret
; --------------------------------
drawNewScreen:
; 功能: 根据 cx 进行屏幕消行后的 上方方块,向下移动
; 入口: cx = 移动的行数 , si = 消行后,消行那一行上方的首地址, di = 可以消行的那行的首地址
; 出口: 无
push cx
push si
push di
loop_drawNewRow:
push cx
push si
push di
mov cx, 14
loop_drawNewCol:
push es:[si]; 将上面一格 的数据 。 放到 di 所在行 进行替换。
pop es:[di]
add si, 2
add di, 2
loop loop_drawNewCol
pop di
pop si
pop cx
; 完成一行的循环之后。 si 和 di 同时向上 移动 一行
sub si, 160
sub di, 160
loop loop_drawNewRow
pop di
pop si
pop cx
ret
; --------------------------------
;===============================
get_top_row:
mov bx, SCREEN_LOCATION
add bx, 2
nextGetTopRow:
push bx
mov cx, 14
loop_nextGetTopCol:
cmp word ptr es:[bx], 4432H
; 如果有一个是 4432H 那么说明我们找到了
je getTopRow
add bx, 2
loop loop_nextGetTopCol
pop bx
; 走到这里说明 这一行没有找到
; 那么我们换一行, 继续找
add bx, 160
jmp nextGetTopRow
getTopRow:
; 找到之后, pop 出来的 bx 就是我们需要的 有 4432H 的 最上面的一行 的首地址。
; 然后我们用 (可以消行的那行的首地址-最上面那行有4432h的那行的首地址)/160 = 我们需要移动的行数
pop bx
ret
;===============================
clear_row:
push bx
mov cx, 14
clearRow:
mov word ptr es:[bx], 0030H
add bx, 2
loop clearRow
pop bx
ret
;===============================
get_row:
; 获取 落下后 落下的那个方块所在 行 的 首地址。
cmp byte ptr es:[bx], '1'
je getRow
sub bx, 2
jmp get_row
getRow:
add bx, 2
ret
set_bottom_block:
; 更换底部方块的颜色
push bx
mov cx, BLOCK_ROW
loop_nextSetBottomRow:
push cx
push bx
mov cx, BLOCK_COL
loop_SetBottomRow:
cmp word ptr es:[bx], 2201h
jne nextSetBottomCol
mov word ptr es:[bx], 4432h
nextSetBottomCol:
add bx, 2
loop loop_SetBottomRow
pop bx
pop cx
add bx, 160
loop loop_nextSetBottomRow
pop bx
ret
; ----------------------------------------------------
isMoveRight:
mov cx, BLOCK_ROW
push bx
loop_moveRight:
push cx
push bx
mov cx, BLOCK_COL
loop_nextMoveRightRow:
cmp word ptr es:[bx], 2201H
; 比较如果 屏幕 我们的背景区域 的像素 不是 2201H 笑脸 和 绿色
; 则继续判断一下个字符。
jne nextMoveRightCol
cmp word ptr es:[bx+2], 2201H
; 如果他的右边也是2201H 那么也是 跳过
je nextMoveRightCol
; 如果是我们就 要判断这个位置的右边的位置是不是0030h 黑色背景和空字符
cmp word ptr es:[bx+2], 0030H
; 如果有 任何一行 右边 不是 0030H 那么都是不允许移动的。
jne notAllowMoveRight
jmp nextRightRow
nextMoveRightCol:
add bx, 2
loop loop_nextMoveRightRow
nextRightRow:
pop bx
pop cx
add bx, 160
loop loop_moveRight
pop bx
call clear_old_block
add bx, 2
call print_block
ret
notAllowMoveRight:
;pop bx
;pop cx
; 或者 add sp,4 也是 可以的。
add sp, 4
pop bx
ret
; ------------------------------------------------------
isMoveLeft:
; si = 方块的内存地址
; bx = 方块在屏幕上的位置
; 边界判断也是在 这个里面编写
; 其实这个函数可以抽象 , 包装成一个计算过程 也就是 函数。 然后 传递参数。 实现左右的判断。
mov cx, BLOCK_ROW
push bx
; ----------------------------------------------------
moveLeft:
push cx
push bx
mov cx, BLOCK_COL
nextMoveLeftRow:
cmp word ptr es:[bx], 2201H
; 比较如果 屏幕 我们的背景区域 的像素 不是 2201H 笑脸 和 绿色
; 则继续判断一下个字符。
jne nextMoveLeftCol
; 如果是我们就 要判断这个位置的左边的位置是不是0030h 黑色背景和空字符
cmp word ptr es:[bx-2], 0030H
; 如果有 任何一行 左边 不是 0030H 那么都是不允许移动的。
jne notAllowMoveLeft
jmp nextLeftRow
nextMoveLeftCol:
add bx, 2
loop nextMoveLeftRow
nextLeftRow:
pop bx
pop cx
add bx, 160
loop moveLeft
pop bx
; ------------------------------------------------
call clear_old_block
sub bx, 2
call print_block
ret
notAllowMoveLeft:
;pop bx
;pop cx
; 或者 add sp,4 也是 可以的。
add sp, 4
pop bx
ret
; =================================================================
change_screen_color:
push bx
push cx
push es
mov bx, 0b800h
mov es, bx
mov bx, 1
mov cx, 2000
changeScreenColor:
inc byte ptr es:[bx]
add bx, 2
loop changeScreenColor
pop es
pop cx
pop bx
ret
; =================================================================
; mov di, 160*16+50*2
showReg:
; 用于调试
push ax
push bx
push cx
push dx
push di
push si
push bp
mov ax, dx
mov dx, 0
mov di, si
call isShortDiv
pop bp
pop si
pop di
pop dx
pop cx
pop bx
pop ax
;调试结束
; 用于调试
push ax
push bx
push cx
push dx
push di
push si
push bp
mov ax, dx
mov dx, 0
add si, 160
mov di, si
call isShortDiv
pop bp
pop si
pop di
pop dx
pop cx
pop bx
pop ax
s2:
jmp s2
;调试结束
; =================================================================
isShortDiv:
mov cx, dx; 判断 dx 被除数 的 高16bit 是不是 0
jcxz short_div
push ax
mov bp, sp; 等下 用 ss:[bp] 去 访问 ax 中 的 被除数 低 16bit
mov cx, 10; 设置 除数
call long_div
add cl, 30H
add sp, 2; 非常重要。 这句。
; 因为 如果 不将 栈顶标记 复位。 也不 pop 出来 long_div后面的 pop 都会 对不上相应的数据
; 很有可能是下次循环的时候 坑了自己
mov es:[di], cl; es:[di] 输入到显存中 去显示。
sub di,2
jmp isShortDiv
; =================================================================
long_div:
;公式: `X/N = int(H/N)*65536+[rem(H/N)*65536+L]/N`
mov ax, dx; 高 16bit 除以 N
mov dx, 0
div cx; 结果 ax 商 = int(H/N) dx 余数 = rem(H/N)
push ax; 暂存 int(H/N)
mov ax, ss:[bp+0]; ax 现在 等于 被除数的 低 16bit L
; 这个时候 dx 高位 是 rem(H/N) 相当于 就 是 已经 乘 了 65536 了
div cx
; 得到 结果 ax 商 dx 余数
mov cx, dx; 将余数放到cx 中
pop dx; 将 int(H/N) 放到 dx 中
; 这个时候 dx 高位 是 int(H/N) 相当于 就 是 已经 乘 了 65536 了
;==============
; 现在 结果是 ax = L dx = H cx = 余数
ret
; =================================================================
short_div:
mov cx, 10; 设置 除数
div cx; 除法
add dl, 30H;转换
mov es:[di],dl; 直接 es:[di] 输入 到 屏幕
mov cx, ax; 判断商
jcxz ShortDivRet
sub di, 2
mov dx,0
jmp short_div
ShortDivRet:
ret
Tetris_end:
nop
code ends
end start
汇编
1
https://gitee.com/onepisYa/studyAsm_WangShuang.git
git@gitee.com:onepisYa/studyAsm_WangShuang.git
onepisYa
studyAsm_WangShuang
StudyAsm_WangShuang
master

搜索帮助